diff options
Diffstat (limited to 'packages/SystemUI/src')
21 files changed, 1279 insertions, 507 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 3a3f2fc40b25..388c085fd1a0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -198,6 +198,13 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** + * @return {@code true} if we are currently animating the screen off from unlock + */ + public boolean isAnimatingScreenOffFromUnlocked() { + return mKeyguardVisibilityHelper.isAnimatingScreenOffFromUnlocked(); + } + + /** * Set the visibility of the keyguard status view based on some new state. */ public void setKeyguardStatusViewVisibility( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 6aca5a702a1a..b6a58dc7cb91 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -22,6 +22,9 @@ import android.view.View; import com.android.systemui.animation.Interpolators; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.AnimatableProperty; +import com.android.systemui.statusbar.notification.PropertyAnimator; +import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -37,6 +40,8 @@ public class KeyguardVisibilityHelper { private final DozeParameters mDozeParameters; private boolean mKeyguardViewVisibilityAnimating; private boolean mLastOccludedState = false; + private boolean mAnimatingScreenOff; + private final AnimationProperties mAnimationProperties = new AnimationProperties(); public KeyguardVisibilityHelper(View view, KeyguardStateController keyguardStateController, DozeParameters dozeParameters) { @@ -89,12 +94,21 @@ public class KeyguardVisibilityHelper { } else if (statusBarState == KEYGUARD) { if (keyguardFadingAway) { mKeyguardViewVisibilityAnimating = true; + float target = mView.getY() - mView.getHeight() * 0.05f; + int delay = 0; + int duration = 125; + // We animate the Y properly separately using the PropertyAnimator, as the panel + // view als needs to update the end position. + mAnimationProperties.setDuration(duration).setDelay(delay); + PropertyAnimator.cancelAnimation(mView, AnimatableProperty.Y); + PropertyAnimator.setProperty(mView, AnimatableProperty.Y, target, + mAnimationProperties, + true /* animate */); mView.animate() .alpha(0) - .translationYBy(-mView.getHeight() * 0.05f) .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) - .setDuration(125) - .setStartDelay(0) + .setDuration(duration) + .setStartDelay(delay) .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable) .start(); } else if (mLastOccludedState && !isOccluded) { @@ -110,20 +124,30 @@ public class KeyguardVisibilityHelper { .start(); } else if (mDozeParameters.shouldControlUnlockedScreenOff()) { mKeyguardViewVisibilityAnimating = true; + mAnimatingScreenOff = true; mView.setVisibility(View.VISIBLE); mView.setAlpha(0f); + float currentY = mView.getY(); + mView.setY(currentY - mView.getHeight() * 0.1f); + int duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP; + int delay = (int) (duration * .6f); + // We animate the Y properly separately using the PropertyAnimator, as the panel + // view als needs to update the end position. + mAnimationProperties.setDuration(duration).setDelay(delay); + PropertyAnimator.cancelAnimation(mView, AnimatableProperty.Y); + PropertyAnimator.setProperty(mView, AnimatableProperty.Y, currentY, + mAnimationProperties, + true /* animate */); - float curTranslationY = mView.getTranslationY(); - mView.setTranslationY(curTranslationY - mView.getHeight() * 0.1f); mView.animate() - .setStartDelay((int) (StackStateAnimator.ANIMATION_DURATION_WAKEUP * .6f)) - .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP) + .setStartDelay(delay) + .setDuration(duration) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .alpha(1f) - .translationY(curTranslationY) .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable) .start(); + } else { mView.setVisibility(View.VISIBLE); mView.setAlpha(1f); @@ -148,5 +172,13 @@ public class KeyguardVisibilityHelper { private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> { mKeyguardViewVisibilityAnimating = false; + mAnimatingScreenOff = false; }; + + /** + * @return {@code true} if we are currently animating the screen off from unlock + */ + public boolean isAnimatingScreenOffFromUnlocked() { + return mAnimatingScreenOff; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index b367bdf08886..7127444befb7 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -145,7 +145,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme final boolean hasUdfps = mAuthController.getUdfpsSensorLocation() != null; mHasUdfpsOrFaceAuthFeatures = hasFaceAuth || hasUdfps; if (!mHasUdfpsOrFaceAuthFeatures) { - ((ViewGroup) mView.getParent()).removeView(mView); + // Posting since removing a view in the middle of onAttach can lead to a crash in the + // iteration loop when the view isn't last + mView.setVisibility(View.GONE); + mView.post(() -> { + mView.setVisibility(View.VISIBLE); + ((ViewGroup) mView.getParent()).removeView(mView); + }); return; } diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index b668e881230d..241c2a939f9a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -64,6 +64,12 @@ class KeyguardMediaController @Inject constructor( mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN) } + /** + * Is the media player visible? + */ + var visible = false + private set + var visibilityChangedListener: ((Boolean) -> Unit)? = null /** @@ -106,11 +112,11 @@ class KeyguardMediaController @Inject constructor( val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD || statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER) // mediaHost.visible required for proper animations handling - val shouldBeVisible = mediaHost.visible && + visible = mediaHost.visible && !bypassController.bypassEnabled && keyguardOrUserSwitcher && notifLockscreenUserManager.shouldShowLockscreenNotifications() - if (shouldBeVisible) { + if (visible) { showMediaPlayer() } else { hideMediaPlayer() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 60e832ace7b0..73dfe5e68d9a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -26,6 +26,7 @@ import android.util.MathUtils import android.view.View import android.view.ViewGroup import android.view.ViewGroupOverlay +import com.android.systemui.R import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.WakefulnessLifecycle @@ -35,6 +36,7 @@ import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.StackStateAnimator import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.UniqueObjectHostView import javax.inject.Inject @@ -74,6 +76,7 @@ class MediaHierarchyManager @Inject constructor( private val bypassController: KeyguardBypassController, private val mediaCarouselController: MediaCarouselController, private val notifLockscreenUserManager: NotificationLockscreenUserManager, + configurationController: ConfigurationController, wakefulnessLifecycle: WakefulnessLifecycle, private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager ) { @@ -183,6 +186,58 @@ class MediaHierarchyManager @Inject constructor( } /** + * distance that the full shade transition takes in order for media to fully transition to the + * shade + */ + private var distanceForFullShadeTransition = 0 + + /** + * Delay after which the media will start transitioning to the full shade on the lockscreen. + */ + private var fullShadeTransitionDelay = 0 + + /** + * The amount of progress we are currently in if we're transitioning to the full shade. + * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full + * shade. + */ + private var fullShadeTransitionProgress = 0f + set(value) { + if (field == value) { + return + } + field = value + if (bypassController.bypassEnabled) { + return + } + updateDesiredLocation() + if (value >= 0) { + updateTargetState() + applyTargetStateIfNotAnimating() + } + } + + private val isTransitioningToFullShade: Boolean + get() = fullShadeTransitionProgress != 0f && !bypassController.bypassEnabled + + /** + * Set the amount of pixels we have currently dragged down if we're transitioning to the full + * shade. 0.0f means we're not transitioning yet. + */ + fun setTransitionToFullShadeAmount(value: Float) { + // If we're transitioning starting on the shade_locked, we don't want any delay and rather + // have it aligned with the rest of the animation + val delay = if (statusbarState == StatusBarState.KEYGUARD) { + fullShadeTransitionDelay + } else { + 0 + } + val progress = MathUtils.saturate((value - delay) / + (distanceForFullShadeTransition - delay)) + fullShadeTransitionProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(progress) + } + + /** * Is the shade currently collapsing from the expanded qs? If we're on the lockscreen and in qs, * we wouldn't want to transition in that case. */ @@ -242,6 +297,12 @@ class MediaHierarchyManager @Inject constructor( } init { + updateConfiguration() + configurationController.addCallback(object : ConfigurationController.ConfigurationListener { + override fun onDensityOrFontScaleChanged() { + updateConfiguration() + } + }) statusBarStateController.addCallback(object : StatusBarStateController.StateListener { override fun onStatePreChange(oldState: Int, newState: Int) { // We're updating the location before the state change happens, since we want the @@ -312,6 +373,13 @@ class MediaHierarchyManager @Inject constructor( }) } + private fun updateConfiguration() { + distanceForFullShadeTransition = context.resources.getDimensionPixelSize( + R.dimen.lockscreen_shade_qs_transition_distance) + fullShadeTransitionDelay = context.resources.getDimensionPixelSize( + R.dimen.lockscreen_shade_media_transition_start_delay) + } + /** * Register a media host and create a view can be attached to a view hierarchy * and where the players will be placed in when the host is the currently desired state. @@ -546,6 +614,9 @@ class MediaHierarchyManager @Inject constructor( if (progress >= 0) { return progress } + if (isTransitioningToFullShade) { + return fullShadeTransitionProgress + } return -1.0f } @@ -643,6 +714,7 @@ class MediaHierarchyManager @Inject constructor( val location = when { qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS qsExpansion > 0.4f && onLockscreen -> LOCATION_QS + onLockscreen && isTransitioningToFullShade -> LOCATION_QQS onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 53b4d5f7d5f8..34c654c9135d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -112,6 +112,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca * otherwise. */ private boolean mTranslateWhileExpanding; + private boolean mPulseExpanding; + + /** + * Are we currently transitioning from lockscreen to the full shade? + */ + private boolean mTransitioningToFullShade; + + /** + * Whether the next Quick settings + */ + private boolean mAnimateNextQsUpdate; @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, @@ -265,6 +276,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } + @Override + public boolean isFullyCollapsed() { + return mLastQSExpansion == 0.0f || mLastQSExpansion == -1; + } + private void setEditLocation(View view) { View edit = view.findViewById(android.R.id.edit); int[] loc = edit.getLocationOnScreen(); @@ -335,14 +351,22 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @Override - public void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) { - if (showCollapsedOnKeyguard != mShowCollapsedOnKeyguard) { - mShowCollapsedOnKeyguard = showCollapsedOnKeyguard; + public void setPulseExpanding(boolean pulseExpanding) { + if (pulseExpanding != mPulseExpanding) { + mPulseExpanding = pulseExpanding; + updateShowCollapsedOnKeyguard(); + } + } + + private void updateShowCollapsedOnKeyguard() { + boolean showCollapsed = mPulseExpanding || mTransitioningToFullShade; + if (showCollapsed != mShowCollapsedOnKeyguard) { + mShowCollapsedOnKeyguard = showCollapsed; updateQsState(); if (mQSAnimator != null) { - mQSAnimator.setShowCollapsedOnKeyguard(showCollapsedOnKeyguard); + mQSAnimator.setShowCollapsedOnKeyguard(showCollapsed); } - if (!showCollapsedOnKeyguard && isKeyguardShowing()) { + if (!showCollapsed && isKeyguardShowing()) { setQsExpansion(mLastQSExpansion, 0); } } @@ -411,13 +435,24 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @Override - public void setQsExpansion(float expansion, float headerTranslation) { - if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); + public void setTransitionToFullShadeAmount(float pxAmount, boolean animated) { + boolean isTransitioningToFullShade = pxAmount > 0; + if (isTransitioningToFullShade != mTransitioningToFullShade) { + mTransitioningToFullShade = isTransitioningToFullShade; + updateShowCollapsedOnKeyguard(); + setQsExpansion(mLastQSExpansion, mLastHeaderTranslation); + } + } + @Override + public void setQsExpansion(float expansion, float proposedTranslation) { + if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + proposedTranslation); + float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation; if (mQSAnimator != null) { final boolean showQSOnLockscreen = expansion > 0; final boolean showQSUnlocked = headerTranslation == 0 || !mTranslateWhileExpanding; - mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked); + mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked + || mTransitioningToFullShade); } mContainer.setExpansion(expansion); final float translationScaleY = (mTranslateWhileExpanding @@ -542,18 +577,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @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 && getView().getTranslationY() != 0) { - mHeaderAnimating = true; - mDelay = delay; - getView().getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn); - } - } - - @Override public void animateHeaderSlidingOut() { if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut"); if (getView().getY() == -mHeader.getHeight()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 6d91ccba0156..12f569ce9fbc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -5,35 +5,489 @@ import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.content.Context +import android.content.res.Configuration +import android.os.SystemClock +import android.util.DisplayMetrics +import android.util.MathUtils import android.view.MotionEvent import android.view.View import android.view.ViewConfiguration +import androidx.annotation.VisibleForTesting +import com.android.internal.logging.nano.MetricsProto.MetricsEvent import com.android.systemui.ExpandHelper import com.android.systemui.Gefingerpoken -import com.android.systemui.animation.Interpolators import com.android.systemui.R +import com.android.systemui.animation.Interpolators import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.media.MediaHierarchyManager +import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.qs.QS +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.statusbar.notification.stack.AmbientState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.LockscreenGestureLogger +import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent +import com.android.systemui.statusbar.phone.NotificationPanelViewController +import com.android.systemui.statusbar.phone.ScrimController +import com.android.systemui.statusbar.phone.StatusBar +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.Utils +import javax.inject.Inject + +private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375L +private const val RUBBERBAND_FACTOR_STATIC = 0.15f +private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f + +/** + * A class that controls the lockscreen to shade transition + */ +@SysUISingleton +class LockscreenShadeTransitionController @Inject constructor( + private val statusBarStateController: SysuiStatusBarStateController, + private val lockscreenGestureLogger: LockscreenGestureLogger, + private val keyguardBypassController: KeyguardBypassController, + private val lockScreenUserManager: NotificationLockscreenUserManager, + private val falsingCollector: FalsingCollector, + private val ambientState: AmbientState, + private val displayMetrics: DisplayMetrics, + private val mediaHierarchyManager: MediaHierarchyManager, + private val scrimController: ScrimController, + private val featureFlags: FeatureFlags, + private val context: Context, + configurationController: ConfigurationController, + falsingManager: FalsingManager +) { + private var useSplitShade: Boolean = false + private lateinit var nsslController: NotificationStackScrollLayoutController + lateinit var notificationPanelController: NotificationPanelViewController + lateinit var statusbar: StatusBar + lateinit var qS: QS + + /** + * A handler that handles the next keyguard dismiss animation. + */ + private var animationHandlerOnKeyguardDismiss: ((Long) -> Unit)? = null + + /** + * The entry that was just dragged down on. + */ + private var draggedDownEntry: NotificationEntry? = null + + /** + * The current animator if any + */ + @VisibleForTesting + internal var dragDownAnimator: ValueAnimator? = null + + /** + * Distance that the full shade transition takes in order for scrim to fully transition to + * the shade (in alpha) + */ + private var scrimTransitionDistance = 0 + + /** + * Distance that the full transition takes in order for us to fully transition to the shade + */ + private var fullTransitionDistance = 0 + + /** + * Flag to make sure that the dragDownAmount is applied to the listeners even when in the + * locked down shade. + */ + private var forceApplyAmount = false + + /** + * A flag to suppress the default animation when unlocking in the locked down shade. + */ + private var nextHideKeyguardNeedsNoAnimation = false + + /** + * The touch helper responsible for the drag down animation. + */ + val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context) + + init { + updateResources() + configurationController.addCallback(object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + updateResources() + touchHelper.updateResources(context) + } + }) + } + + private fun updateResources() { + scrimTransitionDistance = context.resources.getDimensionPixelSize( + R.dimen.lockscreen_shade_scrim_transition_distance) + fullTransitionDistance = context.resources.getDimensionPixelSize( + R.dimen.lockscreen_shade_qs_transition_distance) + useSplitShade = Utils.shouldUseSplitNotificationShade(featureFlags, context.resources) + } + + fun setStackScroller(nsslController: NotificationStackScrollLayoutController) { + this.nsslController = nsslController + touchHelper.host = nsslController.view + touchHelper.expandCallback = nsslController.expandHelperCallback + } + + /** + * Initialize the shelf controller such that clicks on it will expand the shade + */ + fun bindController(notificationShelfController: NotificationShelfController) { + // Bind the click listener of the shelf to go to the full shade + notificationShelfController.setOnClickListener { + if (statusBarStateController.state == StatusBarState.KEYGUARD) { + statusbar.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK") + goToLockedShade(it) + } + } + } + + /** + * @return true if the interaction is accepted, false if it should be cancelled + */ + internal fun canDragDown(): Boolean { + return (statusBarStateController.state == StatusBarState.KEYGUARD || + nsslController.isInLockedDownShade()) && + qS.isFullyCollapsed + } + + /** + * Called by the touch helper when when a gesture has completed all the way and released. + */ + internal fun onDraggedDown(startingChild: View?, dragLengthY: Int) { + if (canDragDown()) { + if (nsslController.isInLockedDownShade()) { + statusBarStateController.setLeaveOpenOnKeyguardHide(true) + statusbar.dismissKeyguardThenExecute(OnDismissAction { + nextHideKeyguardNeedsNoAnimation = true + false + }, + null /* cancelRunnable */, false /* afterKeyguardGone */) + } else { + lockscreenGestureLogger.write( + MetricsEvent.ACTION_LS_SHADE, + (dragLengthY / displayMetrics.density).toInt(), + 0 /* velocityDp */) + lockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN) + if (!ambientState.isDozing() || startingChild != null) { + // go to locked shade while animating the drag down amount from its current + // value + val animationHandler = { delay: Long -> + if (startingChild is ExpandableNotificationRow) { + startingChild.onExpandedByGesture( + true /* drag down is always an open */) + } + notificationPanelController.animateToFullShade(delay) + notificationPanelController.setTransitionToFullShadeAmount(0f, + true /* animated */, delay) + + // Let's reset ourselves, ready for the next animation + + // changing to shade locked will make isInLockDownShade true, so let's + // override that + forceApplyAmount = true + // Reset the behavior. At this point the animation is already started + dragDownAmount = 0f + forceApplyAmount = false + } + val cancelRunnable = Runnable { setDragDownAmountAnimated(0f) } + goToLockedShadeInternal(startingChild, animationHandler, cancelRunnable) + } + } + } else { + setDragDownAmountAnimated(0f) + } + } + + /** + * Called by the touch helper when the drag down was aborted and should be reset. + */ + internal fun onDragDownReset() { + nsslController.setDimmed(true /* dimmed */, true /* animated */) + nsslController.resetScrollPosition() + nsslController.resetCheckSnoozeLeavebehind() + setDragDownAmountAnimated(0f) + } + + /** + * The user has dragged either above or below the threshold which changes the dimmed state. + * @param above whether they dragged above it + */ + internal fun onCrossedThreshold(above: Boolean) { + nsslController.setDimmed(!above /* dimmed */, true /* animate */) + } + + /** + * Called by the touch helper when the drag down was started + */ + internal fun onDragDownStarted() { + nsslController.cancelLongPress() + nsslController.checkSnoozeLeavebehind() + dragDownAnimator?.cancel() + } + + /** + * Do we need a falsing check currently? + */ + internal val isFalsingCheckNeeded: Boolean + get() = statusBarStateController.state == StatusBarState.KEYGUARD + + /** + * Is dragging down enabled on a given view + * @param view The view to check or `null` to check if it's enabled at all + */ + internal fun isDragDownEnabledForView(view: ExpandableView?): Boolean { + if (isDragDownAnywhereEnabled) { + return true + } + if (nsslController.isInLockedDownShade()) { + if (view == null) { + // Dragging down is allowed in general + return true + } + if (view is ExpandableNotificationRow) { + // Only drag down on sensitive views, otherwise the ExpandHelper will take this + return view.entry.isSensitive + } + } + return false + } + + /** + * @return if drag down is enabled anywhere, not just on selected views. + */ + internal val isDragDownAnywhereEnabled: Boolean + get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD && + !keyguardBypassController.bypassEnabled && + qS.isFullyCollapsed) + + /** + * The amount in pixels that the user has dragged down. + */ + internal var dragDownAmount = 0f + set(value) { + if (field != value || forceApplyAmount) { + field = value + if (!nsslController.isInLockedDownShade() || forceApplyAmount) { + nsslController.setTransitionToFullShadeAmount(field) + notificationPanelController.setTransitionToFullShadeAmount(field, + false /* animate */, 0 /* delay */) + mediaHierarchyManager.setTransitionToFullShadeAmount(field) + val scrimProgress = MathUtils.saturate(field / scrimTransitionDistance) + scrimController.setTransitionToFullShadeProgress(scrimProgress) + // TODO: appear qs also in split shade + val qsAmount = if (useSplitShade) 0f else field + qS.setTransitionToFullShadeAmount(qsAmount, false /* animate */) + } + } + } + + private fun setDragDownAmountAnimated( + target: Float, + delay: Long = 0, + endlistener: (() -> Unit)? = null + ) { + val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target) + dragDownAnimator.interpolator = Interpolators.FAST_OUT_SLOW_IN + dragDownAnimator.duration = SPRING_BACK_ANIMATION_LENGTH_MS + dragDownAnimator.addUpdateListener { animation: ValueAnimator -> + dragDownAmount = animation.animatedValue as Float + } + if (delay > 0) { + dragDownAnimator.startDelay = delay + } + if (endlistener != null) { + dragDownAnimator.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator?) { + endlistener.invoke() + } + }) + } + dragDownAnimator.start() + this.dragDownAnimator = dragDownAnimator + } + + /** + * Animate appear the drag down amount. + */ + private fun animateAppear(delay: Long = 0) { + // changing to shade locked will make isInLockDownShade true, so let's override + // that + forceApplyAmount = true + + // we set the value initially to 1 pixel, since that will make sure we're + // transitioning to the full shade. this is important to avoid flickering, + // as the below animation only starts once the shade is unlocked, which can + // be a couple of frames later. if we're setting it to 0, it will use the + // default inset and therefore flicker + dragDownAmount = 1f + setDragDownAmountAnimated(fullTransitionDistance.toFloat(), delay = delay) { + // End listener: + // Reset + dragDownAmount = 0f + forceApplyAmount = false + } + } + + /** + * Ask this controller to go to the locked shade, changing the state change and doing + * an animation, where the qs appears from 0 from the top + * + * If secure with redaction: Show bouncer, go to unlocked shade. + * If secure without redaction or no security: Go to [StatusBarState.SHADE_LOCKED]. + * + * @param expandView The view to expand after going to the shade + * @param needsQSAnimation if this needs the quick settings to slide in from the top or if + * that's already handled separately + */ + @JvmOverloads + fun goToLockedShade(expandedView: View?, needsQSAnimation: Boolean = true) { + if (statusBarStateController.state == StatusBarState.KEYGUARD) { + val animationHandler: ((Long) -> Unit)? + if (needsQSAnimation) { + // Let's use the default animation + animationHandler = null + } else { + // Let's only animate notifications + animationHandler = { delay: Long -> + notificationPanelController.animateToFullShade(delay) + } + } + goToLockedShadeInternal(expandedView, animationHandler, + cancelAction = null) + } + } + + /** + * If secure with redaction: Show bouncer, go to unlocked shade. + * + * If secure without redaction or no security: Go to [StatusBarState.SHADE_LOCKED]. + * + * @param expandView The view to expand after going to the shade. + * @param animationHandler The handler which performs the go to full shade animation. If null, + * the default handler will do the animation, otherwise the caller is + * responsible for the animation. The input value is a Long for the + * delay for the animation. + * @param cancelAction The runnable to invoke when the transition is aborted. This happens if + * the user goes to the bouncer and goes back. + */ + private fun goToLockedShadeInternal( + expandView: View?, + animationHandler: ((Long) -> Unit)? = null, + cancelAction: Runnable? = null + ) { + if (statusbar.isShadeDisabled) { + cancelAction?.run() + return + } + var userId: Int = lockScreenUserManager.getCurrentUserId() + var entry: NotificationEntry? = null + if (expandView is ExpandableNotificationRow) { + entry = expandView.entry + entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */) + // Indicate that the group expansion is changing at this time -- this way the group + // and children backgrounds / divider animations will look correct. + entry.setGroupExpansionChanging(true) + userId = entry.sbn.userId + } + var fullShadeNeedsBouncer = (!lockScreenUserManager.userAllowsPrivateNotificationsInPublic( + lockScreenUserManager.getCurrentUserId()) || + !lockScreenUserManager.shouldShowLockscreenNotifications() || + falsingCollector.shouldEnforceBouncer()) + if (keyguardBypassController.bypassEnabled) { + fullShadeNeedsBouncer = false + } + if (lockScreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) { + statusBarStateController.setLeaveOpenOnKeyguardHide(true) + var onDismissAction: OnDismissAction? = null + if (animationHandler != null) { + onDismissAction = OnDismissAction { + // We're waiting on keyguard to hide before triggering the action, + // as that will make the animation work properly + animationHandlerOnKeyguardDismiss = animationHandler + false + } + } + val cancelHandler = Runnable { + draggedDownEntry?.apply { + setUserLocked(false) + notifyHeightChanged(false /* needsAnimation */) + draggedDownEntry = null + } + cancelAction?.run() + } + statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler) + draggedDownEntry = entry + } else { + statusBarStateController.setState(StatusBarState.SHADE_LOCKED) + // This call needs to be after updating the shade state since otherwise + // the scrimstate resets too early + if (animationHandler != null) { + animationHandler.invoke(0 /* delay */) + } else { + performDefaultGoToFullShadeAnimation(0) + } + } + } + + /** + * Notify this handler that the keyguard was just dismissed and that a animation to + * the full shade should happen. + */ + fun onHideKeyguard(delay: Long) { + if (animationHandlerOnKeyguardDismiss != null) { + animationHandlerOnKeyguardDismiss!!.invoke(delay) + animationHandlerOnKeyguardDismiss = null + } else { + if (nextHideKeyguardNeedsNoAnimation) { + nextHideKeyguardNeedsNoAnimation = false + } else { + performDefaultGoToFullShadeAnimation(delay) + } + } + draggedDownEntry?.apply { + setUserLocked(false) + draggedDownEntry = null + } + } + + /** + * Perform the default appear animation when going to the full shade. This is called when + * not triggered by gestures, e.g. when clicking on the shelf or expand button. + */ + private fun performDefaultGoToFullShadeAnimation(delay: Long) { + notificationPanelController.animateToFullShade(delay) + animateAppear(delay) + } +} /** * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand * the notification where the drag started. */ -class DragDownHelper(private val falsingManager: FalsingManager, - private val expandCallback: ExpandHelper.Callback, - private val dragDownCallback: DragDownCallback, - private val falsingCollector: FalsingCollector, - private val host: View, - context: Context): Gefingerpoken { - - private val minDragDistance: Int +class DragDownHelper( + private val falsingManager: FalsingManager, + private val falsingCollector: FalsingCollector, + private val dragDownCallback: LockscreenShadeTransitionController, + context: Context +) : Gefingerpoken { + + private var dragDownAmountOnStart = 0.0f + lateinit var expandCallback: ExpandHelper.Callback + lateinit var host: View + + private var minDragDistance = 0 private var initialTouchX = 0f private var initialTouchY = 0f - private val touchSlop: Float - private val slopMultiplier: Float + private var touchSlop = 0f + private var slopMultiplier = 0f private val temp2 = IntArray(2) private var draggedFarEnough = false private var startingChild: ExpandableView? = null @@ -41,7 +495,23 @@ class DragDownHelper(private val falsingManager: FalsingManager, var isDraggingDown = false private set + private val isFalseTouch: Boolean + get() { + return if (!dragDownCallback.isFalsingCheckNeeded) { + false + } else { + falsingManager.isFalseTouch(Classifier.NOTIFICATION_DRAG_DOWN) || !draggedFarEnough + } + } + + val isDragDownEnabled: Boolean + get() = dragDownCallback.isDragDownEnabledForView(null) + init { + updateResources(context) + } + + fun updateResources(context: Context) { minDragDistance = context.resources.getDimensionPixelSize( R.dimen.keyguard_drag_down_min_distance) val configuration = ViewConfiguration.get(context) @@ -74,7 +544,8 @@ class DragDownHelper(private val falsingManager: FalsingManager, captureStartingChild(initialTouchX, initialTouchY) initialTouchY = y initialTouchX = x - dragDownCallback.onTouchSlopExceeded() + dragDownCallback.onDragDownStarted() + dragDownAmountOnStart = dragDownCallback.dragDownAmount return startingChild != null || dragDownCallback.isDragDownAnywhereEnabled } } @@ -92,10 +563,9 @@ class DragDownHelper(private val falsingManager: FalsingManager, MotionEvent.ACTION_MOVE -> { lastHeight = y - initialTouchY captureStartingChild(initialTouchX, initialTouchY) + dragDownCallback.dragDownAmount = lastHeight + dragDownAmountOnStart if (startingChild != null) { handleExpansion(lastHeight, startingChild!!) - } else { - dragDownCallback.setEmptyDragAmount(lastHeight) } if (lastHeight > minDragDistance) { if (!draggedFarEnough) { @@ -110,12 +580,10 @@ class DragDownHelper(private val falsingManager: FalsingManager, } return true } - MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch - && dragDownCallback.canDragDown()) { + MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch && + dragDownCallback.canDragDown()) { dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt()) - if (startingChild == null) { - cancelExpansion() - } else { + if (startingChild != null) { expandCallback.setUserLockedChild(startingChild, false) startingChild = null } @@ -132,15 +600,6 @@ class DragDownHelper(private val falsingManager: FalsingManager, return false } - private val isFalseTouch: Boolean - get() { - return if (!dragDownCallback.isFalsingCheckNeeded) { - false - } else { - falsingManager.isFalseTouch(Classifier.NOTIFICATION_DRAG_DOWN) || !draggedFarEnough - } - } - private fun captureStartingChild(x: Float, y: Float) { if (startingChild == null) { startingChild = findView(x, y) @@ -174,7 +633,7 @@ class DragDownHelper(private val falsingManager: FalsingManager, child.actualHeight = (child.collapsedHeight + rubberband).toInt() } - private fun cancelExpansion(child: ExpandableView) { + private fun cancelChildExpansion(child: ExpandableView) { if (child.actualHeight == child.collapsedHeight) { expandCallback.setUserLockedChild(child, false) return @@ -182,7 +641,7 @@ class DragDownHelper(private val falsingManager: FalsingManager, val anim = ObjectAnimator.ofInt(child, "actualHeight", child.actualHeight, child.collapsedHeight) anim.interpolator = Interpolators.FAST_OUT_SLOW_IN - anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() + anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS anim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { expandCallback.setUserLockedChild(child, false) @@ -191,73 +650,18 @@ class DragDownHelper(private val falsingManager: FalsingManager, anim.start() } - private fun cancelExpansion() { - val anim = ValueAnimator.ofFloat(lastHeight, 0f) - anim.interpolator = Interpolators.FAST_OUT_SLOW_IN - anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() - anim.addUpdateListener { animation: ValueAnimator -> - dragDownCallback.setEmptyDragAmount(animation.animatedValue as Float) - } - anim.start() - } - private fun stopDragging() { falsingCollector.onNotificationStopDraggingDown() if (startingChild != null) { - cancelExpansion(startingChild!!) + cancelChildExpansion(startingChild!!) startingChild = null - } else { - cancelExpansion() } isDraggingDown = false dragDownCallback.onDragDownReset() } - private fun findView(x: Float, y: Float): ExpandableView { + private fun findView(x: Float, y: Float): ExpandableView? { host.getLocationOnScreen(temp2) - return expandCallback.getChildAtRawPosition(x + temp2[0] , y + temp2[1]) - } - - val isDragDownEnabled: Boolean - get() = dragDownCallback.isDragDownEnabledForView(null) - - interface DragDownCallback { - - /** - * @return true if the interaction is accepted, false if it should be cancelled - */ - fun canDragDown(): Boolean - - /** - * Call when a view has been dragged. - **/ - fun onDraggedDown(startingChild: View?, dragLengthY: Int) - fun onDragDownReset() - - /** - * The user has dragged either above or below the threshold - * @param above whether they dragged above it - */ - fun onCrossedThreshold(above: Boolean) - fun onTouchSlopExceeded() - fun setEmptyDragAmount(amount: Float) - val isFalsingCheckNeeded: Boolean - - /** - * Is dragging down enabled on a given view - * @param view The view to check or `null` to check if it's enabled at all - */ - fun isDragDownEnabledForView(view: ExpandableView?): Boolean - - /** - * @return if drag down is enabled anywhere, not just on selected views. - */ - val isDragDownAnywhereEnabled: Boolean - } - - companion object { - private const val RUBBERBAND_FACTOR_EXPANDABLE = 0.5f - private const val RUBBERBAND_FACTOR_STATIC = 0.15f - private const val SPRING_BACK_ANIMATION_LENGTH_MS = 375 + return expandCallback.getChildAtRawPosition(x + temp2[0], y + temp2[1]) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 84465a88eed0..9765ace7179f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -42,7 +42,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.phone.ShadeController import javax.inject.Inject import kotlin.math.max @@ -59,6 +58,7 @@ constructor( private val roundnessManager: NotificationRoundnessManager, private val statusBarStateController: StatusBarStateController, private val falsingManager: FalsingManager, + private val lockscreenShadeTransitionController: LockscreenShadeTransitionController, private val falsingCollector: FalsingCollector ) : Gefingerpoken { companion object { @@ -66,7 +66,6 @@ constructor( private val SPRING_BACK_ANIMATION_LENGTH_MS = 375 } private val mPowerManager: PowerManager? - private lateinit var shadeController: ShadeController private val mMinDragDistance: Int private var mInitialTouchX: Float = 0.0f @@ -95,7 +94,7 @@ constructor( var leavingLockscreen: Boolean = false private set private val mTouchSlop: Float - private lateinit var expansionCallback: ExpansionCallback + private lateinit var overStretchHandler: OverStretchHandler private lateinit var stackScrollerController: NotificationStackScrollLayoutController private val mTemp2 = IntArray(2) private var mDraggedFarEnough: Boolean = false @@ -103,7 +102,7 @@ constructor( private var mPulsing: Boolean = false var isWakingToShadeLocked: Boolean = false private set - private var mEmptyDragAmount: Float = 0.0f + private var overStretchAmount: Float = 0.0f private var mWakeUpHeight: Float = 0.0f private var mReachedWakeUpHeight: Boolean = false private var velocityTracker: VelocityTracker? = null @@ -215,6 +214,7 @@ constructor( private fun finishExpansion() { resetClock() + val startingChild = mStartingChild if (mStartingChild != null) { setUserLocked(mStartingChild!!, false) mStartingChild = null @@ -225,7 +225,8 @@ constructor( mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), WAKE_REASON_GESTURE, "com.android.systemui:PULSEDRAG") } - shadeController.goToLockedShade(mStartingChild) + lockscreenShadeTransitionController.goToLockedShade(startingChild, + needsQSAnimation = false) leavingLockscreen = true isExpanding = false if (mStartingChild is ExpandableNotificationRow) { @@ -252,8 +253,8 @@ constructor( true /* increaseSpeed */) expansionHeight = max(mWakeUpHeight, expansionHeight) } - val emptyDragAmount = wakeUpCoordinator.setPulseHeight(expansionHeight) - setEmptyDragAmount(emptyDragAmount * RUBBERBAND_FACTOR_STATIC) + val dragDownAmount = wakeUpCoordinator.setPulseHeight(expansionHeight) + setOverStretchAmount(dragDownAmount) } private fun captureStartingChild(x: Float, y: Float) { @@ -265,9 +266,9 @@ constructor( } } - private fun setEmptyDragAmount(amount: Float) { - mEmptyDragAmount = amount - expansionCallback.setEmptyDragAmount(amount) + private fun setOverStretchAmount(amount: Float) { + overStretchAmount = amount + overStretchHandler.setOverStretchAmount(amount) } private fun reset(child: ExpandableView) { @@ -294,10 +295,12 @@ constructor( } private fun resetClock() { - val anim = ValueAnimator.ofFloat(mEmptyDragAmount, 0f) + val anim = ValueAnimator.ofFloat(overStretchAmount, 0f) anim.interpolator = Interpolators.FAST_OUT_SLOW_IN anim.duration = SPRING_BACK_ANIMATION_LENGTH_MS.toLong() - anim.addUpdateListener { animation -> setEmptyDragAmount(animation.animatedValue as Float) } + anim.addUpdateListener { + animation -> setOverStretchAmount(animation.animatedValue as Float) + } anim.start() } @@ -329,11 +332,9 @@ constructor( fun setUp( stackScrollerController: NotificationStackScrollLayoutController, - expansionCallback: ExpansionCallback, - shadeController: ShadeController + overStrechHandler: OverStretchHandler ) { - this.expansionCallback = expansionCallback - this.shadeController = shadeController + this.overStretchHandler = overStrechHandler this.stackScrollerController = stackScrollerController } @@ -345,7 +346,11 @@ constructor( isWakingToShadeLocked = false } - interface ExpansionCallback { - fun setEmptyDragAmount(amount: Float) + interface OverStretchHandler { + + /** + * Set the overstretch amount in pixels This will be rubberbanded later + */ + fun setOverStretchAmount(amount: Float) } } 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 caf47207de07..66d23477436f 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 @@ -95,6 +95,7 @@ public class AmbientState { /** Height of the notifications panel without top padding when expansion completes. */ private float mStackEndHeight; + private float mTransitionToFullShadeAmount; /** * @return Height of the notifications panel without top padding when expansion completes. @@ -595,6 +596,21 @@ public class AmbientState { } /** + * Set the amount of pixels we have currently dragged down if we're transitioning to the full + * shade. 0.0f means we're not transitioning yet. + */ + public void setTransitionToFullShadeAmount(float transitionToFullShadeAmount) { + mTransitionToFullShadeAmount = transitionToFullShadeAmount; + } + + /** + * get + */ + public float getTransitionToFullShadeAmount() { + return mTransitionToFullShadeAmount; + } + + /** * Returns the currently tracked heads up row, if there is one and it is currently above the * shelf (still appearing). */ 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 4dbdb133e1a7..3244ff915d87 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 @@ -46,7 +46,6 @@ import android.os.Bundle; import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.util.Pair; @@ -71,17 +70,14 @@ import android.widget.ScrollView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.keyguard.KeyguardSliceView; import com.android.settingslib.Utils; -import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -89,7 +85,6 @@ import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ExpandAnimationParameters; import com.android.systemui.statusbar.notification.FakeShadowView; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -108,9 +103,6 @@ import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonV import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpUtil; @@ -151,7 +143,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1; private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; - private final SysuiStatusBarStateController mStatusbarStateController; private ExpandHelper mExpandHelper; private NotificationSwipeHelper mSwipeHelper; @@ -433,13 +424,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private ShadeController mShadeController; private Runnable mOnStackYChanged; - private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class); - private final LockscreenGestureLogger mLockscreenGestureLogger = - Dependency.get(LockscreenGestureLogger.class); protected boolean mClearAllEnabled; private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN; - private NotificationPanelViewController mNotificationPanelController; private final NotificationSectionsManager mSectionsManager; private ForegroundServiceDungeonView mFgsSectionView; @@ -449,6 +436,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private boolean mWillExpand; private int mGapHeight; + /** + * The extra inset during the full shade transition + */ + private float mExtraTopInsetForFullShadeTransition; + private int mWaterfallTopInset; private NotificationStackScrollLayoutController mController; @@ -496,7 +488,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable NotificationSectionsManager notificationSectionsManager, GroupMembershipManager groupMembershipManager, GroupExpansionManager groupExpansionManager, - SysuiStatusBarStateController statusbarStateController, AmbientState ambientState, FeatureFlags featureFlags) { super(context, attrs, 0, 0); @@ -535,7 +526,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll); mGroupMembershipManager = groupMembershipManager; mGroupExpansionManager = groupExpansionManager; - mStatusbarStateController = statusbarStateController; } void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) { @@ -1142,8 +1132,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private void updateStackPosition() { // Consider interpolating from an mExpansionStartY for use on lockscreen and AOD + float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition; final float fraction = mAmbientState.getExpansionFraction(); - final float stackY = MathUtils.lerp(0, mTopPadding, fraction); + final float stackY = MathUtils.lerp(0, endTopPosition, fraction); mAmbientState.setStackY(stackY); if (mOnStackYChanged != null) { mOnStackYChanged.run(); @@ -4278,6 +4269,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable + mGapHeight; } + /** + * @return the padding after the media header on the lockscreen + */ + public int getPaddingAfterMedia() { + return mGapHeight + mPaddingBetweenElements; + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public int getEmptyShadeViewHeight() { return mEmptyShadeView.getHeight(); @@ -4933,12 +4931,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable getChildCount() - offsetFromEnd); } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setNotificationPanelController( - NotificationPanelViewController notificationPanelViewController) { - mNotificationPanelController = notificationPanelViewController; - } - /** * Set how far the wake up is when waking up from pulsing. This is a height and will adjust the * notification positions accordingly. @@ -5155,6 +5147,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** + * Sets the extra top inset for the full shade transition. This is needed to compensate for + * media transitioning to quick settings + */ + public void setExtraTopInsetForFullShadeTransition(float inset) { + mExtraTopInsetForFullShadeTransition = inset; + updateStackPosition(); + requestChildrenUpdate(); + } + + /** * A listener that is notified when the empty space below the notifications is clicked on */ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -5526,108 +5528,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) { - mKeyguardMediaControllorVisible = keyguardMediaControllorVisible; - } - void resetCheckSnoozeLeavebehind() { setCheckForLeaveBehind(true); } - // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ - - @ShadeViewRefactor(RefactorComponent.INPUT) - private final DragDownCallback mDragDownCallback = new DragDownCallback() { - - @Override - public boolean canDragDown() { - return mStatusBarState == StatusBarState.KEYGUARD - && (mController.hasActiveNotifications() || mKeyguardMediaControllorVisible) - || mController.isInLockedDownShade(); - } - - /* Only ever called as a consequence of a lockscreen expansion gesture. */ - @Override - public void onDraggedDown(View startingChild, int dragLengthY) { - boolean canDragDown = - mController.hasActiveNotifications() || mKeyguardMediaControllorVisible; - if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) { - mLockscreenGestureLogger.write( - MetricsEvent.ACTION_LS_SHADE, - (int) (dragLengthY / mDisplayMetrics.density), - 0 /* velocityDp - N/A */); - mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_PULL_SHADE_OPEN); - - if (!mAmbientState.isDozing() || startingChild != null) { - // We have notifications, go to locked shade. - mShadeController.goToLockedShade(startingChild); - if (startingChild instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild; - row.onExpandedByGesture(true /* drag down is always an open */); - } - } - } else if (mController.isInLockedDownShade()) { - mStatusbarStateController.setLeaveOpenOnKeyguardHide(true); - mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */, - null /* cancelRunnable */, false /* afterKeyguardGone */); - } - } - - @Override - public void onDragDownReset() { - setDimmed(true /* dimmed */, true /* animated */); - resetScrollPosition(); - resetCheckSnoozeLeavebehind(); - } - - @Override - public void onCrossedThreshold(boolean above) { - setDimmed(!above /* dimmed */, true /* animate */); - } - - @Override - public void onTouchSlopExceeded() { - cancelLongPress(); - mController.checkSnoozeLeavebehind(); - } - - @Override - public void setEmptyDragAmount(float amount) { - mNotificationPanelController.setEmptyDragAmount(amount); - } - - @Override - public boolean isFalsingCheckNeeded() { - return mStatusBarState == StatusBarState.KEYGUARD; - } - - @Override - public boolean isDragDownEnabledForView(ExpandableView view) { - if (isDragDownAnywhereEnabled()) { - return true; - } - if (mController.isInLockedDownShade()) { - if (view == null) { - // Dragging down is allowed in general - return true; - } - if (view instanceof ExpandableNotificationRow) { - // Only drag down on sensitive views, otherwise the ExpandHelper will take this - return ((ExpandableNotificationRow) view).getEntry().isSensitive(); - } - } - return false; - } - - @Override - public boolean isDragDownAnywhereEnabled() { - return mStatusbarStateController.getState() == StatusBarState.KEYGUARD - && !mKeyguardBypassEnabledProvider.getBypassEnabled(); - } - }; - - public DragDownCallback getDragDownCallback() { return mDragDownCallback; } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private final HeadsUpTouchHelper.Callback mHeadsUpCallback = new HeadsUpTouchHelper.Callback() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index f7eb574feac3..0d42428dd11d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -31,6 +31,7 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeDismissed; import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; @@ -38,6 +39,7 @@ import android.provider.Settings; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; +import android.util.MathUtils; import android.util.Pair; import android.view.Display; import android.view.LayoutInflater; @@ -59,6 +61,7 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; +import com.android.systemui.animation.Interpolators; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; @@ -70,6 +73,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEv import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -107,7 +111,6 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -168,6 +171,7 @@ public class NotificationStackScrollLayoutController { // TODO: StatusBar should be encapsulated behind a Controller private final StatusBar mStatusBar; private final SectionHeaderController mSilentHeaderController; + private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private NotificationStackScrollLayout mView; private boolean mFadeNotificationsOnDismiss; @@ -181,6 +185,9 @@ public class NotificationStackScrollLayoutController { private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener; + private int mTotalDistanceForFullShadeTransition; + private int mTotalExtraMediaInsetFullShadeTransition; + @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @@ -240,8 +247,20 @@ public class NotificationStackScrollLayoutController { public void onThemeChanged() { updateFooter(); } + + @Override + public void onConfigChanged(Configuration newConfig) { + updateResources(); + } }; + private void updateResources() { + mTotalExtraMediaInsetFullShadeTransition = mResources.getDimensionPixelSize( + R.dimen.lockscreen_shade_transition_extra_media_inset); + mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize( + R.dimen.lockscreen_shade_qs_transition_distance); + } + private final StatusBarStateController.StateListener mStateListener = new StatusBarStateController.StateListener() { @Override @@ -571,6 +590,7 @@ public class NotificationStackScrollLayoutController { NotifPipeline notifPipeline, NotifCollection notifCollection, NotificationEntryManager notificationEntryManager, + LockscreenShadeTransitionController lockscreenShadeTransitionController, IStatusBarService iStatusBarService, UiEventLogger uiEventLogger, ForegroundServiceDismissalFeatureController fgFeatureController, @@ -592,6 +612,7 @@ public class NotificationStackScrollLayoutController { mZenModeController = zenModeController; mLockscreenUserManager = lockscreenUserManager; mMetricsLogger = metricsLogger; + mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mResources = resources; @@ -624,6 +645,7 @@ public class NotificationStackScrollLayoutController { mRemoteInputManager = remoteInputManager; mVisualStabilityManager = visualStabilityManager; mShadeController = shadeController; + updateResources(); } public void attach(NotificationStackScrollLayout view) { @@ -676,6 +698,8 @@ public class NotificationStackScrollLayoutController { mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming); + mLockscreenShadeTransitionController.setStackScroller(this); + mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); mFadeNotificationsOnDismiss = // TODO: this should probably be injected directly @@ -705,7 +729,6 @@ public class NotificationStackScrollLayoutController { Settings.Secure.NOTIFICATION_HISTORY_ENABLED); mKeyguardMediaController.setVisibilityChangedListener(visible -> { - mView.setKeyguardMediaControllorVisible(visible); if (visible) { mView.generateAddAnimation( mKeyguardMediaController.getSinglePaneContainer(), @@ -1203,11 +1226,6 @@ public class NotificationStackScrollLayoutController { mView.runAfterAnimationFinished(r); } - public void setNotificationPanelController( - NotificationPanelViewController notificationPanelViewController) { - mView.setNotificationPanelController(notificationPanelViewController); - } - public void setShelfController(NotificationShelfController notificationShelfController) { mView.setShelfController(notificationShelfController); } @@ -1275,7 +1293,10 @@ public class NotificationStackScrollLayoutController { NotificationLogger.getNotificationLocation(entry))); } - boolean hasActiveNotifications() { + /** + * @return if the shade has currently any active notifications. + */ + public boolean hasActiveNotifications() { if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { return !mNotifPipeline.getShadeList().isEmpty(); } else { @@ -1354,11 +1375,60 @@ public class NotificationStackScrollLayoutController { } } + /** + * @return the expand helper callback. + */ + public ExpandHelper.Callback getExpandHelperCallback() { + return mView.getExpandHelperCallback(); + } + + /** + * @return If the shade is in the locked down shade. + */ public boolean isInLockedDownShade() { return mDynamicPrivacyController.isInLockedDownShade(); } /** + * Set the dimmed state for all of the notification views. + */ + public void setDimmed(boolean dimmed, boolean animate) { + mView.setDimmed(dimmed, animate); + } + + /** + * @return the inset during the full shade transition, that needs to be added to the position + * of the quick settings edge. This is relevant for media, that is transitioning + * from the keyguard host to the quick settings one. + */ + public int getFullShadeTransitionInset() { + MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer(); + if (view == null || view.getHeight() == 0 + || mStatusBarStateController.getState() != KEYGUARD) { + return 0; + } + return view.getHeight() + mView.getPaddingAfterMedia(); + } + + /** + * Set the amount of pixels we have currently dragged down if we're transitioning to the full + * shade. 0.0f means we're not transitioning yet. + */ + public void setTransitionToFullShadeAmount(float amount) { + float extraTopInset; + MediaHeaderView view = mKeyguardMediaController.getSinglePaneContainer(); + if (view == null || view.getHeight() == 0 + || mStatusBarStateController.getState() != KEYGUARD) { + extraTopInset = 0; + } else { + extraTopInset = MathUtils.saturate(amount / mTotalDistanceForFullShadeTransition); + extraTopInset = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(extraTopInset); + extraTopInset = extraTopInset * mTotalExtraMediaInsetFullShadeTransition; + } + mView.setExtraTopInsetForFullShadeTransition(extraTopInset); + } + + /** * Enum for UiEvent logged from this class */ enum NotificationPanelEvent implements UiEventLogger.UiEventEnum { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index c64b893970c6..ae6a1e52574d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -251,7 +251,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL super.onFinishInflate(); mPreviewInflater = new PreviewInflater(mContext, new LockPatternUtils(mContext), new ActivityIntentHelper(mContext)); - mPreviewContainer = findViewById(R.id.preview_container); mOverlayContainer = findViewById(R.id.overlay_container); mRightAffordanceView = findViewById(R.id.camera_button); mLeftAffordanceView = findViewById(R.id.left_button); @@ -268,7 +267,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mKeyguardStateController.addCallback(this); setClipChildren(false); setClipToPadding(false); - inflateCameraPreview(); mRightAffordanceView.setOnClickListener(this); mLeftAffordanceView.setOnClickListener(this); initAccessibility(); @@ -276,13 +274,21 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mFlashlightController = Dependency.get(FlashlightController.class); mAccessibilityController = Dependency.get(AccessibilityController.class); mActivityIntentHelper = new ActivityIntentHelper(getContext()); - updateLeftAffordance(); mIndicationPadding = getResources().getDimensionPixelSize( R.dimen.keyguard_indication_area_padding); updateWalletVisibility(); } + /** + * Set the container where the previews are rendered. + */ + public void setPreviewContainer(ViewGroup previewContainer) { + mPreviewContainer = previewContainer; + inflateCameraPreview(); + updateLeftAffordance(); + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -680,6 +686,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void inflateCameraPreview() { + if (mPreviewContainer == null) { + return; + } View previewBefore = mCameraPreview; boolean visibleBefore = false; if (previewBefore != null) { @@ -697,6 +706,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL } private void updateLeftPreview() { + if (mPreviewContainer == null) { + return; + } View previewBefore = mLeftPreview; if (previewBefore != null) { mPreviewContainer.removeView(previewBefore); 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 069c19770aef..f4710f49524d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -140,7 +140,7 @@ public class KeyguardClockPositionAlgorithm { */ private float mQsExpansion; - private float mEmptyDragAmount; + private float mOverStretchAmount; /** * Setting if bypass is enabled. If true the clock should always be positioned like it's dark @@ -181,7 +181,7 @@ public class KeyguardClockPositionAlgorithm { int notificationStackHeight, float panelExpansion, int parentHeight, int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY, int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark, - float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, + float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset, boolean isSplitShade) { mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding, userSwitchHeight); @@ -196,7 +196,7 @@ public class KeyguardClockPositionAlgorithm { mHasCustomClock = hasCustomClock; mHasVisibleNotifs = hasVisibleNotifs; mDarkAmount = dark; - mEmptyDragAmount = emptyDragAmount; + mOverStretchAmount = overStrechAmount; mBypassEnabled = bypassEnabled; mUnlockedStackScrollerPadding = unlockedStackScrollerPadding; mQsExpansion = qsExpansion; @@ -301,7 +301,7 @@ public class KeyguardClockPositionAlgorithm { } clockYDark = clockY + burnInPreventionOffsetY() + shift; } - return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount); + return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mOverStretchAmount); } private int getUserSwitcherY(float panelExpansion) { @@ -312,7 +312,7 @@ public class KeyguardClockPositionAlgorithm { float shadeExpansion = Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(panelExpansion); float userSwitchY = MathUtils.lerp(userSwitchYBouncer, userSwitchYRegular, shadeExpansion); - return (int) (userSwitchY + mEmptyDragAmount); + return (int) (userSwitchY + mOverStretchAmount); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 12dec14dabd0..089277070658 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -114,6 +114,7 @@ import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShelfController; @@ -207,7 +208,6 @@ public class NotificationPanelViewController extends PanelViewController { private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); @VisibleForTesting final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); - private final ExpansionCallback mExpansionCallback = new ExpansionCallback(); private final BiometricUnlockController mBiometricUnlockController; private final NotificationPanelView mView; private final VibratorHelper mVibratorHelper; @@ -312,10 +312,12 @@ public class NotificationPanelViewController extends PanelViewController { // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card. // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications private final int mMaxKeyguardNotifications; + private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private boolean mShouldUseSplitNotificationShade; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; + private ViewGroup mPreviewContainer; private KeyguardAffordanceHelper mAffordanceHelper; private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; private KeyguardUserSwitcherController mKeyguardUserSwitcherController; @@ -365,7 +367,7 @@ public class NotificationPanelViewController extends PanelViewController { private int mStatusBarMinHeight; private int mStatusBarHeaderHeightKeyguard; private int mNotificationsHeaderCollideDistance; - private float mEmptyDragAmount; + private float mOverStretchAmount; private float mDownX; private float mDownY; private int mDisplayCutoutTopInset = 0; // in pixels @@ -481,7 +483,6 @@ public class NotificationPanelViewController extends PanelViewController { private final CommandQueue mCommandQueue; private final NotificationLockscreenUserManager mLockscreenUserManager; private final UserManager mUserManager; - private final ShadeController mShadeController; private final MediaDataManager mMediaDataManager; private NotificationShadeDepthController mDepthController; private int mDisplayId; @@ -505,6 +506,39 @@ public class NotificationPanelViewController extends PanelViewController { private float mSectionPadding; /** + * The amount of progress we are currently in if we're transitioning to the full shade. + * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full + * shade. This value can also go beyond 1.1 when we're overshooting! + */ + private float mTransitioningToFullShadeProgress; + + /** + * Position of the qs bottom during the full shade transition. This is needed as the toppadding + * can change during state changes, which makes it much harder to do animations + */ + private int mTransitionToFullShadeQSPosition; + + /** + * Distance that the full shade transition takes in order for qs to fully transition to the + * shade. + */ + private int mDistanceForQSFullShadeTransition; + + /** + * The maximum overshoot allowed for the top padding for the full shade transition + */ + private int mMaxOverscrollAmountForDragDown; + + /** + * Should we animate the next bounds update + */ + private boolean mAnimateNextNotificationBounds; + /** + * The delay for the next bounds animation + */ + private long mNotificationBoundsAnimationDelay; + + /** * Is this a collapse that started on the panel where we should allow the panel to intercept */ private boolean mIsPanelCollapseOnQQS; @@ -521,6 +555,16 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mDelayShowingKeyguardStatusBar; private boolean mAnimatingQS; + + /** + * The end bounds of a clipping animation. + */ + private final Rect mQsClippingAnimationEndBounds = new Rect(); + + /** + * The animator for the qs clipping bounds. + */ + private ValueAnimator mQsClippingAnimation = null; private final Rect mKeyguardStatusAreaClipBounds = new Rect(); private int mOldLayoutDirection; private NotificationShelfController mNotificationShelfController; @@ -569,7 +613,7 @@ public class NotificationPanelViewController extends PanelViewController { NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, - FalsingCollector falsingCollector, ShadeController shadeController, + FalsingCollector falsingCollector, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, @@ -591,6 +635,7 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, + LockscreenShadeTransitionController lockscreenShadeTransitionController, QSDetailDisplayer qsDetailDisplayer, NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, @@ -677,6 +722,8 @@ public class NotificationPanelViewController extends PanelViewController { } } }; + mLockscreenShadeTransitionController = lockscreenShadeTransitionController; + lockscreenShadeTransitionController.setNotificationPanelController(this); mKeyguardStateController.addCallback(keyguardMonitorCallback); DynamicPrivacyControlListener dynamicPrivacyControlListener = @@ -690,7 +737,6 @@ public class NotificationPanelViewController extends PanelViewController { }); mBottomAreaShadeAlphaAnimator.setDuration(160); mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); - mShadeController = shadeController; mLockscreenUserManager = notificationLockscreenUserManager; mEntryManager = notificationEntryManager; mConversationNotificationManager = conversationNotificationManager; @@ -749,14 +795,22 @@ public class NotificationPanelViewController extends PanelViewController { mOnEmptySpaceClickListener); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); + mPreviewContainer = mView.findViewById(R.id.preview_container); + mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); mLastOrientation = mResources.getConfiguration().orientation; initBottomArea(); mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController); mQsFrame = mView.findViewById(R.id.qs_frame); - mPulseExpansionHandler.setUp( - mNotificationStackScrollLayoutController, mExpansionCallback, mShadeController); + mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController, + amount -> { + float progress = amount / mView.getHeight(); + float overstretch = Interpolators.getOvershootInterpolation(progress, + (float) mMaxOverscrollAmountForDragDown / mView.getHeight(), + 0.2f); + setOverStrechAmount(overstretch); + }); mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { @Override public void onFullyHiddenChanged(boolean isFullyHidden) { @@ -812,6 +866,10 @@ public class NotificationPanelViewController extends PanelViewController { com.android.internal.R.dimen.status_bar_height); mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize( R.dimen.heads_up_status_bar_padding); + mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize( + R.dimen.lockscreen_shade_qs_transition_distance); + mMaxOverscrollAmountForDragDown = mResources.getDimensionPixelSize( + R.dimen.lockscreen_shade_max_top_overshoot); mScrimCornerRadius = mResources.getDimensionPixelSize( R.dimen.notification_scrim_corner_radius); mScreenCornerRadius = mResources.getDimensionPixelSize( @@ -989,6 +1047,7 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate( R.layout.keyguard_bottom_area, mView, false); mKeyguardBottomArea.initFrom(oldBottomArea); + mKeyguardBottomArea.setPreviewContainer(mPreviewContainer); mView.addView(mKeyguardBottomArea, index); initBottomArea(); mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); @@ -1108,58 +1167,28 @@ public class NotificationPanelViewController extends PanelViewController { * showing. */ private void positionClockAndNotifications() { + positionClockAndNotifications(false /* forceUpdate */); + } + + /** + * Positions the clock and notifications dynamically depending on how many notifications are + * showing. + * + * @param forceClockUpdate Should the clock be updated even when not on keyguard + */ + private void positionClockAndNotifications(boolean forceClockUpdate) { boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); - boolean animateClock = animate || mAnimateNextPositionUpdate; int stackScrollerPadding; - if (mBarState != KEYGUARD) { + boolean onKeyguard = isOnKeyguard(); + if (onKeyguard || forceClockUpdate) { + updateClockAppearance(); + } + if (!onKeyguard) { stackScrollerPadding = getUnlockedStackScrollerPadding(); } else { - int totalHeight = mView.getHeight(); - int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); - int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); - int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; - boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); - final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController - .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); - mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); - int userIconHeight = mKeyguardQsUserSwitchController != null - ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0; - mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard, - totalHeight - bottomPadding, - mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), - getExpandedFraction(), - totalHeight, - mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1 - ? mKeyguardStatusViewController.getHeight() - : (int) (mKeyguardStatusViewController.getHeight() - - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), - userIconHeight, - clockPreferredY, userSwitcherPreferredY, hasCustomClock(), - hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, - bypassEnabled, getUnlockedStackScrollerPadding(), - getQsExpansionFraction(), - mDisplayCutoutTopInset, - shouldUseSplitNotificationShade(mFeatureFlags, mResources)); - mClockPositionAlgorithm.run(mClockPositionResult); - mKeyguardStatusViewController.updatePosition( - mClockPositionResult.clockX, mClockPositionResult.clockY, - mClockPositionResult.clockScale, animateClock); - if (mKeyguardQsUserSwitchController != null) { - mKeyguardQsUserSwitchController.updatePosition( - mClockPositionResult.clockX, - mClockPositionResult.userSwitchY, - animateClock); - } - if (mKeyguardUserSwitcherController != null) { - mKeyguardUserSwitcherController.updatePosition( - mClockPositionResult.clockX, - mClockPositionResult.userSwitchY, - animateClock); - } - updateNotificationTranslucency(); - updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; } + mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding); mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX); @@ -1169,6 +1198,60 @@ public class NotificationPanelViewController extends PanelViewController { mAnimateNextPositionUpdate = false; } + private void updateClockAppearance() { + int totalHeight = mView.getHeight(); + int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); + int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); + int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard; + boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); + final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController + .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia(); + mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); + int userIconHeight = mKeyguardQsUserSwitchController != null + ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0; + float expandedFraction = + mKeyguardStatusViewController.isAnimatingScreenOffFromUnlocked() ? 1.0f + : getExpandedFraction(); + float darkamount = mKeyguardStatusViewController.isAnimatingScreenOffFromUnlocked() ? 1.0f + : mInterpolatedDarkAmount; + mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard, + totalHeight - bottomPadding, + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), + expandedFraction, + totalHeight, + mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1 + ? mKeyguardStatusViewController.getHeight() + : (int) (mKeyguardStatusViewController.getHeight() + - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), + userIconHeight, + clockPreferredY, userSwitcherPreferredY, hasCustomClock(), + hasVisibleNotifications, darkamount, mOverStretchAmount, + bypassEnabled, getUnlockedStackScrollerPadding(), + computeQsExpansionFraction(), + mDisplayCutoutTopInset, + shouldUseSplitNotificationShade(mFeatureFlags, mResources)); + mClockPositionAlgorithm.run(mClockPositionResult); + boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending(); + boolean animateClock = animate || mAnimateNextPositionUpdate; + mKeyguardStatusViewController.updatePosition( + mClockPositionResult.clockX, mClockPositionResult.clockY, + mClockPositionResult.clockScale, animateClock); + if (mKeyguardQsUserSwitchController != null) { + mKeyguardQsUserSwitchController.updatePosition( + mClockPositionResult.clockX, + mClockPositionResult.userSwitchY, + animateClock); + } + if (mKeyguardUserSwitcherController != null) { + mKeyguardUserSwitcherController.updatePosition( + mClockPositionResult.clockX, + mClockPositionResult.userSwitchY, + animateClock); + } + updateNotificationTranslucency(); + updateClock(); + } + /** * @return the padding of the stackscroller when unlocked */ @@ -1600,7 +1683,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean flingExpandsQs(float vel) { if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - return getQsExpansionFraction() > 0.5f; + return computeQsExpansionFraction() > 0.5f; } else { return vel > 0; } @@ -1613,7 +1696,7 @@ public class NotificationPanelViewController extends PanelViewController { return !mQsTouchAboveFalsingThreshold; } - private float getQsExpansionFraction() { + private float computeQsExpansionFraction() { return Math.min( 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight - mQsMinExpansionHeight)); @@ -1838,7 +1921,7 @@ public class NotificationPanelViewController extends PanelViewController { mQsTracking = false; mTrackingPointer = -1; trackMovement(event); - float fraction = getQsExpansionFraction(); + float fraction = computeQsExpansionFraction(); if (fraction != 0f || y >= mInitialTouchY) { flingQsWithCurrentVelocity(y, event.getActionMasked() == MotionEvent.ACTION_CANCEL); @@ -2044,18 +2127,19 @@ public class NotificationPanelViewController extends PanelViewController { protected void updateQsExpansion() { if (mQs == null) return; - float qsExpansionFraction = getQsExpansionFraction(); + float qsExpansionFraction = computeQsExpansionFraction(); mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction); mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY); + setQSClippingBounds(); mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction); mDepthController.setQsPanelExpansion(qsExpansionFraction); } private Runnable mOnStackYChanged = () -> { if (mQs != null) { - setNotificationBounds(); + setQSClippingBounds(); } }; @@ -2063,57 +2147,121 @@ public class NotificationPanelViewController extends PanelViewController { * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade * and QS state. */ - private void setNotificationBounds() { + private void setQSClippingBounds() { int top = 0; int bottom = 0; int left = 0; int right = 0; - final int qsPanelBottomY = calculateQsBottomPosition(getQsExpansionFraction()); - final boolean visible = (getQsExpansionFraction() > 0 || qsPanelBottomY > 0) + final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction()); + final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0) && !mShouldUseSplitNotificationShade; - final float notificationTop = mAmbientState.getStackY() - mAmbientState.getScrollY(); setQsExpansionEnabled(mAmbientState.getScrollY() == 0); - int radius = mScrimCornerRadius; if (!mShouldUseSplitNotificationShade) { - top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop) - : notificationTop); + if (mTransitioningToFullShadeProgress > 0.0f) { + // If we're transitioning, let's use the actual value. The else case + // can be wrong during transitions when waiting for the keyguard to unlock + top = mTransitionToFullShadeQSPosition; + } else { + float notificationTop = getQSEdgePosition(); + top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop) + : notificationTop); + } bottom = getView().getBottom(); left = getView().getLeft(); right = getView().getRight(); - radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius, - Math.min(top / (float) mScrimCornerRadius, 1f)); } else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding); bottom = mNotificationStackScrollLayoutController.getHeight(); left = mNotificationStackScrollLayoutController.getLeft(); right = mNotificationStackScrollLayoutController.getRight(); } + applyQSClippingBounds(left, top, right, bottom, visible); + } - // Fancy clipping for quick settings - if (mQs != null) { - mQs.setFancyClipping(top, bottom, radius, visible); + private void applyQSClippingBounds(int left, int top, int right, int bottom, + boolean visible) { + if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) { + if (mQsClippingAnimation != null) { + // update the end position of the animator + mQsClippingAnimationEndBounds.set(left, top, right, bottom); + } else { + applyQSClippingImmediately(left, top, right, bottom, visible); + } + } else { + mQsClippingAnimationEndBounds.set(left, top, right, bottom); + final int startLeft = mKeyguardStatusAreaClipBounds.left; + final int startTop = mKeyguardStatusAreaClipBounds.top; + final int startRight = mKeyguardStatusAreaClipBounds.right; + final int startBottom = mKeyguardStatusAreaClipBounds.bottom; + mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f); + mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mQsClippingAnimation.setDuration( + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE); + mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay); + mQsClippingAnimation.addUpdateListener(animation -> { + float fraction = animation.getAnimatedFraction(); + int animLeft = (int) MathUtils.lerp(startLeft, + mQsClippingAnimationEndBounds.left, fraction); + int animTop = (int) MathUtils.lerp(startTop, + mQsClippingAnimationEndBounds.top, fraction); + int animRight = (int) MathUtils.lerp(startRight, + mQsClippingAnimationEndBounds.right, fraction); + int animBottom = (int) MathUtils.lerp(startBottom, + mQsClippingAnimationEndBounds.bottom, fraction); + applyQSClippingImmediately(animLeft, animTop, animRight, animBottom, + visible /* visible */); + }); + mQsClippingAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mQsClippingAnimation = null; + } + }); + mQsClippingAnimation.start(); } + mAnimateNextNotificationBounds = false; + mNotificationBoundsAnimationDelay = 0; + } + + private void applyQSClippingImmediately(int left, int top, int right, int bottom, + boolean visible) { + // Fancy clipping for quick settings + int radius = mScrimCornerRadius; if (!mShouldUseSplitNotificationShade) { // The padding on this area is large enough that we can use a cheaper clipping strategy mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); mKeyguardStatusViewController.setClipBounds(visible ? mKeyguardStatusAreaClipBounds : null); + radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius, + Math.min(top / (float) mScrimCornerRadius, 1f)); + } + if (mQs != null) { + mQs.setFancyClipping(top, bottom, radius, visible); } mScrimController.setNotificationsBounds(left, top, right, bottom); mScrimController.setScrimCornerRadius(radius); } + private float getQSEdgePosition() { + // TODO: replace StackY with unified calculation + return mAmbientState.getStackY() - mAmbientState.getScrollY(); + } + private int calculateQsBottomPosition(float qsExpansionFraction) { - int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight(); - if (qsExpansionFraction != 0.0) { - qsBottomY = (int) MathUtils.lerp( - qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction); + if (mTransitioningToFullShadeProgress > 0.0f) { + return mTransitionToFullShadeQSPosition; + } else { + int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight(); + if (qsExpansionFraction != 0.0) { + qsBottomY = (int) MathUtils.lerp( + qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction); + } + // to account for shade overshooting animation, see setSectionPadding method + if (mSectionPadding > 0) qsBottomY += mSectionPadding; + return qsBottomY; } - // to account for shade overshooting animation, see setSectionPadding method - if (mSectionPadding > 0) qsBottomY += mSectionPadding; - return qsBottomY; } private String determineAccessibilityPaneTitle() { @@ -2157,7 +2305,7 @@ public class NotificationPanelViewController extends PanelViewController { // from a scrolled quick settings. return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(), (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding), - getQsExpansionFraction()); + computeQsExpansionFraction()); } else { return mQsExpansionHeight + mQsNotificationTopPadding; } @@ -2194,15 +2342,65 @@ public class NotificationPanelViewController extends PanelViewController { } } - private void updateQSPulseExpansion() { if (mQs != null) { - mQs.setShowCollapsedOnKeyguard( + mQs.setPulseExpanding( mKeyguardShowing && mKeyguardBypassController.getBypassEnabled() && mNotificationStackScrollLayoutController.isPulseExpanding()); } } + /** + * Set the amount of pixels we have currently dragged down if we're transitioning to the full + * shade. 0.0f means we're not transitioning yet. + */ + public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) { + mAnimateNextNotificationBounds = animate && !mShouldUseSplitNotificationShade; + mNotificationBoundsAnimationDelay = delay; + float progress = MathUtils.saturate(pxAmount / mView.getHeight()); + + float endPosition = 0; + if (pxAmount > 0.0f) { + if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0 + && !mMediaDataManager.hasActiveMedia()) { + // No notifications are visible, let's animate to the height of qs instead + if (mQs != null) { + // Let's interpolate to the header height + endPosition = mQs.getHeader().getHeight(); + } + } else { + // Interpolating to the new bottom edge position! + endPosition = getQSEdgePosition() - mOverStretchAmount; + + // If we have media, we need to put the boundary below it, as the media header + // still uses the space during the transition. + endPosition += + mNotificationStackScrollLayoutController.getFullShadeTransitionInset(); + } + } + + // Calculate the overshoot amount such that we're reaching the target after our desired + // distance, but only reach it fully once we drag a full shade length. + float transitionProgress = 0; + if (endPosition != 0 && progress != 0) { + transitionProgress = Interpolators.getOvershootInterpolation(progress, + mMaxOverscrollAmountForDragDown / endPosition, + (float) mDistanceForQSFullShadeTransition / (float) mView.getHeight()); + } + mTransitioningToFullShadeProgress = transitionProgress; + + int position = (int) MathUtils.lerp((float) 0, endPosition, + mTransitioningToFullShadeProgress); + if (mTransitioningToFullShadeProgress > 0.0f) { + // we want at least 1 pixel otherwise the panel won't be clipped + position = Math.max(1, position); + } + float overStretchAmount = Math.max(position - endPosition, 0.0f); + setOverStrechAmount(overStretchAmount); + mTransitionToFullShadeQSPosition = position; + updateQsExpansion(); + } + private void trackMovement(MotionEvent event) { if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event); } @@ -2626,7 +2824,7 @@ public class NotificationPanelViewController extends PanelViewController { if (!mKeyguardShowing) { return; } - float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2); + float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2); float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion) * mKeyguardStatusBarAnimateAlpha; newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount; @@ -2649,7 +2847,7 @@ public class NotificationPanelViewController extends PanelViewController { float expansionAlpha = MathUtils.map( isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction()); - float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); + float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); alpha *= mBottomAreaShadeAlpha; mKeyguardBottomArea.setAffordanceAlpha(alpha); mKeyguardBottomArea.setImportantForAccessibility( @@ -2671,7 +2869,7 @@ public class NotificationPanelViewController extends PanelViewController { float expansionAlpha = MathUtils.map( isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction()); - float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction()); + float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); mBigClockContainer.setAlpha(alpha); } @@ -3223,6 +3421,7 @@ public class NotificationPanelViewController extends PanelViewController { mHeightListener.onQsHeightChanged(); } }); + mLockscreenShadeTransitionController.setQS(mQs); mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView()); updateQsExpansion(); } @@ -3460,9 +3659,9 @@ public class NotificationPanelViewController extends PanelViewController { StatusBar statusBar, NotificationShelfController notificationShelfController) { setStatusBar(statusBar); - mNotificationStackScrollLayoutController.setNotificationPanelController(this); mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); mNotificationShelfController = notificationShelfController; + mLockscreenShadeTransitionController.bindController(notificationShelfController); updateMaxDisplayedNotifications(true); } @@ -3513,10 +3712,6 @@ public class NotificationPanelViewController extends PanelViewController { return new OnLayoutChangeListener(); } - public void setEmptyDragAmount(float amount) { - mExpansionCallback.setEmptyDragAmount(amount); - } - @Override protected TouchHandler createTouchHandler() { return new TouchHandler() { @@ -3985,7 +4180,7 @@ public class NotificationPanelViewController extends PanelViewController { mClockPositionResult.clockX, mClockPositionResult.clockYFullyDozing, mClockPositionResult.clockScale, - false); + false /* animate */); } mKeyguardStatusViewController.setKeyguardStatusViewVisibility( @@ -4001,11 +4196,7 @@ public class NotificationPanelViewController extends PanelViewController { if (oldState == KEYGUARD && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) { animateKeyguardStatusBarOut(); - long - delay = - mBarState == StatusBarState.SHADE_LOCKED ? 0 - : mKeyguardStateController.calculateGoingToFullShadeDelay(); - mQs.animateHeaderSlidingIn(delay); + updateQSMinHeight(); } else if (oldState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) { animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD); @@ -4052,11 +4243,12 @@ public class NotificationPanelViewController extends PanelViewController { } } - private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback { - public void setEmptyDragAmount(float amount) { - mEmptyDragAmount = amount * 0.2f; - positionClockAndNotifications(); - } + /** + * Sets the overstretch amount in raw pixels when dragging down. + */ + public void setOverStrechAmount(float amount) { + mOverStretchAmount = amount; + positionClockAndNotifications(true /* forceUpdate */); } private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener { @@ -4103,11 +4295,7 @@ public class NotificationPanelViewController extends PanelViewController { // Calculate quick setting heights. int oldMaxHeight = mQsMaxExpansionHeight; if (mQs != null) { - float previousMin = mQsMinExpansionHeight; - mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); - if (mQsExpansionHeight == previousMin) { - mQsExpansionHeight = mQsMinExpansionHeight; - } + updateQSMinHeight(); mQsMaxExpansionHeight = mQs.getDesiredHeight(); mNotificationStackScrollLayoutController.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); @@ -4149,6 +4337,14 @@ public class NotificationPanelViewController extends PanelViewController { } } + private void updateQSMinHeight() { + float previousMin = mQsMinExpansionHeight; + mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); + if (mQsExpansionHeight == previousMin) { + mQsExpansionHeight = mQsMinExpansionHeight; + } + } + private class DebugDrawable extends Drawable { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index f19cf1bed034..7f4dabd3f59f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -34,15 +34,14 @@ import android.view.View; import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeLog; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -73,7 +72,6 @@ public class NotificationShadeWindowViewController { private final DynamicPrivacyController mDynamicPrivacyController; private final KeyguardBypassController mBypassController; private final PluginManager mPluginManager; - private final FalsingManager mFalsingManager; private final FalsingCollector mFalsingCollector; private final TunerService mTunerService; private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; @@ -87,6 +85,7 @@ public class NotificationShadeWindowViewController { private final ShadeController mShadeController; private final NotificationShadeDepthController mDepthController; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; + private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private GestureDetector mGestureDetector; @@ -119,7 +118,7 @@ public class NotificationShadeWindowViewController { PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, - FalsingManager falsingManager, + LockscreenShadeTransitionController transitionController, FalsingCollector falsingCollector, PluginManager pluginManager, TunerService tunerService, @@ -143,7 +142,7 @@ public class NotificationShadeWindowViewController { mPulseExpansionHandler = pulseExpansionHandler; mDynamicPrivacyController = dynamicPrivacyController; mBypassController = bypassController; - mFalsingManager = falsingManager; + mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mPluginManager = pluginManager; mTunerService = tunerService; @@ -406,11 +405,7 @@ public class NotificationShadeWindowViewController { } }); - ExpandHelper.Callback expandHelperCallback = mStackScrollLayout.getExpandHelperCallback(); - DragDownHelper.DragDownCallback dragDownCallback = mStackScrollLayout.getDragDownCallback(); - setDragDownHelper( - new DragDownHelper(mFalsingManager, expandHelperCallback, dragDownCallback, - mFalsingCollector, mView, mView.getContext())); + setDragDownHelper(mLockscreenShadeTransitionController.getTouchHelper()); mDepthController.setRoot(mView); mNotificationPanelViewController.addExpansionListener(mDepthController); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index c34fa2f049e1..bbde3c3e3144 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -28,6 +28,7 @@ import android.os.Handler; import android.os.Trace; import android.util.Log; import android.util.MathUtils; +import android.util.Pair; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; @@ -42,10 +43,10 @@ import com.android.internal.util.function.TriConsumer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; -import com.android.systemui.animation.Interpolators; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; @@ -98,6 +99,18 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump public static final int OPAQUE = 2; private boolean mClipsQsScrim; + /** + * The amount of progress we are currently in if we're transitioning to the full shade. + * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full + * shade. + */ + private float mTransitionToFullShadeProgress; + + /** + * If we're currently transitioning to the full shade. + */ + private boolean mTransitioningToFullShade; + @IntDef(prefix = {"VISIBILITY_"}, value = { TRANSPARENT, SEMI_TRANSPARENT, @@ -357,7 +370,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " + mNotificationsAlpha); } - applyExpansionToAlpha(); + applyStateToAlpha(); // Scrim might acquire focus when user is navigating with a D-pad or a keyboard. // We need to disable focus otherwise AOD would end up with a gray overlay. @@ -499,10 +512,37 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (!(relevantState && mExpansionAffectsAlpha)) { return; } - applyAndDispatchExpansion(); + applyAndDispatchState(); + } + } + + /** + * Set the amount of progress we are currently in if we're transitioning to the full shade. + * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full + * shade. + */ + public void setTransitionToFullShadeProgress(float progress) { + if (progress != mTransitionToFullShadeProgress) { + mTransitionToFullShadeProgress = progress; + setTransitionToFullShade(progress > 0.0f); + applyAndDispatchState(); + } + } + + /** + * Set if we're currently transitioning to the full shade + */ + private void setTransitionToFullShade(boolean transitioning) { + if (transitioning != mTransitioningToFullShade) { + mTransitioningToFullShade = transitioning; + if (transitioning) { + // Let's make sure the shade locked is ready + ScrimState.SHADE_LOCKED.prepare(mState); + } } } + /** * Set bounds for notifications background, all coordinates are absolute */ @@ -534,7 +574,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (!(relevantState && mExpansionAffectsAlpha)) { return; } - applyAndDispatchExpansion(); + applyAndDispatchState(); } } @@ -553,6 +593,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mScrimBehind != null) { mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim); } + if (mState != ScrimState.UNINITIALIZED) { + // the clipScrimState has changed, let's reprepare ourselves + mState.prepare(mState); + applyAndDispatchState(); + } } @VisibleForTesting @@ -583,7 +628,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - private void applyExpansionToAlpha() { + private void applyStateToAlpha() { if (!mExpansionAffectsAlpha) { return; } @@ -608,47 +653,40 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mInFrontAlpha = 0; } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED || mState == ScrimState.PULSING) { - // Either darken of make the scrim transparent when you - // pull down the shade - float interpolatedFract = getInterpolatedFraction(); - float stateBehind = mClipsQsScrim ? mState.getNotifAlpha() : mState.getBehindAlpha(); - float backAlpha; - if (mDarkenWhileDragging) { - backAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind, - interpolatedFract); - } else { - backAlpha = MathUtils.lerp(0 /* start */, stateBehind, - interpolatedFract); + Pair<Integer, Float> result = calculateBackStateForState(mState); + int behindTint = result.first; + float behindAlpha = result.second; + if (mTransitionToFullShadeProgress > 0.0f) { + Pair<Integer, Float> shadeResult = calculateBackStateForState( + ScrimState.SHADE_LOCKED); + behindAlpha = MathUtils.lerp(behindAlpha, shadeResult.second, + mTransitionToFullShadeProgress); + behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first, + mTransitionToFullShadeProgress); } mInFrontAlpha = mState.getFrontAlpha(); - int backTint; if (mClipsQsScrim) { - backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), - mState.getNotifTint(), interpolatedFract); - } else { - backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), - mState.getBehindTint(), interpolatedFract); - } - if (mQsExpansion > 0) { - backAlpha = MathUtils.lerp(backAlpha, mDefaultScrimAlpha, mQsExpansion); - int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint() - : ScrimState.SHADE_LOCKED.getBehindTint(); - backTint = ColorUtils.blendARGB(backTint, stateTint, mQsExpansion); - } - if (mClipsQsScrim) { - mNotificationsAlpha = backAlpha; - mNotificationsTint = backTint; + mNotificationsAlpha = behindAlpha; + mNotificationsTint = behindTint; mBehindAlpha = 1; mBehindTint = Color.BLACK; } else { - mBehindAlpha = backAlpha; + mBehindAlpha = behindAlpha; if (mState == ScrimState.SHADE_LOCKED) { // going from KEYGUARD to SHADE_LOCKED state mNotificationsAlpha = getInterpolatedFraction(); } else { mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); } - mBehindTint = backTint; + if (mState == ScrimState.KEYGUARD && mTransitionToFullShadeProgress > 0.0f) { + // Interpolate the notification alpha when transitioning! + mNotificationsAlpha = MathUtils.lerp( + mNotificationsAlpha, + getInterpolatedFraction(), + mTransitionToFullShadeProgress); + } + mNotificationsTint = mState.getNotifTint(); + mBehindTint = behindTint; } } if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { @@ -658,8 +696,39 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - private void applyAndDispatchExpansion() { - applyExpansionToAlpha(); + private Pair<Integer, Float> calculateBackStateForState(ScrimState state) { + // Either darken of make the scrim transparent when you + // pull down the shade + float interpolatedFract = getInterpolatedFraction(); + float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha(); + float behindAlpha; + int behindTint; + if (mDarkenWhileDragging) { + behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind, + interpolatedFract); + } else { + behindAlpha = MathUtils.lerp(0 /* start */, stateBehind, + interpolatedFract); + } + if (mClipsQsScrim) { + behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), + state.getNotifTint(), interpolatedFract); + } else { + behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), + state.getBehindTint(), interpolatedFract); + } + if (mQsExpansion > 0) { + behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion); + int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint() + : ScrimState.SHADE_LOCKED.getBehindTint(); + behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion); + } + return new Pair<>(behindTint, behindAlpha); + } + + + private void applyAndDispatchState() { + applyStateToAlpha(); if (mUpdatePending) { return; } @@ -1195,7 +1264,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump public void setExpansionAffectsAlpha(boolean expansionAffectsAlpha) { mExpansionAffectsAlpha = expansionAffectsAlpha; if (expansionAffectsAlpha) { - applyAndDispatchExpansion(); + applyAndDispatchState(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java index 2fa6795e9df0..2d41e5e9be2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java @@ -14,8 +14,6 @@ package com.android.systemui.statusbar.phone; -import android.view.View; - import com.android.systemui.statusbar.StatusBarState; /** @@ -78,15 +76,6 @@ public interface ShadeController { void runPostCollapseRunnables(); /** - * If secure with redaction: Show bouncer, go to unlocked shade. - * - * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> - * - * @param startingChild The view to expand after going to the shade. - */ - void goToLockedShade(View startingChild); - - /** * Close the shade if it was open * * @return true if the shade was open, else false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java index a930a897c2dc..d4458e29a306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import android.util.Log; -import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowManager; @@ -182,12 +181,6 @@ public class ShadeControllerImpl implements ShadeController { } @Override - public void goToLockedShade(View startingChild) { - // TODO: Move this code out of StatusBar into ShadeController. - getStatusBar().goToLockedShade(startingChild); - } - - @Override public boolean collapsePanel() { if (!getNotificationPanelViewController().isFullyCollapsed()) { // close the shade if it was open 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 ded3be43501a..ec012bfde9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -194,6 +194,7 @@ import com.android.systemui.statusbar.KeyboardShortcuts; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LiftReveal; import com.android.systemui.statusbar.LightRevealScrim; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -347,6 +348,8 @@ public class StatusBar extends SystemUI implements DemoMode, ONLY_CORE_APPS = onlyCoreApps; } + private LockscreenShadeTransitionController mLockscreenShadeTransitionController; + public interface ExpansionChangedListener { void onExpansionChanged(float expansion, boolean expanded); } @@ -438,9 +441,6 @@ public class StatusBar extends SystemUI implements DemoMode, KeyguardIndicationController mKeyguardIndicationController; - // RemoteInputView to be activated after unlock - private View mPendingRemoteInputView; - private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private View mReportRejectedTouch; @@ -650,12 +650,6 @@ public class StatusBar extends SystemUI implements DemoMode, private final ScreenLifecycle mScreenLifecycle; private final WakefulnessLifecycle mWakefulnessLifecycle; - private final View.OnClickListener mGoToLockedShadeListener = v -> { - if (mState == StatusBarState.KEYGUARD) { - wakeUpIfDozing(SystemClock.uptimeMillis(), v, "SHADE_CLICK"); - goToLockedShade(null); - } - }; private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; @@ -798,6 +792,7 @@ public class StatusBar extends SystemUI implements DemoMode, OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, + LockscreenShadeTransitionController lockscreenShadeTransitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController) { super(context); @@ -882,6 +877,8 @@ public class StatusBar extends SystemUI implements DemoMode, mAnimationScheduler = animationScheduler; mStatusBarLocationPublisher = locationPublisher; mFeatureFlags = featureFlags; + mLockscreenShadeTransitionController = lockscreenShadeTransitionController; + lockscreenShadeTransitionController.setStatusbar(this); mExpansionChangedListeners = new ArrayList<>(); @@ -1422,7 +1419,8 @@ public class StatusBar extends SystemUI implements DemoMode, mDozeScrimController, mScrimController, mNotificationShadeWindowController, mDynamicPrivacyController, mKeyguardStateController, mKeyguardIndicationController, - this /* statusBar */, mShadeController, mCommandQueue, mInitController, + this /* statusBar */, mShadeController, + mLockscreenShadeTransitionController, mCommandQueue, mInitController, mNotificationInterruptStateProvider); mNotificationShelfController.setOnActivatedListener(mPresenter); @@ -1502,7 +1500,6 @@ public class StatusBar extends SystemUI implements DemoMode, private void inflateShelf() { mNotificationShelfController = mSuperStatusBarViewFactory .getNotificationShelfController(mStackScroller); - mNotificationShelfController.setOnClickListener(mGoToLockedShadeListener); } @Override @@ -1675,7 +1672,7 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned() && (mUserSetup || mUserSwitcherController == null || !mUserSwitcherController.isSimpleUserSwitcher()) - && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0) + && !isShadeDisabled() && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) && !mDozing && !ONLY_CORE_APPS; @@ -1683,6 +1680,10 @@ public class StatusBar extends SystemUI implements DemoMode, Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled); } + public boolean isShadeDisabled() { + return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0; + } + public void addQsTile(ComponentName tile) { if (mQSPanelController != null && mQSPanelController.getHost() != null) { mQSPanelController.getHost().addTile(tile); @@ -3333,7 +3334,6 @@ public class StatusBar extends SystemUI implements DemoMode, public void showKeyguard() { mStatusBarStateController.setKeyguardRequested(true); mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); - mPendingRemoteInputView = null; updateIsKeyguard(); mAssistManagerLazy.get().onLockscreenShown(); } @@ -3392,11 +3392,6 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.setState(StatusBarState.KEYGUARD); } updatePanelExpansionForKeyguard(); - if (mDraggedDownEntry != null) { - mDraggedDownEntry.setUserLocked(false); - mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */); - mDraggedDownEntry = null; - } } private void updatePanelExpansionForKeyguard() { @@ -3524,11 +3519,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } long delay = mKeyguardStateController.calculateGoingToFullShadeDelay(); - mNotificationPanelViewController.animateToFullShade(delay); - if (mDraggedDownEntry != null) { - mDraggedDownEntry.setUserLocked(false); - mDraggedDownEntry = null; - } + mLockscreenShadeTransitionController.onHideKeyguard(delay); // Disable layout transitions in navbar for this transition because the load is just // too heavy for the CPU and GPU on any device. @@ -3719,6 +3710,22 @@ public class StatusBar extends SystemUI implements DemoMode, } } + /** + * Show the bouncer if we're currently on the keyguard or shade locked and aren't hiding. + * @param performAction the action to perform when the bouncer is dismissed. + * @param cancelAction the action to perform when unlock is aborted. + */ + public void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction, + Runnable cancelAction) { + if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) + && !mKeyguardViewMediator.isHiding()) { + mStatusBarKeyguardViewManager.dismissWithAction(performAction, cancelAction, + false /* afterKeyguardGone */); + } else if (cancelAction != null) { + cancelAction.run(); + } + } + void instantCollapseNotificationPanel() { mNotificationPanelViewController.instantCollapse(); mShadeController.runPostCollapseRunnables(); @@ -3896,47 +3903,6 @@ public class StatusBar extends SystemUI implements DemoMode, } /** - * If secure with redaction: Show bouncer, go to unlocked shade. - * - * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> - * - * @param expandView The view to expand after going to the shade. - */ - void goToLockedShade(View expandView) { - if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { - return; - } - - int userId = mLockscreenUserManager.getCurrentUserId(); - ExpandableNotificationRow row = null; - NotificationEntry entry = null; - if (expandView instanceof ExpandableNotificationRow) { - entry = ((ExpandableNotificationRow) expandView).getEntry(); - entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */); - // Indicate that the group expansion is changing at this time -- this way the group - // and children backgrounds / divider animations will look correct. - entry.setGroupExpansionChanging(true); - userId = entry.getSbn().getUserId(); - } - boolean fullShadeNeedsBouncer = !mLockscreenUserManager. - userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId()) - || !mLockscreenUserManager.shouldShowLockscreenNotifications() - || mFalsingCollector.shouldEnforceBouncer(); - if (mKeyguardBypassController.getBypassEnabled()) { - fullShadeNeedsBouncer = false; - } - if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) { - mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); - showBouncerIfKeyguard(); - mDraggedDownEntry = entry; - mPendingRemoteInputView = null; - } else { - mNotificationPanelViewController.animateToFullShade(0 /* delay */); - mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED); - } - } - - /** * Propagation of the bouncer state, indicating that it's fully visible. */ public void setBouncerShowing(boolean bouncerShowing) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 75c544d3aa4b..aa58527cb32e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -48,6 +48,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; @@ -112,6 +113,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, private final KeyguardIndicationController mKeyguardIndicationController; private final StatusBar mStatusBar; private final ShadeController mShadeController; + private final LockscreenShadeTransitionController mShadeTransitionController; private final CommandQueue mCommandQueue; private final AccessibilityManager mAccessibilityManager; @@ -138,6 +140,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, KeyguardIndicationController keyguardIndicationController, StatusBar statusBar, ShadeController shadeController, + LockscreenShadeTransitionController shadeTransitionController, CommandQueue commandQueue, InitController initController, NotificationInterruptStateProvider notificationInterruptStateProvider) { @@ -149,6 +152,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, // TODO: use KeyguardStateController#isOccluded to remove this dependency mStatusBar = statusBar; mShadeController = shadeController; + mShadeTransitionController = shadeTransitionController; mCommandQueue = commandQueue; mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); mNotificationShadeWindowController = notificationShadeWindowController; @@ -394,7 +398,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mHeadsUpManager.setExpanded(clickedEntry, nowExpanded); if (nowExpanded) { if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { - mShadeController.goToLockedShade(clickedEntry.getRow()); + mShadeTransitionController.goToLockedShade(clickedEntry.getRow()); } else if (clickedEntry.isSensitive() && mDynamicPrivacyController.isInLockedDownShade()) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index d0d2cb288aa5..9722d6841c62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -51,6 +51,7 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -213,6 +214,7 @@ public interface StatusBarPhoneModule { OngoingCallController ongoingCallController, SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, + LockscreenShadeTransitionController transitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController) { return new StatusBar( @@ -299,6 +301,7 @@ public interface StatusBarPhoneModule { ongoingCallController, animationScheduler, locationPublisher, + transitionController, featureFlags, keyguardUnlockAnimationController); } |