Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +6 −17 Original line number Diff line number Diff line Loading @@ -417,7 +417,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationShelf mShelf; private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; private float mKeyguardNotificationAvailableSpace = -1; @VisibleForTesting int mStatusBarHeight; private int mMinInteractionHeight; private final Rect mClipRect = new Rect(); Loading Loading @@ -775,9 +774,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = (int) mMaxLayoutHeight; drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); // The space between mTopPadding and mKeyguardBottomPadding determines the available space // for notifications on keyguard. if (mKeyguardBottomPadding >= 0) { y = getHeight() - (int) mKeyguardBottomPadding; drawDebugInfo(canvas, y, Color.GRAY, drawDebugInfo(canvas, y, Color.RED, /* label= */ "getHeight() - mKeyguardBottomPadding = " + y); } Loading @@ -789,7 +790,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight()); drawDebugInfo(canvas, y, Color.BLUE, drawDebugInfo(canvas, y, Color.LTGRAY, /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y); y = (int) mAmbientState.getStackY() + mContentHeight; Loading @@ -800,10 +801,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace); drawDebugInfo(canvas, y, Color.RED, /* label= */ "mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y); drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY, /* label= */ "mRoundedRectClippingBottom) = " + y); } Loading Loading @@ -2267,10 +2264,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0; final int height = (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, mShelf != null ? mShelf.getIntrinsicHeight() : 0); shelfIntrinsicHeight); mIntrinsicContentHeight = height; // The topPadding can be bigger than the regular padding when qs is expanded, in that Loading Loading @@ -4914,15 +4912,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mKeyguardBottomPadding = keyguardBottomPadding; } /** * For debugging only. Enables to draw a line related to the available size for notifications in * keyguard. */ public void setKeyguardAvailableSpaceForDebug(float keyguardNotificationAvailableSpace) { mKeyguardNotificationAvailableSpace = keyguardNotificationAvailableSpace; } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { mShouldShowShelfOnly = shouldShowShelfOnly; Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +0 −6 Original line number Diff line number Diff line Loading @@ -1305,12 +1305,6 @@ public class NotificationStackScrollLayoutController { mView.setKeyguardBottomPadding(keyguardBottomPadding); } /** For debugging only. */ public void mKeyguardNotificationAvailableSpaceForDebug( float keyguardNotificationAvailableSpace) { mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace); } public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationEntry entry, Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +68 −69 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.children import javax.inject.Inject import kotlin.math.max import kotlin.math.min import kotlin.properties.Delegates.notNull private const val TAG = "NotificationStackSizeCalculator" Loading @@ -51,9 +52,7 @@ constructor( */ private var maxKeyguardNotifications by notNull<Int>() /** * Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Int>() init { Loading @@ -61,55 +60,34 @@ constructor( } /** * Given the [availableSpace] constraint, calculates how many notification to show. * Given the [totalAvailableSpace] constraint, calculates how many notification to show. * * This number is only valid in keyguard. * * @param availableSpace space for notifications. This doesn't include the space for the shelf. * @param totalAvailableSpace space for notifications. This includes the space for the shelf. */ fun computeMaxKeyguardNotifications( stack: NotificationStackScrollLayout, availableSpace: Float, shelfHeight: Float totalAvailableSpace: Float, shelfIntrinsicHeight: Float ): Int { log { "computeMaxKeyguardNotifications(" + "availableSpace=$availableSpace shelfHeight=$shelfHeight)" val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) var maxNotifications = stackHeightSequence.lastIndexWhile { stackHeight -> stackHeight <= totalAvailableSpace } if (onLockscreen()) { maxNotifications = min(maxKeyguardNotifications, maxNotifications) } val children: Sequence<ExpandableView> = stack.childrenSequence var remainingSpace: Float = availableSpace var count = 0 var previous: ExpandableView? = null val onLockscreen = true val showableRows = children.filter { it.isShowable(onLockscreen) } val showableRowsCount = showableRows.count() log { "\tshowableRowsCount=$showableRowsCount "} showableRows.forEachIndexed { i, current -> val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) val spaceAfter = remainingSpace - spaceNeeded previous = current log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace " + "spaceAfter=$spaceAfter" } if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) { count += 1 remainingSpace -= spaceNeeded } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) { log { "Show all notifications. Shelf not needed." } // If this is the last one, and it fits using the space shelf would use, then we can // display it, as the shelf will not be needed (as all notifications are shown). return count + 1 } else { // Could be < 0 if the space available is less than the shelf size. Returns 0 in this case. maxNotifications = max(0, maxNotifications) log { "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}" } return count } "computeMaxKeyguardNotifications(" + "availableSpace=$totalAvailableSpace" + " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications" } log { "All fit. Returning $count" } return count return maxNotifications } /** Loading @@ -119,47 +97,60 @@ constructor( * @param stack stack containing notifications as children. * @param maxNotifications Maximum number of notifications. When reached, the others will go * into the shelf. * @param shelfHeight height of the shelf. It might be zero. * @param shelfIntrinsicHeight 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, shelfHeight: Float shelfIntrinsicHeight: Float ): Float { val children: Sequence<ExpandableView> = stack.childrenSequence val maxNotificationsArg = infiniteIfNegative(maxNotifications) val heightPerMaxNotifications = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) val height = heightPerMaxNotifications.elementAtOrElse(maxNotifications) { heightPerMaxNotifications.last() // Height with all notifications visible. } log { "computeHeight(maxNotifications=$maxNotifications) -> $height" } return height } /** The ith result in the sequence is the height with ith max notifications. */ private fun computeHeightPerNotificationLimit( stack: NotificationStackScrollLayout, shelfIntrinsicHeight: Float ): Sequence<Float> = sequence { val children = stack.showableChildren().toList() var height = 0f var previous: ExpandableView? = null var count = 0 val onLockscreen = onLockscreen() log { "computeHeight(maxNotification=$maxNotifications, shelf=$shelfHeight" } children.filter { it.isShowable(onLockscreen) }.forEach { current -> if (count < maxNotificationsArg) { val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) log { "\ti=$count spaceNeeded=$spaceNeeded" } height += spaceNeeded count += 1 yield(dividerHeight + shelfIntrinsicHeight) // Only shelf. children.forEachIndexed { i, currentNotification -> height += currentNotification.spaceNeeded(i, previous, stack, onLockscreen) previous = currentNotification val shelfHeight = if (i == children.lastIndex) { 0f // No shelf needed. } else { height += current.calculateGapAndDividerHeight(stack, previous, count) height += shelfHeight log { "returning height with shelf -> $height" } return height val spaceBeforeShelf = calculateGapAndDividerHeight( stack, previous = currentNotification, current = children[i + 1], i) spaceBeforeShelf + shelfIntrinsicHeight } previous = current yield(height + shelfHeight) } log { "Returning height without shelf -> $height" } return height } fun updateResources() { maxKeyguardNotifications = infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count)) dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) } private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView> Loading @@ -180,7 +171,7 @@ constructor( } else { intrinsicHeight.toFloat() } size += calculateGapAndDividerHeight(stack, previousView, visibleIndex) size += calculateGapAndDividerHeight(stack, previousView, current = this, visibleIndex) return size } Loading @@ -200,18 +191,22 @@ constructor( return true } private fun ExpandableView.calculateGapAndDividerHeight( private fun calculateGapAndDividerHeight( stack: NotificationStackScrollLayout, previous: ExpandableView?, current: ExpandableView?, visibleIndex: Int ): Float { var height = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex) var height = stack.calculateGapHeight(previous, current, visibleIndex) if (visibleIndex != 0) { height += dividerHeight } return height } private fun NotificationStackScrollLayout.showableChildren() = this.childrenSequence.filter { it.isShowable(onLockscreen()) } /** * Can a view be shown on the lockscreen when calculating the number of allowed notifications to * show? Loading Loading @@ -240,4 +235,8 @@ constructor( } else { v } /** Returns the last index where [predicate] returns true, or -1 if it was always false. */ private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int = takeWhile(predicate).count() - 1 } packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +3 −11 Original line number Diff line number Diff line Loading @@ -317,8 +317,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; // Space available for notifications. private float mKeyguardNotificationAvailableSpace; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; Loading Loading @@ -1245,8 +1243,6 @@ public class NotificationPanelViewController extends PanelViewController { mMaxAllowedKeyguardNotifications); mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug( mKeyguardNotificationBottomPadding); mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug( mKeyguardNotificationAvailableSpace); } else { // no max when not on the keyguard mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); Loading Loading @@ -1468,13 +1464,11 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { int notificationPadding = Math.max( 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); float shelfHeight = float shelfIntrinsicHeight = mNotificationShelfController.getVisibility() == View.GONE ? 0 : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; : mNotificationShelfController.getIntrinsicHeight(); // Padding to add to the bottom of the stack to keep a minimum distance from the top of // the lock icon. Loading @@ -1493,13 +1487,11 @@ public class NotificationPanelViewController extends PanelViewController { float availableSpace = mNotificationStackScrollLayoutController.getHeight() - topPadding - shelfHeight - bottomPadding; mKeyguardNotificationAvailableSpace = availableSpace; return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications( mNotificationStackScrollLayoutController.getView(), availableSpace, shelfHeight); shelfIntrinsicHeight); } private void updateClock() { Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +55 −81 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack import android.annotation.DimenRes import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.view.View.VISIBLE Loading @@ -27,6 +28,7 @@ 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.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.google.common.truth.Truth.assertThat import org.junit.Before Loading @@ -34,8 +36,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) Loading @@ -49,17 +51,15 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { private lateinit var sizeCalculator: NotificationStackSizeCalculator private val gapHeight = px(R.dimen.notification_section_divider_height) private val dividerHeight = px(R.dimen.notification_divider_height) private val shelfHeight = px(R.dimen.notification_shelf_height) private val rowHeight = px(R.dimen.notification_max_height) @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) .thenReturn(GAP_HEIGHT) with(testableResources) { addOverride(R.integer.keyguard_max_notification_count, -1) addOverride(R.dimen.notification_divider_height, DIVIDER_HEIGHT.toInt()) } sizeCalculator = NotificationStackSizeCalculator( statusBarStateController = sysuiStatusBarStateController, Loading @@ -68,7 +68,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { @Test fun computeMaxKeyguardNotifications_zeroSpace_returnZero() { val rows = listOf(createMockRow(height = ROW_HEIGHT)) val rows = listOf(createMockRow(height = rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f) Loading @@ -87,105 +87,78 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test fun computeMaxKeyguardNotifications_spaceForOne_returnsOne() { val rowHeight = ROW_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight val shelfHeight = totalSpaceForEachRow / 2 // In this way shelf absence will not leave room for another. val spaceForOne = totalSpaceForEachRow val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight)) fun computeMaxKeyguardNotifications_spaceForOneAndShelf_returnsOne() { setGapHeight(gapHeight) val shelfHeight = rowHeight / 2 // Shelf absence won't leave room for another row. val availableSpace = listOf(rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight).sum() val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications( rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) assertThat(maxNotifications).isEqualTo(1) } @Test fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() { val rowHeight = ROW_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight val shelfHeight = totalSpaceForEachRow + DIVIDER_HEIGHT val spaceForOne = totalSpaceForEachRow val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications( rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) } @Test fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { val rowHeight = ROW_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight val spaceForTwo = totalSpaceForEachRow * 2 + DIVIDER_HEIGHT val rows = setGapHeight(gapHeight) val shelfHeight = shelfHeight + dividerHeight val availableSpace = listOf( createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) rowHeight + dividerHeight, gapHeight + rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight) .sum() val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) } @Test fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() { val rowHeight = ROW_HEIGHT val shelfHeight = SHELF_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT val availableSpace = totalSpaceForEachRow * 2 setGapHeight(gapHeight) val shelfHeight = shelfHeight val availableSpace = listOf( rowHeight + dividerHeight, gapHeight + rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight) .sum() // All rows in separate sections (default setup). val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) assertThat(height).isAtMost(availableSpace + GAP_HEIGHT + SHELF_HEIGHT) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) assertThat(height).isAtMost(availableSpace) } @Test fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() { val rowHeight = ROW_HEIGHT val shelfHeight = SHELF_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT val availableSpace = totalSpaceForEachRow * 1 fun computeHeight_noGapBeforeShelf_returnsAtMostSpaceAvailable() { // Both rows are in the same section. whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) .thenReturn(0f) val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight)) setGapHeight(0f) val rowHeight = rowHeight val shelfHeight = shelfHeight val availableSpace = listOf(rowHeight + dividerHeight, dividerHeight + shelfHeight).sum() val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) assertThat(height).isAtMost(availableSpace) } private fun computeMaxKeyguardNotifications( rows: List<ExpandableView>, availableSpace: Float, shelfHeight: Float = SHELF_HEIGHT shelfHeight: Float = this.shelfHeight ): Int { setupChildren(rows) return sizeCalculator.computeMaxKeyguardNotifications( Loading @@ -204,9 +177,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { (1..number).map { createMockRow() }.toList() private fun createMockRow( height: Float = ROW_HEIGHT, height: Float = rowHeight, isRemoved: Boolean = false, visibility: Int = VISIBLE, visibility: Int = VISIBLE ): ExpandableNotificationRow { val row = mock(ExpandableNotificationRow::class.java) val entry = mock(NotificationEntry::class.java) Loading @@ -220,11 +193,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { return row } /** Default dimensions for tests that don't overwrite them. */ companion object { const val GAP_HEIGHT = 12f const val DIVIDER_HEIGHT = 3f const val SHELF_HEIGHT = 14f const val ROW_HEIGHT = SHELF_HEIGHT * 3 private fun setGapHeight(height: Float) { whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())).thenReturn(height) whenever(stackLayout.calculateGapHeight(nullable(), nullable(), /* visibleIndex= */ eq(0))) .thenReturn(0f) } private fun px(@DimenRes id: Int): Float = testableResources.resources.getDimensionPixelSize(id).toFloat() } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +6 −17 Original line number Diff line number Diff line Loading @@ -417,7 +417,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationShelf mShelf; private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; private float mKeyguardNotificationAvailableSpace = -1; @VisibleForTesting int mStatusBarHeight; private int mMinInteractionHeight; private final Rect mClipRect = new Rect(); Loading Loading @@ -775,9 +774,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = (int) mMaxLayoutHeight; drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); // The space between mTopPadding and mKeyguardBottomPadding determines the available space // for notifications on keyguard. if (mKeyguardBottomPadding >= 0) { y = getHeight() - (int) mKeyguardBottomPadding; drawDebugInfo(canvas, y, Color.GRAY, drawDebugInfo(canvas, y, Color.RED, /* label= */ "getHeight() - mKeyguardBottomPadding = " + y); } Loading @@ -789,7 +790,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight()); drawDebugInfo(canvas, y, Color.BLUE, drawDebugInfo(canvas, y, Color.LTGRAY, /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y); y = (int) mAmbientState.getStackY() + mContentHeight; Loading @@ -800,10 +801,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace); drawDebugInfo(canvas, y, Color.RED, /* label= */ "mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y); drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY, /* label= */ "mRoundedRectClippingBottom) = " + y); } Loading Loading @@ -2267,10 +2264,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0; final int height = (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, mShelf != null ? mShelf.getIntrinsicHeight() : 0); shelfIntrinsicHeight); mIntrinsicContentHeight = height; // The topPadding can be bigger than the regular padding when qs is expanded, in that Loading Loading @@ -4914,15 +4912,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mKeyguardBottomPadding = keyguardBottomPadding; } /** * For debugging only. Enables to draw a line related to the available size for notifications in * keyguard. */ public void setKeyguardAvailableSpaceForDebug(float keyguardNotificationAvailableSpace) { mKeyguardNotificationAvailableSpace = keyguardNotificationAvailableSpace; } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { mShouldShowShelfOnly = shouldShowShelfOnly; Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +0 −6 Original line number Diff line number Diff line Loading @@ -1305,12 +1305,6 @@ public class NotificationStackScrollLayoutController { mView.setKeyguardBottomPadding(keyguardBottomPadding); } /** For debugging only. */ public void mKeyguardNotificationAvailableSpaceForDebug( float keyguardNotificationAvailableSpace) { mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace); } public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationEntry entry, Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +68 −69 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.children import javax.inject.Inject import kotlin.math.max import kotlin.math.min import kotlin.properties.Delegates.notNull private const val TAG = "NotificationStackSizeCalculator" Loading @@ -51,9 +52,7 @@ constructor( */ private var maxKeyguardNotifications by notNull<Int>() /** * Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Int>() init { Loading @@ -61,55 +60,34 @@ constructor( } /** * Given the [availableSpace] constraint, calculates how many notification to show. * Given the [totalAvailableSpace] constraint, calculates how many notification to show. * * This number is only valid in keyguard. * * @param availableSpace space for notifications. This doesn't include the space for the shelf. * @param totalAvailableSpace space for notifications. This includes the space for the shelf. */ fun computeMaxKeyguardNotifications( stack: NotificationStackScrollLayout, availableSpace: Float, shelfHeight: Float totalAvailableSpace: Float, shelfIntrinsicHeight: Float ): Int { log { "computeMaxKeyguardNotifications(" + "availableSpace=$availableSpace shelfHeight=$shelfHeight)" val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) var maxNotifications = stackHeightSequence.lastIndexWhile { stackHeight -> stackHeight <= totalAvailableSpace } if (onLockscreen()) { maxNotifications = min(maxKeyguardNotifications, maxNotifications) } val children: Sequence<ExpandableView> = stack.childrenSequence var remainingSpace: Float = availableSpace var count = 0 var previous: ExpandableView? = null val onLockscreen = true val showableRows = children.filter { it.isShowable(onLockscreen) } val showableRowsCount = showableRows.count() log { "\tshowableRowsCount=$showableRowsCount "} showableRows.forEachIndexed { i, current -> val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) val spaceAfter = remainingSpace - spaceNeeded previous = current log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace " + "spaceAfter=$spaceAfter" } if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) { count += 1 remainingSpace -= spaceNeeded } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) { log { "Show all notifications. Shelf not needed." } // If this is the last one, and it fits using the space shelf would use, then we can // display it, as the shelf will not be needed (as all notifications are shown). return count + 1 } else { // Could be < 0 if the space available is less than the shelf size. Returns 0 in this case. maxNotifications = max(0, maxNotifications) log { "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}" } return count } "computeMaxKeyguardNotifications(" + "availableSpace=$totalAvailableSpace" + " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications" } log { "All fit. Returning $count" } return count return maxNotifications } /** Loading @@ -119,47 +97,60 @@ constructor( * @param stack stack containing notifications as children. * @param maxNotifications Maximum number of notifications. When reached, the others will go * into the shelf. * @param shelfHeight height of the shelf. It might be zero. * @param shelfIntrinsicHeight 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, shelfHeight: Float shelfIntrinsicHeight: Float ): Float { val children: Sequence<ExpandableView> = stack.childrenSequence val maxNotificationsArg = infiniteIfNegative(maxNotifications) val heightPerMaxNotifications = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) val height = heightPerMaxNotifications.elementAtOrElse(maxNotifications) { heightPerMaxNotifications.last() // Height with all notifications visible. } log { "computeHeight(maxNotifications=$maxNotifications) -> $height" } return height } /** The ith result in the sequence is the height with ith max notifications. */ private fun computeHeightPerNotificationLimit( stack: NotificationStackScrollLayout, shelfIntrinsicHeight: Float ): Sequence<Float> = sequence { val children = stack.showableChildren().toList() var height = 0f var previous: ExpandableView? = null var count = 0 val onLockscreen = onLockscreen() log { "computeHeight(maxNotification=$maxNotifications, shelf=$shelfHeight" } children.filter { it.isShowable(onLockscreen) }.forEach { current -> if (count < maxNotificationsArg) { val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) log { "\ti=$count spaceNeeded=$spaceNeeded" } height += spaceNeeded count += 1 yield(dividerHeight + shelfIntrinsicHeight) // Only shelf. children.forEachIndexed { i, currentNotification -> height += currentNotification.spaceNeeded(i, previous, stack, onLockscreen) previous = currentNotification val shelfHeight = if (i == children.lastIndex) { 0f // No shelf needed. } else { height += current.calculateGapAndDividerHeight(stack, previous, count) height += shelfHeight log { "returning height with shelf -> $height" } return height val spaceBeforeShelf = calculateGapAndDividerHeight( stack, previous = currentNotification, current = children[i + 1], i) spaceBeforeShelf + shelfIntrinsicHeight } previous = current yield(height + shelfHeight) } log { "Returning height without shelf -> $height" } return height } fun updateResources() { maxKeyguardNotifications = infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count)) dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) } private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView> Loading @@ -180,7 +171,7 @@ constructor( } else { intrinsicHeight.toFloat() } size += calculateGapAndDividerHeight(stack, previousView, visibleIndex) size += calculateGapAndDividerHeight(stack, previousView, current = this, visibleIndex) return size } Loading @@ -200,18 +191,22 @@ constructor( return true } private fun ExpandableView.calculateGapAndDividerHeight( private fun calculateGapAndDividerHeight( stack: NotificationStackScrollLayout, previous: ExpandableView?, current: ExpandableView?, visibleIndex: Int ): Float { var height = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex) var height = stack.calculateGapHeight(previous, current, visibleIndex) if (visibleIndex != 0) { height += dividerHeight } return height } private fun NotificationStackScrollLayout.showableChildren() = this.childrenSequence.filter { it.isShowable(onLockscreen()) } /** * Can a view be shown on the lockscreen when calculating the number of allowed notifications to * show? Loading Loading @@ -240,4 +235,8 @@ constructor( } else { v } /** Returns the last index where [predicate] returns true, or -1 if it was always false. */ private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int = takeWhile(predicate).count() - 1 }
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +3 −11 Original line number Diff line number Diff line Loading @@ -317,8 +317,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; // Space available for notifications. private float mKeyguardNotificationAvailableSpace; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; Loading Loading @@ -1245,8 +1243,6 @@ public class NotificationPanelViewController extends PanelViewController { mMaxAllowedKeyguardNotifications); mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug( mKeyguardNotificationBottomPadding); mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug( mKeyguardNotificationAvailableSpace); } else { // no max when not on the keyguard mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); Loading Loading @@ -1468,13 +1464,11 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { int notificationPadding = Math.max( 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); float shelfHeight = float shelfIntrinsicHeight = mNotificationShelfController.getVisibility() == View.GONE ? 0 : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; : mNotificationShelfController.getIntrinsicHeight(); // Padding to add to the bottom of the stack to keep a minimum distance from the top of // the lock icon. Loading @@ -1493,13 +1487,11 @@ public class NotificationPanelViewController extends PanelViewController { float availableSpace = mNotificationStackScrollLayoutController.getHeight() - topPadding - shelfHeight - bottomPadding; mKeyguardNotificationAvailableSpace = availableSpace; return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications( mNotificationStackScrollLayoutController.getView(), availableSpace, shelfHeight); shelfIntrinsicHeight); } private void updateClock() { Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +55 −81 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack import android.annotation.DimenRes import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.view.View.VISIBLE Loading @@ -27,6 +28,7 @@ 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.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.google.common.truth.Truth.assertThat import org.junit.Before Loading @@ -34,8 +36,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) Loading @@ -49,17 +51,15 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { private lateinit var sizeCalculator: NotificationStackSizeCalculator private val gapHeight = px(R.dimen.notification_section_divider_height) private val dividerHeight = px(R.dimen.notification_divider_height) private val shelfHeight = px(R.dimen.notification_shelf_height) private val rowHeight = px(R.dimen.notification_max_height) @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) .thenReturn(GAP_HEIGHT) with(testableResources) { addOverride(R.integer.keyguard_max_notification_count, -1) addOverride(R.dimen.notification_divider_height, DIVIDER_HEIGHT.toInt()) } sizeCalculator = NotificationStackSizeCalculator( statusBarStateController = sysuiStatusBarStateController, Loading @@ -68,7 +68,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { @Test fun computeMaxKeyguardNotifications_zeroSpace_returnZero() { val rows = listOf(createMockRow(height = ROW_HEIGHT)) val rows = listOf(createMockRow(height = rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f) Loading @@ -87,105 +87,78 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test fun computeMaxKeyguardNotifications_spaceForOne_returnsOne() { val rowHeight = ROW_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight val shelfHeight = totalSpaceForEachRow / 2 // In this way shelf absence will not leave room for another. val spaceForOne = totalSpaceForEachRow val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight)) fun computeMaxKeyguardNotifications_spaceForOneAndShelf_returnsOne() { setGapHeight(gapHeight) val shelfHeight = rowHeight / 2 // Shelf absence won't leave room for another row. val availableSpace = listOf(rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight).sum() val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications( rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) assertThat(maxNotifications).isEqualTo(1) } @Test fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() { val rowHeight = ROW_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight val shelfHeight = totalSpaceForEachRow + DIVIDER_HEIGHT val spaceForOne = totalSpaceForEachRow val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications( rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) } @Test fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { val rowHeight = ROW_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight val spaceForTwo = totalSpaceForEachRow * 2 + DIVIDER_HEIGHT val rows = setGapHeight(gapHeight) val shelfHeight = shelfHeight + dividerHeight val availableSpace = listOf( createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) rowHeight + dividerHeight, gapHeight + rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight) .sum() val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) } @Test fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() { val rowHeight = ROW_HEIGHT val shelfHeight = SHELF_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT val availableSpace = totalSpaceForEachRow * 2 setGapHeight(gapHeight) val shelfHeight = shelfHeight val availableSpace = listOf( rowHeight + dividerHeight, gapHeight + rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight) .sum() // All rows in separate sections (default setup). val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) assertThat(height).isAtMost(availableSpace + GAP_HEIGHT + SHELF_HEIGHT) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) assertThat(height).isAtMost(availableSpace) } @Test fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() { val rowHeight = ROW_HEIGHT val shelfHeight = SHELF_HEIGHT val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT val availableSpace = totalSpaceForEachRow * 1 fun computeHeight_noGapBeforeShelf_returnsAtMostSpaceAvailable() { // Both rows are in the same section. whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) .thenReturn(0f) val rows = listOf( createMockRow(rowHeight), createMockRow(rowHeight)) setGapHeight(0f) val rowHeight = rowHeight val shelfHeight = shelfHeight val availableSpace = listOf(rowHeight + dividerHeight, dividerHeight + shelfHeight).sum() val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) assertThat(height).isAtMost(availableSpace) } private fun computeMaxKeyguardNotifications( rows: List<ExpandableView>, availableSpace: Float, shelfHeight: Float = SHELF_HEIGHT shelfHeight: Float = this.shelfHeight ): Int { setupChildren(rows) return sizeCalculator.computeMaxKeyguardNotifications( Loading @@ -204,9 +177,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { (1..number).map { createMockRow() }.toList() private fun createMockRow( height: Float = ROW_HEIGHT, height: Float = rowHeight, isRemoved: Boolean = false, visibility: Int = VISIBLE, visibility: Int = VISIBLE ): ExpandableNotificationRow { val row = mock(ExpandableNotificationRow::class.java) val entry = mock(NotificationEntry::class.java) Loading @@ -220,11 +193,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { return row } /** Default dimensions for tests that don't overwrite them. */ companion object { const val GAP_HEIGHT = 12f const val DIVIDER_HEIGHT = 3f const val SHELF_HEIGHT = 14f const val ROW_HEIGHT = SHELF_HEIGHT * 3 private fun setGapHeight(height: Float) { whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())).thenReturn(height) whenever(stackLayout.calculateGapHeight(nullable(), nullable(), /* visibleIndex= */ eq(0))) .thenReturn(0f) } private fun px(@DimenRes id: Int): Float = testableResources.resources.getDimensionPixelSize(id).toFloat() }