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

Commit 7fe627a8 authored by Lyn Han's avatar Lyn Han Committed by Automerger Merge Worker
Browse files

Merge "Lockscreen FSI HUN: collapse when space is low" into udc-dev am: b8c42411

parents 38220961 b8c42411
Loading
Loading
Loading
Loading
+30 −4
Original line number Diff line number Diff line
@@ -299,6 +299,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
     */
    private boolean mShowGroupBackgroundWhenExpanded;

    /**
     * True if we always show the collapsed layout on lockscreen because vertical space is low.
     */
    private boolean mSaveSpaceOnLockscreen;

    /**
     * True if we use intrinsic height regardless of vertical space available on lockscreen.
     */
    private boolean mIgnoreLockscreenConstraints;

    private OnClickListener mExpandClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
@@ -394,6 +404,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        return mGroupExpansionChanging;
    }

    public void setSaveSpaceOnLockscreen(boolean saveSpace) {
        mSaveSpaceOnLockscreen = saveSpace;
    }

    public boolean getSaveSpaceOnLockscreen() {
        return mSaveSpaceOnLockscreen;
    }

    public void setGroupExpansionChanging(boolean changing) {
        mGroupExpansionChanging = changing;
    }
@@ -2546,12 +2564,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
        return super.performClick();
    }

    @Override
    public int getHeightWithoutLockscreenConstraints() {
        mIgnoreLockscreenConstraints = true;
        final int height = getIntrinsicHeight();
        mIgnoreLockscreenConstraints = false;
        return height;
    }

    @Override
    public int getIntrinsicHeight() {
        if (isUserLocked()) {
            return getActualHeight();
        }
        if (mGuts != null && mGuts.isExposed()) {
        } else if (mGuts != null && mGuts.isExposed()) {
            return mGuts.getIntrinsicHeight();
        } else if ((isChildInGroup() && !isGroupExpanded())) {
            return mPrivateLayout.getMinHeight();
@@ -2573,13 +2598,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
            return getCollapsedHeight();
        }
    }

    /**
     * @return {@code true} if the notification can show it's heads up layout. This is mostly true
     * except for legacy use cases.
     */
    public boolean canShowHeadsUp() {
        if (mOnKeyguard && !isDozing() && !isBypassEnabled() && !mEntry.isStickyAndNotDemoted()) {
        if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
                (!mEntry.isStickyAndNotDemoted()
                        || (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
            return false;
        }
        return true;
+5 −0
Original line number Diff line number Diff line
@@ -291,6 +291,11 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
            long duration) {
    }

    public int getHeightWithoutLockscreenConstraints() {
        // ExpandableNotificationRow overrides this.
        return getHeight();
    }

    /**
     * @return The desired notification height.
     */
+220 −80
Original line number Diff line number Diff line
@@ -23,12 +23,14 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.Compile
import com.android.systemui.util.LargeScreenUtils.shouldUseSplitNotificationShade
import com.android.systemui.util.children
import java.io.PrintWriter
import javax.inject.Inject
@@ -51,11 +53,10 @@ class NotificationStackSizeCalculator
constructor(
    private val statusBarStateController: SysuiStatusBarStateController,
    private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
    private val mediaDataManager: MediaDataManager,
    @Main private val resources: Resources
) {

    private lateinit var lastComputeHeightLog : String

    /**
     * Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow shelf.
     * If there are exactly 1 + mMaxKeyguardNotifications, and they fit in the available space
@@ -67,66 +68,156 @@ constructor(
    /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
    private var dividerHeight by notNull<Float>()

    /**
     * True when there is not enough vertical space to show at least one notification with heads up
     * layout. When true, notifications always show collapsed layout.
     */
    private var saveSpaceOnLockscreen = false

    init {
        updateResources()
    }

    /**
     * Returns whether notifications and (shelf if visible) can fit in total space available.
     * [spaceForShelf] is extra vertical space allowed for the shelf to overlap the lock icon.
     * [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon.
     */
    private fun canStackFitInSpace(
        stackHeight: StackHeight,
        spaceForNotifications: Float,
        spaceForShelf: Float,
    ): Boolean {

        val (notificationsHeight, shelfHeightWithSpaceBefore) = stackHeight
        var canFit: Boolean
        notifSpace: Float,
        shelfSpace: Float,
    ): FitResult {
        val (notifHeight, notifHeightSaveSpace, shelfHeightWithSpaceBefore) = stackHeight

        if (shelfHeightWithSpaceBefore == 0f) {
            canFit = notificationsHeight <= spaceForNotifications
            if (notifHeight <= notifSpace) {
                log {
                "canStackFitInSpace[$canFit] = notificationsHeight[$notificationsHeight]" +
                    " <= spaceForNotifications[$spaceForNotifications]"
                    "\tcanStackFitInSpace[FIT] = notifHeight[$notifHeight]" +
                        " <= notifSpace[$notifSpace]"
                }
                return FitResult.FIT
            }
            if (notifHeightSaveSpace <= notifSpace) {
                log {
                    "\tcanStackFitInSpace[FIT_IF_SAVE_SPACE]" +
                        " = notifHeightSaveSpace[$notifHeightSaveSpace]" +
                        " <= notifSpace[$notifSpace]"
                }
                return FitResult.FIT_IF_SAVE_SPACE
            }
            log {
                "\tcanStackFitInSpace[NO_FIT]" +
                    " = notifHeightSaveSpace[$notifHeightSaveSpace] > notifSpace[$notifSpace]"
            }
            return FitResult.NO_FIT
        } else {
            if ((notifHeight + shelfHeightWithSpaceBefore) <= (notifSpace + shelfSpace)) {
                log {
                    "\tcanStackFitInSpace[FIT] = (notifHeight[$notifHeight]" +
                        " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
                        " <= (notifSpace[$notifSpace] " +
                        " + spaceForShelf[$shelfSpace])"
                }
                return FitResult.FIT
            } else if (
                (notifHeightSaveSpace + shelfHeightWithSpaceBefore) <= (notifSpace + shelfSpace)
            ) {
                log {
                    "\tcanStackFitInSpace[FIT_IF_SAVE_SPACE]" +
                        " = (notifHeightSaveSpace[$notifHeightSaveSpace]" +
                        " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
                        " <= (notifSpace[$notifSpace] + shelfSpace[$shelfSpace])"
                }
                return FitResult.FIT_IF_SAVE_SPACE
            } else {
            canFit =
                (notificationsHeight + shelfHeightWithSpaceBefore) <=
                    (spaceForNotifications + spaceForShelf)
                log {
                "canStackFitInSpace[$canFit] = (notificationsHeight[$notificationsHeight]" +
                    "\tcanStackFitInSpace[NO_FIT]" +
                        " = (notifHeightSaveSpace[$notifHeightSaveSpace]" +
                        " + shelfHeightWithSpaceBefore[$shelfHeightWithSpaceBefore])" +
                    " <= (spaceForNotifications[$spaceForNotifications] " +
                    " + spaceForShelf[$spaceForShelf])"
                        " > (notifSpace[$notifSpace] + shelfSpace[$shelfSpace])"
                }
                return FitResult.NO_FIT
            }
        }
        return canFit
    }

    /**
     * Given the [spaceForNotifications] and [spaceForShelf] constraints, calculate how many
     * notifications to show. This number is only valid in keyguard.
     * Given the [notifSpace] and [shelfSpace] constraints, calculate how many notifications to
     * show. This number is only valid in keyguard.
     *
     * @param totalAvailableSpace space for notifications. This includes the space for the shelf.
     */
    fun computeMaxKeyguardNotifications(
        stack: NotificationStackScrollLayout,
        spaceForNotifications: Float,
        spaceForShelf: Float,
        shelfIntrinsicHeight: Float
        notifSpace: Float,
        shelfSpace: Float,
        shelfHeight: Float,
    ): Int {
        log { "\n " }
        log {
            "computeMaxKeyguardNotifications ---" +
                "\n\tnotifSpace $notifSpace" +
                "\n\tspaceForShelf $shelfSpace" +
                "\n\tshelfIntrinsicHeight $shelfHeight"
        }
        if (notifSpace + shelfSpace <= 0f) {
            log { "--- No space to show anything. maxNotifs=0" }
            return 0
        }
        log { "\n" }

        val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
            /* computeHeight= */ false)
        val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight)
        val isMediaShowing = mediaDataManager.hasActiveMediaOrRecommendation()

        var maxNotifications =
        log { "\tGet maxNotifWithoutSavingSpace ---" }
        val maxNotifWithoutSavingSpace =
            stackHeightSequence.lastIndexWhile { heightResult ->
                canStackFitInSpace(
                    heightResult,
                    spaceForNotifications = spaceForNotifications,
                    spaceForShelf = spaceForShelf)
                    notifSpace = notifSpace,
                    shelfSpace = shelfSpace
                ) == FitResult.FIT
            }

        // How many notifications we can show at heightWithoutLockscreenConstraints
        var minCountAtHeightWithoutConstraints =
            if (isMediaShowing && !shouldUseSplitNotificationShade(resources)) 2 else 1
        log {
            "\t---maxNotifWithoutSavingSpace=$maxNotifWithoutSavingSpace " +
                "isMediaShowing=$isMediaShowing" +
                "minCountAtHeightWithoutConstraints=$minCountAtHeightWithoutConstraints"
        }
        log { "\n" }

        var maxNotifications: Int
        if (maxNotifWithoutSavingSpace >= minCountAtHeightWithoutConstraints) {
            saveSpaceOnLockscreen = false
            maxNotifications = maxNotifWithoutSavingSpace
            log {
                "\tDo NOT save space. maxNotifications=maxNotifWithoutSavingSpace=$maxNotifications"
            }
        } else {
            log { "\tSAVE space ---" }
            saveSpaceOnLockscreen = true
            maxNotifications =
                stackHeightSequence.lastIndexWhile { heightResult ->
                    canStackFitInSpace(
                        heightResult,
                        notifSpace = notifSpace,
                        shelfSpace = shelfSpace
                    ) != FitResult.NO_FIT
                }
            log { "\t--- maxNotifications=$maxNotifications" }
        }

        // Must update views immediately to avoid mismatches between initial HUN layout height
        // and the height adapted to lockscreen space constraints, which causes jump cuts.
        stack.showableChildren().toList().forEach { currentNotification ->
            run {
                if (currentNotification is ExpandableNotificationRow) {
                    currentNotification.saveSpaceOnLockscreen = saveSpaceOnLockscreen
                }
            }
        }

        if (onLockscreen()) {
@@ -137,53 +228,80 @@ constructor(
        maxNotifications = max(0, maxNotifications)
        log {
            val sequence = if (SPEW) " stackHeightSequence=${stackHeightSequence.toList()}" else ""
            "computeMaxKeyguardNotifications(" +
                " spaceForNotifications=$spaceForNotifications" +
                " spaceForShelf=$spaceForShelf" +
                " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications$sequence"
            "--- computeMaxKeyguardNotifications(" +
                " notifSpace=$notifSpace" +
                " shelfSpace=$shelfSpace" +
                " shelfHeight=$shelfHeight) -> $maxNotifications$sequence"
        }
        log { "\n" }
        return maxNotifications
    }

    /**
     * Given the [maxNotifications] constraint, calculates the height of the
     * Given the [maxNotifs] constraint, calculates the height of the
     * [NotificationStackScrollLayout]. This might or might not be in keyguard.
     *
     * @param stack stack containing notifications as children.
     * @param maxNotifications Maximum number of notifications. When reached, the others will go
     * into the shelf.
     * @param shelfIntrinsicHeight height of the shelf, without any padding. It might be zero.
     *
     * @param maxNotifs Maximum number of notifications. When reached, the others will go into the
     *   shelf.
     * @param shelfHeight height of the shelf, without any padding. It might be zero.
     * @return height of the stack, including shelf height, if needed.
     */
    fun computeHeight(
        stack: NotificationStackScrollLayout,
        maxNotifications: Int,
        shelfIntrinsicHeight: Float
        maxNotifs: Int,
        shelfHeight: Float
    ): Float {
        log { "\n" }
        lastComputeHeightLog = ""
        val heightPerMaxNotifications =
            computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight,
                    /* computeHeight= */ true)

        val (notificationsHeight, shelfHeightWithSpaceBefore) =
            heightPerMaxNotifications.elementAtOrElse(maxNotifications) {
                heightPerMaxNotifications.last() // Height with all notifications visible.
            }
        lastComputeHeightLog += "\ncomputeHeight(maxNotifications=$maxNotifications," +
                "shelfIntrinsicHeight=$shelfIntrinsicHeight) -> " +
                "${notificationsHeight + shelfHeightWithSpaceBefore}" +
                " = ($notificationsHeight + $shelfHeightWithSpaceBefore)"
        log { "computeHeight ---" }

        val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfHeight)

        val (notifsHeight, notifsHeightSavingSpace, shelfHeightWithSpaceBefore) =
            stackHeightSequence.elementAtOrElse(maxNotifs) {
                stackHeightSequence.last() // Height with all notifications visible.
            }

        var height: Float
        if (saveSpaceOnLockscreen) {
            height = notifsHeightSavingSpace + shelfHeightWithSpaceBefore
            log {
            lastComputeHeightLog
                "--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" +
                    " -> $height=($notifsHeightSavingSpace+$shelfHeightWithSpaceBefore)," +
                    " | saveSpaceOnLockscreen=$saveSpaceOnLockscreen"
            }
        } else {
            height = notifsHeight + shelfHeightWithSpaceBefore
            log {
                "--- computeHeight(maxNotifs=$maxNotifs, shelfHeight=$shelfHeight)" +
                    " -> ${height}=($notifsHeight+$shelfHeightWithSpaceBefore)" +
                    " | saveSpaceOnLockscreen=$saveSpaceOnLockscreen"
            }
        return notificationsHeight + shelfHeightWithSpaceBefore
        }
        return height
    }

    private enum class FitResult {
        FIT,
        FIT_IF_SAVE_SPACE,
        NO_FIT
    }

    data class SpaceNeeded(
        // Float height of spaceNeeded when showing heads up layout for FSI HUNs.
        val whenEnoughSpace: Float,

        // Float height of space needed when showing collapsed layout for FSI HUNs.
        val whenSavingSpace: Float
    )

    private data class StackHeight(
        // Float height with ith max notifications (not including shelf)
        val notificationsHeight: Float,
        val notifsHeight: Float,

        // Float height with ith max notifications
        // (not including shelf, using collapsed layout for FSI HUN)
        val notifsHeightSavingSpace: Float,

        // Float height of shelf (0 if shelf is not showing), and space before the shelf that
        // changes during the lockscreen <=> full shade transition.
@@ -193,20 +311,27 @@ constructor(
    private fun computeHeightPerNotificationLimit(
        stack: NotificationStackScrollLayout,
        shelfHeight: Float,
        computeHeight: Boolean
    ): Sequence<StackHeight> = sequence {
        log { "computeHeightPerNotificationLimit" }

        val children = stack.showableChildren().toList()
        var notifications = 0f
        var notifsWithCollapsedHun = 0f
        var previous: ExpandableView? = null
        val onLockscreen = onLockscreen()

        // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
        yield(StackHeight(notificationsHeight = 0f, shelfHeightWithSpaceBefore = shelfHeight))
        yield(
            StackHeight(
                notifsHeight = 0f,
                notifsHeightSavingSpace = 0f,
                shelfHeightWithSpaceBefore = shelfHeight
            )
        )

        children.forEachIndexed { i, currentNotification ->
            notifications += spaceNeeded(currentNotification, i, previous, stack, onLockscreen)
            val space = getSpaceNeeded(currentNotification, i, previous, stack, onLockscreen)
            notifications += space.whenEnoughSpace
            notifsWithCollapsedHun += space.whenSavingSpace

            previous = currentNotification

            val shelfWithSpaceBefore =
@@ -219,22 +344,23 @@ constructor(
                            stack,
                            previous = currentNotification,
                            current = children[firstViewInShelfIndex],
                            currentIndex = firstViewInShelfIndex)
                            currentIndex = firstViewInShelfIndex
                        )
                    spaceBeforeShelf + shelfHeight
                }

            val currentLog = "computeHeight | i=$i notificationsHeight=$notifications " +
                "shelfHeightWithSpaceBefore=$shelfWithSpaceBefore"
            if (computeHeight) {
                lastComputeHeightLog += "\n" + currentLog
            }
            log {
                currentLog
                "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
                    "notifsHeightSavingSpace=$notifsWithCollapsedHun" +
                    " shelfWithSpaceBefore=$shelfWithSpaceBefore"
            }
            yield(
                StackHeight(
                    notificationsHeight = notifications,
                    shelfHeightWithSpaceBefore = shelfWithSpaceBefore))
                    notifsHeight = notifications,
                    notifsHeightSavingSpace = notifsWithCollapsedHun,
                    shelfHeightWithSpaceBefore = shelfWithSpaceBefore
                )
            )
        }
    }

@@ -256,32 +382,46 @@ constructor(
    }

    @VisibleForTesting
    fun spaceNeeded(
    fun getSpaceNeeded(
        view: ExpandableView,
        visibleIndex: Int,
        previousView: ExpandableView?,
        stack: NotificationStackScrollLayout,
        onLockscreen: Boolean
    ): Float {
        onLockscreen: Boolean,
    ): SpaceNeeded {
        assert(view.isShowable(onLockscreen))

        // Must use heightWithoutLockscreenConstraints because intrinsicHeight references
        // mSaveSpaceOnLockscreen and using intrinsicHeight here will result in stack overflow.
        val height = view.heightWithoutLockscreenConstraints.toFloat()
        val gapAndDividerHeight =
            calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)

        var size =
            if (onLockscreen) {
                if (view is ExpandableNotificationRow && view.entry.isStickyAndNotDemoted) {
                    view.intrinsicHeight.toFloat()
                    height
                } else {
                    view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
                }
            } else {
                view.intrinsicHeight.toFloat()
                height
            }
        size += gapAndDividerHeight

        var sizeWhenSavingSpace =
            if (onLockscreen) {
                view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
            } else {
                height
            }
        sizeWhenSavingSpace += gapAndDividerHeight

        size += calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
        return size
        return SpaceNeeded(size, sizeWhenSavingSpace)
    }

    fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println("NotificationStackSizeCalculator lastComputeHeightLog = $lastComputeHeightLog")
        pw.println("NotificationStackSizeCalculator saveSpaceOnLockscreen=$saveSpaceOnLockscreen")
    }

    private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean {
+121 −55

File changed.

Preview size limit exceeded, changes collapsed.