summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt261
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java152
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt266
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java17
8 files changed, 622 insertions, 191 deletions
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 ade95a8cbfe7..c89f4d797819 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<View> mFromMoreCardAdditions = new HashSet<>();
private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
private ArrayList<View> 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 6bbecc8438bc..d98f8a77cc8d 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 000000000000..3f97155242b4
--- /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<Int>()
+
+ /**
+ * Minimum space between two notifications. There might be more space, see [calculateGapHeight].
+ */
+ private var notificationPadding by notNull<Int>()
+
+ 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<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()
+ 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<ExpandableView> = 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<ExpandableView>
+ 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 1891ab017ef8..5746ffb6debe 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 bf16e0ab39c6..c9de60806b66 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<StatusBarStateController.StateListener> 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 56541f3ceb6f..7a92b96f40db 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 000000000000..d1848e38ca06
--- /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<ExpandableView>,
+ availableSpace: Float,
+ shelfHeight: Float = SHELF_HEIGHT
+ ): Int {
+ setupChildren(rows)
+ return sizeCalculator.computeMaxKeyguardNotifications(
+ stackLayout, availableSpace, shelfHeight)
+ }
+
+ private fun setupChildren(children: List<ExpandableView>) {
+ whenever(stackLayout.getChildAt(any())).thenAnswer { invocation ->
+ val inx = invocation.getArgument<Int>(0)
+ return@thenAnswer children[inx]
+ }
+ whenever(stackLayout.childCount).thenReturn(children.size)
+ }
+
+ private fun createLockscreenRows(number: Int): List<ExpandableNotificationRow> =
+ (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 4bac08ea536b..06b20380c0ab 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<SysUIUnfoldComponent> 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,
() -> {},