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.statusbar.phone; 18 19 import static android.view.View.GONE; 20 21 import static androidx.constraintlayout.widget.ConstraintSet.END; 22 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; 23 import static androidx.constraintlayout.widget.ConstraintSet.START; 24 import static androidx.constraintlayout.widget.ConstraintSet.TOP; 25 import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; 26 27 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE; 28 import static com.android.keyguard.KeyguardClockSwitch.LARGE; 29 import static com.android.keyguard.KeyguardClockSwitch.SMALL; 30 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE; 31 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; 32 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; 33 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 34 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 35 import static com.android.systemui.statusbar.StatusBarState.SHADE; 36 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; 37 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; 38 import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED; 39 import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN; 40 import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING; 41 42 import static java.lang.Float.isNaN; 43 44 import android.animation.Animator; 45 import android.animation.AnimatorListenerAdapter; 46 import android.animation.ValueAnimator; 47 import android.app.ActivityManager; 48 import android.app.Fragment; 49 import android.app.StatusBarManager; 50 import android.content.ContentResolver; 51 import android.content.pm.ResolveInfo; 52 import android.content.res.Configuration; 53 import android.content.res.Resources; 54 import android.database.ContentObserver; 55 import android.graphics.Canvas; 56 import android.graphics.Color; 57 import android.graphics.ColorFilter; 58 import android.graphics.Insets; 59 import android.graphics.Paint; 60 import android.graphics.PointF; 61 import android.graphics.Rect; 62 import android.graphics.Region; 63 import android.graphics.drawable.Drawable; 64 import android.hardware.biometrics.SensorLocationInternal; 65 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; 66 import android.os.Bundle; 67 import android.os.Handler; 68 import android.os.PowerManager; 69 import android.os.SystemClock; 70 import android.os.UserManager; 71 import android.os.VibrationEffect; 72 import android.provider.Settings; 73 import android.transition.ChangeBounds; 74 import android.transition.TransitionManager; 75 import android.util.Log; 76 import android.util.MathUtils; 77 import android.view.LayoutInflater; 78 import android.view.MotionEvent; 79 import android.view.VelocityTracker; 80 import android.view.View; 81 import android.view.ViewGroup; 82 import android.view.ViewPropertyAnimator; 83 import android.view.ViewStub; 84 import android.view.ViewTreeObserver; 85 import android.view.WindowInsets; 86 import android.view.accessibility.AccessibilityEvent; 87 import android.view.accessibility.AccessibilityManager; 88 import android.view.accessibility.AccessibilityNodeInfo; 89 import android.widget.FrameLayout; 90 91 import androidx.annotation.Nullable; 92 import androidx.constraintlayout.widget.ConstraintSet; 93 94 import com.android.internal.annotations.VisibleForTesting; 95 import com.android.internal.jank.InteractionJankMonitor; 96 import com.android.internal.logging.MetricsLogger; 97 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 98 import com.android.internal.policy.ScreenDecorationsUtils; 99 import com.android.internal.policy.SystemBarUtils; 100 import com.android.internal.util.LatencyTracker; 101 import com.android.keyguard.KeyguardStatusView; 102 import com.android.keyguard.KeyguardStatusViewController; 103 import com.android.keyguard.KeyguardUnfoldTransition; 104 import com.android.keyguard.KeyguardUpdateMonitor; 105 import com.android.keyguard.LockIconViewController; 106 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; 107 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; 108 import com.android.keyguard.dagger.KeyguardStatusViewComponent; 109 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; 110 import com.android.systemui.DejankUtils; 111 import com.android.systemui.Dependency; 112 import com.android.systemui.R; 113 import com.android.systemui.animation.ActivityLaunchAnimator; 114 import com.android.systemui.animation.Interpolators; 115 import com.android.systemui.animation.LaunchAnimator; 116 import com.android.systemui.biometrics.AuthController; 117 import com.android.systemui.classifier.Classifier; 118 import com.android.systemui.classifier.FalsingCollector; 119 import com.android.systemui.controls.dagger.ControlsComponent; 120 import com.android.systemui.dagger.qualifiers.DisplayId; 121 import com.android.systemui.dagger.qualifiers.Main; 122 import com.android.systemui.doze.DozeLog; 123 import com.android.systemui.flags.FeatureFlags; 124 import com.android.systemui.fragments.FragmentHostManager.FragmentListener; 125 import com.android.systemui.fragments.FragmentService; 126 import com.android.systemui.media.KeyguardMediaController; 127 import com.android.systemui.media.MediaDataManager; 128 import com.android.systemui.media.MediaHierarchyManager; 129 import com.android.systemui.model.SysUiState; 130 import com.android.systemui.navigationbar.NavigationModeController; 131 import com.android.systemui.plugins.FalsingManager; 132 import com.android.systemui.plugins.FalsingManager.FalsingTapListener; 133 import com.android.systemui.plugins.qs.DetailAdapter; 134 import com.android.systemui.plugins.qs.QS; 135 import com.android.systemui.plugins.statusbar.StatusBarStateController; 136 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; 137 import com.android.systemui.qs.QSDetailDisplayer; 138 import com.android.systemui.screenrecord.RecordingController; 139 import com.android.systemui.shared.system.QuickStepContract; 140 import com.android.systemui.statusbar.CommandQueue; 141 import com.android.systemui.statusbar.GestureRecorder; 142 import com.android.systemui.statusbar.KeyguardAffordanceView; 143 import com.android.systemui.statusbar.KeyguardIndicationController; 144 import com.android.systemui.statusbar.LockscreenShadeTransitionController; 145 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 146 import com.android.systemui.statusbar.NotificationRemoteInputManager; 147 import com.android.systemui.statusbar.NotificationShadeDepthController; 148 import com.android.systemui.statusbar.NotificationShelfController; 149 import com.android.systemui.statusbar.PulseExpansionHandler; 150 import com.android.systemui.statusbar.RemoteInputController; 151 import com.android.systemui.statusbar.StatusBarState; 152 import com.android.systemui.statusbar.SysuiStatusBarStateController; 153 import com.android.systemui.statusbar.VibratorHelper; 154 import com.android.systemui.statusbar.events.PrivacyDotViewController; 155 import com.android.systemui.statusbar.notification.AnimatableProperty; 156 import com.android.systemui.statusbar.notification.ConversationNotificationManager; 157 import com.android.systemui.statusbar.notification.DynamicPrivacyController; 158 import com.android.systemui.statusbar.notification.NotificationEntryManager; 159 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 160 import com.android.systemui.statusbar.notification.PropertyAnimator; 161 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; 162 import com.android.systemui.statusbar.notification.collection.ListEntry; 163 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 164 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; 165 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager; 166 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; 167 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 168 import com.android.systemui.statusbar.notification.row.ExpandableView; 169 import com.android.systemui.statusbar.notification.stack.AmbientState; 170 import com.android.systemui.statusbar.notification.stack.AnimationProperties; 171 import com.android.systemui.statusbar.notification.stack.MediaHeaderView; 172 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 173 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 174 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 175 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; 176 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; 177 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; 178 import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; 179 import com.android.systemui.statusbar.phone.panelstate.PanelState; 180 import com.android.systemui.statusbar.policy.ConfigurationController; 181 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; 182 import com.android.systemui.statusbar.policy.KeyguardStateController; 183 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; 184 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; 185 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 186 import com.android.systemui.unfold.SysUIUnfoldComponent; 187 import com.android.systemui.util.Utils; 188 import com.android.systemui.util.settings.SecureSettings; 189 import com.android.systemui.wallet.controller.QuickAccessWalletController; 190 import com.android.wm.shell.animation.FlingAnimationUtils; 191 192 import java.io.FileDescriptor; 193 import java.io.PrintWriter; 194 import java.util.ArrayList; 195 import java.util.Collections; 196 import java.util.List; 197 import java.util.Optional; 198 import java.util.concurrent.Executor; 199 import java.util.function.Consumer; 200 201 import javax.inject.Inject; 202 import javax.inject.Provider; 203 204 @StatusBarComponent.StatusBarScope 205 public class NotificationPanelViewController extends PanelViewController { 206 207 private static final boolean DEBUG = false; 208 209 /** 210 * The parallax amount of the quick settings translation when dragging down the panel 211 */ 212 private static final float QS_PARALLAX_AMOUNT = 0.175f; 213 214 /** 215 * Fling expanding QS. 216 */ 217 private static final int FLING_EXPAND = 0; 218 219 /** 220 * Fling collapsing QS, potentially stopping when QS becomes QQS. 221 */ 222 private static final int FLING_COLLAPSE = 1; 223 224 /** 225 * Fling until QS is completely hidden. 226 */ 227 private static final int FLING_HIDE = 2; 228 private static final long ANIMATION_DELAY_ICON_FADE_IN = 229 ActivityLaunchAnimator.TIMINGS.getTotalDuration() 230 - CollapsedStatusBarFragment.FADE_IN_DURATION 231 - CollapsedStatusBarFragment.FADE_IN_DELAY - 48; 232 233 private final DozeParameters mDozeParameters; 234 private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener(); 235 private final OnClickListener mOnClickListener = new OnClickListener(); 236 private final OnOverscrollTopChangedListener 237 mOnOverscrollTopChangedListener = 238 new OnOverscrollTopChangedListener(); 239 private final KeyguardAffordanceHelperCallback 240 mKeyguardAffordanceHelperCallback = 241 new KeyguardAffordanceHelperCallback(); 242 private final OnEmptySpaceClickListener 243 mOnEmptySpaceClickListener = 244 new OnEmptySpaceClickListener(); 245 private final MyOnHeadsUpChangedListener 246 mOnHeadsUpChangedListener = 247 new MyOnHeadsUpChangedListener(); 248 private final HeightListener mHeightListener = new HeightListener(); 249 private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); 250 private final SettingsChangeObserver mSettingsChangeObserver; 251 252 @VisibleForTesting final StatusBarStateListener mStatusBarStateListener = 253 new StatusBarStateListener(); 254 private final NotificationPanelView mView; 255 private final VibratorHelper mVibratorHelper; 256 private final MetricsLogger mMetricsLogger; 257 private final ActivityManager mActivityManager; 258 private final ConfigurationController mConfigurationController; 259 private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder; 260 private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; 261 private final NotificationIconAreaController mNotificationIconAreaController; 262 263 // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is 264 // changed. 265 private static final int CAP_HEIGHT = 1456; 266 private static final int FONT_HEIGHT = 2163; 267 268 /** 269 * Maximum time before which we will expand the panel even for slow motions when getting a 270 * touch passed over from launcher. 271 */ 272 private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300; 273 274 private static final String COUNTER_PANEL_OPEN = "panel_open"; 275 private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; 276 private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; 277 278 private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1); 279 private static final Rect EMPTY_RECT = new Rect(); 280 281 private final LayoutInflater mLayoutInflater; 282 private final PowerManager mPowerManager; 283 private final AccessibilityManager mAccessibilityManager; 284 private final NotificationWakeUpCoordinator mWakeUpCoordinator; 285 private final PulseExpansionHandler mPulseExpansionHandler; 286 private final KeyguardBypassController mKeyguardBypassController; 287 private final KeyguardUpdateMonitor mUpdateMonitor; 288 private final ConversationNotificationManager mConversationNotificationManager; 289 private final AuthController mAuthController; 290 private final MediaHierarchyManager mMediaHierarchyManager; 291 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 292 private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; 293 private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; 294 private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; 295 private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; 296 private final QSDetailDisplayer mQSDetailDisplayer; 297 private final FragmentService mFragmentService; 298 private final ScrimController mScrimController; 299 private final PrivacyDotViewController mPrivacyDotViewController; 300 private final QuickAccessWalletController mQuickAccessWalletController; 301 private final ControlsComponent mControlsComponent; 302 private final NotificationRemoteInputManager mRemoteInputManager; 303 304 // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. 305 // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications 306 private final int mMaxKeyguardNotifications; 307 private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; 308 private final TapAgainViewController mTapAgainViewController; 309 private final SplitShadeHeaderController mSplitShadeHeaderController; 310 private final RecordingController mRecordingController; 311 private boolean mShouldUseSplitNotificationShade; 312 // The bottom padding reserved for elements of the keyguard measuring notifications 313 private float mKeyguardNotificationBottomPadding; 314 // Current max allowed keyguard notifications determined by measuring the panel 315 private int mMaxAllowedKeyguardNotifications; 316 317 private ViewGroup mPreviewContainer; 318 private KeyguardAffordanceHelper mAffordanceHelper; 319 private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; 320 private KeyguardUserSwitcherController mKeyguardUserSwitcherController; 321 private KeyguardStatusBarView mKeyguardStatusBar; 322 private KeyguardStatusBarViewController mKeyguardStatusBarViewController; 323 @VisibleForTesting QS mQs; 324 private FrameLayout mQsFrame; 325 private KeyguardStatusViewController mKeyguardStatusViewController; 326 private LockIconViewController mLockIconViewController; 327 private NotificationsQuickSettingsContainer mNotificationContainerParent; 328 private NotificationsQSContainerController mNotificationsQSContainerController; 329 private boolean mAnimateNextPositionUpdate; 330 private float mQuickQsOffsetHeight; 331 private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; 332 333 private int mTrackingPointer; 334 private VelocityTracker mQsVelocityTracker; 335 private boolean mQsTracking; 336 337 /** 338 * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and 339 * the expansion for quick settings. 340 */ 341 private boolean mConflictingQsExpansionGesture; 342 343 private boolean mPanelExpanded; 344 private boolean mQsExpanded; 345 private boolean mQsExpandedWhenExpandingStarted; 346 private boolean mQsFullyExpanded; 347 private boolean mKeyguardShowing; 348 private boolean mKeyguardQsUserSwitchEnabled; 349 private boolean mKeyguardUserSwitcherEnabled; 350 private boolean mDozing; 351 private boolean mDozingOnDown; 352 private boolean mBouncerShowing; 353 private int mBarState; 354 private float mInitialHeightOnTouch; 355 private float mInitialTouchX; 356 private float mInitialTouchY; 357 private float mQsExpansionHeight; 358 private int mQsMinExpansionHeight; 359 private int mQsMaxExpansionHeight; 360 private int mQsPeekHeight; 361 private boolean mStackScrollerOverscrolling; 362 private boolean mQsExpansionFromOverscroll; 363 private float mLastOverscroll; 364 private boolean mQsExpansionEnabledPolicy = true; 365 private boolean mQsExpansionEnabledAmbient = true; 366 private ValueAnimator mQsExpansionAnimator; 367 private FlingAnimationUtils mFlingAnimationUtils; 368 private int mStatusBarMinHeight; 369 private int mStatusBarHeaderHeightKeyguard; 370 private float mOverStretchAmount; 371 private float mDownX; 372 private float mDownY; 373 private int mDisplayTopInset = 0; // in pixels 374 private int mDisplayRightInset = 0; // in pixels 375 private int mSplitShadeStatusBarHeight; 376 377 private final KeyguardClockPositionAlgorithm 378 mClockPositionAlgorithm = 379 new KeyguardClockPositionAlgorithm(); 380 private final KeyguardClockPositionAlgorithm.Result 381 mClockPositionResult = 382 new KeyguardClockPositionAlgorithm.Result(); 383 private boolean mIsExpanding; 384 385 private boolean mBlockTouches; 386 // Used for two finger gesture as well as accessibility shortcut to QS. 387 private boolean mQsExpandImmediate; 388 private boolean mTwoFingerQsExpandPossible; 389 private String mHeaderDebugInfo; 390 391 /** 392 * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still 393 * need to take this into account in our panel height calculation. 394 */ 395 private boolean mQsAnimatorExpand; 396 private boolean mIsLaunchTransitionFinished; 397 private boolean mIsLaunchTransitionRunning; 398 private Runnable mLaunchAnimationEndRunnable; 399 private boolean mOnlyAffordanceInThisMotion; 400 private ValueAnimator mQsSizeChangeAnimator; 401 402 private boolean mQsScrimEnabled = true; 403 private boolean mQsTouchAboveFalsingThreshold; 404 private int mQsFalsingThreshold; 405 406 private HeadsUpTouchHelper mHeadsUpTouchHelper; 407 private boolean mListenForHeadsUp; 408 private int mNavigationBarBottomHeight; 409 private boolean mExpandingFromHeadsUp; 410 private boolean mCollapsedOnDown; 411 private int mPositionMinSideMargin; 412 private int mLastOrientation = -1; 413 private boolean mClosingWithAlphaFadeOut; 414 private boolean mHeadsUpAnimatingAway; 415 private boolean mLaunchingAffordance; 416 private boolean mAffordanceHasPreview; 417 private final FalsingManager mFalsingManager; 418 private final FalsingCollector mFalsingCollector; 419 private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 420 421 private Runnable mHeadsUpExistenceChangedRunnable = () -> { 422 setHeadsUpAnimatingAway(false); 423 updatePanelExpansionAndVisibility(); 424 }; 425 // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager 426 private NotificationGroupManagerLegacy mGroupManager; 427 private boolean mShowIconsWhenExpanded; 428 private int mIndicationBottomPadding; 429 private int mAmbientIndicationBottomPadding; 430 private boolean mIsFullWidth; 431 private boolean mBlockingExpansionForCurrentTouch; 432 433 // TODO (b/204204226): no longer needed once refactor is complete 434 private final boolean mUseCombinedQSHeaders; 435 436 /** 437 * Following variables maintain state of events when input focus transfer may occur. 438 */ 439 private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event 440 private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event 441 442 /** 443 * Current dark amount that follows regular interpolation curve of animation. 444 */ 445 private float mInterpolatedDarkAmount; 446 447 /** 448 * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the 449 * interpolation curve is different. 450 */ 451 private float mLinearDarkAmount; 452 453 private boolean mPulsing; 454 private boolean mUserSetupComplete; 455 private boolean mHideIconsDuringLaunchAnimation = true; 456 private int mStackScrollerMeasuringPass; 457 /** 458 * Non-null if there's a heads-up notification that we're currently tracking the position of. 459 */ 460 @Nullable 461 private ExpandableNotificationRow mTrackedHeadsUpNotification; 462 private final ArrayList<Consumer<ExpandableNotificationRow>> 463 mTrackingHeadsUpListeners = new ArrayList<>(); 464 private HeadsUpAppearanceController mHeadsUpAppearanceController; 465 466 private int mPanelAlpha; 467 private Runnable mPanelAlphaEndAction; 468 private float mBottomAreaShadeAlpha; 469 private final ValueAnimator mBottomAreaShadeAlphaAnimator; 470 private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha", 471 NotificationPanelView::setPanelAlphaInternal, 472 NotificationPanelView::getCurrentPanelAlpha, 473 R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag, 474 R.id.panel_alpha_animator_end_tag); 475 private final AnimationProperties mPanelAlphaOutPropertiesAnimator = 476 new AnimationProperties().setDuration(150).setCustomInterpolator( 477 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT); 478 private final AnimationProperties mPanelAlphaInPropertiesAnimator = 479 new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> { 480 if (mPanelAlphaEndAction != null) { 481 mPanelAlphaEndAction.run(); 482 } 483 }).setCustomInterpolator( 484 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); 485 private final NotificationEntryManager mEntryManager; 486 487 private final CommandQueue mCommandQueue; 488 private final NotificationLockscreenUserManager mLockscreenUserManager; 489 private final UserManager mUserManager; 490 private final MediaDataManager mMediaDataManager; 491 private final SysUiState mSysUiState; 492 493 private NotificationShadeDepthController mDepthController; 494 private int mDisplayId; 495 496 /** 497 * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. 498 * 499 * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary 500 * work, check the current id with the cached id. 501 */ 502 private int mThemeResId; 503 private KeyguardIndicationController mKeyguardIndicationController; 504 private int mShelfHeight; 505 private int mDarkIconSize; 506 private int mHeadsUpInset; 507 private boolean mHeadsUpPinnedMode; 508 private boolean mAllowExpandForSmallExpansion; 509 private Runnable mExpandAfterLayoutRunnable; 510 511 /** 512 * The padding between the start of notifications and the qs boundary on the lockscreen. 513 * On lockscreen, notifications aren't inset this extra amount, but we still want the 514 * qs boundary to be padded. 515 */ 516 private int mLockscreenNotificationQSPadding; 517 518 /** 519 * The amount of progress we are currently in if we're transitioning to the full shade. 520 * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full 521 * shade. This value can also go beyond 1.1 when we're overshooting! 522 */ 523 private float mTransitioningToFullShadeProgress; 524 525 /** 526 * Position of the qs bottom during the full shade transition. This is needed as the toppadding 527 * can change during state changes, which makes it much harder to do animations 528 */ 529 private int mTransitionToFullShadeQSPosition; 530 531 /** 532 * Distance that the full shade transition takes in order for qs to fully transition to the 533 * shade. 534 */ 535 private int mDistanceForQSFullShadeTransition; 536 537 /** 538 * The translation amount for QS for the full shade transition 539 */ 540 private float mQsTranslationForFullShadeTransition; 541 542 /** 543 * The maximum overshoot allowed for the top padding for the full shade transition 544 */ 545 private int mMaxOverscrollAmountForPulse; 546 547 /** 548 * Should we animate the next bounds update 549 */ 550 private boolean mAnimateNextNotificationBounds; 551 /** 552 * The delay for the next bounds animation 553 */ 554 private long mNotificationBoundsAnimationDelay; 555 556 /** 557 * The duration of the notification bounds animation 558 */ 559 private long mNotificationBoundsAnimationDuration; 560 561 /** 562 * Is this a collapse that started on the panel where we should allow the panel to intercept 563 */ 564 private boolean mIsPanelCollapseOnQQS; 565 566 private boolean mAnimatingQS; 567 568 /** 569 * The end bounds of a clipping animation. 570 */ 571 private final Rect mQsClippingAnimationEndBounds = new Rect(); 572 573 /** 574 * The animator for the qs clipping bounds. 575 */ 576 private ValueAnimator mQsClippingAnimation = null; 577 578 /** 579 * Is the current animator resetting the qs translation. 580 */ 581 private boolean mIsQsTranslationResetAnimator; 582 583 /** 584 * Is the current animator resetting the pulse expansion after a drag down 585 */ 586 private boolean mIsPulseExpansionResetAnimator; 587 private final Rect mKeyguardStatusAreaClipBounds = new Rect(); 588 private final Region mQsInterceptRegion = new Region(); 589 590 /** 591 * The alpha of the views which only show on the keyguard but not in shade / shade locked 592 */ 593 private float mKeyguardOnlyContentAlpha = 1.0f; 594 595 private float mUdfpsMaxYBurnInOffset; 596 597 /** 598 * Are we currently in gesture navigation 599 */ 600 private boolean mIsGestureNavigation; 601 private int mOldLayoutDirection; 602 private NotificationShelfController mNotificationShelfController; 603 private int mScrimCornerRadius; 604 private int mScreenCornerRadius; 605 private boolean mQSAnimatingHiddenFromCollapsed; 606 607 private int mQsClipTop; 608 private int mQsClipBottom; 609 private boolean mQsVisible; 610 private final ContentResolver mContentResolver; 611 private float mMinFraction; 612 613 private final Executor mUiExecutor; 614 private final SecureSettings mSecureSettings; 615 616 private KeyguardMediaController mKeyguardMediaController; 617 618 private boolean mStatusViewCentered = true; 619 620 private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; 621 622 private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { 623 @Override 624 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 625 super.onInitializeAccessibilityNodeInfo(host, info); 626 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); 627 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP); 628 } 629 630 @Override 631 public boolean performAccessibilityAction(View host, int action, Bundle args) { 632 if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId() 633 || action 634 == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) { 635 mStatusBarKeyguardViewManager.showBouncer(true); 636 return true; 637 } 638 return super.performAccessibilityAction(host, action, args); 639 } 640 }; 641 642 private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() { 643 @Override 644 public void onDoubleTapRequired() { 645 if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { 646 mTapAgainViewController.show(); 647 } else { 648 mKeyguardIndicationController.showTransientIndication( 649 R.string.notification_tap_again); 650 } 651 mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM); 652 } 653 }; 654 655 @Inject NotificationPanelViewController(NotificationPanelView view, @Main Resources resources, @Main Handler handler, LayoutInflater layoutInflater, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationsQSContainerController notificationsQSContainerController, NotificationStackScrollLayoutController notificationStackScrollLayoutController, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, QSDetailDisplayer qsDetailDisplayer, NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, ScrimController scrimController, UserManager userManager, MediaDataManager mediaDataManager, NotificationShadeDepthController notificationShadeDepthController, AmbientState ambientState, LockIconViewController lockIconViewController, KeyguardMediaController keyguardMediaController, PrivacyDotViewController privacyDotViewController, TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, FragmentService fragmentService, ContentResolver contentResolver, QuickAccessWalletController quickAccessWalletController, RecordingController recordingController, @Main Executor uiExecutor, SecureSettings secureSettings, SplitShadeHeaderController splitShadeHeaderController, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, LockscreenGestureLogger lockscreenGestureLogger, PanelExpansionStateManager panelExpansionStateManager, NotificationRemoteInputManager remoteInputManager, Optional<SysUIUnfoldComponent> unfoldComponent, ControlsComponent controlsComponent, FeatureFlags featureFlags)656 public NotificationPanelViewController(NotificationPanelView view, 657 @Main Resources resources, 658 @Main Handler handler, 659 LayoutInflater layoutInflater, 660 NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, 661 DynamicPrivacyController dynamicPrivacyController, 662 KeyguardBypassController bypassController, FalsingManager falsingManager, 663 FalsingCollector falsingCollector, 664 NotificationLockscreenUserManager notificationLockscreenUserManager, 665 NotificationEntryManager notificationEntryManager, 666 KeyguardStateController keyguardStateController, 667 StatusBarStateController statusBarStateController, DozeLog dozeLog, 668 DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, 669 LatencyTracker latencyTracker, PowerManager powerManager, 670 AccessibilityManager accessibilityManager, @DisplayId int displayId, 671 KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, 672 ActivityManager activityManager, 673 ConfigurationController configurationController, 674 Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, 675 StatusBarTouchableRegionManager statusBarTouchableRegionManager, 676 ConversationNotificationManager conversationNotificationManager, 677 MediaHierarchyManager mediaHierarchyManager, 678 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 679 NotificationsQSContainerController notificationsQSContainerController, 680 NotificationStackScrollLayoutController notificationStackScrollLayoutController, 681 KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, 682 KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, 683 KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, 684 KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, 685 LockscreenShadeTransitionController lockscreenShadeTransitionController, 686 QSDetailDisplayer qsDetailDisplayer, 687 NotificationGroupManagerLegacy groupManager, 688 NotificationIconAreaController notificationIconAreaController, 689 AuthController authController, 690 ScrimController scrimController, 691 UserManager userManager, 692 MediaDataManager mediaDataManager, 693 NotificationShadeDepthController notificationShadeDepthController, 694 AmbientState ambientState, 695 LockIconViewController lockIconViewController, 696 KeyguardMediaController keyguardMediaController, 697 PrivacyDotViewController privacyDotViewController, 698 TapAgainViewController tapAgainViewController, 699 NavigationModeController navigationModeController, 700 FragmentService fragmentService, 701 ContentResolver contentResolver, 702 QuickAccessWalletController quickAccessWalletController, 703 RecordingController recordingController, 704 @Main Executor uiExecutor, 705 SecureSettings secureSettings, 706 SplitShadeHeaderController splitShadeHeaderController, 707 UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, 708 LockscreenGestureLogger lockscreenGestureLogger, 709 PanelExpansionStateManager panelExpansionStateManager, 710 NotificationRemoteInputManager remoteInputManager, 711 Optional<SysUIUnfoldComponent> unfoldComponent, 712 ControlsComponent controlsComponent, 713 FeatureFlags featureFlags) { 714 super(view, 715 falsingManager, 716 dozeLog, 717 keyguardStateController, 718 (SysuiStatusBarStateController) statusBarStateController, 719 vibratorHelper, 720 statusBarKeyguardViewManager, 721 latencyTracker, 722 flingAnimationUtilsBuilder.get(), 723 statusBarTouchableRegionManager, 724 lockscreenGestureLogger, 725 panelExpansionStateManager, 726 ambientState); 727 mView = view; 728 mVibratorHelper = vibratorHelper; 729 mKeyguardMediaController = keyguardMediaController; 730 mPrivacyDotViewController = privacyDotViewController; 731 mQuickAccessWalletController = quickAccessWalletController; 732 mControlsComponent = controlsComponent; 733 mMetricsLogger = metricsLogger; 734 mActivityManager = activityManager; 735 mConfigurationController = configurationController; 736 mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; 737 mMediaHierarchyManager = mediaHierarchyManager; 738 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 739 mNotificationsQSContainerController = notificationsQSContainerController; 740 mNotificationsQSContainerController.init(); 741 mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; 742 mGroupManager = groupManager; 743 mNotificationIconAreaController = notificationIconAreaController; 744 mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; 745 mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; 746 mDepthController = notificationShadeDepthController; 747 mContentResolver = contentResolver; 748 mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory; 749 mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory; 750 mQSDetailDisplayer = qsDetailDisplayer; 751 mFragmentService = fragmentService; 752 mSettingsChangeObserver = new SettingsChangeObserver(handler); 753 mShouldUseSplitNotificationShade = 754 Utils.shouldUseSplitNotificationShade(mResources); 755 mView.setWillNotDraw(!DEBUG); 756 mSplitShadeHeaderController = splitShadeHeaderController; 757 mLayoutInflater = layoutInflater; 758 mFalsingManager = falsingManager; 759 mFalsingCollector = falsingCollector; 760 mPowerManager = powerManager; 761 mWakeUpCoordinator = coordinator; 762 mAccessibilityManager = accessibilityManager; 763 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 764 setPanelAlpha(255, false /* animate */); 765 mCommandQueue = commandQueue; 766 mRecordingController = recordingController; 767 mDisplayId = displayId; 768 mPulseExpansionHandler = pulseExpansionHandler; 769 mDozeParameters = dozeParameters; 770 mScrimController = scrimController; 771 mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); 772 mUserManager = userManager; 773 mMediaDataManager = mediaDataManager; 774 mTapAgainViewController = tapAgainViewController; 775 mUiExecutor = uiExecutor; 776 mSecureSettings = secureSettings; 777 // TODO: inject via dagger instead of Dependency 778 mSysUiState = Dependency.get(SysUiState.class); 779 pulseExpansionHandler.setPulseExpandAbortListener(() -> { 780 if (mQs != null) { 781 mQs.animateHeaderSlidingOut(); 782 } 783 }); 784 mThemeResId = mView.getContext().getThemeResId(); 785 mKeyguardBypassController = bypassController; 786 mUpdateMonitor = keyguardUpdateMonitor; 787 mLockscreenShadeTransitionController = lockscreenShadeTransitionController; 788 lockscreenShadeTransitionController.setNotificationPanelController(this); 789 DynamicPrivacyControlListener 790 dynamicPrivacyControlListener = 791 new DynamicPrivacyControlListener(); 792 dynamicPrivacyController.addListener(dynamicPrivacyControlListener); 793 794 panelExpansionStateManager.addStateListener(this::onPanelStateChanged); 795 796 mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0); 797 mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> { 798 mBottomAreaShadeAlpha = (float) animation.getAnimatedValue(); 799 updateKeyguardBottomAreaAlpha(); 800 }); 801 mBottomAreaShadeAlphaAnimator.setDuration(160); 802 mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); 803 mLockscreenUserManager = notificationLockscreenUserManager; 804 mEntryManager = notificationEntryManager; 805 mConversationNotificationManager = conversationNotificationManager; 806 mAuthController = authController; 807 mLockIconViewController = lockIconViewController; 808 mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; 809 mRemoteInputManager = remoteInputManager; 810 811 int currentMode = navigationModeController.addListener( 812 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode)); 813 mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode); 814 815 mView.setBackgroundColor(Color.TRANSPARENT); 816 OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); 817 mView.addOnAttachStateChangeListener(onAttachStateChangeListener); 818 if (mView.isAttachedToWindow()) { 819 onAttachStateChangeListener.onViewAttachedToWindow(mView); 820 } 821 822 mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener()); 823 824 if (DEBUG) { 825 mView.getOverlay().add(new DebugDrawable()); 826 } 827 828 mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); 829 mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition()); 830 updateUserSwitcherFlags(); 831 onFinishInflate(); 832 833 mUseCombinedQSHeaders = featureFlags.useCombinedQSHeaders(); 834 } 835 onFinishInflate()836 private void onFinishInflate() { 837 loadDimens(); 838 mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); 839 840 FrameLayout userAvatarContainer = null; 841 KeyguardUserSwitcherView keyguardUserSwitcherView = null; 842 843 if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) { 844 if (mKeyguardQsUserSwitchEnabled) { 845 ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub); 846 userAvatarContainer = (FrameLayout) stub.inflate(); 847 } else { 848 ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub); 849 keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate(); 850 } 851 } 852 853 mKeyguardStatusBarViewController = 854 mKeyguardStatusBarViewComponentFactory.build( 855 mKeyguardStatusBar, 856 mNotificationPanelViewStateProvider) 857 .getKeyguardStatusBarViewController(); 858 mKeyguardStatusBarViewController.init(); 859 860 mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); 861 updateViewControllers( 862 mView.findViewById(R.id.keyguard_status_view), 863 userAvatarContainer, 864 keyguardUserSwitcherView); 865 866 NotificationStackScrollLayout stackScrollLayout = mView.findViewById( 867 R.id.notification_stack_scroller); 868 mNotificationStackScrollLayoutController.attach(stackScrollLayout); 869 mNotificationStackScrollLayoutController.setOnHeightChangedListener( 870 mOnHeightChangedListener); 871 mNotificationStackScrollLayoutController.setOverscrollTopChangedListener( 872 mOnOverscrollTopChangedListener); 873 mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled); 874 mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged); 875 mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener( 876 mOnEmptySpaceClickListener); 877 addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); 878 mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); 879 mPreviewContainer = mView.findViewById(R.id.preview_container); 880 mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); 881 mLastOrientation = mResources.getConfiguration().orientation; 882 883 initBottomArea(); 884 885 mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); 886 mQsFrame = mView.findViewById(R.id.qs_frame); 887 mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController); 888 mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { 889 @Override 890 public void onFullyHiddenChanged(boolean isFullyHidden) { 891 mKeyguardStatusBarViewController.updateForHeadsUp(); 892 } 893 894 @Override 895 public void onPulseExpansionChanged(boolean expandingChanged) { 896 if (mKeyguardBypassController.getBypassEnabled()) { 897 // Position the notifications while dragging down while pulsing 898 requestScrollerTopPaddingUpdate(false /* animate */); 899 } 900 } 901 }); 902 903 mView.setRtlChangeListener(layoutDirection -> { 904 if (layoutDirection != mOldLayoutDirection) { 905 mAffordanceHelper.onRtlPropertiesChanged(); 906 mOldLayoutDirection = layoutDirection; 907 } 908 }); 909 910 mView.setAccessibilityDelegate(mAccessibilityDelegate); 911 if (mShouldUseSplitNotificationShade) { 912 updateResources(); 913 } 914 915 mTapAgainViewController.init(); 916 mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); 917 } 918 919 @Override loadDimens()920 protected void loadDimens() { 921 super.loadDimens(); 922 mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get() 923 .setMaxLengthSeconds(0.4f).build(); 924 mStatusBarMinHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); 925 mStatusBarHeaderHeightKeyguard = Utils.getStatusBarHeaderHeightKeyguard(mView.getContext()); 926 mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); 927 mClockPositionAlgorithm.loadDimens(mResources); 928 mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold); 929 mPositionMinSideMargin = mResources.getDimensionPixelSize( 930 R.dimen.notification_panel_min_side_margin); 931 mIndicationBottomPadding = mResources.getDimensionPixelSize( 932 R.dimen.keyguard_indication_bottom_padding); 933 mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height); 934 mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); 935 int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); 936 mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize( 937 R.dimen.heads_up_status_bar_padding); 938 mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize( 939 R.dimen.lockscreen_shade_qs_transition_distance); 940 mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize( 941 R.dimen.pulse_expansion_max_top_overshoot); 942 mScrimCornerRadius = mResources.getDimensionPixelSize( 943 R.dimen.notification_scrim_corner_radius); 944 mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius( 945 mView.getContext()); 946 mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize( 947 R.dimen.notification_side_paddings); 948 mUdfpsMaxYBurnInOffset = mResources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); 949 } 950 updateViewControllers(KeyguardStatusView keyguardStatusView, FrameLayout userAvatarView, KeyguardUserSwitcherView keyguardUserSwitcherView)951 private void updateViewControllers(KeyguardStatusView keyguardStatusView, 952 FrameLayout userAvatarView, 953 KeyguardUserSwitcherView keyguardUserSwitcherView) { 954 // Re-associate the KeyguardStatusViewController 955 KeyguardStatusViewComponent statusViewComponent = 956 mKeyguardStatusViewComponentFactory.build(keyguardStatusView); 957 mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); 958 mKeyguardStatusViewController.init(); 959 960 if (mKeyguardUserSwitcherController != null) { 961 // Try to close the switcher so that callbacks are triggered if necessary. 962 // Otherwise, NPV can get into a state where some of the views are still hidden 963 mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false); 964 } 965 966 mKeyguardQsUserSwitchController = null; 967 mKeyguardUserSwitcherController = null; 968 969 // Re-associate the KeyguardUserSwitcherController 970 if (userAvatarView != null) { 971 KeyguardQsUserSwitchComponent userSwitcherComponent = 972 mKeyguardQsUserSwitchComponentFactory.build(userAvatarView); 973 mKeyguardQsUserSwitchController = 974 userSwitcherComponent.getKeyguardQsUserSwitchController(); 975 mKeyguardQsUserSwitchController.setNotificationPanelViewController(this); 976 mKeyguardQsUserSwitchController.init(); 977 mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true); 978 } else if (keyguardUserSwitcherView != null) { 979 KeyguardUserSwitcherComponent userSwitcherComponent = 980 mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView); 981 mKeyguardUserSwitcherController = 982 userSwitcherComponent.getKeyguardUserSwitcherController(); 983 mKeyguardUserSwitcherController.init(); 984 mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true); 985 } else { 986 mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false); 987 } 988 } 989 990 /** 991 * Returns if there's a custom clock being presented. 992 */ hasCustomClock()993 public boolean hasCustomClock() { 994 return mKeyguardStatusViewController.hasCustomClock(); 995 } 996 setStatusBar(StatusBar bar)997 private void setStatusBar(StatusBar bar) { 998 // TODO: this can be injected. 999 mStatusBar = bar; 1000 mKeyguardBottomArea.setStatusBar(mStatusBar); 1001 } 1002 updateResources()1003 public void updateResources() { 1004 mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext()); 1005 mSplitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(mView.getContext()); 1006 int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); 1007 int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); 1008 mShouldUseSplitNotificationShade = 1009 Utils.shouldUseSplitNotificationShade(mResources); 1010 mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); 1011 if (mQs != null) { 1012 mQs.setInSplitShade(mShouldUseSplitNotificationShade); 1013 } 1014 1015 int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight : 1016 mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top); 1017 mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade); 1018 1019 // To change the constraints at runtime, all children of the ConstraintLayout must have ids 1020 ensureAllViewsHaveIds(mNotificationContainerParent); 1021 ConstraintSet constraintSet = new ConstraintSet(); 1022 constraintSet.clone(mNotificationContainerParent); 1023 if (mShouldUseSplitNotificationShade) { 1024 // width = 0 to take up all available space within constraints 1025 qsWidth = 0; 1026 panelWidth = 0; 1027 constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END); 1028 constraintSet.connect( 1029 R.id.notification_stack_scroller, START, 1030 R.id.qs_edge_guideline, START); 1031 constraintSet.constrainHeight(R.id.split_shade_status_bar, mSplitShadeStatusBarHeight); 1032 } else { 1033 constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END); 1034 constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START); 1035 if (mUseCombinedQSHeaders) { 1036 constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT); 1037 } 1038 } 1039 constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth; 1040 constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth; 1041 constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin); 1042 constraintSet.setMargin(R.id.qs_frame, TOP, topMargin); 1043 constraintSet.applyTo(mNotificationContainerParent); 1044 mAmbientState.setStackTopMargin(topMargin); 1045 mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade); 1046 1047 updateKeyguardStatusViewAlignment(/* animate= */false); 1048 1049 mKeyguardMediaController.refreshMediaPosition(); 1050 } 1051 ensureAllViewsHaveIds(ViewGroup parentView)1052 private static void ensureAllViewsHaveIds(ViewGroup parentView) { 1053 for (int i = 0; i < parentView.getChildCount(); i++) { 1054 View childView = parentView.getChildAt(i); 1055 if (childView.getId() == View.NO_ID) { 1056 childView.setId(View.generateViewId()); 1057 } 1058 } 1059 } 1060 reInflateStub(int viewId, int stubId, int layoutId, boolean enabled)1061 private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) { 1062 View view = mView.findViewById(viewId); 1063 if (view != null) { 1064 int index = mView.indexOfChild(view); 1065 mView.removeView(view); 1066 if (enabled) { 1067 view = mLayoutInflater.inflate(layoutId, mView, false); 1068 mView.addView(view, index); 1069 } else { 1070 // Add the stub back so we can re-inflate it again if necessary 1071 ViewStub stub = new ViewStub(mView.getContext(), layoutId); 1072 stub.setId(stubId); 1073 mView.addView(stub, index); 1074 view = null; 1075 } 1076 } else if (enabled) { 1077 // It's possible the stub was never inflated if the configuration changed 1078 ViewStub stub = mView.findViewById(stubId); 1079 view = stub.inflate(); 1080 } 1081 return view; 1082 } 1083 reInflateViews()1084 private void reInflateViews() { 1085 if (DEBUG) Log.d(TAG, "reInflateViews"); 1086 // Re-inflate the status view group. 1087 KeyguardStatusView keyguardStatusView = 1088 mNotificationContainerParent.findViewById(R.id.keyguard_status_view); 1089 int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView); 1090 mNotificationContainerParent.removeView(keyguardStatusView); 1091 keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate( 1092 R.layout.keyguard_status_view, mNotificationContainerParent, false); 1093 mNotificationContainerParent.addView(keyguardStatusView, statusIndex); 1094 // When it's reinflated, this is centered by default. If it shouldn't be, this will update 1095 // below when resources are updated. 1096 mStatusViewCentered = true; 1097 attachSplitShadeMediaPlayerContainer( 1098 keyguardStatusView.findViewById(R.id.status_view_media_container)); 1099 1100 // we need to update KeyguardStatusView constraints after reinflating it 1101 updateResources(); 1102 1103 // Re-inflate the keyguard user switcher group. 1104 updateUserSwitcherFlags(); 1105 boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled(); 1106 boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled; 1107 boolean showKeyguardUserSwitcher = 1108 !mKeyguardQsUserSwitchEnabled 1109 && mKeyguardUserSwitcherEnabled 1110 && isUserSwitcherEnabled; 1111 FrameLayout userAvatarView = (FrameLayout) reInflateStub( 1112 R.id.keyguard_qs_user_switch_view /* viewId */, 1113 R.id.keyguard_qs_user_switch_stub /* stubId */, 1114 R.layout.keyguard_qs_user_switch /* layoutId */, 1115 showQsUserSwitch /* enabled */); 1116 KeyguardUserSwitcherView keyguardUserSwitcherView = 1117 (KeyguardUserSwitcherView) reInflateStub( 1118 R.id.keyguard_user_switcher_view /* viewId */, 1119 R.id.keyguard_user_switcher_stub /* stubId */, 1120 R.layout.keyguard_user_switcher /* layoutId */, 1121 showKeyguardUserSwitcher /* enabled */); 1122 1123 updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView, 1124 keyguardUserSwitcherView); 1125 1126 // Update keyguard bottom area 1127 int index = mView.indexOfChild(mKeyguardBottomArea); 1128 mView.removeView(mKeyguardBottomArea); 1129 KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; 1130 mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate( 1131 R.layout.keyguard_bottom_area, mView, false); 1132 mKeyguardBottomArea.initFrom(oldBottomArea); 1133 mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); 1134 mView.addView(mKeyguardBottomArea, index); 1135 initBottomArea(); 1136 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); 1137 mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), 1138 mStatusBarStateController.getInterpolatedDozeAmount()); 1139 1140 mKeyguardStatusViewController.setKeyguardStatusViewVisibility( 1141 mBarState, 1142 false, 1143 false, 1144 mBarState); 1145 if (mKeyguardQsUserSwitchController != null) { 1146 mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility( 1147 mBarState, 1148 false, 1149 false, 1150 mBarState); 1151 } 1152 if (mKeyguardUserSwitcherController != null) { 1153 mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility( 1154 mBarState, 1155 false, 1156 false, 1157 mBarState); 1158 } 1159 setKeyguardBottomAreaVisibility(mBarState, false); 1160 1161 mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); 1162 } 1163 attachSplitShadeMediaPlayerContainer(FrameLayout container)1164 private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { 1165 mKeyguardMediaController.attachSplitShadeContainer(container); 1166 } 1167 initBottomArea()1168 private void initBottomArea() { 1169 mAffordanceHelper = new KeyguardAffordanceHelper( 1170 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager); 1171 mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper); 1172 mKeyguardBottomArea.setStatusBar(mStatusBar); 1173 mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete); 1174 mKeyguardBottomArea.setFalsingManager(mFalsingManager); 1175 mKeyguardBottomArea.initWallet(mQuickAccessWalletController); 1176 mKeyguardBottomArea.initControls(mControlsComponent); 1177 } 1178 updateMaxDisplayedNotifications(boolean recompute)1179 private void updateMaxDisplayedNotifications(boolean recompute) { 1180 if (recompute) { 1181 mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1); 1182 } 1183 1184 if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) { 1185 mNotificationStackScrollLayoutController.setMaxDisplayedNotifications( 1186 mMaxAllowedKeyguardNotifications); 1187 mNotificationStackScrollLayoutController.setKeyguardBottomPadding( 1188 mKeyguardNotificationBottomPadding); 1189 } else { 1190 // no max when not on the keyguard 1191 mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); 1192 mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f); 1193 } 1194 } 1195 setKeyguardIndicationController(KeyguardIndicationController indicationController)1196 public void setKeyguardIndicationController(KeyguardIndicationController indicationController) { 1197 mKeyguardIndicationController = indicationController; 1198 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); 1199 } 1200 updateGestureExclusionRect()1201 private void updateGestureExclusionRect() { 1202 Rect exclusionRect = calculateGestureExclusionRect(); 1203 mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST 1204 : Collections.singletonList(exclusionRect)); 1205 } 1206 calculateGestureExclusionRect()1207 private Rect calculateGestureExclusionRect() { 1208 Rect exclusionRect = null; 1209 Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion(); 1210 if (isFullyCollapsed() && touchableRegion != null) { 1211 // Note: The manager also calculates the non-pinned touchable region 1212 exclusionRect = touchableRegion.getBounds(); 1213 } 1214 return exclusionRect != null ? exclusionRect : EMPTY_RECT; 1215 } 1216 setIsFullWidth(boolean isFullWidth)1217 private void setIsFullWidth(boolean isFullWidth) { 1218 mIsFullWidth = isFullWidth; 1219 mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth); 1220 } 1221 startQsSizeChangeAnimation(int oldHeight, final int newHeight)1222 private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) { 1223 if (mQsSizeChangeAnimator != null) { 1224 oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 1225 mQsSizeChangeAnimator.cancel(); 1226 } 1227 mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); 1228 mQsSizeChangeAnimator.setDuration(300); 1229 mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 1230 mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 1231 @Override 1232 public void onAnimationUpdate(ValueAnimator animation) { 1233 requestScrollerTopPaddingUpdate(false /* animate */); 1234 requestPanelHeightUpdate(); 1235 int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); 1236 mQs.setHeightOverride(height); 1237 } 1238 }); 1239 mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { 1240 @Override 1241 public void onAnimationEnd(Animator animation) { 1242 mQsSizeChangeAnimator = null; 1243 } 1244 }); 1245 mQsSizeChangeAnimator.start(); 1246 } 1247 1248 /** 1249 * Positions the clock and notifications dynamically depending on how many notifications are 1250 * showing. 1251 */ positionClockAndNotifications()1252 private void positionClockAndNotifications() { 1253 positionClockAndNotifications(false /* forceUpdate */); 1254 } 1255 1256 /** 1257 * Positions the clock and notifications dynamically depending on how many notifications are 1258 * showing. 1259 * 1260 * @param forceClockUpdate Should the clock be updated even when not on keyguard 1261 */ positionClockAndNotifications(boolean forceClockUpdate)1262 private void positionClockAndNotifications(boolean forceClockUpdate) { 1263 boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); 1264 int stackScrollerPadding; 1265 boolean onKeyguard = isOnKeyguard(); 1266 if (onKeyguard || forceClockUpdate) { 1267 updateClockAppearance(); 1268 } 1269 if (!onKeyguard) { 1270 if (mShouldUseSplitNotificationShade) { 1271 // Quick settings are not on the top of the notifications 1272 // when in split shade mode (they are on the left side), 1273 // so we should not add a padding for them 1274 stackScrollerPadding = 0; 1275 } else { 1276 stackScrollerPadding = getUnlockedStackScrollerPadding(); 1277 } 1278 } else { 1279 stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; 1280 } 1281 1282 mSplitShadeHeaderController.setShadeExpandedFraction(getExpandedFraction()); 1283 mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding); 1284 mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); 1285 1286 mStackScrollerMeasuringPass++; 1287 requestScrollerTopPaddingUpdate(animate); 1288 mStackScrollerMeasuringPass = 0; 1289 mAnimateNextPositionUpdate = false; 1290 } 1291 updateClockAppearance()1292 private void updateClockAppearance() { 1293 int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; 1294 boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); 1295 final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController 1296 .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); 1297 boolean splitShadeWithActiveMedia = 1298 mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia(); 1299 if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade) 1300 || (splitShadeWithActiveMedia && !mDozing)) { 1301 mKeyguardStatusViewController.displayClock(SMALL); 1302 } else { 1303 mKeyguardStatusViewController.displayClock(LARGE); 1304 } 1305 updateKeyguardStatusViewAlignment(true /* animate */); 1306 int userSwitcherHeight = mKeyguardQsUserSwitchController != null 1307 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0; 1308 if (mKeyguardUserSwitcherController != null) { 1309 userSwitcherHeight = mKeyguardUserSwitcherController.getHeight(); 1310 } 1311 float expandedFraction = 1312 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() 1313 ? 1.0f : getExpandedFraction(); 1314 float darkamount = 1315 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying() 1316 ? 1.0f : mInterpolatedDarkAmount; 1317 1318 float udfpsAodTopLocation = -1f; 1319 if (mUpdateMonitor.isUdfpsEnrolled() && mAuthController.getUdfpsProps().size() > 0) { 1320 FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0); 1321 final SensorLocationInternal location = props.getLocation(); 1322 udfpsAodTopLocation = location.sensorLocationY - location.sensorRadius 1323 - mUdfpsMaxYBurnInOffset; 1324 } 1325 1326 mClockPositionAlgorithm.setup( 1327 mStatusBarHeaderHeightKeyguard, 1328 expandedFraction, 1329 mKeyguardStatusViewController.getLockscreenHeight(), 1330 userSwitcherHeight, 1331 userSwitcherPreferredY, 1332 darkamount, mOverStretchAmount, 1333 bypassEnabled, getUnlockedStackScrollerPadding(), 1334 computeQsExpansionFraction(), 1335 mDisplayTopInset, 1336 mShouldUseSplitNotificationShade, 1337 udfpsAodTopLocation, 1338 mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard), 1339 mKeyguardStatusViewController.isClockTopAligned()); 1340 mClockPositionAlgorithm.run(mClockPositionResult); 1341 boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); 1342 boolean animateClock = animate || mAnimateNextPositionUpdate; 1343 mKeyguardStatusViewController.updatePosition( 1344 mClockPositionResult.clockX, mClockPositionResult.clockY, 1345 mClockPositionResult.clockScale, animateClock); 1346 if (mKeyguardQsUserSwitchController != null) { 1347 mKeyguardQsUserSwitchController.updatePosition( 1348 mClockPositionResult.clockX, 1349 mClockPositionResult.userSwitchY, 1350 animateClock); 1351 } 1352 if (mKeyguardUserSwitcherController != null) { 1353 mKeyguardUserSwitcherController.updatePosition( 1354 mClockPositionResult.clockX, 1355 mClockPositionResult.userSwitchY, 1356 animateClock); 1357 } 1358 updateNotificationTranslucency(); 1359 updateClock(); 1360 } 1361 updateKeyguardStatusViewAlignment(boolean animate)1362 private void updateKeyguardStatusViewAlignment(boolean animate) { 1363 boolean hasVisibleNotifications = mNotificationStackScrollLayoutController 1364 .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); 1365 boolean shouldBeCentered = 1366 !mShouldUseSplitNotificationShade || !hasVisibleNotifications || mDozing; 1367 if (mStatusViewCentered != shouldBeCentered) { 1368 mStatusViewCentered = shouldBeCentered; 1369 ConstraintSet constraintSet = new ConstraintSet(); 1370 constraintSet.clone(mNotificationContainerParent); 1371 int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline; 1372 constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END); 1373 if (animate) { 1374 ChangeBounds transition = new ChangeBounds(); 1375 transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 1376 transition.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); 1377 TransitionManager.beginDelayedTransition(mNotificationContainerParent, transition); 1378 } 1379 constraintSet.applyTo(mNotificationContainerParent); 1380 } 1381 } 1382 1383 /** 1384 * @return the padding of the stackscroller when unlocked 1385 */ getUnlockedStackScrollerPadding()1386 private int getUnlockedStackScrollerPadding() { 1387 return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight; 1388 } 1389 1390 /** 1391 * @return the maximum keyguard notifications that can fit on the screen 1392 */ computeMaxKeyguardNotifications()1393 private int computeMaxKeyguardNotifications() { 1394 float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); 1395 int notificationPadding = Math.max( 1396 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); 1397 float shelfSize = 1398 mNotificationShelfController.getVisibility() == View.GONE 1399 ? 0 1400 : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; 1401 1402 float lockIconPadding = 0; 1403 if (mLockIconViewController.getTop() != 0) { 1404 lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop() 1405 + mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); 1406 } 1407 1408 float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); 1409 bottomPadding = Math.max(lockIconPadding, bottomPadding); 1410 mKeyguardNotificationBottomPadding = bottomPadding; 1411 1412 float availableSpace = 1413 mNotificationStackScrollLayoutController.getHeight() 1414 - minPadding 1415 - shelfSize 1416 - bottomPadding; 1417 1418 int count = 0; 1419 ExpandableView previousView = null; 1420 for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { 1421 ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i); 1422 if (child instanceof ExpandableNotificationRow) { 1423 ExpandableNotificationRow row = (ExpandableNotificationRow) child; 1424 boolean suppressedSummary = mGroupManager != null 1425 && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn()); 1426 if (suppressedSummary) { 1427 continue; 1428 } 1429 if (!canShowViewOnLockscreen(child)) { 1430 continue; 1431 } 1432 if (row.isRemoved()) { 1433 continue; 1434 } 1435 } else if (child instanceof MediaHeaderView) { 1436 if (child.getVisibility() == GONE) { 1437 continue; 1438 } 1439 if (child.getIntrinsicHeight() == 0) { 1440 continue; 1441 } 1442 } else { 1443 continue; 1444 } 1445 availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); 1446 availableSpace -= count == 0 ? 0 : notificationPadding; 1447 availableSpace -= mNotificationStackScrollLayoutController 1448 .calculateGapHeight(previousView, child, count); 1449 previousView = child; 1450 if (availableSpace >= 0 1451 && (mMaxKeyguardNotifications == -1 || count < mMaxKeyguardNotifications)) { 1452 count++; 1453 } else if (availableSpace > -shelfSize) { 1454 // if we are exactly the last view, then we can show us still! 1455 int childCount = mNotificationStackScrollLayoutController.getChildCount(); 1456 for (int j = i + 1; j < childCount; j++) { 1457 ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j); 1458 if (view instanceof ExpandableNotificationRow 1459 && canShowViewOnLockscreen(view)) { 1460 return count; 1461 } 1462 } 1463 count++; 1464 return count; 1465 } else { 1466 return count; 1467 } 1468 } 1469 return count; 1470 } 1471 1472 /** 1473 * Can a view be shown on the lockscreen when calculating the number of allowed notifications 1474 * to show? 1475 * 1476 * @param child the view in question 1477 * @return true if it can be shown 1478 */ canShowViewOnLockscreen(ExpandableView child)1479 private boolean canShowViewOnLockscreen(ExpandableView child) { 1480 if (child.hasNoContentHeight()) { 1481 return false; 1482 } 1483 if (child instanceof ExpandableNotificationRow && 1484 !canShowRowOnLockscreen((ExpandableNotificationRow) child)) { 1485 return false; 1486 } else if (child.getVisibility() == GONE) { 1487 // ENRs can be gone and count because their visibility is only set after 1488 // this calculation, but all other views should be up to date 1489 return false; 1490 } 1491 return true; 1492 } 1493 1494 /** 1495 * Can a row be shown on the lockscreen when calculating the number of allowed notifications 1496 * to show? 1497 * 1498 * @param row the row in question 1499 * @return true if it can be shown 1500 */ canShowRowOnLockscreen(ExpandableNotificationRow row)1501 private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) { 1502 boolean suppressedSummary = 1503 mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( 1504 row.getEntry().getSbn()); 1505 if (suppressedSummary) { 1506 return false; 1507 } 1508 if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { 1509 return false; 1510 } 1511 if (row.isRemoved()) { 1512 return false; 1513 } 1514 return true; 1515 } 1516 updateClock()1517 private void updateClock() { 1518 float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha; 1519 mKeyguardStatusViewController.setAlpha(alpha); 1520 if (mKeyguardQsUserSwitchController != null) { 1521 mKeyguardQsUserSwitchController.setAlpha(alpha); 1522 } 1523 if (mKeyguardUserSwitcherController != null) { 1524 mKeyguardUserSwitcherController.setAlpha(alpha); 1525 } 1526 } 1527 animateToFullShade(long delay)1528 public void animateToFullShade(long delay) { 1529 mNotificationStackScrollLayoutController.goToFullShade(delay); 1530 mView.requestLayout(); 1531 mAnimateNextPositionUpdate = true; 1532 } 1533 setQsExpansionEnabled()1534 private void setQsExpansionEnabled() { 1535 if (mQs == null) return; 1536 mQs.setHeaderClickable(isQsExpansionEnabled()); 1537 } 1538 setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy)1539 public void setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy) { 1540 mQsExpansionEnabledPolicy = qsExpansionEnabledPolicy; 1541 setQsExpansionEnabled(); 1542 } 1543 1544 @Override resetViews(boolean animate)1545 public void resetViews(boolean animate) { 1546 mIsLaunchTransitionFinished = false; 1547 mBlockTouches = false; 1548 if (!mLaunchingAffordance) { 1549 mAffordanceHelper.reset(false); 1550 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 1551 } 1552 mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */, 1553 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */); 1554 if (animate && !isFullyCollapsed()) { 1555 animateCloseQs(true /* animateAway */); 1556 } else { 1557 closeQs(); 1558 } 1559 mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate, 1560 !animate /* cancelAnimators */); 1561 mNotificationStackScrollLayoutController.resetScrollPosition(); 1562 } 1563 1564 /** Collapses the panel. */ collapsePanel(boolean animate, boolean delayed, float speedUpFactor)1565 public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) { 1566 boolean waiting = false; 1567 if (animate && !isFullyCollapsed()) { 1568 collapse(delayed, speedUpFactor); 1569 waiting = true; 1570 } else { 1571 resetViews(false /* animate */); 1572 setExpandedFraction(0); // just in case 1573 } 1574 if (!waiting) { 1575 // it's possible that nothing animated, so we replicate the termination 1576 // conditions of panelExpansionChanged here 1577 // TODO(b/200063118): This can likely go away in a future refactor CL. 1578 getPanelExpansionStateManager().updateState(STATE_CLOSED); 1579 } 1580 } 1581 1582 @Override collapse(boolean delayed, float speedUpFactor)1583 public void collapse(boolean delayed, float speedUpFactor) { 1584 if (!canPanelBeCollapsed()) { 1585 return; 1586 } 1587 1588 if (mQsExpanded) { 1589 mQsExpandImmediate = true; 1590 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 1591 } 1592 super.collapse(delayed, speedUpFactor); 1593 } 1594 closeQs()1595 public void closeQs() { 1596 cancelQsAnimation(); 1597 setQsExpansion(mQsMinExpansionHeight); 1598 } 1599 cancelAnimation()1600 public void cancelAnimation() { 1601 mView.animate().cancel(); 1602 } 1603 1604 1605 /** 1606 * Animate QS closing by flinging it. 1607 * If QS is expanded, it will collapse into QQS and stop. 1608 * 1609 * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. 1610 */ animateCloseQs(boolean animateAway)1611 public void animateCloseQs(boolean animateAway) { 1612 if (mQsExpansionAnimator != null) { 1613 if (!mQsAnimatorExpand) { 1614 return; 1615 } 1616 float height = mQsExpansionHeight; 1617 mQsExpansionAnimator.cancel(); 1618 setQsExpansion(height); 1619 } 1620 flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE); 1621 } 1622 isQsExpansionEnabled()1623 private boolean isQsExpansionEnabled() { 1624 return mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient 1625 && !mRemoteInputManager.isRemoteInputActive(); 1626 } 1627 expandWithQs()1628 public void expandWithQs() { 1629 if (isQsExpansionEnabled()) { 1630 mQsExpandImmediate = true; 1631 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 1632 } 1633 if (isFullyCollapsed()) { 1634 expand(true /* animate */); 1635 } else { 1636 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1637 flingSettings(0 /* velocity */, FLING_EXPAND); 1638 } 1639 } 1640 expandWithQsDetail(DetailAdapter qsDetailAdapter)1641 public void expandWithQsDetail(DetailAdapter qsDetailAdapter) { 1642 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1643 flingSettings(0 /* velocity */, FLING_EXPAND); 1644 // When expanding with a panel, there's no meaningful touch point to correspond to. Set the 1645 // origin to somewhere above the screen. This is used for animations. 1646 int x = mQsFrame.getWidth() / 2; 1647 mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight()); 1648 if (mAccessibilityManager.isEnabled()) { 1649 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 1650 } 1651 } 1652 expandWithoutQs()1653 public void expandWithoutQs() { 1654 if (isQsExpanded()) { 1655 flingSettings(0 /* velocity */, FLING_COLLAPSE); 1656 } else { 1657 expand(true /* animate */); 1658 } 1659 } 1660 1661 @Override fling(float vel, boolean expand)1662 public void fling(float vel, boolean expand) { 1663 GestureRecorder gr = mStatusBar.getGestureRecorder(); 1664 if (gr != null) { 1665 gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel); 1666 } 1667 super.fling(vel, expand); 1668 } 1669 1670 @Override flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1671 protected void flingToHeight(float vel, boolean expand, float target, 1672 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { 1673 mHeadsUpTouchHelper.notifyFling(!expand); 1674 mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */); 1675 setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f); 1676 super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing); 1677 } 1678 onQsIntercept(MotionEvent event)1679 private boolean onQsIntercept(MotionEvent event) { 1680 int pointerIndex = event.findPointerIndex(mTrackingPointer); 1681 if (pointerIndex < 0) { 1682 pointerIndex = 0; 1683 mTrackingPointer = event.getPointerId(pointerIndex); 1684 } 1685 final float x = event.getX(pointerIndex); 1686 final float y = event.getY(pointerIndex); 1687 1688 switch (event.getActionMasked()) { 1689 case MotionEvent.ACTION_DOWN: 1690 mInitialTouchY = y; 1691 mInitialTouchX = x; 1692 initVelocityTracker(); 1693 trackMovement(event); 1694 if (mKeyguardShowing 1695 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) { 1696 // Dragging down on the lockscreen statusbar should prohibit other interactions 1697 // immediately, otherwise we'll wait on the touchslop. This is to allow 1698 // dragging down to expanded quick settings directly on the lockscreen. 1699 mView.getParent().requestDisallowInterceptTouchEvent(true); 1700 } 1701 if (mQsExpansionAnimator != null) { 1702 mInitialHeightOnTouch = mQsExpansionHeight; 1703 mQsTracking = true; 1704 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1705 mNotificationStackScrollLayoutController.cancelLongPress(); 1706 } 1707 break; 1708 case MotionEvent.ACTION_POINTER_UP: 1709 final int upPointer = event.getPointerId(event.getActionIndex()); 1710 if (mTrackingPointer == upPointer) { 1711 // gesture is ongoing, find a new pointer to track 1712 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 1713 mTrackingPointer = event.getPointerId(newIndex); 1714 mInitialTouchX = event.getX(newIndex); 1715 mInitialTouchY = event.getY(newIndex); 1716 } 1717 break; 1718 1719 case MotionEvent.ACTION_MOVE: 1720 final float h = y - mInitialTouchY; 1721 trackMovement(event); 1722 if (mQsTracking) { 1723 1724 // Already tracking because onOverscrolled was called. We need to update here 1725 // so we don't stop for a frame until the next touch event gets handled in 1726 // onTouchEvent. 1727 setQsExpansion(h + mInitialHeightOnTouch); 1728 trackMovement(event); 1729 return true; 1730 } 1731 if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded)) 1732 && Math.abs(h) > Math.abs(x - mInitialTouchX) 1733 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { 1734 mView.getParent().requestDisallowInterceptTouchEvent(true); 1735 mQsTracking = true; 1736 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1737 onQsExpansionStarted(); 1738 notifyExpandingFinished(); 1739 mInitialHeightOnTouch = mQsExpansionHeight; 1740 mInitialTouchY = y; 1741 mInitialTouchX = x; 1742 mNotificationStackScrollLayoutController.cancelLongPress(); 1743 return true; 1744 } 1745 break; 1746 1747 case MotionEvent.ACTION_CANCEL: 1748 case MotionEvent.ACTION_UP: 1749 trackMovement(event); 1750 mQsTracking = false; 1751 break; 1752 } 1753 return false; 1754 } 1755 1756 @Override isInContentBounds(float x, float y)1757 protected boolean isInContentBounds(float x, float y) { 1758 float stackScrollerX = mNotificationStackScrollLayoutController.getX(); 1759 return !mNotificationStackScrollLayoutController 1760 .isBelowLastNotification(x - stackScrollerX, y) 1761 && stackScrollerX < x 1762 && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth(); 1763 } 1764 traceQsJank(boolean startTracing, boolean wasCancelled)1765 private void traceQsJank(boolean startTracing, boolean wasCancelled) { 1766 InteractionJankMonitor monitor = InteractionJankMonitor.getInstance(); 1767 if (startTracing) { 1768 monitor.begin(mView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); 1769 } else { 1770 if (wasCancelled) { 1771 monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); 1772 } else { 1773 monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE); 1774 } 1775 } 1776 } 1777 initDownStates(MotionEvent event)1778 private void initDownStates(MotionEvent event) { 1779 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1780 mOnlyAffordanceInThisMotion = false; 1781 mQsTouchAboveFalsingThreshold = mQsFullyExpanded; 1782 mDozingOnDown = isDozing(); 1783 mDownX = event.getX(); 1784 mDownY = event.getY(); 1785 mCollapsedOnDown = isFullyCollapsed(); 1786 mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY); 1787 mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); 1788 mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; 1789 mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; 1790 if (mExpectingSynthesizedDown) { 1791 mLastEventSynthesizedDown = true; 1792 } else { 1793 // down but not synthesized motion event. 1794 mLastEventSynthesizedDown = false; 1795 } 1796 } else { 1797 // not down event at all. 1798 mLastEventSynthesizedDown = false; 1799 } 1800 } 1801 1802 /** 1803 * Can the panel collapse in this motion because it was started on QQS? 1804 * 1805 * @param downX the x location where the touch started 1806 * @param downY the y location where the touch started 1807 * 1808 * @return true if the panel could be collapsed because it stared on QQS 1809 */ canPanelCollapseOnQQS(float downX, float downY)1810 private boolean canPanelCollapseOnQQS(float downX, float downY) { 1811 if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) { 1812 return false; 1813 } 1814 View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader(); 1815 return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth() 1816 && downY <= header.getBottom(); 1817 1818 } 1819 flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent)1820 private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) { 1821 float vel = getCurrentQSVelocity(); 1822 boolean expandsQs = flingExpandsQs(vel); 1823 if (expandsQs) { 1824 if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) { 1825 expandsQs = false; 1826 } else { 1827 logQsSwipeDown(y); 1828 } 1829 } else if (vel < 0) { 1830 mFalsingManager.isFalseTouch(QS_COLLAPSE); 1831 } 1832 1833 flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE); 1834 } 1835 logQsSwipeDown(float y)1836 private void logQsSwipeDown(float y) { 1837 float vel = getCurrentQSVelocity(); 1838 final int 1839 gesture = 1840 mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS 1841 : MetricsEvent.ACTION_SHADE_QS_PULL; 1842 mLockscreenGestureLogger.write(gesture, 1843 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()), 1844 (int) (vel / mStatusBar.getDisplayDensity())); 1845 } 1846 flingExpandsQs(float vel)1847 private boolean flingExpandsQs(float vel) { 1848 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 1849 return computeQsExpansionFraction() > 0.5f; 1850 } else { 1851 return vel > 0; 1852 } 1853 } 1854 isFalseTouch(@lassifier.InteractionType int interactionType)1855 private boolean isFalseTouch(@Classifier.InteractionType int interactionType) { 1856 if (mFalsingManager.isClassifierEnabled()) { 1857 return mFalsingManager.isFalseTouch(interactionType); 1858 } 1859 return !mQsTouchAboveFalsingThreshold; 1860 } 1861 computeQsExpansionFraction()1862 private float computeQsExpansionFraction() { 1863 if (mQSAnimatingHiddenFromCollapsed) { 1864 // When hiding QS from collapsed state, the expansion can sometimes temporarily 1865 // be larger than 0 because of the timing, leading to flickers. 1866 return 0.0f; 1867 } 1868 return Math.min( 1869 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight 1870 - mQsMinExpansionHeight)); 1871 } 1872 1873 @Override shouldExpandWhenNotFlinging()1874 protected boolean shouldExpandWhenNotFlinging() { 1875 if (super.shouldExpandWhenNotFlinging()) { 1876 return true; 1877 } 1878 if (mAllowExpandForSmallExpansion) { 1879 // When we get a touch that came over from launcher, the velocity isn't always correct 1880 // Let's err on expanding if the gesture has been reasonably slow 1881 long timeSinceDown = SystemClock.uptimeMillis() - mDownTime; 1882 return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER; 1883 } 1884 return false; 1885 } 1886 1887 @Override getOpeningHeight()1888 protected float getOpeningHeight() { 1889 return mNotificationStackScrollLayoutController.getOpeningHeight(); 1890 } 1891 1892 handleQsTouch(MotionEvent event)1893 private boolean handleQsTouch(MotionEvent event) { 1894 if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(event.getX())) { 1895 return false; 1896 } 1897 final int action = event.getActionMasked(); 1898 if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f 1899 && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) { 1900 // Down in the empty area while fully expanded - go to QS. 1901 mQsTracking = true; 1902 traceQsJank(true /* startTracing */, false /* wasCancelled */); 1903 mConflictingQsExpansionGesture = true; 1904 onQsExpansionStarted(); 1905 mInitialHeightOnTouch = mQsExpansionHeight; 1906 mInitialTouchY = event.getX(); 1907 mInitialTouchX = event.getY(); 1908 } 1909 if (!isFullyCollapsed()) { 1910 handleQsDown(event); 1911 } 1912 if (!mQsExpandImmediate && mQsTracking) { 1913 onQsTouch(event); 1914 if (!mConflictingQsExpansionGesture) { 1915 return true; 1916 } 1917 } 1918 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 1919 mConflictingQsExpansionGesture = false; 1920 } 1921 if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && isQsExpansionEnabled()) { 1922 mTwoFingerQsExpandPossible = true; 1923 } 1924 if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex()) 1925 < mStatusBarMinHeight) { 1926 mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1); 1927 mQsExpandImmediate = true; 1928 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 1929 requestPanelHeightUpdate(); 1930 1931 // Normally, we start listening when the panel is expanded, but here we need to start 1932 // earlier so the state is already up to date when dragging down. 1933 setListening(true); 1934 } 1935 return false; 1936 } 1937 touchXOutsideOfQs(float touchX)1938 private boolean touchXOutsideOfQs(float touchX) { 1939 return touchX < mQsFrame.getX() || touchX > mQsFrame.getX() + mQsFrame.getWidth(); 1940 } 1941 isInQsArea(float x, float y)1942 private boolean isInQsArea(float x, float y) { 1943 if (touchXOutsideOfQs(x)) { 1944 return false; 1945 } 1946 // Let's reject anything at the very bottom around the home handle in gesture nav 1947 if (mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight) { 1948 return false; 1949 } 1950 return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom() 1951 || y <= mQs.getView().getY() + mQs.getView().getHeight(); 1952 } 1953 isOpenQsEvent(MotionEvent event)1954 private boolean isOpenQsEvent(MotionEvent event) { 1955 final int pointerCount = event.getPointerCount(); 1956 final int action = event.getActionMasked(); 1957 1958 final boolean 1959 twoFingerDrag = 1960 action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2; 1961 1962 final boolean 1963 stylusButtonClickDrag = 1964 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( 1965 MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed( 1966 MotionEvent.BUTTON_STYLUS_SECONDARY)); 1967 1968 final boolean 1969 mouseButtonClickDrag = 1970 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed( 1971 MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed( 1972 MotionEvent.BUTTON_TERTIARY)); 1973 1974 return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag; 1975 } 1976 handleQsDown(MotionEvent event)1977 private void handleQsDown(MotionEvent event) { 1978 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( 1979 event.getX(), event.getY(), -1)) { 1980 mFalsingCollector.onQsDown(); 1981 mQsTracking = true; 1982 onQsExpansionStarted(); 1983 mInitialHeightOnTouch = mQsExpansionHeight; 1984 mInitialTouchY = event.getX(); 1985 mInitialTouchX = event.getY(); 1986 1987 // If we interrupt an expansion gesture here, make sure to update the state correctly. 1988 notifyExpandingFinished(); 1989 } 1990 } 1991 1992 /** 1993 * Input focus transfer is about to happen. 1994 */ startWaitingForOpenPanelGesture()1995 public void startWaitingForOpenPanelGesture() { 1996 if (!isFullyCollapsed()) { 1997 return; 1998 } 1999 mExpectingSynthesizedDown = true; 2000 onTrackingStarted(); 2001 updatePanelExpanded(); 2002 } 2003 2004 /** 2005 * Called when this view is no longer waiting for input focus transfer. 2006 * 2007 * There are two scenarios behind this function call. First, input focus transfer 2008 * has successfully happened and this view already received synthetic DOWN event. 2009 * (mExpectingSynthesizedDown == false). Do nothing. 2010 * 2011 * Second, before input focus transfer finished, user may have lifted finger 2012 * in previous window and this window never received synthetic DOWN event. 2013 * (mExpectingSynthesizedDown == true). 2014 * In this case, we use the velocity to trigger fling event. 2015 * 2016 * @param velocity unit is in px / millis 2017 */ stopWaitingForOpenPanelGesture(boolean cancel, final float velocity)2018 public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) { 2019 if (mExpectingSynthesizedDown) { 2020 mExpectingSynthesizedDown = false; 2021 if (cancel) { 2022 collapse(false /* delayed */, 1.0f /* speedUpFactor */); 2023 } else { 2024 maybeVibrateOnOpening(); 2025 fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */); 2026 } 2027 onTrackingStopped(false); 2028 } 2029 } 2030 2031 @Override flingExpands(float vel, float vectorVel, float x, float y)2032 protected boolean flingExpands(float vel, float vectorVel, float x, float y) { 2033 boolean expands = super.flingExpands(vel, vectorVel, x, y); 2034 2035 // If we are already running a QS expansion, make sure that we keep the panel open. 2036 if (mQsExpansionAnimator != null) { 2037 expands = true; 2038 } 2039 return expands; 2040 } 2041 2042 @Override shouldGestureWaitForTouchSlop()2043 protected boolean shouldGestureWaitForTouchSlop() { 2044 if (mExpectingSynthesizedDown) { 2045 mExpectingSynthesizedDown = false; 2046 return false; 2047 } 2048 return isFullyCollapsed() || mBarState != StatusBarState.SHADE; 2049 } 2050 2051 @Override shouldGestureIgnoreXTouchSlop(float x, float y)2052 protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) { 2053 return !mAffordanceHelper.isOnAffordanceIcon(x, y); 2054 } 2055 onQsTouch(MotionEvent event)2056 private void onQsTouch(MotionEvent event) { 2057 int pointerIndex = event.findPointerIndex(mTrackingPointer); 2058 if (pointerIndex < 0) { 2059 pointerIndex = 0; 2060 mTrackingPointer = event.getPointerId(pointerIndex); 2061 } 2062 final float y = event.getY(pointerIndex); 2063 final float x = event.getX(pointerIndex); 2064 final float h = y - mInitialTouchY; 2065 2066 switch (event.getActionMasked()) { 2067 case MotionEvent.ACTION_DOWN: 2068 mQsTracking = true; 2069 traceQsJank(true /* startTracing */, false /* wasCancelled */); 2070 mInitialTouchY = y; 2071 mInitialTouchX = x; 2072 onQsExpansionStarted(); 2073 mInitialHeightOnTouch = mQsExpansionHeight; 2074 initVelocityTracker(); 2075 trackMovement(event); 2076 break; 2077 2078 case MotionEvent.ACTION_POINTER_UP: 2079 final int upPointer = event.getPointerId(event.getActionIndex()); 2080 if (mTrackingPointer == upPointer) { 2081 // gesture is ongoing, find a new pointer to track 2082 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 2083 final float newY = event.getY(newIndex); 2084 final float newX = event.getX(newIndex); 2085 mTrackingPointer = event.getPointerId(newIndex); 2086 mInitialHeightOnTouch = mQsExpansionHeight; 2087 mInitialTouchY = newY; 2088 mInitialTouchX = newX; 2089 } 2090 break; 2091 2092 case MotionEvent.ACTION_MOVE: 2093 setQsExpansion(h + mInitialHeightOnTouch); 2094 if (h >= getFalsingThreshold()) { 2095 mQsTouchAboveFalsingThreshold = true; 2096 } 2097 trackMovement(event); 2098 break; 2099 2100 case MotionEvent.ACTION_UP: 2101 case MotionEvent.ACTION_CANCEL: 2102 mQsTracking = false; 2103 mTrackingPointer = -1; 2104 trackMovement(event); 2105 float fraction = computeQsExpansionFraction(); 2106 if (fraction != 0f || y >= mInitialTouchY) { 2107 flingQsWithCurrentVelocity(y, 2108 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 2109 } else { 2110 traceQsJank(false /* startTracing */, 2111 event.getActionMasked() == MotionEvent.ACTION_CANCEL); 2112 } 2113 if (mQsVelocityTracker != null) { 2114 mQsVelocityTracker.recycle(); 2115 mQsVelocityTracker = null; 2116 } 2117 break; 2118 } 2119 } 2120 getFalsingThreshold()2121 private int getFalsingThreshold() { 2122 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 2123 return (int) (mQsFalsingThreshold * factor); 2124 } 2125 setOverScrolling(boolean overscrolling)2126 private void setOverScrolling(boolean overscrolling) { 2127 mStackScrollerOverscrolling = overscrolling; 2128 if (mQs == null) return; 2129 mQs.setOverscrolling(overscrolling); 2130 } 2131 onQsExpansionStarted()2132 private void onQsExpansionStarted() { 2133 onQsExpansionStarted(0); 2134 } 2135 onQsExpansionStarted(int overscrollAmount)2136 protected void onQsExpansionStarted(int overscrollAmount) { 2137 cancelQsAnimation(); 2138 cancelHeightAnimator(); 2139 2140 // Reset scroll position and apply that position to the expanded height. 2141 float height = mQsExpansionHeight - overscrollAmount; 2142 setQsExpansion(height); 2143 requestPanelHeightUpdate(); 2144 mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); 2145 2146 // When expanding QS, let's authenticate the user if possible, 2147 // this will speed up notification actions. 2148 if (height == 0) { 2149 mStatusBar.requestFaceAuth(false); 2150 } 2151 } 2152 setQsExpanded(boolean expanded)2153 @VisibleForTesting void setQsExpanded(boolean expanded) { 2154 boolean changed = mQsExpanded != expanded; 2155 if (changed) { 2156 mQsExpanded = expanded; 2157 updateQsState(); 2158 requestPanelHeightUpdate(); 2159 mFalsingCollector.setQsExpanded(expanded); 2160 mStatusBar.setQsExpanded(expanded); 2161 mNotificationsQSContainerController.setQsExpanded(expanded); 2162 mPulseExpansionHandler.setQsExpanded(expanded); 2163 mKeyguardBypassController.setQSExpanded(expanded); 2164 mStatusBarKeyguardViewManager.setQsExpanded(expanded); 2165 mLockIconViewController.setQsExpanded(expanded); 2166 mPrivacyDotViewController.setQsExpanded(expanded); 2167 } 2168 } 2169 maybeAnimateBottomAreaAlpha()2170 private void maybeAnimateBottomAreaAlpha() { 2171 mBottomAreaShadeAlphaAnimator.cancel(); 2172 if (mBarState == StatusBarState.SHADE_LOCKED) { 2173 mBottomAreaShadeAlphaAnimator.setFloatValues(mBottomAreaShadeAlpha, 0.0f); 2174 mBottomAreaShadeAlphaAnimator.start(); 2175 } else { 2176 mBottomAreaShadeAlpha = 1f; 2177 } 2178 } 2179 2180 private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { 2181 @Override 2182 public void run() { 2183 mKeyguardBottomArea.setVisibility(View.GONE); 2184 } 2185 }; 2186 setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)2187 private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { 2188 mKeyguardBottomArea.animate().cancel(); 2189 if (goingToFullShade) { 2190 mKeyguardBottomArea.animate().alpha(0f).setStartDelay( 2191 mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( 2192 mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator( 2193 Interpolators.ALPHA_OUT).withEndAction( 2194 mAnimateKeyguardBottomAreaInvisibleEndRunnable).start(); 2195 } else if (statusBarState == KEYGUARD 2196 || statusBarState == StatusBarState.SHADE_LOCKED) { 2197 mKeyguardBottomArea.setVisibility(View.VISIBLE); 2198 mKeyguardBottomArea.setAlpha(1f); 2199 } else { 2200 mKeyguardBottomArea.setVisibility(View.GONE); 2201 } 2202 } 2203 updateQsState()2204 private void updateQsState() { 2205 mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded); 2206 mNotificationStackScrollLayoutController.setScrollingEnabled( 2207 mBarState != KEYGUARD 2208 && (!mQsExpanded 2209 || mQsExpansionFromOverscroll 2210 || mShouldUseSplitNotificationShade)); 2211 2212 if (mKeyguardUserSwitcherController != null && mQsExpanded 2213 && !mStackScrollerOverscrolling) { 2214 mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true); 2215 } 2216 if (mQs == null) return; 2217 mQs.setExpanded(mQsExpanded); 2218 } 2219 setQsExpansion(float height)2220 void setQsExpansion(float height) { 2221 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); 2222 mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0; 2223 if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling 2224 && !mDozing) { 2225 setQsExpanded(true); 2226 } else if (height <= mQsMinExpansionHeight && mQsExpanded) { 2227 setQsExpanded(false); 2228 } 2229 mQsExpansionHeight = height; 2230 updateQsExpansion(); 2231 requestScrollerTopPaddingUpdate(false /* animate */); 2232 mKeyguardStatusBarViewController.updateViewState(); 2233 if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) { 2234 updateKeyguardBottomAreaAlpha(); 2235 positionClockAndNotifications(); 2236 } 2237 2238 if (mAccessibilityManager.isEnabled()) { 2239 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 2240 } 2241 2242 if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded 2243 && mFalsingCollector.shouldEnforceBouncer()) { 2244 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, 2245 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); 2246 } 2247 if (DEBUG) { 2248 mView.invalidate(); 2249 } 2250 } 2251 updateQsExpansion()2252 private void updateQsExpansion() { 2253 if (mQs == null) return; 2254 final float squishiness = 2255 mQsExpandImmediate || mQsExpanded ? 1f : mNotificationStackScrollLayoutController 2256 .getNotificationSquishinessFraction(); 2257 final float qsExpansionFraction = computeQsExpansionFraction(); 2258 final float adjustedExpansionFraction = mShouldUseSplitNotificationShade 2259 ? 1f : computeQsExpansionFraction(); 2260 mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(), 2261 squishiness); 2262 mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction); 2263 mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); 2264 int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction); 2265 mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY); 2266 setQSClippingBounds(); 2267 2268 // Only need to notify the notification stack when we're not in split screen mode. If we 2269 // do, then the notification panel starts scrolling along with the QS. 2270 if (!mShouldUseSplitNotificationShade) { 2271 mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction); 2272 } 2273 2274 mDepthController.setQsPanelExpansion(qsExpansionFraction); 2275 } 2276 onStackYChanged(boolean shouldAnimate)2277 private void onStackYChanged(boolean shouldAnimate) { 2278 if (mQs != null) { 2279 if (shouldAnimate) { 2280 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD, 2281 0 /* delay */); 2282 mNotificationBoundsAnimationDelay = 0; 2283 } 2284 setQSClippingBounds(); 2285 } 2286 }; 2287 onNotificationScrolled(int newScrollPosition)2288 private void onNotificationScrolled(int newScrollPosition) { 2289 updateQSExpansionEnabledAmbient(); 2290 } 2291 updateQSExpansionEnabledAmbient()2292 private void updateQSExpansionEnabledAmbient() { 2293 final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight; 2294 mQsExpansionEnabledAmbient = mShouldUseSplitNotificationShade 2295 || (mAmbientState.getScrollY() <= scrollRangeToTop); 2296 setQsExpansionEnabled(); 2297 } 2298 2299 /** 2300 * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade 2301 * and QS state. 2302 */ setQSClippingBounds()2303 private void setQSClippingBounds() { 2304 int top; 2305 int bottom; 2306 int left; 2307 int right; 2308 2309 final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction()); 2310 final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0); 2311 2312 if (!mShouldUseSplitNotificationShade) { 2313 if (mTransitioningToFullShadeProgress > 0.0f) { 2314 // If we're transitioning, let's use the actual value. The else case 2315 // can be wrong during transitions when waiting for the keyguard to unlock 2316 top = mTransitionToFullShadeQSPosition; 2317 } else { 2318 final float notificationTop = getQSEdgePosition(); 2319 if (isOnKeyguard()) { 2320 if (mKeyguardBypassController.getBypassEnabled()) { 2321 // When bypassing on the keyguard, let's use the panel bottom. 2322 // this should go away once we unify the stackY position and don't have 2323 // to do this min anymore below. 2324 top = qsPanelBottomY; 2325 } else { 2326 top = (int) Math.min(qsPanelBottomY, notificationTop); 2327 } 2328 } else { 2329 top = (int) notificationTop; 2330 } 2331 } 2332 top += mOverStretchAmount; 2333 // Correction for instant expansion caused by HUN pull down/ 2334 if (mMinFraction > 0f && mMinFraction < 1f) { 2335 float realFraction = 2336 (getExpandedFraction() - mMinFraction) / (1f - mMinFraction); 2337 top *= MathUtils.saturate(realFraction / mMinFraction); 2338 } 2339 bottom = getView().getBottom(); 2340 // notification bounds should take full screen width regardless of insets 2341 left = 0; 2342 right = getView().getRight() + mDisplayRightInset; 2343 } else { 2344 top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight); 2345 bottom = top + mNotificationStackScrollLayoutController.getHeight(); 2346 left = mNotificationStackScrollLayoutController.getLeft(); 2347 right = mNotificationStackScrollLayoutController.getRight(); 2348 } 2349 // top should never be lower than bottom, otherwise it will be invisible. 2350 top = Math.min(top, bottom); 2351 applyQSClippingBounds(left, top, right, bottom, qsVisible); 2352 } 2353 applyQSClippingBounds(int left, int top, int right, int bottom, boolean qsVisible)2354 private void applyQSClippingBounds(int left, int top, int right, int bottom, 2355 boolean qsVisible) { 2356 if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) { 2357 if (mQsClippingAnimation != null) { 2358 // update the end position of the animator 2359 mQsClippingAnimationEndBounds.set(left, top, right, bottom); 2360 } else { 2361 applyQSClippingImmediately(left, top, right, bottom, qsVisible); 2362 } 2363 } else { 2364 mQsClippingAnimationEndBounds.set(left, top, right, bottom); 2365 final int startLeft = mKeyguardStatusAreaClipBounds.left; 2366 final int startTop = mKeyguardStatusAreaClipBounds.top; 2367 final int startRight = mKeyguardStatusAreaClipBounds.right; 2368 final int startBottom = mKeyguardStatusAreaClipBounds.bottom; 2369 if (mQsClippingAnimation != null) { 2370 mQsClippingAnimation.cancel(); 2371 } 2372 mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f); 2373 mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 2374 mQsClippingAnimation.setDuration(mNotificationBoundsAnimationDuration); 2375 mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay); 2376 mQsClippingAnimation.addUpdateListener(animation -> { 2377 float fraction = animation.getAnimatedFraction(); 2378 int animLeft = (int) MathUtils.lerp(startLeft, 2379 mQsClippingAnimationEndBounds.left, fraction); 2380 int animTop = (int) MathUtils.lerp(startTop, 2381 mQsClippingAnimationEndBounds.top, fraction); 2382 int animRight = (int) MathUtils.lerp(startRight, 2383 mQsClippingAnimationEndBounds.right, fraction); 2384 int animBottom = (int) MathUtils.lerp(startBottom, 2385 mQsClippingAnimationEndBounds.bottom, fraction); 2386 applyQSClippingImmediately(animLeft, animTop, animRight, animBottom, 2387 qsVisible /* qsVisible */); 2388 }); 2389 mQsClippingAnimation.addListener(new AnimatorListenerAdapter() { 2390 @Override 2391 public void onAnimationEnd(Animator animation) { 2392 mQsClippingAnimation = null; 2393 mIsQsTranslationResetAnimator = false; 2394 mIsPulseExpansionResetAnimator = false; 2395 } 2396 }); 2397 mQsClippingAnimation.start(); 2398 } 2399 mAnimateNextNotificationBounds = false; 2400 mNotificationBoundsAnimationDelay = 0; 2401 } 2402 applyQSClippingImmediately(int left, int top, int right, int bottom, boolean qsVisible)2403 private void applyQSClippingImmediately(int left, int top, int right, int bottom, 2404 boolean qsVisible) { 2405 // Fancy clipping for quick settings 2406 int radius = mScrimCornerRadius; 2407 boolean clipStatusView = false; 2408 if (!mShouldUseSplitNotificationShade) { 2409 // The padding on this area is large enough that we can use a cheaper clipping strategy 2410 mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); 2411 clipStatusView = qsVisible; 2412 float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; 2413 radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, 2414 Math.min(top / (float) mScrimCornerRadius, 1f)); 2415 } 2416 if (mQs != null) { 2417 float qsTranslation = 0; 2418 boolean pulseExpanding = mPulseExpansionHandler.isExpanding(); 2419 if (mTransitioningToFullShadeProgress > 0.0f || pulseExpanding 2420 || (mQsClippingAnimation != null 2421 && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) { 2422 if (pulseExpanding || mIsPulseExpansionResetAnimator) { 2423 // qsTranslation should only be positive during pulse expansion because it's 2424 // already translating in from the top 2425 qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f); 2426 } else if (!mShouldUseSplitNotificationShade) { 2427 qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT; 2428 } 2429 } 2430 mQsTranslationForFullShadeTransition = qsTranslation; 2431 updateQsFrameTranslation(); 2432 float currentTranslation = mQsFrame.getTranslationY(); 2433 mQsClipTop = (int) (top - currentTranslation); 2434 mQsClipBottom = (int) (bottom - currentTranslation); 2435 mQsVisible = qsVisible; 2436 mQs.setFancyClipping( 2437 mQsClipTop, 2438 mQsClipBottom, 2439 radius, qsVisible 2440 && !mShouldUseSplitNotificationShade); 2441 } 2442 mKeyguardStatusViewController.setClipBounds( 2443 clipStatusView ? mKeyguardStatusAreaClipBounds : null); 2444 if (!qsVisible && mShouldUseSplitNotificationShade) { 2445 // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to 2446 // be visible, otherwise you can see the bounds once swiping up to see bouncer 2447 mScrimController.setNotificationsBounds(0, 0, 0, 0); 2448 } else { 2449 mScrimController.setNotificationsBounds(left, top, right, bottom); 2450 } 2451 2452 if (mShouldUseSplitNotificationShade) { 2453 mKeyguardStatusBarViewController.setNoTopClipping(); 2454 } else { 2455 mKeyguardStatusBarViewController.updateTopClipping(top); 2456 } 2457 2458 mScrimController.setScrimCornerRadius(radius); 2459 int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft(); 2460 int nsslRight = right - mNotificationStackScrollLayoutController.getLeft(); 2461 int nsslTop = top - mNotificationStackScrollLayoutController.getTop(); 2462 int nsslBottom = bottom; 2463 int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0; 2464 mNotificationStackScrollLayoutController.setRoundedClippingBounds( 2465 nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius); 2466 } 2467 getQSEdgePosition()2468 private float getQSEdgePosition() { 2469 // TODO: replace StackY with unified calculation 2470 return Math.max(mQuickQsOffsetHeight * mAmbientState.getExpansionFraction(), 2471 mAmbientState.getStackY() - mAmbientState.getScrollY()); 2472 } 2473 calculateQsBottomPosition(float qsExpansionFraction)2474 private int calculateQsBottomPosition(float qsExpansionFraction) { 2475 if (mTransitioningToFullShadeProgress > 0.0f) { 2476 return mTransitionToFullShadeQSPosition; 2477 } else { 2478 int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight(); 2479 if (qsExpansionFraction != 0.0) { 2480 qsBottomY = (int) MathUtils.lerp( 2481 qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction); 2482 } 2483 return qsBottomY; 2484 } 2485 } 2486 determineAccessibilityPaneTitle()2487 private String determineAccessibilityPaneTitle() { 2488 if (mQs != null && mQs.isCustomizing()) { 2489 return mResources.getString(R.string.accessibility_desc_quick_settings_edit); 2490 } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) { 2491 // Upon initialisation when we are not layouted yet we don't want to announce that we 2492 // are fully expanded, hence the != 0.0f check. 2493 return mResources.getString(R.string.accessibility_desc_quick_settings); 2494 } else if (mBarState == KEYGUARD) { 2495 return mResources.getString(R.string.accessibility_desc_lock_screen); 2496 } else { 2497 return mResources.getString(R.string.accessibility_desc_notification_shade); 2498 } 2499 } 2500 calculateNotificationsTopPadding()2501 private float calculateNotificationsTopPadding() { 2502 if (mShouldUseSplitNotificationShade && !mKeyguardShowing) { 2503 return 0; 2504 } 2505 if (mKeyguardShowing && (mQsExpandImmediate 2506 || mIsExpanding && mQsExpandedWhenExpandingStarted)) { 2507 2508 // Either QS pushes the notifications down when fully expanded, or QS is fully above the 2509 // notifications (mostly on tablets). maxNotificationPadding denotes the normal top 2510 // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings 2511 // panel. We need to take the maximum and linearly interpolate with the panel expansion 2512 // for a nice motion. 2513 int maxNotificationPadding = getKeyguardNotificationStaticPadding(); 2514 int maxQsPadding = mQsMaxExpansionHeight; 2515 int max = mBarState == KEYGUARD ? Math.max( 2516 maxNotificationPadding, maxQsPadding) : maxQsPadding; 2517 return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max, 2518 getExpandedFraction()); 2519 } else if (mQsSizeChangeAnimator != null) { 2520 return Math.max( 2521 (int) mQsSizeChangeAnimator.getAnimatedValue(), 2522 getKeyguardNotificationStaticPadding()); 2523 } else if (mKeyguardShowing) { 2524 // We can only do the smoother transition on Keyguard when we also are not collapsing 2525 // from a scrolled quick settings. 2526 return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), 2527 (float) (mQsMaxExpansionHeight), 2528 computeQsExpansionFraction()); 2529 } else { 2530 return mQsExpansionHeight; 2531 } 2532 } 2533 2534 /** 2535 * @return the topPadding of notifications when on keyguard not respecting quick settings 2536 * expansion 2537 */ getKeyguardNotificationStaticPadding()2538 private int getKeyguardNotificationStaticPadding() { 2539 if (!mKeyguardShowing) { 2540 return 0; 2541 } 2542 if (!mKeyguardBypassController.getBypassEnabled()) { 2543 return mClockPositionResult.stackScrollerPadding; 2544 } 2545 int collapsedPosition = mHeadsUpInset; 2546 if (!mNotificationStackScrollLayoutController.isPulseExpanding()) { 2547 return collapsedPosition; 2548 } else { 2549 int expandedPosition = mClockPositionResult.stackScrollerPadding; 2550 return (int) MathUtils.lerp(collapsedPosition, expandedPosition, 2551 mNotificationStackScrollLayoutController.calculateAppearFractionBypass()); 2552 } 2553 } 2554 2555 requestScrollerTopPaddingUpdate(boolean animate)2556 protected void requestScrollerTopPaddingUpdate(boolean animate) { 2557 mNotificationStackScrollLayoutController.updateTopPadding( 2558 calculateNotificationsTopPadding(), animate); 2559 if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { 2560 // update the position of the header 2561 updateQsExpansion(); 2562 } 2563 } 2564 2565 /** 2566 * Set the amount of pixels we have currently dragged down if we're transitioning to the full 2567 * shade. 0.0f means we're not transitioning yet. 2568 */ setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay)2569 public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) { 2570 if (animate && !mShouldUseSplitNotificationShade) { 2571 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 2572 delay); 2573 mIsQsTranslationResetAnimator = mQsTranslationForFullShadeTransition > 0.0f; 2574 } 2575 2576 float endPosition = 0; 2577 if (pxAmount > 0.0f) { 2578 if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0 2579 && !mMediaDataManager.hasActiveMedia()) { 2580 // No notifications are visible, let's animate to the height of qs instead 2581 if (mQs != null) { 2582 // Let's interpolate to the header height instead of the top padding, 2583 // because the toppadding is way too low because of the large clock. 2584 // we still want to take into account the edgePosition though as that nicely 2585 // overshoots in the stackscroller 2586 endPosition = getQSEdgePosition() 2587 - mNotificationStackScrollLayoutController.getTopPadding() 2588 + mQs.getHeader().getHeight(); 2589 } 2590 } else { 2591 // Interpolating to the new bottom edge position! 2592 endPosition = getQSEdgePosition() 2593 + mNotificationStackScrollLayoutController.getFullShadeTransitionInset(); 2594 if (isOnKeyguard()) { 2595 endPosition -= mLockscreenNotificationQSPadding; 2596 } 2597 } 2598 } 2599 2600 // Calculate the overshoot amount such that we're reaching the target after our desired 2601 // distance, but only reach it fully once we drag a full shade length. 2602 mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation( 2603 MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition)); 2604 2605 int position = (int) MathUtils.lerp((float) 0, endPosition, 2606 mTransitioningToFullShadeProgress); 2607 if (mTransitioningToFullShadeProgress > 0.0f) { 2608 // we want at least 1 pixel otherwise the panel won't be clipped 2609 position = Math.max(1, position); 2610 } 2611 mTransitionToFullShadeQSPosition = position; 2612 updateQsExpansion(); 2613 } 2614 2615 /** 2616 * Notify the panel that the pulse expansion has finished and that we're going to the full 2617 * shade 2618 */ onPulseExpansionFinished()2619 public void onPulseExpansionFinished() { 2620 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 0); 2621 mIsPulseExpansionResetAnimator = true; 2622 } 2623 2624 /** 2625 * Set the alpha of the keyguard elements which only show on the lockscreen, but not in 2626 * shade locked / shade. This is used when dragging down to the full shade. 2627 */ setKeyguardOnlyContentAlpha(float keyguardAlpha)2628 public void setKeyguardOnlyContentAlpha(float keyguardAlpha) { 2629 mKeyguardOnlyContentAlpha = Interpolators.ALPHA_IN.getInterpolation(keyguardAlpha); 2630 if (mBarState == KEYGUARD) { 2631 // If the animator is running, it's already fading out the content and this is a reset 2632 mBottomAreaShadeAlpha = mKeyguardOnlyContentAlpha; 2633 updateKeyguardBottomAreaAlpha(); 2634 } 2635 updateClock(); 2636 } 2637 trackMovement(MotionEvent event)2638 private void trackMovement(MotionEvent event) { 2639 if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event); 2640 } 2641 initVelocityTracker()2642 private void initVelocityTracker() { 2643 if (mQsVelocityTracker != null) { 2644 mQsVelocityTracker.recycle(); 2645 } 2646 mQsVelocityTracker = VelocityTracker.obtain(); 2647 } 2648 getCurrentQSVelocity()2649 private float getCurrentQSVelocity() { 2650 if (mQsVelocityTracker == null) { 2651 return 0; 2652 } 2653 mQsVelocityTracker.computeCurrentVelocity(1000); 2654 return mQsVelocityTracker.getYVelocity(); 2655 } 2656 cancelQsAnimation()2657 private void cancelQsAnimation() { 2658 if (mQsExpansionAnimator != null) { 2659 mQsExpansionAnimator.cancel(); 2660 } 2661 } 2662 2663 /** 2664 * @see #flingSettings(float, int, Runnable, boolean) 2665 */ flingSettings(float vel, int type)2666 public void flingSettings(float vel, int type) { 2667 flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */); 2668 } 2669 2670 /** 2671 * Animates QS or QQS as if the user had swiped up or down. 2672 * 2673 * @param vel Finger velocity or 0 when not initiated by touch events. 2674 * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link 2675 * #FLING_HIDE}. 2676 * @param onFinishRunnable Runnable to be executed at the end of animation. 2677 * @param isClick If originated by click (different interpolator and duration.) 2678 */ flingSettings(float vel, int type, final Runnable onFinishRunnable, boolean isClick)2679 protected void flingSettings(float vel, int type, final Runnable onFinishRunnable, 2680 boolean isClick) { 2681 float target; 2682 switch (type) { 2683 case FLING_EXPAND: 2684 target = mQsMaxExpansionHeight; 2685 break; 2686 case FLING_COLLAPSE: 2687 target = mQsMinExpansionHeight; 2688 break; 2689 case FLING_HIDE: 2690 default: 2691 if (mQs != null) { 2692 mQs.closeDetail(); 2693 } 2694 target = 0; 2695 } 2696 if (target == mQsExpansionHeight) { 2697 if (onFinishRunnable != null) { 2698 onFinishRunnable.run(); 2699 } 2700 traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */); 2701 return; 2702 } 2703 2704 // If we move in the opposite direction, reset velocity and use a different duration. 2705 boolean oppositeDirection = false; 2706 boolean expanding = type == FLING_EXPAND; 2707 if (vel > 0 && !expanding || vel < 0 && expanding) { 2708 vel = 0; 2709 oppositeDirection = true; 2710 } 2711 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); 2712 if (isClick) { 2713 animator.setInterpolator(Interpolators.TOUCH_RESPONSE); 2714 animator.setDuration(368); 2715 } else { 2716 mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); 2717 } 2718 if (oppositeDirection) { 2719 animator.setDuration(350); 2720 } 2721 animator.addUpdateListener(animation -> { 2722 setQsExpansion((Float) animation.getAnimatedValue()); 2723 }); 2724 animator.addListener(new AnimatorListenerAdapter() { 2725 private boolean mIsCanceled; 2726 @Override 2727 public void onAnimationStart(Animator animation) { 2728 notifyExpandingStarted(); 2729 } 2730 2731 @Override 2732 public void onAnimationCancel(Animator animation) { 2733 mIsCanceled = true; 2734 } 2735 2736 @Override 2737 public void onAnimationEnd(Animator animation) { 2738 mQSAnimatingHiddenFromCollapsed = false; 2739 mAnimatingQS = false; 2740 notifyExpandingFinished(); 2741 mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind(); 2742 mQsExpansionAnimator = null; 2743 if (onFinishRunnable != null) { 2744 onFinishRunnable.run(); 2745 } 2746 traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */); 2747 } 2748 }); 2749 // Let's note that we're animating QS. Moving the animator here will cancel it immediately, 2750 // so we need a separate flag. 2751 mAnimatingQS = true; 2752 animator.start(); 2753 mQsExpansionAnimator = animator; 2754 mQsAnimatorExpand = expanding; 2755 mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0; 2756 } 2757 2758 /** 2759 * @return Whether we should intercept a gesture to open Quick Settings. 2760 */ shouldQuickSettingsIntercept(float x, float y, float yDiff)2761 private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { 2762 if (!isQsExpansionEnabled() || mCollapsedOnDown 2763 || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) 2764 || (mKeyguardShowing && mShouldUseSplitNotificationShade)) { 2765 return false; 2766 } 2767 View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); 2768 2769 mQsInterceptRegion.set( 2770 /* left= */ (int) mQsFrame.getX(), 2771 /* top= */ header.getTop(), 2772 /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(), 2773 /* bottom= */ header.getBottom()); 2774 // Also allow QS to intercept if the touch is near the notch. 2775 mStatusBarTouchableRegionManager.updateRegionForNotch(mQsInterceptRegion); 2776 final boolean onHeader = mQsInterceptRegion.contains((int) x, (int) y); 2777 2778 if (mQsExpanded) { 2779 return onHeader || (yDiff < 0 && isInQsArea(x, y)); 2780 } else { 2781 return onHeader; 2782 } 2783 } 2784 2785 @Override canCollapsePanelOnTouch()2786 protected boolean canCollapsePanelOnTouch() { 2787 if (!isInSettings() && mBarState == KEYGUARD) { 2788 return true; 2789 } 2790 2791 if (mNotificationStackScrollLayoutController.isScrolledToBottom()) { 2792 return true; 2793 } 2794 2795 return !mShouldUseSplitNotificationShade && (isInSettings() || mIsPanelCollapseOnQQS); 2796 } 2797 2798 @Override getMaxPanelHeight()2799 protected int getMaxPanelHeight() { 2800 int min = mStatusBarMinHeight; 2801 if (!(mBarState == KEYGUARD) 2802 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) { 2803 int minHeight = mQsMinExpansionHeight; 2804 min = Math.max(min, minHeight); 2805 } 2806 int maxHeight; 2807 if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted 2808 || mPulsing) { 2809 maxHeight = calculatePanelHeightQsExpanded(); 2810 } else { 2811 maxHeight = calculatePanelHeightShade(); 2812 } 2813 maxHeight = Math.max(min, maxHeight); 2814 if (maxHeight == 0 || isNaN(maxHeight)) { 2815 Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: " 2816 + mOverExpansion + ", calculatePanelHeightQsExpanded: " 2817 + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: " 2818 + calculatePanelHeightShade() + ", mStatusBarMinHeight = " 2819 + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight); 2820 } 2821 return maxHeight; 2822 } 2823 isInSettings()2824 public boolean isInSettings() { 2825 return mQsExpanded; 2826 } 2827 isExpanding()2828 public boolean isExpanding() { 2829 return mIsExpanding; 2830 } 2831 2832 @Override onHeightUpdated(float expandedHeight)2833 protected void onHeightUpdated(float expandedHeight) { 2834 if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) { 2835 // Updating the clock position will set the top padding which might 2836 // trigger a new panel height and re-position the clock. 2837 // This is a circular dependency and should be avoided, otherwise we'll have 2838 // a stack overflow. 2839 if (mStackScrollerMeasuringPass > 2) { 2840 if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting."); 2841 } else { 2842 positionClockAndNotifications(); 2843 } 2844 } 2845 if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null 2846 && !mQsExpansionFromOverscroll) { 2847 float t; 2848 if (mKeyguardShowing) { 2849 2850 // On Keyguard, interpolate the QS expansion linearly to the panel expansion 2851 t = expandedHeight / (getMaxPanelHeight()); 2852 } else { 2853 // In Shade, interpolate linearly such that QS is closed whenever panel height is 2854 // minimum QS expansion + minStackHeight 2855 float 2856 panelHeightQsCollapsed = 2857 mNotificationStackScrollLayoutController.getIntrinsicPadding() 2858 + mNotificationStackScrollLayoutController.getLayoutMinHeight(); 2859 float panelHeightQsExpanded = calculatePanelHeightQsExpanded(); 2860 t = 2861 (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded 2862 - panelHeightQsCollapsed); 2863 } 2864 float 2865 targetHeight = 2866 mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight); 2867 setQsExpansion(targetHeight); 2868 } 2869 updateExpandedHeight(expandedHeight); 2870 updateHeader(); 2871 updateNotificationTranslucency(); 2872 updatePanelExpanded(); 2873 updateGestureExclusionRect(); 2874 if (DEBUG) { 2875 mView.invalidate(); 2876 } 2877 } 2878 updatePanelExpanded()2879 private void updatePanelExpanded() { 2880 boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; 2881 if (mPanelExpanded != isExpanded) { 2882 mHeadsUpManager.setIsPanelExpanded(isExpanded); 2883 mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded); 2884 mStatusBar.setPanelExpanded(isExpanded); 2885 mPanelExpanded = isExpanded; 2886 2887 if (!isExpanded && mQs != null && mQs.isCustomizing()) { 2888 mQs.closeCustomizer(); 2889 } 2890 } 2891 } 2892 calculatePanelHeightShade()2893 private int calculatePanelHeightShade() { 2894 int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); 2895 int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; 2896 2897 if (mBarState == KEYGUARD) { 2898 int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight() 2899 + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); 2900 return Math.max(maxHeight, minKeyguardPanelBottom); 2901 } else { 2902 return maxHeight; 2903 } 2904 } 2905 calculatePanelHeightQsExpanded()2906 private int calculatePanelHeightQsExpanded() { 2907 float 2908 notificationHeight = 2909 mNotificationStackScrollLayoutController.getHeight() 2910 - mNotificationStackScrollLayoutController.getEmptyBottomMargin() 2911 - mNotificationStackScrollLayoutController.getTopPadding(); 2912 2913 // When only empty shade view is visible in QS collapsed state, simulate that we would have 2914 // it in expanded QS state as well so we don't run into troubles when fading the view in/out 2915 // and expanding/collapsing the whole panel from/to quick settings. 2916 if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0 2917 && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) { 2918 notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight(); 2919 } 2920 int maxQsHeight = mQsMaxExpansionHeight; 2921 2922 // If an animation is changing the size of the QS panel, take the animated value. 2923 if (mQsSizeChangeAnimator != null) { 2924 maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue(); 2925 } 2926 float totalHeight = Math.max(maxQsHeight, 2927 mBarState == KEYGUARD ? mClockPositionResult.stackScrollerPadding 2928 : 0) + notificationHeight 2929 + mNotificationStackScrollLayoutController.getTopPaddingOverflow(); 2930 if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) { 2931 float 2932 fullyCollapsedHeight = 2933 maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight(); 2934 totalHeight = Math.max(fullyCollapsedHeight, 2935 mNotificationStackScrollLayoutController.getHeight()); 2936 } 2937 return (int) totalHeight; 2938 } 2939 updateNotificationTranslucency()2940 private void updateNotificationTranslucency() { 2941 float alpha = 1f; 2942 if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp 2943 && !mHeadsUpManager.hasPinnedHeadsUp()) { 2944 alpha = getFadeoutAlpha(); 2945 } 2946 if (mBarState == KEYGUARD && !mHintAnimationRunning 2947 && !mKeyguardBypassController.getBypassEnabled()) { 2948 alpha *= mClockPositionResult.clockAlpha; 2949 } 2950 mNotificationStackScrollLayoutController.setAlpha(alpha); 2951 } 2952 getFadeoutAlpha()2953 private float getFadeoutAlpha() { 2954 float alpha; 2955 if (mQsMinExpansionHeight == 0) { 2956 return 1.0f; 2957 } 2958 alpha = getExpandedHeight() / mQsMinExpansionHeight; 2959 alpha = Math.max(0, Math.min(alpha, 1)); 2960 alpha = (float) Math.pow(alpha, 0.75); 2961 return alpha; 2962 } 2963 2964 /** 2965 * Hides the header when notifications are colliding with it. 2966 */ updateHeader()2967 private void updateHeader() { 2968 if (mBarState == KEYGUARD) { 2969 mKeyguardStatusBarViewController.updateViewState(); 2970 } 2971 updateQsExpansion(); 2972 } 2973 getHeaderTranslation()2974 protected float getHeaderTranslation() { 2975 if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { 2976 return -mQs.getQsMinExpansionHeight(); 2977 } 2978 float appearAmount = mNotificationStackScrollLayoutController 2979 .calculateAppearFraction(mExpandedHeight); 2980 float startHeight = -mQsExpansionHeight; 2981 if (!mShouldUseSplitNotificationShade && mBarState == StatusBarState.SHADE) { 2982 // Small parallax as we pull down and clip QS 2983 startHeight = -mQsExpansionHeight * QS_PARALLAX_AMOUNT; 2984 } 2985 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { 2986 appearAmount = mNotificationStackScrollLayoutController.calculateAppearFractionBypass(); 2987 startHeight = -mQs.getQsMinExpansionHeight(); 2988 } 2989 float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)); 2990 return Math.min(0, translation); 2991 } 2992 updateKeyguardBottomAreaAlpha()2993 private void updateKeyguardBottomAreaAlpha() { 2994 // There are two possible panel expansion behaviors: 2995 // • User dragging up to unlock: we want to fade out as quick as possible 2996 // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area. 2997 // • User tapping on lock screen: bouncer won't be visible but panel expansion will 2998 // change due to "unlock hint animation." In this case, fading out the bottom area 2999 // would also hide the message that says "swipe to unlock," we don't want to do that. 3000 float expansionAlpha = MathUtils.map( 3001 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, 3002 getExpandedFraction()); 3003 float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); 3004 alpha *= mBottomAreaShadeAlpha; 3005 mKeyguardBottomArea.setAffordanceAlpha(alpha); 3006 mKeyguardBottomArea.setImportantForAccessibility( 3007 alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS 3008 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); 3009 View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer(); 3010 if (ambientIndicationContainer != null) { 3011 ambientIndicationContainer.setAlpha(alpha); 3012 } 3013 mLockIconViewController.setAlpha(alpha); 3014 } 3015 3016 @Override onExpandingStarted()3017 protected void onExpandingStarted() { 3018 super.onExpandingStarted(); 3019 mNotificationStackScrollLayoutController.onExpansionStarted(); 3020 mIsExpanding = true; 3021 mQsExpandedWhenExpandingStarted = mQsFullyExpanded; 3022 mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted && 3023 /* We also start expanding when flinging closed Qs. Let's exclude that */ 3024 !mAnimatingQS); 3025 if (mQsExpanded) { 3026 onQsExpansionStarted(); 3027 } 3028 // Since there are QS tiles in the header now, we need to make sure we start listening 3029 // immediately so they can be up to date. 3030 if (mQs == null) return; 3031 mQs.setHeaderListening(true); 3032 } 3033 3034 @Override onExpandingFinished()3035 protected void onExpandingFinished() { 3036 mScrimController.onExpandingFinished(); 3037 mNotificationStackScrollLayoutController.onExpansionStopped(); 3038 mHeadsUpManager.onExpandingFinished(); 3039 mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); 3040 mIsExpanding = false; 3041 mMediaHierarchyManager.setCollapsingShadeFromQS(false); 3042 mMediaHierarchyManager.setQsExpanded(mQsExpanded); 3043 if (isFullyCollapsed()) { 3044 DejankUtils.postAfterTraversal(new Runnable() { 3045 @Override 3046 public void run() { 3047 setListening(false); 3048 } 3049 }); 3050 3051 // Workaround b/22639032: Make sure we invalidate something because else RenderThread 3052 // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go 3053 // ahead with rendering and we jank. 3054 mView.postOnAnimation(new Runnable() { 3055 @Override 3056 public void run() { 3057 mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT); 3058 } 3059 }); 3060 } else { 3061 setListening(true); 3062 } 3063 mQsExpandImmediate = false; 3064 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false); 3065 mTwoFingerQsExpandPossible = false; 3066 updateTrackingHeadsUp(null); 3067 mExpandingFromHeadsUp = false; 3068 setPanelScrimMinFraction(0.0f); 3069 } 3070 updateTrackingHeadsUp(@ullable ExpandableNotificationRow pickedChild)3071 private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) { 3072 mTrackedHeadsUpNotification = pickedChild; 3073 for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) { 3074 Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i); 3075 listener.accept(pickedChild); 3076 } 3077 } 3078 3079 @Nullable getTrackedHeadsUpNotification()3080 public ExpandableNotificationRow getTrackedHeadsUpNotification() { 3081 return mTrackedHeadsUpNotification; 3082 } 3083 setListening(boolean listening)3084 private void setListening(boolean listening) { 3085 mKeyguardStatusBarViewController.setBatteryListening(listening); 3086 if (mQs == null) return; 3087 mQs.setListening(listening); 3088 } 3089 3090 @Override expand(boolean animate)3091 public void expand(boolean animate) { 3092 super.expand(animate); 3093 setListening(true); 3094 } 3095 3096 @Override setOverExpansion(float overExpansion)3097 public void setOverExpansion(float overExpansion) { 3098 if (overExpansion == mOverExpansion) { 3099 return; 3100 } 3101 super.setOverExpansion(overExpansion); 3102 // Translating the quick settings by half the overexpansion to center it in the background 3103 // frame 3104 updateQsFrameTranslation(); 3105 mNotificationStackScrollLayoutController.setOverExpansion(overExpansion); 3106 } 3107 updateQsFrameTranslation()3108 private void updateQsFrameTranslation() { 3109 float translation = mOverExpansion / 2.0f + mQsTranslationForFullShadeTransition; 3110 mQsFrame.setTranslationY(translation); 3111 } 3112 3113 @Override onTrackingStarted()3114 protected void onTrackingStarted() { 3115 mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); 3116 super.onTrackingStarted(); 3117 mScrimController.onTrackingStarted(); 3118 if (mQsFullyExpanded) { 3119 mQsExpandImmediate = true; 3120 if (!mShouldUseSplitNotificationShade) { 3121 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true); 3122 } 3123 } 3124 if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { 3125 mAffordanceHelper.animateHideLeftRightIcon(); 3126 } 3127 mNotificationStackScrollLayoutController.onPanelTrackingStarted(); 3128 cancelPendingPanelCollapse(); 3129 } 3130 3131 @Override onTrackingStopped(boolean expand)3132 protected void onTrackingStopped(boolean expand) { 3133 mFalsingCollector.onTrackingStopped(); 3134 super.onTrackingStopped(expand); 3135 if (expand) { 3136 mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */, 3137 true /* animate */); 3138 } 3139 mNotificationStackScrollLayoutController.onPanelTrackingStopped(); 3140 if (expand && (mBarState == KEYGUARD 3141 || mBarState == StatusBarState.SHADE_LOCKED)) { 3142 if (!mHintAnimationRunning) { 3143 mAffordanceHelper.reset(true); 3144 } 3145 } 3146 } 3147 updateMaxHeadsUpTranslation()3148 private void updateMaxHeadsUpTranslation() { 3149 mNotificationStackScrollLayoutController.setHeadsUpBoundaries( 3150 getHeight(), mNavigationBarBottomHeight); 3151 } 3152 3153 @Override startUnlockHintAnimation()3154 protected void startUnlockHintAnimation() { 3155 if (mPowerManager.isPowerSaveMode()) { 3156 onUnlockHintStarted(); 3157 onUnlockHintFinished(); 3158 return; 3159 } 3160 super.startUnlockHintAnimation(); 3161 } 3162 3163 @Override onUnlockHintFinished()3164 protected void onUnlockHintFinished() { 3165 super.onUnlockHintFinished(); 3166 mNotificationStackScrollLayoutController.setUnlockHintRunning(false); 3167 } 3168 3169 @Override onUnlockHintStarted()3170 protected void onUnlockHintStarted() { 3171 super.onUnlockHintStarted(); 3172 mNotificationStackScrollLayoutController.setUnlockHintRunning(true); 3173 } 3174 3175 @Override shouldUseDismissingAnimation()3176 protected boolean shouldUseDismissingAnimation() { 3177 return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen() 3178 || !isTracking()); 3179 } 3180 3181 @Override isTrackingBlocked()3182 protected boolean isTrackingBlocked() { 3183 return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch; 3184 } 3185 isQsExpanded()3186 public boolean isQsExpanded() { 3187 return mQsExpanded; 3188 } 3189 isQsDetailShowing()3190 public boolean isQsDetailShowing() { 3191 return mQs.isShowingDetail(); 3192 } 3193 3194 /** Returns whether the QS customizer is currently active. */ isQsCustomizing()3195 public boolean isQsCustomizing() { 3196 return mQs.isCustomizing(); 3197 } 3198 closeQsDetail()3199 public void closeQsDetail() { 3200 mQs.closeDetail(); 3201 } 3202 3203 /** Close the QS customizer if it is open. */ closeQsCustomizer()3204 public void closeQsCustomizer() { 3205 mQs.closeCustomizer(); 3206 } 3207 isLaunchTransitionFinished()3208 public boolean isLaunchTransitionFinished() { 3209 return mIsLaunchTransitionFinished; 3210 } 3211 isLaunchTransitionRunning()3212 public boolean isLaunchTransitionRunning() { 3213 return mIsLaunchTransitionRunning; 3214 } 3215 setLaunchTransitionEndRunnable(Runnable r)3216 public void setLaunchTransitionEndRunnable(Runnable r) { 3217 mLaunchAnimationEndRunnable = r; 3218 } 3219 updateDozingVisibilities(boolean animate)3220 private void updateDozingVisibilities(boolean animate) { 3221 mKeyguardBottomArea.setDozing(mDozing, animate); 3222 if (!mDozing && animate) { 3223 mKeyguardStatusBarViewController.animateKeyguardStatusBarIn(); 3224 } 3225 } 3226 3227 @Override isDozing()3228 public boolean isDozing() { 3229 return mDozing; 3230 } 3231 setQsScrimEnabled(boolean qsScrimEnabled)3232 public void setQsScrimEnabled(boolean qsScrimEnabled) { 3233 boolean changed = mQsScrimEnabled != qsScrimEnabled; 3234 mQsScrimEnabled = qsScrimEnabled; 3235 if (changed) { 3236 updateQsState(); 3237 } 3238 } 3239 onScreenTurningOn()3240 public void onScreenTurningOn() { 3241 mKeyguardStatusViewController.dozeTimeTick(); 3242 } 3243 3244 @Override onMiddleClicked()3245 protected boolean onMiddleClicked() { 3246 switch (mBarState) { 3247 case KEYGUARD: 3248 if (!mDozingOnDown) { 3249 if (mUpdateMonitor.isFaceEnrolled() 3250 && !mUpdateMonitor.isFaceDetectionRunning() 3251 && !mUpdateMonitor.getUserCanSkipBouncer( 3252 KeyguardUpdateMonitor.getCurrentUser())) { 3253 mUpdateMonitor.requestFaceAuth(true); 3254 } else { 3255 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT, 3256 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); 3257 mLockscreenGestureLogger 3258 .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT); 3259 startUnlockHintAnimation(); 3260 } 3261 } 3262 return true; 3263 case StatusBarState.SHADE_LOCKED: 3264 if (!mQsExpanded) { 3265 mStatusBarStateController.setState(KEYGUARD); 3266 } 3267 return true; 3268 default: 3269 return true; 3270 } 3271 } 3272 setPanelAlpha(int alpha, boolean animate)3273 public void setPanelAlpha(int alpha, boolean animate) { 3274 if (mPanelAlpha != alpha) { 3275 mPanelAlpha = alpha; 3276 PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255 3277 ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator, 3278 animate); 3279 } 3280 } 3281 setPanelAlphaEndAction(Runnable r)3282 public void setPanelAlphaEndAction(Runnable r) { 3283 mPanelAlphaEndAction = r; 3284 } 3285 setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)3286 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { 3287 mHeadsUpAnimatingAway = headsUpAnimatingAway; 3288 mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway); 3289 updateVisibility(); 3290 } 3291 3292 /** Set whether the bouncer is showing. */ setBouncerShowing(boolean bouncerShowing)3293 public void setBouncerShowing(boolean bouncerShowing) { 3294 mBouncerShowing = bouncerShowing; 3295 updateVisibility(); 3296 } 3297 3298 @Override shouldPanelBeVisible()3299 protected boolean shouldPanelBeVisible() { 3300 boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode; 3301 return headsUpVisible || isExpanded() || mBouncerShowing; 3302 } 3303 3304 @Override setHeadsUpManager(HeadsUpManagerPhone headsUpManager)3305 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) { 3306 super.setHeadsUpManager(headsUpManager); 3307 mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, 3308 mNotificationStackScrollLayoutController.getHeadsUpCallback(), 3309 NotificationPanelViewController.this); 3310 } 3311 setTrackedHeadsUp(ExpandableNotificationRow pickedChild)3312 public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) { 3313 if (pickedChild != null) { 3314 updateTrackingHeadsUp(pickedChild); 3315 mExpandingFromHeadsUp = true; 3316 } 3317 // otherwise we update the state when the expansion is finished 3318 } 3319 3320 @Override onClosingFinished()3321 protected void onClosingFinished() { 3322 mStatusBar.onClosingFinished(); 3323 setClosingWithAlphaFadeout(false); 3324 mMediaHierarchyManager.closeGuts(); 3325 } 3326 setClosingWithAlphaFadeout(boolean closing)3327 private void setClosingWithAlphaFadeout(boolean closing) { 3328 mClosingWithAlphaFadeOut = closing; 3329 mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing); 3330 } 3331 updateExpandedHeight(float expandedHeight)3332 protected void updateExpandedHeight(float expandedHeight) { 3333 if (mTracking) { 3334 mNotificationStackScrollLayoutController 3335 .setExpandingVelocity(getCurrentExpandVelocity()); 3336 } 3337 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { 3338 // The expandedHeight is always the full panel Height when bypassing 3339 expandedHeight = getMaxPanelHeight(); 3340 } 3341 mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); 3342 updateKeyguardBottomAreaAlpha(); 3343 updateStatusBarIcons(); 3344 } 3345 3346 /** 3347 * @return whether the notifications are displayed full width and don't have any margins on 3348 * the side. 3349 */ isFullWidth()3350 public boolean isFullWidth() { 3351 return mIsFullWidth; 3352 } 3353 updateStatusBarIcons()3354 private void updateStatusBarIcons() { 3355 boolean 3356 showIconsWhenExpanded = 3357 (isPanelVisibleBecauseOfHeadsUp() || isFullWidth()) 3358 && getExpandedHeight() < getOpeningHeight(); 3359 boolean noVisibleNotifications = true; 3360 if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) { 3361 showIconsWhenExpanded = false; 3362 } 3363 if (showIconsWhenExpanded != mShowIconsWhenExpanded) { 3364 mShowIconsWhenExpanded = showIconsWhenExpanded; 3365 mCommandQueue.recomputeDisableFlags(mDisplayId, false); 3366 } 3367 } 3368 3369 private boolean isOnKeyguard() { 3370 return mBarState == KEYGUARD; 3371 } 3372 3373 /** 3374 * Sets the minimum fraction for the panel expansion offset. This may be non-zero in certain 3375 * cases, such as if there's a heads-up notification. 3376 */ 3377 public void setPanelScrimMinFraction(float minFraction) { 3378 mMinFraction = minFraction; 3379 mDepthController.setPanelPullDownMinFraction(mMinFraction); 3380 mScrimController.setPanelScrimMinFraction(mMinFraction); 3381 } 3382 3383 public void clearNotificationEffects() { 3384 mStatusBar.clearNotificationEffects(); 3385 } 3386 3387 @Override 3388 protected boolean isPanelVisibleBecauseOfHeadsUp() { 3389 return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway) 3390 && mBarState == StatusBarState.SHADE; 3391 } 3392 3393 public void launchCamera(boolean animate, int source) { 3394 if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) { 3395 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP; 3396 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) { 3397 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE; 3398 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) { 3399 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER; 3400 } else { 3401 3402 // Default. 3403 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; 3404 } 3405 3406 // If we are launching it when we are occluded already we don't want it to animate, 3407 // nor setting these flags, since the occluded state doesn't change anymore, hence it's 3408 // never reset. 3409 if (!isFullyCollapsed()) { 3410 setLaunchingAffordance(true); 3411 } else { 3412 animate = false; 3413 } 3414 mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null; 3415 mAffordanceHelper.launchAffordance( 3416 animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); 3417 } 3418 3419 public void onAffordanceLaunchEnded() { 3420 setLaunchingAffordance(false); 3421 } 3422 3423 /** 3424 * Set whether we are currently launching an affordance. This is currently only set when 3425 * launched via a camera gesture. 3426 */ 3427 private void setLaunchingAffordance(boolean launchingAffordance) { 3428 mLaunchingAffordance = launchingAffordance; 3429 mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance); 3430 mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance( 3431 launchingAffordance); 3432 mKeyguardBypassController.setLaunchingAffordance(launchingAffordance); 3433 } 3434 3435 /** 3436 * Return true when a bottom affordance is launching an occluded activity with a splash screen. 3437 */ 3438 public boolean isLaunchingAffordanceWithPreview() { 3439 return mLaunchingAffordance && mAffordanceHasPreview; 3440 } 3441 3442 /** 3443 * Whether the camera application can be launched for the camera launch gesture. 3444 */ 3445 public boolean canCameraGestureBeLaunched() { 3446 if (!mStatusBar.isCameraAllowedByAdmin()) { 3447 return false; 3448 } 3449 3450 ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent(); 3451 String 3452 packageToLaunch = 3453 (resolveInfo == null || resolveInfo.activityInfo == null) ? null 3454 : resolveInfo.activityInfo.packageName; 3455 return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp( 3456 packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress(); 3457 } 3458 3459 /** 3460 * Return true if the applications with the package name is running in foreground. 3461 * 3462 * @param pkgName application package name. 3463 */ 3464 private boolean isForegroundApp(String pkgName) { 3465 List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1); 3466 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); 3467 } 3468 3469 public boolean hideStatusBarIconsWhenExpanded() { 3470 if (mIsLaunchAnimationRunning) { 3471 return mHideIconsDuringLaunchAnimation; 3472 } 3473 if (mHeadsUpAppearanceController != null 3474 && mHeadsUpAppearanceController.shouldBeVisible()) { 3475 return false; 3476 } 3477 return !isFullWidth() || !mShowIconsWhenExpanded; 3478 } 3479 3480 public final QS.ScrollListener mScrollListener = scrollY -> { 3481 if (scrollY > 0 && !mQsFullyExpanded) { 3482 if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand"); 3483 // If we are scrolling QS, we should be fully expanded. 3484 expandWithQs(); 3485 } 3486 }; 3487 3488 private final FragmentListener mFragmentListener = new FragmentListener() { 3489 @Override 3490 public void onFragmentViewCreated(String tag, Fragment fragment) { 3491 mQs = (QS) fragment; 3492 mQs.setPanelView(mHeightListener); 3493 mQs.setExpandClickListener(mOnClickListener); 3494 mQs.setHeaderClickable(isQsExpansionEnabled()); 3495 mQs.setOverscrolling(mStackScrollerOverscrolling); 3496 mQs.setInSplitShade(mShouldUseSplitNotificationShade); 3497 3498 // recompute internal state when qspanel height changes 3499 mQs.getView().addOnLayoutChangeListener( 3500 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 3501 final int height = bottom - top; 3502 final int oldHeight = oldBottom - oldTop; 3503 if (height != oldHeight) { 3504 mHeightListener.onQsHeightChanged(); 3505 } 3506 }); 3507 mQs.setCollapsedMediaVisibilityChangedListener((visible) -> { 3508 if (mQs.getHeader().isShown()) { 3509 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD, 3510 0 /* delay */); 3511 mNotificationStackScrollLayoutController.animateNextTopPaddingChange(); 3512 } 3513 }); 3514 mLockscreenShadeTransitionController.setQS(mQs); 3515 mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView()); 3516 mQs.setScrollListener(mScrollListener); 3517 updateQsExpansion(); 3518 } 3519 3520 @Override 3521 public void onFragmentViewDestroyed(String tag, Fragment fragment) { 3522 // Manual handling of fragment lifecycle is only required because this bridges 3523 // non-fragment and fragment code. Once we are using a fragment for the notification 3524 // panel, mQs will not need to be null cause it will be tied to the same lifecycle. 3525 if (fragment == mQs) { 3526 mQs = null; 3527 } 3528 } 3529 }; 3530 animateNextNotificationBounds(long duration, long delay)3531 private void animateNextNotificationBounds(long duration, long delay) { 3532 mAnimateNextNotificationBounds = true; 3533 mNotificationBoundsAnimationDuration = duration; 3534 mNotificationBoundsAnimationDelay = delay; 3535 } 3536 3537 @Override setTouchAndAnimationDisabled(boolean disabled)3538 public void setTouchAndAnimationDisabled(boolean disabled) { 3539 super.setTouchAndAnimationDisabled(disabled); 3540 if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) { 3541 mAffordanceHelper.reset(false /* animate */); 3542 } 3543 mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled); 3544 } 3545 3546 /** 3547 * Sets the dozing state. 3548 * 3549 * @param dozing {@code true} when dozing. 3550 * @param animate if transition should be animated. 3551 * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor. 3552 */ setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation)3553 public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) { 3554 if (dozing == mDozing) return; 3555 mView.setDozing(dozing); 3556 mDozing = dozing; 3557 mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation); 3558 mKeyguardBottomArea.setDozing(mDozing, animate); 3559 mKeyguardStatusBarViewController.setDozing(mDozing); 3560 3561 if (dozing) { 3562 mBottomAreaShadeAlphaAnimator.cancel(); 3563 } 3564 3565 if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) { 3566 updateDozingVisibilities(animate); 3567 } 3568 3569 final float dozeAmount = dozing ? 1 : 0; 3570 mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate); 3571 } 3572 setPulsing(boolean pulsing)3573 public void setPulsing(boolean pulsing) { 3574 mPulsing = pulsing; 3575 final boolean 3576 animatePulse = 3577 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn(); 3578 if (animatePulse) { 3579 mAnimateNextPositionUpdate = true; 3580 } 3581 // Do not animate the clock when waking up from a pulse. 3582 // The height callback will take care of pushing the clock to the right position. 3583 if (!mPulsing && !mDozing) { 3584 mAnimateNextPositionUpdate = false; 3585 } 3586 mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse); 3587 } 3588 setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible)3589 public void setAmbientIndicationTop(int ambientIndicationTop, boolean ambientTextVisible) { 3590 int ambientIndicationBottomPadding = 0; 3591 if (ambientTextVisible) { 3592 int stackBottom = mNotificationStackScrollLayoutController.getView().getBottom(); 3593 ambientIndicationBottomPadding = stackBottom - ambientIndicationTop; 3594 } 3595 if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) { 3596 mAmbientIndicationBottomPadding = ambientIndicationBottomPadding; 3597 updateMaxDisplayedNotifications(true); 3598 } 3599 } 3600 dozeTimeTick()3601 public void dozeTimeTick() { 3602 mLockIconViewController.dozeTimeTick(); 3603 mKeyguardBottomArea.dozeTimeTick(); 3604 mKeyguardStatusViewController.dozeTimeTick(); 3605 if (mInterpolatedDarkAmount > 0) { 3606 positionClockAndNotifications(); 3607 } 3608 } 3609 setStatusAccessibilityImportance(int mode)3610 public void setStatusAccessibilityImportance(int mode) { 3611 mKeyguardStatusViewController.setStatusAccessibilityImportance(mode); 3612 } 3613 3614 /** 3615 * TODO: this should be removed. 3616 * It's not correct to pass this view forward because other classes will end up adding 3617 * children to it. Theme will be out of sync. 3618 * 3619 * @return bottom area view 3620 */ getKeyguardBottomAreaView()3621 public KeyguardBottomAreaView getKeyguardBottomAreaView() { 3622 return mKeyguardBottomArea; 3623 } 3624 setUserSetupComplete(boolean userSetupComplete)3625 public void setUserSetupComplete(boolean userSetupComplete) { 3626 mUserSetupComplete = userSetupComplete; 3627 mKeyguardBottomArea.setUserSetupComplete(userSetupComplete); 3628 } 3629 applyLaunchAnimationProgress(float linearProgress)3630 public void applyLaunchAnimationProgress(float linearProgress) { 3631 boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, 3632 linearProgress, ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f; 3633 if (hideIcons != mHideIconsDuringLaunchAnimation) { 3634 mHideIconsDuringLaunchAnimation = hideIcons; 3635 if (!hideIcons) { 3636 mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); 3637 } 3638 } 3639 } 3640 addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3641 public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { 3642 mTrackingHeadsUpListeners.add(listener); 3643 } 3644 removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3645 public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) { 3646 mTrackingHeadsUpListeners.remove(listener); 3647 } 3648 setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)3649 public void setHeadsUpAppearanceController( 3650 HeadsUpAppearanceController headsUpAppearanceController) { 3651 mHeadsUpAppearanceController = headsUpAppearanceController; 3652 } 3653 3654 /** 3655 * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the 3656 * security view of the bouncer. 3657 */ onBouncerPreHideAnimation()3658 public void onBouncerPreHideAnimation() { 3659 if (mKeyguardQsUserSwitchController != null) { 3660 mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility( 3661 mBarState, 3662 true /* keyguardFadingAway */, 3663 false /* goingToFullShade */, 3664 mBarState); 3665 } 3666 if (mKeyguardUserSwitcherController != null) { 3667 mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility( 3668 mBarState, 3669 true /* keyguardFadingAway */, 3670 false /* goingToFullShade */, 3671 mBarState); 3672 } 3673 } 3674 3675 /** */ setImportantForAccessibility(int mode)3676 public void setImportantForAccessibility(int mode) { 3677 mView.setImportantForAccessibility(mode); 3678 } 3679 3680 /** 3681 * Do not let the user drag the shade up and down for the current touch session. 3682 * This is necessary to avoid shade expansion while/after the bouncer is dismissed. 3683 */ blockExpansionForCurrentTouch()3684 public void blockExpansionForCurrentTouch() { 3685 mBlockingExpansionForCurrentTouch = mTracking; 3686 } 3687 3688 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)3689 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3690 super.dump(fd, pw, args); 3691 pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect() 3692 + " applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom 3693 + ") qsVisible(" + mQsVisible); 3694 if (mKeyguardStatusBarViewController != null) { 3695 mKeyguardStatusBarViewController.dump(fd, pw, args); 3696 } 3697 } 3698 hasActiveClearableNotifications()3699 public boolean hasActiveClearableNotifications() { 3700 return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL); 3701 } 3702 createRemoteInputDelegate()3703 public RemoteInputController.Delegate createRemoteInputDelegate() { 3704 return mNotificationStackScrollLayoutController.createDelegate(); 3705 } 3706 3707 /** 3708 * Updates the notification views' sections and status bar icons. This is 3709 * triggered by the NotificationPresenter whenever there are changes to the underlying 3710 * notification data being displayed. In the new notification pipeline, this is handled in 3711 * {@link ShadeViewManager}. 3712 */ updateNotificationViews(String reason)3713 public void updateNotificationViews(String reason) { 3714 mNotificationStackScrollLayoutController.updateSectionBoundaries(reason); 3715 mNotificationStackScrollLayoutController.updateFooter(); 3716 3717 mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList()); 3718 } 3719 createVisibleEntriesList()3720 private List<ListEntry> createVisibleEntriesList() { 3721 List<ListEntry> entries = new ArrayList<>( 3722 mNotificationStackScrollLayoutController.getChildCount()); 3723 for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { 3724 View view = mNotificationStackScrollLayoutController.getChildAt(i); 3725 if (view instanceof ExpandableNotificationRow) { 3726 entries.add(((ExpandableNotificationRow) view).getEntry()); 3727 } 3728 } 3729 return entries; 3730 } 3731 onUpdateRowStates()3732 public void onUpdateRowStates() { 3733 mNotificationStackScrollLayoutController.onUpdateRowStates(); 3734 } 3735 hasPulsingNotifications()3736 public boolean hasPulsingNotifications() { 3737 return mNotificationStackScrollLayoutController 3738 .getNotificationListContainer().hasPulsingNotifications(); 3739 } 3740 getActivatedChild()3741 public ActivatableNotificationView getActivatedChild() { 3742 return mNotificationStackScrollLayoutController.getActivatedChild(); 3743 } 3744 setActivatedChild(ActivatableNotificationView o)3745 public void setActivatedChild(ActivatableNotificationView o) { 3746 mNotificationStackScrollLayoutController.setActivatedChild(o); 3747 } 3748 runAfterAnimationFinished(Runnable r)3749 public void runAfterAnimationFinished(Runnable r) { 3750 mNotificationStackScrollLayoutController.runAfterAnimationFinished(r); 3751 } 3752 setScrollingEnabled(boolean b)3753 public void setScrollingEnabled(boolean b) { 3754 mNotificationStackScrollLayoutController.setScrollingEnabled(b); 3755 } 3756 3757 private Runnable mHideExpandedRunnable; 3758 private final Runnable mMaybeHideExpandedRunnable = new Runnable() { 3759 @Override 3760 public void run() { 3761 if (getExpansionFraction() == 0.0f) { 3762 mView.post(mHideExpandedRunnable); 3763 } 3764 } 3765 }; 3766 3767 /** 3768 * Initialize objects instead of injecting to avoid circular dependencies. 3769 * 3770 * @param hideExpandedRunnable a runnable to run when we need to hide the expanded panel. 3771 */ initDependencies( StatusBar statusBar, Runnable hideExpandedRunnable, NotificationShelfController notificationShelfController)3772 public void initDependencies( 3773 StatusBar statusBar, 3774 Runnable hideExpandedRunnable, 3775 NotificationShelfController notificationShelfController) { 3776 setStatusBar(statusBar); 3777 mHideExpandedRunnable = hideExpandedRunnable; 3778 mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); 3779 mNotificationShelfController = notificationShelfController; 3780 mLockscreenShadeTransitionController.bindController(notificationShelfController); 3781 updateMaxDisplayedNotifications(true); 3782 } 3783 setAlpha(float alpha)3784 public void setAlpha(float alpha) { 3785 mView.setAlpha(alpha); 3786 } 3787 fadeOut(long startDelayMs, long durationMs, Runnable endAction)3788 public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) { 3789 return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration( 3790 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction( 3791 endAction); 3792 } 3793 resetViewGroupFade()3794 public void resetViewGroupFade() { 3795 ViewGroupFadeHelper.reset(mView); 3796 } 3797 addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3798 public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { 3799 mView.getViewTreeObserver().addOnGlobalLayoutListener(listener); 3800 } 3801 removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3802 public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) { 3803 mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); 3804 } 3805 getOnHeadsUpChangedListener()3806 public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() { 3807 return mOnHeadsUpChangedListener; 3808 } 3809 getHeight()3810 public int getHeight() { 3811 return mView.getHeight(); 3812 } 3813 setHeaderDebugInfo(String text)3814 public void setHeaderDebugInfo(String text) { 3815 if (DEBUG) mHeaderDebugInfo = text; 3816 } 3817 onThemeChanged()3818 public void onThemeChanged() { 3819 mConfigurationListener.onThemeChanged(); 3820 } 3821 3822 @Override createLayoutChangeListener()3823 public OnLayoutChangeListener createLayoutChangeListener() { 3824 return new OnLayoutChangeListener(); 3825 } 3826 3827 @Override createTouchHandler()3828 protected TouchHandler createTouchHandler() { 3829 return new TouchHandler() { 3830 3831 private long mLastTouchDownTime = -1L; 3832 3833 @Override 3834 public boolean onInterceptTouchEvent(MotionEvent event) { 3835 if (mBlockTouches || mQs.disallowPanelTouches()) { 3836 return false; 3837 } 3838 initDownStates(event); 3839 // Do not let touches go to shade or QS if the bouncer is visible, 3840 // but still let user swipe down to expand the panel, dismissing the bouncer. 3841 if (mStatusBar.isBouncerShowing()) { 3842 return true; 3843 } 3844 if (mCommandQueue.panelsEnabled() 3845 && !mNotificationStackScrollLayoutController.isLongPressInProgress() 3846 && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 3847 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); 3848 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); 3849 return true; 3850 } 3851 if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0) 3852 && mPulseExpansionHandler.onInterceptTouchEvent(event)) { 3853 return true; 3854 } 3855 3856 if (!isFullyCollapsed() && onQsIntercept(event)) { 3857 return true; 3858 } 3859 return super.onInterceptTouchEvent(event); 3860 } 3861 3862 @Override 3863 public boolean onTouch(View v, MotionEvent event) { 3864 if (event.getAction() == MotionEvent.ACTION_DOWN) { 3865 if (event.getDownTime() == mLastTouchDownTime) { 3866 // An issue can occur when swiping down after unlock, where multiple down 3867 // events are received in this handler with identical downTimes. Until the 3868 // source of the issue can be located, detect this case and ignore. 3869 // see b/193350347 3870 Log.w(TAG, "Duplicate down event detected... ignoring"); 3871 return true; 3872 } 3873 mLastTouchDownTime = event.getDownTime(); 3874 } 3875 3876 3877 if (mBlockTouches || (mQsFullyExpanded && mQs != null 3878 && mQs.disallowPanelTouches())) { 3879 return false; 3880 } 3881 3882 // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able 3883 // to pull down QS or expand the shade. 3884 if (mStatusBar.isBouncerShowingScrimmed()) { 3885 return false; 3886 } 3887 3888 // Make sure the next touch won't the blocked after the current ends. 3889 if (event.getAction() == MotionEvent.ACTION_UP 3890 || event.getAction() == MotionEvent.ACTION_CANCEL) { 3891 mBlockingExpansionForCurrentTouch = false; 3892 } 3893 // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately 3894 // without any ACTION_MOVE event. 3895 // In such case, simply expand the panel instead of being stuck at the bottom bar. 3896 if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) { 3897 expand(true /* animate */); 3898 } 3899 initDownStates(event); 3900 3901 // If pulse is expanding already, let's give it the touch. There are situations 3902 // where the panel starts expanding even though we're also pulsing 3903 boolean pulseShouldGetTouch = (!mIsExpanding 3904 && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)) 3905 || mPulseExpansionHandler.isExpanding(); 3906 if (pulseShouldGetTouch && mPulseExpansionHandler.onTouchEvent(event)) { 3907 // We're expanding all the other ones shouldn't get this anymore 3908 return true; 3909 } 3910 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp() 3911 && !mNotificationStackScrollLayoutController.isLongPressInProgress() 3912 && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { 3913 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1); 3914 } 3915 boolean handled = false; 3916 if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded 3917 && mBarState != StatusBarState.SHADE && !mDozing) { 3918 handled |= mAffordanceHelper.onTouchEvent(event); 3919 } 3920 if (mOnlyAffordanceInThisMotion) { 3921 return true; 3922 } 3923 handled |= mHeadsUpTouchHelper.onTouchEvent(event); 3924 3925 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { 3926 return true; 3927 } 3928 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { 3929 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1); 3930 handled = true; 3931 } 3932 3933 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded() 3934 && mStatusBarKeyguardViewManager.isShowing()) { 3935 mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX()); 3936 } 3937 3938 handled |= super.onTouch(v, event); 3939 return !mDozing || mPulsing || handled; 3940 } 3941 }; 3942 } 3943 3944 private final PhoneStatusBarView.TouchEventHandler mStatusBarViewTouchEventHandler = 3945 new PhoneStatusBarView.TouchEventHandler() { 3946 @Override 3947 public void onInterceptTouchEvent(MotionEvent event) { 3948 mStatusBar.onTouchEvent(event); 3949 } 3950 3951 @Override 3952 public boolean handleTouchEvent(MotionEvent event) { 3953 mStatusBar.onTouchEvent(event); 3954 3955 // TODO(b/202981994): Move the touch debugging in this method to a central 3956 // location. (Right now, it's split between StatusBar and here.) 3957 3958 // If panels aren't enabled, ignore the gesture and don't pass it down to the 3959 // panel view. 3960 if (!mCommandQueue.panelsEnabled()) { 3961 if (event.getAction() == MotionEvent.ACTION_DOWN) { 3962 Log.v( 3963 TAG, 3964 String.format( 3965 "onTouchForwardedFromStatusBar: " 3966 + "panel disabled, ignoring touch at (%d,%d)", 3967 (int) event.getX(), 3968 (int) event.getY() 3969 ) 3970 ); 3971 } 3972 return false; 3973 } 3974 3975 // If the view that would receive the touch is disabled, just have status bar 3976 // eat the gesture. 3977 if (event.getAction() == MotionEvent.ACTION_DOWN && !mView.isEnabled()) { 3978 Log.v(TAG, 3979 String.format( 3980 "onTouchForwardedFromStatusBar: " 3981 + "panel view disabled, eating touch at (%d,%d)", 3982 (int) event.getX(), 3983 (int) event.getY() 3984 ) 3985 ); 3986 return true; 3987 } 3988 3989 return mView.dispatchTouchEvent(event); 3990 } 3991 }; 3992 3993 @Override 3994 protected PanelViewController.OnConfigurationChangedListener createOnConfigurationChangedListener()3995 createOnConfigurationChangedListener() { 3996 return new OnConfigurationChangedListener(); 3997 } 3998 getNotificationStackScrollLayoutController()3999 public NotificationStackScrollLayoutController getNotificationStackScrollLayoutController() { 4000 return mNotificationStackScrollLayoutController; 4001 } 4002 4003 /** 4004 * Close the keyguard user switcher if it is open and capable of closing. 4005 * 4006 * Has no effect if user switcher isn't supported, if the user switcher is already closed, or 4007 * if the user switcher uses "simple" mode. The simple user switcher cannot be closed. 4008 * 4009 * @return true if the keyguard user switcher was open, and is now closed 4010 */ closeUserSwitcherIfOpen()4011 public boolean closeUserSwitcherIfOpen() { 4012 if (mKeyguardUserSwitcherController != null) { 4013 return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple( 4014 true /* animate */); 4015 } 4016 return false; 4017 } 4018 updateUserSwitcherFlags()4019 private void updateUserSwitcherFlags() { 4020 mKeyguardUserSwitcherEnabled = mResources.getBoolean( 4021 com.android.internal.R.bool.config_keyguardUserSwitcher); 4022 mKeyguardQsUserSwitchEnabled = 4023 mKeyguardUserSwitcherEnabled && mResources.getBoolean( 4024 R.bool.config_keyguard_user_switch_opens_qs_details); 4025 } 4026 registerSettingsChangeListener()4027 private void registerSettingsChangeListener() { 4028 mContentResolver.registerContentObserver( 4029 Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), 4030 /* notifyForDescendants */ false, 4031 mSettingsChangeObserver 4032 ); 4033 } 4034 unregisterSettingsChangeListener()4035 private void unregisterSettingsChangeListener() { 4036 mContentResolver.unregisterContentObserver(mSettingsChangeObserver); 4037 } 4038 4039 /** 4040 * Updates notification panel-specific flags on {@link SysUiState}. 4041 */ updateSystemUiStateFlags()4042 public void updateSystemUiStateFlags() { 4043 if (SysUiState.DEBUG) { 4044 Log.d(TAG, "Updating panel sysui state flags: fullyExpanded=" 4045 + isFullyExpanded() + " inQs=" + isInSettings()); 4046 } 4047 mSysUiState.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, 4048 isFullyExpanded() && !isInSettings()) 4049 .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isInSettings()) 4050 .commitUpdate(mDisplayId); 4051 } 4052 4053 private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener { 4054 @Override onHeightChanged(ExpandableView view, boolean needsAnimation)4055 public void onHeightChanged(ExpandableView view, boolean needsAnimation) { 4056 4057 // Block update if we are in quick settings and just the top padding changed 4058 // (i.e. view == null). 4059 if (view == null && mQsExpanded) { 4060 return; 4061 } 4062 if (needsAnimation && mInterpolatedDarkAmount == 0) { 4063 mAnimateNextPositionUpdate = true; 4064 } 4065 ExpandableView firstChildNotGone = 4066 mNotificationStackScrollLayoutController.getFirstChildNotGone(); 4067 ExpandableNotificationRow 4068 firstRow = 4069 firstChildNotGone instanceof ExpandableNotificationRow 4070 ? (ExpandableNotificationRow) firstChildNotGone : null; 4071 if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent() 4072 == firstRow))) { 4073 requestScrollerTopPaddingUpdate(false /* animate */); 4074 } 4075 requestPanelHeightUpdate(); 4076 } 4077 4078 @Override onReset(ExpandableView view)4079 public void onReset(ExpandableView view) { 4080 } 4081 } 4082 4083 private class OnClickListener implements View.OnClickListener { 4084 @Override onClick(View v)4085 public void onClick(View v) { 4086 onQsExpansionStarted(); 4087 if (mQsExpanded) { 4088 flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */, 4089 true /* isClick */); 4090 } else if (isQsExpansionEnabled()) { 4091 mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0); 4092 flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */, 4093 true /* isClick */); 4094 } 4095 } 4096 } 4097 4098 private class OnOverscrollTopChangedListener implements 4099 NotificationStackScrollLayout.OnOverscrollTopChangedListener { 4100 @Override onOverscrollTopChanged(float amount, boolean isRubberbanded)4101 public void onOverscrollTopChanged(float amount, boolean isRubberbanded) { 4102 // When in split shade, overscroll shouldn't carry through to QS 4103 if (mShouldUseSplitNotificationShade) { 4104 return; 4105 } 4106 cancelQsAnimation(); 4107 if (!isQsExpansionEnabled()) { 4108 amount = 0f; 4109 } 4110 float rounded = amount >= 1f ? amount : 0f; 4111 setOverScrolling(rounded != 0f && isRubberbanded); 4112 mQsExpansionFromOverscroll = rounded != 0f; 4113 mLastOverscroll = rounded; 4114 updateQsState(); 4115 setQsExpansion(mQsMinExpansionHeight + rounded); 4116 } 4117 4118 @Override flingTopOverscroll(float velocity, boolean open)4119 public void flingTopOverscroll(float velocity, boolean open) { 4120 // in split shade mode we want to expand/collapse QS only when touch happens within QS 4121 if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(mInitialTouchX)) { 4122 return; 4123 } 4124 mLastOverscroll = 0f; 4125 mQsExpansionFromOverscroll = false; 4126 if (open) { 4127 // During overscrolling, qsExpansion doesn't actually change that the qs is 4128 // becoming expanded. Any layout could therefore reset the position again. Let's 4129 // make sure we can expand 4130 setOverScrolling(false); 4131 } 4132 setQsExpansion(mQsExpansionHeight); 4133 boolean canExpand = isQsExpansionEnabled(); 4134 flingSettings(!canExpand && open ? 0f : velocity, 4135 open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> { 4136 setOverScrolling(false); 4137 updateQsState(); 4138 }, false /* isClick */); 4139 } 4140 } 4141 4142 private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener { 4143 @Override onDynamicPrivacyChanged()4144 public void onDynamicPrivacyChanged() { 4145 // Do not request animation when pulsing or waking up, otherwise the clock wiill be out 4146 // of sync with the notification panel. 4147 if (mLinearDarkAmount != 0) { 4148 return; 4149 } 4150 mAnimateNextPositionUpdate = true; 4151 } 4152 } 4153 4154 private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback { 4155 @Override onAnimationToSideStarted(boolean rightPage, float translation, float vel)4156 public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) { 4157 boolean 4158 start = 4159 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage 4160 : !rightPage; 4161 mIsLaunchTransitionRunning = true; 4162 mLaunchAnimationEndRunnable = null; 4163 float displayDensity = mStatusBar.getDisplayDensity(); 4164 int lengthDp = Math.abs((int) (translation / displayDensity)); 4165 int velocityDp = Math.abs((int) (vel / displayDensity)); 4166 if (start) { 4167 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); 4168 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER); 4169 mFalsingCollector.onLeftAffordanceOn(); 4170 if (mFalsingCollector.shouldEnforceBouncer()) { 4171 mStatusBar.executeRunnableDismissingKeyguard( 4172 () -> mKeyguardBottomArea.launchLeftAffordance(), null, 4173 true /* dismissShade */, false /* afterKeyguardGone */, 4174 true /* deferred */); 4175 } else { 4176 mKeyguardBottomArea.launchLeftAffordance(); 4177 } 4178 } else { 4179 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals( 4180 mLastCameraLaunchSource)) { 4181 mLockscreenGestureLogger.write( 4182 MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); 4183 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA); 4184 } 4185 mFalsingCollector.onCameraOn(); 4186 if (mFalsingCollector.shouldEnforceBouncer()) { 4187 mStatusBar.executeRunnableDismissingKeyguard( 4188 () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null, 4189 true /* dismissShade */, false /* afterKeyguardGone */, 4190 true /* deferred */); 4191 } else { 4192 mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource); 4193 } 4194 } 4195 mStatusBar.startLaunchTransitionTimeout(); 4196 mBlockTouches = true; 4197 } 4198 4199 @Override onAnimationToSideEnded()4200 public void onAnimationToSideEnded() { 4201 mIsLaunchTransitionRunning = false; 4202 mIsLaunchTransitionFinished = true; 4203 if (mLaunchAnimationEndRunnable != null) { 4204 mLaunchAnimationEndRunnable.run(); 4205 mLaunchAnimationEndRunnable = null; 4206 } 4207 mStatusBar.readyForKeyguardDone(); 4208 } 4209 4210 @Override getMaxTranslationDistance()4211 public float getMaxTranslationDistance() { 4212 return (float) Math.hypot(mView.getWidth(), getHeight()); 4213 } 4214 4215 @Override onSwipingStarted(boolean rightIcon)4216 public void onSwipingStarted(boolean rightIcon) { 4217 mFalsingCollector.onAffordanceSwipingStarted(rightIcon); 4218 boolean 4219 camera = 4220 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon 4221 : rightIcon; 4222 if (camera) { 4223 mKeyguardBottomArea.bindCameraPrewarmService(); 4224 } 4225 mView.requestDisallowInterceptTouchEvent(true); 4226 mOnlyAffordanceInThisMotion = true; 4227 mQsTracking = false; 4228 } 4229 4230 @Override onSwipingAborted()4231 public void onSwipingAborted() { 4232 mFalsingCollector.onAffordanceSwipingAborted(); 4233 mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); 4234 } 4235 4236 @Override onIconClicked(boolean rightIcon)4237 public void onIconClicked(boolean rightIcon) { 4238 if (mHintAnimationRunning) { 4239 return; 4240 } 4241 mHintAnimationRunning = true; 4242 mAffordanceHelper.startHintAnimation(rightIcon, () -> { 4243 mHintAnimationRunning = false; 4244 mStatusBar.onHintFinished(); 4245 }); 4246 rightIcon = 4247 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon 4248 : rightIcon; 4249 if (rightIcon) { 4250 mStatusBar.onCameraHintStarted(); 4251 } else { 4252 if (mKeyguardBottomArea.isLeftVoiceAssist()) { 4253 mStatusBar.onVoiceAssistHintStarted(); 4254 } else { 4255 mStatusBar.onPhoneHintStarted(); 4256 } 4257 } 4258 } 4259 4260 @Override getLeftIcon()4261 public KeyguardAffordanceView getLeftIcon() { 4262 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4263 ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView(); 4264 } 4265 4266 @Override getRightIcon()4267 public KeyguardAffordanceView getRightIcon() { 4268 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4269 ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView(); 4270 } 4271 4272 @Override getLeftPreview()4273 public View getLeftPreview() { 4274 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4275 ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview(); 4276 } 4277 4278 @Override getRightPreview()4279 public View getRightPreview() { 4280 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL 4281 ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview(); 4282 } 4283 4284 @Override getAffordanceFalsingFactor()4285 public float getAffordanceFalsingFactor() { 4286 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; 4287 } 4288 4289 @Override needsAntiFalsing()4290 public boolean needsAntiFalsing() { 4291 return mBarState == KEYGUARD; 4292 } 4293 } 4294 4295 private class OnEmptySpaceClickListener implements 4296 NotificationStackScrollLayout.OnEmptySpaceClickListener { 4297 @Override onEmptySpaceClicked(float x, float y)4298 public void onEmptySpaceClicked(float x, float y) { 4299 onEmptySpaceClick(x); 4300 } 4301 } 4302 4303 private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener { 4304 @Override onHeadsUpPinnedModeChanged(final boolean inPinnedMode)4305 public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) { 4306 if (inPinnedMode) { 4307 mHeadsUpExistenceChangedRunnable.run(); 4308 updateNotificationTranslucency(); 4309 } else { 4310 setHeadsUpAnimatingAway(true); 4311 mNotificationStackScrollLayoutController.runAfterAnimationFinished( 4312 mHeadsUpExistenceChangedRunnable); 4313 } 4314 updateGestureExclusionRect(); 4315 mHeadsUpPinnedMode = inPinnedMode; 4316 updateVisibility(); 4317 mKeyguardStatusBarViewController.updateForHeadsUp(); 4318 } 4319 4320 @Override onHeadsUpPinned(NotificationEntry entry)4321 public void onHeadsUpPinned(NotificationEntry entry) { 4322 if (!isOnKeyguard()) { 4323 mNotificationStackScrollLayoutController.generateHeadsUpAnimation( 4324 entry.getHeadsUpAnimationView(), true); 4325 } 4326 } 4327 4328 @Override onHeadsUpUnPinned(NotificationEntry entry)4329 public void onHeadsUpUnPinned(NotificationEntry entry) { 4330 4331 // When we're unpinning the notification via active edge they remain heads-upped, 4332 // we need to make sure that an animation happens in this case, otherwise the 4333 // notification 4334 // will stick to the top without any interaction. 4335 if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) { 4336 mNotificationStackScrollLayoutController.generateHeadsUpAnimation( 4337 entry.getHeadsUpAnimationView(), false); 4338 entry.setHeadsUpIsVisible(); 4339 } 4340 } 4341 } 4342 4343 private class HeightListener implements QS.HeightListener { onQsHeightChanged()4344 public void onQsHeightChanged() { 4345 mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0; 4346 if (mQsExpanded && mQsFullyExpanded) { 4347 mQsExpansionHeight = mQsMaxExpansionHeight; 4348 requestScrollerTopPaddingUpdate(false /* animate */); 4349 requestPanelHeightUpdate(); 4350 } 4351 if (mAccessibilityManager.isEnabled()) { 4352 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); 4353 } 4354 mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight); 4355 } 4356 } 4357 4358 private class ConfigurationListener implements ConfigurationController.ConfigurationListener { 4359 @Override onThemeChanged()4360 public void onThemeChanged() { 4361 if (DEBUG) Log.d(TAG, "onThemeChanged"); 4362 mThemeResId = mView.getContext().getThemeResId(); 4363 reInflateViews(); 4364 } 4365 4366 @Override onSmallestScreenWidthChanged()4367 public void onSmallestScreenWidthChanged() { 4368 if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged"); 4369 4370 // Can affect multi-user switcher visibility as it depends on screen size by default: 4371 // it is enabled only for devices with large screens (see config_keyguardUserSwitcher) 4372 reInflateViews(); 4373 } 4374 4375 @Override onDensityOrFontScaleChanged()4376 public void onDensityOrFontScaleChanged() { 4377 if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged"); 4378 reInflateViews(); 4379 } 4380 } 4381 4382 private class SettingsChangeObserver extends ContentObserver { 4383 SettingsChangeObserver(Handler handler)4384 SettingsChangeObserver(Handler handler) { 4385 super(handler); 4386 } 4387 4388 @Override onChange(boolean selfChange)4389 public void onChange(boolean selfChange) { 4390 if (DEBUG) Log.d(TAG, "onSettingsChanged"); 4391 4392 // Can affect multi-user switcher visibility 4393 reInflateViews(); 4394 } 4395 } 4396 4397 private class StatusBarStateListener implements StateListener { 4398 @Override onStateChanged(int statusBarState)4399 public void onStateChanged(int statusBarState) { 4400 boolean goingToFullShade = mStatusBarStateController.goingToFullShade(); 4401 boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway(); 4402 int oldState = mBarState; 4403 boolean keyguardShowing = statusBarState == KEYGUARD; 4404 4405 if (mDozeParameters.shouldControlUnlockedScreenOff() 4406 && oldState == StatusBarState.SHADE 4407 && statusBarState == KEYGUARD) { 4408 // This means we're doing the screen off animation - position the keyguard status 4409 // view where it'll be on AOD, so we can animate it in. 4410 mKeyguardStatusViewController.updatePosition( 4411 mClockPositionResult.clockX, 4412 mClockPositionResult.clockYFullyDozing, 4413 mClockPositionResult.clockScale, 4414 false /* animate */); 4415 } 4416 4417 mKeyguardStatusViewController.setKeyguardStatusViewVisibility( 4418 statusBarState, 4419 keyguardFadingAway, 4420 goingToFullShade, 4421 mBarState); 4422 setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); 4423 4424 mBarState = statusBarState; 4425 mKeyguardShowing = keyguardShowing; 4426 4427 if (oldState == KEYGUARD && (goingToFullShade 4428 || statusBarState == StatusBarState.SHADE_LOCKED)) { 4429 4430 long startDelay; 4431 long duration; 4432 if (mKeyguardStateController.isKeyguardFadingAway()) { 4433 startDelay = mKeyguardStateController.getKeyguardFadingAwayDelay(); 4434 duration = mKeyguardStateController.getShortenedFadingAwayDuration(); 4435 } else { 4436 startDelay = 0; 4437 duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; 4438 } 4439 mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration); 4440 updateQSMinHeight(); 4441 } else if (oldState == StatusBarState.SHADE_LOCKED 4442 && statusBarState == KEYGUARD) { 4443 mKeyguardStatusBarViewController.animateKeyguardStatusBarIn(); 4444 4445 mNotificationStackScrollLayoutController.resetScrollPosition(); 4446 // Only animate header if the header is visible. If not, it will partially 4447 // animate out 4448 // the top of QS 4449 if (!mQsExpanded) { 4450 // TODO(b/185683835) Nicer clipping when using new spacial model 4451 if (mShouldUseSplitNotificationShade) { 4452 mQs.animateHeaderSlidingOut(); 4453 } 4454 } 4455 } else { 4456 final boolean animatingUnlockedShadeToKeyguard = oldState == SHADE 4457 && statusBarState == KEYGUARD 4458 && mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying(); 4459 if (!animatingUnlockedShadeToKeyguard) { 4460 // Only make the status bar visible if we're not animating the screen off, since 4461 // we only want to be showing the clock/notifications during the animation. 4462 mKeyguardStatusBarViewController.updateViewState( 4463 /* alpha= */ 1f, 4464 keyguardShowing ? View.VISIBLE : View.INVISIBLE); 4465 } 4466 if (keyguardShowing && oldState != mBarState) { 4467 if (mQs != null) { 4468 mQs.hideImmediately(); 4469 } 4470 } 4471 } 4472 mKeyguardStatusBarViewController.updateForHeadsUp(); 4473 if (keyguardShowing) { 4474 updateDozingVisibilities(false /* animate */); 4475 } 4476 4477 updateMaxDisplayedNotifications(false); 4478 // The update needs to happen after the headerSlide in above, otherwise the translation 4479 // would reset 4480 maybeAnimateBottomAreaAlpha(); 4481 updateQsState(); 4482 mSplitShadeHeaderController.setShadeExpanded( 4483 mBarState == SHADE || mBarState == SHADE_LOCKED); 4484 } 4485 4486 @Override onDozeAmountChanged(float linearAmount, float amount)4487 public void onDozeAmountChanged(float linearAmount, float amount) { 4488 mInterpolatedDarkAmount = amount; 4489 mLinearDarkAmount = linearAmount; 4490 mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount); 4491 mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); 4492 positionClockAndNotifications(); 4493 } 4494 } 4495 4496 /** 4497 * An interface that provides the current state of the notification panel and related views, 4498 * which is needed to calculate {@link KeyguardStatusBarView}'s state in 4499 * {@link KeyguardStatusBarViewController}. 4500 */ 4501 public interface NotificationPanelViewStateProvider { 4502 /** Returns the expanded height of the panel view. */ getPanelViewExpandedHeight()4503 float getPanelViewExpandedHeight(); 4504 /** Returns the fraction of QS that's expanded. */ getQsExpansionFraction()4505 float getQsExpansionFraction(); 4506 /** 4507 * Returns true if heads up should be visible. 4508 * 4509 * TODO(b/138786270): If HeadsUpAppearanceController was injectable, we could inject it into 4510 * {@link KeyguardStatusBarViewController} and remove this method. 4511 */ shouldHeadsUpBeVisible()4512 boolean shouldHeadsUpBeVisible(); 4513 } 4514 4515 private final NotificationPanelViewStateProvider mNotificationPanelViewStateProvider = 4516 new NotificationPanelViewStateProvider() { 4517 @Override 4518 public float getPanelViewExpandedHeight() { 4519 return getExpandedHeight(); 4520 } 4521 4522 @Override 4523 public float getQsExpansionFraction() { 4524 return computeQsExpansionFraction(); 4525 } 4526 4527 @Override 4528 public boolean shouldHeadsUpBeVisible() { 4529 return mHeadsUpAppearanceController.shouldBeVisible(); 4530 } 4531 }; 4532 4533 /** 4534 * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the 4535 * screen off animation controller in order to animate in AOD without "actually" fully switching 4536 * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the 4537 * change. 4538 */ showAodUi()4539 public void showAodUi() { 4540 setDozing(true /* dozing */, false /* animate */, null); 4541 mStatusBarStateController.setUpcomingState(KEYGUARD); 4542 mEntryManager.updateNotifications("showAodUi"); 4543 mStatusBarStateListener.onStateChanged(KEYGUARD); 4544 mStatusBarStateListener.onDozeAmountChanged(1f, 1f); 4545 setExpandedFraction(1f); 4546 } 4547 4548 /** 4549 * Sets the overstretch amount in raw pixels when dragging down. 4550 */ setOverStrechAmount(float amount)4551 public void setOverStrechAmount(float amount) { 4552 float progress = amount / mView.getHeight(); 4553 float overstretch = Interpolators.getOvershootInterpolation(progress); 4554 mOverStretchAmount = overstretch * mMaxOverscrollAmountForPulse; 4555 positionClockAndNotifications(true /* forceUpdate */); 4556 } 4557 4558 private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { 4559 @Override onViewAttachedToWindow(View v)4560 public void onViewAttachedToWindow(View v) { 4561 mFragmentService.getFragmentHostManager(mView) 4562 .addTagListener(QS.TAG, mFragmentListener); 4563 mStatusBarStateController.addCallback(mStatusBarStateListener); 4564 mConfigurationController.addCallback(mConfigurationListener); 4565 // Theme might have changed between inflating this view and attaching it to the 4566 // window, so 4567 // force a call to onThemeChanged 4568 mConfigurationListener.onThemeChanged(); 4569 mFalsingManager.addTapListener(mFalsingTapListener); 4570 mKeyguardIndicationController.init(); 4571 registerSettingsChangeListener(); 4572 } 4573 4574 @Override onViewDetachedFromWindow(View v)4575 public void onViewDetachedFromWindow(View v) { 4576 unregisterSettingsChangeListener(); 4577 mFragmentService.getFragmentHostManager(mView) 4578 .removeTagListener(QS.TAG, mFragmentListener); 4579 mStatusBarStateController.removeCallback(mStatusBarStateListener); 4580 mConfigurationController.removeCallback(mConfigurationListener); 4581 mFalsingManager.removeTapListener(mFalsingTapListener); 4582 } 4583 } 4584 4585 private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener { 4586 4587 @Override onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)4588 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, 4589 int oldTop, int oldRight, int oldBottom) { 4590 DejankUtils.startDetectingBlockingIpcs("NVP#onLayout"); 4591 super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom); 4592 updateMaxDisplayedNotifications(true); 4593 setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); 4594 4595 // Update Clock Pivot 4596 mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2); 4597 mKeyguardStatusViewController.setPivotY( 4598 (FONT_HEIGHT - CAP_HEIGHT) / 2048f 4599 * mKeyguardStatusViewController.getClockTextSize()); 4600 4601 // Calculate quick setting heights. 4602 int oldMaxHeight = mQsMaxExpansionHeight; 4603 if (mQs != null) { 4604 updateQSMinHeight(); 4605 mQsMaxExpansionHeight = mQs.getDesiredHeight(); 4606 mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight); 4607 } 4608 positionClockAndNotifications(); 4609 if (mQsExpanded && mQsFullyExpanded) { 4610 mQsExpansionHeight = mQsMaxExpansionHeight; 4611 requestScrollerTopPaddingUpdate(false /* animate */); 4612 requestPanelHeightUpdate(); 4613 4614 // Size has changed, start an animation. 4615 if (mQsMaxExpansionHeight != oldMaxHeight) { 4616 startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight); 4617 } 4618 } else if (!mQsExpanded && mQsExpansionAnimator == null) { 4619 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll); 4620 } 4621 updateExpandedHeight(getExpandedHeight()); 4622 updateHeader(); 4623 4624 // If we are running a size change animation, the animation takes care of the height of 4625 // the container. However, if we are not animating, we always need to make the QS 4626 // container 4627 // the desired height so when closing the QS detail, it stays smaller after the size 4628 // change 4629 // animation is finished but the detail view is still being animated away (this 4630 // animation 4631 // takes longer than the size change animation). 4632 if (mQsSizeChangeAnimator == null && mQs != null) { 4633 mQs.setHeightOverride(mQs.getDesiredHeight()); 4634 } 4635 updateMaxHeadsUpTranslation(); 4636 updateGestureExclusionRect(); 4637 if (mExpandAfterLayoutRunnable != null) { 4638 mExpandAfterLayoutRunnable.run(); 4639 mExpandAfterLayoutRunnable = null; 4640 } 4641 DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout"); 4642 } 4643 } 4644 updateQSMinHeight()4645 private void updateQSMinHeight() { 4646 float previousMin = mQsMinExpansionHeight; 4647 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); 4648 if (mQsExpansionHeight == previousMin) { 4649 mQsExpansionHeight = mQsMinExpansionHeight; 4650 } 4651 } 4652 4653 private class DebugDrawable extends Drawable { 4654 4655 @Override draw(Canvas canvas)4656 public void draw(Canvas canvas) { 4657 Paint p = new Paint(); 4658 p.setColor(Color.RED); 4659 p.setStrokeWidth(2); 4660 p.setStyle(Paint.Style.STROKE); 4661 canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p); 4662 p.setTextSize(24); 4663 if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p); 4664 p.setColor(Color.BLUE); 4665 canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p); 4666 p.setColor(Color.GREEN); 4667 canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(), 4668 calculatePanelHeightQsExpanded(), p); 4669 p.setColor(Color.YELLOW); 4670 canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(), 4671 calculatePanelHeightShade(), p); 4672 p.setColor(Color.MAGENTA); 4673 canvas.drawLine( 4674 0, calculateNotificationsTopPadding(), mView.getWidth(), 4675 calculateNotificationsTopPadding(), p); 4676 p.setColor(Color.CYAN); 4677 canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), 4678 mNotificationStackScrollLayoutController.getTopPadding(), p); 4679 p.setColor(Color.GRAY); 4680 canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(), 4681 mClockPositionResult.clockY, p); 4682 } 4683 4684 @Override setAlpha(int alpha)4685 public void setAlpha(int alpha) { 4686 4687 } 4688 4689 @Override setColorFilter(ColorFilter colorFilter)4690 public void setColorFilter(ColorFilter colorFilter) { 4691 4692 } 4693 4694 @Override getOpacity()4695 public int getOpacity() { 4696 return 0; 4697 } 4698 } 4699 4700 private class OnConfigurationChangedListener extends 4701 PanelViewController.OnConfigurationChangedListener { 4702 @Override onConfigurationChanged(Configuration newConfig)4703 public void onConfigurationChanged(Configuration newConfig) { 4704 super.onConfigurationChanged(newConfig); 4705 mAffordanceHelper.onConfigurationChanged(); 4706 mLastOrientation = newConfig.orientation; 4707 } 4708 } 4709 4710 private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener { onApplyWindowInsets(View v, WindowInsets insets)4711 public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { 4712 // the same types of insets that are handled in NotificationShadeWindowView 4713 int insetTypes = WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout(); 4714 Insets combinedInsets = insets.getInsetsIgnoringVisibility(insetTypes); 4715 mDisplayTopInset = combinedInsets.top; 4716 mDisplayRightInset = combinedInsets.right; 4717 4718 mNavigationBarBottomHeight = insets.getStableInsetBottom(); 4719 updateMaxHeadsUpTranslation(); 4720 return insets; 4721 } 4722 } 4723 4724 /** Removes any pending runnables that would collapse the panel. */ cancelPendingPanelCollapse()4725 public void cancelPendingPanelCollapse() { 4726 mView.removeCallbacks(mMaybeHideExpandedRunnable); 4727 } 4728 4729 @PanelState 4730 private int mCurrentPanelState = STATE_CLOSED; 4731 onPanelStateChanged(@anelState int state)4732 private void onPanelStateChanged(@PanelState int state) { 4733 mAmbientState.setIsShadeOpening(state == STATE_OPENING); 4734 updateQSExpansionEnabledAmbient(); 4735 4736 if (state == STATE_OPEN && mCurrentPanelState != state) { 4737 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 4738 } 4739 if (state == STATE_OPENING) { 4740 mStatusBar.makeExpandedVisible(false); 4741 } 4742 if (state == STATE_CLOSED) { 4743 // Close the status bar in the next frame so we can show the end of the 4744 // animation. 4745 mView.post(mMaybeHideExpandedRunnable); 4746 } 4747 mCurrentPanelState = state; 4748 } 4749 4750 /** Returns the handler that the status bar should forward touches to. */ getStatusBarTouchEventHandler()4751 public PhoneStatusBarView.TouchEventHandler getStatusBarTouchEventHandler() { 4752 return mStatusBarViewTouchEventHandler; 4753 } 4754 } 4755