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