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); } }