Merge "Fix NotificationStack size calculation" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
11d05992e7
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
() -> {},
|
||||
|
||||
Reference in New Issue
Block a user