blob: 7d09dcacfb466aa8c3c0b5d175f9ff2a39445eea [file] [log] [blame]
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001/*
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
17package com.android.systemui.statusbar.phone;
18
19import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
20import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
21
22import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
24import android.animation.ValueAnimator;
25import android.app.ActivityManager;
26import android.app.Fragment;
27import android.app.StatusBarManager;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050028import android.content.pm.ResolveInfo;
29import android.content.res.Configuration;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050030import android.graphics.Canvas;
31import android.graphics.Color;
32import android.graphics.ColorFilter;
33import android.graphics.Paint;
34import android.graphics.PointF;
35import android.graphics.Rect;
36import android.graphics.Region;
37import android.graphics.drawable.Drawable;
38import android.hardware.biometrics.BiometricSourceType;
39import android.os.PowerManager;
40import android.os.SystemClock;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050041import android.util.Log;
42import android.util.MathUtils;
43import android.view.LayoutInflater;
44import android.view.MotionEvent;
45import android.view.VelocityTracker;
46import android.view.View;
47import android.view.ViewGroup;
48import android.view.ViewPropertyAnimator;
49import android.view.ViewTreeObserver;
50import android.view.WindowInsets;
51import android.view.accessibility.AccessibilityManager;
52import android.widget.FrameLayout;
53import android.widget.TextView;
54
55import com.android.internal.annotations.VisibleForTesting;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050056import com.android.internal.logging.MetricsLogger;
57import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
58import com.android.internal.util.LatencyTracker;
59import com.android.keyguard.KeyguardClockSwitch;
60import com.android.keyguard.KeyguardStatusView;
61import com.android.keyguard.KeyguardUpdateMonitor;
62import com.android.keyguard.KeyguardUpdateMonitorCallback;
63import com.android.systemui.DejankUtils;
64import com.android.systemui.Interpolators;
65import com.android.systemui.R;
66import com.android.systemui.dagger.qualifiers.DisplayId;
67import com.android.systemui.doze.DozeLog;
68import com.android.systemui.fragments.FragmentHostManager;
69import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
70import com.android.systemui.plugins.FalsingManager;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050071import com.android.systemui.plugins.qs.QS;
72import com.android.systemui.plugins.statusbar.StatusBarStateController;
73import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
74import com.android.systemui.qs.QSFragment;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050075import com.android.systemui.statusbar.CommandQueue;
76import com.android.systemui.statusbar.FlingAnimationUtils;
77import com.android.systemui.statusbar.GestureRecorder;
78import com.android.systemui.statusbar.KeyguardAffordanceView;
79import com.android.systemui.statusbar.KeyguardIndicationController;
80import com.android.systemui.statusbar.NotificationLockscreenUserManager;
81import com.android.systemui.statusbar.NotificationShelf;
82import com.android.systemui.statusbar.PulseExpansionHandler;
83import com.android.systemui.statusbar.RemoteInputController;
84import com.android.systemui.statusbar.StatusBarState;
85import com.android.systemui.statusbar.SysuiStatusBarStateController;
86import com.android.systemui.statusbar.VibratorHelper;
87import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
88import com.android.systemui.statusbar.notification.AnimatableProperty;
89import com.android.systemui.statusbar.notification.DynamicPrivacyController;
90import com.android.systemui.statusbar.notification.NotificationEntryManager;
91import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
92import com.android.systemui.statusbar.notification.PropertyAnimator;
Steve Elliott239e6cb2020-03-25 14:26:42 -040093import com.android.systemui.statusbar.notification.UnreadConversationBadgeManager;
Dave Mankoffaf8163f2020-01-08 14:24:35 -050094import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
95import com.android.systemui.statusbar.notification.collection.NotificationEntry;
96import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
97import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
98import com.android.systemui.statusbar.notification.row.ExpandableView;
99import com.android.systemui.statusbar.notification.stack.AnimationProperties;
100import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
101import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
102import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
103import com.android.systemui.statusbar.policy.ConfigurationController;
104import com.android.systemui.statusbar.policy.KeyguardStateController;
105import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
106import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
107import com.android.systemui.statusbar.policy.ZenModeController;
108import com.android.systemui.util.InjectionInflationController;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500109
110import java.io.FileDescriptor;
111import java.io.PrintWriter;
112import java.util.ArrayList;
113import java.util.Collections;
114import java.util.List;
115import java.util.function.Consumer;
116import java.util.function.Function;
117
118import javax.inject.Inject;
119
120@StatusBarComponent.StatusBarScope
121public class NotificationPanelViewController extends PanelViewController {
122
123 private static final boolean DEBUG = false;
124
125 /**
126 * Fling expanding QS.
127 */
128 private static final int FLING_EXPAND = 0;
129
130 /**
131 * Fling collapsing QS, potentially stopping when QS becomes QQS.
132 */
133 private static final int FLING_COLLAPSE = 1;
134
135 /**
136 * Fling until QS is completely hidden.
137 */
138 private static final int FLING_HIDE = 2;
139 private final DozeParameters mDozeParameters;
140 private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
141 private final OnClickListener mOnClickListener = new OnClickListener();
142 private final OnOverscrollTopChangedListener
143 mOnOverscrollTopChangedListener =
144 new OnOverscrollTopChangedListener();
145 private final KeyguardAffordanceHelperCallback
146 mKeyguardAffordanceHelperCallback =
147 new KeyguardAffordanceHelperCallback();
148 private final OnEmptySpaceClickListener
149 mOnEmptySpaceClickListener =
150 new OnEmptySpaceClickListener();
151 private final MyOnHeadsUpChangedListener
152 mOnHeadsUpChangedListener =
153 new MyOnHeadsUpChangedListener();
154 private final HeightListener mHeightListener = new HeightListener();
155 private final ZenModeControllerCallback
156 mZenModeControllerCallback =
157 new ZenModeControllerCallback();
158 private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
159 private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
160 private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
161 private final NotificationPanelView mView;
162 private final MetricsLogger mMetricsLogger;
163 private final ActivityManager mActivityManager;
164 private final ZenModeController mZenModeController;
165 private final ConfigurationController mConfigurationController;
166 private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
167
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500168 // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
169 // changed.
170 private static final int CAP_HEIGHT = 1456;
171 private static final int FONT_HEIGHT = 2163;
172
173 /**
174 * Maximum time before which we will expand the panel even for slow motions when getting a
175 * touch passed over from launcher.
176 */
177 private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
178
179 private static final String COUNTER_PANEL_OPEN = "panel_open";
180 private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
181 private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
182
183 private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
184 private static final Rect EMPTY_RECT = new Rect();
185
186 private static final AnimationProperties
187 CLOCK_ANIMATION_PROPERTIES =
188 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
189 private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
190 "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
191 (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
192 (Function<NotificationPanelView, Float>) notificationPanelView ->
193 getKeyguardHeadsUpShowingAmount(),
194 R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
195 R.id.keyguard_hun_animator_start_tag);
196 private static final AnimationProperties
197 KEYGUARD_HUN_PROPERTIES =
198 new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
199 @VisibleForTesting
200 final KeyguardUpdateMonitorCallback
201 mKeyguardUpdateCallback =
202 new KeyguardUpdateMonitorCallback() {
203
204 @Override
205 public void onBiometricAuthenticated(int userId,
Haining Chenc06c4812020-01-13 20:38:53 -0800206 BiometricSourceType biometricSourceType,
207 boolean isStrongBiometric) {
208 if (mFirstBypassAttempt
209 && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500210 mDelayShowingKeyguardStatusBar = true;
211 }
212 }
213
214 @Override
215 public void onBiometricRunningStateChanged(boolean running,
216 BiometricSourceType biometricSourceType) {
217 boolean
218 keyguardOrShadeLocked =
219 mBarState == StatusBarState.KEYGUARD
220 || mBarState == StatusBarState.SHADE_LOCKED;
221 if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
222 && !mDelayShowingKeyguardStatusBar) {
223 mFirstBypassAttempt = false;
224 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
225 }
226 }
227
228 @Override
229 public void onFinishedGoingToSleep(int why) {
230 mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
231 mDelayShowingKeyguardStatusBar = false;
232 }
233 };
234
235 private final InjectionInflationController mInjectionInflationController;
236 private final PowerManager mPowerManager;
237 private final AccessibilityManager mAccessibilityManager;
238 private final NotificationWakeUpCoordinator mWakeUpCoordinator;
239 private final PulseExpansionHandler mPulseExpansionHandler;
240 private final KeyguardBypassController mKeyguardBypassController;
241 private final KeyguardUpdateMonitor mUpdateMonitor;
Steve Elliott239e6cb2020-03-25 14:26:42 -0400242 private final UnreadConversationBadgeManager mUnreadConversationBadgeManager;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500243
244 private KeyguardAffordanceHelper mAffordanceHelper;
245 private KeyguardUserSwitcher mKeyguardUserSwitcher;
246 private KeyguardStatusBarView mKeyguardStatusBar;
247 private ViewGroup mBigClockContainer;
248 private QS mQs;
249 private FrameLayout mQsFrame;
250 private KeyguardStatusView mKeyguardStatusView;
251 private View mQsNavbarScrim;
252 private NotificationsQuickSettingsContainer mNotificationContainerParent;
253 private NotificationStackScrollLayout mNotificationStackScroller;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500254 private boolean mAnimateNextPositionUpdate;
255
256 private int mTrackingPointer;
257 private VelocityTracker mQsVelocityTracker;
258 private boolean mQsTracking;
259
260 /**
261 * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
262 * the expansion for quick settings.
263 */
264 private boolean mConflictingQsExpansionGesture;
265
266 private boolean mPanelExpanded;
267 private boolean mQsExpanded;
268 private boolean mQsExpandedWhenExpandingStarted;
269 private boolean mQsFullyExpanded;
270 private boolean mKeyguardShowing;
271 private boolean mDozing;
272 private boolean mDozingOnDown;
273 private int mBarState;
274 private float mInitialHeightOnTouch;
275 private float mInitialTouchX;
276 private float mInitialTouchY;
277 private float mQsExpansionHeight;
278 private int mQsMinExpansionHeight;
279 private int mQsMaxExpansionHeight;
280 private int mQsPeekHeight;
281 private boolean mStackScrollerOverscrolling;
282 private boolean mQsExpansionFromOverscroll;
283 private float mLastOverscroll;
284 private boolean mQsExpansionEnabled = true;
285 private ValueAnimator mQsExpansionAnimator;
286 private FlingAnimationUtils mFlingAnimationUtils;
287 private int mStatusBarMinHeight;
288 private int mNotificationsHeaderCollideDistance;
289 private float mEmptyDragAmount;
290 private float mDownX;
291 private float mDownY;
292
293 private final KeyguardClockPositionAlgorithm
294 mClockPositionAlgorithm =
295 new KeyguardClockPositionAlgorithm();
296 private final KeyguardClockPositionAlgorithm.Result
297 mClockPositionResult =
298 new KeyguardClockPositionAlgorithm.Result();
299 private boolean mIsExpanding;
300
301 private boolean mBlockTouches;
302 // Used for two finger gesture as well as accessibility shortcut to QS.
303 private boolean mQsExpandImmediate;
304 private boolean mTwoFingerQsExpandPossible;
305
306 /**
307 * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
308 * need to take this into account in our panel height calculation.
309 */
310 private boolean mQsAnimatorExpand;
311 private boolean mIsLaunchTransitionFinished;
312 private boolean mIsLaunchTransitionRunning;
313 private Runnable mLaunchAnimationEndRunnable;
314 private boolean mOnlyAffordanceInThisMotion;
315 private boolean mKeyguardStatusViewAnimating;
316 private ValueAnimator mQsSizeChangeAnimator;
317
318 private boolean mShowEmptyShadeView;
319
320 private boolean mQsScrimEnabled = true;
321 private boolean mQsTouchAboveFalsingThreshold;
322 private int mQsFalsingThreshold;
323
324 private float mKeyguardStatusBarAnimateAlpha = 1f;
325 private HeadsUpTouchHelper mHeadsUpTouchHelper;
326 private boolean mListenForHeadsUp;
327 private int mNavigationBarBottomHeight;
328 private boolean mExpandingFromHeadsUp;
329 private boolean mCollapsedOnDown;
330 private int mPositionMinSideMargin;
331 private int mLastOrientation = -1;
332 private boolean mClosingWithAlphaFadeOut;
333 private boolean mHeadsUpAnimatingAway;
334 private boolean mLaunchingAffordance;
335 private boolean mAffordanceHasPreview;
336 private FalsingManager mFalsingManager;
337 private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
338
339 private Runnable mHeadsUpExistenceChangedRunnable = () -> {
340 setHeadsUpAnimatingAway(false);
341 notifyBarPanelExpansionChanged();
342 };
343 private NotificationGroupManager mGroupManager;
344 private boolean mShowIconsWhenExpanded;
345 private int mIndicationBottomPadding;
346 private int mAmbientIndicationBottomPadding;
347 private boolean mIsFullWidth;
348 private boolean mBlockingExpansionForCurrentTouch;
349
350 /**
351 * Following variables maintain state of events when input focus transfer may occur.
352 */
353 private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
354 private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
355
356 /**
357 * Current dark amount that follows regular interpolation curve of animation.
358 */
359 private float mInterpolatedDarkAmount;
360
361 /**
362 * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
363 * interpolation curve is different.
364 */
365 private float mLinearDarkAmount;
366
367 private boolean mPulsing;
368 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
369 private boolean mUserSetupComplete;
370 private int mQsNotificationTopPadding;
371 private float mExpandOffset;
372 private boolean mHideIconsDuringNotificationLaunch = true;
373 private int mStackScrollerMeasuringPass;
374 private ArrayList<Consumer<ExpandableNotificationRow>>
375 mTrackingHeadsUpListeners =
376 new ArrayList<>();
377 private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
378 private HeadsUpAppearanceController mHeadsUpAppearanceController;
379
380 private int mPanelAlpha;
381 private Runnable mPanelAlphaEndAction;
382 private float mBottomAreaShadeAlpha;
383 private final ValueAnimator mBottomAreaShadeAlphaAnimator;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500384 private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
385 NotificationPanelView::setPanelAlphaInternal,
386 NotificationPanelView::getCurrentPanelAlpha,
387 R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
388 R.id.panel_alpha_animator_end_tag);
389 private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
390 new AnimationProperties().setDuration(150).setCustomInterpolator(
391 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
392 private final AnimationProperties mPanelAlphaInPropertiesAnimator =
Selim Cinekfa8b4182020-03-27 16:37:34 -0700393 new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
394 if (mPanelAlphaEndAction != null) {
395 mPanelAlphaEndAction.run();
396 }
397 }).setCustomInterpolator(
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500398 mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
399 private final NotificationEntryManager mEntryManager;
400
401 private final CommandQueue mCommandQueue;
402 private final NotificationLockscreenUserManager mLockscreenUserManager;
403 private final ShadeController mShadeController;
404 private int mDisplayId;
405
406 /**
407 * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
408 *
409 * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
410 * work, check the current id with the cached id.
411 */
412 private int mThemeResId;
413 private KeyguardIndicationController mKeyguardIndicationController;
414 private Consumer<Boolean> mAffordanceLaunchListener;
415 private int mShelfHeight;
416 private Runnable mOnReinflationListener;
417 private int mDarkIconSize;
418 private int mHeadsUpInset;
419 private boolean mHeadsUpPinnedMode;
420 private float mKeyguardHeadsUpShowingAmount = 0.0f;
421 private boolean mShowingKeyguardHeadsUp;
422 private boolean mAllowExpandForSmallExpansion;
423 private Runnable mExpandAfterLayoutRunnable;
424
425 /**
426 * If face auth with bypass is running for the first time after you turn on the screen.
427 * (From aod or screen off)
428 */
429 private boolean mFirstBypassAttempt;
430 /**
431 * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
432 * the keyguard is dismissed to show the status bar.
433 */
434 private boolean mDelayShowingKeyguardStatusBar;
435
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500436 private int mOldLayoutDirection;
437
438 @Inject
439 public NotificationPanelViewController(NotificationPanelView view,
440 InjectionInflationController injectionInflationController,
441 NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
442 DynamicPrivacyController dynamicPrivacyController,
443 KeyguardBypassController bypassController, FalsingManager falsingManager,
Matt Pietalf07bac42020-01-29 15:07:48 -0500444 ShadeController shadeController,
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500445 NotificationLockscreenUserManager notificationLockscreenUserManager,
446 NotificationEntryManager notificationEntryManager,
447 KeyguardStateController keyguardStateController,
448 StatusBarStateController statusBarStateController, DozeLog dozeLog,
449 DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
450 LatencyTracker latencyTracker, PowerManager powerManager,
451 AccessibilityManager accessibilityManager, @DisplayId int displayId,
452 KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
453 ActivityManager activityManager, ZenModeController zenModeController,
454 ConfigurationController configurationController,
Beverly95a0802ac2020-02-10 15:27:40 -0500455 FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
Steve Elliott239e6cb2020-03-25 14:26:42 -0400456 StatusBarTouchableRegionManager statusBarTouchableRegionManager,
457 UnreadConversationBadgeManager unreadConversationBadgeManager) {
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500458 super(view, falsingManager, dozeLog, keyguardStateController,
459 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
Beverly95a0802ac2020-02-10 15:27:40 -0500460 latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500461 mView = view;
462 mMetricsLogger = metricsLogger;
463 mActivityManager = activityManager;
464 mZenModeController = zenModeController;
465 mConfigurationController = configurationController;
466 mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
467 mView.setWillNotDraw(!DEBUG);
468 mInjectionInflationController = injectionInflationController;
469 mFalsingManager = falsingManager;
470 mPowerManager = powerManager;
471 mWakeUpCoordinator = coordinator;
472 mAccessibilityManager = accessibilityManager;
473 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
474 setPanelAlpha(255, false /* animate */);
475 mCommandQueue = commandQueue;
476 mDisplayId = displayId;
477 mPulseExpansionHandler = pulseExpansionHandler;
478 mDozeParameters = dozeParameters;
479 pulseExpansionHandler.setPulseExpandAbortListener(() -> {
480 if (mQs != null) {
481 mQs.animateHeaderSlidingOut();
482 }
483 });
484 mThemeResId = mView.getContext().getThemeResId();
485 mKeyguardBypassController = bypassController;
486 mUpdateMonitor = keyguardUpdateMonitor;
487 mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
488 KeyguardStateController.Callback
489 keyguardMonitorCallback =
490 new KeyguardStateController.Callback() {
491 @Override
492 public void onKeyguardFadingAwayChanged() {
493 if (!mKeyguardStateController.isKeyguardFadingAway()) {
494 mFirstBypassAttempt = false;
495 mDelayShowingKeyguardStatusBar = false;
496 }
497 }
498 };
499 mKeyguardStateController.addCallback(keyguardMonitorCallback);
500 DynamicPrivacyControlListener
501 dynamicPrivacyControlListener =
502 new DynamicPrivacyControlListener();
503 dynamicPrivacyController.addListener(dynamicPrivacyControlListener);
504
505 mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
506 mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
507 mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
508 updateKeyguardBottomAreaAlpha();
509 });
510 mBottomAreaShadeAlphaAnimator.setDuration(160);
511 mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500512 mShadeController = shadeController;
513 mLockscreenUserManager = notificationLockscreenUserManager;
514 mEntryManager = notificationEntryManager;
Steve Elliott239e6cb2020-03-25 14:26:42 -0400515 mUnreadConversationBadgeManager = unreadConversationBadgeManager;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500516
517 mView.setBackgroundColor(Color.TRANSPARENT);
518 OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
519 mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
520 if (mView.isAttachedToWindow()) {
521 onAttachStateChangeListener.onViewAttachedToWindow(mView);
522 }
523
524 mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener());
525
526 if (DEBUG) {
527 mView.getOverlay().add(new DebugDrawable());
528 }
529
530 onFinishInflate();
531 }
532
533 private void onFinishInflate() {
534 loadDimens();
535 mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
536 mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
537
538 KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
539 mBigClockContainer = mView.findViewById(R.id.big_clock_container);
540 keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
541
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500542 mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
543 mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
544 mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
545 mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener);
546 mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener);
547 addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
548 mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
549 mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim);
550 mLastOrientation = mResources.getConfiguration().orientation;
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500551
552 initBottomArea();
553
554 mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
555 mQsFrame = mView.findViewById(R.id.qs_frame);
556 mPulseExpansionHandler.setUp(
557 mNotificationStackScroller, mExpansionCallback, mShadeController);
558 mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
559 @Override
560 public void onFullyHiddenChanged(boolean isFullyHidden) {
561 updateKeyguardStatusBarForHeadsUp();
562 }
563
564 @Override
565 public void onPulseExpansionChanged(boolean expandingChanged) {
566 if (mKeyguardBypassController.getBypassEnabled()) {
567 // Position the notifications while dragging down while pulsing
568 requestScrollerTopPaddingUpdate(false /* animate */);
569 updateQSPulseExpansion();
570 }
571 }
572 });
573
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500574 mView.setRtlChangeListener(layoutDirection -> {
575 if (layoutDirection != mOldLayoutDirection) {
576 mAffordanceHelper.onRtlPropertiesChanged();
577 mOldLayoutDirection = layoutDirection;
578 }
579 });
580 }
581
582 @Override
583 protected void loadDimens() {
584 super.loadDimens();
585 mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset()
586 .setMaxLengthSeconds(0.4f).build();
587 mStatusBarMinHeight = mResources.getDimensionPixelSize(
588 com.android.internal.R.dimen.status_bar_height);
589 mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
590 mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
591 R.dimen.header_notifications_collide_distance);
592 mClockPositionAlgorithm.loadDimens(mResources);
593 mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
594 mPositionMinSideMargin = mResources.getDimensionPixelSize(
595 R.dimen.notification_panel_min_side_margin);
596 mIndicationBottomPadding = mResources.getDimensionPixelSize(
597 R.dimen.keyguard_indication_bottom_padding);
598 mQsNotificationTopPadding = mResources.getDimensionPixelSize(
599 R.dimen.qs_notification_padding);
600 mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
601 mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
602 int statusbarHeight = mResources.getDimensionPixelSize(
603 com.android.internal.R.dimen.status_bar_height);
604 mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
605 R.dimen.heads_up_status_bar_padding);
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500606 }
607
608 /**
609 * Returns if there's a custom clock being presented.
610 */
611 public boolean hasCustomClock() {
612 return mKeyguardStatusView.hasCustomClock();
613 }
614
615 private void setStatusBar(StatusBar bar) {
616 // TODO: this can be injected.
617 mStatusBar = bar;
618 mKeyguardBottomArea.setStatusBar(mStatusBar);
619 }
620 /**
621 * @see #launchCamera(boolean, int)
622 * @see #setLaunchingAffordance(boolean)
623 */
624 public void setLaunchAffordanceListener(Consumer<Boolean> listener) {
625 mAffordanceLaunchListener = listener;
626 }
627
628 public void updateResources() {
629 int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
630 int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
631 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
632 if (lp.width != qsWidth || lp.gravity != panelGravity) {
633 lp.width = qsWidth;
634 lp.gravity = panelGravity;
635 mQsFrame.setLayoutParams(lp);
636 }
637
638 int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
639 lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
640 if (lp.width != panelWidth || lp.gravity != panelGravity) {
641 lp.width = panelWidth;
642 lp.gravity = panelGravity;
643 mNotificationStackScroller.setLayoutParams(lp);
644 }
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500645 }
646
647 private void reInflateViews() {
648 updateShowEmptyShadeView();
649
650 // Re-inflate the status view group.
651 int index = mView.indexOfChild(mKeyguardStatusView);
652 mView.removeView(mKeyguardStatusView);
653 mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
654 LayoutInflater.from(mView.getContext())).inflate(
655 R.layout.keyguard_status_view, mView, false);
656 mView.addView(mKeyguardStatusView, index);
657
658 // Re-associate the clock container with the keyguard clock switch.
659 mBigClockContainer.removeAllViews();
660 KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
661 keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
662
663 // Update keyguard bottom area
664 index = mView.indexOfChild(mKeyguardBottomArea);
665 mView.removeView(mKeyguardBottomArea);
666 KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
667 mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable(
668 LayoutInflater.from(mView.getContext())).inflate(
669 R.layout.keyguard_bottom_area, mView, false);
670 mKeyguardBottomArea.initFrom(oldBottomArea);
671 mView.addView(mKeyguardBottomArea, index);
672 initBottomArea();
673 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
674 mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
675 mStatusBarStateController.getInterpolatedDozeAmount());
676
677 if (mKeyguardStatusBar != null) {
678 mKeyguardStatusBar.onThemeChanged();
679 }
680
681 setKeyguardStatusViewVisibility(mBarState, false, false);
682 setKeyguardBottomAreaVisibility(mBarState, false);
683 if (mOnReinflationListener != null) {
684 mOnReinflationListener.run();
685 }
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500686 }
687
688 private void initBottomArea() {
689 mAffordanceHelper = new KeyguardAffordanceHelper(
690 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
691 mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
692 mKeyguardBottomArea.setStatusBar(mStatusBar);
693 mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
694 }
695
696 public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
697 mKeyguardIndicationController = indicationController;
698 mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
699 }
700
701 private void updateGestureExclusionRect() {
702 Rect exclusionRect = calculateGestureExclusionRect();
703 mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST
704 : Collections.singletonList(exclusionRect));
705 }
706
707 private Rect calculateGestureExclusionRect() {
708 Rect exclusionRect = null;
Beverly95a0802ac2020-02-10 15:27:40 -0500709 Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion();
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500710 if (isFullyCollapsed() && touchableRegion != null) {
Beverly95a0802ac2020-02-10 15:27:40 -0500711 // Note: The manager also calculates the non-pinned touchable region
Dave Mankoffaf8163f2020-01-08 14:24:35 -0500712 exclusionRect = touchableRegion.getBounds();
713 }
714 return exclusionRect != null ? exclusionRect : EMPTY_RECT;
715 }
716
717 private void setIsFullWidth(boolean isFullWidth) {
718 mIsFullWidth = isFullWidth;
719 mNotificationStackScroller.setIsFullWidth(isFullWidth);
720 }
721
722 private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
723 if (mQsSizeChangeAnimator != null) {
724 oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
725 mQsSizeChangeAnimator.cancel();
726 }
727 mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
728 mQsSizeChangeAnimator.setDuration(300);
729 mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
730 mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
731 @Override
732 public void onAnimationUpdate(ValueAnimator animation) {
733 requestScrollerTopPaddingUpdate(false /* animate */);
734 requestPanelHeightUpdate();
735 int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
736 mQs.setHeightOverride(height);
737 }
738 });
739 mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
740 @Override
741 public void onAnimationEnd(Animator animation) {
742 mQsSizeChangeAnimator = null;
743 }
744 });
745 mQsSizeChangeAnimator.start();
746 }
747
748 /**
749 * Positions the clock and notifications dynamically depending on how many notifications are
750 * showing.
751 */
752 private void positionClockAndNotifications() {
753 boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
754 boolean animateClock = animate || mAnimateNextPositionUpdate;
755 int stackScrollerPadding;
756 if (mBarState != StatusBarState.KEYGUARD) {
757 stackScrollerPadding = getUnlockedStackScrollerPadding();
758 } else {
759 int totalHeight = mView.getHeight();
760 int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
761 int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
762 boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
763 final boolean
764 hasVisibleNotifications =
765 !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
766 mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
767 mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
768 mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(),
769 totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
770 - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
771 hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
772 bypassEnabled, getUnlockedStackScrollerPadding());
773 mClockPositionAlgorithm.run(mClockPositionResult);
774 PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
775 mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
776 PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
777 mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
778 updateNotificationTranslucency();
779 updateClock();
780 stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
781 }
782 mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
783 mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
784
785 mStackScrollerMeasuringPass++;
786 requestScrollerTopPaddingUpdate(animate);
787 mStackScrollerMeasuringPass = 0;
788 mAnimateNextPositionUpdate = false;
789 }
790
791 /**
792 * @return the padding of the stackscroller when unlocked
793 */
794 private int getUnlockedStackScrollerPadding() {
795 return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
796 + mQsNotificationTopPadding;
797 }
798
799 /**
800 * @param maximum the maximum to return at most
801 * @return the maximum keyguard notifications that can fit on the screen
802 */
803 public int computeMaxKeyguardNotifications(int maximum) {
804 float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
805 int notificationPadding = Math.max(
806 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
807 NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
808 float
809 shelfSize =
810 shelf.getVisibility() == View.GONE ? 0
811 : shelf.getIntrinsicHeight() + notificationPadding;
812 float
813 availableSpace =
814 mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max(
815 mIndicationBottomPadding, mAmbientIndicationBottomPadding)
816 - mKeyguardStatusView.getLogoutButtonHeight();
817 int count = 0;
818 for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
819 ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
820 if (!(child instanceof ExpandableNotificationRow)) {
821 continue;
822 }
823 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
824 boolean
825 suppressedSummary =
826 mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
827 row.getEntry().getSbn());
828 if (suppressedSummary) {
829 continue;
830 }
831 if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
832 continue;
833 }
834 if (row.isRemoved()) {
835 continue;
836 }
837 availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
838 + notificationPadding;
839 if (availableSpace >= 0 && count < maximum) {
840 count++;
841 } else if (availableSpace > -shelfSize) {
842 // if we are exactly the last view, then we can show us still!
843 for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
844 if (mNotificationStackScroller.getChildAt(
845 j) instanceof ExpandableNotificationRow) {
846 return count;
847 }
848 }
849 count++;
850 return count;
851 } else {
852 return count;
853 }
854 }
855 return count;
856 }
857
858 private void updateClock() {
859 if (!mKeyguardStatusViewAnimating) {
860 mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
861 }
862 }
863
864 public void animateToFullShade(long delay) {
865 mNotificationStackScroller.goToFullShade(delay);
866 mView.requestLayout();
867 mAnimateNextPositionUpdate = true;
868 }
869
870 public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
871 mQsExpansionEnabled = qsExpansionEnabled;
872 if (mQs == null) return;
873 mQs.setHeaderClickable(qsExpansionEnabled);
874 }
875
876 @Override
877 public void resetViews(boolean animate) {
878 mIsLaunchTransitionFinished = false;
879 mBlockTouches = false;
880 if (!mLaunchingAffordance) {
881 mAffordanceHelper.reset(false);
882 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
883 }
884 mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
885 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
886 if (animate) {
887 animateCloseQs(true /* animateAway */);
888 } else {
889 closeQs();
890 }
891 mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
892 !animate /* cancelAnimators */);
893 mNotificationStackScroller.resetScrollPosition();
894 }
895
896 @Override
897 public void collapse(boolean delayed, float speedUpFactor) {
898 if (!canPanelBeCollapsed()) {
899 return;
900 }
901
902 if (mQsExpanded) {
903 mQsExpandImmediate = true;
904 mNotificationStackScroller.setShouldShowShelfOnly(true);
905 }
906 super.collapse(delayed, speedUpFactor);
907 }
908
909 public void closeQs() {
910 cancelQsAnimation();
911 setQsExpansion(mQsMinExpansionHeight);
912 }
913
914 public void cancelAnimation() {
915 mView.animate().cancel();
916 }
917
918
919 /**
920 * Animate QS closing by flinging it.
921 * If QS is expanded, it will collapse into QQS and stop.
922 *
923 * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
924 */
925 public void animateCloseQs(boolean animateAway) {
926 if (mQsExpansionAnimator != null) {
927 if (!mQsAnimatorExpand) {
928 return;
929 }
930 float height = mQsExpansionHeight;
931 mQsExpansionAnimator.cancel();
932 setQsExpansion(height);
933 }
934 flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
935 }
936
937 public void expandWithQs() {
938 if (mQsExpansionEnabled) {
939 mQsExpandImmediate = true;
940 mNotificationStackScroller.setShouldShowShelfOnly(true);
941 }
942 if (isFullyCollapsed()) {
943 expand(true /* animate */);
944 } else {
945 flingSettings(0 /* velocity */, FLING_EXPAND);
946 }
947 }
948
949 public void expandWithoutQs() {
950 if (isQsExpanded()) {
951 flingSettings(0 /* velocity */, FLING_COLLAPSE);
952 } else {
953 expand(true /* animate */);
954 }
955 }
956
957 @Override
958 public void fling(float vel, boolean expand) {
959 GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
960 if (gr != null) {
961 gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
962 }
963 super.fling(vel, expand);
964 }
965
966 @Override
967 protected void flingToHeight(float vel, boolean expand, float target,
968 float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
969 mHeadsUpTouchHelper.notifyFling(!expand);
970 setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
971 super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
972 }
973
974
975 private boolean onQsIntercept(MotionEvent event) {
976 int pointerIndex = event.findPointerIndex(mTrackingPointer);
977 if (pointerIndex < 0) {
978 pointerIndex = 0;
979 mTrackingPointer = event.getPointerId(pointerIndex);
980 }
981 final float x = event.getX(pointerIndex);
982 final float y = event.getY(pointerIndex);
983
984 switch (event.getActionMasked()) {
985 case MotionEvent.ACTION_DOWN:
986 mInitialTouchY = y;
987 mInitialTouchX = x;
988 initVelocityTracker();
989 trackMovement(event);
990 if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
991 mView.getParent().requestDisallowInterceptTouchEvent(true);
992 }
993 if (mQsExpansionAnimator != null) {
994 onQsExpansionStarted();
995 mInitialHeightOnTouch = mQsExpansionHeight;
996 mQsTracking = true;
997 mNotificationStackScroller.cancelLongPress();
998 }
999 break;
1000 case MotionEvent.ACTION_POINTER_UP:
1001 final int upPointer = event.getPointerId(event.getActionIndex());
1002 if (mTrackingPointer == upPointer) {
1003 // gesture is ongoing, find a new pointer to track
1004 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
1005 mTrackingPointer = event.getPointerId(newIndex);
1006 mInitialTouchX = event.getX(newIndex);
1007 mInitialTouchY = event.getY(newIndex);
1008 }
1009 break;
1010
1011 case MotionEvent.ACTION_MOVE:
1012 final float h = y - mInitialTouchY;
1013 trackMovement(event);
1014 if (mQsTracking) {
1015
1016 // Already tracking because onOverscrolled was called. We need to update here
1017 // so we don't stop for a frame until the next touch event gets handled in
1018 // onTouchEvent.
1019 setQsExpansion(h + mInitialHeightOnTouch);
1020 trackMovement(event);
1021 return true;
1022 }
Philip Quinn47169132020-03-23 19:04:55 -07001023 if (Math.abs(h) > getTouchSlop(event)
1024 && Math.abs(h) > Math.abs(x - mInitialTouchX)
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001025 && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
1026 mQsTracking = true;
1027 onQsExpansionStarted();
1028 notifyExpandingFinished();
1029 mInitialHeightOnTouch = mQsExpansionHeight;
1030 mInitialTouchY = y;
1031 mInitialTouchX = x;
1032 mNotificationStackScroller.cancelLongPress();
1033 return true;
1034 }
1035 break;
1036
1037 case MotionEvent.ACTION_CANCEL:
1038 case MotionEvent.ACTION_UP:
1039 trackMovement(event);
1040 if (mQsTracking) {
1041 flingQsWithCurrentVelocity(y,
1042 event.getActionMasked() == MotionEvent.ACTION_CANCEL);
1043 mQsTracking = false;
1044 }
1045 break;
1046 }
1047 return false;
1048 }
1049
1050 @Override
1051 protected boolean isInContentBounds(float x, float y) {
1052 float stackScrollerX = mNotificationStackScroller.getX();
1053 return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
1054 && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
1055 }
1056
1057 private void initDownStates(MotionEvent event) {
1058 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1059 mOnlyAffordanceInThisMotion = false;
1060 mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
1061 mDozingOnDown = isDozing();
1062 mDownX = event.getX();
1063 mDownY = event.getY();
1064 mCollapsedOnDown = isFullyCollapsed();
1065 mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
1066 mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
1067 mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
1068 if (mExpectingSynthesizedDown) {
1069 mLastEventSynthesizedDown = true;
1070 } else {
1071 // down but not synthesized motion event.
1072 mLastEventSynthesizedDown = false;
1073 }
1074 } else {
1075 // not down event at all.
1076 mLastEventSynthesizedDown = false;
1077 }
1078 }
1079
1080 private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
1081 float vel = getCurrentQSVelocity();
1082 final boolean expandsQs = flingExpandsQs(vel);
1083 if (expandsQs) {
1084 logQsSwipeDown(y);
1085 }
1086 flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
1087 }
1088
1089 private void logQsSwipeDown(float y) {
1090 float vel = getCurrentQSVelocity();
1091 final int
1092 gesture =
1093 mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS
1094 : MetricsEvent.ACTION_SHADE_QS_PULL;
1095 mLockscreenGestureLogger.write(gesture,
1096 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
1097 (int) (vel / mStatusBar.getDisplayDensity()));
1098 }
1099
1100 private boolean flingExpandsQs(float vel) {
1101 if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
1102 return false;
1103 }
1104 if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
1105 return getQsExpansionFraction() > 0.5f;
1106 } else {
1107 return vel > 0;
1108 }
1109 }
1110
1111 private boolean isFalseTouch() {
1112 if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
1113 return false;
1114 }
1115 if (mFalsingManager.isClassifierEnabled()) {
1116 return mFalsingManager.isFalseTouch();
1117 }
1118 return !mQsTouchAboveFalsingThreshold;
1119 }
1120
1121 private float getQsExpansionFraction() {
1122 return Math.min(
1123 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
1124 - mQsMinExpansionHeight));
1125 }
1126
1127 @Override
1128 protected boolean shouldExpandWhenNotFlinging() {
1129 if (super.shouldExpandWhenNotFlinging()) {
1130 return true;
1131 }
1132 if (mAllowExpandForSmallExpansion) {
1133 // When we get a touch that came over from launcher, the velocity isn't always correct
1134 // Let's err on expanding if the gesture has been reasonably slow
1135 long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
1136 return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
1137 }
1138 return false;
1139 }
1140
1141 @Override
1142 protected float getOpeningHeight() {
1143 return mNotificationStackScroller.getOpeningHeight();
1144 }
1145
1146
1147 private boolean handleQsTouch(MotionEvent event) {
1148 final int action = event.getActionMasked();
1149 if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
1150 && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
1151
1152 // Down in the empty area while fully expanded - go to QS.
1153 mQsTracking = true;
1154 mConflictingQsExpansionGesture = true;
1155 onQsExpansionStarted();
1156 mInitialHeightOnTouch = mQsExpansionHeight;
1157 mInitialTouchY = event.getX();
1158 mInitialTouchX = event.getY();
1159 }
1160 if (!isFullyCollapsed()) {
1161 handleQsDown(event);
1162 }
1163 if (!mQsExpandImmediate && mQsTracking) {
1164 onQsTouch(event);
1165 if (!mConflictingQsExpansionGesture) {
1166 return true;
1167 }
1168 }
1169 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
1170 mConflictingQsExpansionGesture = false;
1171 }
1172 if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) {
1173 mTwoFingerQsExpandPossible = true;
1174 }
1175 if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
1176 < mStatusBarMinHeight) {
1177 mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
1178 mQsExpandImmediate = true;
1179 mNotificationStackScroller.setShouldShowShelfOnly(true);
1180 requestPanelHeightUpdate();
1181
1182 // Normally, we start listening when the panel is expanded, but here we need to start
1183 // earlier so the state is already up to date when dragging down.
1184 setListening(true);
1185 }
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001186 return false;
1187 }
1188
1189 private boolean isInQsArea(float x, float y) {
1190 return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && (
1191 y <= mNotificationStackScroller.getBottomMostNotificationBottom()
1192 || y <= mQs.getView().getY() + mQs.getView().getHeight());
1193 }
1194
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001195 private boolean isOpenQsEvent(MotionEvent event) {
1196 final int pointerCount = event.getPointerCount();
1197 final int action = event.getActionMasked();
1198
1199 final boolean
1200 twoFingerDrag =
1201 action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
1202
1203 final boolean
1204 stylusButtonClickDrag =
1205 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
1206 MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
1207 MotionEvent.BUTTON_STYLUS_SECONDARY));
1208
1209 final boolean
1210 mouseButtonClickDrag =
1211 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
1212 MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
1213 MotionEvent.BUTTON_TERTIARY));
1214
Matt Pietalf07bac42020-01-29 15:07:48 -05001215 return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001216 }
1217
1218 private void handleQsDown(MotionEvent event) {
1219 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
1220 event.getX(), event.getY(), -1)) {
1221 mFalsingManager.onQsDown();
1222 mQsTracking = true;
1223 onQsExpansionStarted();
1224 mInitialHeightOnTouch = mQsExpansionHeight;
1225 mInitialTouchY = event.getX();
1226 mInitialTouchX = event.getY();
1227
1228 // If we interrupt an expansion gesture here, make sure to update the state correctly.
1229 notifyExpandingFinished();
1230 }
1231 }
1232
1233 /**
1234 * Input focus transfer is about to happen.
1235 */
1236 public void startWaitingForOpenPanelGesture() {
1237 if (!isFullyCollapsed()) {
1238 return;
1239 }
1240 mExpectingSynthesizedDown = true;
1241 onTrackingStarted();
1242 updatePanelExpanded();
1243 }
1244
1245 /**
1246 * Called when this view is no longer waiting for input focus transfer.
1247 *
1248 * There are two scenarios behind this function call. First, input focus transfer
1249 * has successfully happened and this view already received synthetic DOWN event.
1250 * (mExpectingSynthesizedDown == false). Do nothing.
1251 *
1252 * Second, before input focus transfer finished, user may have lifted finger
1253 * in previous window and this window never received synthetic DOWN event.
1254 * (mExpectingSynthesizedDown == true).
1255 * In this case, we use the velocity to trigger fling event.
1256 *
1257 * @param velocity unit is in px / millis
1258 */
1259 public void stopWaitingForOpenPanelGesture(final float velocity) {
1260 if (mExpectingSynthesizedDown) {
1261 mExpectingSynthesizedDown = false;
1262 maybeVibrateOnOpening();
wilsonshihe8321942019-10-18 18:39:46 +08001263 fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001264 onTrackingStopped(false);
1265 }
1266 }
1267
1268 @Override
1269 protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
1270 boolean expands = super.flingExpands(vel, vectorVel, x, y);
1271
1272 // If we are already running a QS expansion, make sure that we keep the panel open.
1273 if (mQsExpansionAnimator != null) {
1274 expands = true;
1275 }
1276 return expands;
1277 }
1278
1279 @Override
1280 protected boolean shouldGestureWaitForTouchSlop() {
1281 if (mExpectingSynthesizedDown) {
1282 mExpectingSynthesizedDown = false;
1283 return false;
1284 }
1285 return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
1286 }
1287
1288 @Override
1289 protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
1290 return !mAffordanceHelper.isOnAffordanceIcon(x, y);
1291 }
1292
1293 private void onQsTouch(MotionEvent event) {
1294 int pointerIndex = event.findPointerIndex(mTrackingPointer);
1295 if (pointerIndex < 0) {
1296 pointerIndex = 0;
1297 mTrackingPointer = event.getPointerId(pointerIndex);
1298 }
1299 final float y = event.getY(pointerIndex);
1300 final float x = event.getX(pointerIndex);
1301 final float h = y - mInitialTouchY;
1302
1303 switch (event.getActionMasked()) {
1304 case MotionEvent.ACTION_DOWN:
1305 mQsTracking = true;
1306 mInitialTouchY = y;
1307 mInitialTouchX = x;
1308 onQsExpansionStarted();
1309 mInitialHeightOnTouch = mQsExpansionHeight;
1310 initVelocityTracker();
1311 trackMovement(event);
1312 break;
1313
1314 case MotionEvent.ACTION_POINTER_UP:
1315 final int upPointer = event.getPointerId(event.getActionIndex());
1316 if (mTrackingPointer == upPointer) {
1317 // gesture is ongoing, find a new pointer to track
1318 final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
1319 final float newY = event.getY(newIndex);
1320 final float newX = event.getX(newIndex);
1321 mTrackingPointer = event.getPointerId(newIndex);
1322 mInitialHeightOnTouch = mQsExpansionHeight;
1323 mInitialTouchY = newY;
1324 mInitialTouchX = newX;
1325 }
1326 break;
1327
1328 case MotionEvent.ACTION_MOVE:
1329 setQsExpansion(h + mInitialHeightOnTouch);
1330 if (h >= getFalsingThreshold()) {
1331 mQsTouchAboveFalsingThreshold = true;
1332 }
1333 trackMovement(event);
1334 break;
1335
1336 case MotionEvent.ACTION_UP:
1337 case MotionEvent.ACTION_CANCEL:
1338 mQsTracking = false;
1339 mTrackingPointer = -1;
1340 trackMovement(event);
1341 float fraction = getQsExpansionFraction();
1342 if (fraction != 0f || y >= mInitialTouchY) {
1343 flingQsWithCurrentVelocity(y,
1344 event.getActionMasked() == MotionEvent.ACTION_CANCEL);
1345 }
1346 if (mQsVelocityTracker != null) {
1347 mQsVelocityTracker.recycle();
1348 mQsVelocityTracker = null;
1349 }
1350 break;
1351 }
1352 }
1353
1354 private int getFalsingThreshold() {
1355 float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
1356 return (int) (mQsFalsingThreshold * factor);
1357 }
1358
1359 private void setOverScrolling(boolean overscrolling) {
1360 mStackScrollerOverscrolling = overscrolling;
1361 if (mQs == null) return;
1362 mQs.setOverscrolling(overscrolling);
1363 }
1364
1365 private void onQsExpansionStarted() {
1366 onQsExpansionStarted(0);
1367 }
1368
1369 protected void onQsExpansionStarted(int overscrollAmount) {
1370 cancelQsAnimation();
1371 cancelHeightAnimator();
1372
1373 // Reset scroll position and apply that position to the expanded height.
1374 float height = mQsExpansionHeight - overscrollAmount;
1375 setQsExpansion(height);
1376 requestPanelHeightUpdate();
1377 mNotificationStackScroller.checkSnoozeLeavebehind();
1378
1379 // When expanding QS, let's authenticate the user if possible,
1380 // this will speed up notification actions.
1381 if (height == 0) {
1382 mStatusBar.requestFaceAuth();
1383 }
1384 }
1385
1386 private void setQsExpanded(boolean expanded) {
1387 boolean changed = mQsExpanded != expanded;
1388 if (changed) {
1389 mQsExpanded = expanded;
1390 updateQsState();
1391 requestPanelHeightUpdate();
1392 mFalsingManager.setQsExpanded(expanded);
1393 mStatusBar.setQsExpanded(expanded);
1394 mNotificationContainerParent.setQsExpanded(expanded);
1395 mPulseExpansionHandler.setQsExpanded(expanded);
1396 mKeyguardBypassController.setQSExpanded(expanded);
1397 }
1398 }
1399
1400 private void maybeAnimateBottomAreaAlpha() {
1401 mBottomAreaShadeAlphaAnimator.cancel();
1402 if (mBarState == StatusBarState.SHADE_LOCKED) {
1403 mBottomAreaShadeAlphaAnimator.start();
1404 } else {
1405 mBottomAreaShadeAlpha = 1f;
1406 }
1407 }
1408
1409 private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
1410 @Override
1411 public void run() {
1412 mKeyguardStatusViewAnimating = false;
1413 mKeyguardStatusView.setVisibility(View.INVISIBLE);
1414 }
1415 };
1416
1417 private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
1418 @Override
1419 public void run() {
1420 mKeyguardStatusViewAnimating = false;
1421 mKeyguardStatusView.setVisibility(View.GONE);
1422 }
1423 };
1424
1425 private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
1426 @Override
1427 public void run() {
1428 mKeyguardStatusViewAnimating = false;
1429 }
1430 };
1431
1432 private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
1433 @Override
1434 public void run() {
1435 mKeyguardStatusBar.setVisibility(View.INVISIBLE);
1436 mKeyguardStatusBar.setAlpha(1f);
1437 mKeyguardStatusBarAnimateAlpha = 1f;
1438 }
1439 };
1440
1441 private void animateKeyguardStatusBarOut() {
1442 ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
1443 anim.addUpdateListener(mStatusBarAnimateAlphaListener);
1444 anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
1445 ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
1446
1447 long duration;
1448 if (mKeyguardStateController.isKeyguardFadingAway()) {
1449 duration = mKeyguardStateController.getShortenedFadingAwayDuration();
1450 } else {
1451 duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
1452 }
1453 anim.setDuration(duration);
1454
1455 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1456 anim.addListener(new AnimatorListenerAdapter() {
1457 @Override
1458 public void onAnimationEnd(Animator animation) {
1459 mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
1460 }
1461 });
1462 anim.start();
1463 }
1464
1465 private final ValueAnimator.AnimatorUpdateListener
1466 mStatusBarAnimateAlphaListener =
1467 new ValueAnimator.AnimatorUpdateListener() {
1468 @Override
1469 public void onAnimationUpdate(ValueAnimator animation) {
1470 mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
1471 updateHeaderKeyguardAlpha();
1472 }
1473 };
1474
1475 private void animateKeyguardStatusBarIn(long duration) {
1476 mKeyguardStatusBar.setVisibility(View.VISIBLE);
1477 mKeyguardStatusBar.setAlpha(0f);
1478 ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1479 anim.addUpdateListener(mStatusBarAnimateAlphaListener);
1480 anim.setDuration(duration);
1481 anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1482 anim.start();
1483 }
1484
1485 private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
1486 @Override
1487 public void run() {
1488 mKeyguardBottomArea.setVisibility(View.GONE);
1489 }
1490 };
1491
1492 private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
1493 mKeyguardBottomArea.animate().cancel();
1494 if (goingToFullShade) {
1495 mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
1496 mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
1497 mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
1498 Interpolators.ALPHA_OUT).withEndAction(
1499 mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
1500 } else if (statusBarState == StatusBarState.KEYGUARD
1501 || statusBarState == StatusBarState.SHADE_LOCKED) {
1502 mKeyguardBottomArea.setVisibility(View.VISIBLE);
1503 mKeyguardBottomArea.setAlpha(1f);
1504 } else {
1505 mKeyguardBottomArea.setVisibility(View.GONE);
1506 }
1507 }
1508
1509 private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
1510 boolean goingToFullShade) {
1511 mKeyguardStatusView.animate().cancel();
1512 mKeyguardStatusViewAnimating = false;
1513 if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
1514 && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
1515 mKeyguardStatusViewAnimating = true;
1516 mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
1517 160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
1518 mAnimateKeyguardStatusViewGoneEndRunnable);
1519 if (keyguardFadingAway) {
1520 mKeyguardStatusView.animate().setStartDelay(
1521 mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
1522 mKeyguardStateController.getShortenedFadingAwayDuration()).start();
1523 }
1524 } else if (mBarState == StatusBarState.SHADE_LOCKED
1525 && statusBarState == StatusBarState.KEYGUARD) {
1526 mKeyguardStatusView.setVisibility(View.VISIBLE);
1527 mKeyguardStatusViewAnimating = true;
1528 mKeyguardStatusView.setAlpha(0f);
1529 mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
1530 320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
1531 mAnimateKeyguardStatusViewVisibleEndRunnable);
1532 } else if (statusBarState == StatusBarState.KEYGUARD) {
1533 if (keyguardFadingAway) {
1534 mKeyguardStatusViewAnimating = true;
1535 mKeyguardStatusView.animate().alpha(0).translationYBy(
1536 -getHeight() * 0.05f).setInterpolator(
1537 Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay(
1538 0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start();
1539 } else {
1540 mKeyguardStatusView.setVisibility(View.VISIBLE);
1541 mKeyguardStatusView.setAlpha(1f);
1542 }
1543 } else {
1544 mKeyguardStatusView.setVisibility(View.GONE);
1545 mKeyguardStatusView.setAlpha(1f);
1546 }
1547 }
1548
1549 private void updateQsState() {
1550 mNotificationStackScroller.setQsExpanded(mQsExpanded);
1551 mNotificationStackScroller.setScrollingEnabled(
1552 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
1553 || mQsExpansionFromOverscroll));
1554 updateEmptyShadeView();
Matt Pietalf07bac42020-01-29 15:07:48 -05001555
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001556 mQsNavbarScrim.setVisibility(
1557 mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
1558 && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE);
1559 if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
1560 mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
1561 }
1562 if (mQs == null) return;
1563 mQs.setExpanded(mQsExpanded);
1564 }
1565
1566 private void setQsExpansion(float height) {
1567 height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
1568 mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
1569 if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
1570 && !mDozing) {
1571 setQsExpanded(true);
1572 } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
1573 setQsExpanded(false);
1574 }
1575 mQsExpansionHeight = height;
1576 updateQsExpansion();
1577 requestScrollerTopPaddingUpdate(false /* animate */);
1578 updateHeaderKeyguardAlpha();
1579 if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) {
1580 updateKeyguardBottomAreaAlpha();
1581 updateBigClockAlpha();
1582 }
1583 if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
1584 && mQsScrimEnabled) {
1585 mQsNavbarScrim.setAlpha(getQsExpansionFraction());
1586 }
1587
1588 if (mAccessibilityManager.isEnabled()) {
1589 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
1590 }
1591
1592 if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
1593 && mFalsingManager.shouldEnforceBouncer()) {
1594 mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
1595 false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
1596 }
1597 for (int i = 0; i < mExpansionListeners.size(); i++) {
1598 mExpansionListeners.get(i).onQsExpansionChanged(
1599 mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
1600 }
1601 if (DEBUG) {
1602 mView.invalidate();
1603 }
1604 }
1605
1606 protected void updateQsExpansion() {
1607 if (mQs == null) return;
1608 float qsExpansionFraction = getQsExpansionFraction();
1609 mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
1610 int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001611 mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
1612 }
1613
1614 private String determineAccessibilityPaneTitle() {
1615 if (mQs != null && mQs.isCustomizing()) {
1616 return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
1617 } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
1618 // Upon initialisation when we are not layouted yet we don't want to announce that we
1619 // are fully expanded, hence the != 0.0f check.
1620 return mResources.getString(R.string.accessibility_desc_quick_settings);
1621 } else if (mBarState == StatusBarState.KEYGUARD) {
1622 return mResources.getString(R.string.accessibility_desc_lock_screen);
1623 } else {
1624 return mResources.getString(R.string.accessibility_desc_notification_shade);
1625 }
1626 }
1627
1628 private float calculateQsTopPadding() {
1629 if (mKeyguardShowing && (mQsExpandImmediate
1630 || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
1631
1632 // Either QS pushes the notifications down when fully expanded, or QS is fully above the
1633 // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
1634 // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
1635 // panel. We need to take the maximum and linearly interpolate with the panel expansion
1636 // for a nice motion.
1637 int maxNotificationPadding = getKeyguardNotificationStaticPadding();
1638 int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
1639 int max = mBarState == StatusBarState.KEYGUARD ? Math.max(
1640 maxNotificationPadding, maxQsPadding) : maxQsPadding;
1641 return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
1642 getExpandedFraction());
1643 } else if (mQsSizeChangeAnimator != null) {
1644 return Math.max(
1645 (int) mQsSizeChangeAnimator.getAnimatedValue(),
1646 getKeyguardNotificationStaticPadding());
1647 } else if (mKeyguardShowing) {
1648 // We can only do the smoother transition on Keyguard when we also are not collapsing
1649 // from a scrolled quick settings.
1650 return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
1651 (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
1652 getQsExpansionFraction());
1653 } else {
1654 return mQsExpansionHeight + mQsNotificationTopPadding;
1655 }
1656 }
1657
1658 /**
1659 * @return the topPadding of notifications when on keyguard not respecting quick settings
1660 * expansion
1661 */
1662 private int getKeyguardNotificationStaticPadding() {
1663 if (!mKeyguardShowing) {
1664 return 0;
1665 }
1666 if (!mKeyguardBypassController.getBypassEnabled()) {
1667 return mClockPositionResult.stackScrollerPadding;
1668 }
1669 int collapsedPosition = mHeadsUpInset;
1670 if (!mNotificationStackScroller.isPulseExpanding()) {
1671 return collapsedPosition;
1672 } else {
1673 int expandedPosition = mClockPositionResult.stackScrollerPadding;
1674 return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
1675 mNotificationStackScroller.calculateAppearFractionBypass());
1676 }
1677 }
1678
1679
1680 protected void requestScrollerTopPaddingUpdate(boolean animate) {
1681 mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
1682 if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
1683 // update the position of the header
1684 updateQsExpansion();
1685 }
1686 }
1687
1688
1689 private void updateQSPulseExpansion() {
1690 if (mQs != null) {
1691 mQs.setShowCollapsedOnKeyguard(
1692 mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
1693 && mNotificationStackScroller.isPulseExpanding());
1694 }
1695 }
1696
1697 private void trackMovement(MotionEvent event) {
1698 if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
1699 }
1700
1701 private void initVelocityTracker() {
1702 if (mQsVelocityTracker != null) {
1703 mQsVelocityTracker.recycle();
1704 }
1705 mQsVelocityTracker = VelocityTracker.obtain();
1706 }
1707
1708 private float getCurrentQSVelocity() {
1709 if (mQsVelocityTracker == null) {
1710 return 0;
1711 }
1712 mQsVelocityTracker.computeCurrentVelocity(1000);
1713 return mQsVelocityTracker.getYVelocity();
1714 }
1715
1716 private void cancelQsAnimation() {
1717 if (mQsExpansionAnimator != null) {
1718 mQsExpansionAnimator.cancel();
1719 }
1720 }
1721
1722 /**
1723 * @see #flingSettings(float, int, Runnable, boolean)
1724 */
1725 public void flingSettings(float vel, int type) {
1726 flingSettings(vel, type, null, false /* isClick */);
1727 }
1728
1729 /**
1730 * Animates QS or QQS as if the user had swiped up or down.
1731 *
1732 * @param vel Finger velocity or 0 when not initiated by touch events.
1733 * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link
1734 * #FLING_HIDE}.
1735 * @param onFinishRunnable Runnable to be executed at the end of animation.
1736 * @param isClick If originated by click (different interpolator and duration.)
1737 */
1738 protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
1739 boolean isClick) {
1740 float target;
1741 switch (type) {
1742 case FLING_EXPAND:
1743 target = mQsMaxExpansionHeight;
1744 break;
1745 case FLING_COLLAPSE:
1746 target = mQsMinExpansionHeight;
1747 break;
1748 case FLING_HIDE:
1749 default:
1750 target = 0;
1751 }
1752 if (target == mQsExpansionHeight) {
1753 if (onFinishRunnable != null) {
1754 onFinishRunnable.run();
1755 }
1756 return;
1757 }
1758
1759 // If we move in the opposite direction, reset velocity and use a different duration.
1760 boolean oppositeDirection = false;
1761 boolean expanding = type == FLING_EXPAND;
1762 if (vel > 0 && !expanding || vel < 0 && expanding) {
1763 vel = 0;
1764 oppositeDirection = true;
1765 }
1766 ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
1767 if (isClick) {
1768 animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
1769 animator.setDuration(368);
1770 } else {
1771 mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
1772 }
1773 if (oppositeDirection) {
1774 animator.setDuration(350);
1775 }
1776 animator.addUpdateListener(animation -> {
1777 setQsExpansion((Float) animation.getAnimatedValue());
1778 });
1779 animator.addListener(new AnimatorListenerAdapter() {
1780 @Override
1781 public void onAnimationEnd(Animator animation) {
1782 mNotificationStackScroller.resetCheckSnoozeLeavebehind();
1783 mQsExpansionAnimator = null;
1784 if (onFinishRunnable != null) {
1785 onFinishRunnable.run();
1786 }
1787 }
1788 });
1789 animator.start();
1790 mQsExpansionAnimator = animator;
1791 mQsAnimatorExpand = expanding;
1792 }
1793
1794 /**
1795 * @return Whether we should intercept a gesture to open Quick Settings.
1796 */
1797 private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
1798 if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing
1799 && mKeyguardBypassController.getBypassEnabled())) {
1800 return false;
1801 }
1802 View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
1803 final boolean
1804 onHeader =
1805 x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()
1806 && y >= header.getTop() && y <= header.getBottom();
1807 if (mQsExpanded) {
1808 return onHeader || (yDiff < 0 && isInQsArea(x, y));
1809 } else {
1810 return onHeader;
1811 }
1812 }
1813
1814 @Override
1815 protected boolean isScrolledToBottom() {
1816 if (!isInSettings()) {
1817 return mBarState == StatusBarState.KEYGUARD
1818 || mNotificationStackScroller.isScrolledToBottom();
1819 } else {
1820 return true;
1821 }
1822 }
1823
1824 @Override
1825 protected int getMaxPanelHeight() {
1826 if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
1827 return getMaxPanelHeightBypass();
1828 } else {
1829 return getMaxPanelHeightNonBypass();
1830 }
1831 }
1832
1833 private int getMaxPanelHeightNonBypass() {
1834 int min = mStatusBarMinHeight;
1835 if (!(mBarState == StatusBarState.KEYGUARD)
1836 && mNotificationStackScroller.getNotGoneChildCount() == 0) {
1837 int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
1838 min = Math.max(min, minHeight);
1839 }
1840 int maxHeight;
1841 if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
1842 || mPulsing) {
1843 maxHeight = calculatePanelHeightQsExpanded();
1844 } else {
1845 maxHeight = calculatePanelHeightShade();
1846 }
Lucas Dupin9e200242020-03-16 15:26:55 -07001847 maxHeight = Math.max(min, maxHeight);
1848 if (maxHeight == 0) {
1849 Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): "
1850 + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
1851 + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
1852 + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
1853 + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
1854 }
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001855 return maxHeight;
1856 }
1857
1858 private int getMaxPanelHeightBypass() {
1859 int position =
1860 mClockPositionAlgorithm.getExpandedClockPosition()
1861 + mKeyguardStatusView.getHeight();
1862 if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
1863 position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
1864 }
1865 return position;
1866 }
1867
1868 public boolean isInSettings() {
1869 return mQsExpanded;
1870 }
1871
1872 public boolean isExpanding() {
1873 return mIsExpanding;
1874 }
1875
1876 @Override
1877 protected void onHeightUpdated(float expandedHeight) {
1878 if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
1879 // Updating the clock position will set the top padding which might
1880 // trigger a new panel height and re-position the clock.
1881 // This is a circular dependency and should be avoided, otherwise we'll have
1882 // a stack overflow.
1883 if (mStackScrollerMeasuringPass > 2) {
1884 if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
1885 } else {
1886 positionClockAndNotifications();
1887 }
1888 }
1889 if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
1890 && !mQsExpansionFromOverscroll) {
1891 float t;
1892 if (mKeyguardShowing) {
1893
1894 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
1895 t = expandedHeight / (getMaxPanelHeight());
1896 } else {
1897 // In Shade, interpolate linearly such that QS is closed whenever panel height is
1898 // minimum QS expansion + minStackHeight
1899 float
1900 panelHeightQsCollapsed =
1901 mNotificationStackScroller.getIntrinsicPadding()
1902 + mNotificationStackScroller.getLayoutMinHeight();
1903 float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
1904 t =
1905 (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
1906 - panelHeightQsCollapsed);
1907 }
1908 float
1909 targetHeight =
1910 mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
1911 setQsExpansion(targetHeight);
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001912 }
1913 updateExpandedHeight(expandedHeight);
1914 updateHeader();
1915 updateNotificationTranslucency();
1916 updatePanelExpanded();
1917 updateGestureExclusionRect();
1918 if (DEBUG) {
1919 mView.invalidate();
1920 }
1921 }
1922
1923 private void updatePanelExpanded() {
1924 boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
1925 if (mPanelExpanded != isExpanded) {
1926 mHeadsUpManager.setIsPanelExpanded(isExpanded);
Beverly95a0802ac2020-02-10 15:27:40 -05001927 mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
Dave Mankoffaf8163f2020-01-08 14:24:35 -05001928 mStatusBar.setPanelExpanded(isExpanded);
1929 mPanelExpanded = isExpanded;
1930 }
1931 }
1932
1933 private int calculatePanelHeightShade() {
1934 int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
1935 int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
1936 maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
1937
1938 if (mBarState == StatusBarState.KEYGUARD) {
1939 int
1940 minKeyguardPanelBottom =
1941 mClockPositionAlgorithm.getExpandedClockPosition()
1942 + mKeyguardStatusView.getHeight()
1943 + mNotificationStackScroller.getIntrinsicContentHeight();
1944 return Math.max(maxHeight, minKeyguardPanelBottom);
1945 } else {
1946 return maxHeight;
1947 }
1948 }
1949
1950 private int calculatePanelHeightQsExpanded() {
1951 float
1952 notificationHeight =
1953 mNotificationStackScroller.getHeight()
1954 - mNotificationStackScroller.getEmptyBottomMargin()
1955 - mNotificationStackScroller.getTopPadding();
1956
1957 // When only empty shade view is visible in QS collapsed state, simulate that we would have
1958 // it in expanded QS state as well so we don't run into troubles when fading the view in/out
1959 // and expanding/collapsing the whole panel from/to quick settings.
1960 if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) {
1961 notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
1962 }
1963 int maxQsHeight = mQsMaxExpansionHeight;
1964
1965 if (mKeyguardShowing) {
1966 maxQsHeight += mQsNotificationTopPadding;
1967 }
1968
1969 // If an animation is changing the size of the QS panel, take the animated value.
1970 if (mQsSizeChangeAnimator != null) {
1971 maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
1972 }
1973 float totalHeight = Math.max(maxQsHeight,
1974 mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding
1975 : 0) + notificationHeight
1976 + mNotificationStackScroller.getTopPaddingOverflow();
1977 if (totalHeight > mNotificationStackScroller.getHeight()) {
1978 float
1979 fullyCollapsedHeight =
1980 maxQsHeight + mNotificationStackScroller.getLayoutMinHeight();
1981 totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
1982 }
1983 return (int) totalHeight;
1984 }
1985
1986 private void updateNotificationTranslucency() {
1987 float alpha = 1f;
1988 if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
1989 && !mHeadsUpManager.hasPinnedHeadsUp()) {
1990 alpha = getFadeoutAlpha();
1991 }
1992 if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
1993 && !mKeyguardBypassController.getBypassEnabled()) {
1994 alpha *= mClockPositionResult.clockAlpha;
1995 }
1996 mNotificationStackScroller.setAlpha(alpha);
1997 }
1998
1999 private float getFadeoutAlpha() {
2000 float alpha;
2001 if (mQsMinExpansionHeight == 0) {
2002 return 1.0f;
2003 }
2004 alpha = getExpandedHeight() / mQsMinExpansionHeight;
2005 alpha = Math.max(0, Math.min(alpha, 1));
2006 alpha = (float) Math.pow(alpha, 0.75);
2007 return alpha;
2008 }
2009
2010 @Override
2011 protected float getOverExpansionAmount() {
2012 return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
2013 }
2014
2015 @Override
2016 protected float getOverExpansionPixels() {
2017 return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
2018 }
2019
2020 /**
2021 * Hides the header when notifications are colliding with it.
2022 */
2023 private void updateHeader() {
2024 if (mBarState == StatusBarState.KEYGUARD) {
2025 updateHeaderKeyguardAlpha();
2026 }
2027 updateQsExpansion();
2028 }
2029
2030 protected float getHeaderTranslation() {
2031 if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
2032 return -mQs.getQsMinExpansionHeight();
2033 }
2034 float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
2035 float startHeight = -mQsExpansionHeight;
2036 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
2037 && mNotificationStackScroller.isPulseExpanding()) {
2038 if (!mPulseExpansionHandler.isExpanding()
2039 && !mPulseExpansionHandler.getLeavingLockscreen()) {
2040 // If we aborted the expansion we need to make sure the header doesn't reappear
2041 // again after the header has animated away
2042 appearAmount = 0;
2043 } else {
2044 appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
2045 }
2046 startHeight = -mQs.getQsMinExpansionHeight();
Dave Mankoffaf8163f2020-01-08 14:24:35 -05002047 }
2048 float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
2049 + mExpandOffset;
2050 return Math.min(0, translation);
2051 }
2052
2053 /**
2054 * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
2055 * during swiping up
2056 */
2057 private float getKeyguardContentsAlpha() {
2058 float alpha;
2059 if (mBarState == StatusBarState.KEYGUARD) {
2060
2061 // When on Keyguard, we hide the header as soon as we expanded close enough to the
2062 // header
2063 alpha =
2064 getExpandedHeight() / (mKeyguardStatusBar.getHeight()
2065 + mNotificationsHeaderCollideDistance);
2066 } else {
2067
2068 // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
2069 // soon as we start translating the stack.
2070 alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
2071 }
2072 alpha = MathUtils.saturate(alpha);
2073 alpha = (float) Math.pow(alpha, 0.75);
2074 return alpha;
2075 }
2076
2077 private void updateHeaderKeyguardAlpha() {
2078 if (!mKeyguardShowing) {
2079 return;
2080 }
2081 float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
2082 float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
2083 * mKeyguardStatusBarAnimateAlpha;
2084 newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
2085 mKeyguardStatusBar.setAlpha(newAlpha);
2086 boolean
2087 hideForBypass =
2088 mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
2089 || mDelayShowingKeyguardStatusBar;
2090 mKeyguardStatusBar.setVisibility(
2091 newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
2092 }
2093
2094 private void updateKeyguardBottomAreaAlpha() {
2095 // There are two possible panel expansion behaviors:
2096 // • User dragging up to unlock: we want to fade out as quick as possible
2097 // (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
2098 // • User tapping on lock screen: bouncer won't be visible but panel expansion will
2099 // change due to "unlock hint animation." In this case, fading out the bottom area
2100 // would also hide the message that says "swipe to unlock," we don't want to do that.
2101 float expansionAlpha = MathUtils.map(
2102 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
2103 getExpandedFraction());
2104 float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
2105 alpha *= mBottomAreaShadeAlpha;
2106 mKeyguardBottomArea.setAffordanceAlpha(alpha);
2107 mKeyguardBottomArea.setImportantForAccessibility(
2108 alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
2109 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
2110 View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
2111 if (ambientIndicationContainer != null) {
2112 ambientIndicationContainer.setAlpha(alpha);
2113 }
2114 }
2115
2116 /**
2117 * Custom clock fades away when user drags up to unlock or pulls down quick settings.
2118 *
2119 * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
2120 * {@link #updateKeyguardBottomAreaAlpha}.
2121 */
2122 private void updateBigClockAlpha() {
2123 float expansionAlpha = MathUtils.map(
2124 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
2125 getExpandedFraction());
2126 float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
2127 mBigClockContainer.setAlpha(alpha);
2128 }
2129
2130 @Override
2131 protected void onExpandingStarted() {
2132 super.onExpandingStarted();
2133 mNotificationStackScroller.onExpansionStarted();
2134 mIsExpanding = true;
2135 mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
2136 if (mQsExpanded) {
2137 onQsExpansionStarted();
2138 }
2139 // Since there are QS tiles in the header now, we need to make sure we start listening
2140 // immediately so they can be up to date.
2141 if (mQs == null) return;
2142 mQs.setHeaderListening(true);
2143 }
2144
2145 @Override
2146 protected void onExpandingFinished() {
2147 super.onExpandingFinished();
2148 mNotificationStackScroller.onExpansionStopped();
2149 mHeadsUpManager.onExpandingFinished();
Steve Elliott239e6cb2020-03-25 14:26:42 -04002150 mUnreadConversationBadgeManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
Dave Mankoffaf8163f2020-01-08 14:24:35 -05002151 mIsExpanding = false;
2152 if (isFullyCollapsed()) {
2153 DejankUtils.postAfterTraversal(new Runnable() {
2154 @Override
2155 public void run() {
2156 setListening(false);
2157 }
2158 });
2159
2160 // Workaround b/22639032: Make sure we invalidate something because else RenderThread
2161 // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
2162 // ahead with rendering and we jank.
2163 mView.postOnAnimation(new Runnable() {
2164 @Override
2165 public void run() {
2166 mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT);
2167 }
2168 });
2169 } else {
2170 setListening(true);
2171 }
2172 mQsExpandImmediate = false;
2173 mNotificationStackScroller.setShouldShowShelfOnly(false);
2174 mTwoFingerQsExpandPossible = false;
2175 notifyListenersTrackingHeadsUp(null);
2176 mExpandingFromHeadsUp = false;
2177 setPanelScrimMinFraction(0.0f);
2178 }
2179
2180 private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
2181 for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
2182 Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
2183 listener.accept(pickedChild);
2184 }
2185 }
2186
2187 private void setListening(boolean listening) {
2188 mKeyguardStatusBar.setListening(listening);
2189 if (mQs == null) return;
2190 mQs.setListening(listening);
Dave Mankoffaf8163f2020-01-08 14:24:35 -05002191 }
2192
2193 @Override
2194 public void expand(boolean animate) {
2195 super.expand(animate);
2196 setListening(true);
2197 }
2198
2199 @Override
2200 protected void setOverExpansion(float overExpansion, boolean isPixels) {
2201 if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
2202 return;
2203 }
2204 if (mBarState != StatusBarState.KEYGUARD) {
2205 mNotificationStackScroller.setOnHeightChangedListener(null);
2206 if (isPixels) {
2207 mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */,
2208 false /* animate */);
2209 } else {
2210 mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */,
2211 false /* animate */);
2212 }
2213 mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
2214 }
2215 }
2216
2217 @Override
2218 protected void onTrackingStarted() {
2219 mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
2220 super.onTrackingStarted();
2221 if (mQsFullyExpanded) {
2222 mQsExpandImmediate = true;
2223 mNotificationStackScroller.setShouldShowShelfOnly(true);
2224 }
2225 if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
2226 mAffordanceHelper.animateHideLeftRightIcon();
2227 }
2228 mNotificationStackScroller.onPanelTrackingStarted();
2229 }
2230
2231 @Override
2232 protected void onTrackingStopped(boolean expand) {
2233 mFalsingManager.onTrackingStopped();
2234 super.onTrackingStopped(expand);
2235 if (expand) {
2236 mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
2237 true /* animate */);
2238 }
2239 mNotificationStackScroller.onPanelTrackingStopped();
2240 if (expand && (mBarState == StatusBarState.KEYGUARD
2241 || mBarState == StatusBarState.SHADE_LOCKED)) {
2242 if (!mHintAnimationRunning) {
2243 mAffordanceHelper.reset(true);
2244 }
2245 }
2246 }
2247
2248 private void updateMaxHeadsUpTranslation() {
2249 mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
2250 }
2251
2252 @Override
2253 protected void startUnlockHintAnimation() {
2254 if (mPowerManager.isPowerSaveMode()) {
2255 onUnlockHintStarted();
2256 onUnlockHintFinished();
2257 return;
2258 }
2259 super.startUnlockHintAnimation();
2260 }
2261
2262 @Override
2263 protected void onUnlockHintFinished() {
2264 super.onUnlockHintFinished();
2265 mNotificationStackScroller.setUnlockHintRunning(false);
2266 }
2267
2268 @Override
2269 protected void onUnlockHintStarted() {
2270 super.onUnlockHintStarted();
2271 mNotificationStackScroller.setUnlockHintRunning(true);
2272 }
2273
2274 @Override
2275 protected float getPeekHeight() {
2276 if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
2277 return mNotificationStackScroller.getPeekHeight();
2278 } else {
2279 return mQsMinExpansionHeight;
2280 }
2281 }
2282
2283 @Override
2284 protected boolean shouldUseDismissingAnimation() {
2285 return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
2286 || !isTracking());
2287 }
2288
2289 @Override
2290 protected boolean fullyExpandedClearAllVisible() {
2291 return mNotificationStackScroller.isFooterViewNotGone()
2292 && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
2293 }
2294
2295 @Override
2296 protected boolean isClearAllVisible() {
2297 return mNotificationStackScroller.isFooterViewContentVisible();
2298 }
2299
2300 @Override
2301 protected int getClearAllHeight() {
2302 return mNotificationStackScroller.getFooterViewHeight();
2303 }
2304
2305 @Override
2306 protected boolean isTrackingBlocked() {
2307 return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
2308 }
2309
2310 public boolean isQsExpanded() {
2311 return mQsExpanded;
2312 }
2313
2314 public boolean isQsDetailShowing() {
2315 return mQs.isShowingDetail();
2316 }
2317
2318 public void closeQsDetail() {
2319 mQs.closeDetail();
2320 }
2321
2322 public boolean isLaunchTransitionFinished() {
2323 return mIsLaunchTransitionFinished;
2324 }
2325
2326 public boolean isLaunchTransitionRunning() {
2327 return mIsLaunchTransitionRunning;
2328 }
2329
2330 public void setLaunchTransitionEndRunnable(Runnable r) {
2331 mLaunchAnimationEndRunnable = r;
2332 }
2333
2334 private void updateDozingVisibilities(boolean animate) {
2335 mKeyguardBottomArea.setDozing(mDozing, animate);
2336 if (!mDozing && animate) {
2337 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2338 }
2339 }
2340
2341 @Override
2342 public boolean isDozing() {
2343 return mDozing;
2344 }
2345
2346 public void showEmptyShadeView(boolean emptyShadeViewVisible) {
2347 mShowEmptyShadeView = emptyShadeViewVisible;
2348 updateEmptyShadeView();
2349 }
2350
2351 private void updateEmptyShadeView() {
2352 // Hide "No notifications" in QS.
2353 mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
2354 }
2355
2356 public void setQsScrimEnabled(boolean qsScrimEnabled) {
2357 boolean changed = mQsScrimEnabled != qsScrimEnabled;
2358 mQsScrimEnabled = qsScrimEnabled;
2359 if (changed) {
2360 updateQsState();
2361 }
2362 }
2363
2364 public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
2365 mKeyguardUserSwitcher = keyguardUserSwitcher;
2366 }
2367
2368 public void onScreenTurningOn() {
2369 mKeyguardStatusView.dozeTimeTick();
2370 }
2371
2372 @Override
2373 protected boolean onMiddleClicked() {
2374 switch (mBarState) {
2375 case StatusBarState.KEYGUARD:
2376 if (!mDozingOnDown) {
2377 if (mKeyguardBypassController.getBypassEnabled()) {
2378 mUpdateMonitor.requestFaceAuth();
2379 } else {
2380 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
2381 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
2382 startUnlockHintAnimation();
2383 }
2384 }
2385 return true;
2386 case StatusBarState.SHADE_LOCKED:
2387 if (!mQsExpanded) {
2388 mStatusBarStateController.setState(StatusBarState.KEYGUARD);
2389 }
2390 return true;
2391 case StatusBarState.SHADE:
2392
2393 // This gets called in the middle of the touch handling, where the state is still
2394 // that we are tracking the panel. Collapse the panel after this is done.
2395 mView.post(mPostCollapseRunnable);
2396 return false;
2397 default:
2398 return true;
2399 }
2400 }
2401
2402 public void setPanelAlpha(int alpha, boolean animate) {
2403 if (mPanelAlpha != alpha) {
2404 mPanelAlpha = alpha;
2405 PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
2406 ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
2407 animate);
2408 }
2409 }
2410
2411 public void setPanelAlphaEndAction(Runnable r) {
2412 mPanelAlphaEndAction = r;
2413 }
2414
2415 private void updateKeyguardStatusBarForHeadsUp() {
2416 boolean
2417 showingKeyguardHeadsUp =
2418 mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
2419 if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
2420 mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
2421 if (mKeyguardShowing) {
2422 PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
2423 showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
2424 true /* animate */);
2425 } else {
2426 PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
2427 }
2428 }
2429 }
2430
2431 private void setKeyguardHeadsUpShowingAmount(float amount) {
2432 mKeyguardHeadsUpShowingAmount = amount;
2433 updateHeaderKeyguardAlpha();
2434 }
2435
2436 private float getKeyguardHeadsUpShowingAmount() {
2437 return mKeyguardHeadsUpShowingAmount;
2438 }
2439
2440 public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
2441 mHeadsUpAnimatingAway = headsUpAnimatingAway;
2442 mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
2443 updateHeadsUpVisibility();
2444 }
2445
2446 private void updateHeadsUpVisibility() {
2447 ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
2448 }
2449
2450 @Override
2451 public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
2452 super.setHeadsUpManager(headsUpManager);
2453 mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
2454 mNotificationStackScroller.getHeadsUpCallback(),
2455 NotificationPanelViewController.this);
2456 }
2457
2458 public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
2459 if (pickedChild != null) {
2460 notifyListenersTrackingHeadsUp(pickedChild);
2461 mExpandingFromHeadsUp = true;
2462 }
2463 // otherwise we update the state when the expansion is finished
2464 }
2465
2466 @Override
2467 protected void onClosingFinished() {
2468 super.onClosingFinished();
2469 resetHorizontalPanelPosition();
2470 setClosingWithAlphaFadeout(false);
2471 }
2472
2473 private void setClosingWithAlphaFadeout(boolean closing) {
2474 mClosingWithAlphaFadeOut = closing;
2475 mNotificationStackScroller.forceNoOverlappingRendering(closing);
2476 }
2477
2478 /**
2479 * Updates the vertical position of the panel so it is positioned closer to the touch
2480 * responsible for opening the panel.
2481 *
2482 * @param x the x-coordinate the touch event
2483 */
2484 protected void updateVerticalPanelPosition(float x) {
2485 if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) {
2486 resetHorizontalPanelPosition();
2487 return;
2488 }
2489 float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
2490 float
2491 rightMost =
2492 mView.getWidth() - mPositionMinSideMargin
2493 - mNotificationStackScroller.getWidth() / 2;
2494 if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
2495 x = mView.getWidth() / 2;
2496 }
2497 x = Math.min(rightMost, Math.max(leftMost, x));
2498 float
2499 center =
2500 mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
2501 setHorizontalPanelTranslation(x - center);
2502 }
2503
2504 private void resetHorizontalPanelPosition() {
2505 setHorizontalPanelTranslation(0f);
2506 }
2507
2508 protected void setHorizontalPanelTranslation(float translation) {
2509 mNotificationStackScroller.setTranslationX(translation);
2510 mQsFrame.setTranslationX(translation);
2511 int size = mVerticalTranslationListener.size();
2512 for (int i = 0; i < size; i++) {
2513 mVerticalTranslationListener.get(i).run();
2514 }
2515 }
2516
2517 protected void updateExpandedHeight(float expandedHeight) {
2518 if (mTracking) {
2519 mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
2520 }
2521 if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
2522 // The expandedHeight is always the full panel Height when bypassing
2523 expandedHeight = getMaxPanelHeightNonBypass();
2524 }
2525 mNotificationStackScroller.setExpandedHeight(expandedHeight);
2526 updateKeyguardBottomAreaAlpha();
2527 updateBigClockAlpha();
2528 updateStatusBarIcons();
2529 }
2530
2531 /**
2532 * @return whether the notifications are displayed full width and don't have any margins on
2533 * the side.
2534 */
2535 public boolean isFullWidth() {
2536 return mIsFullWidth;
2537 }
2538
2539 private void updateStatusBarIcons() {
2540 boolean
2541 showIconsWhenExpanded =
2542 (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
2543 && getExpandedHeight() < getOpeningHeight();
2544 boolean noVisibleNotifications = true;
2545 if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) {
2546 showIconsWhenExpanded = false;
2547 }
2548 if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
2549 mShowIconsWhenExpanded = showIconsWhenExpanded;
2550 mCommandQueue.recomputeDisableFlags(mDisplayId, false);
2551 }
2552 }
2553
2554 private boolean isOnKeyguard() {
2555 return mBarState == StatusBarState.KEYGUARD;
2556 }
2557
2558 public void setPanelScrimMinFraction(float minFraction) {
2559 mBar.panelScrimMinFractionChanged(minFraction);
2560 }
2561
2562 public void clearNotificationEffects() {
2563 mStatusBar.clearNotificationEffects();
2564 }
2565
2566 @Override
2567 protected boolean isPanelVisibleBecauseOfHeadsUp() {
2568 return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
2569 && mBarState == StatusBarState.SHADE;
2570 }
2571
2572 public void launchCamera(boolean animate, int source) {
2573 if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
2574 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
2575 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
2576 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
2577 } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
2578 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
2579 } else {
2580
2581 // Default.
2582 mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
2583 }
2584
2585 // If we are launching it when we are occluded already we don't want it to animate,
2586 // nor setting these flags, since the occluded state doesn't change anymore, hence it's
2587 // never reset.
2588 if (!isFullyCollapsed()) {
2589 setLaunchingAffordance(true);
2590 } else {
2591 animate = false;
2592 }
2593 mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
2594 mAffordanceHelper.launchAffordance(
2595 animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
2596 }
2597
2598 public void onAffordanceLaunchEnded() {
2599 setLaunchingAffordance(false);
2600 }
2601
2602 /**
2603 * Set whether we are currently launching an affordance. This is currently only set when
2604 * launched via a camera gesture.
2605 */
2606 private void setLaunchingAffordance(boolean launchingAffordance) {
2607 mLaunchingAffordance = launchingAffordance;
2608 mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
2609 mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
2610 launchingAffordance);
2611 mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
2612 if (mAffordanceLaunchListener != null) {
2613 mAffordanceLaunchListener.accept(launchingAffordance);
2614 }
2615 }
2616
2617 /**
2618 * Return true when a bottom affordance is launching an occluded activity with a splash screen.
2619 */
2620 public boolean isLaunchingAffordanceWithPreview() {
2621 return mLaunchingAffordance && mAffordanceHasPreview;
2622 }
2623
2624 /**
2625 * Whether the camera application can be launched for the camera launch gesture.
2626 */
2627 public boolean canCameraGestureBeLaunched() {
2628 if (!mStatusBar.isCameraAllowedByAdmin()) {
2629 return false;
2630 }
2631
2632 ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
2633 String
2634 packageToLaunch =
2635 (resolveInfo == null || resolveInfo.activityInfo == null) ? null
2636 : resolveInfo.activityInfo.packageName;
2637 return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
2638 packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
2639 }
2640
2641 /**
2642 * Return true if the applications with the package name is running in foreground.
2643 *
2644 * @param pkgName application package name.
2645 */
2646 private boolean isForegroundApp(String pkgName) {
2647 List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
2648 return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
2649 }
2650
2651 private void setGroupManager(NotificationGroupManager groupManager) {
2652 mGroupManager = groupManager;
2653 }
2654
2655 public boolean hideStatusBarIconsWhenExpanded() {
2656 if (mLaunchingNotification) {
2657 return mHideIconsDuringNotificationLaunch;
2658 }
2659 if (mHeadsUpAppearanceController != null
2660 && mHeadsUpAppearanceController.shouldBeVisible()) {
2661 return false;
2662 }
2663 return !isFullWidth() || !mShowIconsWhenExpanded;
2664 }
2665
2666 private final FragmentListener mFragmentListener = new FragmentListener() {
2667 @Override
2668 public void onFragmentViewCreated(String tag, Fragment fragment) {
2669 mQs = (QS) fragment;
2670 mQs.setPanelView(mHeightListener);
2671 mQs.setExpandClickListener(mOnClickListener);
2672 mQs.setHeaderClickable(mQsExpansionEnabled);
2673 updateQSPulseExpansion();
2674 mQs.setOverscrolling(mStackScrollerOverscrolling);
2675
2676 // recompute internal state when qspanel height changes
2677 mQs.getView().addOnLayoutChangeListener(
2678 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
2679 final int height = bottom - top;
2680 final int oldHeight = oldBottom - oldTop;
2681 if (height != oldHeight) {
2682 mHeightListener.onQsHeightChanged();
2683 }
2684 });
2685 mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
2686 if (mQs instanceof QSFragment) {
2687 mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
2688 }
2689 updateQsExpansion();
2690 }
2691
2692 @Override
2693 public void onFragmentViewDestroyed(String tag, Fragment fragment) {
2694 // Manual handling of fragment lifecycle is only required because this bridges
2695 // non-fragment and fragment code. Once we are using a fragment for the notification
2696 // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
2697 if (fragment == mQs) {
2698 mQs = null;
2699 }
2700 }
2701 };
2702
2703 @Override
2704 public void setTouchAndAnimationDisabled(boolean disabled) {
2705 super.setTouchAndAnimationDisabled(disabled);
2706 if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
2707 mAffordanceHelper.reset(false /* animate */);
2708 }
2709 mNotificationStackScroller.setAnimationsEnabled(!disabled);
2710 }
2711
2712 /**
2713 * Sets the dozing state.
2714 *
2715 * @param dozing {@code true} when dozing.
2716 * @param animate if transition should be animated.
2717 * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
2718 */
2719 public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
2720 if (dozing == mDozing) return;
2721 mView.setDozing(dozing);
2722 mDozing = dozing;
2723 mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
2724 mKeyguardBottomArea.setDozing(mDozing, animate);
2725
2726 if (dozing) {
2727 mBottomAreaShadeAlphaAnimator.cancel();
2728 }
2729
2730 if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
2731 updateDozingVisibilities(animate);
2732 }
2733
2734 final float dozeAmount = dozing ? 1 : 0;
2735 mStatusBarStateController.setDozeAmount(dozeAmount, animate);
2736 }
2737
2738 public void setPulsing(boolean pulsing) {
2739 mPulsing = pulsing;
2740 final boolean
2741 animatePulse =
2742 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
2743 if (animatePulse) {
2744 mAnimateNextPositionUpdate = true;
2745 }
2746 // Do not animate the clock when waking up from a pulse.
2747 // The height callback will take care of pushing the clock to the right position.
2748 if (!mPulsing && !mDozing) {
2749 mAnimateNextPositionUpdate = false;
2750 }
2751 mNotificationStackScroller.setPulsing(pulsing, animatePulse);
2752 mKeyguardStatusView.setPulsing(pulsing);
2753 }
2754
2755 public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
2756 if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
2757 mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
2758 mStatusBar.updateKeyguardMaxNotifications();
2759 }
2760 }
2761
2762 public void dozeTimeTick() {
2763 mKeyguardBottomArea.dozeTimeTick();
2764 mKeyguardStatusView.dozeTimeTick();
2765 if (mInterpolatedDarkAmount > 0) {
2766 positionClockAndNotifications();
2767 }
2768 }
2769
2770 public void setStatusAccessibilityImportance(int mode) {
2771 mKeyguardStatusView.setImportantForAccessibility(mode);
2772 }
2773
2774 /**
2775 * TODO: this should be removed.
2776 * It's not correct to pass this view forward because other classes will end up adding
2777 * children to it. Theme will be out of sync.
2778 *
2779 * @return bottom area view
2780 */
2781 public KeyguardBottomAreaView getKeyguardBottomAreaView() {
2782 return mKeyguardBottomArea;
2783 }
2784
2785 public void setUserSetupComplete(boolean userSetupComplete) {
2786 mUserSetupComplete = userSetupComplete;
2787 mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
2788 }
2789
2790 public void applyExpandAnimationParams(ExpandAnimationParameters params) {
2791 mExpandOffset = params != null ? params.getTopChange() : 0;
2792 updateQsExpansion();
2793 if (params != null) {
2794 boolean hideIcons = params.getProgress(
2795 ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
2796 if (hideIcons != mHideIconsDuringNotificationLaunch) {
2797 mHideIconsDuringNotificationLaunch = hideIcons;
2798 if (!hideIcons) {
2799 mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
2800 }
2801 }
2802 }
2803 }
2804
2805 public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
2806 mTrackingHeadsUpListeners.add(listener);
2807 }
2808
2809 public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
2810 mTrackingHeadsUpListeners.remove(listener);
2811 }
2812
2813 public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
2814 mVerticalTranslationListener.add(verticalTranslationListener);
2815 }
2816
2817 public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
2818 mVerticalTranslationListener.remove(verticalTranslationListener);
2819 }
2820
2821 public void setHeadsUpAppearanceController(
2822 HeadsUpAppearanceController headsUpAppearanceController) {
2823 mHeadsUpAppearanceController = headsUpAppearanceController;
2824 }
2825
2826 /**
2827 * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
2828 * security view of the bouncer.
2829 */
2830 public void onBouncerPreHideAnimation() {
2831 setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
2832 false /* goingToFullShade */);
2833 }
2834
2835 /**
2836 * Do not let the user drag the shade up and down for the current touch session.
2837 * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
2838 */
2839 public void blockExpansionForCurrentTouch() {
2840 mBlockingExpansionForCurrentTouch = mTracking;
2841 }
2842
2843 @Override
2844 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2845 super.dump(fd, pw, args);
2846 pw.println(" gestureExclusionRect: " + calculateGestureExclusionRect());
2847 if (mKeyguardStatusBar != null) {
2848 mKeyguardStatusBar.dump(fd, pw, args);
2849 }
2850 if (mKeyguardStatusView != null) {
2851 mKeyguardStatusView.dump(fd, pw, args);
2852 }
2853 }
2854
2855 public boolean hasActiveClearableNotifications() {
2856 return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
2857 }
2858
2859 private void updateShowEmptyShadeView() {
2860 boolean
2861 showEmptyShadeView =
2862 mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
2863 showEmptyShadeView(showEmptyShadeView);
2864 }
2865
2866 public RemoteInputController.Delegate createRemoteInputDelegate() {
2867 return mNotificationStackScroller.createDelegate();
2868 }
2869
2870 public void updateNotificationViews() {
2871 mNotificationStackScroller.updateSectionBoundaries();
2872 mNotificationStackScroller.updateSpeedBumpIndex();
2873 mNotificationStackScroller.updateFooter();
2874 updateShowEmptyShadeView();
2875 mNotificationStackScroller.updateIconAreaViews();
2876 }
2877
2878 public void onUpdateRowStates() {
2879 mNotificationStackScroller.onUpdateRowStates();
2880 }
2881
2882 public boolean hasPulsingNotifications() {
2883 return mNotificationStackScroller.hasPulsingNotifications();
2884 }
2885
2886 public ActivatableNotificationView getActivatedChild() {
2887 return mNotificationStackScroller.getActivatedChild();
2888 }
2889
2890 public void setActivatedChild(ActivatableNotificationView o) {
2891 mNotificationStackScroller.setActivatedChild(o);
2892 }
2893
2894 public void runAfterAnimationFinished(Runnable r) {
2895 mNotificationStackScroller.runAfterAnimationFinished(r);
2896 }
2897
2898 public void setScrollingEnabled(boolean b) {
2899 mNotificationStackScroller.setScrollingEnabled(b);
2900 }
2901
2902 public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
2903 NotificationShelf notificationShelf,
2904 NotificationIconAreaController notificationIconAreaController,
2905 ScrimController scrimController) {
2906 setStatusBar(statusBar);
2907 setGroupManager(mGroupManager);
2908 mNotificationStackScroller.setNotificationPanelController(this);
2909 mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
2910 mNotificationStackScroller.setStatusBar(statusBar);
2911 mNotificationStackScroller.setGroupManager(groupManager);
2912 mNotificationStackScroller.setShelf(notificationShelf);
2913 mNotificationStackScroller.setScrimController(scrimController);
2914 updateShowEmptyShadeView();
2915 }
2916
2917 public void showTransientIndication(int id) {
2918 mKeyguardIndicationController.showTransientIndication(id);
2919 }
2920
2921 public void setOnReinflationListener(Runnable onReinflationListener) {
2922 mOnReinflationListener = onReinflationListener;
2923 }
2924
Dave Mankoffaf8163f2020-01-08 14:24:35 -05002925 public void setAlpha(float alpha) {
2926 mView.setAlpha(alpha);
2927 }
2928
2929 public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
2930 return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
2931 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
2932 endAction);
2933 }
2934
2935 public void resetViewGroupFade() {
2936 ViewGroupFadeHelper.reset(mView);
2937 }
2938
2939 public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
2940 mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
2941 }
2942
2943 public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
2944 mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
2945 }
2946
2947 public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() {
2948 return mOnHeadsUpChangedListener;
2949 }
2950
2951 public int getHeight() {
2952 return mView.getHeight();
2953 }
2954
2955 public TextView getHeaderDebugInfo() {
2956 return mView.findViewById(R.id.header_debug_info);
2957 }
2958
2959 public void onThemeChanged() {
2960 mConfigurationListener.onThemeChanged();
2961 }
2962
2963 @Override
2964 public OnLayoutChangeListener createLayoutChangeListener() {
2965 return new OnLayoutChangeListener();
2966 }
2967
2968 public void setEmptyDragAmount(float amount) {
2969 mExpansionCallback.setEmptyDragAmount(amount);
2970 }
2971
2972 @Override
2973 protected TouchHandler createTouchHandler() {
2974 return new TouchHandler() {
2975 @Override
2976 public boolean onInterceptTouchEvent(MotionEvent event) {
2977 if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
2978 return false;
2979 }
2980 initDownStates(event);
2981 // Do not let touches go to shade or QS if the bouncer is visible,
2982 // but still let user swipe down to expand the panel, dismissing the bouncer.
2983 if (mStatusBar.isBouncerShowing()) {
2984 return true;
2985 }
2986 if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
2987 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
2988 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
2989 return true;
2990 }
2991 if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
2992 && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
2993 return true;
2994 }
2995
2996 if (!isFullyCollapsed() && onQsIntercept(event)) {
2997 return true;
2998 }
2999 return super.onInterceptTouchEvent(event);
3000 }
3001
3002 @Override
3003 public boolean onTouch(View v, MotionEvent event) {
3004 if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
3005 return false;
3006 }
3007
3008 // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
3009 // to pull down QS or expand the shade.
3010 if (mStatusBar.isBouncerShowingScrimmed()) {
3011 return false;
3012 }
3013
3014 // Make sure the next touch won't the blocked after the current ends.
3015 if (event.getAction() == MotionEvent.ACTION_UP
3016 || event.getAction() == MotionEvent.ACTION_CANCEL) {
3017 mBlockingExpansionForCurrentTouch = false;
3018 }
3019 // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
3020 // without any ACTION_MOVE event.
3021 // In such case, simply expand the panel instead of being stuck at the bottom bar.
3022 if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
3023 expand(true /* animate */);
3024 }
3025 initDownStates(event);
3026 if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
3027 && mPulseExpansionHandler.onTouchEvent(event)) {
3028 // We're expanding all the other ones shouldn't get this anymore
3029 return true;
3030 }
3031 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
3032 && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
3033 mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
3034 }
3035 boolean handled = false;
3036 if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
3037 && mBarState != StatusBarState.SHADE && !mDozing) {
3038 handled |= mAffordanceHelper.onTouchEvent(event);
3039 }
3040 if (mOnlyAffordanceInThisMotion) {
3041 return true;
3042 }
3043 handled |= mHeadsUpTouchHelper.onTouchEvent(event);
3044
3045 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
3046 return true;
3047 }
3048 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
3049 mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
3050 updateVerticalPanelPosition(event.getX());
3051 handled = true;
3052 }
3053 handled |= super.onTouch(v, event);
3054 return !mDozing || mPulsing || handled;
3055 }
3056 };
3057 }
3058
3059 @Override
3060 protected PanelViewController.OnConfigurationChangedListener
3061 createOnConfigurationChangedListener() {
3062 return new OnConfigurationChangedListener();
3063 }
3064
3065 private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
3066 @Override
3067 public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
3068
3069 // Block update if we are in quick settings and just the top padding changed
3070 // (i.e. view == null).
3071 if (view == null && mQsExpanded) {
3072 return;
3073 }
3074 if (needsAnimation && mInterpolatedDarkAmount == 0) {
3075 mAnimateNextPositionUpdate = true;
3076 }
3077 ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
3078 ExpandableNotificationRow
3079 firstRow =
3080 firstChildNotGone instanceof ExpandableNotificationRow
3081 ? (ExpandableNotificationRow) firstChildNotGone : null;
3082 if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
3083 == firstRow))) {
3084 requestScrollerTopPaddingUpdate(false /* animate */);
3085 }
3086 requestPanelHeightUpdate();
3087 }
3088
3089 @Override
3090 public void onReset(ExpandableView view) {
3091 }
3092 }
3093
3094 private class OnClickListener implements View.OnClickListener {
3095 @Override
3096 public void onClick(View v) {
3097 onQsExpansionStarted();
3098 if (mQsExpanded) {
3099 flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
3100 true /* isClick */);
3101 } else if (mQsExpansionEnabled) {
3102 mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
3103 flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
3104 true /* isClick */);
3105 }
3106 }
3107 }
3108
3109 private class OnOverscrollTopChangedListener implements
3110 NotificationStackScrollLayout.OnOverscrollTopChangedListener {
3111 @Override
3112 public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
3113 cancelQsAnimation();
3114 if (!mQsExpansionEnabled) {
3115 amount = 0f;
3116 }
3117 float rounded = amount >= 1f ? amount : 0f;
3118 setOverScrolling(rounded != 0f && isRubberbanded);
3119 mQsExpansionFromOverscroll = rounded != 0f;
3120 mLastOverscroll = rounded;
3121 updateQsState();
3122 setQsExpansion(mQsMinExpansionHeight + rounded);
3123 }
3124
3125 @Override
3126 public void flingTopOverscroll(float velocity, boolean open) {
3127 mLastOverscroll = 0f;
3128 mQsExpansionFromOverscroll = false;
3129 setQsExpansion(mQsExpansionHeight);
3130 flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
3131 open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> {
3132 mStackScrollerOverscrolling = false;
3133 setOverScrolling(false);
3134 updateQsState();
3135 }, false /* isClick */);
3136 }
3137 }
3138
3139 private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener {
3140 @Override
3141 public void onDynamicPrivacyChanged() {
3142 // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
3143 // of sync with the notification panel.
3144 if (mLinearDarkAmount != 0) {
3145 return;
3146 }
3147 mAnimateNextPositionUpdate = true;
3148 }
3149 }
3150
3151 private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
3152 @Override
3153 public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
3154 boolean
3155 start =
3156 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
3157 : !rightPage;
3158 mIsLaunchTransitionRunning = true;
3159 mLaunchAnimationEndRunnable = null;
3160 float displayDensity = mStatusBar.getDisplayDensity();
3161 int lengthDp = Math.abs((int) (translation / displayDensity));
3162 int velocityDp = Math.abs((int) (vel / displayDensity));
3163 if (start) {
3164 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
3165
3166 mFalsingManager.onLeftAffordanceOn();
3167 if (mFalsingManager.shouldEnforceBouncer()) {
3168 mStatusBar.executeRunnableDismissingKeyguard(
3169 () -> mKeyguardBottomArea.launchLeftAffordance(), null,
3170 true /* dismissShade */, false /* afterKeyguardGone */,
3171 true /* deferred */);
3172 } else {
3173 mKeyguardBottomArea.launchLeftAffordance();
3174 }
3175 } else {
3176 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
3177 mLastCameraLaunchSource)) {
3178 mLockscreenGestureLogger.write(
3179 MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
3180 }
3181 mFalsingManager.onCameraOn();
3182 if (mFalsingManager.shouldEnforceBouncer()) {
3183 mStatusBar.executeRunnableDismissingKeyguard(
3184 () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
3185 true /* dismissShade */, false /* afterKeyguardGone */,
3186 true /* deferred */);
3187 } else {
3188 mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
3189 }
3190 }
3191 mStatusBar.startLaunchTransitionTimeout();
3192 mBlockTouches = true;
3193 }
3194
3195 @Override
3196 public void onAnimationToSideEnded() {
3197 mIsLaunchTransitionRunning = false;
3198 mIsLaunchTransitionFinished = true;
3199 if (mLaunchAnimationEndRunnable != null) {
3200 mLaunchAnimationEndRunnable.run();
3201 mLaunchAnimationEndRunnable = null;
3202 }
3203 mStatusBar.readyForKeyguardDone();
3204 }
3205
3206 @Override
3207 public float getMaxTranslationDistance() {
3208 return (float) Math.hypot(mView.getWidth(), getHeight());
3209 }
3210
3211 @Override
3212 public void onSwipingStarted(boolean rightIcon) {
3213 mFalsingManager.onAffordanceSwipingStarted(rightIcon);
3214 boolean
3215 camera =
3216 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
3217 : rightIcon;
3218 if (camera) {
3219 mKeyguardBottomArea.bindCameraPrewarmService();
3220 }
3221 mView.requestDisallowInterceptTouchEvent(true);
3222 mOnlyAffordanceInThisMotion = true;
3223 mQsTracking = false;
3224 }
3225
3226 @Override
3227 public void onSwipingAborted() {
3228 mFalsingManager.onAffordanceSwipingAborted();
3229 mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
3230 }
3231
3232 @Override
3233 public void onIconClicked(boolean rightIcon) {
3234 if (mHintAnimationRunning) {
3235 return;
3236 }
3237 mHintAnimationRunning = true;
3238 mAffordanceHelper.startHintAnimation(rightIcon, () -> {
3239 mHintAnimationRunning = false;
3240 mStatusBar.onHintFinished();
3241 });
3242 rightIcon =
3243 mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
3244 : rightIcon;
3245 if (rightIcon) {
3246 mStatusBar.onCameraHintStarted();
3247 } else {
3248 if (mKeyguardBottomArea.isLeftVoiceAssist()) {
3249 mStatusBar.onVoiceAssistHintStarted();
3250 } else {
3251 mStatusBar.onPhoneHintStarted();
3252 }
3253 }
3254 }
3255
3256 @Override
3257 public KeyguardAffordanceView getLeftIcon() {
3258 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3259 ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
3260 }
3261
3262 @Override
3263 public KeyguardAffordanceView getRightIcon() {
3264 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3265 ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
3266 }
3267
3268 @Override
3269 public View getLeftPreview() {
3270 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3271 ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
3272 }
3273
3274 @Override
3275 public View getRightPreview() {
3276 return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3277 ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
3278 }
3279
3280 @Override
3281 public float getAffordanceFalsingFactor() {
3282 return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
3283 }
3284
3285 @Override
3286 public boolean needsAntiFalsing() {
3287 return mBarState == StatusBarState.KEYGUARD;
3288 }
3289 }
3290
3291 private class OnEmptySpaceClickListener implements
3292 NotificationStackScrollLayout.OnEmptySpaceClickListener {
3293 @Override
3294 public void onEmptySpaceClicked(float x, float y) {
3295 onEmptySpaceClick(x);
3296 }
3297 }
3298
3299 private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
3300 @Override
3301 public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
3302 mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
3303 if (inPinnedMode) {
3304 mHeadsUpExistenceChangedRunnable.run();
3305 updateNotificationTranslucency();
3306 } else {
3307 setHeadsUpAnimatingAway(true);
3308 mNotificationStackScroller.runAfterAnimationFinished(
3309 mHeadsUpExistenceChangedRunnable);
3310 }
3311 updateGestureExclusionRect();
3312 mHeadsUpPinnedMode = inPinnedMode;
3313 updateHeadsUpVisibility();
3314 updateKeyguardStatusBarForHeadsUp();
3315 }
3316
3317 @Override
3318 public void onHeadsUpPinned(NotificationEntry entry) {
3319 if (!isOnKeyguard()) {
3320 mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
3321 true);
3322 }
3323 }
3324
3325 @Override
3326 public void onHeadsUpUnPinned(NotificationEntry entry) {
3327
3328 // When we're unpinning the notification via active edge they remain heads-upped,
3329 // we need to make sure that an animation happens in this case, otherwise the
3330 // notification
3331 // will stick to the top without any interaction.
3332 if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
3333 mNotificationStackScroller.generateHeadsUpAnimation(
3334 entry.getHeadsUpAnimationView(), false);
3335 entry.setHeadsUpIsVisible();
3336 }
3337 }
3338
3339 @Override
3340 public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
3341 mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
3342 }
3343 }
3344
3345 private class HeightListener implements QS.HeightListener {
3346 public void onQsHeightChanged() {
3347 mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
3348 if (mQsExpanded && mQsFullyExpanded) {
3349 mQsExpansionHeight = mQsMaxExpansionHeight;
3350 requestScrollerTopPaddingUpdate(false /* animate */);
3351 requestPanelHeightUpdate();
3352 }
3353 if (mAccessibilityManager.isEnabled()) {
3354 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
3355 }
3356 mNotificationStackScroller.setMaxTopPadding(
3357 mQsMaxExpansionHeight + mQsNotificationTopPadding);
3358 }
3359 }
3360
3361 private class ZenModeControllerCallback implements ZenModeController.Callback {
3362 @Override
3363 public void onZenChanged(int zen) {
3364 updateShowEmptyShadeView();
3365 }
3366 }
3367
3368 private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
3369 @Override
3370 public void onDensityOrFontScaleChanged() {
3371 updateShowEmptyShadeView();
3372 }
3373
3374 @Override
3375 public void onThemeChanged() {
3376 final int themeResId = mView.getContext().getThemeResId();
3377 if (mThemeResId == themeResId) {
3378 return;
3379 }
3380 mThemeResId = themeResId;
3381
3382 reInflateViews();
3383 }
3384
3385 @Override
3386 public void onOverlayChanged() {
3387 reInflateViews();
3388 }
3389
3390 @Override
Matt Pietalf07bac42020-01-29 15:07:48 -05003391 public void onUiModeChanged() {}
Dave Mankoffaf8163f2020-01-08 14:24:35 -05003392 }
3393
3394 private class StatusBarStateListener implements StateListener {
3395 @Override
3396 public void onStateChanged(int statusBarState) {
3397 boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
3398 boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
3399 int oldState = mBarState;
3400 boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
3401 setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
3402 setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
3403
3404 mBarState = statusBarState;
3405 mKeyguardShowing = keyguardShowing;
Dave Mankoffaf8163f2020-01-08 14:24:35 -05003406
3407 if (oldState == StatusBarState.KEYGUARD && (goingToFullShade
3408 || statusBarState == StatusBarState.SHADE_LOCKED)) {
3409 animateKeyguardStatusBarOut();
3410 long
3411 delay =
3412 mBarState == StatusBarState.SHADE_LOCKED ? 0
3413 : mKeyguardStateController.calculateGoingToFullShadeDelay();
3414 mQs.animateHeaderSlidingIn(delay);
3415 } else if (oldState == StatusBarState.SHADE_LOCKED
3416 && statusBarState == StatusBarState.KEYGUARD) {
3417 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
3418 mNotificationStackScroller.resetScrollPosition();
3419 // Only animate header if the header is visible. If not, it will partially
3420 // animate out
3421 // the top of QS
3422 if (!mQsExpanded) {
3423 mQs.animateHeaderSlidingOut();
3424 }
3425 } else {
3426 mKeyguardStatusBar.setAlpha(1f);
3427 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
Dave Mankoffaf8163f2020-01-08 14:24:35 -05003428 if (keyguardShowing && oldState != mBarState) {
3429 if (mQs != null) {
3430 mQs.hideImmediately();
3431 }
3432 }
3433 }
3434 updateKeyguardStatusBarForHeadsUp();
3435 if (keyguardShowing) {
3436 updateDozingVisibilities(false /* animate */);
3437 }
3438 // THe update needs to happen after the headerSlide in above, otherwise the translation
3439 // would reset
3440 updateQSPulseExpansion();
3441 maybeAnimateBottomAreaAlpha();
3442 resetHorizontalPanelPosition();
3443 updateQsState();
3444 }
3445
3446 @Override
3447 public void onDozeAmountChanged(float linearAmount, float amount) {
3448 mInterpolatedDarkAmount = amount;
3449 mLinearDarkAmount = linearAmount;
3450 mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
3451 mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
3452 positionClockAndNotifications();
3453 }
3454 }
3455
3456 private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback {
3457 public void setEmptyDragAmount(float amount) {
3458 mEmptyDragAmount = amount * 0.2f;
3459 positionClockAndNotifications();
3460 }
3461 }
3462
3463 private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
3464 @Override
3465 public void onViewAttachedToWindow(View v) {
3466 FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
3467 mStatusBarStateController.addCallback(mStatusBarStateListener);
3468 mZenModeController.addCallback(mZenModeControllerCallback);
3469 mConfigurationController.addCallback(mConfigurationListener);
3470 mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
3471 // Theme might have changed between inflating this view and attaching it to the
3472 // window, so
3473 // force a call to onThemeChanged
3474 mConfigurationListener.onThemeChanged();
3475 }
3476
3477 @Override
3478 public void onViewDetachedFromWindow(View v) {
3479 FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener);
3480 mStatusBarStateController.removeCallback(mStatusBarStateListener);
3481 mZenModeController.removeCallback(mZenModeControllerCallback);
3482 mConfigurationController.removeCallback(mConfigurationListener);
3483 mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
3484 }
3485 }
3486
3487 private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener {
3488
3489 @Override
3490 public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
3491 int oldTop, int oldRight, int oldBottom) {
3492 DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
3493 super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
3494 setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth());
3495
3496 // Update Clock Pivot
3497 mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
3498 mKeyguardStatusView.setPivotY(
3499 (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize());
3500
3501 // Calculate quick setting heights.
3502 int oldMaxHeight = mQsMaxExpansionHeight;
3503 if (mQs != null) {
3504 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
Dave Mankoffaf8163f2020-01-08 14:24:35 -05003505 mQsMaxExpansionHeight = mQs.getDesiredHeight();
3506 mNotificationStackScroller.setMaxTopPadding(
3507 mQsMaxExpansionHeight + mQsNotificationTopPadding);
3508 }
3509 positionClockAndNotifications();
3510 if (mQsExpanded && mQsFullyExpanded) {
3511 mQsExpansionHeight = mQsMaxExpansionHeight;
3512 requestScrollerTopPaddingUpdate(false /* animate */);
3513 requestPanelHeightUpdate();
3514
3515 // Size has changed, start an animation.
3516 if (mQsMaxExpansionHeight != oldMaxHeight) {
3517 startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
3518 }
3519 } else if (!mQsExpanded) {
3520 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
3521 }
3522 updateExpandedHeight(getExpandedHeight());
3523 updateHeader();
3524
3525 // If we are running a size change animation, the animation takes care of the height of
3526 // the container. However, if we are not animating, we always need to make the QS
3527 // container
3528 // the desired height so when closing the QS detail, it stays smaller after the size
3529 // change
3530 // animation is finished but the detail view is still being animated away (this
3531 // animation
3532 // takes longer than the size change animation).
3533 if (mQsSizeChangeAnimator == null && mQs != null) {
3534 mQs.setHeightOverride(mQs.getDesiredHeight());
3535 }
3536 updateMaxHeadsUpTranslation();
3537 updateGestureExclusionRect();
3538 if (mExpandAfterLayoutRunnable != null) {
3539 mExpandAfterLayoutRunnable.run();
3540 mExpandAfterLayoutRunnable = null;
3541 }
3542 DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
3543 }
3544 }
3545
3546 private class DebugDrawable extends Drawable {
3547
3548 @Override
3549 public void draw(Canvas canvas) {
3550 Paint p = new Paint();
3551 p.setColor(Color.RED);
3552 p.setStrokeWidth(2);
3553 p.setStyle(Paint.Style.STROKE);
3554 canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
3555 p.setColor(Color.BLUE);
3556 canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
3557 p.setColor(Color.GREEN);
3558 canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
3559 calculatePanelHeightQsExpanded(), p);
3560 p.setColor(Color.YELLOW);
3561 canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
3562 calculatePanelHeightShade(), p);
3563 p.setColor(Color.MAGENTA);
3564 canvas.drawLine(
3565 0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p);
3566 p.setColor(Color.CYAN);
3567 canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
3568 mNotificationStackScroller.getTopPadding(), p);
3569 p.setColor(Color.GRAY);
3570 canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
3571 mClockPositionResult.clockY, p);
3572 }
3573
3574 @Override
3575 public void setAlpha(int alpha) {
3576
3577 }
3578
3579 @Override
3580 public void setColorFilter(ColorFilter colorFilter) {
3581
3582 }
3583
3584 @Override
3585 public int getOpacity() {
3586 return 0;
3587 }
3588 }
3589
3590 private class OnConfigurationChangedListener extends
3591 PanelViewController.OnConfigurationChangedListener {
3592 @Override
3593 public void onConfigurationChanged(Configuration newConfig) {
3594 super.onConfigurationChanged(newConfig);
3595 mAffordanceHelper.onConfigurationChanged();
3596 if (newConfig.orientation != mLastOrientation) {
3597 resetHorizontalPanelPosition();
3598 }
3599 mLastOrientation = newConfig.orientation;
3600 }
3601 }
3602
3603 private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
3604 public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
3605 mNavigationBarBottomHeight = insets.getStableInsetBottom();
3606 updateMaxHeadsUpTranslation();
3607 return insets;
3608 }
3609 }
3610}