From 58ab072d3c909c21f273e7a44cbc86f0e6c5bcd1 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Wed, 9 Mar 2022 00:08:06 +0100 Subject: [PATCH] Fix NotificationStack size calculation There was a lot of duplicated code to calculate: 1. How many notifications should fit while on the lockscreen 2. The height of the notification stack The two things are now in NotificationstackSizeCalculator.kt, allowing also some Unit testing, and removing some responsibilities from NotificationPanelViewController. There was a bug in the calculation of the notification stack bottom padding that resulted in less notifications being displayed in the lockscreen: the padding value set was from the bottom of the screen, but the notification stack bottom was a bit higher. By removing this value from the padding, more space can be used. In computeMaxKeyguardNotifications, it seems there was an additional minimumPadding applied (from mClockPositionAlgorithm.getMinStackScrollerPadding()). However, the stack already has the padding at the top. + Added some debug lines, to make debugging of this easier in the future. + Renamed some setters adding "forDebug" suffix, as it was confusing to understand if they were doing anything useful. It turned out some properties are set to NotificationStackLayout only to draw debugging lines. Bug: 222482219 Test: atest NotificationStackSizeCalculator && atest com.android.systemui.statusbar.notification && manual QA request Change-Id: I7d972398ea39602259d6b5efa17672daf8da03c4 --- .../stack/NotificationStackScrollLayout.java | 86 +++--- ...tificationStackScrollLayoutController.java | 23 +- .../stack/NotificationStackSizeCalculator.kt | 261 +++++++++++++++++ .../NotificationPanelViewController.java | 152 ++-------- ...cationStackScrollLayoutControllerTest.java | 4 +- .../NotificationStackScrollLayoutTest.java | 4 +- .../NotificationStackSizeCalculatorTest.kt | 266 ++++++++++++++++++ .../NotificationPanelViewControllerTest.java | 17 +- 8 files changed, 622 insertions(+), 191 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt 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 ade95a8cbfe75..c89f4d7978197 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 @@ -217,6 +217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private HashSet mFromMoreCardAdditions = new HashSet<>(); private ArrayList mAnimationEvents = new ArrayList<>(); private ArrayList mSwipedOutViews = new ArrayList<>(); + private NotificationStackSizeCalculator mNotificationStackSizeCalculator; private final StackStateAnimator mStateAnimator = new StackStateAnimator(this); private boolean mAnimationsEnabled; private boolean mChangePositionInProgress; @@ -415,6 +416,7 @@ 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(); @@ -757,31 +759,34 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } else { mDebugTextUsedYPositions.clear(); } - int y = mTopPadding; - drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding = "+y); + int y = 0; + drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y); + + y = mTopPadding; + drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding = " + y); y = getLayoutHeight(); - drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = "+y); + drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = " + y); y = (int) mMaxLayoutHeight; - drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = "+y); + drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); if (mKeyguardBottomPadding >= 0) { y = getHeight() - (int) mKeyguardBottomPadding; drawDebugInfo(canvas, y, Color.GRAY, - /* label= */ "getHeight() - mKeyguardBottomPadding = "+y); + /* label= */ "getHeight() - mKeyguardBottomPadding = " + y); } y = getHeight() - getEmptyBottomMargin(); drawDebugInfo(canvas, y, Color.GREEN, - /* label= */ "getHeight() - getEmptyBottomMargin() = "+y); + /* label= */ "getHeight() - getEmptyBottomMargin() = " + y); y = (int) (mAmbientState.getStackY()); - drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = "+y); + drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight()); drawDebugInfo(canvas, y, Color.BLUE, - /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = "+y); + /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y); y = (int) mAmbientState.getStackY() + mContentHeight; drawDebugInfo(canvas, y, Color.MAGENTA, @@ -790,6 +795,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = (int) mAmbientState.getStackY() + mIntrinsicContentHeight; 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); } private void drawDebugInfo(Canvas canvas, int y, int color, String label) { @@ -956,13 +965,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } private void reinitView() { - initView(getContext(), mSwipeHelper); + initView(getContext(), mSwipeHelper, mNotificationStackSizeCalculator); } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void initView(Context context, NotificationSwipeHelper swipeHelper) { + void initView(Context context, NotificationSwipeHelper swipeHelper, + NotificationStackSizeCalculator notificationStackSizeCalculator) { mScroller = new OverScroller(getContext()); mSwipeHelper = swipeHelper; + mNotificationStackSizeCalculator = notificationStackSizeCalculator; setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setClipChildren(false); @@ -2248,48 +2259,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; - int height = (int) scrimTopPadding; - float previousPaddingRequest = mPaddingBetweenElements; - int numShownItems = 0; - int numShownNotifs = 0; - boolean finish = false; - int maxDisplayedNotifications = mMaxDisplayedNotifications; - ExpandableView previousView = null; - - for (int i = 0; i < getChildCount(); i++) { - ExpandableView expandableView = (ExpandableView) getChildAt(i); - boolean footerViewOnLockScreen = expandableView == mFooterView && onKeyguard(); - - if (expandableView.getVisibility() != View.GONE - && !expandableView.hasNoContentHeight() && !footerViewOnLockScreen) { - - boolean limitReached = maxDisplayedNotifications != -1 - && numShownNotifs >= maxDisplayedNotifications; - final float viewHeight; - if (limitReached) { - viewHeight = mShelf.getIntrinsicHeight(); - finish = true; - } else { - viewHeight = expandableView.getIntrinsicHeight(); - } - if (height != 0) { - height += mPaddingBetweenElements; - } - float gapHeight = calculateGapHeight(previousView, expandableView, numShownNotifs); - height += gapHeight; - height += viewHeight; - - numShownItems++; - if (viewHeight > 0 || !(expandableView instanceof MediaContainerView)) { - // Only count the media as a notification if it has a positive height. - numShownNotifs++; - } - previousView = expandableView; - if (finish) { - break; - } - } - } + final int height = + (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( + /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, + mShelf != null ? mShelf.getIntrinsicHeight() : 0); mIntrinsicContentHeight = height; // The topPadding can be bigger than the regular padding when qs is expanded, in that @@ -4932,6 +4905,15 @@ 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; 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 6bbecc8438bc3..d98f8a77cc8d8 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 @@ -181,6 +181,7 @@ public class NotificationStackScrollLayoutController { private final SectionHeaderController mSilentHeaderController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final InteractionJankMonitor mJankMonitor; + private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; private final StackStateLogger mStackStateLogger; private final NotificationStackScrollLogger mLogger; @@ -307,6 +308,7 @@ public class NotificationStackScrollLayoutController { R.dimen.lockscreen_shade_notification_movement); mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize( R.dimen.lockscreen_shade_qs_transition_distance); + mNotificationStackSizeCalculator.updateResources(); } private final StatusBarStateController.StateListener mStateListener = @@ -662,7 +664,8 @@ public class NotificationStackScrollLayoutController { ShadeController shadeController, InteractionJankMonitor jankMonitor, StackStateLogger stackLogger, - NotificationStackScrollLogger logger) { + NotificationStackScrollLogger logger, + NotificationStackSizeCalculator notificationStackSizeCalculator) { mStackStateLogger = stackLogger; mLogger = logger; mAllowLongPress = allowLongPress; @@ -688,6 +691,7 @@ public class NotificationStackScrollLayoutController { mCentralSurfaces = centralSurfaces; mScrimController = scrimController; mJankMonitor = jankMonitor; + mNotificationStackSizeCalculator = notificationStackSizeCalculator; groupManager.registerGroupExpansionChangeListener( (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded)); legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() { @@ -758,7 +762,7 @@ public class NotificationStackScrollLayoutController { }); } - mView.initView(mView.getContext(), mSwipeHelper); + mView.initView(mView.getContext(), mSwipeHelper, mNotificationStackSizeCalculator); mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled()); mKeyguardBypassController .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled); @@ -907,6 +911,13 @@ public class NotificationStackScrollLayoutController { return mView.getTop(); } + /** + * @return the bottom of the view. + */ + public int getBottom() { + return mView.getBottom(); + } + public float getTranslationX() { return mView.getTranslationX(); } @@ -1296,10 +1307,16 @@ public class NotificationStackScrollLayoutController { * appear on the keyguard. * Setting a negative number will disable rendering this line. */ - public void setKeyguardBottomPadding(float keyguardBottomPadding) { + public void setKeyguardBottomPaddingForDebug(float keyguardBottomPadding) { 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, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt new file mode 100644 index 0000000000000..3f97155242b4a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack + +import android.content.res.Resources +import android.util.Log +import android.view.View.GONE +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.ExpandableView +import com.android.systemui.util.children +import javax.inject.Inject +import kotlin.math.max +import kotlin.properties.Delegates.notNull + +private const val TAG = "NotificationStackSizeCalculator" +private const val DEBUG = false + +/** Calculates number of notifications to display and the height of the notification stack. */ +@SysUISingleton +class NotificationStackSizeCalculator +@Inject +constructor( + private val groupManager: NotificationGroupManagerLegacy, + private val lockscreenUserManager: NotificationLockscreenUserManager, + private val statusBarStateController: SysuiStatusBarStateController, + @Main private val resources: Resources +) { + + /** + * 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 + * (considering the overflow shelf is not displayed in this case), then all notifications are + * shown. + */ + private var maxKeyguardNotifications by notNull() + + /** + * Minimum space between two notifications. There might be more space, see [calculateGapHeight]. + */ + private var notificationPadding by notNull() + + init { + updateResources() + } + + /** + * Given the [availableSpace] 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. + */ + fun computeMaxKeyguardNotifications( + stack: NotificationStackScrollLayout, + availableSpace: Float, + shelfHeight: Float + ): Int { + log { + "computeMaxKeyguardNotifications(" + + "availableSpace=$availableSpace shelfHeight=$shelfHeight)" + } + + val children: Sequence = 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() + showableRows.forEachIndexed { i, current -> + val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) + previous = current + log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace" } + + if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) { + count += 1 + remainingSpace -= spaceNeeded + } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) { + log { "Showing all notifications. Shelf is not be 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 { + log { + "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}" + } + return count + } + } + log { "All fit. Returning $count" } + return count + } + + /** + * Given the [maxNotifications] 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 shelfHeight height of the shelf. It might be zero. + * + * @return height of the stack, including shelf height, if needed. + */ + fun computeHeight( + stack: NotificationStackScrollLayout, + maxNotifications: Int, + shelfHeight: Float + ): Float { + val children: Sequence = stack.childrenSequence + val maxNotificationsArg = infiniteIfNegative(maxNotifications) + 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 + } else { + height += shelfHeight + log { "returning height with shelf -> $height" } + return height + } + previous = current + } + log { "Returning height without shelf -> $height" } + return height + } + + fun updateResources() { + maxKeyguardNotifications = + infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count)) + + notificationPadding = + max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) + } + + private val NotificationStackScrollLayout.childrenSequence: Sequence + get() = children.map { it as ExpandableView } + + private fun onLockscreen() = statusBarStateController.state == KEYGUARD + + private fun ExpandableView.spaceNeeded( + visibleIndex: Int, + previousView: ExpandableView?, + stack: NotificationStackScrollLayout, + onLockscreen: Boolean + ): Float { + assert(isShowable(onLockscreen)) + var size = + if (onLockscreen) { + getMinHeight(/* ignoreTemporaryStates= */ true).toFloat() + } else { + intrinsicHeight.toFloat() + } + if (visibleIndex != 0) { + size += notificationPadding + } + size += calculateGapHeight(stack, previousView, visibleIndex) + return size + } + + private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean { + if (visibility == GONE || hasNoContentHeight()) return false + if (onLockscreen) { + when (this) { + is ExpandableNotificationRow -> { + if (isSummaryOfSuppressedGroup() || !canShowViewOnLockscreen() || isRemoved) { + return false + } + } + is MediaContainerView -> if (intrinsicHeight == 0) return false + else -> return false + } + } + return true + } + + private fun ExpandableView.calculateGapHeight( + stack: NotificationStackScrollLayout, + previous: ExpandableView?, + visibleIndex: Int + ) = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex) + + private fun ExpandableNotificationRow.isSummaryOfSuppressedGroup() = + groupManager.isSummaryOfSuppressedGroup(entry.sbn) + + /** + * Can a view be shown on the lockscreen when calculating the number of allowed notifications to + * show? + * + * @return `true` if it can be shown. + */ + private fun ExpandableView.canShowViewOnLockscreen(): Boolean { + if (hasNoContentHeight()) { + return false + } + if (this is ExpandableNotificationRow && !canShowRowOnLockscreen()) { + return false + } else if (visibility == GONE) { + return false + } + return true + } + + /** + * Can a row be shown on the lockscreen when calculating the number of allowed notifications to + * show? + * + * @return true if it can be shown + */ + private fun ExpandableNotificationRow.canShowRowOnLockscreen(): Boolean { + if (isSummaryOfSuppressedGroup()) { + return false + } + if (!lockscreenUserManager.shouldShowOnKeyguard(entry)) { + return false + } + return !isRemoved + } + + private fun log(s: () -> String) { + if (DEBUG) { + Log.d(TAG, s()) + } + } + + /** Returns infinite when [v] is negative. Useful in case a resource doesn't limit when -1. */ + private fun infiniteIfNegative(v: Int): Int = + if (v < 0) { + Int.MAX_VALUE + } else { + v + } +} 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 1891ab017ef87..5746ffb6debe7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import static android.view.View.GONE; import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; @@ -144,7 +143,6 @@ 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.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -165,17 +163,16 @@ import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; 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.AnimationProperties; -import com.android.systemui.statusbar.notification.stack.MediaContainerView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; @@ -312,9 +309,6 @@ public class NotificationPanelViewController extends PanelViewController { private final ControlsComponent mControlsComponent; private final NotificationRemoteInputManager mRemoteInputManager; - // 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 final TapAgainViewController mTapAgainViewController; private final SplitShadeHeaderController mSplitShadeHeaderController; @@ -323,6 +317,8 @@ 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; @@ -446,8 +442,6 @@ public class NotificationPanelViewController extends PanelViewController { setHeadsUpAnimatingAway(false); updatePanelExpansionAndVisibility(); }; - // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager - private NotificationGroupManagerLegacy mGroupManager; private boolean mShowIconsWhenExpanded; private int mIndicationBottomPadding; private int mAmbientIndicationBottomPadding; @@ -509,7 +503,6 @@ public class NotificationPanelViewController extends PanelViewController { private final NotificationEntryManager mEntryManager; private final CommandQueue mCommandQueue; - private final NotificationLockscreenUserManager mLockscreenUserManager; private final UserManager mUserManager; private final MediaDataManager mMediaDataManager; private final SysUiState mSysUiState; @@ -651,6 +644,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationPanelUnfoldAnimationController; private final NotificationListContainer mNotificationListContainer; + private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -695,7 +689,6 @@ public class NotificationPanelViewController extends PanelViewController { DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, - NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, @@ -721,7 +714,6 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, - NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, ScrimController scrimController, @@ -753,7 +745,8 @@ public class NotificationPanelViewController extends PanelViewController { SysUiState sysUiState, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationListContainer notificationListContainer, - PanelEventsEmitter panelEventsEmitter) { + PanelEventsEmitter panelEventsEmitter, + NotificationStackSizeCalculator notificationStackSizeCalculator) { super(view, falsingManager, dozeLog, @@ -785,9 +778,9 @@ public class NotificationPanelViewController extends PanelViewController { mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mNotificationsQSContainerController = notificationsQSContainerController; mNotificationListContainer = notificationListContainer; + mNotificationStackSizeCalculator = notificationStackSizeCalculator; mNotificationsQSContainerController.init(); mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; - mGroupManager = groupManager; mNotificationIconAreaController = notificationIconAreaController; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; @@ -849,7 +842,6 @@ public class NotificationPanelViewController extends PanelViewController { }); mBottomAreaShadeAlphaAnimator.setDuration(160); mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); - mLockscreenUserManager = notificationLockscreenUserManager; mEntryManager = notificationEntryManager; mConversationNotificationManager = conversationNotificationManager; mAuthController = authController; @@ -874,7 +866,6 @@ public class NotificationPanelViewController extends PanelViewController { mView.getOverlay().add(new DebugDrawable()); } - mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition()); mNotificationPanelUnfoldAnimationController = unfoldComponent.map( SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); @@ -1238,12 +1229,14 @@ public class NotificationPanelViewController extends PanelViewController { if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) { mNotificationStackScrollLayoutController.setMaxDisplayedNotifications( mMaxAllowedKeyguardNotifications); - mNotificationStackScrollLayoutController.setKeyguardBottomPadding( + mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug( mKeyguardNotificationBottomPadding); + mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug( + mKeyguardNotificationAvailableSpace); } else { // no max when not on the keyguard mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); - mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f); + mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(-1f); } } @@ -1454,127 +1447,38 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { - float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(); int notificationPadding = Math.max( 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); - float shelfSize = + float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); + float shelfHeight = mNotificationShelfController.getVisibility() == View.GONE ? 0 : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; + // Padding to add to the bottom of the stack to keep a minimum distance from the top of + // the lock icon. float lockIconPadding = 0; if (mLockIconViewController.getTop() != 0) { - lockIconPadding = mCentralSurfaces.getDisplayHeight() - mLockIconViewController.getTop() - + mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); + final float lockIconTopWithPadding = mLockIconViewController.getTop() + - mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); + lockIconPadding = mNotificationStackScrollLayoutController.getBottom() + - lockIconTopWithPadding; } - float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); - bottomPadding = Math.max(lockIconPadding, bottomPadding); + float bottomPadding = Math.max(lockIconPadding, + Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)); mKeyguardNotificationBottomPadding = bottomPadding; float availableSpace = mNotificationStackScrollLayoutController.getHeight() - - minPadding - - shelfSize + - topPadding + - shelfHeight - bottomPadding; + mKeyguardNotificationAvailableSpace = availableSpace; - int count = 0; - ExpandableView previousView = null; - for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { - ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i); - if (child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - boolean suppressedSummary = mGroupManager != null - && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn()); - if (suppressedSummary) { - continue; - } - if (!canShowViewOnLockscreen(child)) { - continue; - } - if (row.isRemoved()) { - continue; - } - } else if (child instanceof MediaContainerView) { - if (child.getVisibility() == GONE) { - continue; - } - if (child.getIntrinsicHeight() == 0) { - continue; - } - } else { - continue; - } - availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */); - availableSpace -= count == 0 ? 0 : notificationPadding; - availableSpace -= mNotificationStackScrollLayoutController - .calculateGapHeight(previousView, child, count); - previousView = child; - if (availableSpace >= 0 - && (mMaxKeyguardNotifications == -1 || count < mMaxKeyguardNotifications)) { - count++; - } else if (availableSpace > -shelfSize) { - // if we are exactly the last view, then we can show us still! - int childCount = mNotificationStackScrollLayoutController.getChildCount(); - for (int j = i + 1; j < childCount; j++) { - ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j); - if (view instanceof ExpandableNotificationRow - && canShowViewOnLockscreen(view)) { - return count; - } - } - count++; - return count; - } else { - return count; - } - } - return count; - } - - /** - * Can a view be shown on the lockscreen when calculating the number of allowed notifications - * to show? - * - * @param child the view in question - * @return true if it can be shown - */ - private boolean canShowViewOnLockscreen(ExpandableView child) { - if (child.hasNoContentHeight()) { - return false; - } - if (child instanceof ExpandableNotificationRow && - !canShowRowOnLockscreen((ExpandableNotificationRow) child)) { - return false; - } else if (child.getVisibility() == GONE) { - // ENRs can be gone and count because their visibility is only set after - // this calculation, but all other views should be up to date - return false; - } - return true; - } - - /** - * Can a row be shown on the lockscreen when calculating the number of allowed notifications - * to show? - * - * @param row the row in question - * @return true if it can be shown - */ - private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) { - boolean suppressedSummary = - mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup( - row.getEntry().getSbn()); - if (suppressedSummary) { - return false; - } - if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { - return false; - } - if (row.isRemoved()) { - return false; - } - return true; + return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications( + mNotificationStackScrollLayoutController.getView(), availableSpace, + shelfHeight); } private void updateClock() { @@ -4883,6 +4787,8 @@ public class NotificationPanelViewController extends PanelViewController { "calculateNotificationsTopPadding()"); drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY, "mClockPositionResult.clockY"); + drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY, + "mLockIconViewController.getTop()"); mDebugPaint.setColor(Color.CYAN); canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index bf16e0ab39c65..c9de60806b664 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -134,6 +134,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private InteractionJankMonitor mJankMonitor; @Mock private StackStateLogger mStackLogger; @Mock private NotificationStackScrollLogger mLogger; + @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Captor private ArgumentCaptor mStateListenerArgumentCaptor; @@ -186,7 +187,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mShadeController, mJankMonitor, mStackLogger, - mLogger + mLogger, + mNotificationStackSizeCalculator ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 56541f3ceb6f8..7a92b96f40db2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -103,6 +103,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController; @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock private NotificationShelf mNotificationShelf; + @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Before @UiThreadTest @@ -138,7 +139,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null); - mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper); + mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper, + mNotificationStackSizeCalculator); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setCentralSurfaces(mCentralSurfaces); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt new file mode 100644 index 0000000000000..d1848e38ca06d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack + +import android.service.notification.StatusBarNotification +import android.testing.AndroidTestingRunner +import android.view.View.VISIBLE +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy +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.nullable +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class NotificationStackSizeCalculatorTest : SysuiTestCase() { + + @Mock private lateinit var groupManager: NotificationGroupManagerLegacy + + @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager + + @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController + + @Mock private lateinit var stackLayout: NotificationStackScrollLayout + + private val testableResources = mContext.getOrCreateTestableResources() + + private lateinit var sizeCalculator: NotificationStackSizeCalculator + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) + .thenReturn(GAP_HEIGHT) + whenever(groupManager.isSummaryOfSuppressedGroup(any())).thenReturn(false) + with(testableResources) { + addOverride(R.integer.keyguard_max_notification_count, -1) + addOverride(R.dimen.notification_divider_height, NOTIFICATION_PADDING.toInt()) + } + + sizeCalculator = + NotificationStackSizeCalculator( + groupManager = groupManager, + lockscreenUserManager = notificationLockscreenUserManager, + statusBarStateController = sysuiStatusBarStateController, + testableResources.resources) + } + + @Test + fun computeMaxKeyguardNotifications_zeroSpace_returnZero() { + val rows = listOf(createMockRow(height = ROW_HEIGHT, visibleOnLockscreen = true)) + + val maxNotifications = + computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f) + + assertThat(maxNotifications).isEqualTo(0) + } + + @Test + fun computeMaxKeyguardNotifications_infiniteSpace_returnsAll() { + val numberOfRows = 30 + val rows = createLockscreenRows(numberOfRows) + + val maxNotifications = computeMaxKeyguardNotifications(rows, Float.MAX_VALUE) + + assertThat(maxNotifications).isEqualTo(numberOfRows) + } + + @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, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + 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 + NOTIFICATION_PADDING + val spaceForOne = totalSpaceForEachRow + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = + computeMaxKeyguardNotifications( + rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) + + assertThat(maxNotifications).isEqualTo(1) + } + + @Test + fun computeMaxKeyguardNotifications_invisibleOnLockscreen_returnsZero() { + val rows = listOf(createMockRow(visibleOnLockscreen = false)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, Float.MAX_VALUE) + + assertThat(maxNotifications).isEqualTo(0) + } + + @Test + fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { + val rowHeight = ROW_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + val spaceForTwo = totalSpaceForEachRow * 2 + NOTIFICATION_PADDING + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f) + + assertThat(maxNotifications).isEqualTo(2) + } + + @Test + fun computeHeight_returnsLessThanAvailableSpaceUsedToCalculateMaxNotifications() { + val rowHeight = ROW_HEIGHT + val shelfHeight = SHELF_HEIGHT + val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + NOTIFICATION_PADDING + val availableSpace = totalSpaceForEachRow * 2 + val rows = + listOf( + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true), + createMockRow(rowHeight, visibleOnLockscreen = true)) + + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) + assertThat(maxNotifications).isEqualTo(2) + + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) + assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) + } + + @Test + fun computeHeight_allInvisibleToLockscreen_NotInLockscreen_returnsHigherThanZero() { + setOnLockscreen(false) + val rowHeight = 10f + setupChildren(listOf(createMockRow(rowHeight, visibleOnLockscreen = false))) + + val height = + sizeCalculator.computeHeight( + stackLayout, maxNotifications = Int.MAX_VALUE, SHELF_HEIGHT) + + assertThat(height).isGreaterThan(rowHeight) + } + + @Test + fun computeHeight_allInvisibleToLockscreen_onLockscreen_returnsZero() { + setOnLockscreen(true) + setupChildren(listOf(createMockRow(visibleOnLockscreen = false))) + + val height = + sizeCalculator.computeHeight( + stackLayout, maxNotifications = Int.MAX_VALUE, SHELF_HEIGHT) + + assertThat(height).isEqualTo(0) + } + + private fun computeMaxKeyguardNotifications( + rows: List, + availableSpace: Float, + shelfHeight: Float = SHELF_HEIGHT + ): Int { + setupChildren(rows) + return sizeCalculator.computeMaxKeyguardNotifications( + stackLayout, availableSpace, shelfHeight) + } + + private fun setupChildren(children: List) { + whenever(stackLayout.getChildAt(any())).thenAnswer { invocation -> + val inx = invocation.getArgument(0) + return@thenAnswer children[inx] + } + whenever(stackLayout.childCount).thenReturn(children.size) + } + + private fun createLockscreenRows(number: Int): List = + (1..number).map { createMockRow(visibleOnLockscreen = true) }.toList() + + private fun createMockRow( + height: Float = ROW_HEIGHT, + visibleOnLockscreen: Boolean = true, + isRemoved: Boolean = false, + visibility: Int = VISIBLE, + summaryOfSuppressed: Boolean = false + ): ExpandableNotificationRow { + val row = mock(ExpandableNotificationRow::class.java) + val entry = mock(NotificationEntry::class.java) + val sbn = mock(StatusBarNotification::class.java) + whenever(entry.sbn).thenReturn(sbn) + whenever(row.entry).thenReturn(entry) + whenever(row.isRemoved).thenReturn(isRemoved) + whenever(row.visibility).thenReturn(visibility) + whenever(notificationLockscreenUserManager.shouldShowOnKeyguard(entry)) + .thenReturn(visibleOnLockscreen) + whenever(groupManager.isSummaryOfSuppressedGroup(sbn)).thenReturn(summaryOfSuppressed) + whenever(row.getMinHeight(any())).thenReturn(height.toInt()) + whenever(row.intrinsicHeight).thenReturn(height.toInt()) + return row + } + + private fun setOnLockscreen(onLockscreen: Boolean) { + whenever(sysuiStatusBarStateController.state) + .thenReturn( + if (onLockscreen) { + KEYGUARD + } else { + SHADE + }) + } + + /** Default dimensions for tests that don't overwrite them. */ + companion object { + const val GAP_HEIGHT = 12f + const val NOTIFICATION_PADDING = 3f + const val SHELF_HEIGHT = 14f + const val ROW_HEIGHT = SHELF_HEIGHT * 3 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 4bac08ea536b2..06b20380c0ab2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -104,7 +104,6 @@ import com.android.systemui.statusbar.CommandQueue; 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.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -119,12 +118,12 @@ import com.android.systemui.statusbar.notification.ConversationNotificationManag import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; @@ -174,8 +173,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private NotificationShelfController mNotificationShelfController; @Mock - private NotificationGroupManagerLegacy mGroupManager; - @Mock private KeyguardStatusBarView mKeyguardStatusBar; @Mock private KeyguardUserSwitcherView mUserSwitcherView; @@ -200,10 +197,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock - private ShadeController mShadeController; - @Mock - private NotificationLockscreenUserManager mNotificationLockscreenUserManager; - @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; @@ -336,6 +329,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { private SysUiState mSysUiState; @Mock private NotificationListContainer mNotificationListContainer; + @Mock + private NotificationStackSizeCalculator mNotificationStackSizeCalculator; private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter; private Optional mSysUIUnfoldComponent = Optional.empty(); private SysuiStatusBarStateController mStatusBarStateController; @@ -466,7 +461,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mFeatureFlags, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, mFalsingManager, new FalsingCollectorFake(), - mNotificationLockscreenUserManager, mNotificationEntryManager, + mNotificationEntryManager, mKeyguardStateController, mStatusBarStateController, mStatusBarWindowStateController, @@ -484,7 +479,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mKeyguardUserSwitcherComponentFactory, mKeyguardStatusBarViewComponentFactory, mLockscreenShadeTransitionController, - mGroupManager, mNotificationAreaController, mAuthController, mScrimController, @@ -516,7 +510,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mSysUiState, mKeyguardUnlockAnimationController, mNotificationListContainer, - mPanelEventsEmitter); + mPanelEventsEmitter, + mNotificationStackSizeCalculator); mNotificationPanelViewController.initDependencies( mCentralSurfaces, () -> {},