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