From b57dd8ab1750640773cf85f45d090188eb2b418d Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Fri, 14 Jun 2019 12:27:58 -0700 Subject: [PATCH 1/5] Made sure huns can show on the lock screen even when awake Previously we only showed them if the screen was dozing Bug: 130327302 Test: atest SystemUITests Change-Id: Ib8a0fa19f8031fd2cc213e156ff89dfd24ee6fa3 --- .../NotificationWakeUpCoordinator.kt | 21 +++++++++++++------ .../collection/NotificationEntry.java | 4 ++++ .../statusbar/phone/HeadsUpManagerPhone.java | 4 ++-- .../systemui/statusbar/phone/StatusBar.java | 2 ++ 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 6dc5fb34f9bd5..bf25c455531bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -39,7 +39,7 @@ class NotificationWakeUpCoordinator @Inject constructor( private val mContext: Context, private val mHeadsUpManagerPhone: HeadsUpManagerPhone, private val mStatusBarStateController: StatusBarStateController, - private val mBypassController: KeyguardBypassController) + private val bypassController: KeyguardBypassController) : OnHeadsUpChangedListener, StatusBarStateController.StateListener { private val mNotificationVisibility @@ -67,6 +67,8 @@ class NotificationWakeUpCoordinator @Inject constructor( private var mWakingUp = false private val mEntrySetToClearWhenFinished = mutableSetOf() private val mDozeParameters: DozeParameters; + var fullyAwake: Boolean = false + var willWakeUp = false set(value) { if (!value || mDozeAmount != 0.0f) { @@ -75,7 +77,6 @@ class NotificationWakeUpCoordinator @Inject constructor( } lateinit var iconAreaController : NotificationIconAreaController - var pulsing: Boolean = false set(value) { field = value @@ -118,8 +119,15 @@ class NotificationWakeUpCoordinator @Inject constructor( private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore - var visible = (mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications()) - && pulsing + var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() + var canShow = pulsing + if (bypassController.bypassEnabled) { + // We also allow pulsing on the lock screen! + canShow = canShow || (mWakingUp || willWakeUp || fullyAwake) + && mStatusBarStateController.state == StatusBarState.KEYGUARD + } + visible = visible && canShow + if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) { // let's not make notifications invisible while waking up, otherwise the animation // is strange @@ -173,7 +181,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } private fun updateDozeAmountIfBypass(): Boolean { - if (mBypassController.bypassEnabled) { + if (bypassController.bypassEnabled) { var amount = 1.0f; if (mStatusBarStateController.state == StatusBarState.SHADE || mStatusBarStateController.state == StatusBarState.SHADE_LOCKED) { @@ -247,7 +255,8 @@ class NotificationWakeUpCoordinator @Inject constructor( fun setWakingUp(wakingUp: Boolean) { willWakeUp = false mWakingUp = wakingUp - if (wakingUp && mNotificationsVisible && !mNotificationsVisibleForExpansion) { + if (wakingUp && mNotificationsVisible && !mNotificationsVisibleForExpansion + && !bypassController.bypassEnabled) { // We're waking up while pulsing, let's make sure the animation looks nice mStackScroller.wakeUpFromPulse(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index abcdc7a2925fd..9184dec7c8c86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -660,6 +660,10 @@ public final class NotificationEntry { return row != null && row.isHeadsUp(); } + public boolean showingPulsing() { + return row != null && row.showingPulsing(); + } + public void setHeadsUp(boolean shouldHeadsUp) { if (row != null) row.setHeadsUp(shouldHeadsUp); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index b6ba3695b7053..bec655cc6ae4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -472,8 +472,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, Runnable removeHeadsUpRunnable = () -> { if (!mVisualStabilityManager.isReorderingAllowed() // We don't want to allow reordering while pulsing, but headsup need to - // time out if we're dozing. - && !mStatusBarStateController.isDozing()) { + // time out anyway + && !entry.showingPulsing()) { mEntriesToRemoveWhenReorderingAllowed.add(entry); mVisualStabilityManager.addReorderingAllowedCallback( HeadsUpManagerPhone.this); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index e19fe07791244..43e7a7b809075 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3621,6 +3621,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateNotificationPanelTouchState(); notifyHeadsUpGoingToSleep(); dismissVolumeDialog(); + mWakeUpCoordinator.setFullyAwake(false); } @Override @@ -3643,6 +3644,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onFinishedWakingUp() { + mWakeUpCoordinator.setFullyAwake(true); mWakeUpCoordinator.setWakingUp(false); } }; From b8cc6efc0a820da5ecd0edad9899a452785c87ca Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Fri, 14 Jun 2019 16:37:53 -0700 Subject: [PATCH 2/5] Enabled dragging down from the lock screen when bypassing The pulseExpansionHandler now also works on the lockscreen. This also delays the bypass when the user is currently dragging down. Bug: 134094877 Bug: 130327302 Test: atest SystemUITests Change-Id: I8d5b5b53e9a68e08933866df6831ecbada41ce43 --- .../statusbar/PulseExpansionHandler.kt | 105 ++++++++++++------ .../NotificationWakeUpCoordinator.kt | 23 ++-- .../phone/BiometricUnlockController.java | 2 +- .../phone/KeyguardBypassController.kt | 49 ++++++-- .../phone/NotificationPanelView.java | 10 +- .../systemui/statusbar/phone/StatusBar.java | 2 + .../statusbar/phone/StatusBarWindowView.java | 9 +- .../phone/NotificationPanelViewTest.java | 3 +- 8 files changed, 151 insertions(+), 52 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index a9d4fdeae3970..e132209c35f5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -25,6 +25,7 @@ import android.os.PowerManager import android.os.PowerManager.WAKE_REASON_GESTURE import android.os.SystemClock import android.view.MotionEvent +import android.view.VelocityTracker import android.view.ViewConfiguration import com.android.systemui.Gefingerpoken @@ -36,6 +37,7 @@ import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.ShadeController import javax.inject.Inject @@ -48,22 +50,31 @@ import kotlin.math.max @Singleton class PulseExpansionHandler @Inject constructor(context: Context, - private val mWakeUpCoordinator: NotificationWakeUpCoordinator) : Gefingerpoken { + private val wakeUpCoordinator: NotificationWakeUpCoordinator, + private val bypassController: KeyguardBypassController) : Gefingerpoken { companion object { private val RUBBERBAND_FACTOR_STATIC = 0.25f private val SPRING_BACK_ANIMATION_LENGTH_MS = 375 } private val mPowerManager: PowerManager? - private var mShadeController: ShadeController? = null + private lateinit var shadeController: ShadeController private val mMinDragDistance: Int private var mInitialTouchX: Float = 0.0f private var mInitialTouchY: Float = 0.0f - var isExpanding: Boolean = false - private set + private var isExpanding: Boolean = false + private set(value) { + val changed = field != value + field = value + bypassController.isPulseExpanding = value + if (changed && !value && !leavingLockscreen) { + bypassController.maybePerformPendingUnlock() + } + } + private var leavingLockscreen: Boolean = false private val mTouchSlop: Float - private var mExpansionCallback: ExpansionCallback? = null - private lateinit var mStackScroller: NotificationStackScrollLayout + private lateinit var expansionCallback: ExpansionCallback + private lateinit var stackScroller: NotificationStackScrollLayout private val mTemp2 = IntArray(2) private var mDraggedFarEnough: Boolean = false private var mStartingChild: ExpandableView? = null @@ -74,6 +85,7 @@ constructor(context: Context, private var mEmptyDragAmount: Float = 0.0f private var mWakeUpHeight: Float = 0.0f private var mReachedWakeUpHeight: Boolean = false + private var velocityTracker: VelocityTracker? = null private val isFalseTouch: Boolean get() = mFalsingManager.isFalseTouch @@ -91,9 +103,13 @@ constructor(context: Context, } private fun maybeStartExpansion(event: MotionEvent): Boolean { - if (!mPulsing) { + if (!wakeUpCoordinator.canShowPulsingHuns) { return false } + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain() + } + velocityTracker!!.addMovement(event) val x = event.x val y = event.y @@ -101,6 +117,7 @@ constructor(context: Context, MotionEvent.ACTION_DOWN -> { mDraggedFarEnough = false isExpanding = false + leavingLockscreen = false mStartingChild = null mInitialTouchY = y mInitialTouchX = x @@ -114,29 +131,52 @@ constructor(context: Context, captureStartingChild(mInitialTouchX, mInitialTouchY) mInitialTouchY = y mInitialTouchX = x - mWakeUpHeight = mWakeUpCoordinator.getWakeUpHeight() + mWakeUpHeight = wakeUpCoordinator.getWakeUpHeight() mReachedWakeUpHeight = false return true } } + + MotionEvent.ACTION_UP -> { + recycleVelocityTracker(); + } + + MotionEvent.ACTION_CANCEL -> { + recycleVelocityTracker(); + } } return false } + private fun recycleVelocityTracker() { + velocityTracker?.recycle(); + velocityTracker = null + } + override fun onTouchEvent(event: MotionEvent): Boolean { if (!isExpanding) { return maybeStartExpansion(event) } + velocityTracker!!.addMovement(event) val y = event.y + val moveDistance = y - mInitialTouchY when (event.actionMasked) { - MotionEvent.ACTION_MOVE -> updateExpansionHeight(y - mInitialTouchY) - MotionEvent.ACTION_UP -> if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch) { - finishExpansion() - } else { - cancelExpansion() + MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance) + MotionEvent.ACTION_UP -> { + velocityTracker!!.computeCurrentVelocity(1000 /* units */) + val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 + if (!mFalsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) { + finishExpansion() + } else { + cancelExpansion() + } + recycleVelocityTracker() + } + MotionEvent.ACTION_CANCEL -> { + cancelExpansion() + recycleVelocityTracker() } - MotionEvent.ACTION_CANCEL -> cancelExpansion() } return isExpanding } @@ -147,12 +187,15 @@ constructor(context: Context, setUserLocked(mStartingChild!!, false) mStartingChild = null } + if (shadeController.isDozing) { + isWakingToShadeLocked = true + wakeUpCoordinator.willWakeUp = true + mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE, + "com.android.systemui:PULSEDRAG") + } + shadeController.goToLockedShade(mStartingChild) + leavingLockscreen = true; isExpanding = false - isWakingToShadeLocked = true - mWakeUpCoordinator.willWakeUp = true - mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE, - "com.android.systemui:PULSEDRAG") - mShadeController!!.goToLockedShade(mStartingChild) if (mStartingChild is ExpandableNotificationRow) { val row = mStartingChild as ExpandableNotificationRow? row!!.onExpandedByGesture(true /* userExpanded */) @@ -172,12 +215,12 @@ constructor(context: Context, expansionHeight = max(newHeight.toFloat(), expansionHeight) } else { val target = if (mReachedWakeUpHeight) mWakeUpHeight else 0.0f - mWakeUpCoordinator.setNotificationsVisibleForExpansion(height > target, + wakeUpCoordinator.setNotificationsVisibleForExpansion(height > target, true /* animate */, true /* increaseSpeed */) expansionHeight = max(mWakeUpHeight, expansionHeight) } - val emptyDragAmount = mWakeUpCoordinator.setPulseHeight(expansionHeight) + val emptyDragAmount = wakeUpCoordinator.setPulseHeight(expansionHeight) setEmptyDragAmount(emptyDragAmount * RUBBERBAND_FACTOR_STATIC) } @@ -192,7 +235,7 @@ constructor(context: Context, private fun setEmptyDragAmount(amount: Float) { mEmptyDragAmount = amount - mExpansionCallback!!.setEmptyDragAmount(amount) + expansionCallback.setEmptyDragAmount(amount) } private fun reset(child: ExpandableView) { @@ -234,7 +277,7 @@ constructor(context: Context, } else { resetClock() } - mWakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */, + wakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */, true /* animate */, false /* increaseSpeed */) isExpanding = false @@ -243,21 +286,21 @@ constructor(context: Context, private fun findView(x: Float, y: Float): ExpandableView? { var totalX = x var totalY = y - mStackScroller.getLocationOnScreen(mTemp2) + stackScroller.getLocationOnScreen(mTemp2) totalX += mTemp2[0].toFloat() totalY += mTemp2[1].toFloat() - val childAtRawPosition = mStackScroller.getChildAtRawPosition(totalX, totalY) + val childAtRawPosition = stackScroller.getChildAtRawPosition(totalX, totalY) return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) { childAtRawPosition } else null } - fun setUp(notificationStackScroller: NotificationStackScrollLayout, - expansionCallback: ExpansionCallback, - shadeController: ShadeController) { - mExpansionCallback = expansionCallback - mShadeController = shadeController - mStackScroller = notificationStackScroller + fun setUp(stackScroller: NotificationStackScrollLayout, + expansionCallback: ExpansionCallback, + shadeController: ShadeController) { + this.expansionCallback = expansionCallback + this.shadeController = shadeController + this.stackScroller = stackScroller } fun setPulsing(pulsing: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index bf25c455531bc..1f3675a37c750 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -89,6 +89,21 @@ class NotificationWakeUpCoordinator @Inject constructor( } } + /** + * True if we can show pulsing heads up notifications + */ + var canShowPulsingHuns: Boolean = false + private set + get() { + var canShow = pulsing + if (bypassController.bypassEnabled) { + // We also allow pulsing on the lock screen! + canShow = canShow || (mWakingUp || willWakeUp || fullyAwake) + && mStatusBarStateController.state == StatusBarState.KEYGUARD + } + return canShow + } + init { mHeadsUpManagerPhone.addListener(this) @@ -120,13 +135,7 @@ class NotificationWakeUpCoordinator @Inject constructor( private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() - var canShow = pulsing - if (bypassController.bypassEnabled) { - // We also allow pulsing on the lock screen! - canShow = canShow || (mWakingUp || willWakeUp || fullyAwake) - && mStatusBarStateController.state == StatusBarState.KEYGUARD - } - visible = visible && canShow + visible = visible && canShowPulsingHuns if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) { // let's not make notifications invisible while waking up, otherwise the animation diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 35df7d920e29d..3f6144eb6f815 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -242,7 +242,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } } - private void startWakeAndUnlock(BiometricSourceType biometricSourceType) { + public void startWakeAndUnlock(BiometricSourceType biometricSourceType) { startWakeAndUnlock(calculateMode(biometricSourceType)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 9bbe4bed7c0ee..56b64dfff7ac8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -33,20 +33,32 @@ class KeyguardBypassController { private val unlockMethodCache: UnlockMethodCache private val statusBarStateController: StatusBarStateController - /** - * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. - */ + lateinit var unlockController: BiometricUnlockController + var isPulseExpanding = false + + /** + * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. + */ var bypassEnabled: Boolean = false get() = field && unlockMethodCache.isUnlockingWithFacePossible private set - - lateinit var unlockController: BiometricUnlockController + /** + * The pending unlock type which is set if the bypass was blocked when it happened. + */ + private var pendingUnlockType: BiometricSourceType? = null @Inject constructor(context: Context, tunerService: TunerService, statusBarStateController: StatusBarStateController) { unlockMethodCache = UnlockMethodCache.getInstance(context) this.statusBarStateController = statusBarStateController + statusBarStateController.addCallback(object : StatusBarStateController.StateListener { + override fun onStateChanged(newState: Int) { + if (newState != StatusBarState.KEYGUARD) { + pendingUnlockType = null; + } + } + }) val faceManager = context.getSystemService(FaceManager::class.java) if (faceManager?.isHardwareDetected != true) { return @@ -72,11 +84,30 @@ class KeyguardBypassController { * @return false if we can not wake and unlock right now */ fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean { - if (bypassEnabled && statusBarStateController.state != StatusBarState.KEYGUARD) { - // We're bypassing but not actually on the lockscreen, the user should decide when - // to unlock - return false + if (bypassEnabled) { + if (statusBarStateController.state != StatusBarState.KEYGUARD) { + // We're bypassing but not actually on the lockscreen, the user should decide when + // to unlock + return false + } + if (isPulseExpanding) { + pendingUnlockType = biometricSourceType + return false + } } return true } + + fun maybePerformPendingUnlock() { + if (pendingUnlockType != null) { + if (onBiometricAuthenticated(pendingUnlockType!!)) { + unlockController.startWakeAndUnlock(pendingUnlockType) + pendingUnlockType = null + } + } + } + + fun onStartedGoingToSleep() { + pendingUnlockType = null + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 56a61546fabad..626e62ecba681 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -213,6 +213,8 @@ public class NotificationPanelView extends PanelView implements private int mNotificationsHeaderCollideDistance; private int mUnlockMoveDistance; private float mEmptyDragAmount; + private float mDownX; + private float mDownY; private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); @@ -902,7 +904,8 @@ public class NotificationPanelView extends PanelView implements MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); return true; } - if (mPulseExpansionHandler.onInterceptTouchEvent(event)) { + if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0) + && mPulseExpansionHandler.onInterceptTouchEvent(event)) { return true; } @@ -1003,6 +1006,8 @@ public class NotificationPanelView extends PanelView implements mOnlyAffordanceInThisMotion = false; mQsTouchAboveFalsingThreshold = mQsFullyExpanded; mDozingOnDown = isDozing(); + mDownX = event.getX(); + mDownY = event.getY(); mCollapsedOnDown = isFullyCollapsed(); mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); } @@ -1076,7 +1081,8 @@ public class NotificationPanelView extends PanelView implements || event.getAction() == MotionEvent.ACTION_CANCEL) { mBlockingExpansionForCurrentTouch = false; } - if (!mIsExpanding && mPulseExpansionHandler.onTouchEvent(event)) { + if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0) + && mPulseExpansionHandler.onTouchEvent(event)) { // We're expanding all the other ones shouldn't get this anymore return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 43e7a7b809075..09024842a586b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -786,6 +786,7 @@ public class StatusBar extends SystemUI implements DemoMode, inflateStatusBarWindow(context); mStatusBarWindow.setService(this); + mStatusBarWindow.setBypassController(mKeyguardBypassController); mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener()); // TODO: Deal with the ugliness that comes from having some of the statusbar broken out @@ -3622,6 +3623,7 @@ public class StatusBar extends SystemUI implements DemoMode, notifyHeadsUpGoingToSleep(); dismissVolumeDialog(); mWakeUpCoordinator.setFullyAwake(false); + mKeyguardBypassController.onStartedGoingToSleep(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index de266592533d4..9417295b35214 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -149,6 +149,7 @@ public class StatusBarWindowView extends FrameLayout { * events manually as it's outside of the regular view bounds. */ private boolean mExpandingBelowNotch; + private KeyguardBypassController mBypassController; public StatusBarWindowView(Context context, AttributeSet attrs) { super(context, attrs); @@ -417,6 +418,7 @@ public class StatusBarWindowView extends FrameLayout { if (mNotificationPanel.isFullyExpanded() && mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mService.isBouncerShowing() + && !mBypassController.getBypassEnabled() && !mService.isDozing()) { intercept = mDragDownHelper.onInterceptTouchEvent(ev); } @@ -439,7 +441,8 @@ public class StatusBarWindowView extends FrameLayout { if (mService.isDozing()) { handled = !mService.isPulsing(); } - if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled) + if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled + && !mBypassController.getBypassEnabled()) || mDragDownHelper.isDraggingDown()) { // we still want to finish our drag down gesture when locking the screen handled = mDragDownHelper.onTouchEvent(ev); @@ -518,6 +521,10 @@ public class StatusBarWindowView extends FrameLayout { } } + public void setBypassController(KeyguardBypassController bypassController) { + mBypassController = bypassController; + } + public class LayoutParams extends FrameLayout.LayoutParams { public boolean ignoreRightInset; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 747411adedcee..7742023a23b85 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -118,7 +118,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mock(HeadsUpManagerPhone.class), new StatusBarStateControllerImpl(), bypassController); - PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator); + PulseExpansionHandler expansionHandler = new PulseExpansionHandler(mContext, coordinator, + bypassController); mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler, bypassController); mNotificationPanelView.setHeadsUpManager(mHeadsUpManager); From f89a5dc93f7fca14af9945d5dbef0597c47b1cee Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 18 Jun 2019 15:10:25 -0700 Subject: [PATCH 3/5] Enabling auth to succeed whenever the bouncer is showing Previously we could get stuck in a state where the user had to enter their pin. Fixes: 135545123 Test: drag down with face expiring, then click on notifications. Observe working face Change-Id: Ie9644dc8ff9a6a18067634478f96bb51675da80b --- .../phone/KeyguardBypassController.kt | 26 ++++++++++++------- .../systemui/statusbar/phone/StatusBar.java | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 56b64dfff7ac8..4be4d908979f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -33,20 +33,23 @@ class KeyguardBypassController { private val unlockMethodCache: UnlockMethodCache private val statusBarStateController: StatusBarStateController - lateinit var unlockController: BiometricUnlockController - var isPulseExpanding = false - - /** - * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. - */ - var bypassEnabled: Boolean = false - get() = field && unlockMethodCache.isUnlockingWithFacePossible - private set /** * The pending unlock type which is set if the bypass was blocked when it happened. */ private var pendingUnlockType: BiometricSourceType? = null + lateinit var unlockController: BiometricUnlockController + var isPulseExpanding = false + + /** + * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. + */ + var bypassEnabled: Boolean = false + get() = field && unlockMethodCache.isUnlockingWithFacePossible + private set + + var bouncerShowing: Boolean = false + @Inject constructor(context: Context, tunerService: TunerService, statusBarStateController: StatusBarStateController) { @@ -85,6 +88,11 @@ class KeyguardBypassController { */ fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean { if (bypassEnabled) { + if (bouncerShowing) { + // Whenever the bouncer is showing, we want to unlock. Otherwise we can get stuck + // in the shade locked where the bouncer wouldn't unlock + return true + } if (statusBarStateController.state != StatusBarState.KEYGUARD) { // We're bypassing but not actually on the lockscreen, the user should decide when // to unlock diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 09024842a586b..2d4c1aa0e1791 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3571,6 +3571,7 @@ public class StatusBar extends SystemUI implements DemoMode, */ public void setBouncerShowing(boolean bouncerShowing) { mBouncerShowing = bouncerShowing; + mKeyguardBypassController.setBouncerShowing(bouncerShowing); if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); From b0fada6ca022c156064c2abf9476a6a2350e1e83 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Mon, 17 Jun 2019 19:03:59 -0700 Subject: [PATCH 4/5] Changing the lockscreen layout for the bypass The notifications are now on the top and the user can drag down to the full shade from there directly. The quick settings header also comes down while expanding from the pulse. Bug: 130327302 Change-Id: I488f90aacd5912eda6f9423dc76862f06230d793 --- .../systemui/qs/car/CarQSFragment.java | 5 - .../com/android/systemui/plugins/qs/QS.java | 4 +- .../com/android/systemui/qs/QSAnimator.java | 33 +++- .../com/android/systemui/qs/QSFragment.java | 87 ++++++++--- .../systemui/qs/QuickStatusBarHeader.java | 8 +- .../NotificationViewHierarchyManager.java | 6 +- .../statusbar/PulseExpansionHandler.kt | 12 +- .../NotificationWakeUpCoordinator.kt | 10 +- .../notification/stack/AmbientState.java | 28 +++- .../stack/NotificationStackScrollLayout.java | 19 ++- .../phone/KeyguardClockPositionAlgorithm.java | 29 ++-- .../phone/NotificationPanelView.java | 142 ++++++++++++++++-- .../android/systemui/qs/QSFragmentTest.java | 4 +- .../NotificationViewHierarchyManagerTest.java | 5 +- .../KeyguardClockPositionAlgorithmTest.java | 3 +- 15 files changed, 320 insertions(+), 75 deletions(-) diff --git a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java index 769fc52a574cf..f9cfafa5c4712 100644 --- a/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java +++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java @@ -170,11 +170,6 @@ public class CarQSFragment extends Fragment implements QS { // No detail panel to close. } - @Override - public void setKeyguardShowing(boolean keyguardShowing) { - // No keyguard to show. - } - @Override public void animateHeaderSlidingIn(long delay) { // No header to animate. diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index 30d1352c8a015..85a9fec859f3b 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -34,7 +34,7 @@ public interface QS extends FragmentBase { String ACTION = "com.android.systemui.action.PLUGIN_QS"; - int VERSION = 6; + int VERSION = 7; String TAG = "QS"; @@ -51,7 +51,7 @@ public interface QS extends FragmentBase { void setListening(boolean listening); boolean isShowingDetail(); void closeDetail(); - void setKeyguardShowing(boolean keyguardShowing); + default void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) {} void animateHeaderSlidingIn(long delay); void animateHeaderSlidingOut(); void setQsExpansion(float qsExpansionFraction, float headerTranslation); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index ec2feba8291b7..41f66f7e2021f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -73,6 +73,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private int mNumQuickTiles; private float mLastPosition; private QSTileHost mHost; + private boolean mShowCollapsedOnKeyguard; public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanel panel) { mQs = qs; @@ -98,12 +99,32 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha public void setOnKeyguard(boolean onKeyguard) { mOnKeyguard = onKeyguard; - mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE); + updateQQSVisibility(); if (mOnKeyguard) { clearAnimationState(); } } + + /** + * Sets whether or not the keyguard is currently being shown with a collapsed header. + */ + void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) { + mShowCollapsedOnKeyguard = showCollapsedOnKeyguard; + updateQQSVisibility(); + setCurrentPosition(); + } + + + private void setCurrentPosition() { + setPosition(mLastPosition); + } + + private void updateQQSVisibility() { + mQuickQsPanel.setVisibility(mOnKeyguard + && !mShowCollapsedOnKeyguard ? View.INVISIBLE : View.VISIBLE); + } + public void setHost(QSTileHost qsh) { mHost = qsh; qsh.addCallback(this); @@ -322,7 +343,11 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha public void setPosition(float position) { if (mFirstPageAnimator == null) return; if (mOnKeyguard) { - return; + if (mShowCollapsedOnKeyguard) { + position = 0; + } else { + position = 1; + } } mLastPosition = position; if (mOnFirstPage && mAllowFancy) { @@ -356,7 +381,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onAnimationStarted() { - mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE); + updateQQSVisibility(); if (mOnFirstPage) { final int N = mQuickQsViews.size(); for (int i = 0; i < N; i++) { @@ -410,7 +435,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void run() { updateAnimators(); - setPosition(mLastPosition); + setCurrentPosition(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 087a826844e21..0a3b43a78f13d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -40,8 +40,10 @@ import com.android.systemui.R; import com.android.systemui.R.id; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.qs.QS; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; @@ -50,16 +52,17 @@ import com.android.systemui.util.LifecycleFragment; import javax.inject.Inject; -public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks { +public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks, + StatusBarStateController.StateListener { private static final String TAG = "QS"; private static final boolean DEBUG = false; private static final String EXTRA_EXPANDED = "expanded"; private static final String EXTRA_LISTENING = "listening"; private final Rect mQsBounds = new Rect(); + private final StatusBarStateController mStatusBarStateController; private boolean mQsExpanded; private boolean mHeaderAnimating; - private boolean mKeyguardShowing; private boolean mStackScrollerOverscrolling; private long mDelay; @@ -80,17 +83,27 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private final InjectionInflationController mInjectionInflater; private final QSTileHost mHost; + private boolean mShowCollapsedOnKeyguard; + private boolean mLastKeyguardAndExpanded; + /** + * The last received state from the controller. This should not be used directly to check if + * we're on keyguard but use {@link #isKeyguardShowing()} instead since that is more accurate + * during state transitions which often call into us. + */ + private int mState; @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, InjectionInflationController injectionInflater, Context context, - QSTileHost qsTileHost) { + QSTileHost qsTileHost, + StatusBarStateController statusBarStateController) { mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; mInjectionInflater = injectionInflater; SysUiServiceProvider.getComponent(context, CommandQueue.class) .observe(getLifecycle(), this); mHost = qsTileHost; + mStatusBarStateController = statusBarStateController; } @Override @@ -126,11 +139,14 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } setHost(mHost); + mStatusBarStateController.addCallback(this); + onStateChanged(mStatusBarStateController.getState()); } @Override public void onDestroy() { super.onDestroy(); + mStatusBarStateController.removeCallback(this); if (mListening) { setListening(false); } @@ -235,20 +251,43 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca || mHeaderAnimating; mQSPanel.setExpanded(mQsExpanded); mQSDetail.setExpanded(mQsExpanded); - mHeader.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating) + boolean keyguardShowing = isKeyguardShowing(); + mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating + || mShowCollapsedOnKeyguard) ? View.VISIBLE : View.INVISIBLE); - mHeader.setExpanded((mKeyguardShowing && !mHeaderAnimating) + mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) || (mQsExpanded && !mStackScrollerOverscrolling)); mFooter.setVisibility( - !mQsDisabled && (mQsExpanded || !mKeyguardShowing || mHeaderAnimating) + !mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating + || mShowCollapsedOnKeyguard) ? View.VISIBLE : View.INVISIBLE); - mFooter.setExpanded((mKeyguardShowing && !mHeaderAnimating) + mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) || (mQsExpanded && !mStackScrollerOverscrolling)); mQSPanel.setVisibility(!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE); } + private boolean isKeyguardShowing() { + // We want the freshest state here since otherwise we'll have some weirdness if earlier + // listeners trigger updates + return mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + } + + @Override + public void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) { + if (showCollapsedOnKeyguard != mShowCollapsedOnKeyguard) { + mShowCollapsedOnKeyguard = showCollapsedOnKeyguard; + updateQsState(); + if (mQSAnimator != null) { + mQSAnimator.setShowCollapsedOnKeyguard(showCollapsedOnKeyguard); + } + if (!showCollapsedOnKeyguard && isKeyguardShowing()) { + setQsExpansion(mLastQSExpansion, 0); + } + } + } + public QSPanel getQsPanel() { return mQSPanel; } @@ -280,10 +319,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca updateQsState(); } - @Override - public void setKeyguardShowing(boolean keyguardShowing) { + private void setKeyguardShowing(boolean keyguardShowing) { if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing); - mKeyguardShowing = keyguardShowing; mLastQSExpansion = -1; if (mQSAnimator != null) { @@ -321,16 +358,18 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); mContainer.setExpansion(expansion); final float translationScaleY = expansion - 1; - if (!mHeaderAnimating) { + boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard; + if (!mHeaderAnimating && !headerWillBeAnimating()) { getView().setTranslationY( - mKeyguardShowing + onKeyguardAndExpanded ? translationScaleY * mHeader.getHeight() : headerTranslation); } - if (expansion == mLastQSExpansion) { + if (expansion == mLastQSExpansion && mLastKeyguardAndExpanded == onKeyguardAndExpanded) { return; } mLastQSExpansion = expansion; + mLastKeyguardAndExpanded = onKeyguardAndExpanded; boolean fullyExpanded = expansion == 1; int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom() @@ -338,8 +377,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca float panelTranslationY = translationScaleY * heightDiff; // Let the views animate their contents correctly by giving them the necessary context. - mHeader.setExpansion(mKeyguardShowing, expansion, panelTranslationY); - mFooter.setExpansion(mKeyguardShowing ? 1 : expansion); + mHeader.setExpansion(onKeyguardAndExpanded, expansion, + panelTranslationY); + mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanel.getQsTileRevealController().setExpansion(expansion); mQSPanel.getTileLayout().setExpansion(expansion); mQSPanel.setTranslationY(translationScaleY * heightDiff); @@ -361,12 +401,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } + private boolean headerWillBeAnimating() { + return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard + && !isKeyguardShowing(); + } + @Override public void animateHeaderSlidingIn(long delay) { if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn"); // If the QS is already expanded we don't need to slide in the header as it's already // visible. - if (!mQsExpanded) { + if (!mQsExpanded && getView().getTranslationY() != 0) { mHeaderAnimating = true; mDelay = delay; getView().getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn); @@ -376,6 +421,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Override public void animateHeaderSlidingOut() { if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut"); + if (getView().getY() == -mHeader.getHeight()) { + return; + } mHeaderAnimating = true; getView().animate().y(-mHeader.getHeight()) .setStartDelay(0) @@ -463,7 +511,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .setListener(mAnimateHeaderSlidingInListener) .start(); - getView().setY(-mHeader.getHeight()); return true; } }; @@ -476,4 +523,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca updateQsState(); } }; + + @Override + public void onStateChanged(int newState) { + mState = newState; + setKeyguardShowing(newState == StatusBarState.KEYGUARD); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 410a13ee47301..96533bce5bcca 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -439,19 +439,19 @@ public class QuickStatusBarHeader extends RelativeLayout implements /** * Animates the inner contents based on the given expansion details. * - * @param isKeyguardShowing whether or not we're showing the keyguard (a.k.a. lockscreen) + * @param forceExpanded whether we should show the state expanded forcibly * @param expansionFraction how much the QS panel is expanded/pulled out (up to 1f) * @param panelTranslationY how much the panel has physically moved down vertically (required * for keyguard animations only) */ - public void setExpansion(boolean isKeyguardShowing, float expansionFraction, + public void setExpansion(boolean forceExpanded, float expansionFraction, float panelTranslationY) { - final float keyguardExpansionFraction = isKeyguardShowing ? 1f : expansionFraction; + final float keyguardExpansionFraction = forceExpanded ? 1f : expansionFraction; if (mStatusIconsAlphaAnimator != null) { mStatusIconsAlphaAnimator.setPosition(keyguardExpansionFraction); } - if (isKeyguardShowing) { + if (forceExpanded) { // If the keyguard is showing, we want to offset the text so that it comes in at the // same time as the panel as it slides down. mHeaderTextContainerView.setTranslationY(panelTranslationY); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 964b5dbc42335..0048211030d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.UnlockMethodCache; @@ -80,6 +81,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private final boolean mAlwaysExpandNonGroupedNotification; private final BubbleData mBubbleData; private final DynamicPrivacyController mDynamicPrivacyController; + private final KeyguardBypassController mBypassController; private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; @@ -93,8 +95,10 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle NotificationEntryManager notificationEntryManager, Lazy shadeController, BubbleData bubbleData, + KeyguardBypassController bypassController, DynamicPrivacyController privacyController) { mLockscreenUserManager = notificationLockscreenUserManager; + mBypassController = bypassController; mGroupManager = groupManager; mVisualStabilityManager = visualStabilityManager; mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController; @@ -336,7 +340,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle int visibleNotifications = 0; boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; int maxNotifications = -1; - if (onKeyguard) { + if (onKeyguard && !mBypassController.getBypassEnabled()) { maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */); } mListContainer.setMaxDisplayedNotifications(maxNotifications); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index e132209c35f5d..8ca744b579ed7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -62,16 +62,18 @@ constructor(context: Context, private val mMinDragDistance: Int private var mInitialTouchX: Float = 0.0f private var mInitialTouchY: Float = 0.0f - private var isExpanding: Boolean = false + var isExpanding: Boolean = false private set(value) { val changed = field != value field = value bypassController.isPulseExpanding = value if (changed && !value && !leavingLockscreen) { bypassController.maybePerformPendingUnlock() + pulseExpandAbortListener?.run() } } - private var leavingLockscreen: Boolean = false + var leavingLockscreen: Boolean = false + private set private val mTouchSlop: Float private lateinit var expansionCallback: ExpansionCallback private lateinit var stackScroller: NotificationStackScrollLayout @@ -89,6 +91,8 @@ constructor(context: Context, private val isFalseTouch: Boolean get() = mFalsingManager.isFalseTouch + var qsExpanded: Boolean = false + var pulseExpandAbortListener: Runnable? = null init { mMinDragDistance = context.resources.getDimensionPixelSize( @@ -103,7 +107,7 @@ constructor(context: Context, } private fun maybeStartExpansion(event: MotionEvent): Boolean { - if (!wakeUpCoordinator.canShowPulsingHuns) { + if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded) { return false } if (velocityTracker == null) { @@ -270,6 +274,7 @@ constructor(context: Context, } private fun cancelExpansion() { + isExpanding = false mFalsingManager.onExpansionFromPulseStopped() if (mStartingChild != null) { reset(mStartingChild!!) @@ -280,7 +285,6 @@ constructor(context: Context, wakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */, true /* animate */, false /* increaseSpeed */) - isExpanding = false } private fun findView(x: Float, y: Float): ExpandableView? { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 1f3675a37c750..389b0aa27ef35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -237,7 +237,7 @@ class NotificationWakeUpCoordinator @Inject constructor( } fun getWakeUpHeight() : Float { - return mStackScroller.pulseHeight + return mStackScroller.wakeUpHeight } private fun updateHideAmount() { @@ -257,8 +257,14 @@ class NotificationWakeUpCoordinator @Inject constructor( } } + /** + * Set the height how tall notifications are pulsing. This is only set whenever we are expanding + * from a pulse and determines how much the notifications are expanded. + */ fun setPulseHeight(height: Float): Float { - return mStackScroller.setPulseHeight(height) + val overflow = mStackScroller.setPulseHeight(height) + // no overflow for the bypass experience + return if (bypassController.bypassEnabled) 0.0f else overflow } fun setWakingUp(wakingUp: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index 6220adebaf2bf..f3d068a9d610d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -83,6 +83,7 @@ public class AmbientState { private float mPulseHeight = MAX_PULSE_HEIGHT; private float mDozeAmount = 0.0f; private HeadsUpManager mHeadUpManager; + private Runnable mOnPulseHeightChangedListener; public AmbientState( Context context, @@ -191,7 +192,7 @@ public class AmbientState { public void setHideAmount(float hidemount) { if (hidemount == 1.0f && mHideAmount != hidemount) { // Whenever we are fully hidden, let's reset the pulseHeight again - mPulseHeight = MAX_PULSE_HEIGHT; + setPulseHeight(MAX_PULSE_HEIGHT); } mHideAmount = hidemount; } @@ -502,7 +503,20 @@ public class AmbientState { } public void setPulseHeight(float height) { - mPulseHeight = height; + if (height != mPulseHeight) { + mPulseHeight = height; + if (mOnPulseHeightChangedListener != null) { + mOnPulseHeightChangedListener.run(); + } + } + } + + public float getPulseHeight() { + if (mPulseHeight == MAX_PULSE_HEIGHT) { + // If we're not pulse expanding, the height should be 0 + return 0; + } + return mPulseHeight; } public void setDozeAmount(float dozeAmount) { @@ -510,7 +524,7 @@ public class AmbientState { mDozeAmount = dozeAmount; if (dozeAmount == 0.0f || dozeAmount == 1.0f) { // We woke all the way up, let's reset the pulse height - mPulseHeight = MAX_PULSE_HEIGHT; + setPulseHeight(MAX_PULSE_HEIGHT); } } } @@ -522,4 +536,12 @@ public class AmbientState { public boolean isFullyAwake() { return mDozeAmount == 0.0f; } + + public void setOnPulseHeightChangedListener(Runnable onPulseHeightChangedListener) { + mOnPulseHeightChangedListener = onPulseHeightChangedListener; + } + + public Runnable getOnPulseHeightChangedListener() { + return mOnPulseHeightChangedListener; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index cd2a654619030..3c21699b192d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -32,7 +32,6 @@ import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.WallpaperManager; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -629,7 +628,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd /** * @return the height at which we will wake up when pulsing */ - public float getPulseHeight() { + public float getWakeUpHeight() { ActivatableNotificationView firstChild = getFirstChildWithBackground(); if (firstChild != null) { return firstChild.getCollapsedHeight(); @@ -985,6 +984,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } + public boolean isPulseExpanding() { + return mAmbientState.isPulseExpanding(); + } + @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -2781,7 +2784,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } else { mTopPaddingOverflow = 0; } - setTopPadding(topPadding, animate); + setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled()); setExpandedHeight(mExpandedHeight); } @@ -5584,6 +5587,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return Math.max(0, height - mAmbientState.getInnerHeight(true /* ignorePulseHeight */)); } + public float getPulseHeight() { + return mAmbientState.getPulseHeight(); + } + /** * Set the amount how much we're dozing. This is different from how hidden the shade is, when * the notification is pulsing. @@ -5595,7 +5602,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } public void wakeUpFromPulse() { - setPulseHeight(getPulseHeight()); + setPulseHeight(getWakeUpHeight()); // Let's place the hidden views at the end of the pulsing notification to make sure we have // a smooth animation boolean firstVisibleView = true; @@ -5631,6 +5638,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } + public void setOnPulseHeightChangedListener(Runnable listener) { + mAmbientState.setOnPulseHeightChangedListener(listener); + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index c477162ca761d..179375e31dd38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -112,10 +112,15 @@ public class KeyguardClockPositionAlgorithm { private float mEmptyDragAmount; /** - * If true the clock should always be positioned like it's dark. Used in the bypass, where - * notifications don't expand on the lock screen and should be kept stable + * Setting if bypass is enabled. If true the clock should always be positioned like it's dark + * and other minor adjustments. */ - private boolean mPositionLikeDark; + private boolean mBypassEnabled; + + /** + * The stackscroller padding when unlocked + */ + private int mUnlockedStackScrollerPadding; /** * Refreshes the dimension values. @@ -139,7 +144,7 @@ public class KeyguardClockPositionAlgorithm { public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int clockPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, float emptyDragAmount, - boolean positionLikeDark) { + boolean bypassEnabled, int unlockedStackScrollerPadding) { mMinTopMargin = minTopMargin + mContainerTopPadding; mMaxShadeBottom = maxShadeBottom; mNotificationStackHeight = notificationStackHeight; @@ -151,20 +156,24 @@ public class KeyguardClockPositionAlgorithm { mHasVisibleNotifs = hasVisibleNotifs; mDarkAmount = dark; mEmptyDragAmount = emptyDragAmount; - mPositionLikeDark = positionLikeDark; + mBypassEnabled = bypassEnabled; + mUnlockedStackScrollerPadding = unlockedStackScrollerPadding; } public void run(Result result) { final int y = getClockY(mPanelExpansion); result.clockY = y; result.clockAlpha = getClockAlpha(y); - result.stackScrollerPadding = y + mKeyguardStatusHeight; - result.stackScrollerPaddingExpanded = getClockY(1.0f) + mKeyguardStatusHeight; + result.stackScrollerPadding = mBypassEnabled ? mUnlockedStackScrollerPadding + : y + mKeyguardStatusHeight; + result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding + : getClockY(1.0f) + mKeyguardStatusHeight; result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); } public float getMinStackScrollerPadding() { - return mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin; + return mBypassEnabled ? mUnlockedStackScrollerPadding + : mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin; } private int getMaxClockY() { @@ -176,7 +185,7 @@ public class KeyguardClockPositionAlgorithm { } private int getExpandedPreferredClockY() { - return (mHasCustomClock && (!mHasVisibleNotifs || mPositionLikeDark)) ? getPreferredClockY() + return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? getPreferredClockY() : getExpandedClockPosition(); } @@ -218,7 +227,7 @@ public class KeyguardClockPositionAlgorithm { float clockY = MathUtils.lerp(clockYBouncer, clockYRegular, shadeExpansion); clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion); - float darkAmount = mPositionLikeDark && !mHasCustomClock ? 1.0f : mDarkAmount; + float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount; return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 626e62ecba681..fa401fee82400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -352,6 +352,7 @@ public class NotificationPanelView extends PanelView implements private int mShelfHeight; private Runnable mOnReinflationListener; private int mDarkIconSize; + private int mHeadsUpInset; @Inject public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, @@ -373,6 +374,11 @@ public class NotificationPanelView extends PanelView implements mCommandQueue = getComponent(context, CommandQueue.class); mDisplayId = context.getDisplayId(); mPulseExpansionHandler = pulseExpansionHandler; + pulseExpansionHandler.setPulseExpandAbortListener(() -> { + if (mQs != null) { + mQs.animateHeaderSlidingOut(); + } + }); mThemeResId = context.getThemeResId(); mKeyguardBypassController = bypassController; dynamicPrivacyController.addListener(this); @@ -423,6 +429,16 @@ public class NotificationPanelView extends PanelView implements mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); mQsFrame = findViewById(R.id.qs_frame); mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController); + + + mNotificationStackScroller.setOnPulseHeightChangedListener( + () -> { + if (mKeyguardBypassController.getBypassEnabled()) { + // Position the notifications while dragging down while pulsing + requestScrollerTopPaddingUpdate(false /* animate */); + updateQSPulseExpansion(); + } + }); } @Override @@ -470,6 +486,10 @@ public class NotificationPanelView extends PanelView implements mShelfHeight = getResources().getDimensionPixelSize(R.dimen.notification_shelf_height); mDarkIconSize = getResources().getDimensionPixelSize( R.dimen.status_bar_icon_drawing_size_dark); + int statusbarHeight = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + mHeadsUpInset = statusbarHeight + getResources().getDimensionPixelSize( + R.dimen.heads_up_status_bar_padding); } /** @@ -683,12 +703,12 @@ public class NotificationPanelView extends PanelView implements boolean animateClock = animate || mAnimateNextPositionUpdate; int stackScrollerPadding; if (mBarState != StatusBarState.KEYGUARD) { - stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight - + mQsNotificationTopPadding; + stackScrollerPadding = getUnlockedStackScrollerPadding(); } else { int totalHeight = getHeight(); int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); + boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); mClockPositionAlgorithm.setup( mStatusBarMinHeight, totalHeight - bottomPadding, @@ -702,7 +722,8 @@ public class NotificationPanelView extends PanelView implements mNotificationStackScroller.getVisibleNotificationCount() != 0, mInterpolatedDarkAmount, mEmptyDragAmount, - mKeyguardBypassController.getBypassEnabled()); + bypassEnabled, + getUnlockedStackScrollerPadding()); mClockPositionAlgorithm.run(mClockPositionResult); PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); @@ -722,6 +743,14 @@ public class NotificationPanelView extends PanelView implements mAnimateNextPositionUpdate = false; } + /** + * @return the padding of the stackscroller when unlocked + */ + private int getUnlockedStackScrollerPadding() { + return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight + + mQsNotificationTopPadding; + } + /** * @param maximum the maximum to return at most * @return the maximum keyguard notifications that can fit on the screen @@ -1353,6 +1382,7 @@ public class NotificationPanelView extends PanelView implements mFalsingManager.setQsExpanded(expanded); mStatusBar.setQsExpanded(expanded); mNotificationContainerParent.setQsExpanded(expanded); + mPulseExpansionHandler.setQsExpanded(expanded); } } @@ -1367,9 +1397,6 @@ public class NotificationPanelView extends PanelView implements mBarState = statusBarState; mKeyguardShowing = keyguardShowing; - if (mQs != null) { - mQs.setKeyguardShowing(mKeyguardShowing); - } if (oldState == StatusBarState.KEYGUARD && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) { @@ -1397,7 +1424,9 @@ public class NotificationPanelView extends PanelView implements if (keyguardShowing) { updateDozingVisibilities(false /* animate */); } - + // THe update needs to happen after the headerSlide in above, otherwise the translation + // would reset + updateQSPulseExpansion(); maybeAnimateBottomAreaAlpha(); resetHorizontalPanelPosition(); updateQsState(); @@ -1646,7 +1675,7 @@ public class NotificationPanelView extends PanelView implements // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings // panel. We need to take the maximum and linearly interpolate with the panel expansion // for a nice motion. - int maxNotificationPadding = mClockPositionResult.stackScrollerPadding; + int maxNotificationPadding = getKeyguardNotificationStaticPadding(); int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding; int max = mBarState == StatusBarState.KEYGUARD ? Math.max(maxNotificationPadding, maxQsPadding) @@ -1654,11 +1683,12 @@ public class NotificationPanelView extends PanelView implements return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max, getExpandedFraction()); } else if (mQsSizeChangeAnimator != null) { - return (int) mQsSizeChangeAnimator.getAnimatedValue(); + return Math.max((int) mQsSizeChangeAnimator.getAnimatedValue(), + getKeyguardNotificationStaticPadding()); } else if (mKeyguardShowing) { // We can only do the smoother transition on Keyguard when we also are not collapsing // from a scrolled quick settings. - return MathUtils.lerp((float) mClockPositionResult.stackScrollerPadding, + return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding), getQsExpansionFraction()); } else { @@ -1666,8 +1696,53 @@ public class NotificationPanelView extends PanelView implements } } + /** + * @return the topPadding of notifications when on keyguard not respecting quick settings + * expansion + */ + private int getKeyguardNotificationStaticPadding() { + if (!mKeyguardShowing) { + return 0; + } + if (!mKeyguardBypassController.getBypassEnabled()) { + return mClockPositionResult.stackScrollerPadding; + } + int collapsedPosition = mHeadsUpInset; + if (!mNotificationStackScroller.isPulseExpanding()) { + return collapsedPosition; + } else { + int expandedPosition = mClockPositionResult.stackScrollerPadding; + return (int) MathUtils.lerp(collapsedPosition, expandedPosition, + calculateHeaderAppearAmountBypass()); + } + } + + + private float calculateHeaderAppearAmountBypass() { + float pulseHeight = mNotificationStackScroller.getPulseHeight(); + float wakeUpHeight = mNotificationStackScroller.getWakeUpHeight(); + float dragDownAmount = pulseHeight - wakeUpHeight; + + // The total distance required to fully reveal the header + float totalDistance = mClockPositionResult.stackScrollerPadding; + return MathUtils.smoothStep(0, totalDistance, dragDownAmount); + } + protected void requestScrollerTopPaddingUpdate(boolean animate) { mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate); + if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { + // update the position of the header + updateQsExpansion(); + } + } + + + private void updateQSPulseExpansion() { + if (mQs != null) { + mQs.setShowCollapsedOnKeyguard(mKeyguardShowing + && mKeyguardBypassController.getBypassEnabled() + && mNotificationStackScroller.isPulseExpanding()); + } } private void trackMovement(MotionEvent event) { @@ -1798,8 +1873,16 @@ public class NotificationPanelView extends PanelView implements @Override protected int getMaxPanelHeight() { + if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) { + return getMaxPanelHeightBypass(); + } else { + return getMaxPanelHeightNonBypass(); + } + } + + private int getMaxPanelHeightNonBypass() { int min = mStatusBarMinHeight; - if (mBarState != StatusBarState.KEYGUARD + if (!(mBarState == StatusBarState.KEYGUARD) && mNotificationStackScroller.getNotGoneChildCount() == 0) { int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount()); min = Math.max(min, minHeight); @@ -1815,6 +1898,15 @@ public class NotificationPanelView extends PanelView implements return maxHeight; } + private int getMaxPanelHeightBypass() { + int position = mClockPositionAlgorithm.getExpandedClockPosition() + + mKeyguardStatusView.getHeight(); + if (mNotificationStackScroller.getVisibleNotificationCount() != 0) { + position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; + } + return position; + } + public boolean isInSettings() { return mQsExpanded; } @@ -1964,11 +2056,25 @@ public class NotificationPanelView extends PanelView implements } protected float getHeaderTranslation() { - if (mBarState == StatusBarState.KEYGUARD) { - return 0; + if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { + return -mQs.getQsMinExpansionHeight(); } - float translation = MathUtils.lerp(-mQsMinExpansionHeight, 0, - Math.min(1.0f, mNotificationStackScroller.getAppearFraction(mExpandedHeight))) + float appearAmount = mNotificationStackScroller.getAppearFraction(mExpandedHeight); + float startHeight = -mQsExpansionHeight; + if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() + && mNotificationStackScroller.isPulseExpanding()) { + if (!mPulseExpansionHandler.isExpanding() + && !mPulseExpansionHandler.getLeavingLockscreen()) { + // If we aborted the expansion we need to make sure the header doesn't reappear + // again after the header has animated away + appearAmount = 0; + } else { + appearAmount = calculateHeaderAppearAmountBypass(); + } + startHeight = -mQs.getQsMinExpansionHeight(); + } + float translation = MathUtils.lerp(startHeight, 0, + Math.min(1.0f, appearAmount)) + mExpandOffset; return Math.min(0, translation); } @@ -2729,6 +2835,10 @@ public class NotificationPanelView extends PanelView implements if (mTracking) { mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity()); } + if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { + // The expandedHeight is always the full panel Height when bypassing + expandedHeight = getMaxPanelHeightNonBypass(); + } mNotificationStackScroller.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); updateBigClockAlpha(); @@ -2878,7 +2988,7 @@ public class NotificationPanelView extends PanelView implements mQs.setPanelView(NotificationPanelView.this); mQs.setExpandClickListener(NotificationPanelView.this); mQs.setHeaderClickable(mQsExpansionEnabled); - mQs.setKeyguardShowing(mKeyguardShowing); + updateQSPulseExpansion(); mQs.setOverscrolling(mStackScrollerOverscrolling); // recompute internal state when qspanel height changes diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index db4f5ffcdfeb7..4eee23056bc26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -39,6 +39,7 @@ import com.android.systemui.DumpController; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.phone.AutoTileManager; @@ -139,6 +140,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { new RemoteInputQuickSettingsDisabler(context, mock(ConfigurationController.class)), new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()), context, - mock(QSTileHost.class)); + mock(QSTileHost.class), + mock(StatusBarStateController.class)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index c476d802c4e50..010b85edacdda 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.util.Assert; @@ -99,7 +100,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mViewHierarchyManager = new NotificationViewHierarchyManager(mContext, mLockscreenUserManager, mGroupManager, mVisualStabilityManager, mock(StatusBarStateControllerImpl.class), mEntryManager, - () -> mShadeController, new BubbleData(mContext), mock(DynamicPrivacyController.class)); + () -> mShadeController, new BubbleData(mContext), + mock(KeyguardBypassController.class), + mock(DynamicPrivacyController.class)); Dependency.get(InitController.class).executePostInitTasks(); mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 66c61ce9b7e87..2042faba2b3a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -383,7 +383,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private void positionClock() { mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight, mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mPreferredClockY, - mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* positionLikeDark */); + mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */, + 0 /* unlockedStackScrollerPadding */); mClockPositionAlgorithm.run(mClockPosition); } } From 820ba2d94d968b970105b83837ca0c0a67771267 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 18 Jun 2019 18:59:09 -0700 Subject: [PATCH 5/5] Continued the bypass experience The lock icon now hides as soon as the notifications are showing to avoid the overlap introduced in the previous CL. We're also introducing a new listener that one can listen to for when the notifications are fully hidden. That same listener is now used to hide and show the aod icons Bug: 130327302 Change-Id: I5694a38e542b82bf2738d66bdff28d122a9f89e7 --- packages/SystemUI/Android.bp | 6 ++ .../com/android/systemui/SystemUIFactory.java | 10 ++- .../statusbar/PulseExpansionHandler.kt | 4 +- .../NotificationWakeUpCoordinator.kt | 49 ++++++++++++++- .../stack/NotificationStackScrollLayout.java | 5 +- .../systemui/statusbar/phone/LockIcon.java | 34 +++++++++- .../phone/NotificationIconAreaController.java | 63 ++++++++++++++----- .../phone/NotificationPanelView.java | 23 +++---- .../systemui/statusbar/phone/StatusBar.java | 3 + .../statusbar/phone/StatusBarWindowView.java | 6 ++ .../NotificationIconAreaControllerTest.java | 9 ++- 11 files changed, 175 insertions(+), 37 deletions(-) diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 91a8ab5f692f1..4c52b13247813 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -75,6 +75,7 @@ android_library { "--extra-packages", "com.android.keyguard", ], + kotlincflags: ["-Xjvm-default=enable"], plugins: ["dagger2-compiler-2.19"], } @@ -128,6 +129,7 @@ android_library { "telephony-common", "android.test.base", ], + kotlincflags: ["-Xjvm-default=enable"], aaptflags: [ "--extra-packages", "com.android.keyguard:com.android.systemui", @@ -155,6 +157,8 @@ android_app { "telephony-common", ], + kotlincflags: ["-Xjvm-default=enable"], + dxflags: ["--multi-dex"], aaptflags: [ "--extra-packages", @@ -191,6 +195,8 @@ android_app { "telephony-common", ], + kotlincflags: ["-Xjvm-default=enable"], + srcs: [ "legacy/recents/src/**/*.java", "legacy/recents/src/**/I*.aidl", diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 34cc70c1c42dd..73b8c947f7202 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -48,9 +48,11 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBouncer; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; import com.android.systemui.statusbar.phone.LockIcon; import com.android.systemui.statusbar.phone.LockscreenWallpaper; @@ -145,10 +147,14 @@ public class SystemUIFactory { } public NotificationIconAreaController createNotificationIconAreaController(Context context, - StatusBar statusBar, StatusBarStateController statusBarStateController, + StatusBar statusBar, + NotificationWakeUpCoordinator wakeUpCoordinator, + KeyguardBypassController keyguardBypassController, + StatusBarStateController statusBarStateController, NotificationListener listener) { return new NotificationIconAreaController(context, statusBar, statusBarStateController, - listener, Dependency.get(NotificationMediaManager.class)); + wakeUpCoordinator, keyguardBypassController, listener, + Dependency.get(NotificationMediaManager.class)); } public KeyguardIndicationController createKeyguardIndicationController(Context context, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 8ca744b579ed7..bdc4d2a386420 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -93,6 +93,7 @@ constructor(context: Context, get() = mFalsingManager.isFalseTouch var qsExpanded: Boolean = false var pulseExpandAbortListener: Runnable? = null + var bouncerShowing: Boolean = false init { mMinDragDistance = context.resources.getDimensionPixelSize( @@ -107,7 +108,8 @@ constructor(context: Context, } private fun maybeStartExpansion(event: MotionEvent): Boolean { - if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded) { + if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded + || bouncerShowing) { return false } if (velocityTracker == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 389b0aa27ef35..95af9fde1e98a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -66,7 +66,9 @@ class NotificationWakeUpCoordinator @Inject constructor( private var mLinearVisibilityAmount = 0.0f private var mWakingUp = false private val mEntrySetToClearWhenFinished = mutableSetOf() - private val mDozeParameters: DozeParameters; + private val mDozeParameters: DozeParameters + private var pulseExpanding: Boolean = false + private val wakeUpListeners = arrayListOf() var fullyAwake: Boolean = false var willWakeUp = false @@ -89,6 +91,16 @@ class NotificationWakeUpCoordinator @Inject constructor( } } + var notificationsFullyHidden: Boolean = false + private set(value) { + if (field != value) { + field = value + for (listener in wakeUpListeners) { + listener.onFullyHiddenChanged(value) + } + } + } + /** * True if we can show pulsing heads up notifications */ @@ -104,7 +116,6 @@ class NotificationWakeUpCoordinator @Inject constructor( return canShow } - init { mHeadsUpManagerPhone.addListener(this) mStatusBarStateController.addCallback(this) @@ -113,8 +124,19 @@ class NotificationWakeUpCoordinator @Inject constructor( fun setStackScroller(stackScroller: NotificationStackScrollLayout) { mStackScroller = stackScroller + pulseExpanding = stackScroller.isPulseExpanding + stackScroller.setOnPulseHeightChangedListener { + val nowExpanding = isPulseExpanding() + val changed = nowExpanding != pulseExpanding + pulseExpanding = nowExpanding + for (listener in wakeUpListeners) { + listener.onPulseExpansionChanged(changed) + } + } } + fun isPulseExpanding(): Boolean = mStackScroller.isPulseExpanding + /** * @param visible should notifications be visible * @param animate should this change be animated @@ -132,6 +154,14 @@ class NotificationWakeUpCoordinator @Inject constructor( } } + fun addListener(listener: WakeUpListener) { + wakeUpListeners.add(listener); + } + + fun removeFullyHiddenChangedListener(listener: WakeUpListener) { + wakeUpListeners.remove(listener); + } + private fun updateNotificationVisibility(animate: Boolean, increaseSpeed: Boolean) { // TODO: handle Lockscreen wakeup for bypass when we're not pulsing anymore var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() @@ -244,7 +274,7 @@ class NotificationWakeUpCoordinator @Inject constructor( val linearAmount = Math.min(1.0f - mLinearVisibilityAmount, mLinearDozeAmount) val amount = Math.min(1.0f - mVisibilityAmount, mDozeAmount) mStackScroller.setHideAmount(linearAmount, amount) - iconAreaController.setFullyHidden(linearAmount == 1.0f); + notificationsFullyHidden = linearAmount == 1.0f; } private fun notifyAnimationStart(awake: Boolean) { @@ -300,4 +330,17 @@ class NotificationWakeUpCoordinator @Inject constructor( private fun shouldAnimateVisibility() = mDozeParameters.getAlwaysOn() && !mDozeParameters.getDisplayNeedsBlanking() + + interface WakeUpListener { + /** + * Called whenever the notifications are fully hidden or shown + */ + @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {} + + /** + * Called whenever the pulseExpansion changes + * @param expandingChanged if the user has started or stopped expanding + */ + @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {} + } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 3c21699b192d5..e76f752819aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -3278,7 +3278,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) { - if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) { + if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress && !isFullyHidden()) { // Generate Animations mChildrenToAddAnimated.add(child); if (fromMoreCard) { @@ -3286,7 +3286,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } mNeedsAnimation = true; } - if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress) { + if (isHeadsUp(child) && mAnimationsEnabled && !mChangePositionInProgress + && !isFullyHidden()) { mAddedHeadsUpChildren.add(child); mChildrenToAddAnimated.remove(child); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 07436f8c27fee..2eadd125bacea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -47,6 +47,8 @@ import com.android.systemui.R; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.KeyguardAffordanceView; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -64,7 +66,8 @@ import javax.inject.Named; */ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChangedListener, StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener, - UnlockMethodCache.OnUnlockMethodChangedListener { + UnlockMethodCache.OnUnlockMethodChangedListener, + NotificationWakeUpCoordinator.WakeUpListener { private static final int STATE_LOCKED = 0; private static final int STATE_LOCK_OPEN = 1; @@ -78,6 +81,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private final DockManager mDockManager; private final Handler mMainHandler; private final KeyguardMonitor mKeyguardMonitor; + private final KeyguardBypassController mBypassController; + private final NotificationWakeUpCoordinator mWakeUpCoordinator; private int mLastState = 0; private boolean mTransientBiometricsError; @@ -92,6 +97,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private int mIconColor; private float mDozeAmount; private int mIconRes; + private boolean mBouncerShowing; private boolean mWasPulsingOnThisFrame; private boolean mWakeAndUnlockRunning; private boolean mKeyguardShowing; @@ -150,6 +156,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange StatusBarStateController statusBarStateController, ConfigurationController configurationController, AccessibilityController accessibilityController, + KeyguardBypassController bypassController, + NotificationWakeUpCoordinator wakeUpCoordinator, KeyguardMonitor keyguardMonitor, @Nullable DockManager dockManager, @Named(MAIN_HANDLER_NAME) Handler mainHandler) { @@ -160,6 +168,8 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mAccessibilityController = accessibilityController; mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; + mBypassController = bypassController; + mWakeUpCoordinator = wakeUpCoordinator; mKeyguardMonitor = keyguardMonitor; mDockManager = dockManager; mMainHandler = mainHandler; @@ -173,6 +183,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mKeyguardMonitor.addCallback(mKeyguardMonitorCallback); mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); mUnlockMethodCache.addListener(this); + mWakeUpCoordinator.addListener(this); mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure(); if (mDockManager != null) { mDockManager.addListener(mDockEventListener); @@ -187,6 +198,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mConfigurationController.removeCallback(this); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback); + mWakeUpCoordinator.removeFullyHiddenChangedListener(this); mUnlockMethodCache.removeListener(this); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); @@ -279,6 +291,12 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; + if (mBypassController.getBypassEnabled() + && mStatusBarStateController.getState() == StatusBarState.KEYGUARD + && !mWakeUpCoordinator.getNotificationsFullyHidden() + && !mBouncerShowing) { + invisible = true; + } setVisibility(invisible ? INVISIBLE : VISIBLE); updateClickability(); } @@ -369,6 +387,20 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange return -1; } + @Override + public void onFullyHiddenChanged(boolean isFullyHidden) { + if (mBypassController.getBypassEnabled()) { + update(); + } + } + + public void setBouncerShowing(boolean bouncerShowing) { + mBouncerShowing = bouncerShowing; + if (mBypassController.getBypassEnabled()) { + update(); + } + } + @Retention(RetentionPolicy.SOURCE) @IntDef({ERROR, UNLOCK, LOCK, SCANNING, LOCK_IN}) @interface LockAnimIndex {} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 4fb5b98ec3adf..5310a5449cd7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationUtils; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -41,7 +42,8 @@ import java.util.function.Function; * normally reserved for notifications. */ public class NotificationIconAreaController implements DarkReceiver, - StatusBarStateController.StateListener { + StatusBarStateController.StateListener, + NotificationWakeUpCoordinator.WakeUpListener { public static final String HIGH_PRIORITY = "high_priority"; private static final long AOD_ICONS_APPEAR_DURATION = 200; @@ -51,6 +53,8 @@ public class NotificationIconAreaController implements DarkReceiver, private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons; private final StatusBarStateController mStatusBarStateController; private final NotificationMediaManager mMediaManager; + private final NotificationWakeUpCoordinator mWakeUpCoordinator; + private final KeyguardBypassController mBypassController; private final DozeParameters mDozeParameters; @VisibleForTesting final NotificationListener.NotificationSettingsListener mSettingsListener = @@ -91,6 +95,8 @@ public class NotificationIconAreaController implements DarkReceiver, public NotificationIconAreaController(Context context, StatusBar statusBar, StatusBarStateController statusBarStateController, + NotificationWakeUpCoordinator wakeUpCoordinator, + KeyguardBypassController keyguardBypassController, NotificationListener notificationListener, NotificationMediaManager notificationMediaManager) { mStatusBar = statusBar; @@ -102,6 +108,9 @@ public class NotificationIconAreaController implements DarkReceiver, mMediaManager = notificationMediaManager; notificationListener.addNotificationSettingsListener(mSettingsListener); mDozeParameters = DozeParameters.getInstance(mContext); + mWakeUpCoordinator = wakeUpCoordinator; + wakeUpCoordinator.addListener(this); + mBypassController = keyguardBypassController; initializeNotificationAreaViews(context); reloadAodColor(); @@ -238,7 +247,8 @@ public class NotificationIconAreaController implements DarkReceiver, protected boolean shouldShowNotificationIcon(NotificationEntry entry, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, - boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon) { + boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, + boolean hidePulsing) { final boolean isCenteredNotificationIcon = entry.centeredIcon != null && Objects.equals(entry.centeredIcon, mCenteredIconView); @@ -270,6 +280,9 @@ public class NotificationIconAreaController implements DarkReceiver, if (!showAmbient && entry.shouldSuppressStatusBar()) { return false; } + if (hidePulsing && entry.showingPulsing()) { + return false; + } return true; } @@ -292,7 +305,8 @@ public class NotificationIconAreaController implements DarkReceiver, false /* hideDismissed */, false /* hideRepliedMessages */, false /* hideCurrentMedia */, - true /* hide centered icon */); + true /* hide centered icon */, + false /* hidePulsing */); } public void updateStatusBarIcons() { @@ -302,7 +316,8 @@ public class NotificationIconAreaController implements DarkReceiver, true /* hideDismissed */, true /* hideRepliedMessages */, false /* hideCurrentMedia */, - true /* hide centered icon */); + true /* hide centered icon */, + false /* hidePulsing */); } private void updateCenterIcon() { @@ -312,7 +327,8 @@ public class NotificationIconAreaController implements DarkReceiver, false /* hideDismissed */, false /* hideRepliedMessages */, false /* hideCurrentMedia */, - false /* hide centered icon */); + false /* hide centered icon */, + false /* hidePulsing */); } public void updateAodIcons() { @@ -322,7 +338,8 @@ public class NotificationIconAreaController implements DarkReceiver, true /* hideDismissed */, true /* hideRepliedMessages */, true /* hideCurrentMedia */, - true /* hide centered icon */); + true /* hide centered icon */, + mBypassController.getBypassEnabled() /* hidePulsing */); } @VisibleForTesting @@ -338,11 +355,12 @@ public class NotificationIconAreaController implements DarkReceiver, * @param showAmbient should ambient notification icons be shown * @param hideDismissed should dismissed icons be hidden * @param hideRepliedMessages should messages that have been replied to be hidden + * @param hidePulsing should pulsing notifications be hidden */ private void updateIconsForLayout(Function function, NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, - boolean hideCenteredIcon) { + boolean hideCenteredIcon, boolean hidePulsing) { ArrayList toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); @@ -352,7 +370,7 @@ public class NotificationIconAreaController implements DarkReceiver, if (view instanceof ExpandableNotificationRow) { NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry(); if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed, - hideRepliedMessages, hideCurrentMedia, hideCenteredIcon)) { + hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing)) { StatusBarIconView iconView = function.apply(ent); if (iconView != null) { toShow.add(iconView); @@ -514,6 +532,7 @@ public class NotificationIconAreaController implements DarkReceiver, @Override public void onStateChanged(int newState) { + updateAodIconsVisibility(); updateAnimations(); } @@ -562,17 +581,31 @@ public class NotificationIconAreaController implements DarkReceiver, } } - public void setFullyHidden(boolean fullyHidden) { - if (mFullyHidden != fullyHidden) { - mFullyHidden = fullyHidden; - if (fullyHidden) { - appearAodIcons(); - } + @Override + public void onFullyHiddenChanged(boolean fullyHidden) { + if (fullyHidden && !mBypassController.getBypassEnabled()) { + appearAodIcons(); + } + updateAodIconsVisibility(); + updateAodIcons(); + } + + @Override + public void onPulseExpansionChanged(boolean expandingChanged) { + if (expandingChanged) { updateAodIconsVisibility(); } } private void updateAodIconsVisibility() { - mAodIcons.setVisibility(mFullyHidden ? View.VISIBLE : View.INVISIBLE); + boolean visible = mBypassController.getBypassEnabled() + || mWakeUpCoordinator.getNotificationsFullyHidden(); + if (mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { + visible = false; + } + if (visible && mWakeUpCoordinator.isPulseExpanding()) { + visible = false; + } + mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index fa401fee82400..e805575e38846 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -113,7 +113,8 @@ public class NotificationPanelView extends PanelView implements KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener, OnHeadsUpChangedListener, QS.HeightListener, ZenModeController.Callback, ConfigurationController.ConfigurationListener, StateListener, - PulseExpansionHandler.ExpansionCallback, DynamicPrivacyController.Listener { + PulseExpansionHandler.ExpansionCallback, DynamicPrivacyController.Listener, + NotificationWakeUpCoordinator.WakeUpListener { private static final boolean DEBUG = false; @@ -429,16 +430,16 @@ public class NotificationPanelView extends PanelView implements mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); mQsFrame = findViewById(R.id.qs_frame); mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController); - - - mNotificationStackScroller.setOnPulseHeightChangedListener( - () -> { - if (mKeyguardBypassController.getBypassEnabled()) { - // Position the notifications while dragging down while pulsing - requestScrollerTopPaddingUpdate(false /* animate */); - updateQSPulseExpansion(); - } - }); + mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { + @Override + public void onPulseExpansionChanged(boolean expandingChanged) { + if (mKeyguardBypassController.getBypassEnabled()) { + // Position the notifications while dragging down while pulsing + requestScrollerTopPaddingUpdate(false /* animate */); + updateQSPulseExpansion(); + } + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 2d4c1aa0e1791..96e7efe1335ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -799,6 +799,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationIconAreaController = SystemUIFactory.getInstance() .createNotificationIconAreaController(context, this, + mWakeUpCoordinator, mKeyguardBypassController, mStatusBarStateController, mNotificationListener); mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController); inflateShelf(); @@ -3572,6 +3573,8 @@ public class StatusBar extends SystemUI implements DemoMode, public void setBouncerShowing(boolean bouncerShowing) { mBouncerShowing = bouncerShowing; mKeyguardBypassController.setBouncerShowing(bouncerShowing); + mPulseExpansionHandler.setBouncerShowing(bouncerShowing); + mStatusBarWindow.setBouncerShowing(bouncerShowing); if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 9417295b35214..a82e14efca2f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -525,6 +525,12 @@ public class StatusBarWindowView extends FrameLayout { mBypassController = bypassController; } + public void setBouncerShowing(boolean bouncerShowing) { + if (mLockIcon != null) { + mLockIcon.setBouncerShowing(bouncerShowing); + } + } + public class LayoutParams extends FrameLayout.LayoutParams { public boolean ignoreRightInset; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java index 61b753079f0b7..b1d5d2672ca67 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconAreaControllerTest.java @@ -33,12 +33,12 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @SmallTest @@ -57,6 +57,10 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { @Mock StatusBarStateController mStatusBarStateController; @Mock + NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; + @Mock + KeyguardBypassController mBypassController; + @Mock private NotificationMediaManager mMediaManager; private NotificationIconAreaController mController; @@ -67,7 +71,8 @@ public class NotificationIconAreaControllerTest extends SysuiTestCase { when(mStatusBarWindowView.findViewById(R.id.clock_notification_icon_container)).thenReturn( mIconContainer); mController = new NotificationIconAreaController(mContext, mStatusBar, - mStatusBarStateController, mListener, mMediaManager); + mStatusBarStateController, mNotificationWakeUpCoordinator, mBypassController, + mListener, mMediaManager); } @Test