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