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/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/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/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/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 a9d4fdeae3970..bdc4d2a386420 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,33 @@ 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(value) { + val changed = field != value + field = value + bypassController.isPulseExpanding = value + if (changed && !value && !leavingLockscreen) { + bypassController.maybePerformPendingUnlock() + pulseExpandAbortListener?.run() + } + } + var leavingLockscreen: Boolean = false private set 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,9 +87,13 @@ 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 + var qsExpanded: Boolean = false + var pulseExpandAbortListener: Runnable? = null + var bouncerShowing: Boolean = false init { mMinDragDistance = context.resources.getDimensionPixelSize( @@ -91,9 +108,14 @@ constructor(context: Context, } private fun maybeStartExpansion(event: MotionEvent): Boolean { - if (!mPulsing) { + if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded + || bouncerShowing) { return false } + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain() + } + velocityTracker!!.addMovement(event) val x = event.x val y = event.y @@ -101,6 +123,7 @@ constructor(context: Context, MotionEvent.ACTION_DOWN -> { mDraggedFarEnough = false isExpanding = false + leavingLockscreen = false mStartingChild = null mInitialTouchY = y mInitialTouchX = x @@ -114,29 +137,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 +193,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 +221,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 +241,7 @@ constructor(context: Context, private fun setEmptyDragAmount(amount: Float) { mEmptyDragAmount = amount - mExpansionCallback!!.setEmptyDragAmount(amount) + expansionCallback.setEmptyDragAmount(amount) } private fun reset(child: ExpandableView) { @@ -227,6 +276,7 @@ constructor(context: Context, } private fun cancelExpansion() { + isExpanding = false mFalsingManager.onExpansionFromPulseStopped() if (mStartingChild != null) { reset(mStartingChild!!) @@ -234,30 +284,29 @@ constructor(context: Context, } else { resetClock() } - mWakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */, + wakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */, true /* animate */, false /* increaseSpeed */) - isExpanding = false } 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 6dc5fb34f9bd5..95af9fde1e98a 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 @@ -66,7 +66,11 @@ 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 set(value) { if (!value || mDozeAmount != 0.0f) { @@ -75,7 +79,6 @@ class NotificationWakeUpCoordinator @Inject constructor( } lateinit var iconAreaController : NotificationIconAreaController - var pulsing: Boolean = false set(value) { field = value @@ -88,6 +91,30 @@ 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 + */ + 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) @@ -97,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 @@ -116,10 +154,19 @@ 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()) - && pulsing + var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() + visible = visible && canShowPulsingHuns + if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) { // let's not make notifications invisible while waking up, otherwise the animation // is strange @@ -173,7 +220,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) { @@ -220,14 +267,14 @@ class NotificationWakeUpCoordinator @Inject constructor( } fun getWakeUpHeight() : Float { - return mStackScroller.pulseHeight + return mStackScroller.wakeUpHeight } private fun updateHideAmount() { 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) { @@ -240,14 +287,21 @@ 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) { 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(); } @@ -276,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/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/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 aad35b2461781..2e58add664e88 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 @@ -630,7 +630,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(); @@ -986,6 +986,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) { @@ -2782,7 +2786,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } else { mTopPaddingOverflow = 0; } - setTopPadding(topPadding, animate); + setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled()); setExpandedHeight(mExpandedHeight); } @@ -3276,7 +3280,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) { @@ -3284,7 +3288,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); } @@ -5585,6 +5590,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. @@ -5596,7 +5605,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; @@ -5632,6 +5641,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/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/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/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 9bbe4bed7c0ee..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,6 +33,14 @@ class KeyguardBypassController { private val unlockMethodCache: UnlockMethodCache private val statusBarStateController: StatusBarStateController + /** + * 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. */ @@ -40,13 +48,20 @@ class KeyguardBypassController { get() = field && unlockMethodCache.isUnlockingWithFacePossible private set - lateinit var unlockController: BiometricUnlockController + var bouncerShowing: Boolean = false @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 +87,35 @@ 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 (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 + 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/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/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 69c2a706144ee..103a7c0dbd6d3 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 56a61546fabad..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; @@ -213,6 +214,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(); @@ -350,6 +353,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, @@ -371,6 +375,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); @@ -421,6 +430,16 @@ public class NotificationPanelView extends PanelView implements mWakeUpCoordinator.setStackScroller(mNotificationStackScroller); mQsFrame = findViewById(R.id.qs_frame); mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController); + 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 @@ -468,6 +487,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); } /** @@ -681,12 +704,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, @@ -700,7 +723,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); @@ -720,6 +744,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 @@ -902,7 +934,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 +1036,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 +1111,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; } @@ -1347,6 +1383,7 @@ public class NotificationPanelView extends PanelView implements mFalsingManager.setQsExpanded(expanded); mStatusBar.setQsExpanded(expanded); mNotificationContainerParent.setQsExpanded(expanded); + mPulseExpansionHandler.setQsExpanded(expanded); } } @@ -1361,9 +1398,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)) { @@ -1391,7 +1425,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(); @@ -1640,7 +1676,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) @@ -1648,11 +1684,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 { @@ -1660,8 +1697,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) { @@ -1792,8 +1874,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); @@ -1809,6 +1899,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; } @@ -1958,11 +2057,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); } @@ -2723,6 +2836,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(); @@ -2872,7 +2989,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/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c36f81de91d38..6149a386eed03 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 @@ -798,6 +799,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationIconAreaController = SystemUIFactory.getInstance() .createNotificationIconAreaController(context, this, + mWakeUpCoordinator, mKeyguardBypassController, mStatusBarStateController, mNotificationListener); mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController); inflateShelf(); @@ -3570,6 +3572,9 @@ 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 */); @@ -3621,6 +3626,8 @@ public class StatusBar extends SystemUI implements DemoMode, updateNotificationPanelTouchState(); notifyHeadsUpGoingToSleep(); dismissVolumeDialog(); + mWakeUpCoordinator.setFullyAwake(false); + mKeyguardBypassController.onStartedGoingToSleep(); } @Override @@ -3643,6 +3650,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onFinishedWakingUp() { + mWakeUpCoordinator.setFullyAwake(true); mWakeUpCoordinator.setWakingUp(false); if (mLaunchCameraWhenFinishedWaking) { mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource); 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..a82e14efca2f1 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,16 @@ public class StatusBarWindowView extends FrameLayout { } } + public void setBypassController(KeyguardBypassController bypassController) { + 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/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); } } 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 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);