Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03f19e4a authored by Yining Liu's avatar Yining Liu
Browse files

Fix lockscreen notification shelf jump animation

Fix an animation issue when the last notification on lockscreen stack is
dismissed, and the shelf needs to translate up.

The height of NSSL depends on the calculation result of
NotificationStackSizeCalculator.computeMaxKeyguardNotifications(), which
is triggered by getLockscreenDisplayConfig, combination of several flows,
including notificationStackChanged.

notificationStackChanged is triggered by the height changes of NSSL, but
using debounce to filter out the updates that are triggered by continuous
height changes triggered by animations.

When the last row is dismissed on lockscreen and we need to show the
shelf only. We need to update the notification stack's height, and the
according to the latest result of max notifications, calculated by
computeMaxKeyguardNotifications. However, the debouncing prevented the
re-calculation with the animation going on, thus lead to unpredictable
update timing of the height of NotificationStackScrollLayout.

This change added a new runnable for the NSSL to trigger when it needs to
trigger the max notification re-calculation immediately, instead of
eventually, and runs it whenever a top-level notification is removed from
NSSL on lockscreen.

Test: manual
Bug: 330387368
Flag: com.android.server.notification.notification_minimalism
Change-Id: I9adecb7a6b10988f80a67b21061e6c4fe8cc5bed
parent 078a8491
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent;
@@ -274,6 +275,7 @@ public class NotificationStackScrollLayout
    private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
    private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
    private Runnable mOnHeightChangedRunnable;
    private Runnable mOnKeyguardTopLevelNotificationRemovedRunnable;
    private OnEmptySpaceClickListener mOnEmptySpaceClickListener;
    private boolean mNeedsAnimation;
    private boolean mTopPaddingNeedsAnimation;
@@ -2883,6 +2885,11 @@ public class NotificationStackScrollLayout
        // notification which becomes a child notification
        ExpandableView expandableView = (ExpandableView) child;
        if (!mChildTransferInProgress) {
            if (NotificationMinimalism.isEnabled()
                    && mAmbientState.isOnKeyguard()
                    && this.mOnKeyguardTopLevelNotificationRemovedRunnable != null) {
                mOnKeyguardTopLevelNotificationRemovedRunnable.run();
            }
            onViewRemovedInternal(expandableView, this);
        }
        mShelf.requestRoundnessResetFor(expandableView);
@@ -4552,6 +4559,10 @@ public class NotificationStackScrollLayout
        this.mOnHeightChangedRunnable = r;
    }

    void setOnKeyguardTopLevelNotificationRemovedRunnable(Runnable r) {
        this.mOnKeyguardTopLevelNotificationRemovedRunnable = r;
    }

    void onChildAnimationFinished() {
        setAnimationRunning(false);
        if (SceneContainerFlag.isEnabled()) {
+8 −0
Original line number Diff line number Diff line
@@ -1091,6 +1091,14 @@ public class NotificationStackScrollLayoutController implements Dumpable {
        mView.setOnHeightChangedRunnable(r);
    }

    /**
     * Invoked when a top-level notification(one with NSSL as parent, not group-child) is removed
     * from the lockscreen NSSL.
     */
    public void setOnKeyguardTopLevelNotificationRemovedRunnable(Runnable r) {
        mView.setOnKeyguardTopLevelNotificationRemovedRunnable(r);
    }

    public void setOverscrollTopChangedListener(
            OnOverscrollTopChangedListener listener) {
        SceneContainerFlag.assertInLegacyMode();
+35 −3
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.Lazy
import javax.inject.Inject
@@ -56,9 +57,30 @@ constructor(
    private val _topPosition = MutableStateFlow(0f)
    val topPosition = _topPosition.asStateFlow()

    private val _notificationStackChanged = MutableStateFlow(0L)
    /**
     * A flow that will be updated when the Notification Stack changes and requires updating, this
     * flow will always be debounced before using, for instant updating, use
     * [notificationStackChangedInstant] instead.
     */
    private val notificationStackChangedDebounced = MutableStateFlow(0L)

    /**
     * Same as [notificationStackChanged], but instead of debouncing, it will emit a value
     * immediately.
     */
    private val notificationStackChangedInstant = MutableStateFlow(0L)

    /** An internal modification was made to notifications */
    val notificationStackChanged = _notificationStackChanged.debounce(20L)
    val notificationStackChanged =
        if (NotificationMinimalism.isEnabled) {
            notificationStackChangedDebounced.debounce(20L).combine(
                notificationStackChangedInstant
            ) { i, j ->
                i + j
            }
        } else {
            notificationStackChangedDebounced.debounce(20L)
        }

    /* Warning: Even though the value it emits only contains the split shade status, this flow must
     * emit a value whenever the configuration *or* the split shade status changes. Adding a
@@ -117,7 +139,17 @@ constructor(
    /** An internal modification was made to notifications */
    fun notificationStackChanged() {
        logger?.notificationStackChanged()
        _notificationStackChanged.value = _notificationStackChanged.value + 1
        notificationStackChangedDebounced.value = notificationStackChangedDebounced.value + 1
    }

    /**
     * A change in notification stack requires instant update on heights. This method should only be
     * used when immediate update is required, otherwise, use [notificationStackChanged], which will
     * trigger updates eventually.
     */
    fun notificationsInStackChangedInstant() {
        if (!NotificationMinimalism.isEnabled) return
        notificationStackChangedInstant.value += 1
    }

    @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
+3 −0
Original line number Diff line number Diff line
@@ -206,6 +206,9 @@ constructor(
        }

        controller.setOnHeightChangedRunnable { viewModel.notificationStackChanged() }
        controller.setOnKeyguardTopLevelNotificationRemovedRunnable {
            viewModel.notificationStackChangedInstant()
        }
        disposables += DisposableHandle { controller.setOnHeightChangedRunnable(null) }
        disposables += view.onLayoutChanged { viewModel.notificationStackChanged() }

+19 −18
Original line number Diff line number Diff line
@@ -866,7 +866,8 @@ constructor(
                ) { isOnLockscreen, statusBarState, showAllNotifications ->
                    (statusBarState == SHADE_LOCKED || !isOnLockscreen || showAllNotifications) to
                        isOnLockscreen
            }.dumpWhileCollecting("showUnlimitedNotificationsAndIsOnLockScreen")
                }
                .dumpWhileCollecting("showUnlimitedNotificationsAndIsOnLockScreen")

        @Suppress("UNCHECKED_CAST")
        return combineTransform(
@@ -874,15 +875,15 @@ constructor(
                showUnlimitedNotificationsAndIsOnLockScreen,
                shadeInteractor.isUserInteracting.dumpWhileCollecting("isUserInteracting"),
                availableHeight,
                interactor.notificationStackChanged,
                interactor.useExtraShelfSpace,
                interactor.notificationStackChanged,
            ) { flows ->
                val showLimitedNotifications = flows[0] as Boolean
                val (showUnlimitedNotifications, isOnLockscreen) =
                    flows[1] as Pair<Boolean, Boolean>
                val isUserInteracting = flows[2] as Boolean
                val availableHeight = flows[3] as Float
                val useExtraShelfSpace = flows[5] as Boolean
                val useExtraShelfSpace = flows[4] as Boolean

                if (!isUserInteracting) {
                    if (showLimitedNotifications) {
@@ -937,11 +938,7 @@ constructor(
                            bounds.map { it.top },
                            isOnLockscreenWithoutShade,
                            interactor.notificationStackChanged,
                        ) {
                            maxNotifications,
                            top,
                            isOnLockscreenWithoutShade,
                            notificationStackChanged ->
                        ) { maxNotifications, top, isOnLockscreenWithoutShade, _ ->
                            if (isOnLockscreenWithoutShade && maxNotifications != -1) {
                                val height = calculateHeight(maxNotifications)
                                top + height
@@ -960,6 +957,10 @@ constructor(
        interactor.notificationStackChanged()
    }

    fun notificationStackChangedInstant() {
        interactor.notificationsInStackChangedInstant()
    }

    data class ConfigurationBasedDimensions(
        val horizontalPosition: HorizontalPosition,
        val marginStart: Int,
Loading