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