Merge "Fix NotificationStack size calculation" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-18 16:31:33 +00:00
committed by Android (Google) Code Review
8 changed files with 622 additions and 191 deletions

View File

@@ -217,6 +217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private HashSet<View> mFromMoreCardAdditions = new HashSet<>(); private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>(); private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
private ArrayList<View> mSwipedOutViews = new ArrayList<>(); private ArrayList<View> mSwipedOutViews = new ArrayList<>();
private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateAnimator mStateAnimator = new StackStateAnimator(this); private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
private boolean mAnimationsEnabled; private boolean mAnimationsEnabled;
private boolean mChangePositionInProgress; private boolean mChangePositionInProgress;
@@ -415,6 +416,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
private NotificationShelf mShelf; private NotificationShelf mShelf;
private int mMaxDisplayedNotifications = -1; private int mMaxDisplayedNotifications = -1;
private float mKeyguardBottomPadding = -1; private float mKeyguardBottomPadding = -1;
private float mKeyguardNotificationAvailableSpace = -1;
@VisibleForTesting int mStatusBarHeight; @VisibleForTesting int mStatusBarHeight;
private int mMinInteractionHeight; private int mMinInteractionHeight;
private final Rect mClipRect = new Rect(); private final Rect mClipRect = new Rect();
@@ -757,31 +759,34 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
} else { } else {
mDebugTextUsedYPositions.clear(); mDebugTextUsedYPositions.clear();
} }
int y = mTopPadding; int y = 0;
drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding = "+y); drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y);
y = mTopPadding;
drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding = " + y);
y = getLayoutHeight(); y = getLayoutHeight();
drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = "+y); drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight() = " + y);
y = (int) mMaxLayoutHeight; y = (int) mMaxLayoutHeight;
drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = "+y); drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y);
if (mKeyguardBottomPadding >= 0) { if (mKeyguardBottomPadding >= 0) {
y = getHeight() - (int) mKeyguardBottomPadding; y = getHeight() - (int) mKeyguardBottomPadding;
drawDebugInfo(canvas, y, Color.GRAY, drawDebugInfo(canvas, y, Color.GRAY,
/* label= */ "getHeight() - mKeyguardBottomPadding = "+y); /* label= */ "getHeight() - mKeyguardBottomPadding = " + y);
} }
y = getHeight() - getEmptyBottomMargin(); y = getHeight() - getEmptyBottomMargin();
drawDebugInfo(canvas, y, Color.GREEN, drawDebugInfo(canvas, y, Color.GREEN,
/* label= */ "getHeight() - getEmptyBottomMargin() = "+y); /* label= */ "getHeight() - getEmptyBottomMargin() = " + y);
y = (int) (mAmbientState.getStackY()); 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()); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
drawDebugInfo(canvas, y, Color.BLUE, drawDebugInfo(canvas, y, Color.BLUE,
/* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = "+y); /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
y = (int) mAmbientState.getStackY() + mContentHeight; y = (int) mAmbientState.getStackY() + mContentHeight;
drawDebugInfo(canvas, y, Color.MAGENTA, drawDebugInfo(canvas, y, Color.MAGENTA,
@@ -790,6 +795,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
y = (int) mAmbientState.getStackY() + mIntrinsicContentHeight; y = (int) mAmbientState.getStackY() + mIntrinsicContentHeight;
drawDebugInfo(canvas, y, Color.YELLOW, drawDebugInfo(canvas, y, Color.YELLOW,
/* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); /* 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) { 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() { private void reinitView() {
initView(getContext(), mSwipeHelper); initView(getContext(), mSwipeHelper, mNotificationStackSizeCalculator);
} }
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void initView(Context context, NotificationSwipeHelper swipeHelper) { void initView(Context context, NotificationSwipeHelper swipeHelper,
NotificationStackSizeCalculator notificationStackSizeCalculator) {
mScroller = new OverScroller(getContext()); mScroller = new OverScroller(getContext());
mSwipeHelper = swipeHelper; mSwipeHelper = swipeHelper;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
setClipChildren(false); setClipChildren(false);
@@ -2248,48 +2259,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateContentHeight() { private void updateContentHeight() {
final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
int height = (int) scrimTopPadding; final int height =
float previousPaddingRequest = mPaddingBetweenElements; (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
int numShownItems = 0; /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
int numShownNotifs = 0; mShelf != null ? mShelf.getIntrinsicHeight() : 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;
}
}
}
mIntrinsicContentHeight = height; mIntrinsicContentHeight = height;
// The topPadding can be bigger than the regular padding when qs is expanded, in that // 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; 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) @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
mShouldShowShelfOnly = shouldShowShelfOnly; mShouldShowShelfOnly = shouldShowShelfOnly;

View File

@@ -181,6 +181,7 @@ public class NotificationStackScrollLayoutController {
private final SectionHeaderController mSilentHeaderController; private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final InteractionJankMonitor mJankMonitor; private final InteractionJankMonitor mJankMonitor;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateLogger mStackStateLogger; private final StackStateLogger mStackStateLogger;
private final NotificationStackScrollLogger mLogger; private final NotificationStackScrollLogger mLogger;
@@ -307,6 +308,7 @@ public class NotificationStackScrollLayoutController {
R.dimen.lockscreen_shade_notification_movement); R.dimen.lockscreen_shade_notification_movement);
mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize( mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
R.dimen.lockscreen_shade_qs_transition_distance); R.dimen.lockscreen_shade_qs_transition_distance);
mNotificationStackSizeCalculator.updateResources();
} }
private final StatusBarStateController.StateListener mStateListener = private final StatusBarStateController.StateListener mStateListener =
@@ -662,7 +664,8 @@ public class NotificationStackScrollLayoutController {
ShadeController shadeController, ShadeController shadeController,
InteractionJankMonitor jankMonitor, InteractionJankMonitor jankMonitor,
StackStateLogger stackLogger, StackStateLogger stackLogger,
NotificationStackScrollLogger logger) { NotificationStackScrollLogger logger,
NotificationStackSizeCalculator notificationStackSizeCalculator) {
mStackStateLogger = stackLogger; mStackStateLogger = stackLogger;
mLogger = logger; mLogger = logger;
mAllowLongPress = allowLongPress; mAllowLongPress = allowLongPress;
@@ -688,6 +691,7 @@ public class NotificationStackScrollLayoutController {
mCentralSurfaces = centralSurfaces; mCentralSurfaces = centralSurfaces;
mScrimController = scrimController; mScrimController = scrimController;
mJankMonitor = jankMonitor; mJankMonitor = jankMonitor;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
groupManager.registerGroupExpansionChangeListener( groupManager.registerGroupExpansionChangeListener(
(changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded)); (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() { 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()); mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
mKeyguardBypassController mKeyguardBypassController
.registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled); .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
@@ -907,6 +911,13 @@ public class NotificationStackScrollLayoutController {
return mView.getTop(); return mView.getTop();
} }
/**
* @return the bottom of the view.
*/
public int getBottom() {
return mView.getBottom();
}
public float getTranslationX() { public float getTranslationX() {
return mView.getTranslationX(); return mView.getTranslationX();
} }
@@ -1296,10 +1307,16 @@ public class NotificationStackScrollLayoutController {
* appear on the keyguard. * appear on the keyguard.
* Setting a negative number will disable rendering this line. * Setting a negative number will disable rendering this line.
*/ */
public void setKeyguardBottomPadding(float keyguardBottomPadding) { public void setKeyguardBottomPaddingForDebug(float keyguardBottomPadding) {
mView.setKeyguardBottomPadding(keyguardBottomPadding); mView.setKeyguardBottomPadding(keyguardBottomPadding);
} }
/** For debugging only. */
public void mKeyguardNotificationAvailableSpaceForDebug(
float keyguardNotificationAvailableSpace) {
mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace);
}
public RemoteInputController.Delegate createDelegate() { public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() { return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationEntry entry, public void setRemoteInputActive(NotificationEntry entry,

View File

@@ -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
}
}

View File

@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 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.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; 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.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController; 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.ViewGroupFadeHelper;
import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry; 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.collection.render.ShadeViewManager;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties; 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.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 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.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
@@ -312,9 +309,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final ControlsComponent mControlsComponent; private final ControlsComponent mControlsComponent;
private final NotificationRemoteInputManager mRemoteInputManager; 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 LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final TapAgainViewController mTapAgainViewController; private final TapAgainViewController mTapAgainViewController;
private final SplitShadeHeaderController mSplitShadeHeaderController; private final SplitShadeHeaderController mSplitShadeHeaderController;
@@ -323,6 +317,8 @@ public class NotificationPanelViewController extends PanelViewController {
private boolean mShouldUseSplitNotificationShade; private boolean mShouldUseSplitNotificationShade;
// The bottom padding reserved for elements of the keyguard measuring notifications // The bottom padding reserved for elements of the keyguard measuring notifications
private float mKeyguardNotificationBottomPadding; private float mKeyguardNotificationBottomPadding;
// Space available for notifications.
private float mKeyguardNotificationAvailableSpace;
// Current max allowed keyguard notifications determined by measuring the panel // Current max allowed keyguard notifications determined by measuring the panel
private int mMaxAllowedKeyguardNotifications; private int mMaxAllowedKeyguardNotifications;
@@ -446,8 +442,6 @@ public class NotificationPanelViewController extends PanelViewController {
setHeadsUpAnimatingAway(false); setHeadsUpAnimatingAway(false);
updatePanelExpansionAndVisibility(); updatePanelExpansionAndVisibility();
}; };
// TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager
private NotificationGroupManagerLegacy mGroupManager;
private boolean mShowIconsWhenExpanded; private boolean mShowIconsWhenExpanded;
private int mIndicationBottomPadding; private int mIndicationBottomPadding;
private int mAmbientIndicationBottomPadding; private int mAmbientIndicationBottomPadding;
@@ -509,7 +503,6 @@ public class NotificationPanelViewController extends PanelViewController {
private final NotificationEntryManager mEntryManager; private final NotificationEntryManager mEntryManager;
private final CommandQueue mCommandQueue; private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final UserManager mUserManager; private final UserManager mUserManager;
private final MediaDataManager mMediaDataManager; private final MediaDataManager mMediaDataManager;
private final SysUiState mSysUiState; private final SysUiState mSysUiState;
@@ -651,6 +644,7 @@ public class NotificationPanelViewController extends PanelViewController {
mNotificationPanelUnfoldAnimationController; mNotificationPanelUnfoldAnimationController;
private final NotificationListContainer mNotificationListContainer; private final NotificationListContainer mNotificationListContainer;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override @Override
@@ -695,7 +689,6 @@ public class NotificationPanelViewController extends PanelViewController {
DynamicPrivacyController dynamicPrivacyController, DynamicPrivacyController dynamicPrivacyController,
KeyguardBypassController bypassController, FalsingManager falsingManager, KeyguardBypassController bypassController, FalsingManager falsingManager,
FalsingCollector falsingCollector, FalsingCollector falsingCollector,
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotificationEntryManager notificationEntryManager, NotificationEntryManager notificationEntryManager,
KeyguardStateController keyguardStateController, KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController, StatusBarStateController statusBarStateController,
@@ -721,7 +714,6 @@ public class NotificationPanelViewController extends PanelViewController {
KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
LockscreenShadeTransitionController lockscreenShadeTransitionController, LockscreenShadeTransitionController lockscreenShadeTransitionController,
NotificationGroupManagerLegacy groupManager,
NotificationIconAreaController notificationIconAreaController, NotificationIconAreaController notificationIconAreaController,
AuthController authController, AuthController authController,
ScrimController scrimController, ScrimController scrimController,
@@ -753,7 +745,8 @@ public class NotificationPanelViewController extends PanelViewController {
SysUiState sysUiState, SysUiState sysUiState,
KeyguardUnlockAnimationController keyguardUnlockAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController,
NotificationListContainer notificationListContainer, NotificationListContainer notificationListContainer,
PanelEventsEmitter panelEventsEmitter) { PanelEventsEmitter panelEventsEmitter,
NotificationStackSizeCalculator notificationStackSizeCalculator) {
super(view, super(view,
falsingManager, falsingManager,
dozeLog, dozeLog,
@@ -785,9 +778,9 @@ public class NotificationPanelViewController extends PanelViewController {
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationsQSContainerController = notificationsQSContainerController; mNotificationsQSContainerController = notificationsQSContainerController;
mNotificationListContainer = notificationListContainer; mNotificationListContainer = notificationListContainer;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
mNotificationsQSContainerController.init(); mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController; mNotificationIconAreaController = notificationIconAreaController;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory; mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
@@ -849,7 +842,6 @@ public class NotificationPanelViewController extends PanelViewController {
}); });
mBottomAreaShadeAlphaAnimator.setDuration(160); mBottomAreaShadeAlphaAnimator.setDuration(160);
mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mLockscreenUserManager = notificationLockscreenUserManager;
mEntryManager = notificationEntryManager; mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager; mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController; mAuthController = authController;
@@ -874,7 +866,6 @@ public class NotificationPanelViewController extends PanelViewController {
mView.getOverlay().add(new DebugDrawable()); mView.getOverlay().add(new DebugDrawable());
} }
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition()); mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
mNotificationPanelUnfoldAnimationController = unfoldComponent.map( mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
@@ -1238,12 +1229,14 @@ public class NotificationPanelViewController extends PanelViewController {
if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) { if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications( mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications); mMaxAllowedKeyguardNotifications);
mNotificationStackScrollLayoutController.setKeyguardBottomPadding( mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug(
mKeyguardNotificationBottomPadding); mKeyguardNotificationBottomPadding);
mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug(
mKeyguardNotificationAvailableSpace);
} else { } else {
// no max when not on the keyguard // no max when not on the keyguard
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); 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 * @return the maximum keyguard notifications that can fit on the screen
*/ */
private int computeMaxKeyguardNotifications() { private int computeMaxKeyguardNotifications() {
float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
int notificationPadding = Math.max( int notificationPadding = Math.max(
1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
float shelfSize = float topPadding = mNotificationStackScrollLayoutController.getTopPadding();
float shelfHeight =
mNotificationShelfController.getVisibility() == View.GONE mNotificationShelfController.getVisibility() == View.GONE
? 0 ? 0
: mNotificationShelfController.getIntrinsicHeight() + notificationPadding; : 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; float lockIconPadding = 0;
if (mLockIconViewController.getTop() != 0) { if (mLockIconViewController.getTop() != 0) {
lockIconPadding = mCentralSurfaces.getDisplayHeight() - mLockIconViewController.getTop() final float lockIconTopWithPadding = mLockIconViewController.getTop()
+ mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding); - mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding);
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- lockIconTopWithPadding;
} }
float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); float bottomPadding = Math.max(lockIconPadding,
bottomPadding = Math.max(lockIconPadding, bottomPadding); Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
mKeyguardNotificationBottomPadding = bottomPadding; mKeyguardNotificationBottomPadding = bottomPadding;
float availableSpace = float availableSpace =
mNotificationStackScrollLayoutController.getHeight() mNotificationStackScrollLayoutController.getHeight()
- minPadding - topPadding
- shelfSize - shelfHeight
- bottomPadding; - bottomPadding;
mKeyguardNotificationAvailableSpace = availableSpace;
int count = 0; return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications(
ExpandableView previousView = null; mNotificationStackScrollLayoutController.getView(), availableSpace,
for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { shelfHeight);
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;
} }
private void updateClock() { private void updateClock() {
@@ -4883,6 +4787,8 @@ public class NotificationPanelViewController extends PanelViewController {
"calculateNotificationsTopPadding()"); "calculateNotificationsTopPadding()");
drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY, drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY,
"mClockPositionResult.clockY"); "mClockPositionResult.clockY");
drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
"mLockIconViewController.getTop()");
mDebugPaint.setColor(Color.CYAN); mDebugPaint.setColor(Color.CYAN);
canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),

View File

@@ -134,6 +134,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private InteractionJankMonitor mJankMonitor; @Mock private InteractionJankMonitor mJankMonitor;
@Mock private StackStateLogger mStackLogger; @Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger; @Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Captor @Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -186,7 +187,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mShadeController, mShadeController,
mJankMonitor, mJankMonitor,
mStackLogger, mStackLogger,
mLogger mLogger,
mNotificationStackSizeCalculator
); );
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);

View File

@@ -103,6 +103,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationStackScrollLayoutController mStackScrollLayoutController; @Mock private NotificationStackScrollLayoutController mStackScrollLayoutController;
@Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Mock private NotificationShelf mNotificationShelf; @Mock private NotificationShelf mNotificationShelf;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Before @Before
@UiThreadTest @UiThreadTest
@@ -138,7 +139,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
// holds a copy of the CUT's instances of these KeyguardBypassController, so they still // 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. // refer to the CUT's member variables, not the spy's member variables.
mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null); mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null);
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper); mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
mNotificationStackSizeCalculator);
mStackScroller = spy(mStackScrollerInternal); mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelfController(notificationShelfController); mStackScroller.setShelfController(notificationShelfController);
mStackScroller.setCentralSurfaces(mCentralSurfaces); mStackScroller.setCentralSurfaces(mCentralSurfaces);

View File

@@ -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
}
}

View File

@@ -104,7 +104,6 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController; 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.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; 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.AmbientState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 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.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
@@ -174,8 +173,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock @Mock
private NotificationShelfController mNotificationShelfController; private NotificationShelfController mNotificationShelfController;
@Mock @Mock
private NotificationGroupManagerLegacy mGroupManager;
@Mock
private KeyguardStatusBarView mKeyguardStatusBar; private KeyguardStatusBarView mKeyguardStatusBar;
@Mock @Mock
private KeyguardUserSwitcherView mUserSwitcherView; private KeyguardUserSwitcherView mUserSwitcherView;
@@ -200,10 +197,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
@Mock @Mock
private DynamicPrivacyController mDynamicPrivacyController; private DynamicPrivacyController mDynamicPrivacyController;
@Mock @Mock
private ShadeController mShadeController;
@Mock
private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock
private NotificationEntryManager mNotificationEntryManager; private NotificationEntryManager mNotificationEntryManager;
@Mock @Mock
private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@@ -336,6 +329,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private SysUiState mSysUiState; private SysUiState mSysUiState;
@Mock @Mock
private NotificationListContainer mNotificationListContainer; private NotificationListContainer mNotificationListContainer;
@Mock
private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter; private NotificationPanelViewController.PanelEventsEmitter mPanelEventsEmitter;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController; private SysuiStatusBarStateController mStatusBarStateController;
@@ -466,7 +461,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mFeatureFlags, mFeatureFlags,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(), mFalsingManager, new FalsingCollectorFake(),
mNotificationLockscreenUserManager, mNotificationEntryManager, mNotificationEntryManager,
mKeyguardStateController, mKeyguardStateController,
mStatusBarStateController, mStatusBarStateController,
mStatusBarWindowStateController, mStatusBarWindowStateController,
@@ -484,7 +479,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mKeyguardUserSwitcherComponentFactory, mKeyguardUserSwitcherComponentFactory,
mKeyguardStatusBarViewComponentFactory, mKeyguardStatusBarViewComponentFactory,
mLockscreenShadeTransitionController, mLockscreenShadeTransitionController,
mGroupManager,
mNotificationAreaController, mNotificationAreaController,
mAuthController, mAuthController,
mScrimController, mScrimController,
@@ -516,7 +510,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mSysUiState, mSysUiState,
mKeyguardUnlockAnimationController, mKeyguardUnlockAnimationController,
mNotificationListContainer, mNotificationListContainer,
mPanelEventsEmitter); mPanelEventsEmitter,
mNotificationStackSizeCalculator);
mNotificationPanelViewController.initDependencies( mNotificationPanelViewController.initDependencies(
mCentralSurfaces, mCentralSurfaces,
() -> {}, () -> {},