• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.shade;
18 
19 import static android.view.View.INVISIBLE;
20 import static android.view.View.VISIBLE;
21 
22 import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
23 import static com.android.systemui.Flags.msdlFeedback;
24 import static com.android.systemui.Flags.predictiveBackAnimateShade;
25 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
26 import static com.android.systemui.classifier.Classifier.GENERIC;
27 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
28 import static com.android.systemui.classifier.Classifier.UNLOCK;
29 import static com.android.systemui.keyguard.shared.model.KeyguardState.AOD;
30 import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
31 import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
32 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
33 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
34 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPENING;
35 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
36 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
37 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
38 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
39 import static com.android.systemui.statusbar.StatusBarState.SHADE;
40 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
41 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
42 import static com.android.systemui.util.DumpUtilsKt.asIndenting;
43 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
44 
45 import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
46 
47 import static java.lang.Float.isNaN;
48 
49 import android.animation.Animator;
50 import android.animation.AnimatorListenerAdapter;
51 import android.animation.ValueAnimator;
52 import android.annotation.NonNull;
53 import android.annotation.Nullable;
54 import android.content.res.Configuration;
55 import android.content.res.Resources;
56 import android.graphics.Color;
57 import android.graphics.Insets;
58 import android.graphics.Rect;
59 import android.graphics.Region;
60 import android.graphics.RenderEffect;
61 import android.graphics.Shader;
62 import android.os.Build;
63 import android.os.Bundle;
64 import android.os.Trace;
65 import android.util.IndentingPrintWriter;
66 import android.util.Log;
67 import android.util.MathUtils;
68 import android.view.Display;
69 import android.view.HapticFeedbackConstants;
70 import android.view.InputDevice;
71 import android.view.MotionEvent;
72 import android.view.VelocityTracker;
73 import android.view.View;
74 import android.view.View.AccessibilityDelegate;
75 import android.view.ViewConfiguration;
76 import android.view.ViewPropertyAnimator;
77 import android.view.ViewTreeObserver;
78 import android.view.WindowInsets;
79 import android.view.accessibility.AccessibilityEvent;
80 import android.view.accessibility.AccessibilityManager;
81 import android.view.accessibility.AccessibilityNodeInfo;
82 
83 import com.android.app.animation.Interpolators;
84 import com.android.internal.annotations.VisibleForTesting;
85 import com.android.internal.logging.MetricsLogger;
86 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
87 import com.android.internal.policy.SystemBarUtils;
88 import com.android.internal.statusbar.IStatusBarService;
89 import com.android.internal.util.LatencyTracker;
90 import com.android.keyguard.ActiveUnlockConfig;
91 import com.android.keyguard.KeyguardUnfoldTransition;
92 import com.android.keyguard.KeyguardUpdateMonitor;
93 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
94 import com.android.systemui.DejankUtils;
95 import com.android.systemui.Dumpable;
96 import com.android.systemui.Gefingerpoken;
97 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
98 import com.android.systemui.classifier.Classifier;
99 import com.android.systemui.classifier.FalsingCollector;
100 import com.android.systemui.common.domain.interactor.SysUIStateDisplaysInteractor;
101 import com.android.systemui.dagger.SysUISingleton;
102 import com.android.systemui.dagger.qualifiers.DisplayId;
103 import com.android.systemui.dagger.qualifiers.Main;
104 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
105 import com.android.systemui.doze.DozeLog;
106 import com.android.systemui.dump.DumpManager;
107 import com.android.systemui.dump.DumpsysTableLogger;
108 import com.android.systemui.fragments.FragmentService;
109 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
110 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
111 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
112 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
113 import com.android.systemui.keyguard.shared.model.ClockSize;
114 import com.android.systemui.keyguard.shared.model.Edge;
115 import com.android.systemui.keyguard.shared.model.TransitionState;
116 import com.android.systemui.keyguard.shared.model.TransitionStep;
117 import com.android.systemui.keyguard.ui.binder.KeyguardTouchViewBinder;
118 import com.android.systemui.keyguard.ui.transitions.BlurConfig;
119 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
120 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
121 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
122 import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
123 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
124 import com.android.systemui.model.StateChange;
125 import com.android.systemui.model.SysUiState;
126 import com.android.systemui.navigationbar.NavigationBarController;
127 import com.android.systemui.navigationbar.NavigationModeController;
128 import com.android.systemui.navigationbar.views.NavigationBarView;
129 import com.android.systemui.plugins.ActivityStarter;
130 import com.android.systemui.plugins.FalsingManager;
131 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
132 import com.android.systemui.plugins.qs.QS;
133 import com.android.systemui.plugins.statusbar.StatusBarStateController;
134 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
135 import com.android.systemui.power.domain.interactor.PowerInteractor;
136 import com.android.systemui.power.shared.model.WakefulnessModel;
137 import com.android.systemui.qs.flags.QSComposeFragment;
138 import com.android.systemui.res.R;
139 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
140 import com.android.systemui.settings.brightness.data.repository.BrightnessMirrorShowingRepository;
141 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
142 import com.android.systemui.shade.data.repository.FlingInfo;
143 import com.android.systemui.shade.data.repository.ShadeDisplaysRepository;
144 import com.android.systemui.shade.data.repository.ShadeRepository;
145 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
146 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
147 import com.android.systemui.shared.system.QuickStepContract;
148 import com.android.systemui.statusbar.CommandQueue;
149 import com.android.systemui.statusbar.GestureRecorder;
150 import com.android.systemui.statusbar.KeyguardIndicationController;
151 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
152 import com.android.systemui.statusbar.NotificationShadeDepthController;
153 import com.android.systemui.statusbar.NotificationShadeWindowController;
154 import com.android.systemui.statusbar.PulseExpansionHandler;
155 import com.android.systemui.statusbar.StatusBarState;
156 import com.android.systemui.statusbar.SysuiStatusBarStateController;
157 import com.android.systemui.statusbar.VibratorHelper;
158 import com.android.systemui.statusbar.notification.AnimatableProperty;
159 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
160 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
161 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
162 import com.android.systemui.statusbar.notification.PropertyAnimator;
163 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
164 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
165 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
166 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
167 import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
168 import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
169 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
170 import com.android.systemui.statusbar.notification.row.ExpandableView;
171 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
172 import com.android.systemui.statusbar.notification.stack.AmbientState;
173 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
174 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
175 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
176 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
177 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
178 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
179 import com.android.systemui.statusbar.phone.CentralSurfaces;
180 import com.android.systemui.statusbar.phone.DozeParameters;
181 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
182 import com.android.systemui.statusbar.phone.KeyguardBypassController;
183 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
184 import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController;
185 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
186 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
187 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
188 import com.android.systemui.statusbar.phone.ScrimController;
189 import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
190 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
191 import com.android.systemui.statusbar.phone.TapAgainViewController;
192 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
193 import com.android.systemui.statusbar.policy.ConfigurationController;
194 import com.android.systemui.statusbar.policy.KeyguardStateController;
195 import com.android.systemui.statusbar.policy.SplitShadeStateController;
196 import com.android.systemui.unfold.SysUIUnfoldComponent;
197 import com.android.systemui.util.Compile;
198 import com.android.systemui.util.Utils;
199 import com.android.systemui.util.time.SystemClock;
200 import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel;
201 import com.android.wm.shell.animation.FlingAnimationUtils;
202 
203 import dalvik.annotation.optimization.NeverCompile;
204 
205 import com.google.android.msdl.data.model.MSDLToken;
206 import com.google.android.msdl.domain.MSDLPlayer;
207 
208 import dagger.Lazy;
209 
210 import kotlin.Unit;
211 
212 import kotlinx.coroutines.CoroutineDispatcher;
213 import kotlinx.coroutines.flow.Flow;
214 import kotlinx.coroutines.flow.MutableStateFlow;
215 import kotlinx.coroutines.flow.StateFlow;
216 
217 import java.io.PrintWriter;
218 import java.util.ArrayList;
219 import java.util.Collections;
220 import java.util.Optional;
221 import java.util.Set;
222 import java.util.function.Consumer;
223 
224 import javax.inject.Inject;
225 import javax.inject.Provider;
226 
227 @SysUISingleton
228 public final class NotificationPanelViewController implements
229         ShadeSurface, Dumpable, BrightnessMirrorShowingInteractor {
230 
231     public static final String TAG = NotificationPanelView.class.getSimpleName();
232     private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
233     private static final boolean DEBUG_DRAWABLE = false;
234     /** The parallax amount of the quick settings translation when dragging down the panel. */
235     public static final float QS_PARALLAX_AMOUNT = 0.175f;
236     private static final int NO_FIXED_DURATION = -1;
237     private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L;
238     private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L;
239 
240     /**
241      * The factor of the usual high velocity that is needed in order to reach the maximum overshoot
242      * when flinging. A low value will make it that most flings will reach the maximum overshoot.
243      */
244     private static final float FACTOR_OF_HIGH_VELOCITY_FOR_MAX_OVERSHOOT = 0.5f;
245     /**
246      * Maximum time before which we will expand the panel even for slow motions when getting a
247      * touch passed over from launcher.
248      */
249     private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
250     private static final int MAX_DOWN_EVENT_BUFFER_SIZE = 50;
251     private static final String COUNTER_PANEL_OPEN = "panel_open";
252     public static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
253     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
254     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
255     private static final Rect EMPTY_RECT = new Rect();
256     //TODO(b/394977231) delete this temporary workaround used only by tests
257     private static final boolean DISABLE_LONG_PRESS_EXPAND = Build.HARDWARE.equals("cutf_cvm");
258     /**
259      * Whether the Shade should animate to reflect Back gesture progress.
260      * To minimize latency at runtime, we cache this, else we'd be reading it every time
261      * updateQsExpansion() is called... and it's called very often.
262      * <p>
263      * Whenever we change this flag, SysUI is restarted, so it's never going to be "stale".
264      */
265 
266     public final boolean mAnimateBack;
267     /**
268      * The minimum scale to "squish" the Shade and associated elements down to, for Back gesture
269      */
270     public static final float SHADE_BACK_ANIM_MIN_SCALE = 0.9f;
271     private final ShadeTouchableRegionManager mShadeTouchableRegionManager;
272     private final Resources mResources;
273     private final KeyguardStateController mKeyguardStateController;
274     private final SysuiStatusBarStateController mStatusBarStateController;
275     private final AmbientState mAmbientState;
276     private final LockscreenGestureLogger mLockscreenGestureLogger;
277     private final SystemClock mSystemClock;
278     private final ShadeLogger mShadeLog;
279     private final DozeParameters mDozeParameters;
280     private final NotificationStackScrollLayout.OnEmptySpaceClickListener
281             mOnEmptySpaceClickListener = this::onEmptySpaceClick;
282     private final ShadeHeadsUpChangedListener mOnHeadsUpChangedListener =
283             new ShadeHeadsUpChangedListener();
284     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
285     private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
286     private final NotificationPanelView mView;
287     private final VibratorHelper mVibratorHelper;
288     private final MSDLPlayer mMSDLPlayer;
289     private final MetricsLogger mMetricsLogger;
290     private final ConfigurationController mConfigurationController;
291     private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
292     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
293     private final AccessibilityManager mAccessibilityManager;
294     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
295     private final PulseExpansionHandler mPulseExpansionHandler;
296     private final KeyguardBypassController mKeyguardBypassController;
297     private final KeyguardUpdateMonitor mUpdateMonitor;
298     private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
299     private final ConversationNotificationManager mConversationNotificationManager;
300     private final MediaHierarchyManager mMediaHierarchyManager;
301     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
302     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
303     private final FragmentService mFragmentService;
304     private final IStatusBarService mStatusBarService;
305     private final ScrimController mScrimController;
306     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
307     private final TapAgainViewController mTapAgainViewController;
308     private final ShadeHeaderController mShadeHeaderController;
309     private final boolean mVibrateOnOpening;
310     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
311     private final FlingAnimationUtils mFlingAnimationUtilsClosing;
312     private final FlingAnimationUtils mFlingAnimationUtilsDismissing;
313     private final LatencyTracker mLatencyTracker;
314     private final DozeLog mDozeLog;
315     /** Whether or not the NotificationPanelView can be expanded or collapsed with a drag. */
316     private final boolean mNotificationsDragEnabled;
317     private final NotificationShadeWindowController mNotificationShadeWindowController;
318     private final ShadeExpansionStateManager mShadeExpansionStateManager;
319     private final ShadeRepository mShadeRepository;
320     private final ShadeAnimationInteractor mShadeAnimationInteractor;
321     private final FalsingTapListener mFalsingTapListener = this::falsingAdditionalTapRequired;
322     private final AccessibilityDelegate mAccessibilityDelegate = new ShadeAccessibilityDelegate();
323     private final NotificationGutsManager mGutsManager;
324     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
325     private final QuickSettingsControllerImpl mQsController;
326     private final TouchHandler mTouchHandler = new TouchHandler();
327     private final BlurConfig mBlurConfig;
328 
329     private long mDownTime;
330     private long mStatusBarLongPressDowntime = -1L;
331     private boolean mTouchSlopExceededBeforeDown;
332     private float mOverExpansion;
333     private CentralSurfaces mCentralSurfaces;
334     private HeadsUpManager mHeadsUpManager;
335     private float mExpandedHeight = 0;
336     /** The current squish amount for the predictive back animation */
337     private float mCurrentBackProgress = 0.0f;
338     private boolean mExpanding;
339     private boolean mSplitShadeEnabled;
340     private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
341     private NotificationsQuickSettingsContainer mNotificationContainerParent;
342     private final NotificationsQSContainerController mNotificationsQSContainerController;
343     private boolean mAnimateNextPositionUpdate;
344     private final ScreenOffAnimationController mScreenOffAnimationController;
345     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
346     private TrackingStartedListener mTrackingStartedListener;
347     private OpenCloseListener mOpenCloseListener;
348     private GestureRecorder mGestureRecorder;
349 
350     private boolean mDozing;
351     private boolean mDozingOnDown;
352     private boolean mBouncerShowing;
353     private int mBarState;
354     private FlingAnimationUtils mFlingAnimationUtils;
355     private int mStatusBarMinHeight;
356     private int mStatusBarHeaderHeightKeyguard;
357     private float mOverStretchAmount;
358     private float mDownX;
359     private float mDownY;
360     private int mDisplayTopInset = 0; // in pixels
361     private int mDisplayRightInset = 0; // in pixels
362     private int mDisplayLeftInset = 0; // in pixels
363 
364     @VisibleForTesting
365     KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
366     private final KeyguardClockPositionAlgorithm.Result
367             mClockPositionResult =
368             new KeyguardClockPositionAlgorithm.Result();
369     /**
370      * Indicates shade (or just QS) is expanding or collapsing but doesn't fully cover KEYGUARD
371      * state when shade can be expanded with swipe down or swipe down from the top to full QS.
372      */
373     private boolean mIsExpandingOrCollapsing;
374 
375     /**
376      * Indicates drag starting height when swiping down or up on heads-up notifications.
377      * This usually serves as a threshold from when shade expansion should really start. Otherwise
378      * this value would be height of shade and it will be immediately expanded to some extent.
379      */
380     private int mHeadsUpStartHeight;
381     private HeadsUpTouchHelper mHeadsUpTouchHelper;
382     private boolean mListenForHeadsUp;
383     private int mNavigationBarBottomHeight;
384     private boolean mExpandingFromHeadsUp;
385     private boolean mCollapsedOnDown;
386     private boolean mClosingWithAlphaFadeOut;
387     private boolean mHeadsUpAnimatingAway;
388     private final FalsingManager mFalsingManager;
389     private final FalsingCollector mFalsingCollector;
390     private final ShadeHeadsUpTrackerImpl mShadeHeadsUpTracker = new ShadeHeadsUpTrackerImpl();
391     private final ShadeFoldAnimatorImpl mShadeFoldAnimator = new ShadeFoldAnimatorImpl();
392 
393     @VisibleForTesting
394     Set<Animator> mTestSetOfAnimatorsUsed;
395 
396     private boolean mShowIconsWhenExpanded;
397     /** Whether the notifications are displayed full width (no margins on the side). */
398     private boolean mIsFullWidth;
399     private boolean mBlockingExpansionForCurrentTouch;
400     // Following variables maintain state of events when input focus transfer may occur.
401     private boolean mExpectingSynthesizedDown;
402     private boolean mLastEventSynthesizedDown;
403 
404     /** Current dark amount that follows regular interpolation curve of animation. */
405     private float mInterpolatedDarkAmount;
406     /**
407      * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
408      * interpolation curve is different.
409      */
410     private float mLinearDarkAmount;
411     private boolean mPulsing;
412     private int mStackScrollerMeasuringPass;
413     /** Non-null if a heads-up notification's position is being tracked. */
414     @Nullable
415     private ExpandableNotificationRow mTrackedHeadsUpNotification;
416     private final ArrayList<Consumer<ExpandableNotificationRow>>
417             mTrackingHeadsUpListeners = new ArrayList<>();
418     private HeadsUpAppearanceController mHeadsUpAppearanceController;
419 
420     private final BrightnessMirrorShowingRepository mBrightnessMirrorShowingRepository;
421     /**
422      * This flow would track whether the brightness mirror should be showing, but aware of the
423      * alpha transitions of NPV.
424      *
425      * When the repository flow emits true, this will also emit true (and start the alpha animation
426      * of NPV to go to 0f). However, when the repository emits false, this will first animate the
427      * alpha to 1f, and then emit false. This guarantees that the mirror is always showing while
428      * the alpha of NPV is animating.
429      */
430     private final MutableStateFlow<Boolean> mIsBrightnessMirrorShowing = MutableStateFlow(false);
431 
432     private int mPanelAlpha;
433     private Runnable mPanelAlphaEndAction;
434     private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
435             (view, alpha) -> {
436                 setAlphaInternal(alpha);
437             },
438             NotificationPanelView::getCurrentPanelAlpha,
439             R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
440             R.id.panel_alpha_animator_end_tag);
441     private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
442             new AnimationProperties().setDuration(150).setCustomInterpolator(
443                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
444     private final AnimationProperties mPanelAlphaInPropertiesAnimator =
445             new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
446                 if (mPanelAlphaEndAction != null) {
447                     mPanelAlphaEndAction.run();
448                 }
449                 // Once the animation for the alpha has finished (NPV is visible again), dismiss
450                 // the mirror
451                 postToView(() -> mIsBrightnessMirrorShowing.setValue(false));
452             }).setCustomInterpolator(
453                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
454 
455     private final CommandQueue mCommandQueue;
456     private final MediaDataManager mMediaDataManager;
457     @PanelState
458     private int mCurrentPanelState = STATE_CLOSED;
459     @Deprecated // Use SysUIStateInteractor instead
460     private final SysUiState mSysUiState;
461     private final SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor;
462     private final Lazy<ShadeDisplaysRepository> mShadeDisplaysRepository;
463     private final NotificationShadeDepthController mDepthController;
464     private final NavigationBarController mNavigationBarController;
465     private final int mDisplayId;
466 
467     private final KeyguardIndicationController mKeyguardIndicationController;
468     private int mHeadsUpInset;
469     private boolean mHeadsUpPinnedMode;
470     private boolean mAllowExpandForSmallExpansion;
471     private Runnable mExpandAfterLayoutRunnable;
472     private Runnable mHideExpandedRunnable;
473 
474     /** The maximum overshoot allowed for the top padding for the full shade transition. */
475     private int mMaxOverscrollAmountForPulse;
476 
477     /** Whether a collapse that started on the panel should allow the panel to intercept. */
478     private boolean mIsPanelCollapseOnQQS;
479 
480     /** Are we currently in gesture navigation. */
481     private boolean mIsGestureNavigation;
482     private int mOldLayoutDirection;
483 
484     private float mMinFraction;
485 
486     private final KeyguardMediaController mKeyguardMediaController;
487 
488     private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
489 
490     /** The drag distance required to fully expand the split shade. */
491     private int mSplitShadeFullTransitionDistance;
492     /** The drag distance required to fully transition scrims. */
493     private int mSplitShadeScrimTransitionDistance;
494 
495     private final NotificationListContainer mNotificationListContainer;
496     private final NPVCDownEventState.Buffer mLastDownEvents;
497     private final KeyguardClockInteractor mKeyguardClockInteractor;
498     private final WallpaperFocalAreaViewModel mWallpaperFocalAreaViewModel;
499     private float mMinExpandHeight;
500     private boolean mPanelUpdateWhenAnimatorEnds;
501     private boolean mHasVibratedOnOpen = false;
502     private int mFixedDuration = NO_FIXED_DURATION;
503     /** The overshoot amount when the panel flings open. */
504     private float mPanelFlingOvershootAmount;
505     /** The amount of pixels that we have overexpanded the last time with a gesture. */
506     private float mLastGesturedOverExpansion = -1;
507     /** Whether the current animator is the spring back animation. */
508     private boolean mIsSpringBackAnimation;
509     private float mHintDistance;
510     private float mInitialOffsetOnTouch;
511     private boolean mCollapsedAndHeadsUpOnDown;
512     private float mExpandedFraction = 0;
513     private float mExpansionDragDownAmountPx = 0;
514     private boolean mPanelClosedOnDown;
515     private boolean mHasLayoutedSinceDown;
516     private float mUpdateFlingVelocity;
517     private boolean mUpdateFlingOnLayout;
518     private boolean mTouchSlopExceeded;
519     private int mTrackingPointer;
520     private int mTouchSlop;
521     private float mSlopMultiplier;
522     private boolean mTouchAboveFalsingThreshold;
523     private boolean mTouchStartedInEmptyArea;
524     private boolean mMotionAborted;
525     private boolean mUpwardsWhenThresholdReached;
526     private boolean mAnimatingOnDown;
527     private boolean mHandlingPointerUp;
528     private ValueAnimator mHeightAnimator;
529     /** Whether an instant expand request is currently pending and we are waiting for layout. */
530     private boolean mInstantExpanding;
531     private boolean mAnimateAfterExpanding;
532     private boolean mIsFlinging;
533     private String mViewName;
534     private float mInitialExpandY;
535     private float mInitialExpandX;
536     private boolean mTouchDisabled;
537     private boolean mInitialTouchFromKeyguard;
538     /** Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time. */
539     private float mNextCollapseSpeedUpFactor = 1.0f;
540     private boolean mGestureWaitForTouchSlop;
541     private boolean mIgnoreXTouchSlop;
542     private boolean mExpandLatencyTracking;
543     private boolean mUseExternalTouch = false;
544     private final DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel;
545     private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
546     private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
547     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
548     private final KeyguardInteractor mKeyguardInteractor;
549     private final PowerInteractor mPowerInteractor;
550     private final CoroutineDispatcher mMainDispatcher;
551     private final SplitShadeStateController mSplitShadeStateController;
552     private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
553             mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
554     private final Runnable mHeadsUpExistenceChangedRunnable = () -> {
555         setHeadsUpAnimatingAway(false);
556         updateExpansionAndVisibility();
557     };
558     private final Runnable mMaybeHideExpandedRunnable = () -> {
559         if (getExpandedFraction() == 0.0f) {
560             postToView(mHideExpandedRunnable);
561         }
562     };
563 
564     private final ActivityStarter mActivityStarter;
565 
566     @Nullable
567     private RenderEffect mBlurRenderEffect = null;
568 
569     @Inject
NotificationPanelViewController(NotificationPanelView view, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, NotificationShadeWindowController notificationShadeWindowController, DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ShadeLogger shadeLogger, @ShadeDisplayAware ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, ShadeTouchableRegionManager shadeTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationGutsManager gutsManager, NotificationsQSContainerController notificationsQSContainerController, NotificationStackScrollLayoutController notificationStackScrollLayoutController, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, ScrimController scrimController, MediaDataManager mediaDataManager, NotificationShadeDepthController notificationShadeDepthController, AmbientState ambientState, KeyguardMediaController keyguardMediaController, TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, NavigationBarController navigationBarController, QuickSettingsControllerImpl quickSettingsController, FragmentService fragmentService, IStatusBarService statusBarService, ShadeHeaderController shadeHeaderController, ScreenOffAnimationController screenOffAnimationController, LockscreenGestureLogger lockscreenGestureLogger, ShadeExpansionStateManager shadeExpansionStateManager, ShadeRepository shadeRepository, Optional<SysUIUnfoldComponent> unfoldComponent, SysUiState sysUiState, SysUIStateDisplaysInteractor sysUIStateDisplaysInteractor, KeyguardUnlockAnimationController keyguardUnlockAnimationController, KeyguardIndicationController keyguardIndicationController, NotificationListContainer notificationListContainer, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, SystemClock systemClock, KeyguardClockInteractor keyguardClockInteractor, AlternateBouncerInteractor alternateBouncerInteractor, DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel, @Main CoroutineDispatcher mainDispatcher, KeyguardTransitionInteractor keyguardTransitionInteractor, DumpManager dumpManager, KeyguardTouchHandlingViewModel keyguardTouchHandlingViewModel, WallpaperFocalAreaViewModel wallpaperFocalAreaViewModel, KeyguardInteractor keyguardInteractor, ActivityStarter activityStarter, SharedNotificationContainerInteractor sharedNotificationContainerInteractor, ActiveNotificationsInteractor activeNotificationsInteractor, ShadeAnimationInteractor shadeAnimationInteractor, DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor, SplitShadeStateController splitShadeStateController, PowerInteractor powerInteractor, KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm, MSDLPlayer msdlPlayer, BrightnessMirrorShowingRepository brightnessMirrorShowingRepository, BlurConfig blurConfig, Lazy<ShadeDisplaysRepository> shadeDisplaysRepository)570     public NotificationPanelViewController(NotificationPanelView view,
571             NotificationWakeUpCoordinator coordinator,
572             PulseExpansionHandler pulseExpansionHandler,
573             DynamicPrivacyController dynamicPrivacyController,
574             KeyguardBypassController bypassController,
575             FalsingManager falsingManager,
576             FalsingCollector falsingCollector,
577             KeyguardStateController keyguardStateController,
578             StatusBarStateController statusBarStateController,
579             NotificationShadeWindowController notificationShadeWindowController,
580             DozeLog dozeLog,
581             DozeParameters dozeParameters,
582             CommandQueue commandQueue,
583             VibratorHelper vibratorHelper,
584             LatencyTracker latencyTracker,
585             AccessibilityManager accessibilityManager,
586             @DisplayId int displayId,
587             KeyguardUpdateMonitor keyguardUpdateMonitor,
588             MetricsLogger metricsLogger,
589             ShadeLogger shadeLogger,
590             @ShadeDisplayAware ConfigurationController configurationController,
591             Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
592             ShadeTouchableRegionManager shadeTouchableRegionManager,
593             ConversationNotificationManager conversationNotificationManager,
594             MediaHierarchyManager mediaHierarchyManager,
595             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
596             NotificationGutsManager gutsManager,
597             NotificationsQSContainerController notificationsQSContainerController,
598             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
599             KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
600             LockscreenShadeTransitionController lockscreenShadeTransitionController,
601             ScrimController scrimController,
602             MediaDataManager mediaDataManager,
603             NotificationShadeDepthController notificationShadeDepthController,
604             AmbientState ambientState,
605             KeyguardMediaController keyguardMediaController,
606             TapAgainViewController tapAgainViewController,
607             NavigationModeController navigationModeController,
608             NavigationBarController navigationBarController,
609             QuickSettingsControllerImpl quickSettingsController,
610             FragmentService fragmentService,
611             IStatusBarService statusBarService,
612             ShadeHeaderController shadeHeaderController,
613             ScreenOffAnimationController screenOffAnimationController,
614             LockscreenGestureLogger lockscreenGestureLogger,
615             ShadeExpansionStateManager shadeExpansionStateManager,
616             ShadeRepository shadeRepository,
617             Optional<SysUIUnfoldComponent> unfoldComponent,
618             SysUiState sysUiState,
619             SysUIStateDisplaysInteractor sysUIStateDisplaysInteractor,
620             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
621             KeyguardIndicationController keyguardIndicationController,
622             NotificationListContainer notificationListContainer,
623             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
624             SystemClock systemClock,
625             KeyguardClockInteractor keyguardClockInteractor,
626             AlternateBouncerInteractor alternateBouncerInteractor,
627             DreamingToLockscreenTransitionViewModel dreamingToLockscreenTransitionViewModel,
628             @Main CoroutineDispatcher mainDispatcher,
629             KeyguardTransitionInteractor keyguardTransitionInteractor,
630             DumpManager dumpManager,
631             KeyguardTouchHandlingViewModel keyguardTouchHandlingViewModel,
632             WallpaperFocalAreaViewModel wallpaperFocalAreaViewModel,
633             KeyguardInteractor keyguardInteractor,
634             ActivityStarter activityStarter,
635             SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
636             ActiveNotificationsInteractor activeNotificationsInteractor,
637             ShadeAnimationInteractor shadeAnimationInteractor,
638             DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
639             SplitShadeStateController splitShadeStateController,
640             PowerInteractor powerInteractor,
641             KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
642             MSDLPlayer msdlPlayer,
643             BrightnessMirrorShowingRepository brightnessMirrorShowingRepository,
644             BlurConfig blurConfig,
645             Lazy<ShadeDisplaysRepository> shadeDisplaysRepository) {
646         mBlurConfig = blurConfig;
647         SceneContainerFlag.assertInLegacyMode();
648         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
649             @Override
650             public void onKeyguardFadingAwayChanged() {
651                 updateExpandedHeightToMaxHeight();
652             }
653         });
654         mAmbientState = ambientState;
655         mView = view;
656         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
657         mLockscreenGestureLogger = lockscreenGestureLogger;
658         mShadeExpansionStateManager = shadeExpansionStateManager;
659         mShadeRepository = shadeRepository;
660         mShadeAnimationInteractor = shadeAnimationInteractor;
661         mShadeLog = shadeLogger;
662         mGutsManager = gutsManager;
663         mDreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel;
664         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
665         mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
666         mActiveNotificationsInteractor = activeNotificationsInteractor;
667         mKeyguardInteractor = keyguardInteractor;
668         mPowerInteractor = powerInteractor;
669         mClockPositionAlgorithm = keyguardClockPositionAlgorithm;
670         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
671             @Override
672             public void onViewAttachedToWindow(View v) {
673                 mViewName = mResources.getResourceName(mView.getId());
674             }
675 
676             @Override
677             public void onViewDetachedFromWindow(View v) {}
678         });
679 
680         mView.addOnLayoutChangeListener(new ShadeLayoutChangeListener());
681         mView.setOnTouchListener(getTouchHandler());
682         mView.setOnConfigurationChangedListener(config -> loadDimens());
683 
684         mResources = mView.getResources();
685         mKeyguardStateController = keyguardStateController;
686         mQsController = quickSettingsController;
687         mKeyguardIndicationController = keyguardIndicationController;
688         mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
689         mNotificationShadeWindowController = notificationShadeWindowController;
690         FlingAnimationUtils.Builder fauBuilder = flingAnimationUtilsBuilder.get();
691         mFlingAnimationUtils = fauBuilder
692                 .reset()
693                 .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS)
694                 .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
695                 .build();
696         mFlingAnimationUtilsClosing = fauBuilder
697                 .reset()
698                 .setMaxLengthSeconds(FLING_CLOSING_MAX_LENGTH_SECONDS)
699                 .setSpeedUpFactor(FLING_CLOSING_SPEED_UP_FACTOR)
700                 .build();
701         mFlingAnimationUtilsDismissing = fauBuilder
702                 .reset()
703                 .setMaxLengthSeconds(0.5f)
704                 .setSpeedUpFactor(0.6f)
705                 .setX2(0.6f)
706                 .setY2(0.84f)
707                 .build();
708         mLatencyTracker = latencyTracker;
709         mFalsingManager = falsingManager;
710         mDozeLog = dozeLog;
711         mNotificationsDragEnabled = mResources.getBoolean(
712                 R.bool.config_enableNotificationShadeDrag);
713         mVibratorHelper = vibratorHelper;
714         mMSDLPlayer = msdlPlayer;
715         mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
716         mShadeTouchableRegionManager = shadeTouchableRegionManager;
717         mSystemClock = systemClock;
718         mKeyguardMediaController = keyguardMediaController;
719         mMetricsLogger = metricsLogger;
720         mConfigurationController = configurationController;
721         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
722         mMediaHierarchyManager = mediaHierarchyManager;
723         mNotificationsQSContainerController = notificationsQSContainerController;
724         mNotificationListContainer = notificationListContainer;
725         mNavigationBarController = navigationBarController;
726         mNotificationsQSContainerController.init();
727         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
728         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
729         mDepthController = notificationShadeDepthController;
730         mFragmentService = fragmentService;
731         mStatusBarService = statusBarService;
732         mSplitShadeStateController = splitShadeStateController;
733         mSplitShadeEnabled =
734                 mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
735         mView.setWillNotDraw(!DEBUG_DRAWABLE);
736         mShadeHeaderController = shadeHeaderController;
737         mAnimateBack = predictiveBackAnimateShade();
738         mFalsingCollector = falsingCollector;
739         mWakeUpCoordinator = coordinator;
740         mMainDispatcher = mainDispatcher;
741         mAccessibilityManager = accessibilityManager;
742         mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
743         setAlpha(255, false /* animate */);
744         mCommandQueue = commandQueue;
745         mDisplayId = displayId;
746         mPulseExpansionHandler = pulseExpansionHandler;
747         mDozeParameters = dozeParameters;
748         mScrimController = scrimController;
749         mMediaDataManager = mediaDataManager;
750         mTapAgainViewController = tapAgainViewController;
751         mSysUiState = sysUiState;
752         mSysUIStateDisplaysInteractor = sysUIStateDisplaysInteractor;
753         mShadeDisplaysRepository = shadeDisplaysRepository;
754         mKeyguardBypassController = bypassController;
755         mUpdateMonitor = keyguardUpdateMonitor;
756         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
757         dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
758         quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
759         quickSettingsController.setApplyClippingImmediatelyListener(
760                 this::onQsClippingImmediatelyApplied);
761         quickSettingsController.setFlingQsWithoutClickListener(this::onFlingQsWithoutClick);
762         quickSettingsController.setExpansionHeightSetToMaxListener(this::onExpansionHeightSetToMax);
763         shadeExpansionStateManager.addStateListener(this::onPanelStateChanged);
764         mConversationNotificationManager = conversationNotificationManager;
765         mScreenOffAnimationController = screenOffAnimationController;
766         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
767         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
768         mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
769 
770         int currentMode = navigationModeController.addListener(
771                 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
772         mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode);
773 
774         mView.setBackgroundColor(Color.TRANSPARENT);
775         ShadeAttachStateChangeListener
776                 onAttachStateChangeListener = new ShadeAttachStateChangeListener();
777         mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
778         if (mView.isAttachedToWindow()) {
779             onAttachStateChangeListener.onViewAttachedToWindow(mView);
780         }
781 
782         mView.setOnApplyWindowInsetsListener((v, insets) -> onApplyShadeWindowInsets(insets));
783 
784         if (DEBUG_DRAWABLE) {
785             mView.getOverlay().add(new DebugDrawable(this, mView,
786                     mNotificationStackScrollLayoutController, mQsController));
787         }
788 
789         mKeyguardUnfoldTransition = unfoldComponent.map(
790                 SysUIUnfoldComponent::getKeyguardUnfoldTransition);
791 
792         mKeyguardClockInteractor = keyguardClockInteractor;
793         mWallpaperFocalAreaViewModel = wallpaperFocalAreaViewModel;
794         KeyguardTouchViewBinder.bind(
795                 mView.requireViewById(R.id.keyguard_long_press),
796                 keyguardTouchHandlingViewModel,
797                 (x, y) -> {
798                     onEmptySpaceClick(x, y);
799                     return Unit.INSTANCE;
800                 },
801                 mFalsingManager);
802         mActivityStarter = activityStarter;
803         mBrightnessMirrorShowingRepository = brightnessMirrorShowingRepository;
804         mIsBrightnessMirrorShowing.setValue(
805                 mBrightnessMirrorShowingRepository.isShowing().getValue()
806         );
807         onFinishInflate();
808         keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
809                 new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() {
810                     @Override
811                     public void onUnlockAnimationFinished() {
812                         unlockAnimationFinished();
813                     }
814 
815                     @Override
816                     public void onUnlockAnimationStarted(
817                             boolean playingCannedAnimation,
818                             boolean isWakeAndUnlockNotFromDream,
819                             long startDelay,
820                             long unlockAnimationDuration) {
821                         unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlockNotFromDream,
822                                 startDelay);
823                     }
824                 });
825         mAlternateBouncerInteractor = alternateBouncerInteractor;
826         dumpManager.registerDumpable(this);
827     }
828 
unlockAnimationFinished()829     private void unlockAnimationFinished() {
830         // Make sure the clock is in the correct position after the unlock animation
831         // so that it's not in the wrong place when we show the keyguard again.
832         positionClockAndNotifications(true /* forceClockUpdate */);
833         mScrimController.onUnlockAnimationFinished();
834     }
835 
unlockAnimationStarted( boolean playingCannedAnimation, boolean isWakeAndUnlockNotFromDream, long unlockAnimationStartDelay)836     private void unlockAnimationStarted(
837             boolean playingCannedAnimation,
838             boolean isWakeAndUnlockNotFromDream,
839             long unlockAnimationStartDelay) {
840         // Disable blurs while we're unlocking so that panel expansion does not
841         // cause blurring. This will eventually be re-enabled by the panel view on
842         // ACTION_UP, since the user's finger might still be down after a swipe to
843         // unlock gesture, and we don't want that to cause blurring either.
844         mDepthController.setBlursDisabledForUnlock(isTracking());
845 
846         if (playingCannedAnimation && !isWakeAndUnlockNotFromDream) {
847             // Hide the panel so it's not in the way or the surface behind the
848             // keyguard, which will be appearing. If we're wake and unlocking, the
849             // lock screen is hidden instantly so should not be flung away.
850             if (isTracking() || mIsFlinging) {
851                 // Instant collapse the notification panel since the notification
852                 // panel is already in the middle animating
853                 onTrackingStopped(false);
854                 instantCollapse();
855             } else {
856                 mView.animate().cancel();
857                 mView.postDelayed(() -> {
858                     instantCollapse();
859                 }, unlockAnimationStartDelay);
860             }
861         }
862     }
863 
864     @VisibleForTesting
onFinishInflate()865     void onFinishInflate() {
866         loadDimens();
867         mKeyguardStatusBarViewController =
868                 mKeyguardStatusBarViewComponentFactory.build(
869                                 mView.findViewById(R.id.keyguard_header),
870                                 mShadeViewStateProvider)
871                         .getKeyguardStatusBarViewController();
872         mKeyguardStatusBarViewController.init();
873         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
874         mNotificationStackScrollLayoutController.setOnHeightChangedListener(
875                 new NsslHeightChangedListener());
876         mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
877                 mOnEmptySpaceClickListener);
878         mQsController.init();
879         mShadeHeadsUpTracker.addTrackingHeadsUpListener(
880                 mNotificationStackScrollLayoutController::setTrackingHeadsUp);
881         mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
882         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
883             @Override
884             public void onFullyHiddenChanged(boolean isFullyHidden) {
885                 mKeyguardStatusBarViewController.updateForHeadsUp();
886             }
887         });
888 
889         mView.setRtlChangeListener(layoutDirection -> {
890             if (layoutDirection != mOldLayoutDirection) {
891                 mOldLayoutDirection = layoutDirection;
892             }
893         });
894 
895         mView.setAccessibilityDelegate(mAccessibilityDelegate);
896         if (mSplitShadeEnabled) {
897             updateResources();
898         }
899 
900         mTapAgainViewController.init();
901         mShadeHeaderController.init();
902         mShadeHeaderController.setShadeCollapseAction(
903                 () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f));
904 
905         // Dreaming->Lockscreen
906         collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
907                 setDreamLockscreenTransitionAlpha(),
908                 mMainDispatcher);
909 
910         collectFlow(mView, mKeyguardTransitionInteractor.transition(
911                 Edge.Companion.create(AOD, LOCKSCREEN)),
912                 (TransitionStep step) -> {
913                 if (step.getTransitionState() == TransitionState.FINISHED) {
914                     updateExpandedHeightToMaxHeight();
915                 }
916             }, mMainDispatcher);
917 
918         if (com.android.systemui.Flags.bouncerUiRevamp()) {
919             collectFlow(mView, mKeyguardInteractor.primaryBouncerShowing,
920                     this::handleBouncerShowingChanged);
921         }
922 
923         // Ensures that flags are updated when an activity launches
924         collectFlow(mView,
925                 mShadeAnimationInteractor.isLaunchingActivity(),
926                 isLaunchingActivity -> {
927                     if (isLaunchingActivity) {
928                         updateSystemUiStateFlags();
929                     }
930                 },
931                 mMainDispatcher);
932         if (QSComposeFragment.isEnabled()) {
933             collectFlow(mView,
934                     mBrightnessMirrorShowingRepository.isShowing(),
935                     this::onBrightnessMirrorShowingChanged
936             );
937         }
938     }
939 
onBrightnessMirrorShowingChanged(boolean isShowing)940     private void onBrightnessMirrorShowingChanged(boolean isShowing) {
941         if (!mIsBrightnessMirrorShowing.getValue()) {
942             // Immediately set the value of the mirror if we are not showing the mirror, and then
943             // start fading the shade.
944             mIsBrightnessMirrorShowing.setValue(isShowing);
945         }
946         setAlpha(isShowing ? 0 : 255, true);
947     }
948 
949     @androidx.annotation.NonNull
950     @Override
isShowing()951     public StateFlow<Boolean> isShowing() {
952         return mIsBrightnessMirrorShowing;
953     }
954 
955     @Override
setMirrorShowing(boolean showing)956     public void setMirrorShowing(boolean showing) {
957         mBrightnessMirrorShowingRepository.setMirrorShowing(showing);
958     }
959 
960     @VisibleForTesting
loadDimens()961     void loadDimens() {
962         final ViewConfiguration configuration = ViewConfiguration.get(this.mView.getContext());
963         mTouchSlop = configuration.getScaledTouchSlop();
964         mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
965         mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
966         mPanelFlingOvershootAmount = mResources.getDimension(R.dimen.panel_overshoot_amount);
967         mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get()
968                 .setMaxLengthSeconds(0.4f).build();
969         mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
970         mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext());
971         mClockPositionAlgorithm.loadDimens(mView.getContext(), mResources);
972         int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext());
973         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
974                 R.dimen.heads_up_status_bar_padding);
975         mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize(
976                 R.dimen.pulse_expansion_max_top_overshoot);
977         mSplitShadeScrimTransitionDistance = mResources.getDimensionPixelSize(
978                 R.dimen.split_shade_scrim_transition_distance);
979         // TODO (b/265193930): remove this and make QsController listen to NotificationPanelViews
980         mQsController.loadDimens();
981     }
982 
handleBouncerShowingChanged(Boolean isBouncerShowing)983     private void handleBouncerShowingChanged(Boolean isBouncerShowing) {
984         if (!com.android.systemui.Flags.bouncerUiRevamp()) return;
985         if (isBouncerShowing && isExpanded()) {
986             if (mBlurRenderEffect == null) {
987                 mBlurRenderEffect = RenderEffect.createBlurEffect(
988                         mBlurConfig.getMaxBlurRadiusPx(),
989                         mBlurConfig.getMaxBlurRadiusPx(),
990                         Shader.TileMode.CLAMP);
991             }
992             debugLog("Applying blur RenderEffect to shade.");
993             mView.setRenderEffect(mBlurRenderEffect);
994         } else {
995             debugLog("Resetting blur RenderEffect on shade.");
996             mView.setRenderEffect(null);
997         }
998     }
999 
1000     @Override
updateResources()1001     public void updateResources() {
1002         try {
1003             Trace.beginSection("NSSLC#updateResources");
1004             final boolean newSplitShadeEnabled =
1005                     mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
1006             final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
1007             mSplitShadeEnabled = newSplitShadeEnabled;
1008             mQsController.updateResources();
1009             mNotificationsQSContainerController.updateResources();
1010             updateKeyguardStatusViewAlignment();
1011             mKeyguardMediaController.refreshMediaPosition(
1012                     "NotificationPanelViewController.updateResources");
1013 
1014             if (splitShadeChanged) {
1015                 if (isPanelVisibleBecauseOfHeadsUp()) {
1016                     // workaround for b/324642496, because HUNs set state to OPENING
1017                     onPanelStateChanged(STATE_CLOSED);
1018                 }
1019                 onSplitShadeEnabledChanged();
1020             }
1021 
1022             mSplitShadeFullTransitionDistance =
1023                     mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
1024         } finally {
1025             Trace.endSection();
1026         }
1027     }
1028 
onSplitShadeEnabledChanged()1029     private void onSplitShadeEnabledChanged() {
1030         mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
1031         // Reset any left over overscroll state. It is a rare corner case but can happen.
1032         mQsController.setOverScrollAmount(0);
1033         mScrimController.setNotificationsOverScrollAmount(0);
1034 
1035         // when we switch between split shade and regular shade we want to enforce setting qs to
1036         // the default state: expanded for split shade and collapsed otherwise
1037         if (!isKeyguardShowing() && isPanelExpanded()) {
1038             mQsController.setExpanded(mSplitShadeEnabled);
1039         }
1040         if (isKeyguardShowing() && mQsController.getExpanded() && mSplitShadeEnabled) {
1041             // In single column keyguard - when you swipe from the top - QS is fully expanded and
1042             // StatusBarState is KEYGUARD. That state doesn't make sense for split shade,
1043             // where notifications are always visible and we effectively go to fully expanded
1044             // shade, that is SHADE_LOCKED.
1045             // Also we might just be switching from regular expanded shade, so we don't want
1046             // to force state transition if it's already correct.
1047             mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED, /* force= */false);
1048         }
1049         updateClockAppearance();
1050         mQsController.updateQsState();
1051     }
1052 
1053     @VisibleForTesting
reInflateViews()1054     void reInflateViews() {
1055         debugLog("reInflateViews");
1056         updateResources();
1057         mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
1058                 mStatusBarStateController.getInterpolatedDozeAmount());
1059     }
1060 
1061     /** Sets a listener to be notified when the shade starts opening or finishes closing. */
setOpenCloseListener(OpenCloseListener openCloseListener)1062     public void setOpenCloseListener(OpenCloseListener openCloseListener) {
1063         SceneContainerFlag.assertInLegacyMode();
1064         mOpenCloseListener = openCloseListener;
1065     }
1066 
1067     /** Sets a listener to be notified when touch tracking begins. */
setTrackingStartedListener(TrackingStartedListener trackingStartedListener)1068     public void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
1069         mTrackingStartedListener = trackingStartedListener;
1070     }
1071 
updateGestureExclusionRect()1072     private void updateGestureExclusionRect() {
1073         Rect exclusionRect = calculateGestureExclusionRect();
1074         mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList()
1075                 : Collections.singletonList(exclusionRect));
1076     }
1077 
calculateGestureExclusionRect()1078     private Rect calculateGestureExclusionRect() {
1079         Rect exclusionRect = null;
1080         Region touchableRegion = mShadeTouchableRegionManager.calculateTouchableRegion();
1081         if (isFullyCollapsed() && touchableRegion != null) {
1082             // Note: The manager also calculates the non-pinned touchable region
1083             exclusionRect = touchableRegion.getBounds();
1084         }
1085         return exclusionRect != null ? exclusionRect : EMPTY_RECT;
1086     }
1087 
setIsFullWidth(boolean isFullWidth)1088     private void setIsFullWidth(boolean isFullWidth) {
1089         mIsFullWidth = isFullWidth;
1090         mScrimController.setClipsQsScrim(isFullWidth);
1091         mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth);
1092         mQsController.setNotificationPanelFullWidth(isFullWidth);
1093     }
1094 
1095     /**
1096      * Positions the clock and notifications dynamically depending on how many notifications are
1097      * showing.
1098      */
positionClockAndNotifications()1099     void positionClockAndNotifications() {
1100         positionClockAndNotifications(false /* forceUpdate */);
1101     }
1102 
1103     /**
1104      * Positions the clock and notifications dynamically depending on how many notifications are
1105      * showing.
1106      *
1107      * @param forceClockUpdate Should the clock be updated even when not on keyguard
1108      */
positionClockAndNotifications(boolean forceClockUpdate)1109     private void positionClockAndNotifications(boolean forceClockUpdate) {
1110         int stackScrollerPadding;
1111         boolean onKeyguard = isKeyguardShowing();
1112 
1113         if (onKeyguard || forceClockUpdate) {
1114             updateClockAppearance();
1115         }
1116         if (!onKeyguard) {
1117             if (mSplitShadeEnabled) {
1118                 // Quick settings are not on the top of the notifications
1119                 // when in split shade mode (they are on the left side),
1120                 // so we should not add a padding for them
1121                 stackScrollerPadding = 0;
1122             } else {
1123                 stackScrollerPadding = mQsController.getHeaderHeight();
1124             }
1125         } else {
1126             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
1127         }
1128 
1129         mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
1130 
1131         mStackScrollerMeasuringPass++;
1132         requestScrollerTopPaddingUpdate();
1133         mStackScrollerMeasuringPass = 0;
1134         mAnimateNextPositionUpdate = false;
1135     }
1136 
updateClockAppearance()1137     private void updateClockAppearance() {
1138         mKeyguardClockInteractor.setClockSize(computeDesiredClockSize());
1139         updateKeyguardStatusViewAlignment();
1140 
1141         float darkAmount =
1142                 mScreenOffAnimationController.shouldExpandNotifications()
1143                         ? 1.0f : mInterpolatedDarkAmount;
1144 
1145         mClockPositionAlgorithm.setup(
1146                 darkAmount, mOverStretchAmount,
1147                 mKeyguardBypassController.getBypassEnabled(),
1148                 mQsController.getHeaderHeight(),
1149                 mSplitShadeEnabled);
1150         mClockPositionAlgorithm.run(mClockPositionResult);
1151     }
1152 
getClockPositionResult()1153     KeyguardClockPositionAlgorithm.Result getClockPositionResult() {
1154         return mClockPositionResult;
1155     }
1156 
computeDesiredClockSize()1157     private ClockSize computeDesiredClockSize() {
1158         if (mSplitShadeEnabled) {
1159             return computeDesiredClockSizeForSplitShade();
1160         }
1161         return computeDesiredClockSizeForSingleShade();
1162     }
1163 
computeDesiredClockSizeForSingleShade()1164     private ClockSize computeDesiredClockSizeForSingleShade() {
1165         if (hasVisibleNotifications()) {
1166             return ClockSize.SMALL;
1167         }
1168         return ClockSize.LARGE;
1169     }
1170 
computeDesiredClockSizeForSplitShade()1171     private ClockSize computeDesiredClockSizeForSplitShade() {
1172         // Media is not visible to the user on AOD.
1173         boolean isMediaVisibleToUser =
1174                 mMediaDataManager.hasActiveMediaOrRecommendation() && !isOnAod();
1175         if (isMediaVisibleToUser) {
1176             // When media is visible, it overlaps with the large clock. Use small clock instead.
1177             return ClockSize.SMALL;
1178         }
1179         return ClockSize.LARGE;
1180     }
1181 
updateKeyguardStatusViewAlignment()1182     private void updateKeyguardStatusViewAlignment() {
1183         boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
1184         mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
1185     }
1186 
shouldKeyguardStatusViewBeCentered()1187     private boolean shouldKeyguardStatusViewBeCentered() {
1188         if (mSplitShadeEnabled) {
1189             return shouldKeyguardStatusViewBeCenteredInSplitShade();
1190         }
1191         return true;
1192     }
1193 
shouldKeyguardStatusViewBeCenteredInSplitShade()1194     private boolean shouldKeyguardStatusViewBeCenteredInSplitShade() {
1195         if (!hasVisibleNotifications()) {
1196             // No notifications visible. It is safe to have the clock centered as there will be no
1197             // overlap.
1198             return true;
1199         }
1200         if (mNotificationListContainer.hasPulsingNotifications()) {
1201             // Pulsing notification appears on the right. Move clock left to avoid overlap.
1202             return false;
1203         }
1204         // "Visible" notifications are actually not visible on AOD (unless pulsing), so it is safe
1205         // to center the clock without overlap.
1206         return isOnAod();
1207     }
1208 
isOnAod()1209     private boolean isOnAod() {
1210         return mDozing && mDozeParameters.getAlwaysOn();
1211     }
1212 
hasVisibleNotifications()1213     private boolean hasVisibleNotifications() {
1214         return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
1215                 || mMediaDataManager.hasActiveMediaOrRecommendation();
1216     }
1217 
1218     @Override
transitionToExpandedShade(long delay)1219     public void transitionToExpandedShade(long delay) {
1220         mNotificationStackScrollLayoutController.goToFullShade(delay);
1221         mView.requestLayout();
1222         mAnimateNextPositionUpdate = true;
1223     }
1224 
1225     @Override
animateCollapseQs(boolean fullyCollapse)1226     public void animateCollapseQs(boolean fullyCollapse) {
1227         if (mSplitShadeEnabled) {
1228             collapse(true, false, 1.0f);
1229         } else {
1230             mQsController.animateCloseQs(fullyCollapse);
1231         }
1232     }
1233 
1234     @Override
resetViews(boolean animate)1235     public void resetViews(boolean animate) {
1236         mGutsManager.closeAndSaveGuts(true /* leavebehind */, true /* force */,
1237                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
1238         if (animate && !isFullyCollapsed()) {
1239             animateCollapseQs(true);
1240         } else {
1241             closeQsIfPossible();
1242         }
1243         mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate,
1244                 !animate /* cancelAnimators */);
1245         mNotificationStackScrollLayoutController.resetScrollPosition();
1246     }
1247 
collapse(boolean animate, boolean delayed, float speedUpFactor)1248     public void collapse(boolean animate, boolean delayed, float speedUpFactor) {
1249         boolean waiting = false;
1250         if (animate && !isFullyCollapsed()) {
1251             collapse(delayed, speedUpFactor);
1252             waiting = true;
1253         } else {
1254             resetViews(false /* animate */);
1255             setExpandedFraction(0); // just in case
1256         }
1257         if (!waiting) {
1258             // it's possible that nothing animated, so we replicate the termination
1259             // conditions of panelExpansionChanged here
1260             // TODO(b/200063118): This can likely go away in a future refactor CL.
1261             getShadeExpansionStateManager().updateState(STATE_CLOSED);
1262         }
1263     }
1264 
collapse(boolean delayed, float speedUpFactor)1265     public void collapse(boolean delayed, float speedUpFactor) {
1266         if (!canBeCollapsed()) {
1267             return;
1268         }
1269 
1270         if (mQsController.getExpanded()) {
1271             mQsController.setExpandImmediate(true);
1272             setShowShelfOnly(true);
1273         }
1274         debugLog("collapse: %s", this);
1275         if (canBeCollapsed()) {
1276             cancelHeightAnimator();
1277             notifyExpandingStarted();
1278 
1279             // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
1280             setClosing(true);
1281             mUpdateFlingOnLayout = false;
1282             if (delayed) {
1283                 mNextCollapseSpeedUpFactor = speedUpFactor;
1284                 this.mView.postDelayed(mFlingCollapseRunnable, 120);
1285             } else {
1286                 fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
1287             }
1288         }
1289     }
1290 
setShowShelfOnly(boolean shelfOnly)1291     private void setShowShelfOnly(boolean shelfOnly) {
1292         mNotificationStackScrollLayoutController.setShouldShowShelfOnly(
1293                 shelfOnly && !mSplitShadeEnabled);
1294     }
1295 
1296     @VisibleForTesting
cancelHeightAnimator()1297     void cancelHeightAnimator() {
1298         if (mHeightAnimator != null) {
1299             if (mHeightAnimator.isRunning()) {
1300                 mPanelUpdateWhenAnimatorEnds = false;
1301             }
1302             mHeightAnimator.cancel();
1303         }
1304         endClosing();
1305     }
1306 
1307     @Override
cancelAnimation()1308     public void cancelAnimation() {
1309         mView.animate().cancel();
1310     }
1311 
expandToQs()1312     public void expandToQs() {
1313         if (mQsController.isExpansionEnabled()) {
1314             mQsController.setExpandImmediate(true);
1315             setShowShelfOnly(true);
1316         }
1317         if (mSplitShadeEnabled && isKeyguardShowing()) {
1318             // It's a special case as this method is likely to not be initiated by finger movement
1319             // but rather called from adb shell or accessibility service.
1320             // We're using LockscreenShadeTransitionController because on lockscreen that's the
1321             // source of truth for all shade motion. Not using it would make part of state to be
1322             // outdated and will cause bugs. Ideally we'd use this controller also for non-split
1323             // case but currently motion in portrait looks worse than when using flingSettings.
1324             // TODO: make below function transitioning smoothly also in portrait with null target
1325             mLockscreenShadeTransitionController.goToLockedShade(
1326                     /* expandedView= */null, /* needsQSAnimation= */true);
1327         } else if (isFullyCollapsed()) {
1328             expand(true /* animate */);
1329         } else {
1330             mQsController.traceQsJank(true /* startTracing */, false /* wasCancelled */);
1331             mQsController.flingQs(0, FLING_EXPAND);
1332         }
1333     }
1334 
1335     @Override
expandToNotifications()1336     public void expandToNotifications() {
1337         if (mSplitShadeEnabled && (isShadeFullyExpanded() || isExpandingOrCollapsing())) {
1338             return;
1339         }
1340         if (mQsController.getExpanded()) {
1341             mQsController.flingQs(0, FLING_COLLAPSE);
1342         } else {
1343             expand(true /* animate */);
1344         }
1345     }
1346 
fling(float vel)1347     private void fling(float vel) {
1348         if (mGestureRecorder != null) {
1349             mGestureRecorder.tag("fling " + ((vel > 0) ? "open" : "closed"),
1350                     "notifications,v=" + vel);
1351         }
1352         fling(vel, true, 1.0f /* collapseSpeedUpFactor */, false);
1353     }
1354 
1355     @VisibleForTesting
flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1356     void flingToHeight(float vel, boolean expand, float target,
1357             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
1358         mQsController.setLastShadeFlingWasExpanding(expand);
1359         mHeadsUpTouchHelper.notifyFling(!expand);
1360         mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
1361         setClosingWithAlphaFadeout(!expand && !isKeyguardShowing() && getFadeoutAlpha() == 1.0f);
1362         mNotificationStackScrollLayoutController.setPanelFlinging(true);
1363         mShadeRepository.setCurrentFling(new FlingInfo(expand, vel));
1364         if (target == mExpandedHeight && mOverExpansion == 0.0f) {
1365             // We're at the target and didn't fling and there's no overshoot
1366             onFlingEnd(false /* cancelled */);
1367             return;
1368         }
1369         mIsFlinging = true;
1370         // we want to perform an overshoot animation when flinging open
1371         final boolean addOverscroll =
1372                 expand
1373                         && mStatusBarStateController.getState() != KEYGUARD
1374                         && mOverExpansion == 0.0f
1375                         && vel >= 0;
1376         final boolean shouldSpringBack = addOverscroll || (mOverExpansion != 0.0f && expand);
1377         float overshootAmount = 0.0f;
1378         if (addOverscroll) {
1379             // Let's overshoot depending on the amount of velocity
1380             overshootAmount = MathUtils.lerp(
1381                     0.2f,
1382                     1.0f,
1383                     MathUtils.saturate(vel
1384                             / (this.mFlingAnimationUtils.getHighVelocityPxPerSecond()
1385                             * FACTOR_OF_HIGH_VELOCITY_FOR_MAX_OVERSHOOT)));
1386             overshootAmount += mOverExpansion / mPanelFlingOvershootAmount;
1387         }
1388         ValueAnimator animator = createHeightAnimator(target, overshootAmount);
1389         if (expand) {
1390             maybeVibrateOnOpening(true /* openingWithTouch */);
1391             if (expandBecauseOfFalsing && vel < 0) {
1392                 vel = 0;
1393             }
1394             this.mFlingAnimationUtils.apply(animator, mExpandedHeight,
1395                     target + overshootAmount * mPanelFlingOvershootAmount, vel,
1396                     this.mView.getHeight());
1397             if (vel == 0) {
1398                 animator.setDuration(SHADE_OPEN_SPRING_OUT_DURATION);
1399             }
1400         } else {
1401             mHasVibratedOnOpen = false;
1402             if (shouldUseDismissingAnimation()) {
1403                 if (vel == 0) {
1404                     animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
1405                     long duration = (long) (200 + mExpandedHeight / this.mView.getHeight() * 100);
1406                     animator.setDuration(duration);
1407                 } else {
1408                     mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
1409                             this.mView.getHeight());
1410                 }
1411             } else {
1412                 mFlingAnimationUtilsClosing.apply(
1413                         animator, mExpandedHeight, target, vel, this.mView.getHeight());
1414             }
1415 
1416             // Make it shorter if we run a canned animation
1417             if (vel == 0) {
1418                 animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
1419             }
1420             if (mFixedDuration != NO_FIXED_DURATION) {
1421                 animator.setDuration(mFixedDuration);
1422             }
1423 
1424             // Reset Predictive Back animation's transform after Shade is completely hidden.
1425             animator.addListener(new AnimatorListenerAdapter() {
1426                 @Override
1427                 public void onAnimationEnd(Animator animation) {
1428                     resetBackTransformation();
1429                 }
1430             });
1431         }
1432         animator.addListener(new AnimatorListenerAdapter() {
1433             private boolean mCancelled;
1434 
1435             @Override
1436             public void onAnimationStart(Animator animation) {
1437                 if (!mStatusBarStateController.isDozing()) {
1438                     mQsController.beginJankMonitoring(isFullyCollapsed());
1439                 }
1440             }
1441 
1442             @Override
1443             public void onAnimationCancel(Animator animation) {
1444                 mCancelled = true;
1445             }
1446 
1447             @Override
1448             public void onAnimationEnd(Animator animation) {
1449                 if (shouldSpringBack && !mCancelled) {
1450                     // After the shade is flung open to an overscrolled state, spring back
1451                     // the shade by reducing section padding to 0.
1452                     springBack();
1453                 } else {
1454                     onFlingEnd(mCancelled);
1455                 }
1456             }
1457         });
1458         if (!mScrimController.isScreenOn()) {
1459             animator.setDuration(1);
1460         }
1461         setAnimator(animator);
1462         animator.start();
1463     }
1464 
1465     @VisibleForTesting
onFlingEnd(boolean cancelled)1466     void onFlingEnd(boolean cancelled) {
1467         mIsFlinging = false;
1468         mExpectingSynthesizedDown = false;
1469         // No overshoot when the animation ends
1470         setOverExpansionInternal(0);
1471         setAnimator(null);
1472         mKeyguardStateController.notifyPanelFlingEnd();
1473         if (!cancelled) {
1474             mQsController.endJankMonitoring();
1475             notifyExpandingFinished();
1476         } else {
1477             mQsController.cancelJankMonitoring();
1478         }
1479         updateExpansionAndVisibility();
1480         mNotificationStackScrollLayoutController.setPanelFlinging(false);
1481         mShadeLog.d("onFlingEnd called"); // TODO(b/277909752): remove log when bug is fixed
1482         // expandImmediate should be always reset at the end of animation
1483         mQsController.setExpandImmediate(false);
1484         mShadeRepository.setCurrentFling(null);
1485     }
1486 
isInContentBounds(float x, float y)1487     private boolean isInContentBounds(float x, float y) {
1488         float stackScrollerX = mNotificationStackScrollLayoutController.getX();
1489         return !mNotificationStackScrollLayoutController
1490                 .isBelowLastNotification(x - stackScrollerX, y)
1491                 && stackScrollerX < x
1492                 && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
1493     }
1494 
initDownStates(MotionEvent event)1495     private void initDownStates(MotionEvent event) {
1496         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1497             mDozingOnDown = mDozing;
1498             mDownX = event.getX();
1499             mDownY = event.getY();
1500             mCollapsedOnDown = isFullyCollapsed();
1501             mQsController.setCollapsedOnDown(mCollapsedOnDown);
1502             mIsPanelCollapseOnQQS = mQsController.canPanelCollapseOnQQS(mDownX, mDownY);
1503             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
1504             mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
1505             mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
1506             // When false, down but not synthesized motion event.
1507             mLastEventSynthesizedDown = mExpectingSynthesizedDown;
1508             mLastDownEvents.insert(
1509                     event.getEventTime(),
1510                     mDownX,
1511                     mDownY,
1512                     mQsController.updateAndGetTouchAboveFalsingThreshold(),
1513                     mDozingOnDown,
1514                     mCollapsedOnDown,
1515                     mIsPanelCollapseOnQQS,
1516                     mListenForHeadsUp,
1517                     mAllowExpandForSmallExpansion,
1518                     mTouchSlopExceededBeforeDown,
1519                     mLastEventSynthesizedDown
1520             );
1521         } else {
1522             // not down event at all.
1523             mLastEventSynthesizedDown = false;
1524         }
1525     }
1526 
flingExpandsQs(float vel)1527     boolean flingExpandsQs(float vel) {
1528         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
1529             return mQsController.computeExpansionFraction() > 0.5f;
1530         } else {
1531             return vel > 0;
1532         }
1533     }
1534 
shouldExpandWhenNotFlinging()1535     private boolean shouldExpandWhenNotFlinging() {
1536         if (getExpandedFraction() > 0.5f) {
1537             return true;
1538         }
1539         if (mAllowExpandForSmallExpansion) {
1540             // When we get a touch that came over from launcher, the velocity isn't always correct
1541             // Let's err on expanding if the gesture has been reasonably slow
1542             long timeSinceDown = mSystemClock.uptimeMillis() - mDownTime;
1543             return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
1544         }
1545         return false;
1546     }
1547 
getOpeningHeight()1548     private float getOpeningHeight() {
1549         return mNotificationStackScrollLayoutController.getOpeningHeight();
1550     }
1551 
getDisplayDensity()1552     float getDisplayDensity() {
1553         if (ShadeWindowGoesAround.isEnabled()) {
1554             return mView.getContext().getResources().getConfiguration().densityDpi;
1555         } else {
1556             return mCentralSurfaces.getDisplayDensity();
1557         }
1558     }
1559 
1560     /** Return whether a touch is near the gesture handle at the bottom of screen */
isInGestureNavHomeHandleArea(float y)1561     boolean isInGestureNavHomeHandleArea(float y) {
1562         return mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight;
1563     }
1564 
1565     @Override
startInputFocusTransfer()1566     public void startInputFocusTransfer() {
1567         if (!mCommandQueue.panelsEnabled()) {
1568             return;
1569         }
1570         if (!isFullyCollapsed()) {
1571             return;
1572         }
1573         mExpectingSynthesizedDown = true;
1574         onTrackingStarted();
1575         updatePanelExpanded();
1576     }
1577 
1578     @Override
cancelInputFocusTransfer()1579     public void cancelInputFocusTransfer() {
1580         if (!mCommandQueue.panelsEnabled()) {
1581             return;
1582         }
1583         if (mExpectingSynthesizedDown) {
1584             mExpectingSynthesizedDown = false;
1585             collapse(false /* delayed */, 1.0f /* speedUpFactor */);
1586             onTrackingStopped(false);
1587         }
1588     }
1589 
1590     /**
1591      * There are two scenarios behind this function call. First, input focus transfer has
1592      * successfully happened and this view already received synthetic DOWN event.
1593      * (mExpectingSynthesizedDown == false). Do nothing.
1594      * <p>
1595      * Second, before input focus transfer finished, user may have lifted finger in previous window
1596      * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
1597      * this case, we use the velocity to trigger fling event.
1598      */
1599     @Override
finishInputFocusTransfer(final float velocity)1600     public void finishInputFocusTransfer(final float velocity) {
1601         if (!mCommandQueue.panelsEnabled()) {
1602             return;
1603         }
1604         if (mExpectingSynthesizedDown) {
1605             // Window never will receive touch events that typically trigger haptic on open.
1606             maybeVibrateOnOpening(false /* openingWithTouch */);
1607             fling(velocity > 1f ? 1000f * velocity : 0  /* expand */);
1608             onTrackingStopped(false);
1609         }
1610     }
1611 
flingExpands(float vel, float vectorVel, float x, float y)1612     private boolean flingExpands(float vel, float vectorVel, float x, float y) {
1613         boolean expands = true;
1614         if (!this.mFalsingManager.isUnlockingDisabled()) {
1615             @Classifier.InteractionType int interactionType = y - mInitialExpandY > 0
1616                     ? QUICK_SETTINGS : (
1617                     mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
1618             if (!isFalseTouch(x, y, interactionType)) {
1619                 mShadeLog.logFlingExpands(vel, vectorVel, interactionType,
1620                         this.mFlingAnimationUtils.getMinVelocityPxPerSecond(),
1621                         mExpandedFraction > 0.5f, mAllowExpandForSmallExpansion);
1622                 if (Math.abs(vectorVel) < this.mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
1623                     expands = shouldExpandWhenNotFlinging();
1624                 } else {
1625                     expands = vel > 0;
1626                 }
1627             }
1628         }
1629 
1630         // If we are already running a QS expansion, make sure that we keep the panel open.
1631         if (mQsController.isExpansionAnimating()) {
1632             expands = true;
1633         }
1634         return expands;
1635     }
1636 
shouldGestureWaitForTouchSlop()1637     private boolean shouldGestureWaitForTouchSlop() {
1638         if (mExpectingSynthesizedDown) {
1639             mExpectingSynthesizedDown = false;
1640             return false;
1641         }
1642         return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
1643     }
1644 
getFalsingThreshold()1645     int getFalsingThreshold() {
1646         float factor = ShadeViewController.getFalsingThresholdFactor(getWakefulness());
1647         return (int) (mQsController.getFalsingThreshold() * factor);
1648     }
1649 
getWakefulness()1650     private WakefulnessModel getWakefulness() {
1651         return mPowerInteractor.getDetailedWakefulness().getValue();
1652     }
1653 
1654     /**
1655      * When the back gesture triggers a fully-expanded shade --> QQS shade collapse transition,
1656      * the expansionFraction goes down from 1.0 --> 0.0 (collapsing), so the current "squish" amount
1657      * (mCurrentBackProgress) must be un-applied from various UI elements in tandem, such that,
1658      * as the shade ends up in its half-expanded state (with QQS above), it is back at 100% scale.
1659      * Without this, the shade would collapse, and stay squished.
1660      */
adjustBackAnimationScale(float expansionFraction)1661     void adjustBackAnimationScale(float expansionFraction) {
1662         if (expansionFraction > 0.0f) { // collapsing
1663             float animatedFraction = expansionFraction * mCurrentBackProgress;
1664             applyBackScaling(animatedFraction);
1665         } else {
1666             // collapsed! reset, so that if we re-expand shade, it won't start off "squished"
1667             mCurrentBackProgress = 0;
1668         }
1669     }
1670 
1671     //TODO(b/270981268): allow cancelling back animation mid-flight
1672     @Override
onBackPressed()1673     public void onBackPressed() {
1674         closeQsIfPossible();
1675     }
1676 
1677     @Override
onBackProgressed(float progressFraction)1678     public void onBackProgressed(float progressFraction) {
1679         // TODO: non-linearly transform progress fraction into squish amount (ease-in, linear out)
1680         mCurrentBackProgress = progressFraction;
1681         applyBackScaling(progressFraction);
1682         mQsController.setClippingBounds();
1683     }
1684 
1685     /** Resets back progress. */
resetBackTransformation()1686     private void resetBackTransformation() {
1687         mCurrentBackProgress = 0.0f;
1688         applyBackScaling(0.0f);
1689     }
1690 
1691     /**
1692      * Scales multiple elements in tandem to achieve the illusion of the QS+Shade shrinking
1693      * as a single visual element (used by the Predictive Back Gesture preview animation).
1694      * fraction = 0 implies "no scaling", and 1 means "scale down to minimum size (90%)".
1695      */
applyBackScaling(float fraction)1696     private void applyBackScaling(float fraction) {
1697         if (mNotificationContainerParent == null) {
1698             return;
1699         }
1700         float scale = MathUtils.lerp(1.0f, SHADE_BACK_ANIM_MIN_SCALE, fraction);
1701         mNotificationContainerParent.applyBackScaling(scale, mSplitShadeEnabled);
1702         mScrimController.applyBackScaling(scale);
1703     }
1704 
determineAccessibilityPaneTitle()1705     String determineAccessibilityPaneTitle() {
1706         if (mQsController != null && mQsController.isCustomizing()) {
1707             return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
1708         } else if (mQsController != null && mQsController.getExpansionHeight() != 0.0f
1709                 && mQsController.getFullyExpanded()) {
1710             // Upon initialisation when we are not layouted yet we don't want to announce that we
1711             // are fully expanded, hence the != 0.0f check.
1712             if (mSplitShadeEnabled) {
1713                 // In split shade, QS is expanded but it also shows notifications
1714                 return mResources.getString(R.string.accessibility_desc_qs_notification_shade);
1715             } else {
1716                 return mResources.getString(R.string.accessibility_desc_quick_settings);
1717             }
1718         } else if (mBarState == KEYGUARD) {
1719             return mResources.getString(R.string.accessibility_desc_lock_screen);
1720         } else {
1721             return mResources.getString(R.string.accessibility_desc_notification_shade);
1722         }
1723     }
1724 
1725     /** Returns the topPadding of notifications when on keyguard not respecting QS expansion. */
getKeyguardNotificationStaticPadding()1726     int getKeyguardNotificationStaticPadding() {
1727         SceneContainerFlag.assertInLegacyMode();
1728         if (!isKeyguardShowing()) {
1729             return 0;
1730         }
1731 
1732         if (!mKeyguardBypassController.getBypassEnabled()) {
1733             if (!mSplitShadeEnabled) {
1734                 return (int) mKeyguardInteractor.getNotificationContainerBounds()
1735                         .getValue().getTop();
1736             }
1737 
1738             return mClockPositionResult.stackScrollerPadding;
1739         }
1740         int collapsedPosition = mHeadsUpInset;
1741         if (!mNotificationStackScrollLayoutController.isPulseExpanding()) {
1742             return collapsedPosition;
1743         } else {
1744             int expandedPosition =
1745                     mClockPositionResult.stackScrollerPadding;
1746             return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
1747                     mNotificationStackScrollLayoutController.calculateAppearFractionBypass());
1748         }
1749     }
1750 
isKeyguardShowing()1751     boolean isKeyguardShowing() {
1752         return mBarState == KEYGUARD;
1753     }
1754 
requestScrollerTopPaddingUpdate()1755     void requestScrollerTopPaddingUpdate() {
1756         if (!SceneContainerFlag.isEnabled()) {
1757             float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
1758                     getKeyguardNotificationStaticPadding(), mExpandedFraction);
1759             mSharedNotificationContainerInteractor.setTopPosition(padding);
1760         }
1761 
1762         if (isKeyguardShowing()
1763                 && mKeyguardBypassController.getBypassEnabled()) {
1764             // update the position of the header
1765             mQsController.updateExpansion();
1766         }
1767     }
1768 
1769     @Override
setKeyguardStatusBarAlpha(float alpha)1770     public void setKeyguardStatusBarAlpha(float alpha) {
1771         mKeyguardStatusBarViewController.setAlpha(alpha);
1772     }
1773 
1774     @VisibleForTesting
canCollapsePanelOnTouch()1775     boolean canCollapsePanelOnTouch() {
1776         if (!mQsController.getExpanded() && mBarState == KEYGUARD) {
1777             return true;
1778         }
1779 
1780         if (mNotificationStackScrollLayoutController.isScrolledToBottom()) {
1781             return true;
1782         }
1783 
1784         return !mSplitShadeEnabled && (mQsController.getExpanded() || mIsPanelCollapseOnQQS);
1785     }
1786 
getMaxPanelHeight()1787     int getMaxPanelHeight() {
1788         int min = mStatusBarMinHeight;
1789         if (!(mBarState == KEYGUARD)
1790                 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
1791             int minHeight = mQsController.getMinExpansionHeight();
1792             min = Math.max(min, minHeight);
1793         }
1794         int maxHeight;
1795         if (mQsController.isExpandImmediate() || mQsController.getExpanded()
1796                 || mIsExpandingOrCollapsing && mQsController.getExpandedWhenExpandingStarted()
1797                 || mPulsing || mSplitShadeEnabled) {
1798             maxHeight = mQsController.calculatePanelHeightExpanded(
1799                     mClockPositionResult.stackScrollerPadding);
1800         } else {
1801             maxHeight = calculatePanelHeightShade();
1802         }
1803         maxHeight = Math.max(min, maxHeight);
1804         if (maxHeight == 0) {
1805             Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: "
1806                     + mOverExpansion + ", calculatePanelHeightQsExpanded: "
1807                     + mQsController.calculatePanelHeightExpanded(
1808                             mClockPositionResult.stackScrollerPadding)
1809                     + ", calculatePanelHeightShade: " + calculatePanelHeightShade()
1810                     + ", mStatusBarMinHeight = " + mStatusBarMinHeight
1811                     + ", mQsMinExpansionHeight = " + mQsController.getMinExpansionHeight());
1812         }
1813         return maxHeight;
1814     }
1815 
isExpandingOrCollapsing()1816     public boolean isExpandingOrCollapsing() {
1817         float lockscreenExpansionProgress = mQsController.getLockscreenShadeDragProgress();
1818         return mIsExpandingOrCollapsing
1819                 || (0 < lockscreenExpansionProgress && lockscreenExpansionProgress < 1);
1820     }
1821 
onHeightUpdated(float expandedHeight)1822     private void onHeightUpdated(float expandedHeight) {
1823         if (expandedHeight <= 0) {
1824             mShadeLog.logExpansionChanged("onHeightUpdated: fully collapsed.",
1825                     mExpandedFraction, isExpanded(), isTracking(), mExpansionDragDownAmountPx);
1826         } else if (isFullyExpanded()) {
1827             mShadeLog.logExpansionChanged("onHeightUpdated: fully expanded.",
1828                     mExpandedFraction, isExpanded(), isTracking(), mExpansionDragDownAmountPx);
1829         }
1830         if (!mQsController.getExpanded() || mQsController.isExpandImmediate()
1831                 || mIsExpandingOrCollapsing && mQsController.getExpandedWhenExpandingStarted()) {
1832             // Updating the clock position will set the top padding which might
1833             // trigger a new panel height and re-position the clock.
1834             // This is a circular dependency and should be avoided, otherwise we'll have
1835             // a stack overflow.
1836             if (mStackScrollerMeasuringPass > 2) {
1837                 debugLog("Unstable notification panel height. Aborting.");
1838             } else {
1839                 positionClockAndNotifications();
1840             }
1841         }
1842         boolean goingBetweenClosedShadeAndExpandedQs =
1843                 mQsController.isGoingBetweenClosedShadeAndExpandedQs();
1844         // in split shade we react when HUN is visible only if shade height is over HUN start
1845         // height - which means user is swiping down. Otherwise shade QS will either not show at all
1846         // with HUN movement or it will blink when touching HUN initially
1847         boolean qsShouldExpandWithHeadsUp = !mSplitShadeEnabled
1848                 || (!mHeadsUpManager.isTrackingHeadsUp().getValue()
1849                 || expandedHeight > mHeadsUpStartHeight);
1850         if (goingBetweenClosedShadeAndExpandedQs && qsShouldExpandWithHeadsUp) {
1851             float qsExpansionFraction;
1852             if (mSplitShadeEnabled) {
1853                 qsExpansionFraction = 1;
1854             } else if (isKeyguardShowing()) {
1855                 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
1856                 qsExpansionFraction = expandedHeight / (getMaxPanelHeight());
1857             } else {
1858                 // In Shade, interpolate linearly such that QS is closed whenever panel height is
1859                 // minimum QS expansion + minStackHeight
1860                 float panelHeightQsCollapsed =
1861                         mNotificationStackScrollLayoutController.getIntrinsicPadding()
1862                                 + mNotificationStackScrollLayoutController.getLayoutMinHeight();
1863                 float panelHeightQsExpanded = mQsController.calculatePanelHeightExpanded(
1864                         mClockPositionResult.stackScrollerPadding);
1865                 qsExpansionFraction = (expandedHeight - panelHeightQsCollapsed)
1866                         / (panelHeightQsExpanded - panelHeightQsCollapsed);
1867             }
1868             float targetHeight = mQsController.getMinExpansionHeight() + qsExpansionFraction
1869                     * (mQsController.getMaxExpansionHeight()
1870                     - mQsController.getMinExpansionHeight());
1871             mQsController.setExpansionHeight(targetHeight);
1872         }
1873         updateExpandedHeight(expandedHeight);
1874         updateHeader();
1875         updatePanelExpanded();
1876         updateGestureExclusionRect();
1877         if (DEBUG_DRAWABLE) {
1878             mView.invalidate();
1879         }
1880     }
1881 
updatePanelExpanded()1882     private void updatePanelExpanded() {
1883         boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
1884         if (isPanelExpanded() != isExpanded) {
1885             setExpandedOrAwaitingInputTransfer(isExpanded);
1886             updateSystemUiStateFlags();
1887             if (!isExpanded) {
1888                 mQsController.closeQsCustomizer();
1889             }
1890         }
1891     }
1892 
setExpandedOrAwaitingInputTransfer(boolean expandedOrAwaitingInputTransfer)1893     private void setExpandedOrAwaitingInputTransfer(boolean expandedOrAwaitingInputTransfer) {
1894         mShadeRepository.setLegacyExpandedOrAwaitingInputTransfer(expandedOrAwaitingInputTransfer);
1895     }
1896 
1897     @Override
isPanelExpanded()1898     public boolean isPanelExpanded() {
1899         return mShadeRepository.getLegacyExpandedOrAwaitingInputTransfer().getValue();
1900     }
1901 
calculatePanelHeightShade()1902     private int calculatePanelHeightShade() {
1903         // Bypass should always occupy the full height
1904         if (mBarState == KEYGUARD && mKeyguardBypassController.getBypassEnabled()) {
1905             return mNotificationStackScrollLayoutController.getHeight();
1906         }
1907 
1908         int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin();
1909         int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin;
1910 
1911         if (mBarState == KEYGUARD) {
1912             int minKeyguardPanelBottom = mNotificationStackScrollLayoutController
1913                     .getIntrinsicContentHeight();
1914             return Math.max(maxHeight, minKeyguardPanelBottom);
1915         } else {
1916             return maxHeight;
1917         }
1918     }
1919 
getFadeoutAlpha()1920     private float getFadeoutAlpha() {
1921         float alpha;
1922         if (mQsController.getMinExpansionHeight() == 0) {
1923             return 1.0f;
1924         }
1925         alpha = getExpandedHeight() / mQsController.getMinExpansionHeight();
1926         alpha = Math.max(0, Math.min(alpha, 1));
1927         alpha = (float) Math.pow(alpha, 0.75);
1928         return alpha;
1929     }
1930 
1931     /** Hides the header when notifications are colliding with it. */
updateHeader()1932     private void updateHeader() {
1933         if (mBarState == KEYGUARD) {
1934             mKeyguardStatusBarViewController.updateViewState();
1935         }
1936         mQsController.updateExpansion();
1937     }
1938 
onExpandingFinished()1939     private void onExpandingFinished() {
1940         if (!SceneContainerFlag.isEnabled()) {
1941             mNotificationStackScrollLayoutController.onExpansionStopped();
1942         }
1943         mHeadsUpManager.onExpandingFinished();
1944         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
1945         mIsExpandingOrCollapsing = false;
1946         mMediaHierarchyManager.setCollapsingShadeFromQS(false);
1947         mMediaHierarchyManager.setQsExpanded(mQsController.getExpanded());
1948         if (isFullyCollapsed()) {
1949             DejankUtils.postAfterTraversal(() -> setListening(false));
1950 
1951             // Workaround b/22639032: Make sure we invalidate something because else RenderThread
1952             // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
1953             // ahead with rendering and we jank.
1954             mView.postOnAnimation(
1955                     () -> mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT));
1956         } else {
1957             setListening(true);
1958         }
1959         if (mBarState != SHADE) {
1960             // TODO(b/277909752): remove below logs when bug is fixed
1961             mShadeLog.d("onExpandingFinished called");
1962             if (mSplitShadeEnabled && !mQsController.getExpanded()) {
1963                 mShadeLog.d("onExpandingFinished called before QS got expanded");
1964             }
1965             // updating qsExpandImmediate is done in onPanelStateChanged for unlocked shade but
1966             // on keyguard panel state is always OPEN so we need to have that extra update
1967             mQsController.setExpandImmediate(false);
1968         }
1969         setShowShelfOnly(false);
1970         mQsController.setTwoFingerExpandPossible(false);
1971         mShadeHeadsUpTracker.updateTrackingHeadsUp(null);
1972         mExpandingFromHeadsUp = false;
1973         setPanelScrimMinFraction(0.0f);
1974         // Reset status bar alpha so alpha can be calculated upon updating view state.
1975         setKeyguardStatusBarAlpha(-1f);
1976     }
1977 
setListening(boolean listening)1978     private void setListening(boolean listening) {
1979         mKeyguardStatusBarViewController.setBatteryListening(listening);
1980         mQsController.setListening(listening);
1981     }
1982 
expand(boolean animate)1983     public void expand(boolean animate) {
1984         if (isFullyCollapsed() || isCollapsing()) {
1985             mInstantExpanding = true;
1986             mAnimateAfterExpanding = animate;
1987             mUpdateFlingOnLayout = false;
1988             abortAnimations();
1989             if (isTracking()) {
1990                 // The panel is expanded after this call.
1991                 onTrackingStopped(true /* expands */);
1992             }
1993             if (mExpanding) {
1994                 notifyExpandingFinished();
1995             }
1996             updateExpansionAndVisibility();
1997             // Wait for window manager to pickup the change, so we know the maximum height of the
1998             // panel then.
1999             this.mView.getViewTreeObserver().addOnGlobalLayoutListener(
2000                     new ViewTreeObserver.OnGlobalLayoutListener() {
2001                         @Override
2002                         public void onGlobalLayout() {
2003                             if (!mInstantExpanding) {
2004                                 mView.getViewTreeObserver().removeOnGlobalLayoutListener(
2005                                         this);
2006                                 return;
2007                             }
2008                             if (mNotificationShadeWindowController.getWindowRootView()
2009                                     .isVisibleToUser()) {
2010                                 mView.getViewTreeObserver().removeOnGlobalLayoutListener(
2011                                         this);
2012                                 if (mAnimateAfterExpanding) {
2013                                     notifyExpandingStarted();
2014                                     mQsController.beginJankMonitoring(isFullyCollapsed());
2015                                     fling(0  /* expand */);
2016                                 } else {
2017                                     setExpandedFraction(1f);
2018                                 }
2019                                 mInstantExpanding = false;
2020                             }
2021                         }
2022                     });
2023             // Make sure a layout really happens.
2024             this.mView.requestLayout();
2025         }
2026 
2027         setListening(true);
2028     }
2029 
2030     @VisibleForTesting
setOverExpansion(float overExpansion)2031     void setOverExpansion(float overExpansion) {
2032         if (overExpansion == mOverExpansion) {
2033             return;
2034         }
2035         mOverExpansion = overExpansion;
2036         if (mSplitShadeEnabled) {
2037             mQsController.setOverScrollAmount((int) overExpansion);
2038             mScrimController.setNotificationsOverScrollAmount((int) overExpansion);
2039         } else {
2040             // Translating the quick settings by half the overexpansion to center it in the
2041             // background frame
2042             mQsController.updateQsFrameTranslation();
2043         }
2044         mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
2045     }
2046 
falsingAdditionalTapRequired()2047     private void falsingAdditionalTapRequired() {
2048         if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
2049             mTapAgainViewController.show();
2050         } else {
2051             mKeyguardIndicationController.showTransientIndication(
2052                     R.string.notification_tap_again);
2053         }
2054 
2055         if (!mStatusBarStateController.isDozing()) {
2056             performHapticFeedback(HapticFeedbackConstants.REJECT);
2057         }
2058     }
2059 
onTrackingStarted()2060     private void onTrackingStarted() {
2061         endClosing();
2062         mShadeRepository.setLegacyShadeTracking(true);
2063         if (mTrackingStartedListener != null) {
2064             mTrackingStartedListener.onTrackingStarted();
2065         }
2066         notifyExpandingStarted();
2067         updateExpansionAndVisibility();
2068         mScrimController.onTrackingStarted();
2069         if (mQsController.getFullyExpanded()) {
2070             mQsController.setExpandImmediate(true);
2071             setShowShelfOnly(true);
2072         }
2073         mNotificationStackScrollLayoutController.onPanelTrackingStarted();
2074         cancelPendingCollapse();
2075     }
2076 
onTrackingStopped(boolean expand)2077     private void onTrackingStopped(boolean expand) {
2078         mShadeRepository.setLegacyShadeTracking(false);
2079 
2080         updateExpansionAndVisibility();
2081         if (expand) {
2082             mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */,
2083                     true /* animate */);
2084         }
2085         mNotificationStackScrollLayoutController.onPanelTrackingStopped();
2086 
2087         // If we unlocked from a swipe, the user's finger might still be down after the
2088         // unlock animation ends. We need to wait until ACTION_UP to enable blurs again.
2089         mDepthController.setBlursDisabledForUnlock(false);
2090     }
2091 
updateMaxHeadsUpTranslation()2092     private void updateMaxHeadsUpTranslation() {
2093         mNotificationStackScrollLayoutController.setHeadsUpBoundaries(
2094                 mView.getHeight(), mNavigationBarBottomHeight);
2095     }
2096 
shouldUseDismissingAnimation()2097     private boolean shouldUseDismissingAnimation() {
2098         return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
2099                 || !isTracking());
2100     }
2101 
2102     @VisibleForTesting
getMaxPanelTransitionDistance()2103     int getMaxPanelTransitionDistance() {
2104         // Traditionally the value is based on the number of notifications. On split-shade, we want
2105         // the required distance to be a specific and constant value, to make sure the expansion
2106         // motion has the expected speed. We also only want this on non-lockscreen for now.
2107         if (mSplitShadeEnabled && mBarState == SHADE) {
2108             boolean transitionFromHeadsUp = (mHeadsUpManager != null
2109                     && mHeadsUpManager.isTrackingHeadsUp().getValue()) || mExpandingFromHeadsUp;
2110             // heads-up starting height is too close to mSplitShadeFullTransitionDistance and
2111             // when dragging HUN transition is already 90% complete. It makes shade become
2112             // immediately visible when starting to drag. We want to set distance so that
2113             // nothing is immediately visible when dragging (important for HUN swipe up motion) -
2114             // 0.4 expansion fraction is a good starting point.
2115             if (transitionFromHeadsUp) {
2116                 double maxDistance = Math.max(mSplitShadeFullTransitionDistance,
2117                         mHeadsUpStartHeight * 2.5);
2118                 return (int) Math.min(getMaxPanelHeight(), maxDistance);
2119             } else {
2120                 return mSplitShadeFullTransitionDistance;
2121             }
2122         } else {
2123             return getMaxPanelHeight();
2124         }
2125     }
2126 
isLaunchingActivity()2127     private boolean isLaunchingActivity() {
2128         return mShadeAnimationInteractor.isLaunchingActivity().getValue();
2129     }
2130 
2131     @VisibleForTesting
setClosing(boolean isClosing)2132     void setClosing(boolean isClosing) {
2133         mShadeRepository.setLegacyIsClosing(isClosing);
2134         mAmbientState.setIsClosing(isClosing);
2135     }
2136 
updateDozingVisibilities(boolean animate)2137     private void updateDozingVisibilities(boolean animate) {
2138         mKeyguardInteractor.setAnimateDozingTransitions(animate);
2139         if (!mDozing && animate) {
2140             mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
2141         }
2142     }
2143 
onMiddleClicked(float x, float y)2144     private void onMiddleClicked(float x, float y) {
2145         switch (mBarState) {
2146             case KEYGUARD:
2147                 if (!mDozingOnDown) {
2148                     mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
2149                     // Try triggering face auth, this "might" run. Check
2150                     // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
2151                     mDeviceEntryFaceAuthInteractor.onNotificationPanelClicked();
2152 
2153                     if (mDeviceEntryFaceAuthInteractor.canFaceAuthRun()) {
2154                         mUpdateMonitor.requestActiveUnlock(
2155                                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT_LEGACY,
2156                                 "lockScreenEmptySpaceTap");
2157                     } else {
2158                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
2159                                 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
2160                         mLockscreenGestureLogger
2161                                 .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
2162                         mKeyguardIndicationController.showActionToUnlock();
2163                         mKeyguardClockInteractor.handleFidgetTap(x, y);
2164                     }
2165                 }
2166                 break;
2167             case StatusBarState.SHADE_LOCKED:
2168                 if (!mQsController.getExpanded()) {
2169                     mStatusBarStateController.setState(KEYGUARD);
2170                 }
2171                 break;
2172         }
2173     }
2174 
2175     @Override
setAlpha(int alpha, boolean animate)2176     public void setAlpha(int alpha, boolean animate) {
2177         if (mPanelAlpha != alpha) {
2178             mPanelAlpha = alpha;
2179             PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
2180                             ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
2181                     animate);
2182         }
2183     }
2184 
setAlphaInternal(float alpha)2185     private void setAlphaInternal(float alpha) {
2186         mKeyguardInteractor.setPanelAlpha(alpha / 255f);
2187         mView.setPanelAlphaInternal(alpha);
2188     }
2189 
2190     @Override
setAlphaChangeAnimationEndAction(Runnable r)2191     public void setAlphaChangeAnimationEndAction(Runnable r) {
2192         mPanelAlphaEndAction = r;
2193     }
2194 
setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)2195     private void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
2196         mHeadsUpAnimatingAway = headsUpAnimatingAway;
2197         mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
2198         updateVisibility();
2199     }
2200 
2201     @Override
setBouncerShowing(boolean bouncerShowing)2202     public void setBouncerShowing(boolean bouncerShowing) {
2203         mBouncerShowing = bouncerShowing;
2204         updateVisibility();
2205     }
2206 
shouldPanelBeVisible()2207     private boolean shouldPanelBeVisible() {
2208         boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode;
2209         return headsUpVisible || isExpanded() || mBouncerShowing;
2210     }
2211 
setHeadsUpManager(HeadsUpManager headsUpManager)2212     private void setHeadsUpManager(HeadsUpManager headsUpManager) {
2213         mHeadsUpManager = headsUpManager;
2214         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
2215         mHeadsUpTouchHelper = new HeadsUpTouchHelper(
2216                 headsUpManager,
2217                 mStatusBarService,
2218                 mNotificationStackScrollLayoutController.getHeadsUpCallback(),
2219                 new HeadsUpNotificationViewControllerImpl());
2220     }
2221 
onClosingFinished()2222     private void onClosingFinished() {
2223         if (mOpenCloseListener != null) {
2224             mOpenCloseListener.onClosingFinished();
2225         }
2226         setClosingWithAlphaFadeout(false);
2227         mMediaHierarchyManager.closeGuts();
2228     }
2229 
setClosingWithAlphaFadeout(boolean closing)2230     private void setClosingWithAlphaFadeout(boolean closing) {
2231         mClosingWithAlphaFadeOut = closing;
2232         mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing);
2233     }
2234 
updateExpandedHeight(float expandedHeight)2235     private void updateExpandedHeight(float expandedHeight) {
2236         if (isTracking()) {
2237             mNotificationStackScrollLayoutController
2238                     .setExpandingVelocity(getCurrentExpandVelocity());
2239         }
2240         if (mKeyguardBypassController.getBypassEnabled() && isKeyguardShowing()) {
2241             // The expandedHeight is always the full panel Height when bypassing
2242             expandedHeight = getMaxPanelHeight();
2243         }
2244         if (!SceneContainerFlag.isEnabled()) {
2245             mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
2246         }
2247         updateStatusBarIcons();
2248     }
2249 
updateStatusBarIcons()2250     private void updateStatusBarIcons() {
2251         boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
2252         if (showIconsWhenExpanded && isKeyguardShowing()) {
2253             showIconsWhenExpanded = false;
2254         }
2255         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
2256             mShowIconsWhenExpanded = showIconsWhenExpanded;
2257             mCommandQueue.recomputeDisableFlags(mDisplayId, false);
2258         }
2259     }
2260 
2261     /** @deprecated Temporary a11y solution until dual shade launch b/371224114 */
2262     @Override
2263     @Deprecated
2264     public void onStatusBarLongPress(MotionEvent event) {
2265         Log.i(TAG, "Status Bar was long pressed.");
2266         if (DISABLE_LONG_PRESS_EXPAND) {
2267             //TODO(b/394977231) delete this temporary workaround used only by tests
2268             Log.i(TAG, "Ignoring status Bar long press on virtualized test device.");
2269             return;
2270         }
2271         ShadeExpandsOnStatusBarLongPress.unsafeAssertInNewMode();
2272         mStatusBarLongPressDowntime = event.getDownTime();
2273         if (isTracking()) {
2274             onTrackingStopped(true);
2275         }
2276         if (!mQsController.getExpanded()) {
2277             performHapticFeedback(HapticFeedbackConstants.GESTURE_START);
2278             if (isExpanded() && mBarState != KEYGUARD) {
2279                 mShadeLog.d("Status Bar was long pressed. Expanding to QS.");
2280                 mQsController.flingQs(0, FLING_EXPAND);
2281             } else {
2282                 if (mBarState == KEYGUARD) {
2283                     mShadeLog.d("Lockscreen Status Bar was long pressed. Expanding to Notifications.");
2284                     mLockscreenShadeTransitionController.goToLockedShade(
2285                             /* expandedView= */null, /* needsQSAnimation= */true);
2286                 } else {
2287                     mShadeLog.d("Status Bar was long pressed. Expanding to Notifications.");
2288                     expandToNotifications();
2289                 }
2290             }
2291         }
2292     }
2293 
2294     @Override
2295     public int getBarState() {
2296         return mBarState;
2297     }
2298 
2299     /** Called when a HUN is dragged up or down to indicate the starting height for shade motion. */
2300     @VisibleForTesting
2301     void setHeadsUpDraggingStartingHeight(int startHeight) {
2302         mHeadsUpStartHeight = startHeight;
2303         float scrimMinFraction;
2304         if (mSplitShadeEnabled) {
2305             boolean highHun = mHeadsUpStartHeight * 2.5
2306                     > mSplitShadeFullTransitionDistance;
2307             // if HUN height is higher than 40% of predefined transition distance, it means HUN
2308             // is too high for regular transition. In that case we need to calculate transition
2309             // distance - here we take scrim transition distance as equal to shade transition
2310             // distance. It doesn't result in perfect motion - usually scrim transition distance
2311             // should be longer - but it's good enough for HUN case.
2312             float transitionDistance =
2313                     highHun ? getMaxPanelTransitionDistance() : mSplitShadeFullTransitionDistance;
2314             scrimMinFraction = mHeadsUpStartHeight / transitionDistance;
2315         } else {
2316             int transitionDistance = getMaxPanelHeight();
2317             scrimMinFraction = transitionDistance > 0f
2318                     ? (float) mHeadsUpStartHeight / transitionDistance : 0f;
2319         }
setPanelScrimMinFraction(scrimMinFraction)2320         setPanelScrimMinFraction(scrimMinFraction);
2321     }
2322 
2323     /**
2324      * Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain
2325      * cases, such as if there's a heads-up notification.
2326      */
setPanelScrimMinFraction(float minFraction)2327     private void setPanelScrimMinFraction(float minFraction) {
2328         mMinFraction = minFraction;
2329         mDepthController.setPanelPullDownMinFraction(mMinFraction);
2330         mScrimController.setPanelScrimMinFraction(mMinFraction);
2331     }
2332 
isPanelVisibleBecauseOfHeadsUp()2333     private boolean isPanelVisibleBecauseOfHeadsUp() {
2334         boolean headsUpVisible = (mHeadsUpManager != null && mHeadsUpManager.hasPinnedHeadsUp())
2335                 || mHeadsUpAnimatingAway;
2336         return headsUpVisible && mBarState == StatusBarState.SHADE;
2337     }
2338 
isPanelVisibleBecauseScrimIsAnimatingOff()2339     private boolean isPanelVisibleBecauseScrimIsAnimatingOff() {
2340         return mUnlockedScreenOffAnimationController.isAnimationPlaying();
2341     }
2342 
shouldHideStatusBarIconsWhenExpanded()2343     public boolean shouldHideStatusBarIconsWhenExpanded() {
2344         if (isLaunchingActivity()) {
2345             return false;
2346         }
2347         if (mHeadsUpAppearanceController != null
2348                 && mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible()) {
2349             return false;
2350         }
2351         return !mShowIconsWhenExpanded;
2352     }
2353 
2354     @Override
setTouchAndAnimationDisabled(boolean disabled)2355     public void setTouchAndAnimationDisabled(boolean disabled) {
2356         mTouchDisabled = disabled;
2357         if (mTouchDisabled) {
2358             cancelHeightAnimator();
2359             if (isTracking()) {
2360                 onTrackingStopped(true /* expanded */);
2361             }
2362             notifyExpandingFinished();
2363         }
2364         // TODO(b/332732878): replace this call when scene container is enabled
2365         mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
2366     }
2367 
2368     @Override
setDozing(boolean dozing, boolean animate)2369     public void setDozing(boolean dozing, boolean animate) {
2370         if (dozing == mDozing) return;
2371         mView.setDozing(dozing);
2372         mDozing = dozing;
2373         // TODO (b/) make listeners for this
2374         mNotificationStackScrollLayoutController.setDozing(mDozing, animate);
2375         mKeyguardInteractor.setAnimateDozingTransitions(animate);
2376         mKeyguardStatusBarViewController.setDozing(mDozing);
2377         mQsController.setDozing(mDozing);
2378 
2379         if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
2380             updateDozingVisibilities(animate);
2381         }
2382 
2383         final float dozeAmount = dozing ? 1 : 0;
2384         mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate);
2385 
2386         updateKeyguardStatusViewAlignment();
2387     }
2388 
2389     @Override
setPulsing(boolean pulsing)2390     public void setPulsing(boolean pulsing) {
2391         mPulsing = pulsing;
2392         final boolean
2393                 animatePulse =
2394                 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
2395         if (animatePulse) {
2396             mAnimateNextPositionUpdate = true;
2397         }
2398         // Do not animate the clock when waking up from a pulse.
2399         // The height callback will take care of pushing the clock to the right position.
2400         if (!mPulsing && !mDozing) {
2401             mAnimateNextPositionUpdate = false;
2402         }
2403         mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
2404 
2405         updateKeyguardStatusViewAlignment();
2406     }
2407 
performHapticFeedback(int constant)2408     public void performHapticFeedback(int constant) {
2409         if (msdlFeedback()) {
2410             MSDLToken token;
2411             switch (constant) {
2412                 case HapticFeedbackConstants.GESTURE_START ->
2413                         token = MSDLToken.SWIPE_THRESHOLD_INDICATOR;
2414                 case HapticFeedbackConstants.REJECT -> token = MSDLToken.FAILURE;
2415                 default -> token = null;
2416             }
2417             if (token != null) {
2418                 mMSDLPlayer.playToken(token, null);
2419             }
2420         } else {
2421             mVibratorHelper.performHapticFeedback(mView, constant);
2422         }
2423     }
2424 
2425     private class ShadeHeadsUpTrackerImpl implements ShadeHeadsUpTracker {
2426         @Override
addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)2427         public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
2428             mTrackingHeadsUpListeners.add(listener);
2429         }
2430 
2431         @Override
removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)2432         public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
2433             mTrackingHeadsUpListeners.remove(listener);
2434         }
2435 
2436         @Override
setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)2437         public void setHeadsUpAppearanceController(
2438                 HeadsUpAppearanceController headsUpAppearanceController) {
2439             mHeadsUpAppearanceController = headsUpAppearanceController;
2440         }
2441 
2442         @Override
getTrackedHeadsUpNotification()2443         @Nullable public ExpandableNotificationRow getTrackedHeadsUpNotification() {
2444             return mTrackedHeadsUpNotification;
2445         }
2446 
updateTrackingHeadsUp(@ullable ExpandableNotificationRow pickedChild)2447         private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
2448             mTrackedHeadsUpNotification = pickedChild;
2449             for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
2450                 Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
2451                 listener.accept(pickedChild);
2452             }
2453         }
2454     }
2455 
2456     @Override
getShadeHeadsUpTracker()2457     public ShadeHeadsUpTracker getShadeHeadsUpTracker() {
2458         return mShadeHeadsUpTracker;
2459     }
2460 
2461     @Override
getShadeFoldAnimator()2462     public ShadeFoldAnimatorImpl getShadeFoldAnimator() {
2463         return mShadeFoldAnimator;
2464     }
2465 
2466     @Deprecated
2467     public final class ShadeFoldAnimatorImpl implements ShadeFoldAnimator {
2468         /** Updates the views to the initial state for the fold to AOD animation. */
2469         @Override
prepareFoldToAodAnimation()2470         public void prepareFoldToAodAnimation() {
2471             setDozing(true /* dozing */, false /* animate */);
2472 
2473             // Move the content of the AOD all the way to the left
2474             // so we can animate to the initial position
2475             final int translationAmount = mView.getResources().getDimensionPixelSize(
2476                     R.dimen.below_clock_padding_start);
2477             mView.setTranslationX(-translationAmount);
2478             mView.setAlpha(0);
2479         }
2480 
2481         /**
2482          * Starts fold to AOD animation.
2483          *
2484          * @param startAction  invoked when the animation starts.
2485          * @param endAction    invoked when the animation finishes, also if it was cancelled.
2486          * @param cancelAction invoked when the animation is cancelled, before endAction.
2487          */
2488         @Override
startFoldToAodAnimation( Runnable startAction, Runnable endAction, Runnable cancelAction)2489         public void startFoldToAodAnimation(
2490                 Runnable startAction, Runnable endAction, Runnable cancelAction) {
2491 
2492         }
2493 
2494         /**
2495          * Builds the default NPVC fold animator
2496          *
2497          * @deprecated Temporary stop-gap. Do not use outside of keyguard fold transition.
2498          */
2499         @Deprecated
buildViewAnimator( Runnable startAction, Runnable endAction, Runnable cancelAction)2500         public ViewPropertyAnimator buildViewAnimator(
2501                 Runnable startAction, Runnable endAction, Runnable cancelAction) {
2502             final ViewPropertyAnimator viewAnimator = mView.animate();
2503             viewAnimator.cancel();
2504             return viewAnimator
2505                     .translationX(0)
2506                     .alpha(1f)
2507                     .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
2508                     .setInterpolator(EMPHASIZED_DECELERATE)
2509                     .setListener(new AnimatorListenerAdapter() {
2510                         @Override
2511                         public void onAnimationStart(Animator animation) {
2512                             startAction.run();
2513                         }
2514 
2515                         @Override
2516                         public void onAnimationCancel(Animator animation) {
2517                             cancelAction.run();
2518                         }
2519 
2520                         @Override
2521                         public void onAnimationEnd(Animator animation) {
2522                             endAction.run();
2523 
2524                             viewAnimator.setListener(null);
2525                             viewAnimator.setUpdateListener(null);
2526                         }
2527                     });
2528         }
2529 
2530         /** Cancels fold to AOD transition and resets view state. */
2531         @Override
2532         public void cancelFoldToAodAnimation() {
2533             cancelAnimation();
2534             resetAlpha();
2535             resetTranslation();
2536         }
2537     }
2538 
2539     @Override
2540     public void setImportantForAccessibility(int mode) {
2541         mView.setImportantForAccessibility(mode);
2542     }
2543 
2544     @Override
2545     public void blockExpansionForCurrentTouch() {
2546         mBlockingExpansionForCurrentTouch = isTracking();
2547     }
2548 
2549     @NeverCompile
2550     @Override
2551     public void dump(PrintWriter pw, String[] args) {
2552         pw.println(TAG + ":");
2553         IndentingPrintWriter ipw = asIndenting(pw);
2554         ipw.increaseIndent();
2555 
2556         ipw.print("mDownTime="); ipw.println(mDownTime);
2557         ipw.print("mTouchSlopExceededBeforeDown="); ipw.println(mTouchSlopExceededBeforeDown);
2558         ipw.print("mIsLaunchAnimationRunning="); ipw.println(isLaunchingActivity());
2559         ipw.print("mOverExpansion="); ipw.println(mOverExpansion);
2560         ipw.print("mExpandedHeight="); ipw.println(mExpandedHeight);
2561         ipw.print("isTracking()="); ipw.println(isTracking());
2562         ipw.print("mExpanding="); ipw.println(mExpanding);
2563         ipw.print("mSplitShadeEnabled="); ipw.println(mSplitShadeEnabled);
2564         ipw.print("mAnimateNextPositionUpdate="); ipw.println(mAnimateNextPositionUpdate);
2565         ipw.print("isPanelExpanded()="); ipw.println(isPanelExpanded());
2566         ipw.print("mDozing="); ipw.println(mDozing);
2567         ipw.print("mDozingOnDown="); ipw.println(mDozingOnDown);
2568         ipw.print("mBouncerShowing="); ipw.println(mBouncerShowing);
2569         ipw.print("mBarState="); ipw.println(mBarState);
2570         ipw.print("mStatusBarMinHeight="); ipw.println(mStatusBarMinHeight);
2571         ipw.print("mStatusBarHeaderHeightKeyguard="); ipw.println(mStatusBarHeaderHeightKeyguard);
2572         ipw.print("mOverStretchAmount="); ipw.println(mOverStretchAmount);
2573         ipw.print("mDownX="); ipw.println(mDownX);
2574         ipw.print("mDownY="); ipw.println(mDownY);
2575         ipw.print("mDisplayTopInset="); ipw.println(mDisplayTopInset);
2576         ipw.print("mDisplayRightInset="); ipw.println(mDisplayRightInset);
2577         ipw.print("mDisplayLeftInset="); ipw.println(mDisplayLeftInset);
2578         ipw.print("mIsExpandingOrCollapsing="); ipw.println(mIsExpandingOrCollapsing);
2579         ipw.print("mHeadsUpStartHeight="); ipw.println(mHeadsUpStartHeight);
2580         ipw.print("mListenForHeadsUp="); ipw.println(mListenForHeadsUp);
2581         ipw.print("mNavigationBarBottomHeight="); ipw.println(mNavigationBarBottomHeight);
2582         ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
2583         ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
2584         ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
2585         ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
2586         ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
2587         ipw.print("mIsFullWidth="); ipw.println(mIsFullWidth);
2588         ipw.print("mBlockingExpansionForCurrentTouch=");
2589         ipw.println(mBlockingExpansionForCurrentTouch);
2590         ipw.print("mExpectingSynthesizedDown="); ipw.println(mExpectingSynthesizedDown);
2591         ipw.print("mLastEventSynthesizedDown="); ipw.println(mLastEventSynthesizedDown);
2592         ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
2593         ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
2594         ipw.print("mPulsing="); ipw.println(mPulsing);
2595         ipw.print("mStackScrollerMeasuringPass="); ipw.println(mStackScrollerMeasuringPass);
2596         ipw.print("mPanelAlpha="); ipw.println(mPanelAlpha);
2597         ipw.print("mHeadsUpInset="); ipw.println(mHeadsUpInset);
2598         ipw.print("mHeadsUpPinnedMode="); ipw.println(mHeadsUpPinnedMode);
2599         ipw.print("mAllowExpandForSmallExpansion="); ipw.println(mAllowExpandForSmallExpansion);
2600         ipw.print("mMaxOverscrollAmountForPulse="); ipw.println(mMaxOverscrollAmountForPulse);
2601         ipw.print("mIsPanelCollapseOnQQS="); ipw.println(mIsPanelCollapseOnQQS);
2602         ipw.print("mIsGestureNavigation="); ipw.println(mIsGestureNavigation);
2603         ipw.print("mOldLayoutDirection="); ipw.println(mOldLayoutDirection);
2604         ipw.print("mMinFraction="); ipw.println(mMinFraction);
2605         ipw.print("mSplitShadeFullTransitionDistance=");
2606         ipw.println(mSplitShadeFullTransitionDistance);
2607         ipw.print("mSplitShadeScrimTransitionDistance=");
2608         ipw.println(mSplitShadeScrimTransitionDistance);
2609         ipw.print("mMinExpandHeight="); ipw.println(mMinExpandHeight);
2610         ipw.print("mPanelUpdateWhenAnimatorEnds="); ipw.println(mPanelUpdateWhenAnimatorEnds);
2611         ipw.print("mHasVibratedOnOpen="); ipw.println(mHasVibratedOnOpen);
2612         ipw.print("mFixedDuration="); ipw.println(mFixedDuration);
2613         ipw.print("mPanelFlingOvershootAmount="); ipw.println(mPanelFlingOvershootAmount);
2614         ipw.print("mLastGesturedOverExpansion="); ipw.println(mLastGesturedOverExpansion);
2615         ipw.print("mIsSpringBackAnimation="); ipw.println(mIsSpringBackAnimation);
2616         ipw.print("mHintDistance="); ipw.println(mHintDistance);
2617         ipw.print("mInitialOffsetOnTouch="); ipw.println(mInitialOffsetOnTouch);
2618         ipw.print("mCollapsedAndHeadsUpOnDown="); ipw.println(mCollapsedAndHeadsUpOnDown);
2619         ipw.print("mExpandedFraction="); ipw.println(mExpandedFraction);
2620         ipw.print("mExpansionDragDownAmountPx="); ipw.println(mExpansionDragDownAmountPx);
2621         ipw.print("mPanelClosedOnDown="); ipw.println(mPanelClosedOnDown);
2622         ipw.print("mHasLayoutedSinceDown="); ipw.println(mHasLayoutedSinceDown);
2623         ipw.print("mUpdateFlingVelocity="); ipw.println(mUpdateFlingVelocity);
2624         ipw.print("mUpdateFlingOnLayout="); ipw.println(mUpdateFlingOnLayout);
2625         ipw.print("isClosing()="); ipw.println(isClosing());
2626         ipw.print("mTouchSlopExceeded="); ipw.println(mTouchSlopExceeded);
2627         ipw.print("mTrackingPointer="); ipw.println(mTrackingPointer);
2628         ipw.print("mTouchSlop="); ipw.println(mTouchSlop);
2629         ipw.print("mSlopMultiplier="); ipw.println(mSlopMultiplier);
2630         ipw.print("mTouchAboveFalsingThreshold="); ipw.println(mTouchAboveFalsingThreshold);
2631         ipw.print("mTouchStartedInEmptyArea="); ipw.println(mTouchStartedInEmptyArea);
2632         ipw.print("mMotionAborted="); ipw.println(mMotionAborted);
2633         ipw.print("mUpwardsWhenThresholdReached="); ipw.println(mUpwardsWhenThresholdReached);
2634         ipw.print("mAnimatingOnDown="); ipw.println(mAnimatingOnDown);
2635         ipw.print("mHandlingPointerUp="); ipw.println(mHandlingPointerUp);
2636         ipw.print("mInstantExpanding="); ipw.println(mInstantExpanding);
2637         ipw.print("mAnimateAfterExpanding="); ipw.println(mAnimateAfterExpanding);
2638         ipw.print("mIsFlinging="); ipw.println(mIsFlinging);
2639         ipw.print("mViewName="); ipw.println(mViewName);
2640         ipw.print("mInitialExpandY="); ipw.println(mInitialExpandY);
2641         ipw.print("mInitialExpandX="); ipw.println(mInitialExpandX);
2642         ipw.print("mTouchDisabled="); ipw.println(mTouchDisabled);
2643         ipw.print("mInitialTouchFromKeyguard="); ipw.println(mInitialTouchFromKeyguard);
2644         ipw.print("mNextCollapseSpeedUpFactor="); ipw.println(mNextCollapseSpeedUpFactor);
2645         ipw.print("mGestureWaitForTouchSlop="); ipw.println(mGestureWaitForTouchSlop);
2646         ipw.print("mIgnoreXTouchSlop="); ipw.println(mIgnoreXTouchSlop);
2647         ipw.print("mExpandLatencyTracking="); ipw.println(mExpandLatencyTracking);
2648         ipw.println("gestureExclusionRect:" + calculateGestureExclusionRect());
2649         Trace.beginSection("Table<DownEvents>");
2650         new DumpsysTableLogger(
2651                 TAG,
2652                 NPVCDownEventState.TABLE_HEADERS,
2653                 mLastDownEvents.toList()
2654         ).printTableData(ipw);
2655         Trace.endSection();
2656     }
2657 
2658     @Override
2659     public void initDependencies(
2660             CentralSurfaces centralSurfaces,
2661             GestureRecorder recorder,
2662             Runnable hideExpandedRunnable,
2663             HeadsUpManager headsUpManager) {
2664         setHeadsUpManager(headsUpManager);
2665         // TODO(b/254859580): this can be injected.
2666         mCentralSurfaces = centralSurfaces;
2667 
2668         mGestureRecorder = recorder;
2669         mHideExpandedRunnable = hideExpandedRunnable;
2670     }
2671 
2672     @Override
2673     public void resetTranslation() {
2674         mView.setTranslationX(0f);
2675     }
2676 
2677     @Override
2678     public void resetAlpha() {
2679         mView.setAlpha(1f);
2680     }
2681 
2682     @Override
2683     public void fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
2684         mView.animate().cancel();
2685         mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
2686                 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
2687                 endAction);
2688     }
2689 
2690     @Override
2691     public void resetViewGroupFade() {
2692         ViewGroupFadeHelper.reset(mView);
2693     }
2694 
2695     public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
2696         mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
2697     }
2698 
2699     public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
2700         mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
2701     }
2702 
2703     @Override
2704     public void onThemeChanged() {
2705         mConfigurationListener.onThemeChanged();
2706     }
2707 
2708     @VisibleForTesting
2709     TouchHandler getTouchHandler() {
2710         return mTouchHandler;
2711     }
2712 
2713     @Override
2714     public void updateSystemUiStateFlags() {
2715         if (SysUiState.DEBUG) {
2716             Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
2717                     + isFullyExpanded() + " inQs=" + mQsController.getExpanded());
2718         }
2719         if (ShadeWindowGoesAround.isEnabled()) {
2720             setPerDisplaySysUIStateFlags();
2721         } else {
2722             setDefaultDisplayFlags();
2723         }
2724     }
2725 
2726     private int getShadeDisplayId() {
2727         if (ShadeWindowGoesAround.isEnabled()) {
2728             var pendingDisplayId =
2729                     mShadeDisplaysRepository.get().getPendingDisplayId().getValue();
2730             // Use the pendingDisplayId from the repository, *not* the Shade's context.
2731             // This ensures correct UI state updates also if this method is called just *before*
2732             // the Shade window moves to another display.
2733             // The pendingDisplayId is guaranteed to be updated before this method is called.
2734             return pendingDisplayId;
2735         } else {
2736             return Display.DEFAULT_DISPLAY;
2737         }
2738     }
2739 
2740     private void setPerDisplaySysUIStateFlags() {
2741         mSysUIStateDisplaysInteractor.setFlagsExclusivelyToDisplay(
2742                 getShadeDisplayId(),
2743                 new StateChange()
2744                         .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
2745                                 isPanelExpanded() && !isCollapsing())
2746                         .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
2747                                 isFullyExpanded() && !mQsController.getExpanded())
2748                         .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
2749                                 isFullyExpanded() && mQsController.getExpanded())
2750         );
2751     }
2752 
2753     @Deprecated
2754     private void setDefaultDisplayFlags() {
2755         mSysUiState
2756                 .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
2757                         isPanelExpanded() && !isCollapsing())
2758                 .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
2759                         isFullyExpanded() && !mQsController.getExpanded())
2760                 .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
2761                         isFullyExpanded() && mQsController.getExpanded()).commitUpdate(
2762                         mDisplayId);
2763     }
2764 
2765     private void debugLog(String fmt, Object... args) {
2766         if (DEBUG_LOGCAT) {
2767             Log.d(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
2768         }
2769     }
2770 
2771     @VisibleForTesting
2772     void notifyExpandingStarted() {
2773         if (!mExpanding) {
2774             DejankUtils.notifyRendererOfExpensiveFrame(mView, "notifyExpandingStarted");
2775             mExpanding = true;
2776             mIsExpandingOrCollapsing = true;
2777             mQsController.onExpandingStarted(mQsController.getFullyExpanded());
2778         }
2779     }
2780 
2781     void notifyExpandingFinished() {
2782         endClosing();
2783         if (mExpanding) {
2784             mExpanding = false;
2785             onExpandingFinished();
2786         }
2787     }
2788 
2789     float getTouchSlop(MotionEvent event) {
2790         // Adjust the touch slop if another gesture may be being performed.
2791         return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
2792                 ? mTouchSlop * mSlopMultiplier
2793                 : mTouchSlop;
2794     }
2795 
2796     private void addMovement(MotionEvent event) {
2797         // Add movement to velocity tracker using raw screen X and Y coordinates instead
2798         // of window coordinates because the window frame may be moving at the same time.
2799         float deltaX = event.getRawX() - event.getX();
2800         float deltaY = event.getRawY() - event.getY();
2801         event.offsetLocation(deltaX, deltaY);
2802         mVelocityTracker.addMovement(event);
2803         event.offsetLocation(-deltaX, -deltaY);
2804     }
2805 
2806     @Override
2807     public void startExpandLatencyTracking() {
2808         if (mLatencyTracker.isEnabled()) {
2809             mLatencyTracker.onActionStart(LatencyTracker.ACTION_EXPAND_PANEL);
2810             mExpandLatencyTracking = true;
2811         }
2812     }
2813 
2814     private void startOpening(MotionEvent event) {
2815         updateExpansionAndVisibility();
2816         //TODO: keyguard opens QS a different way; log that too?
2817 
2818         // Log the position of the swipe that opened the panel
2819         float width = mCentralSurfaces.getDisplayWidth();
2820         float height = mCentralSurfaces.getDisplayHeight();
2821         int rot = mCentralSurfaces.getRotation();
2822 
2823         mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
2824                 (int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
2825         mLockscreenGestureLogger
2826                 .log(LockscreenUiEvent.LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND);
2827     }
2828 
2829     /**
2830      * Maybe vibrate as panel is opened.
2831      *
2832      * @param openingWithTouch Whether the panel is being opened with touch. If the panel is
2833      *                         instead being opened programmatically (such as by the open panel
2834      *                         gesture), we always play haptic.
2835      */
2836     private void maybeVibrateOnOpening(boolean openingWithTouch) {
2837         if (mVibrateOnOpening && mBarState != KEYGUARD && mBarState != SHADE_LOCKED) {
2838             if (!openingWithTouch || !mHasVibratedOnOpen) {
2839                 performHapticFeedback(HapticFeedbackConstants.GESTURE_START);
2840                 mHasVibratedOnOpen = true;
2841                 mShadeLog.v("Vibrating on opening, mHasVibratedOnOpen=true");
2842             }
2843         }
2844     }
2845 
2846     /**
2847      * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
2848      * horizontal direction
2849      */
2850     private boolean isDirectionUpwards(float x, float y) {
2851         float xDiff = x - mInitialExpandX;
2852         float yDiff = y - mInitialExpandY;
2853         if (yDiff >= 0) {
2854             return false;
2855         }
2856         return Math.abs(yDiff) >= Math.abs(xDiff);
2857     }
2858 
2859     /** Called when a MotionEvent is about to trigger Shade expansion. */
2860     private void startExpandMotion(float newX, float newY, boolean startTracking,
2861             float expandedHeight) {
2862         if (!mHandlingPointerUp && !mStatusBarStateController.isDozing()) {
2863             mQsController.beginJankMonitoring(isFullyCollapsed());
2864         }
2865         mInitialOffsetOnTouch = expandedHeight;
2866         if (!isTracking() || isFullyCollapsed()) {
2867             mInitialExpandY = newY;
2868             mInitialExpandX = newX;
2869         } else {
2870             mShadeLog.d("not setting mInitialExpandY in startExpandMotion");
2871         }
2872         mInitialTouchFromKeyguard = mKeyguardStateController.isShowing();
2873         if (startTracking) {
2874             mTouchSlopExceeded = true;
2875             setExpandedHeight(mInitialOffsetOnTouch);
2876             onTrackingStarted();
2877         }
2878     }
2879 
2880     private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
2881         mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
2882         mTrackingPointer = -1;
2883         mStatusBarLongPressDowntime = -1L;
2884         mAmbientState.setSwipingUp(false);
2885         if ((isTracking() && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
2886                 || Math.abs(y - mInitialExpandY) > mTouchSlop
2887                 || (!isFullyExpanded() && !isFullyCollapsed())
2888                 || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
2889             mVelocityTracker.computeCurrentVelocity(1000);
2890             float vel = mVelocityTracker.getYVelocity();
2891             float vectorVel = (float) Math.hypot(
2892                     mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
2893 
2894             final boolean onKeyguard = mKeyguardStateController.isShowing();
2895             final boolean expand;
2896             if (mKeyguardStateController.isKeyguardFadingAway()
2897                     || (mInitialTouchFromKeyguard && !onKeyguard)) {
2898                 // Don't expand for any touches that started from the keyguard and ended after the
2899                 // keyguard is gone.
2900                 expand = false;
2901             } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
2902                 if (onKeyguard) {
2903                     expand = true;
2904                     mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
2905                             forceCancel, expand);
2906                 } else {
2907                     // If we get a cancel, put the shade back to the state it was in when the
2908                     // gesture started
2909                     expand = !mPanelClosedOnDown;
2910                     mShadeLog.logEndMotionEvent("endMotionEvent: cancel", forceCancel, expand);
2911                 }
2912             } else {
2913                 expand = flingExpands(vel, vectorVel, x, y);
2914                 mShadeLog.logEndMotionEvent("endMotionEvent: flingExpands", forceCancel, expand);
2915             }
2916 
2917             mDozeLog.traceFling(
2918                     expand,
2919                     mTouchAboveFalsingThreshold,
2920                     /* screenOnFromTouch=*/ getWakefulness().isAwakeFromTapOrGesture());
2921             // Log collapse gesture if on lock screen.
2922             if (!expand && onKeyguard) {
2923                 float displayDensity = getDisplayDensity();
2924                 int heightDp = (int) Math.abs((y - mInitialExpandY) / displayDensity);
2925                 int velocityDp = (int) Math.abs(vel / displayDensity);
2926                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
2927                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
2928             }
2929             float dy = y - mInitialExpandY;
2930             @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
2931                     : dy > 0 ? QUICK_SETTINGS
2932                             : (mKeyguardStateController.canDismissLockScreen()
2933                                     ? UNLOCK : BOUNCER_UNLOCK);
2934 
2935             // don't fling while in keyguard to avoid jump in shade expand animation;
2936             // touch has been intercepted already so flinging here is redundant
2937             if (mBarState == KEYGUARD && mExpandedFraction >= 1.0) {
2938                 mShadeLog.d("NPVC endMotionEvent - skipping fling on keyguard");
2939             } else {
2940                 fling(vel, expand, isFalseTouch(x, y, interactionType));
2941             }
2942             onTrackingStopped(expand);
2943             mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
2944             if (mUpdateFlingOnLayout) {
2945                 mUpdateFlingVelocity = vel;
2946             }
2947         } else if (!mCentralSurfaces.isBouncerShowing()
2948                 && !mAlternateBouncerInteractor.isVisibleState()
2949                 && !mKeyguardStateController.isKeyguardGoingAway()) {
2950             onEmptySpaceClick(x, y);
2951             onTrackingStopped(true);
2952         }
2953         mVelocityTracker.clear();
2954     }
2955 
2956     private float getCurrentExpandVelocity() {
2957         mVelocityTracker.computeCurrentVelocity(1000);
2958         return mVelocityTracker.getYVelocity();
2959     }
2960 
2961     private void endClosing() {
2962         if (isClosing()) {
2963             setClosing(false);
2964             onClosingFinished();
2965         }
2966     }
2967 
2968     /**
2969      * @param x the final x-coordinate when the finger was lifted
2970      * @param y the final y-coordinate when the finger was lifted
2971      * @return whether this motion should be regarded as a false touch
2972      */
2973     private boolean isFalseTouch(float x, float y,
2974             @Classifier.InteractionType int interactionType) {
2975         if (mFalsingManager.isClassifierEnabled()) {
2976             return mFalsingManager.isFalseTouch(interactionType);
2977         }
2978         if (!mTouchAboveFalsingThreshold) {
2979             return true;
2980         }
2981         if (mUpwardsWhenThresholdReached) {
2982             return false;
2983         }
2984         return !isDirectionUpwards(x, y);
2985     }
2986 
2987     private void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
2988         fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
2989     }
2990 
2991     private void fling(float vel, boolean expand, float collapseSpeedUpFactor,
2992             boolean expandBecauseOfFalsing) {
2993         float target = expand ? getMaxPanelTransitionDistance() : 0;
2994         if (!expand) {
2995             setClosing(true);
2996         }
2997         flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
2998     }
2999 
3000     private void springBack() {
3001         if (mOverExpansion == 0) {
3002             onFlingEnd(false /* cancelled */);
3003             return;
3004         }
3005         mIsSpringBackAnimation = true;
3006         ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
3007         animator.addUpdateListener(
3008                 animation -> setOverExpansionInternal((float) animation.getAnimatedValue()));
3009         animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
3010         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
3011         animator.addListener(new AnimatorListenerAdapter() {
3012             private boolean mCancelled;
3013 
3014             @Override
3015             public void onAnimationCancel(Animator animation) {
3016                 mCancelled = true;
3017             }
3018 
3019             @Override
3020             public void onAnimationEnd(Animator animation) {
3021                 mIsSpringBackAnimation = false;
3022                 onFlingEnd(mCancelled);
3023             }
3024         });
3025         setAnimator(animator);
3026         animator.start();
3027     }
3028 
3029     @VisibleForTesting
3030     void setExpandedHeight(float height) {
3031         debugLog("setExpandedHeight(%.1f)", height);
3032         setExpandedHeightInternal(height);
3033     }
3034 
3035     /** Try to set expanded height to max. */
3036     void updateExpandedHeightToMaxHeight() {
3037         float currentMaxPanelHeight = getMaxPanelHeight();
3038 
3039         if (isFullyCollapsed()) {
3040             return;
3041         }
3042 
3043         if (currentMaxPanelHeight == mExpandedHeight) {
3044             return;
3045         }
3046 
3047         if (isTracking() && !(mBlockingExpansionForCurrentTouch
3048                 || mQsController.isTrackingBlocked())) {
3049             return;
3050         }
3051 
3052         if (mHeightAnimator != null && !mIsSpringBackAnimation) {
3053             mPanelUpdateWhenAnimatorEnds = true;
3054             return;
3055         }
3056 
3057         setExpandedHeight(currentMaxPanelHeight);
3058     }
3059 
3060     private void setExpandedHeightInternal(float h) {
3061         if (isNaN(h)) {
3062             Log.wtf(TAG, "ExpandedHeight set to NaN");
3063         }
3064         mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> {
3065             if (mExpandLatencyTracking && h != 0f) {
3066                 DejankUtils.postAfterTraversal(
3067                         () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
3068                 mExpandLatencyTracking = false;
3069             }
3070             float maxPanelHeight = getMaxPanelTransitionDistance();
3071             mExpandedHeight = Math.min(h, maxPanelHeight);
3072             // If we are closing the panel and we are almost there due to a slow decelerating
3073             // interpolator, abort the animation.
3074             if (mExpandedHeight < 1f && mExpandedHeight != 0f && isClosing()) {
3075                 mExpandedHeight = 0f;
3076                 if (mHeightAnimator != null) {
3077                     mHeightAnimator.end();
3078                 }
3079             }
3080             mExpandedFraction = Math.min(1f,
3081                     maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
3082             if (mExpandedFraction > 0f && mExpectingSynthesizedDown) {
3083                 mExpectingSynthesizedDown = false;
3084             }
3085             mShadeRepository.setLegacyShadeExpansion(mExpandedFraction);
3086             mQsController.setShadeExpansion(mExpandedHeight, mExpandedFraction);
3087             mExpansionDragDownAmountPx = h;
3088             if (!SceneContainerFlag.isEnabled()) {
3089                 mAmbientState.setExpansionFraction(mExpandedFraction);
3090             }
3091             onHeightUpdated(mExpandedHeight);
3092             updateExpansionAndVisibility();
3093         });
3094     }
3095 
3096     /**
3097      * Set the current overexpansion
3098      *
3099      * @param overExpansion the amount of overexpansion to apply
3100      */
3101     private void setOverExpansionInternal(float overExpansion) {
3102         mLastGesturedOverExpansion = -1;
3103         setOverExpansion(overExpansion);
3104     }
3105 
3106     /** Sets the expanded height relative to a number from 0 to 1. */
3107     @VisibleForTesting
3108     void setExpandedFraction(float frac) {
3109         final int maxDist = getMaxPanelTransitionDistance();
3110         setExpandedHeight(maxDist * frac);
3111     }
3112 
3113     float getExpandedHeight() {
3114         return mExpandedHeight;
3115     }
3116 
3117     float getExpandedFraction() {
3118         return mExpandedFraction;
3119     }
3120 
3121     @Override
3122     public StateFlow<Float> getUdfpsTransitionToFullShadeProgress() {
3123         return mShadeRepository.getUdfpsTransitionToFullShadeProgress();
3124     }
3125 
3126     @Override
3127     public Flow<Float> getLegacyPanelExpansion() {
3128         return  mShadeRepository.getLegacyShadeExpansion();
3129     }
3130 
3131     @Override
3132     public boolean isFullyExpanded() {
3133         return mExpandedHeight >= getMaxPanelTransitionDistance();
3134     }
3135 
3136     public boolean isShadeFullyExpanded() {
3137         if (mBarState == SHADE) {
3138             return isFullyExpanded();
3139         } else if (mBarState == SHADE_LOCKED) {
3140             return true;
3141         } else {
3142             // case of swipe from the top of keyguard to expanded QS
3143             return mQsController.computeExpansionFraction() == 1;
3144         }
3145     }
3146 
3147     @Override
3148     public boolean isFullyCollapsed() {
3149         return mExpandedFraction <= 0.0f;
3150     }
3151 
3152     @Override
3153     public boolean isCollapsing() {
3154         return isClosing() || isLaunchingActivity();
3155     }
3156 
3157     public boolean isTracking() {
3158         return mShadeRepository.getLegacyShadeTracking().getValue();
3159     }
3160 
3161     @Override
3162     public boolean canBeCollapsed() {
3163         return !isFullyCollapsed() && !isTracking() && !isClosing();
3164     }
3165 
3166     public void instantCollapse() {
3167         abortAnimations();
3168         setExpandedFraction(0f);
3169         if (mExpanding) {
3170             notifyExpandingFinished();
3171         }
3172         if (mInstantExpanding) {
3173             mInstantExpanding = false;
3174             updateExpansionAndVisibility();
3175         }
3176     }
3177 
3178     private void abortAnimations() {
3179         cancelHeightAnimator();
3180         mView.removeCallbacks(mFlingCollapseRunnable);
3181     }
3182 
3183     private void setAnimator(ValueAnimator animator) {
3184         // TODO(b/341163515): Should we clean up the old animator?
3185         registerAnimatorForTest(animator);
3186         mHeightAnimator = animator;
3187         if (animator == null && mPanelUpdateWhenAnimatorEnds) {
3188             mPanelUpdateWhenAnimatorEnds = false;
3189             updateExpandedHeightToMaxHeight();
3190         }
3191     }
3192 
3193     /** Returns whether a shade or QS expansion animation is running */
3194     private boolean isShadeOrQsHeightAnimationRunning() {
3195         return mHeightAnimator != null && !mIsSpringBackAnimation;
3196     }
3197 
3198     /**
3199      * Create an animator that can also overshoot
3200      *
3201      * @param targetHeight    the target height
3202      * @param overshootAmount the amount of overshoot desired
3203      */
3204     private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
3205         float startExpansion = mOverExpansion;
3206         ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
3207         registerAnimatorForTest(animator);
3208         animator.addUpdateListener(
3209                 animation -> {
3210                     if (overshootAmount > 0.0f
3211                             // Also remove the overExpansion when collapsing
3212                             || (targetHeight == 0.0f && startExpansion != 0)) {
3213                         final float expansion = MathUtils.lerp(
3214                                 startExpansion,
3215                                 mPanelFlingOvershootAmount * overshootAmount,
3216                                 Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
3217                                         animator.getAnimatedFraction()));
3218                         setOverExpansionInternal(expansion);
3219                     }
3220                     setExpandedHeightInternal((float) animation.getAnimatedValue());
3221                 });
3222         return animator;
3223     }
3224 
3225     private void registerAnimatorForTest(Animator animator) {
3226         if (mTestSetOfAnimatorsUsed != null && animator != null) {
3227             mTestSetOfAnimatorsUsed.add(animator);
3228         }
3229     }
3230 
3231     /** Update the visibility of {@link NotificationPanelView} if necessary. */
3232     private void updateVisibility() {
3233         mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
3234     }
3235 
3236 
3237     @Override
3238     public void updateExpansionAndVisibility() {
3239         if (!SceneContainerFlag.isEnabled()) {
3240             mShadeExpansionStateManager.onPanelExpansionChanged(
3241                     mExpandedFraction, isExpanded(), isTracking());
3242         }
3243         updateVisibility();
3244     }
3245 
3246     @Override
3247     public boolean isExpanded() {
3248         return mExpandedFraction > 0f
3249                 || mInstantExpanding
3250                 || isPanelVisibleBecauseOfHeadsUp()
3251                 || isTracking()
3252                 || mHeightAnimator != null
3253                 || isPanelVisibleBecauseScrimIsAnimatingOff()
3254                 && !mIsSpringBackAnimation;
3255     }
3256 
3257     /** Called when the user performs a click anywhere in the empty area of the panel. */
3258     private void onEmptySpaceClick(float x, float y) {
3259         onMiddleClicked(x, y);
3260     }
3261 
3262     @VisibleForTesting
3263     boolean isClosing() {
3264         return mShadeRepository.getLegacyIsClosing().getValue();
3265     }
3266 
3267     public void collapseWithDuration(int animationDuration) {
3268         mFixedDuration = animationDuration;
3269         collapse(false /* delayed */, 1.0f /* speedUpFactor */);
3270         mFixedDuration = NO_FIXED_DURATION;
3271     }
3272 
3273     void postToView(Runnable action) {
3274         mView.post(action);
3275     }
3276 
3277     /** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */
3278     @Override
3279     public boolean handleExternalInterceptTouch(MotionEvent event) {
3280         try {
3281             mUseExternalTouch = true;
3282             return mTouchHandler.onInterceptTouchEvent(event);
3283         } finally {
3284             mUseExternalTouch = false;
3285         }
3286     }
3287 
3288     @Override
3289     public boolean handleExternalTouch(MotionEvent event) {
3290         try {
3291             mUseExternalTouch = true;
3292             return mTouchHandler.onTouchEvent(event);
3293         } finally {
3294             mUseExternalTouch = false;
3295         }
3296     }
3297 
3298     @Override
3299     public void updateTouchableRegion() {
3300         //A layout will ensure that onComputeInternalInsets will be called and after that we can
3301         // resize the layout. Make sure that the window stays small for one frame until the
3302         // touchableRegion is set.
3303         mView.requestLayout();
3304         mNotificationShadeWindowController.setForceWindowCollapsed(true);
3305         postToView(() -> {
3306             mNotificationShadeWindowController.setForceWindowCollapsed(false);
3307         });
3308     }
3309 
3310     @Override
3311     public boolean isViewEnabled() {
3312         return mView.isEnabled();
3313     }
3314 
3315     float getOverStretchAmount() {
3316         return mOverStretchAmount;
3317     }
3318 
3319     float getMinFraction() {
3320         return mMinFraction;
3321     }
3322 
3323     int getNavigationBarBottomHeight() {
3324         return mNavigationBarBottomHeight;
3325     }
3326 
3327     boolean isExpandingFromHeadsUp() {
3328         return mExpandingFromHeadsUp;
3329     }
3330 
3331     /**
3332      * We don't always want to close QS when requested as shade might be in a different state
3333      * already e.g. when going from collapse to expand very quickly. In that case StatusBar
3334      * window might send signal to collapse QS but we might be already expanding and in split
3335      * shade QS are always expanded
3336      */
3337     private void closeQsIfPossible() {
3338         boolean openOrOpening = isShadeFullyExpanded() || isExpandingOrCollapsing();
3339         if (!(mSplitShadeEnabled && openOrOpening)) {
3340             mQsController.closeQs();
3341         }
3342     }
3343 
3344     @Override
3345     public void setQsScrimEnabled(boolean qsScrimEnabled) {
3346         mQsController.setScrimEnabled(qsScrimEnabled);
3347     }
3348 
3349     private ShadeExpansionStateManager getShadeExpansionStateManager() {
3350         return mShadeExpansionStateManager;
3351     }
3352 
3353     void onQsExpansionChanged() {
3354         updateExpandedHeightToMaxHeight();
3355         updateSystemUiStateFlags();
3356         NavigationBarView navigationBarView =
3357                 mNavigationBarController.getNavigationBarView(mDisplayId);
3358         if (navigationBarView != null) {
3359             navigationBarView.onStatusBarPanelStateChanged();
3360         }
3361     }
3362 
3363     @VisibleForTesting
3364     void onQsSetExpansionHeightCalled(boolean qsFullyExpanded) {
3365         requestScrollerTopPaddingUpdate();
3366         mKeyguardStatusBarViewController.updateViewState();
3367         int barState = getBarState();
3368         if (barState == SHADE_LOCKED || barState == KEYGUARD) {
3369             positionClockAndNotifications();
3370         }
3371 
3372         if (mAccessibilityManager.isEnabled()) {
3373             mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
3374         }
3375 
3376         if (!mFalsingManager.isUnlockingDisabled() && qsFullyExpanded
3377                 && mFalsingCollector.shouldEnforceBouncer()) {
3378             mActivityStarter.executeRunnableDismissingKeyguard(null, null,
3379                     false, true, false);
3380         }
3381         if (DEBUG_DRAWABLE) {
3382             mView.invalidate();
3383         }
3384     }
3385 
3386     private void onQsClippingImmediatelyApplied(boolean clipStatusView,
3387             Rect lastQsClipBounds, int top, boolean qsFragmentCreated, boolean qsVisible) {
3388         if (qsFragmentCreated) {
3389             mKeyguardInteractor.setQuickSettingsVisible(qsVisible);
3390         }
3391 
3392         if (mSplitShadeEnabled) {
3393             mKeyguardStatusBarViewController.setNoTopClipping();
3394         } else {
3395             mKeyguardStatusBarViewController.updateTopClipping(top);
3396         }
3397     }
3398 
3399     private void onFlingQsWithoutClick(ValueAnimator animator, float qsExpansionHeight,
3400             float target, float vel) {
3401         mFlingAnimationUtils.apply(animator, qsExpansionHeight, target, vel);
3402     }
3403 
3404     private void onExpansionHeightSetToMax(boolean requestPaddingUpdate) {
3405         if (requestPaddingUpdate) {
3406             requestScrollerTopPaddingUpdate();
3407         }
3408         updateExpandedHeightToMaxHeight();
3409     }
3410 
3411     private final class NsslHeightChangedListener implements
3412             ExpandableView.OnHeightChangedListener {
3413         @Override
3414         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
3415             // Block update if we are in QS and just the top padding changed (i.e. view == null).
3416             if (view == null && mQsController.getExpanded()) {
3417                 return;
3418             }
3419             if (needsAnimation && mInterpolatedDarkAmount == 0) {
3420                 mAnimateNextPositionUpdate = true;
3421             }
3422             ExpandableView firstChildNotGone =
3423                     mNotificationStackScrollLayoutController.getFirstChildNotGone();
3424             ExpandableNotificationRow
3425                     firstRow =
3426                     firstChildNotGone instanceof ExpandableNotificationRow
3427                             ? (ExpandableNotificationRow) firstChildNotGone : null;
3428             if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
3429                     == firstRow))) {
3430                 requestScrollerTopPaddingUpdate();
3431             }
3432             updateExpandedHeightToMaxHeight();
3433         }
3434 
3435         @Override
3436         public void onReset(ExpandableView view) {}
3437     }
3438 
3439     private void onDynamicPrivacyChanged() {
3440         // Do not request animation when pulsing or waking up, otherwise the clock will be out
3441         // of sync with the notification panel.
3442         if (mLinearDarkAmount != 0) {
3443             return;
3444         }
3445         mAnimateNextPositionUpdate = true;
3446     }
3447 
3448     private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener {
3449         @Override
3450         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
3451             if (inPinnedMode) {
3452                 mHeadsUpExistenceChangedRunnable.run();
3453             } else {
3454                 setHeadsUpAnimatingAway(true);
3455                 mNotificationStackScrollLayoutController.runAfterAnimationFinished(
3456                         mHeadsUpExistenceChangedRunnable);
3457             }
3458             updateGestureExclusionRect();
3459             mHeadsUpPinnedMode = inPinnedMode;
3460             updateVisibility();
3461             mKeyguardStatusBarViewController.updateForHeadsUp();
3462         }
3463 
3464         @Override
3465         public void onHeadsUpPinned(NotificationEntry entry) {
3466             if (!isKeyguardShowing()) {
3467                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true);
3468             }
3469         }
3470 
3471         @Override
3472         public void onHeadsUpUnPinned(NotificationEntry entry) {
3473             // When we're unpinning the notification via active edge they remain heads-upped,
3474             // we need to make sure that an animation happens in this case, otherwise the
3475             // notification
3476             // will stick to the top without any interaction.
3477             if (isFullyCollapsed() && entry.isRowHeadsUp() && !isKeyguardShowing()) {
3478                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, false);
3479                 entry.setHeadsUpIsVisible();
3480             }
3481         }
3482     }
3483 
3484     private final class ConfigurationListener implements
3485             ConfigurationController.ConfigurationListener {
3486         @Override
3487         public void onConfigChanged(Configuration newConfig) {
3488             if (ShadeWindowGoesAround.isEnabled()) {
3489                 updateResources();
3490             }
3491         }
3492 
3493         @Override
3494         public void onThemeChanged() {
3495             debugLog("onThemeChanged");
3496             reInflateViews();
3497         }
3498 
3499         @Override
3500         public void onDensityOrFontScaleChanged() {
3501             debugLog("onDensityOrFontScaleChanged");
3502             reInflateViews();
3503         }
3504     }
3505 
3506     private final class StatusBarStateListener implements StateListener {
3507         @Override
3508         public void onStateChanged(int statusBarState) {
3509             onStateChanged(statusBarState, false);
3510         }
3511 
3512         private void onStateChanged(
3513                 int statusBarState,
3514                 boolean animatingUnlockedShadeToKeyguardBypass
3515         ) {
3516             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
3517             int oldState = mBarState;
3518             boolean keyguardShowing = statusBarState == KEYGUARD;
3519 
3520             // TODO: maybe add a listener for barstate
3521             mBarState = statusBarState;
3522             mQsController.setBarState(statusBarState);
3523 
3524             boolean fromShadeToKeyguard = statusBarState == KEYGUARD
3525                     && (oldState == SHADE || oldState == SHADE_LOCKED);
3526             if (mSplitShadeEnabled && fromShadeToKeyguard) {
3527                 // user can go to keyguard from different shade states and closing animation
3528                 // may not fully run - we always want to make sure we close QS when that happens
3529                 // as we never need QS open in fresh keyguard state
3530                 mQsController.closeQs();
3531             }
3532 
3533             if (oldState == KEYGUARD && (goingToFullShade
3534                     || statusBarState == StatusBarState.SHADE_LOCKED)) {
3535 
3536                 long startDelay;
3537                 long duration;
3538                 if (mKeyguardStateController.isKeyguardFadingAway()) {
3539                     startDelay = mKeyguardStateController.getKeyguardFadingAwayDelay();
3540                     duration = mKeyguardStateController.getShortenedFadingAwayDuration();
3541                 } else {
3542                     startDelay = 0;
3543                     duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
3544                 }
3545                 mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
3546                 mQsController.updateMinHeight();
3547             } else if (oldState == StatusBarState.SHADE_LOCKED
3548                     && statusBarState == KEYGUARD) {
3549                 mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
3550 
3551                 mNotificationStackScrollLayoutController.resetScrollPosition();
3552             } else {
3553                 // this else branch means we are doing one of:
3554                 //  - from KEYGUARD to SHADE (but not fully expanded as when swiping from the top)
3555                 //  - from SHADE to KEYGUARD
3556                 //  - from SHADE_LOCKED to SHADE
3557                 //  - getting notified again about the current SHADE or KEYGUARD state
3558                 final boolean animatingUnlockedShadeToKeyguard = oldState == SHADE
3559                         && statusBarState == KEYGUARD
3560                         && mScreenOffAnimationController.isKeyguardShowDelayed()
3561                         //Bypasses animatingUnlockedShadeToKeyguard for b/337742708
3562                         && !animatingUnlockedShadeToKeyguardBypass;
3563                 if (!animatingUnlockedShadeToKeyguard) {
3564                     // Only make the status bar visible if we're not animating the screen off, since
3565                     // we only want to be showing the clock/notifications during the animation.
3566                     mShadeLog.logKeyguardStatudBarVisibiliy(keyguardShowing, isOnAod(),
3567                             animatingUnlockedShadeToKeyguardBypass, oldState, statusBarState);
3568                     mKeyguardStatusBarViewController.updateViewState(
3569                             /* alpha= */ 1f,
3570                             keyguardShowing ? View.VISIBLE : View.INVISIBLE);
3571                 }
3572                 if (keyguardShowing && oldState != mBarState) {
3573                     mQsController.hideQsImmediately();
3574                 }
3575             }
3576             mKeyguardStatusBarViewController.updateForHeadsUp();
3577             if (keyguardShowing) {
3578                 updateDozingVisibilities(false /* animate */);
3579             }
3580 
3581             // The update needs to happen after the headerSlide in above, otherwise the translation
3582             // would reset
3583             mQsController.updateQsState();
3584         }
3585 
3586         @Override
3587         public void onDozeAmountChanged(float linearAmount, float amount) {
3588             mInterpolatedDarkAmount = amount;
3589             mLinearDarkAmount = linearAmount;
3590             positionClockAndNotifications();
3591         }
3592     }
3593 
3594     private final ShadeViewStateProvider mShadeViewStateProvider =
3595             new ShadeViewStateProvider() {
3596                 @Override
3597                 public float getPanelViewExpandedHeight() {
3598                     return getExpandedHeight();
3599                 }
3600 
3601                 @Override
3602                 public boolean shouldHeadsUpBeVisible() {
3603                     return mHeadsUpAppearanceController != null &&
3604                             mHeadsUpAppearanceController.shouldHeadsUpStatusBarBeVisible();
3605                 }
3606 
3607                 @Override
3608                 public float getLockscreenShadeDragProgress() {
3609                     return mQsController.getLockscreenShadeDragProgress();
3610                 }
3611             };
3612 
3613     @Override
3614     public void showAodUi() {
3615         setDozing(true /* dozing */, false /* animate */);
3616         mStatusBarStateController.setUpcomingState(KEYGUARD);
3617         mStatusBarStateController.setState(KEYGUARD);
3618         mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
3619         setExpandedFraction(1f);
3620     }
3621 
3622     @Override
3623     public void setOverStretchAmount(float amount) {
3624         float progress = amount / mView.getHeight();
3625         float overStretch = Interpolators.getOvershootInterpolation(progress);
3626         mOverStretchAmount = overStretch * mMaxOverscrollAmountForPulse;
3627         positionClockAndNotifications(true /* forceUpdate */);
3628     }
3629 
3630     private final class ShadeAttachStateChangeListener implements View.OnAttachStateChangeListener {
3631         @Override
3632         public void onViewAttachedToWindow(View v) {
3633             mFragmentService.getFragmentHostManager(mView)
3634                     .addTagListener(QS.TAG, mQsController.getQsFragmentListener());
3635             if (!SceneContainerFlag.isEnabled()) {
3636                 mStatusBarStateController.addCallback(mStatusBarStateListener);
3637                 // Bypass animatingUnlockedShadeToKeyguard in onStateChanged for b/337742708
3638                 mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState(), true);
3639             }
3640             mConfigurationController.addCallback(mConfigurationListener);
3641             // Theme might have changed between inflating this view and attaching it to the
3642             // window, so
3643             // force a call to onThemeChanged
3644             mConfigurationListener.onThemeChanged();
3645             mFalsingManager.addTapListener(mFalsingTapListener);
3646             mKeyguardIndicationController.init();
3647         }
3648 
3649         @Override
3650         public void onViewDetachedFromWindow(View v) {
3651             mFragmentService.getFragmentHostManager(mView)
3652                     .removeTagListener(QS.TAG, mQsController.getQsFragmentListener());
3653             mStatusBarStateController.removeCallback(mStatusBarStateListener);
3654             mConfigurationController.removeCallback(mConfigurationListener);
3655             mFalsingManager.removeTapListener(mFalsingTapListener);
3656         }
3657     }
3658 
3659     private final class ShadeLayoutChangeListener implements View.OnLayoutChangeListener {
3660         @Override
3661         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
3662                 int oldTop, int oldRight, int oldBottom) {
3663             DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
3664             updateExpandedHeightToMaxHeight();
3665             mHasLayoutedSinceDown = true;
3666             if (mUpdateFlingOnLayout) {
3667                 abortAnimations();
3668                 fling(mUpdateFlingVelocity);
3669                 mUpdateFlingOnLayout = false;
3670             }
3671             setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
3672 
3673             int oldMaxHeight = mQsController.updateHeightsOnShadeLayoutChange();
3674             positionClockAndNotifications();
3675             mQsController.handleShadeLayoutChanged(oldMaxHeight);
3676             updateExpandedHeight(getExpandedHeight());
3677             updateHeader();
3678 
3679             // If we are running a size change animation, the animation takes care of the height
3680             // of the container. However, if we are not animating, we always need to make the QS
3681             // container the desired height so when closing the QS detail, it stays smaller after
3682             // the size change animation is finished but the detail view is still being animated
3683             // away (this animation takes longer than the size change animation).
3684             mQsController.setHeightOverrideToDesiredHeight();
3685 
3686             updateMaxHeadsUpTranslation();
3687             updateGestureExclusionRect();
3688             if (mExpandAfterLayoutRunnable != null) {
3689                 mExpandAfterLayoutRunnable.run();
3690                 mExpandAfterLayoutRunnable = null;
3691             }
3692             DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
3693         }
3694     }
3695 
3696     @NonNull
3697     private WindowInsets onApplyShadeWindowInsets(WindowInsets insets) {
3698         // the same types of insets that are handled in NotificationShadeWindowView
3699         int insetTypes = WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
3700         Insets combinedInsets = insets.getInsetsIgnoringVisibility(insetTypes);
3701         mDisplayTopInset = combinedInsets.top;
3702         mDisplayRightInset = combinedInsets.right;
3703         mDisplayLeftInset = combinedInsets.left;
3704         mQsController.setDisplayInsets(mDisplayLeftInset, mDisplayRightInset);
3705 
3706         mNavigationBarBottomHeight = insets.getStableInsetBottom();
3707         updateMaxHeadsUpTranslation();
3708         return insets;
3709     }
3710 
3711     @Override
3712     public void cancelPendingCollapse() {
3713         mView.removeCallbacks(mMaybeHideExpandedRunnable);
3714     }
3715 
3716     private void onPanelStateChanged(@PanelState int state) {
3717         mShadeLog.logPanelStateChanged(state);
3718         mQsController.updateExpansionEnabledAmbient();
3719 
3720         if (state == STATE_OPEN && mCurrentPanelState != state) {
3721             mQsController.setExpandImmediate(false);
3722             mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
3723         }
3724         if (state == STATE_OPENING) {
3725             // we need to ignore it on keyguard as this is a false alarm - transition from unlocked
3726             // to locked will trigger this event and we're not actually in the process of opening
3727             // the shade, lockscreen is just always expanded
3728             if (mSplitShadeEnabled && !isKeyguardShowing()) {
3729                 mQsController.setExpandImmediate(true);
3730             }
3731             if (mOpenCloseListener != null) {
3732                 mOpenCloseListener.onOpenStarted();
3733             }
3734         }
3735         if (state == STATE_CLOSED) {
3736             mQsController.setExpandImmediate(false);
3737             // Close the status bar in the next frame so we can show the end of the animation.
3738             mView.post(mMaybeHideExpandedRunnable);
3739         }
3740         mCurrentPanelState = state;
3741     }
3742 
3743     private Consumer<Float> setDreamLockscreenTransitionAlpha() {
3744         return (Float alpha) -> {
3745             // Also animate the status bar's alpha during transitions between the lockscreen and
3746             // dreams.
3747             mKeyguardStatusBarViewController.setAlpha(alpha);
3748         };
3749     }
3750 
3751     @VisibleForTesting
3752     StatusBarStateController getStatusBarStateController() {
3753         return mStatusBarStateController;
3754     }
3755 
3756     @VisibleForTesting
3757     StateListener getStatusBarStateListener() {
3758         return mStatusBarStateListener;
3759     }
3760 
3761     /** Handles MotionEvents for the Shade. */
3762     public final class TouchHandler implements View.OnTouchListener, Gefingerpoken {
3763         private long mLastTouchDownTime = -1L;
3764 
3765         /**
3766          * With the shade and lockscreen being separated in the view hierarchy, touch handling now
3767          * originates with the parent window through {@link #handleExternalTouch}. This allows for
3768          * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
3769          * handling.
3770          *
3771          * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
3772          */
3773         @Override
3774         public boolean onInterceptTouchEvent(MotionEvent event) {
3775             if (!mUseExternalTouch) {
3776                 return false;
3777             }
3778 
3779             mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
3780             if (mQsController.disallowTouches()) {
3781                 mShadeLog.logMotionEvent(event,
3782                         "NPVC not intercepting touch, panel touches disallowed");
3783                 return false;
3784             }
3785             initDownStates(event);
3786             // Do not let touches go to shade or QS if the bouncer is visible,
3787             // but still let user swipe down to expand the panel, dismissing the bouncer.
3788             if (mCentralSurfaces.isBouncerShowing()) {
3789                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
3790                         + "bouncer is showing");
3791                 return true;
3792             }
3793             if (mCommandQueue.panelsEnabled()
3794                     && !mNotificationStackScrollLayoutController.isLongPressInProgress()
3795                     && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
3796                 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
3797                 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
3798                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
3799                         + "HeadsUpTouchHelper");
3800                 return true;
3801             }
3802             if (!mQsController.shouldQuickSettingsIntercept(mDownX, mDownY, 0)
3803                     && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
3804                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
3805                         + "PulseExpansionHandler");
3806                 return true;
3807             }
3808 
3809             if (!isFullyCollapsed() && mQsController.onIntercept(event)) {
3810                 debugLog("onQsIntercept true");
3811                 mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
3812                         + "QsIntercept");
3813                 return true;
3814             }
3815 
3816             if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled) {
3817                 mShadeLog.logNotInterceptingTouchInstantExpanding(mInstantExpanding,
3818                         !mNotificationsDragEnabled, mTouchDisabled);
3819                 return false;
3820             }
3821             if (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
3822                 mShadeLog.logMotionEventStatusBarState(event, mStatusBarStateController.getState(),
3823                         "NPVC MotionEvent not intercepted: non-down action, motion was aborted");
3824                 return false;
3825             }
3826 
3827             /* If the user drags anywhere inside the panel we intercept it if the movement is
3828              upwards. This allows closing the shade from anywhere inside the panel.
3829              We only do this if the current content is scrolled to the bottom, i.e.
3830              canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
3831              gesture possible. */
3832             int pointerIndex = event.findPointerIndex(mTrackingPointer);
3833             if (pointerIndex < 0) {
3834                 pointerIndex = 0;
3835                 mTrackingPointer = event.getPointerId(pointerIndex);
3836             }
3837             final float x = event.getX(pointerIndex);
3838             final float y = event.getY(pointerIndex);
3839             boolean canCollapsePanel = canCollapsePanelOnTouch();
3840             final boolean isTrackpadThreeFingerSwipe = isTrackpadThreeFingerSwipe(event);
3841 
3842             switch (event.getActionMasked()) {
3843                 case MotionEvent.ACTION_DOWN:
3844                     mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
3845                     mMinExpandHeight = 0.0f;
3846                     mDownTime = mSystemClock.uptimeMillis();
3847                     if (mAnimatingOnDown && isClosing()) {
3848                         cancelHeightAnimator();
3849                         mTouchSlopExceeded = true;
3850                         mShadeLog.v("NotificationPanelViewController MotionEvent intercepted:"
3851                                 + " mAnimatingOnDown: true, isClosing(): true");
3852                         return true;
3853                     }
3854 
3855                     if (!isTracking() || isFullyCollapsed()) {
3856                         mInitialExpandY = y;
3857                         mInitialExpandX = x;
3858                     } else {
3859                         mShadeLog.d("not setting mInitialExpandY in onInterceptTouch");
3860                     }
3861                     mTouchStartedInEmptyArea = !isInContentBounds(x, y);
3862                     mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
3863                     mMotionAborted = false;
3864                     mPanelClosedOnDown = isFullyCollapsed();
3865                     mShadeLog.logPanelClosedOnDown("intercept down touch", mPanelClosedOnDown,
3866                             mExpandedFraction);
3867                     mCollapsedAndHeadsUpOnDown = false;
3868                     mHasLayoutedSinceDown = false;
3869                     mUpdateFlingOnLayout = false;
3870                     mTouchAboveFalsingThreshold = false;
3871                     addMovement(event);
3872                     break;
3873                 case MotionEvent.ACTION_POINTER_UP:
3874                     if (isTrackpadThreeFingerSwipe) {
3875                         break;
3876                     }
3877                     final int upPointer = event.getPointerId(event.getActionIndex());
3878                     if (mTrackingPointer == upPointer) {
3879                         // gesture is ongoing, find a new pointer to track
3880                         final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
3881                         mTrackingPointer = event.getPointerId(newIndex);
3882                         mInitialExpandX = event.getX(newIndex);
3883                         mInitialExpandY = event.getY(newIndex);
3884                     }
3885                     break;
3886                 case MotionEvent.ACTION_POINTER_DOWN:
3887                     mShadeLog.logMotionEventStatusBarState(event,
3888                             mStatusBarStateController.getState(),
3889                             "onInterceptTouchEvent: pointer down action");
3890                     if (!isTrackpadThreeFingerSwipe
3891                             && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
3892                         mMotionAborted = true;
3893                         mVelocityTracker.clear();
3894                     }
3895                     break;
3896                 case MotionEvent.ACTION_MOVE:
3897                     final float h = y - mInitialExpandY;
3898                     addMovement(event);
3899                     final boolean openShadeWithoutHun =
3900                             mPanelClosedOnDown && !mCollapsedAndHeadsUpOnDown;
3901                     if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown
3902                             || openShadeWithoutHun) {
3903                         float hAbs = Math.abs(h);
3904                         float touchSlop = getTouchSlop(event);
3905                         if ((h < -touchSlop
3906                                 || ((openShadeWithoutHun || mAnimatingOnDown) && hAbs > touchSlop))
3907                                 && hAbs > Math.abs(x - mInitialExpandX)) {
3908                             cancelHeightAnimator();
3909                             startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
3910                             mShadeLog.v("NotificationPanelViewController MotionEvent"
3911                                     + " intercepted: startExpandMotion");
3912                             return true;
3913                         }
3914                     }
3915                     break;
3916                 case MotionEvent.ACTION_CANCEL:
3917                 case MotionEvent.ACTION_UP:
3918                     mVelocityTracker.clear();
3919                     break;
3920             }
3921             return false;
3922         }
3923 
3924         @Override
3925         public boolean onTouch(View v, MotionEvent event) {
3926             return onTouchEvent(event);
3927         }
3928 
3929         /**
3930          * With the shade and lockscreen being separated in the view hierarchy, touch handling now
3931          * originates with the parent window through {@link #handleExternalTouch}. This allows for
3932          * parity with the legacy hierarchy while not undertaking a massive refactoring of touch
3933          * handling.
3934          *
3935          * @see NotificationShadeWindowViewController#didNotificationPanelInterceptEvent
3936          */
3937         @Override
3938         public boolean onTouchEvent(MotionEvent event) {
3939             if (!mUseExternalTouch) {
3940                 return false;
3941             }
3942 
3943             if (mAlternateBouncerInteractor.isVisibleState()) {
3944                 // never send touches to shade if the alternate bouncer is showing
3945                 return false;
3946             }
3947 
3948             if (event.getAction() == MotionEvent.ACTION_DOWN) {
3949                 if (event.getDownTime() == mLastTouchDownTime) {
3950                     // An issue can occur when swiping down after unlock, where multiple down
3951                     // events are received in this handler with identical downTimes. Until the
3952                     // source of the issue can be located, detect this case and ignore.
3953                     // see b/193350347
3954                     mShadeLog.logMotionEvent(event,
3955                             "onTouch: duplicate down event detected... ignoring");
3956                     return true;
3957                 }
3958                 mLastTouchDownTime = event.getDownTime();
3959             }
3960 
3961             if (mQsController.isFullyExpandedAndTouchesDisallowed()) {
3962                 mShadeLog.logMotionEvent(event,
3963                         "onTouch: ignore touch, panel touches disallowed and qs fully expanded");
3964                 return false;
3965             }
3966 
3967             // Do not allow panel expansion if bouncer is scrimmed,
3968             // otherwise user would be able to pull down QS or expand the shade.
3969             if (mCentralSurfaces.isBouncerShowingScrimmed()) {
3970                 mShadeLog.logMotionEvent(event,
3971                         "onTouch: ignore touch, bouncer scrimmed or showing over dream");
3972                 return false;
3973             }
3974 
3975             // Make sure the next touch won't the blocked after the current ends.
3976             if (event.getAction() == MotionEvent.ACTION_UP
3977                     || event.getAction() == MotionEvent.ACTION_CANCEL) {
3978                 mBlockingExpansionForCurrentTouch = false;
3979             }
3980             // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
3981             // without any ACTION_MOVE event.
3982             // In such case, simply expand the panel instead of being stuck at the bottom bar.
3983             if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
3984                 expand(true /* animate */);
3985             }
3986             initDownStates(event);
3987 
3988             // If pulse is expanding already, let's give it the touch. There are situations
3989             // where the panel starts expanding even though we're also pulsing
3990             boolean pulseShouldGetTouch = (!mIsExpandingOrCollapsing
3991                     && !mQsController.shouldQuickSettingsIntercept(mDownX, mDownY, 0))
3992                     || mPulseExpansionHandler.isExpanding();
3993             if (pulseShouldGetTouch && mPulseExpansionHandler.onTouchEvent(event)) {
3994                 // We're expanding all the other ones shouldn't get this anymore
3995                 mShadeLog.logMotionEvent(event, "onTouch: PulseExpansionHandler handled event");
3996                 return true;
3997             }
3998             if (mPulsing) {
3999                 mShadeLog.logMotionEvent(event, "onTouch: eat touch, device pulsing");
4000                 return true;
4001             }
4002             if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
4003                     && !mNotificationStackScrollLayoutController.isLongPressInProgress()
4004                     && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
4005                 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
4006             }
4007             boolean handled = mHeadsUpTouchHelper.onTouchEvent(event);
4008 
4009             // This touch session has already resulted in shade expansion. Ignore everything else.
4010             if (ShadeExpandsOnStatusBarLongPress.isEnabled()
4011                     && event.getActionMasked() != MotionEvent.ACTION_DOWN
4012                     && event.getDownTime() == mStatusBarLongPressDowntime) {
4013                 mShadeLog.d("Touch has same down time as Status Bar long press. Ignoring.");
4014                 return false;
4015             }
4016             if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && mQsController.handleTouch(
4017                     event, isFullyCollapsed(), isShadeOrQsHeightAnimationRunning())) {
4018                 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
4019                     mShadeLog.logMotionEvent(event, "onTouch: handleQsTouch handled event");
4020                 }
4021                 return true;
4022             }
4023             if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
4024                 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
4025                 handled = true;
4026             }
4027 
4028             if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded()
4029                     && mKeyguardStateController.isShowing()) {
4030                 mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
4031             }
4032 
4033             handled |= handleTouch(event);
4034             return !mDozing || handled;
4035         }
4036 
4037         private boolean handleTouch(MotionEvent event) {
4038             if (mInstantExpanding) {
4039                 mShadeLog.logMotionEvent(event,
4040                         "handleTouch: touch ignored due to instant expanding");
4041                 return false;
4042             }
4043             if (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL) {
4044                 mShadeLog.logMotionEvent(event, "handleTouch: non-cancel action, touch disabled");
4045                 return false;
4046             }
4047             if (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
4048                 mShadeLog.logMotionEventStatusBarState(event, mStatusBarStateController.getState(),
4049                         "handleTouch: non-down action, motion was aborted");
4050                 return false;
4051             }
4052 
4053             // If dragging should not expand the notifications shade, then return false.
4054             if (!mNotificationsDragEnabled) {
4055                 if (isTracking()) {
4056                     // Turn off tracking if it's on or the shade can get stuck in the down position.
4057                     onTrackingStopped(true /* expand */);
4058                 }
4059                 mShadeLog.logMotionEvent(event, "handleTouch: drag not enabled");
4060                 return false;
4061             }
4062 
4063             /*
4064              * We capture touch events here and update the expand height here in case according to
4065              * the users fingers. This also handles multi-touch.
4066              *
4067              * Flinging is also enabled in order to open or close the shade.
4068              */
4069             int pointerIndex = event.findPointerIndex(mTrackingPointer);
4070             if (pointerIndex < 0) {
4071                 pointerIndex = 0;
4072                 mTrackingPointer = event.getPointerId(pointerIndex);
4073             }
4074             final float x = event.getX(pointerIndex);
4075             final float y = event.getY(pointerIndex);
4076 
4077             boolean isDown = event.getActionMasked() == MotionEvent.ACTION_DOWN;
4078             if (isDown
4079                     || event.getActionMasked() == MotionEvent.ACTION_MOVE) {
4080                 mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
4081                 mIgnoreXTouchSlop = true;
4082             }
4083 
4084             final boolean isTrackpadThreeFingerSwipe = isTrackpadThreeFingerSwipe(event);
4085             if (com.android.systemui.Flags.disableShadeTrackpadTwoFingerSwipe()
4086                     && !isTrackpadThreeFingerSwipe && isTwoFingerSwipeTrackpadEvent(event)
4087                     && !isPanelExpanded()) {
4088                 if (isDown) {
4089                     mShadeLog.d("ignoring down event for two finger trackpad swipe");
4090                 }
4091                 return false;
4092             }
4093 
4094             switch (event.getActionMasked()) {
4095                 case MotionEvent.ACTION_DOWN:
4096                     if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack) {
4097                         // Cache the gesture insets now, so we can quickly query them during
4098                         // ACTION_MOVE and decide whether to intercept events for back gesture anim.
4099                         mQsController.updateGestureInsetsCache();
4100                     }
4101                     mShadeLog.logMotionEvent(event, "onTouch: down action");
4102                     startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
4103                     mMinExpandHeight = 0.0f;
4104                     mPanelClosedOnDown = isFullyCollapsed();
4105                     mShadeLog.logPanelClosedOnDown("handle down touch", mPanelClosedOnDown,
4106                             mExpandedFraction);
4107                     mHasLayoutedSinceDown = false;
4108                     mUpdateFlingOnLayout = false;
4109                     mMotionAborted = false;
4110                     mDownTime = mSystemClock.uptimeMillis();
4111                     mStatusBarLongPressDowntime = -1L;
4112                     mTouchAboveFalsingThreshold = false;
4113                     mCollapsedAndHeadsUpOnDown =
4114                             isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
4115                     addMovement(event);
4116                     boolean regularHeightAnimationRunning = isShadeOrQsHeightAnimationRunning();
4117                     if (!mGestureWaitForTouchSlop || regularHeightAnimationRunning) {
4118                         mTouchSlopExceeded = regularHeightAnimationRunning
4119                                 || mTouchSlopExceededBeforeDown;
4120                         cancelHeightAnimator();
4121                         onTrackingStarted();
4122                     }
4123                     if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
4124                             && !mCentralSurfaces.isBouncerShowing()) {
4125                         startOpening(event);
4126                     }
4127                     break;
4128 
4129                 case MotionEvent.ACTION_POINTER_UP:
4130                     if (isTrackpadThreeFingerSwipe) {
4131                         break;
4132                     }
4133                     final int upPointer = event.getPointerId(event.getActionIndex());
4134                     if (mTrackingPointer == upPointer) {
4135                         // gesture is ongoing, find a new pointer to track
4136                         final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
4137                         final float newY = event.getY(newIndex);
4138                         final float newX = event.getX(newIndex);
4139                         mTrackingPointer = event.getPointerId(newIndex);
4140                         mHandlingPointerUp = true;
4141                         startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
4142                         mHandlingPointerUp = false;
4143                     }
4144                     break;
4145                 case MotionEvent.ACTION_POINTER_DOWN:
4146                     mShadeLog.logMotionEventStatusBarState(event,
4147                             mStatusBarStateController.getState(),
4148                             "handleTouch: pointer down action");
4149                     if (!isTrackpadThreeFingerSwipe
4150                             && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
4151                         mMotionAborted = true;
4152                         endMotionEvent(event, x, y, true /* forceCancel */);
4153                         return false;
4154                     }
4155                     break;
4156                 case MotionEvent.ACTION_MOVE:
4157                     // If the shade is half-collapsed, a horizontal swipe inwards from L/R edge
4158                     // must be routed to the back gesture (which shows a preview animation).
4159                     if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack
4160                             && mQsController.shouldBackBypassQuickSettings(x)) {
4161                         return false;
4162                     }
4163                     if (isFullyCollapsed()) {
4164                         // If panel is fully collapsed, reset haptic effect before adding movement.
4165                         mHasVibratedOnOpen = false;
4166                         mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
4167                     }
4168                     addMovement(event);
4169                     if (!isFullyCollapsed()) {
4170                         maybeVibrateOnOpening(true /* openingWithTouch */);
4171                     }
4172                     float h = y - mInitialExpandY;
4173 
4174                     // If the panel was collapsed when touching, we only need to check for the
4175                     // y-component of the gesture, as we have no conflicting horizontal gesture.
4176                     if (Math.abs(h) > getTouchSlop(event)
4177                             && (Math.abs(h) > Math.abs(x - mInitialExpandX)
4178                             || mIgnoreXTouchSlop)) {
4179                         mTouchSlopExceeded = true;
4180                         if (mGestureWaitForTouchSlop
4181                                 && !isTracking()
4182                                 && !mCollapsedAndHeadsUpOnDown
4183                                 && !isTwoFingerSwipeTrackpadEvent(event)
4184                         ) {
4185                             if (mInitialOffsetOnTouch != 0f) {
4186                                 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
4187                                 h = 0;
4188                             }
4189                             cancelHeightAnimator();
4190                             onTrackingStarted();
4191                         }
4192                     }
4193                     float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
4194                     newHeight = Math.max(newHeight, mMinExpandHeight);
4195                     if (-h >= getFalsingThreshold()) {
4196                         mTouchAboveFalsingThreshold = true;
4197                         mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
4198                     }
4199                     if ((!mGestureWaitForTouchSlop || isTracking())
4200                             && !(mBlockingExpansionForCurrentTouch
4201                             || mQsController.isTrackingBlocked())) {
4202                         // Count h==0 as part of swipe-up,
4203                         // otherwise {@link NotificationStackScrollLayout}
4204                         // wrongly enables stack height updates at the start of lockscreen swipe-up
4205                         mAmbientState.setSwipingUp(h <= 0);
4206                         setExpandedHeightInternal(newHeight);
4207                     }
4208                     break;
4209 
4210                 case MotionEvent.ACTION_UP:
4211                 case MotionEvent.ACTION_CANCEL:
4212                     mShadeLog.logMotionEvent(event, "onTouch: up/cancel action");
4213                     addMovement(event);
4214                     endMotionEvent(event, x, y, false /* forceCancel */);
4215                     // mHeightAnimator is null, there is no remaining frame, ends instrumenting.
4216                     if (mHeightAnimator == null) {
4217                         if (event.getActionMasked() == MotionEvent.ACTION_UP) {
4218                             mQsController.endJankMonitoring();
4219                         } else {
4220                             mQsController.cancelJankMonitoring();
4221                         }
4222                     }
4223                     break;
4224             }
4225             return !mGestureWaitForTouchSlop || isTracking();
4226         }
4227     }
4228 
4229     private static boolean isTwoFingerSwipeTrackpadEvent(MotionEvent event) {
4230         //SOURCE_MOUSE because SOURCE_TOUCHPAD is reserved for "captured" touchpads
4231         return event.getSource() == InputDevice.SOURCE_MOUSE
4232                 && event.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER
4233                 && event.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
4234     }
4235 
4236     private final class HeadsUpNotificationViewControllerImpl implements
4237             HeadsUpTouchHelper.HeadsUpNotificationViewController {
4238         @Override
4239         public void setHeadsUpDraggingStartingHeight(int startHeight) {
4240             NotificationPanelViewController.this.setHeadsUpDraggingStartingHeight(startHeight);
4241         }
4242 
4243         @Override
4244         public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
4245             if (pickedChild != null) {
4246                 mShadeHeadsUpTracker.updateTrackingHeadsUp(pickedChild);
4247                 mExpandingFromHeadsUp = true;
4248             }
4249             // otherwise we update the state when the expansion is finished
4250         }
4251 
4252         @Override
4253         public void startExpand(float x, float y, boolean startTracking, float expandedHeight) {
4254             startExpandMotion(x, y, startTracking, expandedHeight);
4255         }
4256     }
4257 
4258     private final class ShadeAccessibilityDelegate extends AccessibilityDelegate {
4259         @Override
4260         public void onInitializeAccessibilityNodeInfo(View host,
4261                 AccessibilityNodeInfo info) {
4262             super.onInitializeAccessibilityNodeInfo(host, info);
4263             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
4264             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
4265         }
4266 
4267         @Override
4268         public boolean performAccessibilityAction(View host, int action, Bundle args) {
4269             if (action
4270                     == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
4271                     || action
4272                     == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
4273                 mStatusBarKeyguardViewManager.showPrimaryBouncer(true,
4274                         "NotificationPanelViewController#performAccessibilityAction");
4275                 return true;
4276             }
4277             return super.performAccessibilityAction(host, action, args);
4278         }
4279     }
4280 }
4281