• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.shade;
18 
19 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
20 import static android.view.View.INVISIBLE;
21 import static android.view.View.VISIBLE;
22 
23 import static androidx.constraintlayout.widget.ConstraintSet.END;
24 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
25 
26 import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION;
27 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
28 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
29 import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
30 import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
31 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
32 import static com.android.systemui.classifier.Classifier.GENERIC;
33 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
34 import static com.android.systemui.classifier.Classifier.UNLOCK;
35 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
36 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
37 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPENING;
38 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
39 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
40 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
41 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
42 import static com.android.systemui.statusbar.StatusBarState.SHADE;
43 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
44 import static com.android.systemui.statusbar.VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES;
45 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
46 import static com.android.systemui.util.DumpUtilsKt.asIndenting;
47 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
48 
49 import static java.lang.Float.isNaN;
50 
51 import android.animation.Animator;
52 import android.animation.AnimatorListenerAdapter;
53 import android.animation.ValueAnimator;
54 import android.annotation.NonNull;
55 import android.annotation.Nullable;
56 import android.app.StatusBarManager;
57 import android.content.ContentResolver;
58 import android.content.res.Resources;
59 import android.database.ContentObserver;
60 import android.graphics.Color;
61 import android.graphics.Insets;
62 import android.graphics.Rect;
63 import android.graphics.Region;
64 import android.hardware.biometrics.SensorLocationInternal;
65 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
66 import android.os.Bundle;
67 import android.os.Handler;
68 import android.os.PowerManager;
69 import android.os.Process;
70 import android.os.Trace;
71 import android.os.UserManager;
72 import android.os.VibrationEffect;
73 import android.provider.Settings;
74 import android.transition.ChangeBounds;
75 import android.transition.Transition;
76 import android.transition.TransitionListenerAdapter;
77 import android.transition.TransitionManager;
78 import android.transition.TransitionSet;
79 import android.transition.TransitionValues;
80 import android.util.IndentingPrintWriter;
81 import android.util.Log;
82 import android.util.MathUtils;
83 import android.view.InputDevice;
84 import android.view.LayoutInflater;
85 import android.view.MotionEvent;
86 import android.view.VelocityTracker;
87 import android.view.View;
88 import android.view.View.AccessibilityDelegate;
89 import android.view.ViewConfiguration;
90 import android.view.ViewGroup;
91 import android.view.ViewPropertyAnimator;
92 import android.view.ViewStub;
93 import android.view.ViewTreeObserver;
94 import android.view.WindowInsets;
95 import android.view.accessibility.AccessibilityEvent;
96 import android.view.accessibility.AccessibilityManager;
97 import android.view.accessibility.AccessibilityNodeInfo;
98 import android.view.animation.Interpolator;
99 import android.widget.FrameLayout;
100 
101 import androidx.constraintlayout.widget.ConstraintSet;
102 
103 import com.android.internal.annotations.VisibleForTesting;
104 import com.android.internal.jank.InteractionJankMonitor;
105 import com.android.internal.logging.MetricsLogger;
106 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
107 import com.android.internal.policy.SystemBarUtils;
108 import com.android.internal.util.LatencyTracker;
109 import com.android.keyguard.ActiveUnlockConfig;
110 import com.android.keyguard.FaceAuthApiRequestReason;
111 import com.android.keyguard.KeyguardClockSwitch.ClockSize;
112 import com.android.keyguard.KeyguardStatusView;
113 import com.android.keyguard.KeyguardStatusViewController;
114 import com.android.keyguard.KeyguardUnfoldTransition;
115 import com.android.keyguard.KeyguardUpdateMonitor;
116 import com.android.keyguard.LockIconViewController;
117 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
118 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
119 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
120 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
121 import com.android.systemui.DejankUtils;
122 import com.android.systemui.Dumpable;
123 import com.android.systemui.Gefingerpoken;
124 import com.android.systemui.R;
125 import com.android.systemui.animation.ActivityLaunchAnimator;
126 import com.android.systemui.animation.Interpolators;
127 import com.android.systemui.animation.LaunchAnimator;
128 import com.android.systemui.biometrics.AuthController;
129 import com.android.systemui.classifier.Classifier;
130 import com.android.systemui.classifier.FalsingCollector;
131 import com.android.systemui.dagger.qualifiers.DisplayId;
132 import com.android.systemui.dagger.qualifiers.Main;
133 import com.android.systemui.doze.DozeLog;
134 import com.android.systemui.dump.DumpManager;
135 import com.android.systemui.dump.DumpsysTableLogger;
136 import com.android.systemui.flags.FeatureFlags;
137 import com.android.systemui.flags.Flags;
138 import com.android.systemui.fragments.FragmentService;
139 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
140 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
141 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
142 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
143 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
144 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
145 import com.android.systemui.keyguard.shared.model.TransitionState;
146 import com.android.systemui.keyguard.shared.model.TransitionStep;
147 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
148 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
149 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
150 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel;
151 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel;
152 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel;
153 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
154 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
155 import com.android.systemui.media.controls.pipeline.MediaDataManager;
156 import com.android.systemui.media.controls.ui.KeyguardMediaController;
157 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
158 import com.android.systemui.model.SysUiState;
159 import com.android.systemui.navigationbar.NavigationBarController;
160 import com.android.systemui.navigationbar.NavigationBarView;
161 import com.android.systemui.navigationbar.NavigationModeController;
162 import com.android.systemui.plugins.ClockAnimations;
163 import com.android.systemui.plugins.FalsingManager;
164 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
165 import com.android.systemui.plugins.qs.QS;
166 import com.android.systemui.plugins.statusbar.StatusBarStateController;
167 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
168 import com.android.systemui.shade.transition.ShadeTransitionController;
169 import com.android.systemui.shared.system.QuickStepContract;
170 import com.android.systemui.statusbar.CommandQueue;
171 import com.android.systemui.statusbar.GestureRecorder;
172 import com.android.systemui.statusbar.KeyguardIndicationController;
173 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
174 import com.android.systemui.statusbar.NotificationShadeDepthController;
175 import com.android.systemui.statusbar.NotificationShadeWindowController;
176 import com.android.systemui.statusbar.NotificationShelfController;
177 import com.android.systemui.statusbar.PulseExpansionHandler;
178 import com.android.systemui.statusbar.RemoteInputController;
179 import com.android.systemui.statusbar.StatusBarState;
180 import com.android.systemui.statusbar.SysuiStatusBarStateController;
181 import com.android.systemui.statusbar.VibratorHelper;
182 import com.android.systemui.statusbar.notification.AnimatableProperty;
183 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
184 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
185 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
186 import com.android.systemui.statusbar.notification.PropertyAnimator;
187 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
188 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
189 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
190 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
191 import com.android.systemui.statusbar.notification.row.ExpandableView;
192 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
193 import com.android.systemui.statusbar.notification.stack.AmbientState;
194 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
195 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
196 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
197 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
198 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
199 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
200 import com.android.systemui.statusbar.phone.BounceInterpolator;
201 import com.android.systemui.statusbar.phone.CentralSurfaces;
202 import com.android.systemui.statusbar.phone.DozeParameters;
203 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
204 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
205 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
206 import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
207 import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController;
208 import com.android.systemui.statusbar.phone.KeyguardBypassController;
209 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
210 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
211 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
212 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
213 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
214 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
215 import com.android.systemui.statusbar.phone.ScrimController;
216 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
217 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
218 import com.android.systemui.statusbar.phone.TapAgainViewController;
219 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
220 import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
221 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
222 import com.android.systemui.statusbar.policy.ConfigurationController;
223 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
224 import com.android.systemui.statusbar.policy.KeyguardStateController;
225 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
226 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
227 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
228 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
229 import com.android.systemui.unfold.SysUIUnfoldComponent;
230 import com.android.systemui.util.Compile;
231 import com.android.systemui.util.LargeScreenUtils;
232 import com.android.systemui.util.Utils;
233 import com.android.systemui.util.time.SystemClock;
234 import com.android.wm.shell.animation.FlingAnimationUtils;
235 
236 import java.io.PrintWriter;
237 import java.util.ArrayList;
238 import java.util.Collections;
239 import java.util.List;
240 import java.util.Optional;
241 import java.util.function.Consumer;
242 
243 import javax.inject.Inject;
244 import javax.inject.Provider;
245 
246 import kotlin.Unit;
247 import kotlinx.coroutines.CoroutineDispatcher;
248 
249 @CentralSurfacesComponent.CentralSurfacesScope
250 public final class NotificationPanelViewController implements Dumpable {
251 
252     public static final String TAG = NotificationPanelView.class.getSimpleName();
253     public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
254     public static final float FLING_SPEED_UP_FACTOR = 0.6f;
255     public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f;
256     public static final float FLING_CLOSING_SPEED_UP_FACTOR = 0.6f;
257     public static final int WAKEUP_ANIMATION_DELAY_MS = 250;
258     private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
259     private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
260     private static final boolean DEBUG_DRAWABLE = false;
261     private static final VibrationEffect ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT =
262             VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false);
263     /** The parallax amount of the quick settings translation when dragging down the panel. */
264     public static final float QS_PARALLAX_AMOUNT = 0.175f;
265     /** Fling expanding QS. */
266     public static final int FLING_EXPAND = 0;
267     /** Fling collapsing QS, potentially stopping when QS becomes QQS. */
268     public static final int FLING_COLLAPSE = 1;
269     /** Fling until QS is completely hidden. */
270     public static final int FLING_HIDE = 2;
271     /** The delay to reset the hint text when the hint animation is finished running. */
272     private static final int HINT_RESET_DELAY_MS = 1200;
273     private static final long ANIMATION_DELAY_ICON_FADE_IN =
274             ActivityLaunchAnimator.TIMINGS.getTotalDuration()
275                     - CollapsedStatusBarFragment.FADE_IN_DURATION
276                     - CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
277     private static final int NO_FIXED_DURATION = -1;
278     private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
279     private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
280 
281     /**
282      * The factor of the usual high velocity that is needed in order to reach the maximum overshoot
283      * when flinging. A low value will make it that most flings will reach the maximum overshoot.
284      */
285     private static final float FACTOR_OF_HIGH_VELOCITY_FOR_MAX_OVERSHOOT = 0.5f;
286     /**
287      * Maximum time before which we will expand the panel even for slow motions when getting a
288      * touch passed over from launcher.
289      */
290     private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
291     private static final int MAX_DOWN_EVENT_BUFFER_SIZE = 50;
292     private static final String COUNTER_PANEL_OPEN = "panel_open";
293     public static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
294     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
295     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
296     private static final Rect EMPTY_RECT = new Rect();
297     /**
298      * Duration to use for the animator when the keyguard status view alignment changes, and a
299      * custom clock animation is in use.
300      */
301     private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000;
302 
303     private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
304     private final Resources mResources;
305     private final KeyguardStateController mKeyguardStateController;
306     private final SysuiStatusBarStateController mStatusBarStateController;
307     private final AmbientState mAmbientState;
308     private final LockscreenGestureLogger mLockscreenGestureLogger;
309     private final SystemClock mSystemClock;
310     private final ShadeLogger mShadeLog;
311     private final DozeParameters mDozeParameters;
312     private final NotificationStackScrollLayout.OnEmptySpaceClickListener
313             mOnEmptySpaceClickListener = (x, y) -> onEmptySpaceClick();
314     private final ShadeHeadsUpChangedListener mOnHeadsUpChangedListener =
315             new ShadeHeadsUpChangedListener();
316     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
317     private final SettingsChangeObserver mSettingsChangeObserver;
318     private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
319     private final NotificationPanelView mView;
320     private final VibratorHelper mVibratorHelper;
321     private final MetricsLogger mMetricsLogger;
322     private final ConfigurationController mConfigurationController;
323     private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
324     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
325     private final LayoutInflater mLayoutInflater;
326     private final FeatureFlags mFeatureFlags;
327     private final PowerManager mPowerManager;
328     private final AccessibilityManager mAccessibilityManager;
329     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
330     private final PulseExpansionHandler mPulseExpansionHandler;
331     private final KeyguardBypassController mKeyguardBypassController;
332     private final KeyguardUpdateMonitor mUpdateMonitor;
333     private final ConversationNotificationManager mConversationNotificationManager;
334     private final AuthController mAuthController;
335     private final MediaHierarchyManager mMediaHierarchyManager;
336     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
337     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
338     private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
339     private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
340     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
341     private final FragmentService mFragmentService;
342     private final ScrimController mScrimController;
343     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
344     private final TapAgainViewController mTapAgainViewController;
345     private final ShadeHeaderController mShadeHeaderController;
346     private final boolean mVibrateOnOpening;
347     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
348     private final FlingAnimationUtils mFlingAnimationUtilsClosing;
349     private final FlingAnimationUtils mFlingAnimationUtilsDismissing;
350     private final LatencyTracker mLatencyTracker;
351     private final DozeLog mDozeLog;
352     /** Whether or not the NotificationPanelView can be expanded or collapsed with a drag. */
353     private final boolean mNotificationsDragEnabled;
354     private final Interpolator mBounceInterpolator;
355     private final NotificationShadeWindowController mNotificationShadeWindowController;
356     private final ShadeExpansionStateManager mShadeExpansionStateManager;
357     private final FalsingTapListener mFalsingTapListener = this::falsingAdditionalTapRequired;
358     private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate();
359     private final NotificationGutsManager mGutsManager;
360     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
361     private final QuickSettingsController mQsController;
362     private final InteractionJankMonitor mInteractionJankMonitor;
363     private final TouchHandler mTouchHandler = new TouchHandler();
364 
365     private long mDownTime;
366     private boolean mTouchSlopExceededBeforeDown;
367     private boolean mIsLaunchAnimationRunning;
368     private float mOverExpansion;
369     private CentralSurfaces mCentralSurfaces;
370     private HeadsUpManagerPhone mHeadsUpManager;
371     private float mExpandedHeight = 0;
372     private boolean mTracking;
373     private boolean mHintAnimationRunning;
374     private KeyguardBottomAreaView mKeyguardBottomArea;
375     private boolean mExpanding;
376     private boolean mSplitShadeEnabled;
377     /** The bottom padding reserved for elements of the keyguard measuring notifications. */
378     private float mKeyguardNotificationBottomPadding;
379     /**
380      * The top padding from where notification should start in lockscreen.
381      * Should be static also during animations and should match the Y of the first notification.
382      */
383     private float mKeyguardNotificationTopPadding;
384     /** Current max allowed keyguard notifications determined by measuring the panel. */
385     private int mMaxAllowedKeyguardNotifications;
386     private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
387     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
388     private KeyguardStatusBarView mKeyguardStatusBar;
389     private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
390     private KeyguardStatusViewController mKeyguardStatusViewController;
391     private final LockIconViewController mLockIconViewController;
392     private NotificationsQuickSettingsContainer mNotificationContainerParent;
393     private final NotificationsQSContainerController mNotificationsQSContainerController;
394     private final Provider<KeyguardBottomAreaViewController>
395             mKeyguardBottomAreaViewControllerProvider;
396     private boolean mAnimateNextPositionUpdate;
397     private final ScreenOffAnimationController mScreenOffAnimationController;
398     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
399     private TrackingStartedListener mTrackingStartedListener;
400     private OpenCloseListener mOpenCloseListener;
401     private GestureRecorder mGestureRecorder;
402     private boolean mPanelExpanded;
403 
404     private boolean mKeyguardQsUserSwitchEnabled;
405     private boolean mKeyguardUserSwitcherEnabled;
406     private boolean mDozing;
407     private boolean mDozingOnDown;
408     private boolean mBouncerShowing;
409     private int mBarState;
410     private FlingAnimationUtils mFlingAnimationUtils;
411     private int mStatusBarMinHeight;
412     private int mStatusBarHeaderHeightKeyguard;
413     private float mOverStretchAmount;
414     private float mDownX;
415     private float mDownY;
416     private int mDisplayTopInset = 0; // in pixels
417     private int mDisplayRightInset = 0; // in pixels
418     private int mDisplayLeftInset = 0; // in pixels
419 
420     private final KeyguardClockPositionAlgorithm
421             mClockPositionAlgorithm =
422             new KeyguardClockPositionAlgorithm();
423     private final KeyguardClockPositionAlgorithm.Result
424             mClockPositionResult =
425             new KeyguardClockPositionAlgorithm.Result();
426     private boolean mIsExpanding;
427 
428     private String mHeaderDebugInfo;
429 
430     /**
431      * Indicates drag starting height when swiping down or up on heads-up notifications.
432      * This usually serves as a threshold from when shade expansion should really start. Otherwise
433      * this value would be height of shade and it will be immediately expanded to some extent.
434      */
435     private int mHeadsUpStartHeight;
436     private HeadsUpTouchHelper mHeadsUpTouchHelper;
437     private boolean mListenForHeadsUp;
438     private int mNavigationBarBottomHeight;
439     private boolean mExpandingFromHeadsUp;
440     private boolean mCollapsedOnDown;
441     private boolean mClosingWithAlphaFadeOut;
442     private boolean mHeadsUpAnimatingAway;
443     private final FalsingManager mFalsingManager;
444     private final FalsingCollector mFalsingCollector;
445 
446     private boolean mShowIconsWhenExpanded;
447     private int mIndicationBottomPadding;
448     private int mAmbientIndicationBottomPadding;
449     /** Whether the notifications are displayed full width (no margins on the side). */
450     private boolean mIsFullWidth;
451     private boolean mBlockingExpansionForCurrentTouch;
452      // Following variables maintain state of events when input focus transfer may occur.
453     private boolean mExpectingSynthesizedDown;
454     private boolean mLastEventSynthesizedDown;
455 
456     /** Current dark amount that follows regular interpolation curve of animation. */
457     private float mInterpolatedDarkAmount;
458     /**
459      * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
460      * interpolation curve is different.
461      */
462     private float mLinearDarkAmount;
463     private boolean mPulsing;
464     private boolean mHideIconsDuringLaunchAnimation = true;
465     private int mStackScrollerMeasuringPass;
466     /** Non-null if a heads-up notification's position is being tracked. */
467     @Nullable
468     private ExpandableNotificationRow mTrackedHeadsUpNotification;
469     private final ArrayList<Consumer<ExpandableNotificationRow>>
470             mTrackingHeadsUpListeners = new ArrayList<>();
471     private HeadsUpAppearanceController mHeadsUpAppearanceController;
472 
473     private int mPanelAlpha;
474     private Runnable mPanelAlphaEndAction;
475     private float mBottomAreaShadeAlpha;
476     final ValueAnimator mBottomAreaShadeAlphaAnimator;
477     private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
478             NotificationPanelView::setPanelAlphaInternal,
479             NotificationPanelView::getCurrentPanelAlpha,
480             R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
481             R.id.panel_alpha_animator_end_tag);
482     private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
483             new AnimationProperties().setDuration(150).setCustomInterpolator(
484                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
485     private final AnimationProperties mPanelAlphaInPropertiesAnimator =
486             new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
487                 if (mPanelAlphaEndAction != null) {
488                     mPanelAlphaEndAction.run();
489                 }
490             }).setCustomInterpolator(
491                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
492 
493     private final CommandQueue mCommandQueue;
494     private final UserManager mUserManager;
495     private final MediaDataManager mMediaDataManager;
496     @PanelState
497     private int mCurrentPanelState = STATE_CLOSED;
498     private final SysUiState mSysUiState;
499     private final NotificationShadeDepthController mDepthController;
500     private final NavigationBarController mNavigationBarController;
501     private final int mDisplayId;
502 
503     private final KeyguardIndicationController mKeyguardIndicationController;
504     private int mHeadsUpInset;
505     private boolean mHeadsUpPinnedMode;
506     private boolean mAllowExpandForSmallExpansion;
507     private Runnable mExpandAfterLayoutRunnable;
508     private Runnable mHideExpandedRunnable;
509 
510     /** The maximum overshoot allowed for the top padding for the full shade transition. */
511     private int mMaxOverscrollAmountForPulse;
512 
513     /** Whether a collapse that started on the panel should allow the panel to intercept. */
514     private boolean mIsPanelCollapseOnQQS;
515 
516     /** Alpha of the views which only show on the keyguard but not in shade / shade locked. */
517     private float mKeyguardOnlyContentAlpha = 1.0f;
518     /** Y translation of the views that only show on the keyguard but in shade / shade locked. */
519     private int mKeyguardOnlyTransitionTranslationY = 0;
520     private float mUdfpsMaxYBurnInOffset;
521     /** Are we currently in gesture navigation. */
522     private boolean mIsGestureNavigation;
523     private int mOldLayoutDirection;
524     private NotificationShelfController mNotificationShelfController;
525 
526     private final ContentResolver mContentResolver;
527     private float mMinFraction;
528 
529     private final KeyguardMediaController mKeyguardMediaController;
530 
531     private boolean mStatusViewCentered = true;
532 
533     private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
534     private final Optional<NotificationPanelUnfoldAnimationController>
535             mNotificationPanelUnfoldAnimationController;
536 
537     /** The drag distance required to fully expand the split shade. */
538     private int mSplitShadeFullTransitionDistance;
539     /** The drag distance required to fully transition scrims. */
540     private int mSplitShadeScrimTransitionDistance;
541 
542     private final NotificationListContainer mNotificationListContainer;
543     private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
544     private final NPVCDownEventState.Buffer mLastDownEvents;
545     private final KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel;
546     private final KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
547     private float mMinExpandHeight;
548     private final ShadeHeightLogger mShadeHeightLogger;
549     private boolean mPanelUpdateWhenAnimatorEnds;
550     private boolean mHasVibratedOnOpen = false;
551     private int mFixedDuration = NO_FIXED_DURATION;
552     /** The overshoot amount when the panel flings open. */
553     private float mPanelFlingOvershootAmount;
554     /** The amount of pixels that we have overexpanded the last time with a gesture. */
555     private float mLastGesturedOverExpansion = -1;
556     /** Whether the current animator is the spring back animation. */
557     private boolean mIsSpringBackAnimation;
558     private float mHintDistance;
559     private float mInitialOffsetOnTouch;
560     private boolean mCollapsedAndHeadsUpOnDown;
561     private float mExpandedFraction = 0;
562     private float mExpansionDragDownAmountPx = 0;
563     private boolean mPanelClosedOnDown;
564     private boolean mHasLayoutedSinceDown;
565     private float mUpdateFlingVelocity;
566     private boolean mUpdateFlingOnLayout;
567     private boolean mClosing;
568     private boolean mTouchSlopExceeded;
569     private int mTrackingPointer;
570     private int mTouchSlop;
571     private float mSlopMultiplier;
572     private boolean mTouchAboveFalsingThreshold;
573     private boolean mTouchStartedInEmptyArea;
574     private boolean mMotionAborted;
575     private boolean mUpwardsWhenThresholdReached;
576     private boolean mAnimatingOnDown;
577     private boolean mHandlingPointerUp;
578     private ValueAnimator mHeightAnimator;
579     /** Whether an instant expand request is currently pending and we are waiting for layout. */
580     private boolean mInstantExpanding;
581     private boolean mAnimateAfterExpanding;
582     private boolean mIsFlinging;
583     private String mViewName;
584     private float mInitialExpandY;
585     private float mInitialExpandX;
586     private boolean mTouchDisabled;
587     private boolean mInitialTouchFromKeyguard;
588     /** Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time. */
589     private float mNextCollapseSpeedUpFactor = 1.0f;
590     private boolean mGestureWaitForTouchSlop;
591     private boolean mIgnoreXTouchSlop;
592     private boolean mExpandLatencyTracking;
593     /**
594      * Whether we're waking up and will play the delayed doze animation in
595      * {@link NotificationWakeUpCoordinator}. If so, we'll want to keep the clock centered until the
596      * delayed doze animation starts.
597      */
598     private boolean mWillPlayDelayedDozeAmountAnimation = false;
599     private final DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
600     private final OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel;
601     private final LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel;
602     private final GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel;
603     private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
604 
605     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
606     private final KeyguardInteractor mKeyguardInteractor;
607     private final CoroutineDispatcher mMainDispatcher;
608     private boolean mIsOcclusionTransitionRunning = false;
609     private int mDreamingToLockscreenTransitionTranslationY;
610     private int mOccludedToLockscreenTransitionTranslationY;
611     private int mLockscreenToDreamingTransitionTranslationY;
612     private int mGoneToDreamingTransitionTranslationY;
613     private int mLockscreenToOccludedTransitionTranslationY;
614 
615     private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
616             mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
617     private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable =
618             () -> mKeyguardBottomArea.setVisibility(View.GONE);
619     private final Runnable mHeadsUpExistenceChangedRunnable = () -> {
620         setHeadsUpAnimatingAway(false);
621         updatePanelExpansionAndVisibility();
622     };
623     private final Runnable mMaybeHideExpandedRunnable = () -> {
624         if (getExpandedFraction() == 0.0f) {
625             postToView(mHideExpandedRunnable);
626         }
627     };
628 
629     private final Consumer<TransitionStep> mDreamingToLockscreenTransition =
630             (TransitionStep step) -> {
631                 mIsOcclusionTransitionRunning =
632                     step.getTransitionState() == TransitionState.RUNNING;
633             };
634 
635     private final Consumer<TransitionStep> mOccludedToLockscreenTransition =
636             (TransitionStep step) -> {
637                 mIsOcclusionTransitionRunning =
638                     step.getTransitionState() == TransitionState.RUNNING;
639             };
640 
641     private final Consumer<TransitionStep> mLockscreenToDreamingTransition =
642             (TransitionStep step) -> {
643                 mIsOcclusionTransitionRunning =
644                     step.getTransitionState() == TransitionState.RUNNING;
645             };
646 
647     private final Consumer<TransitionStep> mGoneToDreamingTransition =
648             (TransitionStep step) -> {
649                 mIsOcclusionTransitionRunning =
650                     step.getTransitionState() == TransitionState.RUNNING;
651             };
652 
653     private final Consumer<TransitionStep> mLockscreenToOccludedTransition =
654             (TransitionStep step) -> {
655                 mIsOcclusionTransitionRunning =
656                     step.getTransitionState() == TransitionState.RUNNING;
657             };
658 
659     private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
660             new TransitionListenerAdapter() {
661                 @Override
662                 public void onTransitionCancel(Transition transition) {
663                     mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
664                 }
665 
666                 @Override
667                 public void onTransitionEnd(Transition transition) {
668                     mInteractionJankMonitor.end(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
669                 }
670             };
671 
672     @Inject
NotificationPanelViewController(NotificationPanelView view, @Main Handler handler, LayoutInflater layoutInflater, FeatureFlags featureFlags, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, StatusBarWindowStateController statusBarWindowStateController, NotificationShadeWindowController notificationShadeWindowController, DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ShadeLogger shadeLogger, ShadeHeightLogger shadeHeightLogger, ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationGutsManager gutsManager, NotificationsQSContainerController notificationsQSContainerController, NotificationStackScrollLayoutController notificationStackScrollLayoutController, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, AuthController authController, ScrimController scrimController, UserManager userManager, MediaDataManager mediaDataManager, NotificationShadeDepthController notificationShadeDepthController, AmbientState ambientState, LockIconViewController lockIconViewController, KeyguardMediaController keyguardMediaController, TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, NavigationBarController navigationBarController, QuickSettingsController quickSettingsController, FragmentService fragmentService, ContentResolver contentResolver, ShadeHeaderController shadeHeaderController, ScreenOffAnimationController screenOffAnimationController, LockscreenGestureLogger lockscreenGestureLogger, ShadeExpansionStateManager shadeExpansionStateManager, Optional<SysUIUnfoldComponent> unfoldComponent, SysUiState sysUiState, Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider, KeyguardUnlockAnimationController keyguardUnlockAnimationController, KeyguardIndicationController keyguardIndicationController, NotificationListContainer notificationListContainer, NotificationStackSizeCalculator notificationStackSizeCalculator, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, ShadeTransitionController shadeTransitionController, InteractionJankMonitor interactionJankMonitor, SystemClock systemClock, KeyguardBottomAreaViewModel keyguardBottomAreaViewModel, KeyguardBottomAreaInteractor keyguardBottomAreaInteractor, AlternateBouncerInteractor alternateBouncerInteractor, DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel, OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel, LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel, GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel, LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel, @Main CoroutineDispatcher mainDispatcher, KeyguardTransitionInteractor keyguardTransitionInteractor, DumpManager dumpManager, KeyguardLongPressViewModel keyguardLongPressViewModel, KeyguardInteractor keyguardInteractor)673     public NotificationPanelViewController(NotificationPanelView view,
674             @Main Handler handler,
675             LayoutInflater layoutInflater,
676             FeatureFlags featureFlags,
677             NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
678             DynamicPrivacyController dynamicPrivacyController,
679             KeyguardBypassController bypassController, FalsingManager falsingManager,
680             FalsingCollector falsingCollector,
681             KeyguardStateController keyguardStateController,
682             StatusBarStateController statusBarStateController,
683             StatusBarWindowStateController statusBarWindowStateController,
684             NotificationShadeWindowController notificationShadeWindowController,
685             DozeLog dozeLog,
686             DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
687             LatencyTracker latencyTracker, PowerManager powerManager,
688             AccessibilityManager accessibilityManager, @DisplayId int displayId,
689             KeyguardUpdateMonitor keyguardUpdateMonitor,
690             MetricsLogger metricsLogger,
691             ShadeLogger shadeLogger,
692             ShadeHeightLogger shadeHeightLogger,
693             ConfigurationController configurationController,
694             Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
695             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
696             ConversationNotificationManager conversationNotificationManager,
697             MediaHierarchyManager mediaHierarchyManager,
698             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
699             NotificationGutsManager gutsManager,
700             NotificationsQSContainerController notificationsQSContainerController,
701             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
702             KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
703             KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
704             KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
705             KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
706             LockscreenShadeTransitionController lockscreenShadeTransitionController,
707             AuthController authController,
708             ScrimController scrimController,
709             UserManager userManager,
710             MediaDataManager mediaDataManager,
711             NotificationShadeDepthController notificationShadeDepthController,
712             AmbientState ambientState,
713             LockIconViewController lockIconViewController,
714             KeyguardMediaController keyguardMediaController,
715             TapAgainViewController tapAgainViewController,
716             NavigationModeController navigationModeController,
717             NavigationBarController navigationBarController,
718             QuickSettingsController quickSettingsController,
719             FragmentService fragmentService,
720             ContentResolver contentResolver,
721             ShadeHeaderController shadeHeaderController,
722             ScreenOffAnimationController screenOffAnimationController,
723             LockscreenGestureLogger lockscreenGestureLogger,
724             ShadeExpansionStateManager shadeExpansionStateManager,
725             Optional<SysUIUnfoldComponent> unfoldComponent,
726             SysUiState sysUiState,
727             Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
728             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
729             KeyguardIndicationController keyguardIndicationController,
730             NotificationListContainer notificationListContainer,
731             NotificationStackSizeCalculator notificationStackSizeCalculator,
732             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
733             ShadeTransitionController shadeTransitionController,
734             InteractionJankMonitor interactionJankMonitor,
735             SystemClock systemClock,
736             KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
737             KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
738             AlternateBouncerInteractor alternateBouncerInteractor,
739             DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
740             OccludedToLockscreenTransitionViewModel occludedToLockscreenTransitionViewModel,
741             LockscreenToDreamingTransitionViewModel lockscreenToDreamingTransitionViewModel,
742             GoneToDreamingTransitionViewModel goneToDreamingTransitionViewModel,
743             LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel,
744             @Main CoroutineDispatcher mainDispatcher,
745             KeyguardTransitionInteractor keyguardTransitionInteractor,
746             DumpManager dumpManager,
747             KeyguardLongPressViewModel keyguardLongPressViewModel,
748             KeyguardInteractor keyguardInteractor) {
749         mInteractionJankMonitor = interactionJankMonitor;
750         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
751             @Override
752             public void onKeyguardFadingAwayChanged() {
753                 updateExpandedHeightToMaxHeight();
754             }
755         });
756         mAmbientState = ambientState;
757         mView = view;
758         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
759         mLockscreenGestureLogger = lockscreenGestureLogger;
760         mShadeExpansionStateManager = shadeExpansionStateManager;
761         mShadeLog = shadeLogger;
762         mShadeHeightLogger = shadeHeightLogger;
763         mGutsManager = gutsManager;
764         mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
765         mOccludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel;
766         mLockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel;
767         mGoneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel;
768         mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
769         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
770         mKeyguardInteractor = keyguardInteractor;
771         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
772             @Override
773             public void onViewAttachedToWindow(View v) {
774                 mViewName = mResources.getResourceName(mView.getId());
775             }
776 
777             @Override
778             public void onViewDetachedFromWindow(View v) {}
779         });
780 
781         mView.addOnLayoutChangeListener(new ShadeLayoutChangeListener());
782         mView.setOnTouchListener(getTouchHandler());
783         mView.setOnConfigurationChangedListener(config -> loadDimens());
784 
785         mResources = mView.getResources();
786         mKeyguardStateController = keyguardStateController;
787         mQsController = quickSettingsController;
788         mKeyguardIndicationController = keyguardIndicationController;
789         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
790         mNotificationShadeWindowController = notificationShadeWindowController;
791         FlingAnimationUtils.Builder fauBuilder = flingAnimationUtilsBuilder.get();
792         mFlingAnimationUtils = fauBuilder
793                 .reset()
794                 .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS)
795                 .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
796                 .build();
797         mFlingAnimationUtilsClosing = fauBuilder
798                 .reset()
799                 .setMaxLengthSeconds(FLING_CLOSING_MAX_LENGTH_SECONDS)
800                 .setSpeedUpFactor(FLING_CLOSING_SPEED_UP_FACTOR)
801                 .build();
802         mFlingAnimationUtilsDismissing = fauBuilder
803                 .reset()
804                 .setMaxLengthSeconds(0.5f)
805                 .setSpeedUpFactor(0.6f)
806                 .setX2(0.6f)
807                 .setY2(0.84f)
808                 .build();
809         mLatencyTracker = latencyTracker;
810         mBounceInterpolator = new BounceInterpolator();
811         mFalsingManager = falsingManager;
812         mDozeLog = dozeLog;
813         mNotificationsDragEnabled = mResources.getBoolean(
814                 R.bool.config_enableNotificationShadeDrag);
815         mVibratorHelper = vibratorHelper;
816         mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
817         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
818         mSystemClock = systemClock;
819         mKeyguardMediaController = keyguardMediaController;
820         mMetricsLogger = metricsLogger;
821         mConfigurationController = configurationController;
822         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
823         mMediaHierarchyManager = mediaHierarchyManager;
824         mNotificationsQSContainerController = notificationsQSContainerController;
825         mNotificationListContainer = notificationListContainer;
826         mNotificationStackSizeCalculator = notificationStackSizeCalculator;
827         mNavigationBarController = navigationBarController;
828         mKeyguardBottomAreaViewControllerProvider = keyguardBottomAreaViewControllerProvider;
829         mNotificationsQSContainerController.init();
830         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
831         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
832         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
833         mDepthController = notificationShadeDepthController;
834         mContentResolver = contentResolver;
835         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
836         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
837         mFragmentService = fragmentService;
838         mSettingsChangeObserver = new SettingsChangeObserver(handler);
839         mSplitShadeEnabled =
840                 LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
841         mView.setWillNotDraw(!DEBUG_DRAWABLE);
842         mShadeHeaderController = shadeHeaderController;
843         mLayoutInflater = layoutInflater;
844         mFeatureFlags = featureFlags;
845         mFalsingCollector = falsingCollector;
846         mPowerManager = powerManager;
847         mWakeUpCoordinator = coordinator;
848         mMainDispatcher = mainDispatcher;
849         mAccessibilityManager = accessibilityManager;
850         mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
851         setPanelAlpha(255, false /* animate */);
852         mCommandQueue = commandQueue;
853         mDisplayId = displayId;
854         mPulseExpansionHandler = pulseExpansionHandler;
855         mDozeParameters = dozeParameters;
856         mScrimController = scrimController;
857         mUserManager = userManager;
858         mMediaDataManager = mediaDataManager;
859         mTapAgainViewController = tapAgainViewController;
860         mSysUiState = sysUiState;
861         statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
862         mKeyguardBypassController = bypassController;
863         mUpdateMonitor = keyguardUpdateMonitor;
864         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
865         lockscreenShadeTransitionController.setNotificationPanelController(this);
866         shadeTransitionController.setNotificationPanelViewController(this);
867         dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
868         quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
869         quickSettingsController.setQsStateUpdateListener(this::onQsStateUpdated);
870         quickSettingsController.setApplyClippingImmediatelyListener(
871                 this::onQsClippingImmediatelyApplied);
872         quickSettingsController.setFlingQsWithoutClickListener(this::onFlingQsWithoutClick);
873         quickSettingsController.setExpansionHeightSetToMaxListener(this::onExpansionHeightSetToMax);
874         shadeExpansionStateManager.addStateListener(this::onPanelStateChanged);
875 
876         mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
877         mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
878             mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
879             updateKeyguardBottomAreaAlpha();
880         });
881         mBottomAreaShadeAlphaAnimator.setDuration(160);
882         mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
883         mConversationNotificationManager = conversationNotificationManager;
884         mAuthController = authController;
885         mLockIconViewController = lockIconViewController;
886         mScreenOffAnimationController = screenOffAnimationController;
887         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
888         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
889 
890         int currentMode = navigationModeController.addListener(
891                 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
892         mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode);
893 
894         mView.setBackgroundColor(Color.TRANSPARENT);
895         ShadeAttachStateChangeListener
896                 onAttachStateChangeListener = new ShadeAttachStateChangeListener();
897         mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
898         if (mView.isAttachedToWindow()) {
899             onAttachStateChangeListener.onViewAttachedToWindow(mView);
900         }
901 
902         mView.setOnApplyWindowInsetsListener((v, insets) -> onApplyShadeWindowInsets(insets));
903 
904         if (DEBUG_DRAWABLE) {
905             mView.getOverlay().add(new DebugDrawable(this, mView,
906                     mNotificationStackScrollLayoutController, mLockIconViewController,
907                     mQsController));
908         }
909 
910         mKeyguardUnfoldTransition = unfoldComponent.map(
911                 SysUIUnfoldComponent::getKeyguardUnfoldTransition);
912         mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
913                 SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
914 
915         updateUserSwitcherFlags();
916         mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
917         mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
918         KeyguardLongPressViewBinder.bind(
919                 mView.requireViewById(R.id.keyguard_long_press),
920                 keyguardLongPressViewModel,
921                 () -> {
922                     onEmptySpaceClick();
923                     return Unit.INSTANCE;
924                 },
925                 mFalsingManager);
926         onFinishInflate();
927         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
928                 new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
929                     @Override
930                     public void onUnlockAnimationFinished() {
931                         unlockAnimationFinished();
932                     }
933 
934                     @Override
935                     public void onUnlockAnimationStarted(
936                             boolean playingCannedAnimation,
937                             boolean isWakeAndUnlock,
938                             long startDelay,
939                             long unlockAnimationDuration) {
940                         unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlock, startDelay);
941                     }
942                 });
943         mAlternateBouncerInteractor = alternateBouncerInteractor;
944         dumpManager.registerDumpable(this);
945     }
946 
unlockAnimationFinished()947     private void unlockAnimationFinished() {
948         // Make sure the clock is in the correct position after the unlock animation
949         // so that it's not in the wrong place when we show the keyguard again.
950         positionClockAndNotifications(true /* forceClockUpdate */);
951     }
952 
unlockAnimationStarted( boolean playingCannedAnimation, boolean isWakeAndUnlock, long unlockAnimationStartDelay)953     private void unlockAnimationStarted(
954             boolean playingCannedAnimation,
955             boolean isWakeAndUnlock,
956             long unlockAnimationStartDelay) {
957         // Disable blurs while we're unlocking so that panel expansion does not
958         // cause blurring. This will eventually be re-enabled by the panel view on
959         // ACTION_UP, since the user's finger might still be down after a swipe to
960         // unlock gesture, and we don't want that to cause blurring either.
961         mDepthController.setBlursDisabledForUnlock(mTracking);
962 
963         if (playingCannedAnimation && !isWakeAndUnlock) {
964             // Hide the panel so it's not in the way or the surface behind the
965             // keyguard, which will be appearing. If we're wake and unlocking, the
966             // lock screen is hidden instantly so should not be flung away.
967             if (isTracking() || mIsFlinging) {
968                 // Instant collapse the notification panel since the notification
969                 // panel is already in the middle animating
970                 onTrackingStopped(false);
971                 instantCollapse();
972             } else {
973                 mView.animate().cancel();
974                 mView.animate()
975                         .alpha(0f)
976                         .setStartDelay(0)
977                         // Translate up by 4%.
978                         .translationY(mView.getHeight() * -0.04f)
979                         // This start delay is to give us time to animate out before
980                         // the launcher icons animation starts, so use that as our
981                         // duration.
982                         .setDuration(unlockAnimationStartDelay)
983                         .setInterpolator(EMPHASIZED_ACCELERATE)
984                         .withEndAction(() -> {
985                             instantCollapse();
986                             mView.setAlpha(1f);
987                             mView.setTranslationY(0f);
988                         })
989                         .start();
990             }
991         }
992     }
993 
994     @VisibleForTesting
onFinishInflate()995     void onFinishInflate() {
996         loadDimens();
997         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
998 
999         FrameLayout userAvatarContainer = null;
1000         KeyguardUserSwitcherView keyguardUserSwitcherView = null;
1001 
1002         if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled(
1003                 mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user))) {
1004             if (mKeyguardQsUserSwitchEnabled) {
1005                 ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
1006                 userAvatarContainer = (FrameLayout) stub.inflate();
1007             } else {
1008                 ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
1009                 keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
1010             }
1011         }
1012 
1013         mKeyguardStatusBarViewController =
1014                 mKeyguardStatusBarViewComponentFactory.build(
1015                                 mKeyguardStatusBar,
1016                                 mNotificationPanelViewStateProvider)
1017                         .getKeyguardStatusBarViewController();
1018         mKeyguardStatusBarViewController.init();
1019 
1020         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
1021         updateViewControllers(
1022                 mView.findViewById(R.id.keyguard_status_view),
1023                 userAvatarContainer,
1024                 keyguardUserSwitcherView);
1025 
1026         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
1027                 R.id.notification_stack_scroller);
1028         mNotificationStackScrollLayoutController.attach(stackScrollLayout);
1029         mNotificationStackScrollLayoutController.setOnHeightChangedListener(
1030                 new NsslHeightChangedListener());
1031         mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
1032                 mOnEmptySpaceClickListener);
1033         mQsController.initNotificationStackScrollLayoutController();
1034         mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
1035         addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
1036         setKeyguardBottomArea(mView.findViewById(R.id.keyguard_bottom_area));
1037 
1038         initBottomArea();
1039 
1040         mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
1041         mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController);
1042         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
1043             @Override
1044             public void onFullyHiddenChanged(boolean isFullyHidden) {
1045                 mKeyguardStatusBarViewController.updateForHeadsUp();
1046             }
1047 
1048             @Override
1049             public void onPulseExpansionChanged(boolean expandingChanged) {
1050                 if (mKeyguardBypassController.getBypassEnabled()) {
1051                     // Position the notifications while dragging down while pulsing
1052                     requestScrollerTopPaddingUpdate(false /* animate */);
1053                 }
1054             }
1055 
1056             @Override
1057             public void onDelayedDozeAmountAnimationRunning(boolean running) {
1058                 // On running OR finished, the animation is no longer waiting to play
1059                 setWillPlayDelayedDozeAmountAnimation(false);
1060             }
1061         });
1062 
1063         mView.setRtlChangeListener(layoutDirection -> {
1064             if (layoutDirection != mOldLayoutDirection) {
1065                 mOldLayoutDirection = layoutDirection;
1066             }
1067         });
1068 
1069         mView.setAccessibilityDelegate(mAccessibilityDelegate);
1070         if (mSplitShadeEnabled) {
1071             updateResources();
1072         }
1073 
1074         mTapAgainViewController.init();
1075         mShadeHeaderController.init();
1076         mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
1077         mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
1078                 controller.setup(mNotificationContainerParent));
1079 
1080         // Dreaming->Lockscreen
1081         collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(),
1082                 mDreamingToLockscreenTransition, mMainDispatcher);
1083         collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
1084                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
1085         collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(
1086                 mDreamingToLockscreenTransitionTranslationY),
1087                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
1088 
1089         // Occluded->Lockscreen
1090         collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
1091                 mOccludedToLockscreenTransition, mMainDispatcher);
1092         collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
1093                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
1094         collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(
1095                 mOccludedToLockscreenTransitionTranslationY),
1096                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
1097 
1098         // Lockscreen->Dreaming
1099         collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(),
1100                 mLockscreenToDreamingTransition, mMainDispatcher);
1101         collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
1102                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
1103         collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(
1104                 mLockscreenToDreamingTransitionTranslationY),
1105                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
1106 
1107         // Gone->Dreaming
1108         collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
1109                 mGoneToDreamingTransition, mMainDispatcher);
1110         collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
1111                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
1112         collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY(
1113                 mGoneToDreamingTransitionTranslationY),
1114                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
1115 
1116         // Lockscreen->Occluded
1117         collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
1118                 mLockscreenToOccludedTransition, mMainDispatcher);
1119         collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
1120                 setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
1121         collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(
1122                 mLockscreenToOccludedTransitionTranslationY),
1123                 setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
1124     }
1125 
1126     @VisibleForTesting
loadDimens()1127     void loadDimens() {
1128         final ViewConfiguration configuration = ViewConfiguration.get(this.mView.getContext());
1129         mTouchSlop = configuration.getScaledTouchSlop();
1130         mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
1131         mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
1132         mPanelFlingOvershootAmount = mResources.getDimension(R.dimen.panel_overshoot_amount);
1133         mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get()
1134                 .setMaxLengthSeconds(0.4f).build();
1135         mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
1136         mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext());
1137         mClockPositionAlgorithm.loadDimens(mResources);
1138         mIndicationBottomPadding = mResources.getDimensionPixelSize(
1139                 R.dimen.keyguard_indication_bottom_padding);
1140         int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
1141         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
1142                 R.dimen.heads_up_status_bar_padding);
1143         mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize(
1144                 R.dimen.pulse_expansion_max_top_overshoot);
1145         mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
1146         mSplitShadeScrimTransitionDistance = mResources.getDimensionPixelSize(
1147                 R.dimen.split_shade_scrim_transition_distance);
1148         mDreamingToLockscreenTransitionTranslationY = mResources.getDimensionPixelSize(
1149                 R.dimen.dreaming_to_lockscreen_transition_lockscreen_translation_y);
1150         mOccludedToLockscreenTransitionTranslationY = mResources.getDimensionPixelSize(
1151                 R.dimen.occluded_to_lockscreen_transition_lockscreen_translation_y);
1152         mLockscreenToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
1153                 R.dimen.lockscreen_to_dreaming_transition_lockscreen_translation_y);
1154         mGoneToDreamingTransitionTranslationY = mResources.getDimensionPixelSize(
1155                 R.dimen.gone_to_dreaming_transition_lockscreen_translation_y);
1156         mLockscreenToOccludedTransitionTranslationY = mResources.getDimensionPixelSize(
1157                 R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y);
1158         // TODO (b/265193930): remove this and make QsController listen to NotificationPanelViews
1159         mQsController.loadDimens();
1160     }
1161 
updateViewControllers(KeyguardStatusView keyguardStatusView, FrameLayout userAvatarView, KeyguardUserSwitcherView keyguardUserSwitcherView)1162     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
1163             FrameLayout userAvatarView,
1164             KeyguardUserSwitcherView keyguardUserSwitcherView) {
1165         // Re-associate the KeyguardStatusViewController
1166         KeyguardStatusViewComponent statusViewComponent =
1167                 mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
1168         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
1169         mKeyguardStatusViewController.init();
1170         updateClockAppearance();
1171 
1172         if (mKeyguardUserSwitcherController != null) {
1173             // Try to close the switcher so that callbacks are triggered if necessary.
1174             // Otherwise, NPV can get into a state where some of the views are still hidden
1175             mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
1176         }
1177 
1178         mKeyguardQsUserSwitchController = null;
1179         mKeyguardUserSwitcherController = null;
1180 
1181         // Re-associate the KeyguardUserSwitcherController
1182         if (userAvatarView != null) {
1183             KeyguardQsUserSwitchComponent userSwitcherComponent =
1184                     mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
1185             mKeyguardQsUserSwitchController =
1186                     userSwitcherComponent.getKeyguardQsUserSwitchController();
1187             mKeyguardQsUserSwitchController.init();
1188             mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
1189         } else if (keyguardUserSwitcherView != null) {
1190             KeyguardUserSwitcherComponent userSwitcherComponent =
1191                     mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
1192             mKeyguardUserSwitcherController =
1193                     userSwitcherComponent.getKeyguardUserSwitcherController();
1194             mKeyguardUserSwitcherController.init();
1195             mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
1196         } else {
1197             mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false);
1198         }
1199     }
1200 
updateResources()1201     public void updateResources() {
1202         final boolean newSplitShadeEnabled =
1203                 LargeScreenUtils.shouldUseSplitNotificationShade(mResources);
1204         final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
1205         mSplitShadeEnabled = newSplitShadeEnabled;
1206         mQsController.updateResources();
1207         mNotificationsQSContainerController.updateResources();
1208         updateKeyguardStatusViewAlignment(/* animate= */false);
1209         mKeyguardMediaController.refreshMediaPosition();
1210 
1211         if (splitShadeChanged) {
1212             onSplitShadeEnabledChanged();
1213         }
1214 
1215         mSplitShadeFullTransitionDistance =
1216                 mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
1217     }
1218 
onSplitShadeEnabledChanged()1219     private void onSplitShadeEnabledChanged() {
1220         mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
1221         // Reset any left over overscroll state. It is a rare corner case but can happen.
1222         mQsController.setOverScrollAmount(0);
1223         mScrimController.setNotificationsOverScrollAmount(0);
1224         mNotificationStackScrollLayoutController.setOverExpansion(0);
1225         mNotificationStackScrollLayoutController.setOverScrollAmount(0);
1226 
1227         // when we switch between split shade and regular shade we want to enforce setting qs to
1228         // the default state: expanded for split shade and collapsed otherwise
1229         if (!isOnKeyguard() && mPanelExpanded) {
1230             mQsController.setExpanded(mSplitShadeEnabled);
1231         }
1232         if (isOnKeyguard() && mQsController.getExpanded() && mSplitShadeEnabled) {
1233             // In single column keyguard - when you swipe from the top - QS is fully expanded and
1234             // StatusBarState is KEYGUARD. That state doesn't make sense for split shade,
1235             // where notifications are always visible and we effectively go to fully expanded
1236             // shade, that is SHADE_LOCKED.
1237             // Also we might just be switching from regular expanded shade, so we don't want
1238             // to force state transition if it's already correct.
1239             mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED, /* force= */false);
1240         }
1241         updateClockAppearance();
1242         mQsController.updateQsState();
1243         mNotificationStackScrollLayoutController.updateFooter();
1244     }
1245 
reInflateStub(int viewId, int stubId, int layoutId, boolean enabled)1246     private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
1247         View view = mView.findViewById(viewId);
1248         if (view != null) {
1249             int index = mView.indexOfChild(view);
1250             mView.removeView(view);
1251             if (enabled) {
1252                 view = mLayoutInflater.inflate(layoutId, mView, false);
1253                 mView.addView(view, index);
1254             } else {
1255                 // Add the stub back so we can re-inflate it again if necessary
1256                 ViewStub stub = new ViewStub(mView.getContext(), layoutId);
1257                 stub.setId(stubId);
1258                 mView.addView(stub, index);
1259                 view = null;
1260             }
1261         } else if (enabled) {
1262             // It's possible the stub was never inflated if the configuration changed
1263             ViewStub stub = mView.findViewById(stubId);
1264             view = stub.inflate();
1265         }
1266         return view;
1267     }
1268 
1269     @VisibleForTesting
reInflateViews()1270     void reInflateViews() {
1271         debugLog("reInflateViews");
1272         // Re-inflate the status view group.
1273         KeyguardStatusView keyguardStatusView =
1274                 mNotificationContainerParent.findViewById(R.id.keyguard_status_view);
1275         int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView);
1276         mNotificationContainerParent.removeView(keyguardStatusView);
1277         keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate(
1278                 R.layout.keyguard_status_view, mNotificationContainerParent, false);
1279         mNotificationContainerParent.addView(keyguardStatusView, statusIndex);
1280         // When it's reinflated, this is centered by default. If it shouldn't be, this will update
1281         // below when resources are updated.
1282         mStatusViewCentered = true;
1283         attachSplitShadeMediaPlayerContainer(
1284                 keyguardStatusView.findViewById(R.id.status_view_media_container));
1285 
1286         // we need to update KeyguardStatusView constraints after reinflating it
1287         updateResources();
1288 
1289         // Re-inflate the keyguard user switcher group.
1290         updateUserSwitcherFlags();
1291         boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled(
1292                 mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user));
1293         boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
1294         boolean showKeyguardUserSwitcher =
1295                 !mKeyguardQsUserSwitchEnabled
1296                         && mKeyguardUserSwitcherEnabled
1297                         && isUserSwitcherEnabled;
1298         FrameLayout userAvatarView = (FrameLayout) reInflateStub(
1299                 R.id.keyguard_qs_user_switch_view /* viewId */,
1300                 R.id.keyguard_qs_user_switch_stub /* stubId */,
1301                 R.layout.keyguard_qs_user_switch /* layoutId */,
1302                 showQsUserSwitch /* enabled */);
1303         KeyguardUserSwitcherView keyguardUserSwitcherView =
1304                 (KeyguardUserSwitcherView) reInflateStub(
1305                         R.id.keyguard_user_switcher_view /* viewId */,
1306                         R.id.keyguard_user_switcher_stub /* stubId */,
1307                         R.layout.keyguard_user_switcher /* layoutId */,
1308                         showKeyguardUserSwitcher /* enabled */);
1309 
1310         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
1311                 keyguardUserSwitcherView);
1312 
1313         // Update keyguard bottom area
1314         int index = mView.indexOfChild(mKeyguardBottomArea);
1315         mView.removeView(mKeyguardBottomArea);
1316         KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
1317         setKeyguardBottomArea(mKeyguardBottomAreaViewControllerProvider.get().getView());
1318         mKeyguardBottomArea.initFrom(oldBottomArea);
1319         mView.addView(mKeyguardBottomArea, index);
1320         initBottomArea();
1321         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
1322         mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
1323                 mStatusBarStateController.getInterpolatedDozeAmount());
1324 
1325         mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
1326                 mBarState,
1327                 false,
1328                 false,
1329                 mBarState);
1330         if (mKeyguardQsUserSwitchController != null) {
1331             mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
1332                     mBarState,
1333                     false,
1334                     false,
1335                     mBarState);
1336         }
1337         if (mKeyguardUserSwitcherController != null) {
1338             mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
1339                     mBarState,
1340                     false,
1341                     false,
1342                     mBarState);
1343         }
1344         setKeyguardBottomAreaVisibility(mBarState, false);
1345 
1346         mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
1347         mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
1348     }
1349 
attachSplitShadeMediaPlayerContainer(FrameLayout container)1350     private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
1351         mKeyguardMediaController.attachSplitShadeContainer(container);
1352     }
1353 
initBottomArea()1354     private void initBottomArea() {
1355         mKeyguardBottomArea.init(
1356                 mKeyguardBottomAreaViewModel,
1357                 mFalsingManager,
1358                 mLockIconViewController,
1359                 stringResourceId ->
1360                         mKeyguardIndicationController.showTransientIndication(stringResourceId),
1361                 mVibratorHelper);
1362     }
1363 
1364     @VisibleForTesting
setMaxDisplayedNotifications(int maxAllowed)1365     void setMaxDisplayedNotifications(int maxAllowed) {
1366         mMaxAllowedKeyguardNotifications = maxAllowed;
1367     }
1368 
1369     @VisibleForTesting
isFlinging()1370     boolean isFlinging() {
1371         return mIsFlinging;
1372     }
1373 
updateMaxDisplayedNotifications(boolean recompute)1374     private void updateMaxDisplayedNotifications(boolean recompute) {
1375         if (recompute) {
1376             setMaxDisplayedNotifications(Math.max(computeMaxKeyguardNotifications(), 1));
1377         } else {
1378             if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
1379         }
1380 
1381         if (getKeyguardShowing() && !mKeyguardBypassController.getBypassEnabled()) {
1382             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
1383                     mMaxAllowedKeyguardNotifications);
1384             mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(
1385                     mKeyguardNotificationBottomPadding);
1386         } else {
1387             // no max when not on the keyguard
1388             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
1389             mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(-1f);
1390         }
1391     }
1392 
shouldAvoidChangingNotificationsCount()1393     private boolean shouldAvoidChangingNotificationsCount() {
1394         return mHintAnimationRunning || mUnlockedScreenOffAnimationController.isAnimationPlaying();
1395     }
1396 
setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea)1397     private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) {
1398         mKeyguardBottomArea = keyguardBottomArea;
1399         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
1400     }
1401 
setOpenCloseListener(OpenCloseListener openCloseListener)1402     void setOpenCloseListener(OpenCloseListener openCloseListener) {
1403         mOpenCloseListener = openCloseListener;
1404     }
1405 
setTrackingStartedListener(TrackingStartedListener trackingStartedListener)1406     void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
1407         mTrackingStartedListener = trackingStartedListener;
1408     }
1409 
updateGestureExclusionRect()1410     private void updateGestureExclusionRect() {
1411         Rect exclusionRect = calculateGestureExclusionRect();
1412         mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList()
1413                 : Collections.singletonList(exclusionRect));
1414     }
1415 
calculateGestureExclusionRect()1416     private Rect calculateGestureExclusionRect() {
1417         Rect exclusionRect = null;
1418         Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion();
1419         if (isFullyCollapsed() && touchableRegion != null) {
1420             // Note: The manager also calculates the non-pinned touchable region
1421             exclusionRect = touchableRegion.getBounds();
1422         }
1423         return exclusionRect != null ? exclusionRect : EMPTY_RECT;
1424     }
1425 
setIsFullWidth(boolean isFullWidth)1426     private void setIsFullWidth(boolean isFullWidth) {
1427         mIsFullWidth = isFullWidth;
1428         mScrimController.setClipsQsScrim(isFullWidth);
1429         mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth);
1430         mQsController.setNotificationPanelFullWidth(isFullWidth);
1431     }
1432 
1433     /**
1434      * Positions the clock and notifications dynamically depending on how many notifications are
1435      * showing.
1436      */
positionClockAndNotifications()1437     void positionClockAndNotifications() {
1438         positionClockAndNotifications(false /* forceUpdate */);
1439     }
1440 
1441     /**
1442      * Positions the clock and notifications dynamically depending on how many notifications are
1443      * showing.
1444      *
1445      * @param forceClockUpdate Should the clock be updated even when not on keyguard
1446      */
positionClockAndNotifications(boolean forceClockUpdate)1447     private void positionClockAndNotifications(boolean forceClockUpdate) {
1448         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
1449         int stackScrollerPadding;
1450         boolean onKeyguard = isOnKeyguard();
1451 
1452         if (onKeyguard || forceClockUpdate) {
1453             updateClockAppearance();
1454         }
1455         if (!onKeyguard) {
1456             if (mSplitShadeEnabled) {
1457                 // Quick settings are not on the top of the notifications
1458                 // when in split shade mode (they are on the left side),
1459                 // so we should not add a padding for them
1460                 stackScrollerPadding = 0;
1461             } else {
1462                 stackScrollerPadding = mQsController.getUnlockedStackScrollerPadding();
1463             }
1464         } else {
1465             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
1466         }
1467 
1468         mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
1469 
1470         mStackScrollerMeasuringPass++;
1471         requestScrollerTopPaddingUpdate(animate);
1472         mStackScrollerMeasuringPass = 0;
1473         mAnimateNextPositionUpdate = false;
1474     }
1475 
updateClockAppearance()1476     private void updateClockAppearance() {
1477         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
1478         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
1479         boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
1480         mKeyguardStatusViewController.displayClock(computeDesiredClockSize(),
1481                 shouldAnimateClockChange);
1482         updateKeyguardStatusViewAlignment(/* animate= */true);
1483         int userSwitcherHeight = mKeyguardQsUserSwitchController != null
1484                 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
1485         if (mKeyguardUserSwitcherController != null) {
1486             userSwitcherHeight = mKeyguardUserSwitcherController.getHeight();
1487         }
1488         float expandedFraction =
1489                 mScreenOffAnimationController.shouldExpandNotifications()
1490                         ? 1.0f : getExpandedFraction();
1491         float darkAmount =
1492                 mScreenOffAnimationController.shouldExpandNotifications()
1493                         ? 1.0f : mInterpolatedDarkAmount;
1494 
1495         float udfpsAodTopLocation = -1f;
1496         if (mUpdateMonitor.isUdfpsEnrolled() && mAuthController.getUdfpsProps().size() > 0) {
1497             FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
1498             final SensorLocationInternal location = props.getLocation();
1499             udfpsAodTopLocation = location.sensorLocationY - location.sensorRadius
1500                     - mUdfpsMaxYBurnInOffset;
1501         }
1502 
1503         mClockPositionAlgorithm.setup(
1504                 mStatusBarHeaderHeightKeyguard,
1505                 expandedFraction,
1506                 mKeyguardStatusViewController.getLockscreenHeight(),
1507                 userSwitcherHeight,
1508                 userSwitcherPreferredY,
1509                 darkAmount, mOverStretchAmount,
1510                 bypassEnabled,
1511                 mQsController.getUnlockedStackScrollerPadding(),
1512                 mQsController.computeExpansionFraction(),
1513                 mDisplayTopInset,
1514                 mSplitShadeEnabled,
1515                 udfpsAodTopLocation,
1516                 mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
1517                 mKeyguardStatusViewController.isClockTopAligned());
1518         mClockPositionAlgorithm.run(mClockPositionResult);
1519         mKeyguardBottomAreaInteractor.setClockPosition(
1520                 mClockPositionResult.clockX, mClockPositionResult.clockY);
1521         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
1522         boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
1523         mKeyguardStatusViewController.updatePosition(
1524                 mClockPositionResult.clockX, mClockPositionResult.clockY,
1525                 mClockPositionResult.clockScale, animateClock);
1526         if (mKeyguardQsUserSwitchController != null) {
1527             mKeyguardQsUserSwitchController.updatePosition(
1528                     mClockPositionResult.clockX,
1529                     mClockPositionResult.userSwitchY,
1530                     animateClock);
1531         }
1532         if (mKeyguardUserSwitcherController != null) {
1533             mKeyguardUserSwitcherController.updatePosition(
1534                     mClockPositionResult.clockX,
1535                     mClockPositionResult.userSwitchY,
1536                     animateClock);
1537         }
1538         updateNotificationTranslucency();
1539         updateClock();
1540     }
1541 
getClockPositionResult()1542     public KeyguardClockPositionAlgorithm.Result getClockPositionResult() {
1543         return mClockPositionResult;
1544     }
1545 
1546     @ClockSize
computeDesiredClockSize()1547     private int computeDesiredClockSize() {
1548         if (mSplitShadeEnabled) {
1549             return computeDesiredClockSizeForSplitShade();
1550         }
1551         return computeDesiredClockSizeForSingleShade();
1552     }
1553 
1554     @ClockSize
computeDesiredClockSizeForSingleShade()1555     private int computeDesiredClockSizeForSingleShade() {
1556         if (hasVisibleNotifications()) {
1557             return SMALL;
1558         }
1559         return LARGE;
1560     }
1561 
1562     @ClockSize
computeDesiredClockSizeForSplitShade()1563     private int computeDesiredClockSizeForSplitShade() {
1564         // Media is not visible to the user on AOD.
1565         boolean isMediaVisibleToUser =
1566                 mMediaDataManager.hasActiveMediaOrRecommendation() && !isOnAod();
1567         if (isMediaVisibleToUser) {
1568             // When media is visible, it overlaps with the large clock. Use small clock instead.
1569             return SMALL;
1570         }
1571         return LARGE;
1572     }
1573 
updateKeyguardStatusViewAlignment(boolean animate)1574     private void updateKeyguardStatusViewAlignment(boolean animate) {
1575         boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
1576         if (mStatusViewCentered != shouldBeCentered) {
1577             mStatusViewCentered = shouldBeCentered;
1578             ConstraintSet constraintSet = new ConstraintSet();
1579             constraintSet.clone(mNotificationContainerParent);
1580             int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline;
1581             constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END);
1582             if (animate) {
1583                 mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
1584                 ChangeBounds transition = new ChangeBounds();
1585                 if (mSplitShadeEnabled) {
1586                     // Excluding media from the transition on split-shade, as it doesn't transition
1587                     // horizontally properly.
1588                     transition.excludeTarget(R.id.status_view_media_container, true);
1589                 }
1590 
1591                 transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
1592                 transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1593 
1594                 ClockAnimations clockAnims = mKeyguardStatusViewController.getClockAnimations();
1595                 boolean customClockAnimation = clockAnims != null
1596                         && clockAnims.getHasCustomPositionUpdatedAnimation();
1597 
1598                 if (mFeatureFlags.isEnabled(Flags.STEP_CLOCK_ANIMATION) && customClockAnimation) {
1599                     // Find the clock, so we can exclude it from this transition.
1600                     FrameLayout clockContainerView =
1601                             mView.findViewById(R.id.lockscreen_clock_view_large);
1602 
1603                     // The clock container can sometimes be null. If it is, just fall back to the
1604                     // old animation rather than setting up the custom animations.
1605                     if (clockContainerView == null || clockContainerView.getChildCount() == 0) {
1606                         transition.addListener(mKeyguardStatusAlignmentTransitionListener);
1607                         TransitionManager.beginDelayedTransition(
1608                                 mNotificationContainerParent, transition);
1609                     } else {
1610                         View clockView = clockContainerView.getChildAt(0);
1611 
1612                         transition.excludeTarget(clockView, /* exclude= */ true);
1613 
1614                         TransitionSet set = new TransitionSet();
1615                         set.addTransition(transition);
1616 
1617                         SplitShadeTransitionAdapter adapter =
1618                                 new SplitShadeTransitionAdapter(mKeyguardStatusViewController);
1619 
1620                         // Use linear here, so the actual clock can pick its own interpolator.
1621                         adapter.setInterpolator(Interpolators.LINEAR);
1622                         adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION);
1623                         adapter.addTarget(clockView);
1624                         set.addTransition(adapter);
1625                         set.addListener(mKeyguardStatusAlignmentTransitionListener);
1626                         TransitionManager.beginDelayedTransition(mNotificationContainerParent, set);
1627                     }
1628                 } else {
1629                     transition.addListener(mKeyguardStatusAlignmentTransitionListener);
1630                     TransitionManager.beginDelayedTransition(
1631                             mNotificationContainerParent, transition);
1632                 }
1633             }
1634 
1635             constraintSet.applyTo(mNotificationContainerParent);
1636         }
1637         mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(mStatusViewCentered));
1638     }
1639 
shouldKeyguardStatusViewBeCentered()1640     private boolean shouldKeyguardStatusViewBeCentered() {
1641         if (mSplitShadeEnabled) {
1642             return shouldKeyguardStatusViewBeCenteredInSplitShade();
1643         }
1644         return true;
1645     }
1646 
shouldKeyguardStatusViewBeCenteredInSplitShade()1647     private boolean shouldKeyguardStatusViewBeCenteredInSplitShade() {
1648         if (!hasVisibleNotifications()) {
1649             // No notifications visible. It is safe to have the clock centered as there will be no
1650             // overlap.
1651             return true;
1652         }
1653         if (hasPulsingNotifications()) {
1654             // Pulsing notification appears on the right. Move clock left to avoid overlap.
1655             return false;
1656         }
1657         if (mWillPlayDelayedDozeAmountAnimation) {
1658             return true;
1659         }
1660         // "Visible" notifications are actually not visible on AOD (unless pulsing), so it is safe
1661         // to center the clock without overlap.
1662         return isOnAod();
1663     }
1664 
1665     /**
1666      * Notify us that {@link NotificationWakeUpCoordinator} is going to play the doze wakeup
1667      * animation after a delay. If so, we'll keep the clock centered until that animation starts.
1668      */
setWillPlayDelayedDozeAmountAnimation(boolean willPlay)1669     public void setWillPlayDelayedDozeAmountAnimation(boolean willPlay) {
1670         if (mWillPlayDelayedDozeAmountAnimation == willPlay) return;
1671 
1672         mWillPlayDelayedDozeAmountAnimation = willPlay;
1673         mWakeUpCoordinator.logDelayingClockWakeUpAnimation(willPlay);
1674 
1675         // Once changing this value, see if we should move the clock.
1676         positionClockAndNotifications();
1677     }
1678 
isOnAod()1679     private boolean isOnAod() {
1680         return mDozing && mDozeParameters.getAlwaysOn();
1681     }
1682 
hasVisibleNotifications()1683     private boolean hasVisibleNotifications() {
1684         return mNotificationStackScrollLayoutController
1685                 .getVisibleNotificationCount() != 0
1686                 || mMediaDataManager.hasActiveMediaOrRecommendation();
1687     }
1688 
1689     /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
getLockIconPadding()1690     private float getLockIconPadding() {
1691         float lockIconPadding = 0f;
1692         if (mLockIconViewController.getTop() != 0f) {
1693             lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
1694                     - mLockIconViewController.getTop();
1695         }
1696         return lockIconPadding;
1697     }
1698 
1699     /** Returns space available to show notifications on lockscreen. */
1700     @VisibleForTesting
getVerticalSpaceForLockscreenNotifications()1701     float getVerticalSpaceForLockscreenNotifications() {
1702         final float lockIconPadding = getLockIconPadding();
1703 
1704         float bottomPadding = Math.max(lockIconPadding,
1705                 Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
1706         mKeyguardNotificationBottomPadding = bottomPadding;
1707 
1708         float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
1709                 // getMinStackScrollerPadding is from the top of the screen,
1710                 // but we need it from the top of the NSSL.
1711                 - mNotificationStackScrollLayoutController.getTop();
1712         mKeyguardNotificationTopPadding = staticTopPadding;
1713 
1714         // To debug the available space, enable debug lines in this class. If you change how the
1715         // available space is calculated, please also update those lines.
1716         final float verticalSpace =
1717                 mNotificationStackScrollLayoutController.getHeight()
1718                         - staticTopPadding
1719                         - bottomPadding;
1720 
1721         if (SPEW_LOGCAT) {
1722             Log.i(TAG, "\n");
1723             Log.i(TAG, "staticTopPadding[" + staticTopPadding
1724                     + "] = Clock.padding["
1725                     + mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
1726                     + "] - NSSLC.top[" + mNotificationStackScrollLayoutController.getTop()
1727                     + "]"
1728             );
1729             Log.i(TAG, "bottomPadding[" + bottomPadding
1730                     + "] = max(ambientIndicationBottomPadding[" + mAmbientIndicationBottomPadding
1731                     + "], mIndicationBottomPadding[" + mIndicationBottomPadding
1732                     + "], lockIconPadding[" + lockIconPadding
1733                     + "])"
1734             );
1735             Log.i(TAG, "verticalSpaceForNotifications[" + verticalSpace
1736                     + "] = NSSL.height[" + mNotificationStackScrollLayoutController.getHeight()
1737                     + "] - staticTopPadding[" + staticTopPadding
1738                     + "] - bottomPadding[" + bottomPadding
1739                     + "]"
1740             );
1741         }
1742         return verticalSpace;
1743     }
1744 
1745     /** Returns extra space available to show the shelf on lockscreen */
1746     @VisibleForTesting
getVerticalSpaceForLockscreenShelf()1747     float getVerticalSpaceForLockscreenShelf() {
1748         final float lockIconPadding = getLockIconPadding();
1749 
1750         final float noShelfOverlapBottomPadding =
1751                 Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
1752 
1753         final float extraSpaceForShelf = lockIconPadding - noShelfOverlapBottomPadding;
1754 
1755         if (extraSpaceForShelf > 0f) {
1756             return Math.min(mNotificationShelfController.getIntrinsicHeight(),
1757                     extraSpaceForShelf);
1758         }
1759         return 0f;
1760     }
1761 
1762     /**
1763      * @return Maximum number of notifications that can fit on keyguard.
1764      */
1765     @VisibleForTesting
computeMaxKeyguardNotifications()1766     int computeMaxKeyguardNotifications() {
1767         if (mAmbientState.getFractionToShade() > 0) {
1768             if (SPEW_LOGCAT) {
1769                 Log.v(TAG, "Internally skipping computeMaxKeyguardNotifications()"
1770                         + " fractionToShade=" + mAmbientState.getFractionToShade()
1771                 );
1772             }
1773             return mMaxAllowedKeyguardNotifications;
1774         }
1775         return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications(
1776                 mNotificationStackScrollLayoutController.getView(),
1777                 getVerticalSpaceForLockscreenNotifications(),
1778                 getVerticalSpaceForLockscreenShelf(),
1779                 mNotificationShelfController.getIntrinsicHeight()
1780         );
1781     }
1782 
updateClock()1783     private void updateClock() {
1784         if (mIsOcclusionTransitionRunning) {
1785             return;
1786         }
1787         float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
1788         mKeyguardStatusViewController.setAlpha(alpha);
1789         mKeyguardStatusViewController
1790             .setTranslationY(mKeyguardOnlyTransitionTranslationY, /* excludeMedia= */true);
1791 
1792         if (mKeyguardQsUserSwitchController != null) {
1793             mKeyguardQsUserSwitchController.setAlpha(alpha);
1794         }
1795         if (mKeyguardUserSwitcherController != null) {
1796             mKeyguardUserSwitcherController.setAlpha(alpha);
1797         }
1798     }
1799 
animateToFullShade(long delay)1800     public void animateToFullShade(long delay) {
1801         mNotificationStackScrollLayoutController.goToFullShade(delay);
1802         mView.requestLayout();
1803         mAnimateNextPositionUpdate = true;
1804     }
1805 
1806     /** Animate QS closing. */
animateCloseQs(boolean animateAway)1807     public void animateCloseQs(boolean animateAway) {
1808         if (mSplitShadeEnabled) {
1809             collapsePanel(true, false, 1.0f);
1810         } else {
1811             mQsController.animateCloseQs(animateAway);
1812         }
1813 
1814     }
1815 
resetViews(boolean animate)1816     public void resetViews(boolean animate) {
1817         mGutsManager.closeAndSaveGuts(true /* leavebehind */, true /* force */,
1818                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
1819         if (animate && !isFullyCollapsed()) {
1820             animateCloseQs(true);
1821         } else {
1822             closeQsIfPossible();
1823         }
1824         mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate,
1825                 !animate /* cancelAnimators */);
1826         mNotificationStackScrollLayoutController.resetScrollPosition();
1827     }
1828 
1829     /** Collapses the panel. */
collapsePanel(boolean animate, boolean delayed, float speedUpFactor)1830     public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
1831         boolean waiting = false;
1832         if (animate && !isFullyCollapsed()) {
1833             collapse(delayed, speedUpFactor);
1834             waiting = true;
1835         } else {
1836             resetViews(false /* animate */);
1837             mShadeHeightLogger.logFunctionCall("collapsePanel");
1838             setExpandedFraction(0); // just in case
1839         }
1840         if (!waiting) {
1841             // it's possible that nothing animated, so we replicate the termination
1842             // conditions of panelExpansionChanged here
1843             // TODO(b/200063118): This can likely go away in a future refactor CL.
1844             getShadeExpansionStateManager().updateState(STATE_CLOSED);
1845         }
1846     }
1847 
collapse(boolean delayed, float speedUpFactor)1848     public void collapse(boolean delayed, float speedUpFactor) {
1849         if (!canPanelBeCollapsed()) {
1850             return;
1851         }
1852 
1853         if (mQsController.getExpanded()) {
1854             mQsController.setExpandImmediate(true);
1855             setShowShelfOnly(true);
1856         }
1857         debugLog("collapse: %s", this);
1858         if (canPanelBeCollapsed()) {
1859             cancelHeightAnimator();
1860             notifyExpandingStarted();
1861 
1862             // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
1863             setClosing(true);
1864             if (delayed) {
1865                 mNextCollapseSpeedUpFactor = speedUpFactor;
1866                 this.mView.postDelayed(mFlingCollapseRunnable, 120);
1867             } else {
1868                 fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
1869             }
1870         }
1871     }
1872 
setShowShelfOnly(boolean shelfOnly)1873     private void setShowShelfOnly(boolean shelfOnly) {
1874         mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
1875                 shelfOnly && !mSplitShadeEnabled);
1876     }
1877 
1878     @VisibleForTesting
cancelHeightAnimator()1879     void cancelHeightAnimator() {
1880         if (mHeightAnimator != null) {
1881             if (mHeightAnimator.isRunning()) {
1882                 mPanelUpdateWhenAnimatorEnds = false;
1883             }
1884             mHeightAnimator.cancel();
1885         }
1886         endClosing();
1887     }
1888 
cancelAnimation()1889     public void cancelAnimation() {
1890         mView.animate().cancel();
1891     }
1892 
expandWithQs()1893     public void expandWithQs() {
1894         if (mQsController.isExpansionEnabled()) {
1895             mQsController.setExpandImmediate(true);
1896             setShowShelfOnly(true);
1897         }
1898         if (mSplitShadeEnabled && isOnKeyguard()) {
1899             // It's a special case as this method is likely to not be initiated by finger movement
1900             // but rather called from adb shell or accessibility service.
1901             // We're using LockscreenShadeTransitionController because on lockscreen that's the
1902             // source of truth for all shade motion. Not using it would make part of state to be
1903             // outdated and will cause bugs. Ideally we'd use this controller also for non-split
1904             // case but currently motion in portrait looks worse than when using flingSettings.
1905             // TODO: make below function transitioning smoothly also in portrait with null target
1906             mLockscreenShadeTransitionController.goToLockedShade(
1907                     /* expandedView= */null, /* needsQSAnimation= */true);
1908         } else if (isFullyCollapsed()) {
1909             expand(true /* animate */);
1910         } else {
1911             mQsController.traceQsJank(true /* startTracing */, false /* wasCancelled */);
1912             mQsController.flingQs(0, FLING_EXPAND);
1913         }
1914     }
1915 
1916     /**
1917      * Expand shade so that notifications are visible.
1918      * Non-split shade: just expanding shade or collapsing QS when they're expanded.
1919      * Split shade: only expanding shade, notifications are always visible
1920      *
1921      * Called when `adb shell cmd statusbar expand-notifications` is executed.
1922      */
expandShadeToNotifications()1923     public void expandShadeToNotifications() {
1924         if (mSplitShadeEnabled && (isShadeFullyOpen() || isExpanding())) {
1925             return;
1926         }
1927         if (mQsController.getExpanded()) {
1928             mQsController.flingQs(0, FLING_COLLAPSE);
1929         } else {
1930             expand(true /* animate */);
1931         }
1932     }
1933 
fling(float vel)1934     private void fling(float vel) {
1935         if (mGestureRecorder != null) {
1936             mGestureRecorder.tag("fling " + ((vel > 0) ? "open" : "closed"),
1937                     "notifications,v=" + vel);
1938         }
1939         fling(vel, true, 1.0f /* collapseSpeedUpFactor */, false);
1940     }
1941 
1942     @VisibleForTesting
flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1943     void flingToHeight(float vel, boolean expand, float target,
1944             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
1945         mQsController.setLastShadeFlingWasExpanding(expand);
1946         mHeadsUpTouchHelper.notifyFling(!expand);
1947         mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
1948         setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
1949         mNotificationStackScrollLayoutController.setPanelFlinging(true);
1950         if (target == mExpandedHeight && mOverExpansion == 0.0f) {
1951             // We're at the target and didn't fling and there's no overshoot
1952             onFlingEnd(false /* cancelled */);
1953             return;
1954         }
1955         mIsFlinging = true;
1956         // we want to perform an overshoot animation when flinging open
1957         final boolean addOverscroll =
1958                 expand
1959                         && mStatusBarStateController.getState() != KEYGUARD
1960                         && mOverExpansion == 0.0f
1961                         && vel >= 0;
1962         final boolean shouldSpringBack = addOverscroll || (mOverExpansion != 0.0f && expand);
1963         float overshootAmount = 0.0f;
1964         if (addOverscroll) {
1965             // Let's overshoot depending on the amount of velocity
1966             overshootAmount = MathUtils.lerp(
1967                     0.2f,
1968                     1.0f,
1969                     MathUtils.saturate(vel
1970                             / (this.mFlingAnimationUtils.getHighVelocityPxPerSecond()
1971                             * FACTOR_OF_HIGH_VELOCITY_FOR_MAX_OVERSHOOT)));
1972             overshootAmount += mOverExpansion / mPanelFlingOvershootAmount;
1973         }
1974         ValueAnimator animator = createHeightAnimator(target, overshootAmount);
1975         if (expand) {
1976             maybeVibrateOnOpening(true /* openingWithTouch */);
1977             if (expandBecauseOfFalsing && vel < 0) {
1978                 vel = 0;
1979             }
1980             this.mFlingAnimationUtils.apply(animator, mExpandedHeight,
1981                     target + overshootAmount * mPanelFlingOvershootAmount, vel,
1982                     this.mView.getHeight());
1983             if (vel == 0) {
1984                 animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
1985             }
1986         } else {
1987             mHasVibratedOnOpen = false;
1988             if (shouldUseDismissingAnimation()) {
1989                 if (vel == 0) {
1990                     animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
1991                     long duration = (long) (200 + mExpandedHeight / this.mView.getHeight() * 100);
1992                     animator.setDuration(duration);
1993                 } else {
1994                     mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
1995                             this.mView.getHeight());
1996                 }
1997             } else {
1998                 mFlingAnimationUtilsClosing.apply(
1999                         animator, mExpandedHeight, target, vel, this.mView.getHeight());
2000             }
2001 
2002             // Make it shorter if we run a canned animation
2003             if (vel == 0) {
2004                 animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
2005             }
2006             if (mFixedDuration != NO_FIXED_DURATION) {
2007                 animator.setDuration(mFixedDuration);
2008             }
2009         }
2010         animator.addListener(new AnimatorListenerAdapter() {
2011             private boolean mCancelled;
2012 
2013             @Override
2014             public void onAnimationStart(Animator animation) {
2015                 if (!mStatusBarStateController.isDozing()) {
2016                     mQsController.beginJankMonitoring(isFullyCollapsed());
2017                 }
2018             }
2019 
2020             @Override
2021             public void onAnimationCancel(Animator animation) {
2022                 mCancelled = true;
2023             }
2024 
2025             @Override
2026             public void onAnimationEnd(Animator animation) {
2027                 if (shouldSpringBack && !mCancelled) {
2028                     // After the shade is flung open to an overscrolled state, spring back
2029                     // the shade by reducing section padding to 0.
2030                     springBack();
2031                 } else {
2032                     onFlingEnd(mCancelled);
2033                 }
2034             }
2035         });
2036         setAnimator(animator);
2037         animator.start();
2038     }
2039 
2040     @VisibleForTesting
onFlingEnd(boolean cancelled)2041     void onFlingEnd(boolean cancelled) {
2042         mIsFlinging = false;
2043         // No overshoot when the animation ends
2044         setOverExpansionInternal(0, false /* isFromGesture */);
2045         setAnimator(null);
2046         mKeyguardStateController.notifyPanelFlingEnd();
2047         if (!cancelled) {
2048             mQsController.endJankMonitoring();
2049             notifyExpandingFinished();
2050         } else {
2051             mQsController.cancelJankMonitoring();
2052         }
2053         updatePanelExpansionAndVisibility();
2054         mNotificationStackScrollLayoutController.setPanelFlinging(false);
2055     }
2056 
isInContentBounds(float x, float y)2057     private boolean isInContentBounds(float x, float y) {
2058         float stackScrollerX = mNotificationStackScrollLayoutController.getX();
2059         return !mNotificationStackScrollLayoutController
2060                 .isBelowLastNotification(x - stackScrollerX, y)
2061                 && stackScrollerX < x
2062                 && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
2063     }
2064 
initDownStates(MotionEvent event)2065     private void initDownStates(MotionEvent event) {
2066         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
2067             mDozingOnDown = mDozing;
2068             mDownX = event.getX();
2069             mDownY = event.getY();
2070             mCollapsedOnDown = isFullyCollapsed();
2071             mQsController.setCollapsedOnDown(mCollapsedOnDown);
2072             mIsPanelCollapseOnQQS = mQsController.canPanelCollapseOnQQS(mDownX, mDownY);
2073             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
2074             mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
2075             mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
2076             // When false, down but not synthesized motion event.
2077             mLastEventSynthesizedDown = mExpectingSynthesizedDown;
2078             mLastDownEvents.insert(
2079                     event.getEventTime(),
2080                     mDownX,
2081                     mDownY,
2082                     mQsController.updateAndGetTouchAboveFalsingThreshold(),
2083                     mDozingOnDown,
2084                     mCollapsedOnDown,
2085                     mIsPanelCollapseOnQQS,
2086                     mListenForHeadsUp,
2087                     mAllowExpandForSmallExpansion,
2088                     mTouchSlopExceededBeforeDown,
2089                     mLastEventSynthesizedDown
2090             );
2091         } else {
2092             // not down event at all.
2093             mLastEventSynthesizedDown = false;
2094         }
2095     }
2096 
flingExpandsQs(float vel)2097     boolean flingExpandsQs(float vel) {
2098         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
2099             return mQsController.computeExpansionFraction() > 0.5f;
2100         } else {
2101             return vel > 0;
2102         }
2103     }
2104 
shouldExpandWhenNotFlinging()2105     private boolean shouldExpandWhenNotFlinging() {
2106         if (getExpandedFraction() > 0.5f) {
2107             return true;
2108         }
2109         if (mAllowExpandForSmallExpansion) {
2110             // When we get a touch that came over from launcher, the velocity isn't always correct
2111             // Let's err on expanding if the gesture has been reasonably slow
2112             long timeSinceDown = mSystemClock.uptimeMillis() - mDownTime;
2113             return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
2114         }
2115         return false;
2116     }
2117 
getOpeningHeight()2118     private float getOpeningHeight() {
2119         return mNotificationStackScrollLayoutController.getOpeningHeight();
2120     }
2121 
getDisplayDensity()2122     float getDisplayDensity() {
2123         return mCentralSurfaces.getDisplayDensity();
2124     }
2125 
2126     /** Return whether a touch is near the gesture handle at the bottom of screen */
isInGestureNavHomeHandleArea(float x, float y)2127     public boolean isInGestureNavHomeHandleArea(float x, float y) {
2128         return mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight;
2129     }
2130 
2131     /** Input focus transfer is about to happen. */
startWaitingForOpenPanelGesture()2132     public void startWaitingForOpenPanelGesture() {
2133         if (!isFullyCollapsed()) {
2134             return;
2135         }
2136         mExpectingSynthesizedDown = true;
2137         onTrackingStarted();
2138         updatePanelExpanded();
2139     }
2140 
2141     /**
2142      * Called when this view is no longer waiting for input focus transfer.
2143      *
2144      * There are two scenarios behind this function call. First, input focus transfer
2145      * has successfully happened and this view already received synthetic DOWN event.
2146      * (mExpectingSynthesizedDown == false). Do nothing.
2147      *
2148      * Second, before input focus transfer finished, user may have lifted finger
2149      * in previous window and this window never received synthetic DOWN event.
2150      * (mExpectingSynthesizedDown == true).
2151      * In this case, we use the velocity to trigger fling event.
2152      *
2153      * @param velocity unit is in px / millis
2154      */
stopWaitingForOpenPanelGesture(boolean cancel, final float velocity)2155     public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) {
2156         if (mExpectingSynthesizedDown) {
2157             mExpectingSynthesizedDown = false;
2158             if (cancel) {
2159                 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
2160             } else {
2161                 // Window never will receive touch events that typically trigger haptic on open.
2162                 maybeVibrateOnOpening(false /* openingWithTouch */);
2163                 fling(velocity > 1f ? 1000f * velocity : 0  /* expand */);
2164             }
2165             onTrackingStopped(false);
2166         }
2167     }
2168 
flingExpands(float vel, float vectorVel, float x, float y)2169     private boolean flingExpands(float vel, float vectorVel, float x, float y) {
2170         boolean expands = true;
2171         if (!this.mFalsingManager.isUnlockingDisabled()) {
2172             @Classifier.InteractionType int interactionType = y - mInitialExpandY > 0
2173                     ? QUICK_SETTINGS : (
2174                     mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
2175             if (!isFalseTouch(x, y, interactionType)) {
2176                 if (Math.abs(vectorVel) < this.mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
2177                     expands = shouldExpandWhenNotFlinging();
2178                 } else {
2179                     expands = vel > 0;
2180                 }
2181             }
2182         }
2183 
2184         // If we are already running a QS expansion, make sure that we keep the panel open.
2185         if (mQsController.isExpansionAnimating()) {
2186             expands = true;
2187         }
2188         return expands;
2189     }
2190 
shouldGestureWaitForTouchSlop()2191     private boolean shouldGestureWaitForTouchSlop() {
2192         if (mExpectingSynthesizedDown) {
2193             mExpectingSynthesizedDown = false;
2194             return false;
2195         }
2196         return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
2197     }
2198 
getFalsingThreshold()2199     int getFalsingThreshold() {
2200         float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
2201         return (int) (mQsController.getFalsingThreshold() * factor);
2202     }
2203 
maybeAnimateBottomAreaAlpha()2204     private void maybeAnimateBottomAreaAlpha() {
2205         mBottomAreaShadeAlphaAnimator.cancel();
2206         if (mBarState == StatusBarState.SHADE_LOCKED) {
2207             mBottomAreaShadeAlphaAnimator.setFloatValues(mBottomAreaShadeAlpha, 0.0f);
2208             mBottomAreaShadeAlphaAnimator.start();
2209         } else {
2210             mBottomAreaShadeAlpha = 1f;
2211         }
2212     }
2213 
setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)2214     private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
2215         mKeyguardBottomArea.animate().cancel();
2216         if (goingToFullShade) {
2217             mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
2218                     mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
2219                     mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
2220                     Interpolators.ALPHA_OUT).withEndAction(
2221                     mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
2222         } else if (statusBarState == KEYGUARD
2223                 || statusBarState == StatusBarState.SHADE_LOCKED) {
2224             mKeyguardBottomArea.setVisibility(View.VISIBLE);
2225             if (!mIsOcclusionTransitionRunning) {
2226                 mKeyguardBottomArea.setAlpha(1f);
2227             }
2228         } else {
2229             mKeyguardBottomArea.setVisibility(View.GONE);
2230         }
2231     }
2232 
2233     /** */
getLockscreenShadeDragProgress()2234     public float getLockscreenShadeDragProgress() {
2235         // mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade
2236         // transition. If that's not the case we should follow QS expansion fraction for when
2237         // user is pulling from the same top to go directly to expanded QS
2238         return mQsController.getTransitioningToFullShadeProgress() > 0
2239                 ? mLockscreenShadeTransitionController.getQSDragProgress()
2240                 : mQsController.computeExpansionFraction();
2241     }
2242 
determineAccessibilityPaneTitle()2243     String determineAccessibilityPaneTitle() {
2244         if (mQsController != null && mQsController.isCustomizing()) {
2245             return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
2246         } else if (mQsController != null && mQsController.getExpansionHeight() != 0.0f
2247                 && mQsController.getFullyExpanded()) {
2248             // Upon initialisation when we are not layouted yet we don't want to announce that we
2249             // are fully expanded, hence the != 0.0f check.
2250             if (mSplitShadeEnabled) {
2251                 // In split shade, QS is expanded but it also shows notifications
2252                 return mResources.getString(R.string.accessibility_desc_qs_notification_shade);
2253             } else {
2254                 return mResources.getString(R.string.accessibility_desc_quick_settings);
2255             }
2256         } else if (mBarState == KEYGUARD) {
2257             return mResources.getString(R.string.accessibility_desc_lock_screen);
2258         } else {
2259             return mResources.getString(R.string.accessibility_desc_notification_shade);
2260         }
2261     }
2262 
2263     /** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
getKeyguardNotificationStaticPadding()2264     public int getKeyguardNotificationStaticPadding() {
2265         if (!getKeyguardShowing()) {
2266             return 0;
2267         }
2268         if (!mKeyguardBypassController.getBypassEnabled()) {
2269             return mClockPositionResult.stackScrollerPadding;
2270         }
2271         int collapsedPosition = mHeadsUpInset;
2272         if (!mNotificationStackScrollLayoutController.isPulseExpanding()) {
2273             return collapsedPosition;
2274         } else {
2275             int expandedPosition =
2276                     mClockPositionResult.stackScrollerPadding;
2277             return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
2278                     mNotificationStackScrollLayoutController.calculateAppearFractionBypass());
2279         }
2280     }
2281 
getKeyguardShowing()2282     public boolean getKeyguardShowing() {
2283         return mBarState == KEYGUARD;
2284     }
2285 
getKeyguardNotificationTopPadding()2286     public float getKeyguardNotificationTopPadding() {
2287         return mKeyguardNotificationTopPadding;
2288     }
2289 
getKeyguardNotificationBottomPadding()2290     public float getKeyguardNotificationBottomPadding() {
2291         return mKeyguardNotificationBottomPadding;
2292     }
2293 
requestScrollerTopPaddingUpdate(boolean animate)2294     void requestScrollerTopPaddingUpdate(boolean animate) {
2295         mNotificationStackScrollLayoutController.updateTopPadding(
2296                 mQsController.calculateNotificationsTopPadding(mIsExpanding,
2297                         getKeyguardNotificationStaticPadding(), mExpandedFraction), animate);
2298         if (getKeyguardShowing()
2299                 && mKeyguardBypassController.getBypassEnabled()) {
2300             // update the position of the header
2301             mQsController.updateExpansion();
2302         }
2303     }
2304 
2305     /**
2306      * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
2307      * but not in shade locked / shade. This is used when dragging down to the full shade.
2308      */
setKeyguardTransitionProgress(float keyguardAlpha, int keyguardTranslationY)2309     public void setKeyguardTransitionProgress(float keyguardAlpha, int keyguardTranslationY) {
2310         mKeyguardOnlyContentAlpha = Interpolators.ALPHA_IN.getInterpolation(keyguardAlpha);
2311         mKeyguardOnlyTransitionTranslationY = keyguardTranslationY;
2312         if (mBarState == KEYGUARD) {
2313             // If the animator is running, it's already fading out the content and this is a reset
2314             mBottomAreaShadeAlpha = mKeyguardOnlyContentAlpha;
2315             updateKeyguardBottomAreaAlpha();
2316         }
2317         updateClock();
2318     }
2319 
2320     /**
2321      * Sets the alpha value to be set on the keyguard status bar.
2322      *
2323      * @param alpha value between 0 and 1. -1 if the value is to be reset.
2324      */
setKeyguardStatusBarAlpha(float alpha)2325     public void setKeyguardStatusBarAlpha(float alpha) {
2326         mKeyguardStatusBarViewController.setAlpha(alpha);
2327     }
2328 
2329     /** */
getKeyguardOnlyContentAlpha()2330     public float getKeyguardOnlyContentAlpha() {
2331         return mKeyguardOnlyContentAlpha;
2332     }
2333 
2334     @VisibleForTesting
canCollapsePanelOnTouch()2335     boolean canCollapsePanelOnTouch() {
2336         if (!mQsController.getExpanded() && mBarState == KEYGUARD) {
2337             return true;
2338         }
2339 
2340         if (mNotificationStackScrollLayoutController.isScrolledToBottom()) {
2341             return true;
2342         }
2343 
2344         return !mSplitShadeEnabled && (mQsController.getExpanded() || mIsPanelCollapseOnQQS);
2345     }
2346 
getMaxPanelHeight()2347     int getMaxPanelHeight() {
2348         int min = mStatusBarMinHeight;
2349         if (!(mBarState == KEYGUARD)
2350                 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
2351             int minHeight = mQsController.getMinExpansionHeight();
2352             min = Math.max(min, minHeight);
2353         }
2354         int maxHeight;
2355         if (mQsController.isExpandImmediate() || mQsController.getExpanded()
2356                 || mIsExpanding && mQsController.getExpandedWhenExpandingStarted()
2357                 || mPulsing || mSplitShadeEnabled) {
2358             maxHeight = mQsController.calculatePanelHeightExpanded(
2359                     mClockPositionResult.stackScrollerPadding);
2360         } else {
2361             maxHeight = calculatePanelHeightShade();
2362         }
2363         maxHeight = Math.max(min, maxHeight);
2364         if (maxHeight == 0) {
2365             Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: "
2366                     + mOverExpansion + ", calculatePanelHeightQsExpanded: "
2367                     + mQsController.calculatePanelHeightExpanded(
2368                             mClockPositionResult.stackScrollerPadding)
2369                     + ", calculatePanelHeightShade: " + calculatePanelHeightShade()
2370                     + ", mStatusBarMinHeight = " + mStatusBarMinHeight
2371                     + ", mQsMinExpansionHeight = " + mQsController.getMinExpansionHeight());
2372         }
2373         return maxHeight;
2374     }
2375 
isExpanding()2376     public boolean isExpanding() {
2377         return mIsExpanding;
2378     }
2379 
onHeightUpdated(float expandedHeight)2380     private void onHeightUpdated(float expandedHeight) {
2381         if (expandedHeight <= 0) {
2382             mShadeLog.logExpansionChanged("onHeightUpdated: fully collapsed.",
2383                     mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
2384         } else if (isFullyExpanded()) {
2385             mShadeLog.logExpansionChanged("onHeightUpdated: fully expanded.",
2386                     mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
2387         }
2388         if (!mQsController.getExpanded() || mQsController.isExpandImmediate()
2389                 || mIsExpanding && mQsController.getExpandedWhenExpandingStarted()) {
2390             // Updating the clock position will set the top padding which might
2391             // trigger a new panel height and re-position the clock.
2392             // This is a circular dependency and should be avoided, otherwise we'll have
2393             // a stack overflow.
2394             if (mStackScrollerMeasuringPass > 2) {
2395                 debugLog("Unstable notification panel height. Aborting.");
2396             } else {
2397                 positionClockAndNotifications();
2398             }
2399         }
2400         boolean goingBetweenClosedShadeAndExpandedQs =
2401                 mQsController.isGoingBetweenClosedShadeAndExpandedQs();
2402         // in split shade we react when HUN is visible only if shade height is over HUN start
2403         // height - which means user is swiping down. Otherwise shade QS will either not show at all
2404         // with HUN movement or it will blink when touching HUN initially
2405         boolean qsShouldExpandWithHeadsUp = !mSplitShadeEnabled
2406                 || (!mHeadsUpManager.isTrackingHeadsUp() || expandedHeight > mHeadsUpStartHeight);
2407         if (goingBetweenClosedShadeAndExpandedQs && qsShouldExpandWithHeadsUp) {
2408             float qsExpansionFraction;
2409             if (mSplitShadeEnabled) {
2410                 qsExpansionFraction = 1;
2411             } else if (getKeyguardShowing()) {
2412                 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
2413                 qsExpansionFraction = expandedHeight / (getMaxPanelHeight());
2414             } else {
2415                 // In Shade, interpolate linearly such that QS is closed whenever panel height is
2416                 // minimum QS expansion + minStackHeight
2417                 float panelHeightQsCollapsed =
2418                         mNotificationStackScrollLayoutController.getIntrinsicPadding()
2419                                 + mNotificationStackScrollLayoutController.getLayoutMinHeight();
2420                 float panelHeightQsExpanded = mQsController.calculatePanelHeightExpanded(
2421                         mClockPositionResult.stackScrollerPadding);
2422                 qsExpansionFraction = (expandedHeight - panelHeightQsCollapsed)
2423                         / (panelHeightQsExpanded - panelHeightQsCollapsed);
2424             }
2425             float targetHeight = mQsController.getMinExpansionHeight() + qsExpansionFraction
2426                     * (mQsController.getMaxExpansionHeight()
2427                     - mQsController.getMinExpansionHeight());
2428             mQsController.setExpansionHeight(targetHeight);
2429         }
2430         updateExpandedHeight(expandedHeight);
2431         updateHeader();
2432         updateNotificationTranslucency();
2433         updatePanelExpanded();
2434         updateGestureExclusionRect();
2435         if (DEBUG_DRAWABLE) {
2436             mView.invalidate();
2437         }
2438     }
2439 
updatePanelExpanded()2440     private void updatePanelExpanded() {
2441         boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
2442         if (mPanelExpanded != isExpanded) {
2443             mPanelExpanded = isExpanded;
2444             mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
2445             if (!isExpanded) {
2446                 mQsController.closeQsCustomizer();
2447             }
2448         }
2449     }
2450 
isPanelExpanded()2451     public boolean isPanelExpanded() {
2452         return mPanelExpanded;
2453     }
2454 
calculatePanelHeightShade()2455     private int calculatePanelHeightShade() {
2456         int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin();
2457         int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin;
2458 
2459         if (mBarState == KEYGUARD) {
2460             int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight()
2461                     + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
2462             return Math.max(maxHeight, minKeyguardPanelBottom);
2463         } else {
2464             return maxHeight;
2465         }
2466     }
2467 
updateNotificationTranslucency()2468     private void updateNotificationTranslucency() {
2469         if (mIsOcclusionTransitionRunning) {
2470             return;
2471         }
2472         float alpha = 1f;
2473         if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
2474                 && !mHeadsUpManager.hasPinnedHeadsUp()) {
2475             alpha = getFadeoutAlpha();
2476         }
2477         if (mBarState == KEYGUARD && !mHintAnimationRunning
2478                 && !mKeyguardBypassController.getBypassEnabled()) {
2479             alpha *= mClockPositionResult.clockAlpha;
2480         }
2481         mNotificationStackScrollLayoutController.setAlpha(alpha);
2482     }
2483 
getFadeoutAlpha()2484     private float getFadeoutAlpha() {
2485         float alpha;
2486         if (mQsController.getMinExpansionHeight() == 0) {
2487             return 1.0f;
2488         }
2489         alpha = getExpandedHeight() / mQsController.getMinExpansionHeight();
2490         alpha = Math.max(0, Math.min(alpha, 1));
2491         alpha = (float) Math.pow(alpha, 0.75);
2492         return alpha;
2493     }
2494 
2495     /** Hides the header when notifications are colliding with it. */
updateHeader()2496     private void updateHeader() {
2497         if (mBarState == KEYGUARD) {
2498             mKeyguardStatusBarViewController.updateViewState();
2499         }
2500         mQsController.updateExpansion();
2501     }
2502 
updateKeyguardBottomAreaAlpha()2503     private void updateKeyguardBottomAreaAlpha() {
2504         if (mIsOcclusionTransitionRunning) {
2505             return;
2506         }
2507         // There are two possible panel expansion behaviors:
2508         // • User dragging up to unlock: we want to fade out as quick as possible
2509         //   (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
2510         // • User tapping on lock screen: bouncer won't be visible but panel expansion will
2511         //   change due to "unlock hint animation." In this case, fading out the bottom area
2512         //   would also hide the message that says "swipe to unlock," we don't want to do that.
2513         float expansionAlpha = MathUtils.map(
2514                 isUnlockHintRunning() ? 0 : KeyguardBouncerConstants.ALPHA_EXPANSION_THRESHOLD, 1f,
2515                 0f, 1f,
2516                 getExpandedFraction());
2517         float alpha = Math.min(expansionAlpha, 1 - mQsController.computeExpansionFraction());
2518         alpha *= mBottomAreaShadeAlpha;
2519         mKeyguardBottomAreaInteractor.setAlpha(alpha);
2520         mLockIconViewController.setAlpha(alpha);
2521     }
2522 
onExpandingFinished()2523     private void onExpandingFinished() {
2524         mNotificationStackScrollLayoutController.onExpansionStopped();
2525         mHeadsUpManager.onExpandingFinished();
2526         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
2527         mIsExpanding = false;
2528         mMediaHierarchyManager.setCollapsingShadeFromQS(false);
2529         mMediaHierarchyManager.setQsExpanded(mQsController.getExpanded());
2530         if (isFullyCollapsed()) {
2531             DejankUtils.postAfterTraversal(() -> setListening(false));
2532 
2533             // Workaround b/22639032: Make sure we invalidate something because else RenderThread
2534             // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
2535             // ahead with rendering and we jank.
2536             mView.postOnAnimation(
2537                     () -> mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT));
2538         } else {
2539             setListening(true);
2540         }
2541         if (mBarState != SHADE) {
2542             // updating qsExpandImmediate is done in onPanelStateChanged for unlocked shade but
2543             // on keyguard panel state is always OPEN so we need to have that extra update
2544             mQsController.setExpandImmediate(false);
2545         }
2546         setShowShelfOnly(false);
2547         mQsController.setTwoFingerExpandPossible(false);
2548         updateTrackingHeadsUp(null);
2549         mExpandingFromHeadsUp = false;
2550         setPanelScrimMinFraction(0.0f);
2551         // Reset status bar alpha so alpha can be calculated upon updating view state.
2552         setKeyguardStatusBarAlpha(-1f);
2553     }
2554 
updateTrackingHeadsUp(@ullable ExpandableNotificationRow pickedChild)2555     private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
2556         mTrackedHeadsUpNotification = pickedChild;
2557         for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
2558             Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
2559             listener.accept(pickedChild);
2560         }
2561     }
2562 
2563     @Nullable
getTrackedHeadsUpNotification()2564     public ExpandableNotificationRow getTrackedHeadsUpNotification() {
2565         return mTrackedHeadsUpNotification;
2566     }
2567 
setListening(boolean listening)2568     private void setListening(boolean listening) {
2569         mKeyguardStatusBarViewController.setBatteryListening(listening);
2570         mQsController.setListening(listening);
2571     }
2572 
expand(boolean animate)2573     public void expand(boolean animate) {
2574         if (isFullyCollapsed() || isCollapsing()) {
2575             mInstantExpanding = true;
2576             mAnimateAfterExpanding = animate;
2577             mUpdateFlingOnLayout = false;
2578             abortAnimations();
2579             if (mTracking) {
2580                 // The panel is expanded after this call.
2581                 onTrackingStopped(true /* expands */);
2582             }
2583             if (mExpanding) {
2584                 notifyExpandingFinished();
2585             }
2586             updatePanelExpansionAndVisibility();
2587             // Wait for window manager to pickup the change, so we know the maximum height of the
2588             // panel then.
2589             this.mView.getViewTreeObserver().addOnGlobalLayoutListener(
2590                     new ViewTreeObserver.OnGlobalLayoutListener() {
2591                         @Override
2592                         public void onGlobalLayout() {
2593                             if (!mInstantExpanding) {
2594                                 mView.getViewTreeObserver().removeOnGlobalLayoutListener(
2595                                         this);
2596                                 return;
2597                             }
2598                             if (mCentralSurfaces.getNotificationShadeWindowView()
2599                                     .isVisibleToUser()) {
2600                                 mView.getViewTreeObserver().removeOnGlobalLayoutListener(
2601                                         this);
2602                                 if (mAnimateAfterExpanding) {
2603                                     notifyExpandingStarted();
2604                                     mQsController.beginJankMonitoring(isFullyCollapsed());
2605                                     fling(0  /* expand */);
2606                                 } else {
2607                                     mShadeHeightLogger.logFunctionCall("expand");
2608                                     setExpandedFraction(1f);
2609                                 }
2610                                 mInstantExpanding = false;
2611                             }
2612                         }
2613                     });
2614             // Make sure a layout really happens.
2615             this.mView.requestLayout();
2616         }
2617 
2618         setListening(true);
2619     }
2620 
2621     @VisibleForTesting
setTouchSlopExceeded(boolean isTouchSlopExceeded)2622     void setTouchSlopExceeded(boolean isTouchSlopExceeded) {
2623         mTouchSlopExceeded = isTouchSlopExceeded;
2624     }
2625 
setOverExpansion(float overExpansion)2626     public void setOverExpansion(float overExpansion) {
2627         if (overExpansion == mOverExpansion) {
2628             return;
2629         }
2630         mOverExpansion = overExpansion;
2631         if (mSplitShadeEnabled) {
2632             mQsController.setOverScrollAmount((int) overExpansion);
2633             mScrimController.setNotificationsOverScrollAmount((int) overExpansion);
2634         } else {
2635             // Translating the quick settings by half the overexpansion to center it in the
2636             // background frame
2637             mQsController.updateQsFrameTranslation();
2638         }
2639         mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
2640     }
2641 
falsingAdditionalTapRequired()2642     private void falsingAdditionalTapRequired() {
2643         if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
2644             mTapAgainViewController.show();
2645         } else {
2646             mKeyguardIndicationController.showTransientIndication(
2647                     R.string.notification_tap_again);
2648         }
2649 
2650         if (!mStatusBarStateController.isDozing()) {
2651             mVibratorHelper.vibrate(
2652                     Process.myUid(),
2653                     mView.getContext().getPackageName(),
2654                     ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT,
2655                     "falsing-additional-tap-required",
2656                     TOUCH_VIBRATION_ATTRIBUTES);
2657         }
2658     }
2659 
onTrackingStarted()2660     private void onTrackingStarted() {
2661         mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
2662         endClosing();
2663         mTracking = true;
2664         mTrackingStartedListener.onTrackingStarted();
2665         notifyExpandingStarted();
2666         updatePanelExpansionAndVisibility();
2667         mScrimController.onTrackingStarted();
2668         if (mQsController.getFullyExpanded()) {
2669             mQsController.setExpandImmediate(true);
2670             setShowShelfOnly(true);
2671         }
2672         mNotificationStackScrollLayoutController.onPanelTrackingStarted();
2673         cancelPendingPanelCollapse();
2674     }
2675 
onTrackingStopped(boolean expand)2676     private void onTrackingStopped(boolean expand) {
2677         mFalsingCollector.onTrackingStopped();
2678         mTracking = false;
2679         updatePanelExpansionAndVisibility();
2680         if (expand) {
2681             mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */,
2682                     true /* animate */);
2683         }
2684         mNotificationStackScrollLayoutController.onPanelTrackingStopped();
2685 
2686         // If we unlocked from a swipe, the user's finger might still be down after the
2687         // unlock animation ends. We need to wait until ACTION_UP to enable blurs again.
2688         mDepthController.setBlursDisabledForUnlock(false);
2689     }
2690 
updateMaxHeadsUpTranslation()2691     private void updateMaxHeadsUpTranslation() {
2692         mNotificationStackScrollLayoutController.setHeadsUpBoundaries(
2693                 mView.getHeight(), mNavigationBarBottomHeight);
2694     }
2695 
2696     @VisibleForTesting
startUnlockHintAnimation()2697     void startUnlockHintAnimation() {
2698         if (mPowerManager.isPowerSaveMode() || mAmbientState.getDozeAmount() > 0f) {
2699             onUnlockHintStarted();
2700             onUnlockHintFinished();
2701             return;
2702         }
2703 
2704         // We don't need to hint the user if an animation is already running or the user is changing
2705         // the expansion.
2706         if (mHeightAnimator != null || mTracking) {
2707             return;
2708         }
2709         notifyExpandingStarted();
2710         startUnlockHintAnimationPhase1(() -> {
2711             notifyExpandingFinished();
2712             onUnlockHintFinished();
2713             mHintAnimationRunning = false;
2714         });
2715         onUnlockHintStarted();
2716         mHintAnimationRunning = true;
2717     }
2718 
2719     @VisibleForTesting
onUnlockHintFinished()2720     void onUnlockHintFinished() {
2721         // Delay the reset a bit so the user can read the text.
2722         mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
2723         mScrimController.setExpansionAffectsAlpha(true);
2724         mNotificationStackScrollLayoutController.setUnlockHintRunning(false);
2725     }
2726 
2727     @VisibleForTesting
onUnlockHintStarted()2728     void onUnlockHintStarted() {
2729         mFalsingCollector.onUnlockHintStarted();
2730         mKeyguardIndicationController.showActionToUnlock();
2731         mScrimController.setExpansionAffectsAlpha(false);
2732         mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
2733     }
2734 
shouldUseDismissingAnimation()2735     private boolean shouldUseDismissingAnimation() {
2736         return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
2737                 || !isTracking());
2738     }
2739 
2740     @VisibleForTesting
getMaxPanelTransitionDistance()2741     int getMaxPanelTransitionDistance() {
2742         // Traditionally the value is based on the number of notifications. On split-shade, we want
2743         // the required distance to be a specific and constant value, to make sure the expansion
2744         // motion has the expected speed. We also only want this on non-lockscreen for now.
2745         if (mSplitShadeEnabled && mBarState == SHADE) {
2746             boolean transitionFromHeadsUp = (mHeadsUpManager != null
2747                     && mHeadsUpManager.isTrackingHeadsUp()) || mExpandingFromHeadsUp;
2748             // heads-up starting height is too close to mSplitShadeFullTransitionDistance and
2749             // when dragging HUN transition is already 90% complete. It makes shade become
2750             // immediately visible when starting to drag. We want to set distance so that
2751             // nothing is immediately visible when dragging (important for HUN swipe up motion) -
2752             // 0.4 expansion fraction is a good starting point.
2753             if (transitionFromHeadsUp) {
2754                 double maxDistance = Math.max(mSplitShadeFullTransitionDistance,
2755                         mHeadsUpStartHeight * 2.5);
2756                 return (int) Math.min(getMaxPanelHeight(), maxDistance);
2757             } else {
2758                 return mSplitShadeFullTransitionDistance;
2759             }
2760         } else {
2761             return getMaxPanelHeight();
2762         }
2763     }
2764 
setIsLaunchAnimationRunning(boolean running)2765     public void setIsLaunchAnimationRunning(boolean running) {
2766         boolean wasRunning = mIsLaunchAnimationRunning;
2767         mIsLaunchAnimationRunning = running;
2768         if (wasRunning != mIsLaunchAnimationRunning) {
2769             mShadeExpansionStateManager.notifyLaunchingActivityChanged(running);
2770         }
2771     }
2772 
2773     @VisibleForTesting
setClosing(boolean isClosing)2774     void setClosing(boolean isClosing) {
2775         if (mClosing != isClosing) {
2776             mClosing = isClosing;
2777             mShadeExpansionStateManager.notifyPanelCollapsingChanged(isClosing);
2778         }
2779         mAmbientState.setIsClosing(isClosing);
2780     }
2781 
updateDozingVisibilities(boolean animate)2782     private void updateDozingVisibilities(boolean animate) {
2783         mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
2784         if (!mDozing && animate) {
2785             mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
2786         }
2787     }
2788 
onScreenTurningOn()2789     public void onScreenTurningOn() {
2790         mKeyguardStatusViewController.dozeTimeTick();
2791     }
2792 
onMiddleClicked()2793     private void onMiddleClicked() {
2794         switch (mBarState) {
2795             case KEYGUARD:
2796                 if (!mDozingOnDown) {
2797                     mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
2798                     // Try triggering face auth, this "might" run. Check
2799                     // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
2800                     boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
2801                             FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
2802 
2803                     if (didFaceAuthRun) {
2804                         mUpdateMonitor.requestActiveUnlock(
2805                                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
2806                                 "lockScreenEmptySpaceTap");
2807                     } else {
2808                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
2809                                 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
2810                         mLockscreenGestureLogger
2811                                 .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
2812                         startUnlockHintAnimation();
2813                     }
2814                 }
2815                 break;
2816             case StatusBarState.SHADE_LOCKED:
2817                 if (!mQsController.getExpanded()) {
2818                     mStatusBarStateController.setState(KEYGUARD);
2819                 }
2820                 break;
2821         }
2822     }
2823 
setPanelAlpha(int alpha, boolean animate)2824     public void setPanelAlpha(int alpha, boolean animate) {
2825         if (mPanelAlpha != alpha) {
2826             mPanelAlpha = alpha;
2827             PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
2828                             ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
2829                     animate);
2830         }
2831     }
2832 
setPanelAlphaEndAction(Runnable r)2833     public void setPanelAlphaEndAction(Runnable r) {
2834         mPanelAlphaEndAction = r;
2835     }
2836 
setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)2837     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
2838         mHeadsUpAnimatingAway = headsUpAnimatingAway;
2839         mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
2840         updateVisibility();
2841     }
2842 
2843     /** Set whether the bouncer is showing. */
setBouncerShowing(boolean bouncerShowing)2844     public void setBouncerShowing(boolean bouncerShowing) {
2845         mBouncerShowing = bouncerShowing;
2846         updateVisibility();
2847     }
2848 
shouldPanelBeVisible()2849     private boolean shouldPanelBeVisible() {
2850         boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode;
2851         return headsUpVisible || isExpanded() || mBouncerShowing;
2852     }
2853 
setHeadsUpManager(HeadsUpManagerPhone headsUpManager)2854     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
2855         mHeadsUpManager = headsUpManager;
2856         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
2857         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
2858                 mNotificationStackScrollLayoutController.getHeadsUpCallback(),
2859                 NotificationPanelViewController.this);
2860     }
2861 
setTrackedHeadsUp(ExpandableNotificationRow pickedChild)2862     public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
2863         if (pickedChild != null) {
2864             updateTrackingHeadsUp(pickedChild);
2865             mExpandingFromHeadsUp = true;
2866         }
2867         // otherwise we update the state when the expansion is finished
2868     }
2869 
onClosingFinished()2870     private void onClosingFinished() {
2871         mOpenCloseListener.onClosingFinished();
2872         setClosingWithAlphaFadeout(false);
2873         mMediaHierarchyManager.closeGuts();
2874     }
2875 
setClosingWithAlphaFadeout(boolean closing)2876     private void setClosingWithAlphaFadeout(boolean closing) {
2877         mClosingWithAlphaFadeOut = closing;
2878         mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing);
2879     }
2880 
updateExpandedHeight(float expandedHeight)2881     private void updateExpandedHeight(float expandedHeight) {
2882         if (mTracking) {
2883             mNotificationStackScrollLayoutController
2884                     .setExpandingVelocity(getCurrentExpandVelocity());
2885         }
2886         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
2887             // The expandedHeight is always the full panel Height when bypassing
2888             expandedHeight = getMaxPanelHeight();
2889         }
2890         mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
2891         updateKeyguardBottomAreaAlpha();
2892         updateStatusBarIcons();
2893     }
2894 
updateStatusBarIcons()2895     private void updateStatusBarIcons() {
2896         boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
2897         if (showIconsWhenExpanded && isOnKeyguard()) {
2898             showIconsWhenExpanded = false;
2899         }
2900         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
2901             mShowIconsWhenExpanded = showIconsWhenExpanded;
2902             mCommandQueue.recomputeDisableFlags(mDisplayId, false);
2903         }
2904     }
2905 
2906     public int getBarState() {
2907         return mBarState;
2908     }
2909 
2910     private boolean isOnKeyguard() {
2911         return mBarState == KEYGUARD;
2912     }
2913 
2914     /** Called when a HUN is dragged up or down to indicate the starting height for shade motion. */
2915     public void setHeadsUpDraggingStartingHeight(int startHeight) {
2916         mHeadsUpStartHeight = startHeight;
2917         float scrimMinFraction;
2918         if (mSplitShadeEnabled) {
2919             boolean highHun = mHeadsUpStartHeight * 2.5
2920                     >
2921                     (mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)
2922                     ? mSplitShadeFullTransitionDistance : mSplitShadeScrimTransitionDistance);
2923             // if HUN height is higher than 40% of predefined transition distance, it means HUN
2924             // is too high for regular transition. In that case we need to calculate transition
2925             // distance - here we take scrim transition distance as equal to shade transition
2926             // distance. It doesn't result in perfect motion - usually scrim transition distance
2927             // should be longer - but it's good enough for HUN case.
2928             float transitionDistance =
2929                     highHun ? getMaxPanelTransitionDistance() : mSplitShadeFullTransitionDistance;
2930             scrimMinFraction = mHeadsUpStartHeight / transitionDistance;
2931         } else {
2932             int transitionDistance = getMaxPanelHeight();
2933             scrimMinFraction = transitionDistance > 0f
2934                     ? (float) mHeadsUpStartHeight / transitionDistance : 0f;
2935         }
setPanelScrimMinFraction(scrimMinFraction)2936         setPanelScrimMinFraction(scrimMinFraction);
2937     }
2938 
2939     /**
2940      * Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain
2941      * cases, such as if there's a heads-up notification.
2942      */
setPanelScrimMinFraction(float minFraction)2943     private void setPanelScrimMinFraction(float minFraction) {
2944         mMinFraction = minFraction;
2945         mDepthController.setPanelPullDownMinFraction(mMinFraction);
2946         mScrimController.setPanelScrimMinFraction(mMinFraction);
2947     }
2948 
clearNotificationEffects()2949     public void clearNotificationEffects() {
2950         mCentralSurfaces.clearNotificationEffects();
2951     }
2952 
isPanelVisibleBecauseOfHeadsUp()2953     private boolean isPanelVisibleBecauseOfHeadsUp() {
2954         return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
2955                 && mBarState == StatusBarState.SHADE;
2956     }
2957 
hideStatusBarIconsWhenExpanded()2958     public boolean hideStatusBarIconsWhenExpanded() {
2959         if (mIsLaunchAnimationRunning) {
2960             return mHideIconsDuringLaunchAnimation;
2961         }
2962         if (mHeadsUpAppearanceController != null
2963                 && mHeadsUpAppearanceController.shouldBeVisible()) {
2964             return false;
2965         }
2966         return !mShowIconsWhenExpanded;
2967     }
2968 
setTouchAndAnimationDisabled(boolean disabled)2969     public void setTouchAndAnimationDisabled(boolean disabled) {
2970         mTouchDisabled = disabled;
2971         if (mTouchDisabled) {
2972             cancelHeightAnimator();
2973             if (mTracking) {
2974                 onTrackingStopped(true /* expanded */);
2975             }
2976             notifyExpandingFinished();
2977         }
2978         mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
2979     }
2980 
2981     /**
2982      * Sets the dozing state.
2983      *
2984      * @param dozing  {@code true} when dozing.
2985      * @param animate if transition should be animated.
2986      */
setDozing(boolean dozing, boolean animate)2987     public void setDozing(boolean dozing, boolean animate) {
2988         if (dozing == mDozing) return;
2989         mView.setDozing(dozing);
2990         mDozing = dozing;
2991         // TODO (b/) make listeners for this
2992         mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
2993         mKeyguardBottomAreaInteractor.setAnimateDozingTransitions(animate);
2994         mKeyguardStatusBarViewController.setDozing(mDozing);
2995         mQsController.setDozing(mDozing);
2996 
2997         if (dozing) {
2998             mBottomAreaShadeAlphaAnimator.cancel();
2999         }
3000 
3001         if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
3002             updateDozingVisibilities(animate);
3003         }
3004 
3005         final float dozeAmount = dozing ? 1 : 0;
3006         mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate);
3007 
3008         updateKeyguardStatusViewAlignment(animate);
3009     }
3010 
setPulsing(boolean pulsing)3011     public void setPulsing(boolean pulsing) {
3012         mPulsing = pulsing;
3013         final boolean
3014                 animatePulse =
3015                 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
3016         if (animatePulse) {
3017             mAnimateNextPositionUpdate = true;
3018         }
3019         // Do not animate the clock when waking up from a pulse.
3020         // The height callback will take care of pushing the clock to the right position.
3021         if (!mPulsing && !mDozing) {
3022             mAnimateNextPositionUpdate = false;
3023         }
3024         mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
3025 
3026         updateKeyguardStatusViewAlignment(/* animate= */ true);
3027     }
3028 
setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible)3029     public void setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible) {
3030         int ambientIndicationBottomPadding = 0;
3031         if (ambientTextVisible) {
3032             int stackBottom = mNotificationStackScrollLayoutController.getBottom();
3033             ambientIndicationBottomPadding = stackBottom - ambientIndicationTop;
3034         }
3035         if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
3036             mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
3037             updateMaxDisplayedNotifications(true);
3038         }
3039     }
3040 
dozeTimeTick()3041     public void dozeTimeTick() {
3042         mLockIconViewController.dozeTimeTick();
3043         mKeyguardStatusViewController.dozeTimeTick();
3044         if (mInterpolatedDarkAmount > 0) {
3045             positionClockAndNotifications();
3046         }
3047     }
3048 
setStatusAccessibilityImportance(int mode)3049     public void setStatusAccessibilityImportance(int mode) {
3050         mKeyguardStatusViewController.setStatusAccessibilityImportance(mode);
3051     }
3052 
3053     //TODO(b/254875405): this should be removed.
getKeyguardBottomAreaView()3054     public KeyguardBottomAreaView getKeyguardBottomAreaView() {
3055         return mKeyguardBottomArea;
3056     }
3057 
applyLaunchAnimationProgress(float linearProgress)3058     public void applyLaunchAnimationProgress(float linearProgress) {
3059         boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS,
3060                 linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
3061         if (hideIcons != mHideIconsDuringLaunchAnimation) {
3062             mHideIconsDuringLaunchAnimation = hideIcons;
3063             if (!hideIcons) {
3064                 mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
3065             }
3066         }
3067     }
3068 
addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3069     public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
3070         mTrackingHeadsUpListeners.add(listener);
3071     }
3072 
removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3073     public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
3074         mTrackingHeadsUpListeners.remove(listener);
3075     }
3076 
setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)3077     public void setHeadsUpAppearanceController(
3078             HeadsUpAppearanceController headsUpAppearanceController) {
3079         mHeadsUpAppearanceController = headsUpAppearanceController;
3080     }
3081 
3082     /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
startBouncerPreHideAnimation()3083     public void startBouncerPreHideAnimation() {
3084         if (mKeyguardQsUserSwitchController != null) {
3085             mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
3086                     mBarState,
3087                     true /* keyguardFadingAway */,
3088                     false /* goingToFullShade */,
3089                     mBarState);
3090         }
3091         if (mKeyguardUserSwitcherController != null) {
3092             mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
3093                     mBarState,
3094                     true /* keyguardFadingAway */,
3095                     false /* goingToFullShade */,
3096                     mBarState);
3097         }
3098     }
3099 
3100     /** Updates the views to the initial state for the fold to AOD animation. */
prepareFoldToAodAnimation()3101     public void prepareFoldToAodAnimation() {
3102         // Force show AOD UI even if we are not locked
3103         showAodUi();
3104 
3105         // Move the content of the AOD all the way to the left
3106         // so we can animate to the initial position
3107         final int translationAmount = mView.getResources().getDimensionPixelSize(
3108                 R.dimen.below_clock_padding_start);
3109         mView.setTranslationX(-translationAmount);
3110         mView.setAlpha(0);
3111     }
3112 
3113     /**
3114      * Starts fold to AOD animation.
3115      *
3116      * @param startAction  invoked when the animation starts.
3117      * @param endAction    invoked when the animation finishes, also if it was cancelled.
3118      * @param cancelAction invoked when the animation is cancelled, before endAction.
3119      */
startFoldToAodAnimation(Runnable startAction, Runnable endAction, Runnable cancelAction)3120     public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
3121             Runnable cancelAction) {
3122         final ViewPropertyAnimator viewAnimator = mView.animate();
3123         viewAnimator.cancel();
3124         viewAnimator
3125                 .translationX(0)
3126                 .alpha(1f)
3127                 .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
3128                 .setInterpolator(EMPHASIZED_DECELERATE)
3129                 .setListener(new AnimatorListenerAdapter() {
3130                     @Override
3131                     public void onAnimationStart(Animator animation) {
3132                         startAction.run();
3133                     }
3134 
3135                     @Override
3136                     public void onAnimationCancel(Animator animation) {
3137                         cancelAction.run();
3138                     }
3139 
3140                     @Override
3141                     public void onAnimationEnd(Animator animation) {
3142                         endAction.run();
3143 
3144                         viewAnimator.setListener(null);
3145                         viewAnimator.setUpdateListener(null);
3146                     }
3147                 })
3148                 .setUpdateListener(anim ->
3149                         mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction()))
3150                 .start();
3151     }
3152 
3153     /** Cancels fold to AOD transition and resets view state. */
cancelFoldToAodAnimation()3154     public void cancelFoldToAodAnimation() {
3155         cancelAnimation();
3156         resetAlpha();
3157         resetTranslation();
3158     }
3159 
setImportantForAccessibility(int mode)3160     public void setImportantForAccessibility(int mode) {
3161         mView.setImportantForAccessibility(mode);
3162     }
3163 
3164     /**
3165      * Do not let the user drag the shade up and down for the current touch session.
3166      * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
3167      */
blockExpansionForCurrentTouch()3168     public void blockExpansionForCurrentTouch() {
3169         mBlockingExpansionForCurrentTouch = mTracking;
3170     }
3171 
3172     @Override
dump(PrintWriter pw, String[] args)3173     public void dump(PrintWriter pw, String[] args) {
3174         pw.println(TAG + ":");
3175         IndentingPrintWriter ipw = asIndenting(pw);
3176         ipw.increaseIndent();
3177 
3178         ipw.print("mDownTime="); ipw.println(mDownTime);
3179         ipw.print("mTouchSlopExceededBeforeDown="); ipw.println(mTouchSlopExceededBeforeDown);
3180         ipw.print("mIsLaunchAnimationRunning="); ipw.println(mIsLaunchAnimationRunning);
3181         ipw.print("mOverExpansion="); ipw.println(mOverExpansion);
3182         ipw.print("mExpandedHeight="); ipw.println(mExpandedHeight);
3183         ipw.print("mTracking="); ipw.println(mTracking);
3184         ipw.print("mHintAnimationRunning="); ipw.println(mHintAnimationRunning);
3185         ipw.print("mExpanding="); ipw.println(mExpanding);
3186         ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled);
3187         ipw.print("mKeyguardNotificationBottomPadding=");
3188         ipw.println(mKeyguardNotificationBottomPadding);
3189         ipw.print("mKeyguardNotificationTopPadding="); ipw.println(mKeyguardNotificationTopPadding);
3190         ipw.print("mMaxAllowedKeyguardNotifications=");
3191         ipw.println(mMaxAllowedKeyguardNotifications);
3192         ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
3193         ipw.print("mPanelExpanded="); ipw.println(mPanelExpanded);
3194         ipw.print("mKeyguardQsUserSwitchEnabled="); ipw.println(mKeyguardQsUserSwitchEnabled);
3195         ipw.print("mKeyguardUserSwitcherEnabled="); ipw.println(mKeyguardUserSwitcherEnabled);
3196         ipw.print("mDozing="); ipw.println(mDozing);
3197         ipw.print("mDozingOnDown="); ipw.println(mDozingOnDown);
3198         ipw.print("mBouncerShowing="); ipw.println(mBouncerShowing);
3199         ipw.print("mBarState="); ipw.println(mBarState);
3200         ipw.print("mStatusBarMinHeight="); ipw.println(mStatusBarMinHeight);
3201         ipw.print("mStatusBarHeaderHeightKeyguard="); ipw.println(mStatusBarHeaderHeightKeyguard);
3202         ipw.print("mOverStretchAmount="); ipw.println(mOverStretchAmount);
3203         ipw.print("mDownX="); ipw.println(mDownX);
3204         ipw.print("mDownY="); ipw.println(mDownY);
3205         ipw.print("mDisplayTopInset="); ipw.println(mDisplayTopInset);
3206         ipw.print("mDisplayRightInset="); ipw.println(mDisplayRightInset);
3207         ipw.print("mDisplayLeftInset="); ipw.println(mDisplayLeftInset);
3208         ipw.print("mIsExpanding="); ipw.println(mIsExpanding);
3209         ipw.print("mHeaderDebugInfo="); ipw.println(mHeaderDebugInfo);
3210         ipw.print("mHeadsUpStartHeight="); ipw.println(mHeadsUpStartHeight);
3211         ipw.print("mListenForHeadsUp="); ipw.println(mListenForHeadsUp);
3212         ipw.print("mNavigationBarBottomHeight="); ipw.println(mNavigationBarBottomHeight);
3213         ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
3214         ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
3215         ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
3216         ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
3217         ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
3218         ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding);
3219         ipw.print("mAmbientIndicationBottomPadding="); ipw.println(mAmbientIndicationBottomPadding);
3220         ipw.print("mIsFullWidth="); ipw.println(mIsFullWidth);
3221         ipw.print("mBlockingExpansionForCurrentTouch=");
3222         ipw.println(mBlockingExpansionForCurrentTouch);
3223         ipw.print("mExpectingSynthesizedDown="); ipw.println(mExpectingSynthesizedDown);
3224         ipw.print("mLastEventSynthesizedDown="); ipw.println(mLastEventSynthesizedDown);
3225         ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
3226         ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
3227         ipw.print("mPulsing="); ipw.println(mPulsing);
3228         ipw.print("mHideIconsDuringLaunchAnimation="); ipw.println(mHideIconsDuringLaunchAnimation);
3229         ipw.print("mStackScrollerMeasuringPass="); ipw.println(mStackScrollerMeasuringPass);
3230         ipw.print("mPanelAlpha="); ipw.println(mPanelAlpha);
3231         ipw.print("mBottomAreaShadeAlpha="); ipw.println(mBottomAreaShadeAlpha);
3232         ipw.print("mHeadsUpInset="); ipw.println(mHeadsUpInset);
3233         ipw.print("mHeadsUpPinnedMode="); ipw.println(mHeadsUpPinnedMode);
3234         ipw.print("mAllowExpandForSmallExpansion="); ipw.println(mAllowExpandForSmallExpansion);
3235         ipw.print("mMaxOverscrollAmountForPulse="); ipw.println(mMaxOverscrollAmountForPulse);
3236         ipw.print("mIsPanelCollapseOnQQS="); ipw.println(mIsPanelCollapseOnQQS);
3237         ipw.print("mKeyguardOnlyContentAlpha="); ipw.println(mKeyguardOnlyContentAlpha);
3238         ipw.print("mKeyguardOnlyTransitionTranslationY=");
3239         ipw.println(mKeyguardOnlyTransitionTranslationY);
3240         ipw.print("mUdfpsMaxYBurnInOffset="); ipw.println(mUdfpsMaxYBurnInOffset);
3241         ipw.print("mIsGestureNavigation="); ipw.println(mIsGestureNavigation);
3242         ipw.print("mOldLayoutDirection="); ipw.println(mOldLayoutDirection);
3243         ipw.print("mMinFraction="); ipw.println(mMinFraction);
3244         ipw.print("mStatusViewCentered="); ipw.println(mStatusViewCentered);
3245         ipw.print("mSplitShadeFullTransitionDistance=");
3246         ipw.println(mSplitShadeFullTransitionDistance);
3247         ipw.print("mSplitShadeScrimTransitionDistance=");
3248         ipw.println(mSplitShadeScrimTransitionDistance);
3249         ipw.print("mMinExpandHeight="); ipw.println(mMinExpandHeight);
3250         ipw.print("mPanelUpdateWhenAnimatorEnds="); ipw.println(mPanelUpdateWhenAnimatorEnds);
3251         ipw.print("mHasVibratedOnOpen="); ipw.println(mHasVibratedOnOpen);
3252         ipw.print("mFixedDuration="); ipw.println(mFixedDuration);
3253         ipw.print("mPanelFlingOvershootAmount="); ipw.println(mPanelFlingOvershootAmount);
3254         ipw.print("mLastGesturedOverExpansion="); ipw.println(mLastGesturedOverExpansion);
3255         ipw.print("mIsSpringBackAnimation="); ipw.println(mIsSpringBackAnimation);
3256         ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled);
3257         ipw.print("mHintDistance="); ipw.println(mHintDistance);
3258         ipw.print("mInitialOffsetOnTouch="); ipw.println(mInitialOffsetOnTouch);
3259         ipw.print("mCollapsedAndHeadsUpOnDown="); ipw.println(mCollapsedAndHeadsUpOnDown);
3260         ipw.print("mExpandedFraction="); ipw.println(mExpandedFraction);
3261         ipw.print("mExpansionDragDownAmountPx="); ipw.println(mExpansionDragDownAmountPx);
3262         ipw.print("mPanelClosedOnDown="); ipw.println(mPanelClosedOnDown);
3263         ipw.print("mHasLayoutedSinceDown="); ipw.println(mHasLayoutedSinceDown);
3264         ipw.print("mUpdateFlingVelocity="); ipw.println(mUpdateFlingVelocity);
3265         ipw.print("mUpdateFlingOnLayout="); ipw.println(mUpdateFlingOnLayout);
3266         ipw.print("mClosing="); ipw.println(mClosing);
3267         ipw.print("mTouchSlopExceeded="); ipw.println(mTouchSlopExceeded);
3268         ipw.print("mTrackingPointer="); ipw.println(mTrackingPointer);
3269         ipw.print("mTouchSlop="); ipw.println(mTouchSlop);
3270         ipw.print("mSlopMultiplier="); ipw.println(mSlopMultiplier);
3271         ipw.print("mTouchAboveFalsingThreshold="); ipw.println(mTouchAboveFalsingThreshold);
3272         ipw.print("mTouchStartedInEmptyArea="); ipw.println(mTouchStartedInEmptyArea);
3273         ipw.print("mMotionAborted="); ipw.println(mMotionAborted);
3274         ipw.print("mUpwardsWhenThresholdReached="); ipw.println(mUpwardsWhenThresholdReached);
3275         ipw.print("mAnimatingOnDown="); ipw.println(mAnimatingOnDown);
3276         ipw.print("mHandlingPointerUp="); ipw.println(mHandlingPointerUp);
3277         ipw.print("mInstantExpanding="); ipw.println(mInstantExpanding);
3278         ipw.print("mAnimateAfterExpanding="); ipw.println(mAnimateAfterExpanding);
3279         ipw.print("mIsFlinging="); ipw.println(mIsFlinging);
3280         ipw.print("mViewName="); ipw.println(mViewName);
3281         ipw.print("mInitialExpandY="); ipw.println(mInitialExpandY);
3282         ipw.print("mInitialExpandX="); ipw.println(mInitialExpandX);
3283         ipw.print("mTouchDisabled="); ipw.println(mTouchDisabled);
3284         ipw.print("mInitialTouchFromKeyguard="); ipw.println(mInitialTouchFromKeyguard);
3285         ipw.print("mNextCollapseSpeedUpFactor="); ipw.println(mNextCollapseSpeedUpFactor);
3286         ipw.print("mGestureWaitForTouchSlop="); ipw.println(mGestureWaitForTouchSlop);
3287         ipw.print("mIgnoreXTouchSlop="); ipw.println(mIgnoreXTouchSlop);
3288         ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
3289         ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
3290         ipw.println("gestureExclusionRect:" + calculateGestureExclusionRect());
3291         new DumpsysTableLogger(
3292                 TAG,
3293                 NPVCDownEventState.TABLE_HEADERS,
3294                 mLastDownEvents.toList()
3295         ).printTableData(ipw);
3296     }
3297 
3298 
createRemoteInputDelegate()3299     public RemoteInputController.Delegate createRemoteInputDelegate() {
3300         return mNotificationStackScrollLayoutController.createDelegate();
3301     }
3302 
hasPulsingNotifications()3303     public boolean hasPulsingNotifications() {
3304         return mNotificationListContainer.hasPulsingNotifications();
3305     }
3306 
getActivatedChild()3307     public ActivatableNotificationView getActivatedChild() {
3308         return mNotificationStackScrollLayoutController.getActivatedChild();
3309     }
3310 
setActivatedChild(ActivatableNotificationView o)3311     public void setActivatedChild(ActivatableNotificationView o) {
3312         mNotificationStackScrollLayoutController.setActivatedChild(o);
3313     }
3314 
runAfterAnimationFinished(Runnable r)3315     public void runAfterAnimationFinished(Runnable r) {
3316         mNotificationStackScrollLayoutController.runAfterAnimationFinished(r);
3317     }
3318 
3319     /**
3320      * Initialize objects instead of injecting to avoid circular dependencies.
3321      *
3322      * @param hideExpandedRunnable a runnable to run when we need to hide the expanded panel.
3323      */
initDependencies( CentralSurfaces centralSurfaces, GestureRecorder recorder, Runnable hideExpandedRunnable, NotificationShelfController notificationShelfController)3324     public void initDependencies(
3325             CentralSurfaces centralSurfaces,
3326             GestureRecorder recorder,
3327             Runnable hideExpandedRunnable,
3328             NotificationShelfController notificationShelfController) {
3329         // TODO(b/254859580): this can be injected.
3330         mCentralSurfaces = centralSurfaces;
3331 
3332         mGestureRecorder = recorder;
3333         mHideExpandedRunnable = hideExpandedRunnable;
3334         mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
3335         mNotificationShelfController = notificationShelfController;
3336         mLockscreenShadeTransitionController.bindController(notificationShelfController);
3337         updateMaxDisplayedNotifications(true);
3338     }
3339 
resetTranslation()3340     public void resetTranslation() {
3341         mView.setTranslationX(0f);
3342     }
3343 
resetAlpha()3344     public void resetAlpha() {
3345         mView.setAlpha(1f);
3346     }
3347 
fadeOut(long startDelayMs, long durationMs, Runnable endAction)3348     public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
3349         mView.animate().cancel();
3350         return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
3351                 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
3352                 endAction);
3353     }
3354 
resetViewGroupFade()3355     public void resetViewGroupFade() {
3356         ViewGroupFadeHelper.reset(mView);
3357     }
3358 
addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3359     void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
3360         mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
3361     }
3362 
removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3363     void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
3364         mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
3365     }
3366 
setHeaderDebugInfo(String text)3367     public void setHeaderDebugInfo(String text) {
3368         if (DEBUG_DRAWABLE) mHeaderDebugInfo = text;
3369     }
3370 
getHeaderDebugInfo()3371     public String getHeaderDebugInfo() {
3372         return mHeaderDebugInfo;
3373     }
3374 
onThemeChanged()3375     public void onThemeChanged() {
3376         mConfigurationListener.onThemeChanged();
3377     }
3378 
3379     @VisibleForTesting
getTouchHandler()3380     TouchHandler getTouchHandler() {
3381         return mTouchHandler;
3382     }
3383 
getNotificationStackScrollLayoutController()3384     public NotificationStackScrollLayoutController getNotificationStackScrollLayoutController() {
3385         return mNotificationStackScrollLayoutController;
3386     }
3387 
disable(int state1, int state2, boolean animated)3388     public void disable(int state1, int state2, boolean animated) {
3389         mShadeHeaderController.disable(state1, state2, animated);
3390     }
3391 
3392     /**
3393      * Close the keyguard user switcher if it is open and capable of closing.
3394      *
3395      * Has no effect if user switcher isn't supported, if the user switcher is already closed, or
3396      * if the user switcher uses "simple" mode. The simple user switcher cannot be closed.
3397      *
3398      * @return true if the keyguard user switcher was open, and is now closed
3399      */
closeUserSwitcherIfOpen()3400     public boolean closeUserSwitcherIfOpen() {
3401         if (mKeyguardUserSwitcherController != null) {
3402             return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(
3403                     true /* animate */);
3404         }
3405         return false;
3406     }
3407 
updateUserSwitcherFlags()3408     private void updateUserSwitcherFlags() {
3409         mKeyguardUserSwitcherEnabled = mResources.getBoolean(
3410                 com.android.internal.R.bool.config_keyguardUserSwitcher);
3411         mKeyguardQsUserSwitchEnabled =
3412                 mKeyguardUserSwitcherEnabled
3413                         && mFeatureFlags.isEnabled(Flags.QS_USER_DETAIL_SHORTCUT);
3414     }
3415 
registerSettingsChangeListener()3416     private void registerSettingsChangeListener() {
3417         mContentResolver.registerContentObserver(
3418                 Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED),
3419                 /* notifyForDescendants */ false,
3420                 mSettingsChangeObserver
3421         );
3422     }
3423 
3424     /** Updates notification panel-specific flags on {@link SysUiState}. */
updateSystemUiStateFlags()3425     public void updateSystemUiStateFlags() {
3426         if (SysUiState.DEBUG) {
3427             Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
3428                     + isFullyExpanded() + " inQs=" + mQsController.getExpanded());
3429         }
3430         mSysUiState
3431                 .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, getExpandedFraction() > 0)
3432                 .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
3433                         isFullyExpanded() && !mQsController.getExpanded())
3434                 .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
3435                         isFullyExpanded() && mQsController.getExpanded()).commitUpdate(mDisplayId);
3436     }
3437 
debugLog(String fmt, Object... args)3438     private void debugLog(String fmt, Object... args) {
3439         if (DEBUG_LOGCAT) {
3440             Log.d(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
3441         }
3442     }
3443 
3444     @VisibleForTesting
notifyExpandingStarted()3445     void notifyExpandingStarted() {
3446         if (!mExpanding) {
3447             mExpanding = true;
3448             mIsExpanding = true;
3449             mQsController.onExpandingStarted(mQsController.getFullyExpanded());
3450         }
3451     }
3452 
notifyExpandingFinished()3453     void notifyExpandingFinished() {
3454         endClosing();
3455         if (mExpanding) {
3456             mExpanding = false;
3457             onExpandingFinished();
3458         }
3459     }
3460 
getTouchSlop(MotionEvent event)3461     float getTouchSlop(MotionEvent event) {
3462         // Adjust the touch slop if another gesture may be being performed.
3463         return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
3464                 ? mTouchSlop * mSlopMultiplier
3465                 : mTouchSlop;
3466     }
3467 
addMovement(MotionEvent event)3468     private void addMovement(MotionEvent event) {
3469         // Add movement to velocity tracker using raw screen X and Y coordinates instead
3470         // of window coordinates because the window frame may be moving at the same time.
3471         float deltaX = event.getRawX() - event.getX();
3472         float deltaY = event.getRawY() - event.getY();
3473         event.offsetLocation(deltaX, deltaY);
3474         mVelocityTracker.addMovement(event);
3475         event.offsetLocation(-deltaX, -deltaY);
3476     }
3477 
3478     /** If the latency tracker is enabled, begins tracking expand latency. */
startExpandLatencyTracking()3479     public void startExpandLatencyTracking() {
3480         if (mLatencyTracker.isEnabled()) {
3481             mLatencyTracker.onActionStart(LatencyTracker.ACTION_EXPAND_PANEL);
3482             mExpandLatencyTracking = true;
3483         }
3484     }
3485 
startOpening(MotionEvent event)3486     private void startOpening(MotionEvent event) {
3487         updatePanelExpansionAndVisibility();
3488         //TODO: keyguard opens QS a different way; log that too?
3489 
3490         // Log the position of the swipe that opened the panel
3491         float width = mCentralSurfaces.getDisplayWidth();
3492         float height = mCentralSurfaces.getDisplayHeight();
3493         int rot = mCentralSurfaces.getRotation();
3494 
3495         mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
3496                 (int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
3497         mLockscreenGestureLogger
3498                 .log(LockscreenUiEvent.LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND);
3499     }
3500 
3501     /**
3502      * Maybe vibrate as panel is opened.
3503      *
3504      * @param openingWithTouch Whether the panel is being opened with touch. If the panel is
3505      *                         instead being opened programmatically (such as by the open panel
3506      *                         gesture), we always play haptic.
3507      */
maybeVibrateOnOpening(boolean openingWithTouch)3508     private void maybeVibrateOnOpening(boolean openingWithTouch) {
3509         if (mVibrateOnOpening) {
3510             if (!openingWithTouch || !mHasVibratedOnOpen) {
3511                 mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
3512                 mHasVibratedOnOpen = true;
3513                 mShadeLog.v("Vibrating on opening, mHasVibratedOnOpen=true");
3514             }
3515         }
3516     }
3517 
3518     /**
3519      * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
3520      * horizontal direction
3521      */
isDirectionUpwards(float x, float y)3522     private boolean isDirectionUpwards(float x, float y) {
3523         float xDiff = x - mInitialExpandX;
3524         float yDiff = y - mInitialExpandY;
3525         if (yDiff >= 0) {
3526             return false;
3527         }
3528         return Math.abs(yDiff) >= Math.abs(xDiff);
3529     }
3530 
3531     /** Called when a MotionEvent is about to trigger Shade expansion. */
startExpandMotion(float newX, float newY, boolean startTracking, float expandedHeight)3532     public void startExpandMotion(float newX, float newY, boolean startTracking,
3533             float expandedHeight) {
3534         if (!mHandlingPointerUp && !mStatusBarStateController.isDozing()) {
3535             mQsController.beginJankMonitoring(isFullyCollapsed());
3536         }
3537         mInitialOffsetOnTouch = expandedHeight;
3538         if (!mTracking || isFullyCollapsed()) {
3539             mInitialExpandY = newY;
3540             mInitialExpandX = newX;
3541         } else {
3542             mShadeLog.d("not setting mInitialExpandY in startExpandMotion");
3543         }
3544         mInitialTouchFromKeyguard = mKeyguardStateController.isShowing();
3545         if (startTracking) {
3546             mTouchSlopExceeded = true;
3547             mShadeHeightLogger.logFunctionCall("startExpandMotion");
3548             setExpandedHeight(mInitialOffsetOnTouch);
3549             onTrackingStarted();
3550         }
3551     }
3552 
endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel)3553     private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
3554         mTrackingPointer = -1;
3555         mAmbientState.setSwipingUp(false);
3556         if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
3557                 || Math.abs(y - mInitialExpandY) > mTouchSlop
3558                 || (!isFullyExpanded() && !isFullyCollapsed())
3559                 || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
3560             mVelocityTracker.computeCurrentVelocity(1000);
3561             float vel = mVelocityTracker.getYVelocity();
3562             float vectorVel = (float) Math.hypot(
3563                     mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
3564 
3565             final boolean onKeyguard = mKeyguardStateController.isShowing();
3566             final boolean expand;
3567             if (mKeyguardStateController.isKeyguardFadingAway()
3568                     || (mInitialTouchFromKeyguard && !onKeyguard)) {
3569                 // Don't expand for any touches that started from the keyguard and ended after the
3570                 // keyguard is gone.
3571                 expand = false;
3572             } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
3573                 if (onKeyguard) {
3574                     expand = true;
3575                 } else if (mCentralSurfaces.isBouncerShowingOverDream()) {
3576                     expand = false;
3577                 } else {
3578                     // If we get a cancel, put the shade back to the state it was in when the
3579                     // gesture started
3580                     expand = !mPanelClosedOnDown;
3581                 }
3582             } else {
3583                 expand = flingExpands(vel, vectorVel, x, y);
3584             }
3585 
3586             mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
3587                     mCentralSurfaces.isWakeUpComingFromTouch());
3588             // Log collapse gesture if on lock screen.
3589             if (!expand && onKeyguard) {
3590                 float displayDensity = mCentralSurfaces.getDisplayDensity();
3591                 int heightDp = (int) Math.abs((y - mInitialExpandY) / displayDensity);
3592                 int velocityDp = (int) Math.abs(vel / displayDensity);
3593                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
3594                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
3595             }
3596             @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
3597                     : y - mInitialExpandY > 0 ? QUICK_SETTINGS
3598                             : (mKeyguardStateController.canDismissLockScreen()
3599                                     ? UNLOCK : BOUNCER_UNLOCK);
3600 
3601             // don't fling while in keyguard to avoid jump in shade expand animation;
3602             // touch has been intercepted already so flinging here is redundant
3603             if (mBarState == KEYGUARD && mExpandedFraction >= 1.0) {
3604                 mShadeLog.d("NPVC endMotionEvent - skipping fling on keyguard");
3605             } else {
3606                 fling(vel, expand, isFalseTouch(x, y, interactionType));
3607             }
3608             onTrackingStopped(expand);
3609             mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
3610             if (mUpdateFlingOnLayout) {
3611                 mUpdateFlingVelocity = vel;
3612             }
3613         } else if (!mCentralSurfaces.isBouncerShowing()
3614                 && !mAlternateBouncerInteractor.isVisibleState()
3615                 && !mKeyguardStateController.isKeyguardGoingAway()) {
3616             onEmptySpaceClick();
3617             onTrackingStopped(true);
3618         }
3619         mVelocityTracker.clear();
3620     }
3621 
getCurrentExpandVelocity()3622     private float getCurrentExpandVelocity() {
3623         mVelocityTracker.computeCurrentVelocity(1000);
3624         return mVelocityTracker.getYVelocity();
3625     }
3626 
endClosing()3627     private void endClosing() {
3628         if (mClosing) {
3629             setClosing(false);
3630             onClosingFinished();
3631         }
3632     }
3633 
3634     /**
3635      * @param x the final x-coordinate when the finger was lifted
3636      * @param y the final y-coordinate when the finger was lifted
3637      * @return whether this motion should be regarded as a false touch
3638      */
isFalseTouch(float x, float y, @Classifier.InteractionType int interactionType)3639     private boolean isFalseTouch(float x, float y,
3640             @Classifier.InteractionType int interactionType) {
3641         if (mFalsingManager.isClassifierEnabled()) {
3642             return mFalsingManager.isFalseTouch(interactionType);
3643         }
3644         if (!mTouchAboveFalsingThreshold) {
3645             return true;
3646         }
3647         if (mUpwardsWhenThresholdReached) {
3648             return false;
3649         }
3650         return !isDirectionUpwards(x, y);
3651     }
3652 
fling(float vel, boolean expand, boolean expandBecauseOfFalsing)3653     private void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
3654         fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
3655     }
3656 
fling(float vel, boolean expand, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)3657     private void fling(float vel, boolean expand, float collapseSpeedUpFactor,
3658             boolean expandBecauseOfFalsing) {
3659         float target = expand ? getMaxPanelTransitionDistance() : 0;
3660         if (!expand) {
3661             setClosing(true);
3662         }
3663         flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
3664     }
3665 
springBack()3666     private void springBack() {
3667         if (mOverExpansion == 0) {
3668             onFlingEnd(false /* cancelled */);
3669             return;
3670         }
3671         mIsSpringBackAnimation = true;
3672         ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
3673         animator.addUpdateListener(
3674                 animation -> setOverExpansionInternal((float) animation.getAnimatedValue(),
3675                         false /* isFromGesture */));
3676         animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
3677         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3678         animator.addListener(new AnimatorListenerAdapter() {
3679             private boolean mCancelled;
3680 
3681             @Override
3682             public void onAnimationCancel(Animator animation) {
3683                 mCancelled = true;
3684             }
3685 
3686             @Override
3687             public void onAnimationEnd(Animator animation) {
3688                 mIsSpringBackAnimation = false;
3689                 onFlingEnd(mCancelled);
3690             }
3691         });
3692         setAnimator(animator);
3693         animator.start();
3694     }
3695 
3696     @VisibleForTesting
setExpandedHeight(float height)3697     void setExpandedHeight(float height) {
3698         debugLog("setExpandedHeight(%.1f)", height);
3699         mShadeHeightLogger.logFunctionCall("setExpandedHeight");
3700         setExpandedHeightInternal(height);
3701     }
3702 
3703     /** Try to set expanded height to max. */
updateExpandedHeightToMaxHeight()3704     void updateExpandedHeightToMaxHeight() {
3705         float currentMaxPanelHeight = getMaxPanelHeight();
3706 
3707         if (isFullyCollapsed()) {
3708             return;
3709         }
3710 
3711         if (currentMaxPanelHeight == mExpandedHeight) {
3712             return;
3713         }
3714 
3715         if (mTracking && !(mBlockingExpansionForCurrentTouch
3716                 || mQsController.isTrackingBlocked())) {
3717             return;
3718         }
3719 
3720         if (mHeightAnimator != null && !mIsSpringBackAnimation) {
3721             mPanelUpdateWhenAnimatorEnds = true;
3722             return;
3723         }
3724 
3725         mShadeHeightLogger.logFunctionCall("updateExpandedHeightToMaxHeight");
3726         setExpandedHeight(currentMaxPanelHeight);
3727     }
3728 
setExpandedHeightInternal(float h)3729     private void setExpandedHeightInternal(float h) {
3730         mShadeHeightLogger.logSetExpandedHeightInternal(h, mSystemClock.currentTimeMillis());
3731 
3732         if (isNaN(h)) {
3733             Log.wtf(TAG, "ExpandedHeight set to NaN");
3734         }
3735         mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> {
3736             if (mExpandLatencyTracking && h != 0f) {
3737                 DejankUtils.postAfterTraversal(
3738                         () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
3739                 mExpandLatencyTracking = false;
3740             }
3741             float maxPanelHeight = getMaxPanelTransitionDistance();
3742             if (mHeightAnimator == null) {
3743                 // Split shade has its own overscroll logic
3744                 if (mTracking) {
3745                     float overExpansionPixels = Math.max(0, h - maxPanelHeight);
3746                     setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
3747                 }
3748             }
3749             mExpandedHeight = Math.min(h, maxPanelHeight);
3750             // If we are closing the panel and we are almost there due to a slow decelerating
3751             // interpolator, abort the animation.
3752             if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
3753                 mExpandedHeight = 0f;
3754                 if (mHeightAnimator != null) {
3755                     mHeightAnimator.end();
3756                 }
3757             }
3758             mExpandedFraction = Math.min(1f,
3759                     maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
3760             mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
3761             mExpansionDragDownAmountPx = h;
3762             mAmbientState.setExpansionFraction(mExpandedFraction);
3763             onHeightUpdated(mExpandedHeight);
3764             updatePanelExpansionAndVisibility();
3765         });
3766     }
3767 
3768     /**
3769      * Set the current overexpansion
3770      *
3771      * @param overExpansion the amount of overexpansion to apply
3772      * @param isFromGesture is this amount from a gesture and needs to be rubberBanded?
3773      */
setOverExpansionInternal(float overExpansion, boolean isFromGesture)3774     private void setOverExpansionInternal(float overExpansion, boolean isFromGesture) {
3775         if (!isFromGesture) {
3776             mLastGesturedOverExpansion = -1;
3777             setOverExpansion(overExpansion);
3778         } else if (mLastGesturedOverExpansion != overExpansion) {
3779             mLastGesturedOverExpansion = overExpansion;
3780             final float heightForFullOvershoot = mView.getHeight() / 3.0f;
3781             float newExpansion = MathUtils.saturate(overExpansion / heightForFullOvershoot);
3782             newExpansion = Interpolators.getOvershootInterpolation(newExpansion);
3783             setOverExpansion(newExpansion * mPanelFlingOvershootAmount * 2.0f);
3784         }
3785     }
3786 
3787     /** Sets the expanded height relative to a number from 0 to 1. */
setExpandedFraction(float frac)3788     public void setExpandedFraction(float frac) {
3789         final int maxDist = getMaxPanelTransitionDistance();
3790         mShadeHeightLogger.logFunctionCall("setExpandedFraction");
3791         setExpandedHeight(maxDist * frac);
3792     }
3793 
getExpandedHeight()3794     float getExpandedHeight() {
3795         return mExpandedHeight;
3796     }
3797 
getExpandedFraction()3798     float getExpandedFraction() {
3799         return mExpandedFraction;
3800     }
3801 
3802     /**
3803      * This method should not be used anymore, you should probably use {@link #isShadeFullyOpen()}
3804      * instead. It was overused as indicating if shade is open or we're on keyguard/AOD.
3805      * Moving forward we should be explicit about the what state we're checking.
3806      * @return if panel is covering the screen, which means we're in expanded shade or keyguard/AOD
3807      *
3808      * @deprecated depends on the state you check, use {@link #isShadeFullyOpen()},
3809      * {@link #isOnAod()}, {@link #isOnKeyguard()} instead.
3810      */
3811     @Deprecated
isFullyExpanded()3812     public boolean isFullyExpanded() {
3813         return mExpandedHeight >= getMaxPanelTransitionDistance();
3814     }
3815 
3816     /**
3817      * Returns true if shade is fully opened, that is we're actually in the notification shade
3818      * with QQS or QS. It's different from {@link #isFullyExpanded()} that it will not report
3819      * shade as always expanded if we're on keyguard/AOD. It will return true only when user goes
3820      * from keyguard to shade.
3821      */
isShadeFullyOpen()3822     public boolean isShadeFullyOpen() {
3823         if (mBarState == SHADE) {
3824             return isFullyExpanded();
3825         } else if (mBarState == SHADE_LOCKED) {
3826             return true;
3827         } else {
3828             // case of two finger swipe from the top of keyguard
3829             return mQsController.computeExpansionFraction() == 1;
3830         }
3831     }
3832 
isFullyCollapsed()3833     public boolean isFullyCollapsed() {
3834         return mExpandedFraction <= 0.0f;
3835     }
3836 
isCollapsing()3837     public boolean isCollapsing() {
3838         return mClosing || mIsLaunchAnimationRunning;
3839     }
3840 
isTracking()3841     public boolean isTracking() {
3842         return mTracking;
3843     }
3844 
3845     /** Returns whether the shade can be collapsed. */
canPanelBeCollapsed()3846     public boolean canPanelBeCollapsed() {
3847         return !isFullyCollapsed() && !mTracking && !mClosing;
3848     }
3849 
3850     /** Collapses the shade instantly without animation. */
instantCollapse()3851     public void instantCollapse() {
3852         abortAnimations();
3853         mShadeHeightLogger.logFunctionCall("instantCollapse");
3854         setExpandedFraction(0f);
3855         if (mExpanding) {
3856             notifyExpandingFinished();
3857         }
3858         if (mInstantExpanding) {
3859             mInstantExpanding = false;
3860             updatePanelExpansionAndVisibility();
3861         }
3862     }
3863 
abortAnimations()3864     private void abortAnimations() {
3865         cancelHeightAnimator();
3866         mView.removeCallbacks(mFlingCollapseRunnable);
3867     }
3868 
isUnlockHintRunning()3869     public boolean isUnlockHintRunning() {
3870         return mHintAnimationRunning;
3871     }
3872 
3873     /**
3874      * Phase 1: Move everything upwards.
3875      */
startUnlockHintAnimationPhase1(final Runnable onAnimationFinished)3876     private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
3877         float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
3878         ValueAnimator animator = createHeightAnimator(target);
3879         animator.setDuration(250);
3880         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3881         animator.addListener(new AnimatorListenerAdapter() {
3882             private boolean mCancelled;
3883 
3884             @Override
3885             public void onAnimationCancel(Animator animation) {
3886                 mCancelled = true;
3887             }
3888 
3889             @Override
3890             public void onAnimationEnd(Animator animation) {
3891                 if (mCancelled) {
3892                     setAnimator(null);
3893                     onAnimationFinished.run();
3894                 } else {
3895                     startUnlockHintAnimationPhase2(onAnimationFinished);
3896                 }
3897             }
3898         });
3899         animator.start();
3900         setAnimator(animator);
3901 
3902         final List<ViewPropertyAnimator> indicationAnimators =
3903                 mKeyguardBottomArea.getIndicationAreaAnimators();
3904         for (final ViewPropertyAnimator indicationAreaAnimator : indicationAnimators) {
3905             indicationAreaAnimator
3906                     .translationY(-mHintDistance)
3907                     .setDuration(250)
3908                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
3909                     .withEndAction(() -> indicationAreaAnimator
3910                             .translationY(0)
3911                             .setDuration(450)
3912                             .setInterpolator(mBounceInterpolator)
3913                             .start())
3914                     .start();
3915         }
3916     }
3917 
setAnimator(ValueAnimator animator)3918     private void setAnimator(ValueAnimator animator) {
3919         mHeightAnimator = animator;
3920         if (animator == null && mPanelUpdateWhenAnimatorEnds) {
3921             mPanelUpdateWhenAnimatorEnds = false;
3922             updateExpandedHeightToMaxHeight();
3923         }
3924     }
3925 
3926     /** Returns whether a shade or QS expansion animation is running */
isShadeOrQsHeightAnimationRunning()3927     public boolean isShadeOrQsHeightAnimationRunning() {
3928         return mHeightAnimator != null && !mHintAnimationRunning && !mIsSpringBackAnimation;
3929     }
3930 
3931     /**
3932      * Phase 2: Bounce down.
3933      */
startUnlockHintAnimationPhase2(final Runnable onAnimationFinished)3934     private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
3935         ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
3936         animator.setDuration(450);
3937         animator.setInterpolator(mBounceInterpolator);
3938         animator.addListener(new AnimatorListenerAdapter() {
3939             @Override
3940             public void onAnimationEnd(Animator animation) {
3941                 setAnimator(null);
3942                 onAnimationFinished.run();
3943                 updatePanelExpansionAndVisibility();
3944             }
3945         });
3946         animator.start();
3947         setAnimator(animator);
3948     }
3949 
createHeightAnimator(float targetHeight)3950     private ValueAnimator createHeightAnimator(float targetHeight) {
3951         return createHeightAnimator(targetHeight, 0.0f /* performOvershoot */);
3952     }
3953 
3954     /**
3955      * Create an animator that can also overshoot
3956      *
3957      * @param targetHeight    the target height
3958      * @param overshootAmount the amount of overshoot desired
3959      */
createHeightAnimator(float targetHeight, float overshootAmount)3960     private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
3961         float startExpansion = mOverExpansion;
3962         ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
3963         animator.addUpdateListener(
3964                 animation -> {
3965                     if (overshootAmount > 0.0f
3966                             // Also remove the overExpansion when collapsing
3967                             || (targetHeight == 0.0f && startExpansion != 0)) {
3968                         final float expansion = MathUtils.lerp(
3969                                 startExpansion,
3970                                 mPanelFlingOvershootAmount * overshootAmount,
3971                                 Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
3972                                         animator.getAnimatedFraction()));
3973                         setOverExpansionInternal(expansion, false /* isFromGesture */);
3974                     }
3975                     mShadeHeightLogger.logFunctionCall("height animator update");
3976                     setExpandedHeightInternal((float) animation.getAnimatedValue());
3977                 });
3978         return animator;
3979     }
3980 
3981     /** Update the visibility of {@link NotificationPanelView} if necessary. */
updateVisibility()3982     private void updateVisibility() {
3983         mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
3984     }
3985 
3986     /**
3987      * Updates the panel expansion and {@link NotificationPanelView} visibility if necessary.
3988      *
3989      * TODO(b/200063118): Could public calls to this method be replaced with calls to
3990      *   {@link #updateVisibility()}? That would allow us to make this method private.
3991      */
updatePanelExpansionAndVisibility()3992     public void updatePanelExpansionAndVisibility() {
3993         mShadeExpansionStateManager.onPanelExpansionChanged(
3994                 mExpandedFraction, isExpanded(), mTracking, mExpansionDragDownAmountPx);
3995         updateVisibility();
3996     }
3997 
isExpanded()3998     public boolean isExpanded() {
3999         return mExpandedFraction > 0f
4000                 || mInstantExpanding
4001                 || isPanelVisibleBecauseOfHeadsUp()
4002                 || mTracking
4003                 || mHeightAnimator != null
4004                 && !mIsSpringBackAnimation;
4005     }
4006 
4007     /** Called when the user performs a click anywhere in the empty area of the panel. */
onEmptySpaceClick()4008     private void onEmptySpaceClick() {
4009         if (!mHintAnimationRunning)  {
4010             onMiddleClicked();
4011         }
4012     }
4013 
4014     @VisibleForTesting
isClosing()4015     boolean isClosing() {
4016         return mClosing;
4017     }
4018 
4019     /** Collapses the shade with an animation duration in milliseconds. */
collapseWithDuration(int animationDuration)4020     public void collapseWithDuration(int animationDuration) {
4021         mFixedDuration = animationDuration;
4022         collapse(false /* delayed */, 1.0f /* speedUpFactor */);
4023         mFixedDuration = NO_FIXED_DURATION;
4024     }
4025 
4026     /** Returns the NotificationPanelView. */
getView()4027     public ViewGroup getView() {
4028         // TODO(b/254878364): remove this method, or at least reduce references to it.
4029         return mView;
4030     }
4031 
4032     /** */
postToView(Runnable action)4033     public boolean postToView(Runnable action) {
4034         return mView.post(action);
4035     }
4036 
4037     /** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */
handleExternalInterceptTouch(MotionEvent event)4038     public boolean handleExternalInterceptTouch(MotionEvent event) {
4039         return mTouchHandler.onInterceptTouchEvent(event);
4040     }
4041 
4042     /** Sends an external (e.g. Status Bar) touch event to the Shade touch handler. */
handleExternalTouch(MotionEvent event)4043     public boolean handleExternalTouch(MotionEvent event) {
4044         return mTouchHandler.onTouchEvent(event);
4045     }
4046 
4047     /** */
requestLayoutOnView()4048     public void requestLayoutOnView() {
4049         mView.requestLayout();
4050     }
4051 
4052     /** */
resetViewAlphas()4053     public void resetViewAlphas() {
4054         ViewGroupFadeHelper.reset(mView);
4055     }
4056 
4057     /** */
isViewEnabled()4058     public boolean isViewEnabled() {
4059         return mView.isEnabled();
4060     }
4061 
getOverStretchAmount()4062     float getOverStretchAmount() {
4063         return mOverStretchAmount;
4064     }
4065 
getMinFraction()4066     float getMinFraction() {
4067         return mMinFraction;
4068     }
4069 
getNavigationBarBottomHeight()4070     int getNavigationBarBottomHeight() {
4071         return mNavigationBarBottomHeight;
4072     }
4073 
isExpandingFromHeadsUp()4074     boolean isExpandingFromHeadsUp() {
4075         return mExpandingFromHeadsUp;
4076     }
4077 
4078     /**
4079      * We don't always want to close QS when requested as shade might be in a different state
4080      * already e.g. when going from collapse to expand very quickly. In that case StatusBar
4081      * window might send signal to collapse QS but we might be already expanding and in split
4082      * shade QS are always expanded
4083      */
closeQsIfPossible()4084     private void closeQsIfPossible() {
4085         boolean openOrOpening = isShadeFullyOpen() || isExpanding();
4086         if (!(mSplitShadeEnabled && openOrOpening)) {
4087             mQsController.closeQs();
4088         }
4089     }
4090 
4091     /** TODO: remove need for this delegate (b/254870148) */
setQsScrimEnabled(boolean qsScrimEnabled)4092     public void setQsScrimEnabled(boolean qsScrimEnabled) {
4093         mQsController.setScrimEnabled(qsScrimEnabled);
4094     }
4095 
getShadeExpansionStateManager()4096     private ShadeExpansionStateManager getShadeExpansionStateManager() {
4097         return mShadeExpansionStateManager;
4098     }
4099 
onQsExpansionChanged(boolean expanded)4100     private void onQsExpansionChanged(boolean expanded) {
4101         updateExpandedHeightToMaxHeight();
4102         setStatusAccessibilityImportance(expanded
4103                 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
4104                 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
4105         updateSystemUiStateFlags();
4106         NavigationBarView navigationBarView =
4107                 mNavigationBarController.getNavigationBarView(mDisplayId);
4108         if (navigationBarView != null) {
4109             navigationBarView.onStatusBarPanelStateChanged();
4110         }
4111     }
4112 
4113     @VisibleForTesting
onQsSetExpansionHeightCalled(boolean qsFullyExpanded)4114     void onQsSetExpansionHeightCalled(boolean qsFullyExpanded) {
4115         requestScrollerTopPaddingUpdate(false);
4116         mKeyguardStatusBarViewController.updateViewState();
4117         int barState = getBarState();
4118         if (barState == SHADE_LOCKED || barState == KEYGUARD) {
4119             updateKeyguardBottomAreaAlpha();
4120             positionClockAndNotifications();
4121         }
4122 
4123         if (mAccessibilityManager.isEnabled()) {
4124             mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
4125         }
4126 
4127         if (!mFalsingManager.isUnlockingDisabled() && qsFullyExpanded
4128                 && mFalsingCollector.shouldEnforceBouncer()) {
4129             mCentralSurfaces.executeRunnableDismissingKeyguard(null, null,
4130                     false, true, false);
4131         }
4132         if (DEBUG_DRAWABLE) {
4133             mView.invalidate();
4134         }
4135     }
4136 
onQsStateUpdated(boolean qsExpanded, boolean isStackScrollerOverscrolling)4137     private void onQsStateUpdated(boolean qsExpanded, boolean isStackScrollerOverscrolling) {
4138         if (mKeyguardUserSwitcherController != null && qsExpanded
4139                 && !isStackScrollerOverscrolling) {
4140             mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
4141         }
4142     }
4143 
onQsClippingImmediatelyApplied(boolean clipStatusView, Rect lastQsClipBounds, int top, boolean qsFragmentCreated, boolean qsVisible)4144     private void onQsClippingImmediatelyApplied(boolean clipStatusView,
4145             Rect lastQsClipBounds, int top, boolean qsFragmentCreated, boolean qsVisible) {
4146         if (qsFragmentCreated) {
4147             mKeyguardInteractor.setQuickSettingsVisible(qsVisible);
4148         }
4149 
4150         // The padding on this area is large enough that
4151         // we can use a cheaper clipping strategy
4152         mKeyguardStatusViewController.setClipBounds(
4153                 clipStatusView ? lastQsClipBounds : null);
4154         if (mSplitShadeEnabled) {
4155             mKeyguardStatusBarViewController.setNoTopClipping();
4156         } else {
4157             mKeyguardStatusBarViewController.updateTopClipping(top);
4158         }
4159     }
4160 
onFlingQsWithoutClick(ValueAnimator animator, float qsExpansionHeight, float target, float vel)4161     private void onFlingQsWithoutClick(ValueAnimator animator, float qsExpansionHeight,
4162             float target, float vel) {
4163         mFlingAnimationUtils.apply(animator, qsExpansionHeight, target, vel);
4164     }
4165 
onExpansionHeightSetToMax(boolean requestPaddingUpdate)4166     private void onExpansionHeightSetToMax(boolean requestPaddingUpdate) {
4167         if (requestPaddingUpdate) {
4168             requestScrollerTopPaddingUpdate(false /* animate */);
4169         }
4170         updateExpandedHeightToMaxHeight();
4171     }
4172 
4173     private final class NsslHeightChangedListener implements
4174             ExpandableView.OnHeightChangedListener {
4175         @Override
onHeightChanged(ExpandableView view, boolean needsAnimation)4176         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
4177             // Block update if we are in QS and just the top padding changed (i.e. view == null).
4178             if (view == null && mQsController.getExpanded()) {
4179                 return;
4180             }
4181             if (needsAnimation && mInterpolatedDarkAmount == 0) {
4182                 mAnimateNextPositionUpdate = true;
4183             }
4184             ExpandableView firstChildNotGone =
4185                     mNotificationStackScrollLayoutController.getFirstChildNotGone();
4186             ExpandableNotificationRow
4187                     firstRow =
4188                     firstChildNotGone instanceof ExpandableNotificationRow
4189                             ? (ExpandableNotificationRow) firstChildNotGone : null;
4190             if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
4191                     == firstRow))) {
4192                 requestScrollerTopPaddingUpdate(false /* animate */);
4193             }
4194             if (getKeyguardShowing()) {
4195                 updateMaxDisplayedNotifications(true);
4196             }
4197             updateExpandedHeightToMaxHeight();
4198         }
4199 
4200         @Override
onReset(ExpandableView view)4201         public void onReset(ExpandableView view) {}
4202     }
4203 
onDynamicPrivacyChanged()4204     private void onDynamicPrivacyChanged() {
4205         // Do not request animation when pulsing or waking up, otherwise the clock will be out
4206         // of sync with the notification panel.
4207         if (mLinearDarkAmount != 0) {
4208             return;
4209         }
4210         mAnimateNextPositionUpdate = true;
4211     }
4212 
4213     private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener {
4214         @Override
onHeadsUpPinnedModeChanged(final boolean inPinnedMode)4215         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
4216             if (inPinnedMode) {
4217                 mHeadsUpExistenceChangedRunnable.run();
4218                 updateNotificationTranslucency();
4219             } else {
4220                 setHeadsUpAnimatingAway(true);
4221                 mNotificationStackScrollLayoutController.runAfterAnimationFinished(
4222                         mHeadsUpExistenceChangedRunnable);
4223             }
4224             updateGestureExclusionRect();
4225             mHeadsUpPinnedMode = inPinnedMode;
4226             updateVisibility();
4227             mKeyguardStatusBarViewController.updateForHeadsUp();
4228         }
4229 
4230         @Override
onHeadsUpPinned(NotificationEntry entry)4231         public void onHeadsUpPinned(NotificationEntry entry) {
4232             if (!isOnKeyguard()) {
4233                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
4234                         entry.getHeadsUpAnimationView(), true);
4235             }
4236         }
4237 
4238         @Override
onHeadsUpUnPinned(NotificationEntry entry)4239         public void onHeadsUpUnPinned(NotificationEntry entry) {
4240 
4241             // When we're unpinning the notification via active edge they remain heads-upped,
4242             // we need to make sure that an animation happens in this case, otherwise the
4243             // notification
4244             // will stick to the top without any interaction.
4245             if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
4246                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
4247                         entry.getHeadsUpAnimationView(), false);
4248                 entry.setHeadsUpIsVisible();
4249             }
4250         }
4251     }
4252 
4253     private final class ConfigurationListener implements
4254             ConfigurationController.ConfigurationListener {
4255         @Override
onThemeChanged()4256         public void onThemeChanged() {
4257             debugLog("onThemeChanged");
4258             reInflateViews();
4259         }
4260 
4261         @Override
onSmallestScreenWidthChanged()4262         public void onSmallestScreenWidthChanged() {
4263             Trace.beginSection("onSmallestScreenWidthChanged");
4264             debugLog("onSmallestScreenWidthChanged");
4265 
4266             // Can affect multi-user switcher visibility as it depends on screen size by default:
4267             // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
4268             boolean prevKeyguardUserSwitcherEnabled = mKeyguardUserSwitcherEnabled;
4269             boolean prevKeyguardQsUserSwitchEnabled = mKeyguardQsUserSwitchEnabled;
4270             updateUserSwitcherFlags();
4271             if (prevKeyguardUserSwitcherEnabled != mKeyguardUserSwitcherEnabled
4272                     || prevKeyguardQsUserSwitchEnabled != mKeyguardQsUserSwitchEnabled) {
4273                 reInflateViews();
4274             }
4275 
4276             Trace.endSection();
4277         }
4278 
4279         @Override
onDensityOrFontScaleChanged()4280         public void onDensityOrFontScaleChanged() {
4281             debugLog("onDensityOrFontScaleChanged");
4282             reInflateViews();
4283         }
4284     }
4285 
4286     private final class SettingsChangeObserver extends ContentObserver {
SettingsChangeObserver(Handler handler)4287         SettingsChangeObserver(Handler handler) {
4288             super(handler);
4289         }
4290 
4291         @Override
onChange(boolean selfChange)4292         public void onChange(boolean selfChange) {
4293             debugLog("onSettingsChanged");
4294 
4295             // Can affect multi-user switcher visibility
4296             reInflateViews();
4297         }
4298     }
4299 
4300     private final class StatusBarStateListener implements StateListener {
4301         @Override
onStateChanged(int statusBarState)4302         public void onStateChanged(int statusBarState) {
4303             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
4304             boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
4305             int oldState = mBarState;
4306             boolean keyguardShowing = statusBarState == KEYGUARD;
4307 
4308             if (mDozeParameters.shouldDelayKeyguardShow()
4309                     && oldState == StatusBarState.SHADE
4310                     && statusBarState == KEYGUARD) {
4311                 // This means we're doing the screen off animation - position the keyguard status
4312                 // view where it'll be on AOD, so we can animate it in.
4313                 mKeyguardStatusViewController.updatePosition(
4314                         mClockPositionResult.clockX,
4315                         mClockPositionResult.clockYFullyDozing,
4316                         mClockPositionResult.clockScale,
4317                         false /* animate */);
4318             }
4319 
4320             mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
4321                     statusBarState,
4322                     keyguardFadingAway,
4323                     goingToFullShade,
4324                     mBarState);
4325 
4326             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
4327 
4328             // TODO: maybe add a listener for barstate
4329             mBarState = statusBarState;
4330             mQsController.setBarState(statusBarState);
4331 
4332             boolean fromShadeToKeyguard = statusBarState == KEYGUARD
4333                     && (oldState == SHADE || oldState == SHADE_LOCKED);
4334             if (mSplitShadeEnabled && fromShadeToKeyguard) {
4335                 // user can go to keyguard from different shade states and closing animation
4336                 // may not fully run - we always want to make sure we close QS when that happens
4337                 // as we never need QS open in fresh keyguard state
4338                 mQsController.closeQs();
4339             }
4340 
4341             if (oldState == KEYGUARD && (goingToFullShade
4342                     || statusBarState == StatusBarState.SHADE_LOCKED)) {
4343 
4344                 long startDelay;
4345                 long duration;
4346                 if (mKeyguardStateController.isKeyguardFadingAway()) {
4347                     startDelay = mKeyguardStateController.getKeyguardFadingAwayDelay();
4348                     duration = mKeyguardStateController.getShortenedFadingAwayDuration();
4349                 } else {
4350                     startDelay = 0;
4351                     duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
4352                 }
4353                 mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
4354                 mQsController.updateMinHeight();
4355             } else if (oldState == StatusBarState.SHADE_LOCKED
4356                     && statusBarState == KEYGUARD) {
4357                 mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
4358 
4359                 mNotificationStackScrollLayoutController.resetScrollPosition();
4360             } else {
4361                 // this else branch means we are doing one of:
4362                 //  - from KEYGUARD to SHADE (but not fully expanded as when swiping from the top)
4363                 //  - from SHADE to KEYGUARD
4364                 //  - from SHADE_LOCKED to SHADE
4365                 //  - getting notified again about the current SHADE or KEYGUARD state
4366                 final boolean animatingUnlockedShadeToKeyguard = oldState == SHADE
4367                         && statusBarState == KEYGUARD
4368                         && mScreenOffAnimationController.isKeyguardShowDelayed();
4369                 if (!animatingUnlockedShadeToKeyguard) {
4370                     // Only make the status bar visible if we're not animating the screen off, since
4371                     // we only want to be showing the clock/notifications during the animation.
4372                     if (keyguardShowing) {
4373                         mShadeLog.v("Updating keyguard status bar state to visible");
4374                     } else {
4375                         mShadeLog.v("Updating keyguard status bar state to invisible");
4376                     }
4377                     mKeyguardStatusBarViewController.updateViewState(
4378                             /* alpha= */ 1f,
4379                             keyguardShowing ? View.VISIBLE : View.INVISIBLE);
4380                 }
4381                 if (keyguardShowing && oldState != mBarState) {
4382                     mQsController.hideQsImmediately();
4383                 }
4384             }
4385             mKeyguardStatusBarViewController.updateForHeadsUp();
4386             if (keyguardShowing) {
4387                 updateDozingVisibilities(false /* animate */);
4388             }
4389 
4390             updateMaxDisplayedNotifications(false);
4391             // The update needs to happen after the headerSlide in above, otherwise the translation
4392             // would reset
4393             maybeAnimateBottomAreaAlpha();
4394             mQsController.updateQsState();
4395         }
4396 
4397         @Override
onDozeAmountChanged(float linearAmount, float amount)4398         public void onDozeAmountChanged(float linearAmount, float amount) {
4399             mInterpolatedDarkAmount = amount;
4400             mLinearDarkAmount = linearAmount;
4401             positionClockAndNotifications();
4402         }
4403     }
4404 
4405     /**
4406      * An interface that provides the current state of the notification panel and related views,
4407      * which is needed to calculate {@link KeyguardStatusBarView}'s state in
4408      * {@link KeyguardStatusBarViewController}.
4409      */
4410     public interface NotificationPanelViewStateProvider {
4411         /** Returns the expanded height of the panel view. */
getPanelViewExpandedHeight()4412         float getPanelViewExpandedHeight();
4413 
4414         /**
4415          * Returns true if heads up should be visible.
4416          *
4417          * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into
4418          * {@link KeyguardStatusBarViewController} and remove this method.
4419          */
shouldHeadsUpBeVisible()4420         boolean shouldHeadsUpBeVisible();
4421 
4422         /** Return the fraction of the shade that's expanded, when in lockscreen. */
getLockscreenShadeDragProgress()4423         float getLockscreenShadeDragProgress();
4424     }
4425 
4426     private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider =
4427             new NotificationPanelViewStateProvider() {
4428                 @Override
4429                 public float getPanelViewExpandedHeight() {
4430                     return getExpandedHeight();
4431                 }
4432 
4433                 @Override
4434                 public boolean shouldHeadsUpBeVisible() {
4435                     return mHeadsUpAppearanceController.shouldBeVisible();
4436                 }
4437 
4438                 @Override
4439                 public float getLockscreenShadeDragProgress() {
4440                     return NotificationPanelViewController.this.getLockscreenShadeDragProgress();
4441                 }
4442             };
4443 
4444     /**
4445      * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
4446      * screen off animation controller in order to animate in AOD without "actually" fully switching
4447      * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
4448      * change.
4449      */
showAodUi()4450     public void showAodUi() {
4451         setDozing(true /* dozing */, false /* animate */);
4452         mStatusBarStateController.setUpcomingState(KEYGUARD);
4453         mStatusBarStateListener.onStateChanged(KEYGUARD);
4454         mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
4455         mShadeHeightLogger.logFunctionCall("showAodUi");
4456         setExpandedFraction(1f);
4457     }
4458 
4459     /** Sets the overstretch amount in raw pixels when dragging down. */
setOverStretchAmount(float amount)4460     public void setOverStretchAmount(float amount) {
4461         float progress = amount / mView.getHeight();
4462         float overStretch = Interpolators.getOvershootInterpolation(progress);
4463         mOverStretchAmount = overStretch * mMaxOverscrollAmountForPulse;
4464         positionClockAndNotifications(true /* forceUpdate */);
4465     }
4466 
4467     private final class ShadeAttachStateChangeListener implements View.OnAttachStateChangeListener {
4468         @Override
onViewAttachedToWindow(View v)4469         public void onViewAttachedToWindow(View v) {
4470             mFragmentService.getFragmentHostManager(mView)
4471                     .addTagListener(QS.TAG, mQsController.getQsFragmentListener());
4472             mStatusBarStateController.addCallback(mStatusBarStateListener);
4473             mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
4474             mConfigurationController.addCallback(mConfigurationListener);
4475             // Theme might have changed between inflating this view and attaching it to the
4476             // window, so
4477             // force a call to onThemeChanged
4478             mConfigurationListener.onThemeChanged();
4479             mFalsingManager.addTapListener(mFalsingTapListener);
4480             mKeyguardIndicationController.init();
4481             registerSettingsChangeListener();
4482         }
4483 
4484         @Override
onViewDetachedFromWindow(View v)4485         public void onViewDetachedFromWindow(View v) {
4486             mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
4487             mFragmentService.getFragmentHostManager(mView)
4488                     .removeTagListener(QS.TAG, mQsController.getQsFragmentListener());
4489             mStatusBarStateController.removeCallback(mStatusBarStateListener);
4490             mConfigurationController.removeCallback(mConfigurationListener);
4491             mFalsingManager.removeTapListener(mFalsingTapListener);
4492         }
4493     }
4494 
4495     private final class ShadeLayoutChangeListener implements View.OnLayoutChangeListener {
4496         @Override
onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)4497         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
4498                 int oldTop, int oldRight, int oldBottom) {
4499             DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
4500             updateExpandedHeightToMaxHeight();
4501             mHasLayoutedSinceDown = true;
4502             if (mUpdateFlingOnLayout) {
4503                 abortAnimations();
4504                 fling(mUpdateFlingVelocity);
4505                 mUpdateFlingOnLayout = false;
4506             }
4507             updateMaxDisplayedNotifications(!shouldAvoidChangingNotificationsCount());
4508             setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
4509 
4510             // Update Clock Pivot (used by anti-burnin transformations)
4511             mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
4512 
4513             int oldMaxHeight = mQsController.updateHeightsOnShadeLayoutChange();
4514             positionClockAndNotifications();
4515             mQsController.handleShadeLayoutChanged(oldMaxHeight);
4516             updateExpandedHeight(getExpandedHeight());
4517             updateHeader();
4518 
4519             // If we are running a size change animation, the animation takes care of the height
4520             // of the container. However, if we are not animating, we always need to make the QS
4521             // container the desired height so when closing the QS detail, it stays smaller after
4522             // the size change animation is finished but the detail view is still being animated
4523             // away (this animation takes longer than the size change animation).
4524             mQsController.setHeightOverrideToDesiredHeight();
4525 
4526             updateMaxHeadsUpTranslation();
4527             updateGestureExclusionRect();
4528             if (mExpandAfterLayoutRunnable != null) {
4529                 mExpandAfterLayoutRunnable.run();
4530                 mExpandAfterLayoutRunnable = null;
4531             }
4532             DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
4533         }
4534     }
4535 
4536     @NonNull
onApplyShadeWindowInsets(WindowInsets insets)4537     private WindowInsets onApplyShadeWindowInsets(WindowInsets insets) {
4538         // the same types of insets that are handled in NotificationShadeWindowView
4539         int insetTypes = WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
4540         Insets combinedInsets = insets.getInsetsIgnoringVisibility(insetTypes);
4541         mDisplayTopInset = combinedInsets.top;
4542         mDisplayRightInset = combinedInsets.right;
4543         mDisplayLeftInset = combinedInsets.left;
4544         mQsController.setDisplayInsets(mDisplayRightInset, mDisplayLeftInset);
4545 
4546         mNavigationBarBottomHeight = insets.getStableInsetBottom();
4547         updateMaxHeadsUpTranslation();
4548         return insets;
4549     }
4550 
4551     /** Removes any pending runnables that would collapse the panel. */
cancelPendingPanelCollapse()4552     public void cancelPendingPanelCollapse() {
4553         mView.removeCallbacks(mMaybeHideExpandedRunnable);
4554     }
4555 
onPanelStateChanged(@anelState int state)4556     private void onPanelStateChanged(@PanelState int state) {
4557         mQsController.updateExpansionEnabledAmbient();
4558 
4559         if (state == STATE_OPEN && mCurrentPanelState != state) {
4560             mQsController.setExpandImmediate(false);
4561             mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
4562         }
4563         if (state == STATE_OPENING) {
4564             // we need to ignore it on keyguard as this is a false alarm - transition from unlocked
4565             // to locked will trigger this event and we're not actually in the process of opening
4566             // the shade, lockscreen is just always expanded
4567             if (mSplitShadeEnabled && !isOnKeyguard()) {
4568                 mQsController.setExpandImmediate(true);
4569             }
4570             mOpenCloseListener.onOpenStarted();
4571         }
4572         if (state == STATE_CLOSED) {
4573             mQsController.setExpandImmediate(false);
4574             // Close the status bar in the next frame so we can show the end of the
4575             // animation.
4576             mView.post(mMaybeHideExpandedRunnable);
4577         }
4578         mCurrentPanelState = state;
4579     }
4580 
setTransitionAlpha( NotificationStackScrollLayoutController stackScroller)4581     private Consumer<Float> setTransitionAlpha(
4582             NotificationStackScrollLayoutController stackScroller) {
4583         return (Float alpha) -> {
4584             mKeyguardStatusViewController.setAlpha(alpha);
4585             stackScroller.setAlpha(alpha);
4586 
4587             mKeyguardBottomAreaInteractor.setAlpha(alpha);
4588             mLockIconViewController.setAlpha(alpha);
4589 
4590             if (mKeyguardQsUserSwitchController != null) {
4591                 mKeyguardQsUserSwitchController.setAlpha(alpha);
4592             }
4593             if (mKeyguardUserSwitcherController != null) {
4594                 mKeyguardUserSwitcherController.setAlpha(alpha);
4595             }
4596         };
4597     }
4598 
4599     private Consumer<Float> setTransitionY(
4600                 NotificationStackScrollLayoutController stackScroller) {
4601         return (Float translationY) -> {
4602             mKeyguardStatusViewController.setTranslationY(translationY,  /* excludeMedia= */false);
4603             stackScroller.setTranslationY(translationY);
4604         };
4605     }
4606 
4607     @VisibleForTesting
4608     StatusBarStateController getStatusBarStateController() {
4609         return mStatusBarStateController;
4610     }
4611 
4612     @VisibleForTesting
4613     StateListener getStatusBarStateListener() {
4614         return mStatusBarStateListener;
4615     }
4616 
4617     @VisibleForTesting
4618     boolean isHintAnimationRunning() {
4619         return mHintAnimationRunning;
4620     }
4621 
4622     private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) {
4623         if (state != WINDOW_STATE_SHOWING
4624                 && mStatusBarStateController.getState() == StatusBarState.SHADE) {
4625             collapsePanel(
4626                     false /* animate */,
4627                     false /* delayed */,
4628                     1.0f /* speedUpFactor */);
4629         }
4630     }
4631 
4632     /** Handles MotionEvents for the Shade. */
4633     public final class TouchHandler implements View.OnTouchListener, Gefingerpoken {
4634         private long mLastTouchDownTime = -1L;
4635 
4636         /** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
4637         public boolean onInterceptTouchEvent(MotionEvent event) {
4638             mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
4639             if (mQsController.disallowTouches()) {
4640                 mShadeLog.logMotionEvent(event,
4641                         "NPVC not intercepting touch, panel touches disallowed");
4642                 return false;
4643             }
4644             initDownStates(event);
4645             // Do not let touches go to shade or QS if the bouncer is visible,
4646             // but still let user swipe down to expand the panel, dismissing the bouncer.
4647             if (mCentralSurfaces.isBouncerShowing()) {
4648                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
4649                         + "bouncer is showing");
4650                 return true;
4651             }
4652             if (mCommandQueue.panelsEnabled()
4653                     && !mNotificationStackScrollLayoutController.isLongPressInProgress()
4654                     && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
4655                 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
4656                 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
4657                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
4658                         + "HeadsUpTouchHelper");
4659                 return true;
4660             }
4661             if (!mQsController.shouldQuickSettingsIntercept(mDownX, mDownY, 0)
4662                     && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
4663                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
4664                         + "PulseExpansionHandler");
4665                 return true;
4666             }
4667 
4668             if (!isFullyCollapsed() && mQsController.onIntercept(event)) {
4669                 debugLog("onQsIntercept true");
4670                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
4671                         + "QsIntercept");
4672                 return true;
4673             }
4674 
4675             if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled) {
4676                 mShadeLog.logNotInterceptingTouchInstantExpanding(mInstantExpanding,
4677                         !mNotificationsDragEnabled, mTouchDisabled);
4678                 return false;
4679             }
4680             if (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
4681                 mShadeLog.logMotionEventStatusBarState(event, mStatusBarStateController.getState(),
4682                         "NPVC MotionEvent not intercepted: non-down action, motion was aborted");
4683                 return false;
4684             }
4685 
4686             /* If the user drags anywhere inside the panel we intercept it if the movement is
4687              upwards. This allows closing the shade from anywhere inside the panel.
4688              We only do this if the current content is scrolled to the bottom, i.e.
4689              canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
4690              gesture possible. */
4691             int pointerIndex = event.findPointerIndex(mTrackingPointer);
4692             if (pointerIndex < 0) {
4693                 pointerIndex = 0;
4694                 mTrackingPointer = event.getPointerId(pointerIndex);
4695             }
4696             final float x = event.getX(pointerIndex);
4697             final float y = event.getY(pointerIndex);
4698             boolean canCollapsePanel = canCollapsePanelOnTouch();
4699 
4700             switch (event.getActionMasked()) {
4701                 case MotionEvent.ACTION_DOWN:
4702                     mCentralSurfaces.userActivity();
4703                     mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
4704                     mMinExpandHeight = 0.0f;
4705                     mDownTime = mSystemClock.uptimeMillis();
4706                     if (mAnimatingOnDown && mClosing && !mHintAnimationRunning) {
4707                         cancelHeightAnimator();
4708                         mTouchSlopExceeded = true;
4709                         mShadeLog.v("NotificationPanelViewController MotionEvent intercepted:"
4710                                 + " mAnimatingOnDown: true, mClosing: true, mHintAnimationRunning:"
4711                                 + " false");
4712                         return true;
4713                     }
4714                     if (!mTracking || isFullyCollapsed()) {
4715                         mInitialExpandY = y;
4716                         mInitialExpandX = x;
4717                     } else {
4718                         mShadeLog.d("not setting mInitialExpandY in onInterceptTouch");
4719                     }
4720                     mTouchStartedInEmptyArea = !isInContentBounds(x, y);
4721                     mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
4722                     mMotionAborted = false;
4723                     mPanelClosedOnDown = isFullyCollapsed();
4724                     mCollapsedAndHeadsUpOnDown = false;
4725                     mHasLayoutedSinceDown = false;
4726                     mUpdateFlingOnLayout = false;
4727                     mTouchAboveFalsingThreshold = false;
4728                     addMovement(event);
4729                     break;
4730                 case MotionEvent.ACTION_POINTER_UP:
4731                     final int upPointer = event.getPointerId(event.getActionIndex());
4732                     if (mTrackingPointer == upPointer) {
4733                         // gesture is ongoing, find a new pointer to track
4734                         final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
4735                         mTrackingPointer = event.getPointerId(newIndex);
4736                         mInitialExpandX = event.getX(newIndex);
4737                         mInitialExpandY = event.getY(newIndex);
4738                     }
4739                     break;
4740                 case MotionEvent.ACTION_POINTER_DOWN:
4741                     mShadeLog.logMotionEventStatusBarState(event,
4742                             mStatusBarStateController.getState(),
4743                             "onInterceptTouchEvent: pointer down action");
4744                     if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
4745                         mMotionAborted = true;
4746                         mVelocityTracker.clear();
4747                     }
4748                     break;
4749                 case MotionEvent.ACTION_MOVE:
4750                     final float h = y - mInitialExpandY;
4751                     addMovement(event);
4752                     final boolean openShadeWithoutHun =
4753                             mPanelClosedOnDown && !mCollapsedAndHeadsUpOnDown;
4754                     if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown
4755                             || openShadeWithoutHun) {
4756                         float hAbs = Math.abs(h);
4757                         float touchSlop = getTouchSlop(event);
4758                         if ((h < -touchSlop
4759                                 || ((openShadeWithoutHun || mAnimatingOnDown) && hAbs > touchSlop))
4760                                 && hAbs > Math.abs(x - mInitialExpandX)) {
4761                             cancelHeightAnimator();
4762                             startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
4763                             mShadeLog.v("NotificationPanelViewController MotionEvent"
4764                                     + " intercepted: startExpandMotion");
4765                             return true;
4766                         }
4767                     }
4768                     break;
4769                 case MotionEvent.ACTION_CANCEL:
4770                 case MotionEvent.ACTION_UP:
4771                     mVelocityTracker.clear();
4772                     break;
4773             }
4774             return false;
4775         }
4776 
4777         @Override
4778         public boolean onTouch(View v, MotionEvent event) {
4779             return onTouchEvent(event);
4780         }
4781 
4782         @Override
4783         public boolean onTouchEvent(MotionEvent event) {
4784             if (event.getAction() == MotionEvent.ACTION_DOWN) {
4785                 if (event.getDownTime() == mLastTouchDownTime) {
4786                     // An issue can occur when swiping down after unlock, where multiple down
4787                     // events are received in this handler with identical downTimes. Until the
4788                     // source of the issue can be located, detect this case and ignore.
4789                     // see b/193350347
4790                     mShadeLog.logMotionEvent(event,
4791                             "onTouch: duplicate down event detected... ignoring");
4792                     return true;
4793                 }
4794                 mLastTouchDownTime = event.getDownTime();
4795             }
4796 
4797             if (mQsController.isFullyExpandedAndTouchesDisallowed()) {
4798                 mShadeLog.logMotionEvent(event,
4799                         "onTouch: ignore touch, panel touches disallowed and qs fully expanded");
4800                 return false;
4801             }
4802 
4803             // Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
4804             // otherwise user would be able to pull down QS or expand the shade.
4805             if (mCentralSurfaces.isBouncerShowingScrimmed()
4806                     || mCentralSurfaces.isBouncerShowingOverDream()) {
4807                 mShadeLog.logMotionEvent(event,
4808                         "onTouch: ignore touch, bouncer scrimmed or showing over dream");
4809                 return false;
4810             }
4811 
4812             // Make sure the next touch won't the blocked after the current ends.
4813             if (event.getAction() == MotionEvent.ACTION_UP
4814                     || event.getAction() == MotionEvent.ACTION_CANCEL) {
4815                 mBlockingExpansionForCurrentTouch = false;
4816             }
4817             // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
4818             // without any ACTION_MOVE event.
4819             // In such case, simply expand the panel instead of being stuck at the bottom bar.
4820             if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
4821                 expand(true /* animate */);
4822             }
4823             initDownStates(event);
4824 
4825             // If pulse is expanding already, let's give it the touch. There are situations
4826             // where the panel starts expanding even though we're also pulsing
4827             boolean pulseShouldGetTouch = (!mIsExpanding
4828                     && !mQsController.shouldQuickSettingsIntercept(mDownX, mDownY, 0))
4829                     || mPulseExpansionHandler.isExpanding();
4830             if (pulseShouldGetTouch && mPulseExpansionHandler.onTouchEvent(event)) {
4831                 // We're expanding all the other ones shouldn't get this anymore
4832                 mShadeLog.logMotionEvent(event, "onTouch: PulseExpansionHandler handled event");
4833                 return true;
4834             }
4835             if (mPulsing) {
4836                 mShadeLog.logMotionEvent(event, "onTouch: eat touch, device pulsing");
4837                 return true;
4838             }
4839             if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
4840                     && !mNotificationStackScrollLayoutController.isLongPressInProgress()
4841                     && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
4842                 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
4843             }
4844             boolean handled = mHeadsUpTouchHelper.onTouchEvent(event);
4845 
4846             if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && mQsController.handleTouch(
4847                     event, isFullyCollapsed(), isShadeOrQsHeightAnimationRunning())) {
4848                 mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event");
4849                 return true;
4850             }
4851             if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
4852                 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
4853                 handled = true;
4854             }
4855 
4856             if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded()
4857                     && mKeyguardStateController.isShowing()) {
4858                 mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
4859             }
4860 
4861             handled |= handleTouch(event);
4862             return !mDozing || handled;
4863         }
4864 
4865         private boolean handleTouch(MotionEvent event) {
4866             if (mInstantExpanding) {
4867                 mShadeLog.logMotionEvent(event,
4868                         "handleTouch: touch ignored due to instant expanding");
4869                 return false;
4870             }
4871             if (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL) {
4872                 mShadeLog.logMotionEvent(event, "handleTouch: non-cancel action, touch disabled");
4873                 return false;
4874             }
4875             if (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
4876                 mShadeLog.logMotionEventStatusBarState(event, mStatusBarStateController.getState(),
4877                         "handleTouch: non-down action, motion was aborted");
4878                 return false;
4879             }
4880 
4881             // If dragging should not expand the notifications shade, then return false.
4882             if (!mNotificationsDragEnabled) {
4883                 if (mTracking) {
4884                     // Turn off tracking if it's on or the shade can get stuck in the down position.
4885                     onTrackingStopped(true /* expand */);
4886                 }
4887                 mShadeLog.logMotionEvent(event, "handleTouch: drag not enabled");
4888                 return false;
4889             }
4890 
4891             // On expanding, single mouse click expands the panel instead of dragging.
4892             if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
4893                 if (event.getAction() == MotionEvent.ACTION_UP) {
4894                     expand(true);
4895                 }
4896                 return true;
4897             }
4898 
4899             /*
4900              * We capture touch events here and update the expand height here in case according to
4901              * the users fingers. This also handles multi-touch.
4902              *
4903              * Flinging is also enabled in order to open or close the shade.
4904              */
4905             int pointerIndex = event.findPointerIndex(mTrackingPointer);
4906             if (pointerIndex < 0) {
4907                 pointerIndex = 0;
4908                 mTrackingPointer = event.getPointerId(pointerIndex);
4909             }
4910             final float x = event.getX(pointerIndex);
4911             final float y = event.getY(pointerIndex);
4912 
4913             if (event.getActionMasked() == MotionEvent.ACTION_DOWN
4914                     || event.getActionMasked() == MotionEvent.ACTION_MOVE) {
4915                 mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
4916                 mIgnoreXTouchSlop = true;
4917             }
4918 
4919             switch (event.getActionMasked()) {
4920                 case MotionEvent.ACTION_DOWN:
4921                     mShadeLog.logMotionEvent(event, "onTouch: down action");
4922                     startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
4923                     mMinExpandHeight = 0.0f;
4924                     mPanelClosedOnDown = isFullyCollapsed();
4925                     mHasLayoutedSinceDown = false;
4926                     mUpdateFlingOnLayout = false;
4927                     mMotionAborted = false;
4928                     mDownTime = mSystemClock.uptimeMillis();
4929                     mTouchAboveFalsingThreshold = false;
4930                     mCollapsedAndHeadsUpOnDown =
4931                             isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
4932                     addMovement(event);
4933                     boolean regularHeightAnimationRunning = isShadeOrQsHeightAnimationRunning();
4934                     if (!mGestureWaitForTouchSlop || regularHeightAnimationRunning) {
4935                         mTouchSlopExceeded = regularHeightAnimationRunning
4936                                 || mTouchSlopExceededBeforeDown;
4937                         cancelHeightAnimator();
4938                         onTrackingStarted();
4939                     }
4940                     if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
4941                             && !mCentralSurfaces.isBouncerShowing()) {
4942                         startOpening(event);
4943                     }
4944                     break;
4945 
4946                 case MotionEvent.ACTION_POINTER_UP:
4947                     final int upPointer = event.getPointerId(event.getActionIndex());
4948                     if (mTrackingPointer == upPointer) {
4949                         // gesture is ongoing, find a new pointer to track
4950                         final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
4951                         final float newY = event.getY(newIndex);
4952                         final float newX = event.getX(newIndex);
4953                         mTrackingPointer = event.getPointerId(newIndex);
4954                         mHandlingPointerUp = true;
4955                         startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
4956                         mHandlingPointerUp = false;
4957                     }
4958                     break;
4959                 case MotionEvent.ACTION_POINTER_DOWN:
4960                     mShadeLog.logMotionEventStatusBarState(event,
4961                             mStatusBarStateController.getState(),
4962                             "handleTouch: pointer down action");
4963                     if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
4964                         mMotionAborted = true;
4965                         endMotionEvent(event, x, y, true /* forceCancel */);
4966                         return false;
4967                     }
4968                     break;
4969                 case MotionEvent.ACTION_MOVE:
4970                     if (isFullyCollapsed()) {
4971                         // If panel is fully collapsed, reset haptic effect before adding movement.
4972                         mHasVibratedOnOpen = false;
4973                         mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
4974                     }
4975                     addMovement(event);
4976                     if (!isFullyCollapsed() && !isOnKeyguard()) {
4977                         maybeVibrateOnOpening(true /* openingWithTouch */);
4978                     }
4979                     float h = y - mInitialExpandY;
4980 
4981                     // If the panel was collapsed when touching, we only need to check for the
4982                     // y-component of the gesture, as we have no conflicting horizontal gesture.
4983                     if (Math.abs(h) > getTouchSlop(event)
4984                             && (Math.abs(h) > Math.abs(x - mInitialExpandX)
4985                             || mIgnoreXTouchSlop)) {
4986                         mTouchSlopExceeded = true;
4987                         if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
4988                             if (mInitialOffsetOnTouch != 0f) {
4989                                 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
4990                                 h = 0;
4991                             }
4992                             cancelHeightAnimator();
4993                             onTrackingStarted();
4994                         }
4995                     }
4996                     float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
4997                     newHeight = Math.max(newHeight, mMinExpandHeight);
4998                     if (-h >= getFalsingThreshold()) {
4999                         mTouchAboveFalsingThreshold = true;
5000                         mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
5001                     }
5002                     if ((!mGestureWaitForTouchSlop || mTracking)
5003                             && !(mBlockingExpansionForCurrentTouch
5004                             || mQsController.isTrackingBlocked())) {
5005                         // Count h==0 as part of swipe-up,
5006                         // otherwise {@link NotificationStackScrollLayout}
5007                         // wrongly enables stack height updates at the start of lockscreen swipe-up
5008                         mAmbientState.setSwipingUp(h <= 0);
5009                         mShadeHeightLogger.logFunctionCall("ACTION_MOVE");
5010                         setExpandedHeightInternal(newHeight);
5011                     }
5012                     break;
5013 
5014                 case MotionEvent.ACTION_UP:
5015                 case MotionEvent.ACTION_CANCEL:
5016                     mShadeLog.logMotionEvent(event, "onTouch: up/cancel action");
5017                     addMovement(event);
5018                     endMotionEvent(event, x, y, false /* forceCancel */);
5019                     // mHeightAnimator is null, there is no remaining frame, ends instrumenting.
5020                     if (mHeightAnimator == null) {
5021                         if (event.getActionMasked() == MotionEvent.ACTION_UP) {
5022                             mQsController.endJankMonitoring();
5023                         } else {
5024                             mQsController.cancelJankMonitoring();
5025                         }
5026                     }
5027                     break;
5028             }
5029             return !mGestureWaitForTouchSlop || mTracking;
5030         }
5031     }
5032 
5033     static class SplitShadeTransitionAdapter extends Transition {
5034         private static final String PROP_BOUNDS = "splitShadeTransitionAdapter:bounds";
5035         private static final String[] TRANSITION_PROPERTIES = { PROP_BOUNDS };
5036 
5037         private final KeyguardStatusViewController mController;
5038 
5039         SplitShadeTransitionAdapter(KeyguardStatusViewController controller) {
5040             mController = controller;
5041         }
5042 
5043         private void captureValues(TransitionValues transitionValues) {
5044             Rect boundsRect = new Rect();
5045             boundsRect.left = transitionValues.view.getLeft();
5046             boundsRect.top = transitionValues.view.getTop();
5047             boundsRect.right = transitionValues.view.getRight();
5048             boundsRect.bottom = transitionValues.view.getBottom();
5049             transitionValues.values.put(PROP_BOUNDS, boundsRect);
5050         }
5051 
5052         @Override
5053         public void captureEndValues(TransitionValues transitionValues) {
5054             captureValues(transitionValues);
5055         }
5056 
5057         @Override
5058         public void captureStartValues(TransitionValues transitionValues) {
5059             captureValues(transitionValues);
5060         }
5061 
5062         @Nullable
5063         @Override
5064         public Animator createAnimator(ViewGroup sceneRoot, @Nullable TransitionValues startValues,
5065                 @Nullable TransitionValues endValues) {
5066             if (startValues == null || endValues == null) {
5067                 return null;
5068             }
5069             ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
5070 
5071             Rect from = (Rect) startValues.values.get(PROP_BOUNDS);
5072             Rect to = (Rect) endValues.values.get(PROP_BOUNDS);
5073 
5074             anim.addUpdateListener(animation -> {
5075                 ClockAnimations clockAnims = mController.getClockAnimations();
5076                 if (clockAnims == null) {
5077                     return;
5078                 }
5079 
5080                 clockAnims.onPositionUpdated(from, to, animation.getAnimatedFraction());
5081             });
5082 
5083             return anim;
5084         }
5085 
5086         @Override
5087         public String[] getTransitionProperties() {
5088             return TRANSITION_PROPERTIES;
5089         }
5090     }
5091 
5092     private final class ShadeAccessibilityDelegate extends AccessibilityDelegate {
5093         @Override
5094         public void onInitializeAccessibilityNodeInfo(View host,
5095                 AccessibilityNodeInfo info) {
5096             super.onInitializeAccessibilityNodeInfo(host, info);
5097             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
5098             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
5099         }
5100 
5101         @Override
5102         public boolean performAccessibilityAction(View host, int action, Bundle args) {
5103             if (action
5104                     == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
5105                     || action
5106                     == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
5107                 mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
5108                 return true;
5109             }
5110             return super.performAccessibilityAction(host, action, args);
5111         }
5112     }
5113 
5114     /** Listens for when touch tracking begins. */
5115     interface TrackingStartedListener {
5116         void onTrackingStarted();
5117     }
5118 
5119     /** Listens for when shade begins opening of finishes closing. */
5120     interface OpenCloseListener {
5121         /** Called when the shade finishes closing. */
5122         void onClosingFinished();
5123         /** Called when the shade starts opening. */
5124         void onOpenStarted();
5125     }
5126 }
5127