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