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