Merge "Fix Notification group dismissing animations" into tm-qpr-dev am: 6b08882da7
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20291263 Change-Id: I8605cb60bc2c4d8ffbe4e97a7d4f8b65886a9ffc Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -676,9 +676,6 @@ public final class NotificationEntry extends ListEntry {
|
|||||||
return row != null && row.areChildrenExpanded();
|
return row != null && row.areChildrenExpanded();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean keepInParent() {
|
|
||||||
return row != null && row.keepInParent();
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: probably less confusing to say "is group fully visible"
|
//TODO: probably less confusing to say "is group fully visible"
|
||||||
public boolean isGroupNotFullyVisible() {
|
public boolean isGroupNotFullyVisible() {
|
||||||
@@ -698,10 +695,6 @@ public final class NotificationEntry extends ListEntry {
|
|||||||
return row != null && row.isSummaryWithChildren();
|
return row != null && row.isSummaryWithChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeepInParent(boolean keep) {
|
|
||||||
if (row != null) row.setKeepInParent(keep);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDensityOrFontScaleChanged() {
|
public void onDensityOrFontScaleChanged() {
|
||||||
if (row != null) row.onDensityOrFontScaleChanged();
|
if (row != null) row.onDensityOrFontScaleChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,4 +55,8 @@ class MediaContainerController @Inject constructor(
|
|||||||
|
|
||||||
override val view: View
|
override val view: View
|
||||||
get() = mediaContainerView!!
|
get() = mediaContainerView!!
|
||||||
|
|
||||||
|
override fun offerToKeepInParentForAnimation(): Boolean = false
|
||||||
|
override fun removeFromParentIfKeptForAnimation(): Boolean = false
|
||||||
|
override fun resetKeepInParentForAnimation() {}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@ import java.lang.StringBuilder
|
|||||||
* below.
|
* below.
|
||||||
*/
|
*/
|
||||||
interface NodeController {
|
interface NodeController {
|
||||||
|
|
||||||
/** A string that uniquely(ish) represents the node in the tree. Used for debugging. */
|
/** A string that uniquely(ish) represents the node in the tree. Used for debugging. */
|
||||||
val nodeLabel: String
|
val nodeLabel: String
|
||||||
|
|
||||||
@@ -64,6 +65,27 @@ interface NodeController {
|
|||||||
|
|
||||||
/** Called when this view has been removed */
|
/** Called when this view has been removed */
|
||||||
fun onViewRemoved() {}
|
fun onViewRemoved() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before removing a node from its parent
|
||||||
|
*
|
||||||
|
* If returned true, the ShadeViewDiffer won't detach this row and the view system is
|
||||||
|
* responsible for ensuring the row is in eventually removed from the parent.
|
||||||
|
*
|
||||||
|
* @return false to opt out from this feature
|
||||||
|
*/
|
||||||
|
fun offerToKeepInParentForAnimation(): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called before a node is reattached. Removes the view from its parent
|
||||||
|
* if it was flagged to be kept before.
|
||||||
|
*
|
||||||
|
* @return whether it did a removal
|
||||||
|
*/
|
||||||
|
fun removeFromParentIfKeptForAnimation(): Boolean
|
||||||
|
|
||||||
|
/** Called when a node is being reattached */
|
||||||
|
fun resetKeepInParentForAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +112,7 @@ fun treeSpecToStr(tree: NodeSpec): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun treeSpecToStrHelper(tree: NodeSpec, sb: StringBuilder, indent: String) {
|
private fun treeSpecToStrHelper(tree: NodeSpec, sb: StringBuilder, indent: String) {
|
||||||
sb.append("${indent}{${tree.controller.nodeLabel}}\n")
|
sb.append("$indent{${tree.controller.nodeLabel}}\n")
|
||||||
if (tree.children.isNotEmpty()) {
|
if (tree.children.isNotEmpty()) {
|
||||||
val childIndent = "$indent "
|
val childIndent = "$indent "
|
||||||
for (child in tree.children) {
|
for (child in tree.children) {
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class RootNodeController(
|
|||||||
override val view: View
|
override val view: View
|
||||||
) : NodeController, PipelineDumpable {
|
) : NodeController, PipelineDumpable {
|
||||||
override val nodeLabel: String = "<root>"
|
override val nodeLabel: String = "<root>"
|
||||||
|
override fun offerToKeepInParentForAnimation(): Boolean = false
|
||||||
|
override fun removeFromParentIfKeptForAnimation(): Boolean = false
|
||||||
|
override fun resetKeepInParentForAnimation() {}
|
||||||
|
|
||||||
override fun getChildAt(index: Int): View? {
|
override fun getChildAt(index: Int): View? {
|
||||||
return listContainer.getContainerChildAt(index)
|
return listContainer.getContainerChildAt(index)
|
||||||
|
|||||||
@@ -100,4 +100,7 @@ internal class SectionHeaderNodeControllerImpl @Inject constructor(
|
|||||||
|
|
||||||
override val view: View
|
override val view: View
|
||||||
get() = _view!!
|
get() = _view!!
|
||||||
|
override fun offerToKeepInParentForAnimation(): Boolean = false
|
||||||
|
override fun removeFromParentIfKeptForAnimation(): Boolean = false
|
||||||
|
override fun resetKeepInParentForAnimation() {}
|
||||||
}
|
}
|
||||||
@@ -86,10 +86,10 @@ class ShadeViewDiffer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun maybeDetachChild(
|
private fun maybeDetachChild(
|
||||||
parentNode: ShadeNode,
|
parentNode: ShadeNode,
|
||||||
parentSpec: NodeSpec?,
|
parentSpec: NodeSpec?,
|
||||||
childNode: ShadeNode,
|
childNode: ShadeNode,
|
||||||
childSpec: NodeSpec?
|
childSpec: NodeSpec?
|
||||||
) {
|
) {
|
||||||
val newParentNode = childSpec?.parent?.let { getNode(it) }
|
val newParentNode = childSpec?.parent?.let { getNode(it) }
|
||||||
|
|
||||||
@@ -100,14 +100,27 @@ class ShadeViewDiffer(
|
|||||||
nodes.remove(childNode.controller)
|
nodes.remove(childNode.controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.logDetachingChild(
|
if (childCompletelyRemoved && parentSpec == null &&
|
||||||
key = childNode.label,
|
childNode.offerToKeepInParentForAnimation()) {
|
||||||
isTransfer = !childCompletelyRemoved,
|
// If both the child and the parent are being removed at the same time, then
|
||||||
isParentRemoved = parentSpec == null,
|
// keep the child attached to the parent for animation purposes
|
||||||
oldParent = parentNode.label,
|
logger.logSkipDetachingChild(
|
||||||
newParent = newParentNode?.label)
|
key = childNode.label,
|
||||||
parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved)
|
parentKey = parentNode.label,
|
||||||
childNode.parent = null
|
isTransfer = !childCompletelyRemoved,
|
||||||
|
isParentRemoved = true
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
logger.logDetachingChild(
|
||||||
|
key = childNode.label,
|
||||||
|
isTransfer = !childCompletelyRemoved,
|
||||||
|
isParentRemoved = parentSpec == null,
|
||||||
|
oldParent = parentNode.label,
|
||||||
|
newParent = newParentNode?.label
|
||||||
|
)
|
||||||
|
parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved)
|
||||||
|
childNode.parent = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +132,16 @@ class ShadeViewDiffer(
|
|||||||
val childNode = getNode(childSpec)
|
val childNode = getNode(childSpec)
|
||||||
|
|
||||||
if (childNode.view != currView) {
|
if (childNode.view != currView) {
|
||||||
|
val removedFromParent = childNode.removeFromParentIfKeptForAnimation()
|
||||||
|
if (removedFromParent) {
|
||||||
|
logger.logDetachingChild(
|
||||||
|
key = childNode.label,
|
||||||
|
isTransfer = false,
|
||||||
|
isParentRemoved = true,
|
||||||
|
oldParent = null,
|
||||||
|
newParent = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
when (childNode.parent) {
|
when (childNode.parent) {
|
||||||
null -> {
|
null -> {
|
||||||
@@ -142,6 +165,8 @@ class ShadeViewDiffer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
childNode.resetKeepInParentForAnimation()
|
||||||
|
|
||||||
if (childSpec.children.isNotEmpty()) {
|
if (childSpec.children.isNotEmpty()) {
|
||||||
attachChildren(childNode, specMap)
|
attachChildren(childNode, specMap)
|
||||||
}
|
}
|
||||||
@@ -213,4 +238,16 @@ private class ShadeNode(val controller: NodeController) {
|
|||||||
controller.removeChild(child.controller, isTransfer)
|
controller.removeChild(child.controller, isTransfer)
|
||||||
child.controller.onViewRemoved()
|
child.controller.onViewRemoved()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun offerToKeepInParentForAnimation(): Boolean {
|
||||||
|
return controller.offerToKeepInParentForAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromParentIfKeptForAnimation(): Boolean {
|
||||||
|
return controller.removeFromParentIfKeptForAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetKeepInParentForAnimation() {
|
||||||
|
controller.resetKeepInParentForAnimation()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,20 @@ class ShadeViewDifferLogger @Inject constructor(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun logSkipDetachingChild(
|
||||||
|
key: String,
|
||||||
|
parentKey: String?,
|
||||||
|
isTransfer: Boolean,
|
||||||
|
isParentRemoved: Boolean
|
||||||
|
) {
|
||||||
|
buffer.log(TAG, LogLevel.DEBUG, {
|
||||||
|
str1 = key
|
||||||
|
str2 = parentKey
|
||||||
|
bool1 = isTransfer
|
||||||
|
bool2 = isParentRemoved
|
||||||
|
}, { "Skip detaching $str1 from $str2 isTransfer=$bool1 isParentRemoved=$bool2" })
|
||||||
|
}
|
||||||
|
|
||||||
fun logAttachingChild(key: String, parent: String, atIndex: Int) {
|
fun logAttachingChild(key: String, parent: String, atIndex: Int) {
|
||||||
buffer.log(TAG, LogLevel.DEBUG, {
|
buffer.log(TAG, LogLevel.DEBUG, {
|
||||||
str1 = key
|
str1 = key
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
private NotificationContentView mPrivateLayout;
|
private NotificationContentView mPrivateLayout;
|
||||||
private NotificationContentView[] mLayouts;
|
private NotificationContentView[] mLayouts;
|
||||||
private int mNotificationColor;
|
private int mNotificationColor;
|
||||||
private ExpansionLogger mLogger;
|
private ExpandableNotificationRowLogger mLogger;
|
||||||
private String mLoggingKey;
|
private String mLoggingKey;
|
||||||
private NotificationGuts mGuts;
|
private NotificationGuts mGuts;
|
||||||
private NotificationEntry mEntry;
|
private NotificationEntry mEntry;
|
||||||
@@ -339,7 +339,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private boolean mKeepInParent;
|
private boolean mKeepInParentForDismissAnimation;
|
||||||
private boolean mRemoved;
|
private boolean mRemoved;
|
||||||
private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
|
private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
|
||||||
new FloatProperty<ExpandableNotificationRow>("translate") {
|
new FloatProperty<ExpandableNotificationRow>("translate") {
|
||||||
@@ -825,6 +825,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
if (mChildrenContainer == null) {
|
if (mChildrenContainer == null) {
|
||||||
mChildrenContainerStub.inflate();
|
mChildrenContainerStub.inflate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (row.keepInParentForDismissAnimation()) {
|
||||||
|
logSkipAttachingKeepInParentChild(row);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mChildrenContainer.addNotification(row, childIndex);
|
mChildrenContainer.addNotification(row, childIndex);
|
||||||
onAttachedChildrenCountChanged();
|
onAttachedChildrenCountChanged();
|
||||||
row.setIsChildInGroup(true, this);
|
row.setIsChildInGroup(true, this);
|
||||||
@@ -833,12 +839,38 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
public void removeChildNotification(ExpandableNotificationRow row) {
|
public void removeChildNotification(ExpandableNotificationRow row) {
|
||||||
if (mChildrenContainer != null) {
|
if (mChildrenContainer != null) {
|
||||||
mChildrenContainer.removeNotification(row);
|
mChildrenContainer.removeNotification(row);
|
||||||
|
row.setKeepInParentForDismissAnimation(false);
|
||||||
}
|
}
|
||||||
onAttachedChildrenCountChanged();
|
onAttachedChildrenCountChanged();
|
||||||
row.setIsChildInGroup(false, null);
|
row.setIsChildInGroup(false, null);
|
||||||
row.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
|
row.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the children notifications which were marked to keep for the dismissal animation.
|
||||||
|
*/
|
||||||
|
public void removeChildrenWithKeepInParent() {
|
||||||
|
if (mChildrenContainer == null) return;
|
||||||
|
|
||||||
|
List<ExpandableNotificationRow> clonedList = new ArrayList<>(
|
||||||
|
mChildrenContainer.getAttachedChildren());
|
||||||
|
boolean childCountChanged = false;
|
||||||
|
for (ExpandableNotificationRow child : clonedList) {
|
||||||
|
if (child.keepInParentForDismissAnimation()) {
|
||||||
|
mChildrenContainer.removeNotification(child);
|
||||||
|
child.setIsChildInGroup(false, null);
|
||||||
|
child.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
|
||||||
|
child.setKeepInParentForDismissAnimation(false);
|
||||||
|
logKeepInParentChildDetached(child);
|
||||||
|
childCountChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childCountChanged) {
|
||||||
|
onAttachedChildrenCountChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the child notification at [index], or null if no such child.
|
* Returns the child notification at [index], or null if no such child.
|
||||||
*/
|
*/
|
||||||
@@ -1361,12 +1393,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean keepInParent() {
|
/**
|
||||||
return mKeepInParent;
|
* @return if this entry should be kept in its parent during removal.
|
||||||
|
*/
|
||||||
|
public boolean keepInParentForDismissAnimation() {
|
||||||
|
return mKeepInParentForDismissAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setKeepInParent(boolean keepInParent) {
|
public void setKeepInParentForDismissAnimation(boolean keepInParent) {
|
||||||
mKeepInParent = keepInParent;
|
mKeepInParentForDismissAnimation = keepInParent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1537,8 +1572,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
mUseIncreasedHeadsUpHeight = use;
|
mUseIncreasedHeadsUpHeight = use;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ExpansionLogger {
|
/**
|
||||||
|
* Interface for logging {{@link ExpandableNotificationRow} events.}
|
||||||
|
*/
|
||||||
|
public interface ExpandableNotificationRowLogger {
|
||||||
|
/**
|
||||||
|
* Called when the notification is expanded / collapsed.
|
||||||
|
*/
|
||||||
void logNotificationExpansion(String key, boolean userAction, boolean expanded);
|
void logNotificationExpansion(String key, boolean userAction, boolean expanded);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a notification which was previously kept in its parent for the
|
||||||
|
* dismiss animation is finally detached from its parent.
|
||||||
|
*/
|
||||||
|
void logKeepInParentChildDetached(NotificationEntry child, NotificationEntry oldParent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we want to attach a notification to a new parent,
|
||||||
|
* but it still has the keepInParent flag set, so we skip it.
|
||||||
|
*/
|
||||||
|
void logSkipAttachingKeepInParentChild(
|
||||||
|
NotificationEntry child,
|
||||||
|
NotificationEntry newParent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
|
||||||
@@ -1556,7 +1612,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
|
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
|
||||||
String appName,
|
String appName,
|
||||||
String notificationKey,
|
String notificationKey,
|
||||||
ExpansionLogger logger,
|
ExpandableNotificationRowLogger logger,
|
||||||
KeyguardBypassController bypassController,
|
KeyguardBypassController bypassController,
|
||||||
GroupMembershipManager groupMembershipManager,
|
GroupMembershipManager groupMembershipManager,
|
||||||
GroupExpansionManager groupExpansionManager,
|
GroupExpansionManager groupExpansionManager,
|
||||||
@@ -3567,6 +3623,18 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void logKeepInParentChildDetached(ExpandableNotificationRow child) {
|
||||||
|
if (mLogger != null) {
|
||||||
|
mLogger.logKeepInParentChildDetached(child.getEntry(), getEntry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logSkipAttachingKeepInParentChild(ExpandableNotificationRow child) {
|
||||||
|
if (mLogger != null) {
|
||||||
|
mLogger.logSkipAttachingKeepInParentChild(child.getEntry(), getEntry());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setTargetPoint(Point p) {
|
private void setTargetPoint(Point p) {
|
||||||
mTargetPoint = p;
|
mTargetPoint = p;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,13 +83,11 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
private final GroupExpansionManager mGroupExpansionManager;
|
private final GroupExpansionManager mGroupExpansionManager;
|
||||||
private final RowContentBindStage mRowContentBindStage;
|
private final RowContentBindStage mRowContentBindStage;
|
||||||
private final NotificationLogger mNotificationLogger;
|
private final NotificationLogger mNotificationLogger;
|
||||||
|
private final NotificationRowLogger mLogBufferLogger;
|
||||||
private final HeadsUpManager mHeadsUpManager;
|
private final HeadsUpManager mHeadsUpManager;
|
||||||
private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
|
private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
|
||||||
private final StatusBarStateController mStatusBarStateController;
|
private final StatusBarStateController mStatusBarStateController;
|
||||||
private final MetricsLogger mMetricsLogger;
|
private final MetricsLogger mMetricsLogger;
|
||||||
|
|
||||||
private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
|
|
||||||
this::logNotificationExpansion;
|
|
||||||
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
|
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
|
||||||
private final NotificationGutsManager mNotificationGutsManager;
|
private final NotificationGutsManager mNotificationGutsManager;
|
||||||
private final OnUserInteractionCallback mOnUserInteractionCallback;
|
private final OnUserInteractionCallback mOnUserInteractionCallback;
|
||||||
@@ -101,8 +99,32 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
private final Optional<BubblesManager> mBubblesManagerOptional;
|
private final Optional<BubblesManager> mBubblesManagerOptional;
|
||||||
private final SmartReplyConstants mSmartReplyConstants;
|
private final SmartReplyConstants mSmartReplyConstants;
|
||||||
private final SmartReplyController mSmartReplyController;
|
private final SmartReplyController mSmartReplyController;
|
||||||
|
|
||||||
private final ExpandableNotificationRowDragController mDragController;
|
private final ExpandableNotificationRowDragController mDragController;
|
||||||
|
private final ExpandableNotificationRow.ExpandableNotificationRowLogger mLoggerCallback =
|
||||||
|
new ExpandableNotificationRow.ExpandableNotificationRowLogger() {
|
||||||
|
@Override
|
||||||
|
public void logNotificationExpansion(String key, boolean userAction,
|
||||||
|
boolean expanded) {
|
||||||
|
mNotificationLogger.onExpansionChanged(key, userAction, expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logKeepInParentChildDetached(
|
||||||
|
NotificationEntry child,
|
||||||
|
NotificationEntry oldParent
|
||||||
|
) {
|
||||||
|
mLogBufferLogger.logKeepInParentChildDetached(child, oldParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void logSkipAttachingKeepInParentChild(
|
||||||
|
NotificationEntry child,
|
||||||
|
NotificationEntry newParent
|
||||||
|
) {
|
||||||
|
mLogBufferLogger.logSkipAttachingKeepInParentChild(child, newParent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ExpandableNotificationRowController(
|
public ExpandableNotificationRowController(
|
||||||
@@ -110,6 +132,7 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
ActivatableNotificationViewController activatableNotificationViewController,
|
ActivatableNotificationViewController activatableNotificationViewController,
|
||||||
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
|
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
|
||||||
MetricsLogger metricsLogger,
|
MetricsLogger metricsLogger,
|
||||||
|
NotificationRowLogger logBufferLogger,
|
||||||
NotificationListContainer listContainer,
|
NotificationListContainer listContainer,
|
||||||
NotificationMediaManager mediaManager,
|
NotificationMediaManager mediaManager,
|
||||||
SmartReplyConstants smartReplyConstants,
|
SmartReplyConstants smartReplyConstants,
|
||||||
@@ -163,6 +186,7 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
mBubblesManagerOptional = bubblesManagerOptional;
|
mBubblesManagerOptional = bubblesManagerOptional;
|
||||||
mDragController = dragController;
|
mDragController = dragController;
|
||||||
mMetricsLogger = metricsLogger;
|
mMetricsLogger = metricsLogger;
|
||||||
|
mLogBufferLogger = logBufferLogger;
|
||||||
mSmartReplyConstants = smartReplyConstants;
|
mSmartReplyConstants = smartReplyConstants;
|
||||||
mSmartReplyController = smartReplyController;
|
mSmartReplyController = smartReplyController;
|
||||||
}
|
}
|
||||||
@@ -177,7 +201,7 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
mRemoteInputViewSubcomponentFactory,
|
mRemoteInputViewSubcomponentFactory,
|
||||||
mAppName,
|
mAppName,
|
||||||
mNotificationKey,
|
mNotificationKey,
|
||||||
mExpansionLogger,
|
mLoggerCallback,
|
||||||
mKeyguardBypassController,
|
mKeyguardBypassController,
|
||||||
mGroupMembershipManager,
|
mGroupMembershipManager,
|
||||||
mGroupExpansionManager,
|
mGroupExpansionManager,
|
||||||
@@ -243,10 +267,6 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
|
|
||||||
mNotificationLogger.onExpansionChanged(key, userAction, expanded);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public String getNodeLabel() {
|
public String getNodeLabel() {
|
||||||
@@ -336,4 +356,29 @@ public class ExpandableNotificationRowController implements NotifViewController
|
|||||||
public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
|
public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
|
||||||
mView.setFeedbackIcon(icon);
|
mView.setFeedbackIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean offerToKeepInParentForAnimation() {
|
||||||
|
if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)) {
|
||||||
|
mView.setKeepInParentForDismissAnimation(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeFromParentIfKeptForAnimation() {
|
||||||
|
ExpandableNotificationRow parent = mView.getNotificationParent();
|
||||||
|
if (mView.keepInParentForDismissAnimation() && parent != null) {
|
||||||
|
parent.removeChildNotification(mView);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetKeepInParentForAnimation() {
|
||||||
|
mView.setKeepInParentForDismissAnimation(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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.row
|
||||||
|
|
||||||
|
import com.android.systemui.log.dagger.NotificationLog
|
||||||
|
import com.android.systemui.plugins.log.LogBuffer
|
||||||
|
import com.android.systemui.plugins.log.LogLevel
|
||||||
|
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||||
|
import com.android.systemui.statusbar.notification.logKey
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class NotificationRowLogger @Inject constructor(@NotificationLog private val buffer: LogBuffer) {
|
||||||
|
fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) {
|
||||||
|
buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.DEBUG,
|
||||||
|
{
|
||||||
|
str1 = child.logKey
|
||||||
|
str2 = oldParent.logKey
|
||||||
|
},
|
||||||
|
{ "Detach child $str1 kept in parent $str2" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logSkipAttachingKeepInParentChild(child: NotificationEntry, newParent: NotificationEntry?) {
|
||||||
|
buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.WARNING,
|
||||||
|
{
|
||||||
|
str1 = child.logKey
|
||||||
|
str2 = newParent.logKey
|
||||||
|
},
|
||||||
|
{ "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val TAG = "NotifRow"
|
||||||
@@ -2774,6 +2774,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mSwipedOutViews.remove(child);
|
mSwipedOutViews.remove(child);
|
||||||
|
|
||||||
|
if (child instanceof ExpandableNotificationRow) {
|
||||||
|
((ExpandableNotificationRow) child).removeChildrenWithKeepInParent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateAnimationState(false, child);
|
updateAnimationState(false, child);
|
||||||
|
|
||||||
|
|||||||
@@ -443,7 +443,11 @@ public class NotificationStackScrollLayoutController {
|
|||||||
if (!row.isDismissed()) {
|
if (!row.isDismissed()) {
|
||||||
handleChildViewDismissed(view);
|
handleChildViewDismissed(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.removeFromTransientContainer();
|
row.removeFromTransientContainer();
|
||||||
|
if (row instanceof ExpandableNotificationRow) {
|
||||||
|
((ExpandableNotificationRow) row).removeChildrenWithKeepInParent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
|
|||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
|
||||||
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
|
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
|
||||||
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||||
import com.android.systemui.util.time.FakeSystemClock;
|
import com.android.systemui.util.time.FakeSystemClock;
|
||||||
|
|
||||||
@@ -741,22 +742,24 @@ public class NotifCollectionTest extends SysuiTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testGroupChildrenAreDismissedLocallyWhenSummaryIsDismissed() {
|
public void testGroupChildrenAreDismissedLocallyWhenSummaryIsDismissed() {
|
||||||
// GIVEN a collection with two grouped notifs in it
|
// GIVEN a collection with two grouped notifs in it
|
||||||
CollectionEvent notif0 = postNotif(
|
CollectionEvent groupNotif = postNotif(
|
||||||
buildNotif(TEST_PACKAGE, 0)
|
buildNotif(TEST_PACKAGE, 0)
|
||||||
.setGroup(mContext, GROUP_1)
|
.setGroup(mContext, GROUP_1)
|
||||||
.setGroupSummary(mContext, true));
|
.setGroupSummary(mContext, true));
|
||||||
CollectionEvent notif1 = postNotif(
|
CollectionEvent childNotif = postNotif(
|
||||||
buildNotif(TEST_PACKAGE, 1)
|
buildNotif(TEST_PACKAGE, 1)
|
||||||
.setGroup(mContext, GROUP_1));
|
.setGroup(mContext, GROUP_1));
|
||||||
NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
|
NotificationEntry groupEntry = mCollectionListener.getEntry(groupNotif.key);
|
||||||
NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
|
NotificationEntry childEntry = mCollectionListener.getEntry(childNotif.key);
|
||||||
|
ExpandableNotificationRow childRow = mock(ExpandableNotificationRow.class);
|
||||||
|
childEntry.setRow(childRow);
|
||||||
|
|
||||||
// WHEN the summary is dismissed
|
// WHEN the summary is dismissed
|
||||||
mCollection.dismissNotification(entry0, defaultStats(entry0));
|
mCollection.dismissNotification(groupEntry, defaultStats(groupEntry));
|
||||||
|
|
||||||
// THEN all members of the group are marked as dismissed locally
|
// THEN all members of the group are marked as dismissed locally
|
||||||
assertEquals(DISMISSED, entry0.getDismissState());
|
assertEquals(DISMISSED, groupEntry.getDismissState());
|
||||||
assertEquals(PARENT_DISMISSED, entry1.getDismissState());
|
assertEquals(PARENT_DISMISSED, childEntry.getDismissState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.render
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
@@ -26,6 +27,10 @@ import org.junit.Assert
|
|||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.ArgumentMatchers.isNull
|
||||||
|
import org.mockito.Mockito.anyBoolean
|
||||||
|
import org.mockito.Mockito.matches
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@@ -124,6 +129,64 @@ class ShadeViewDifferTest : SysuiTestCase() {
|
|||||||
Assert.assertNull(controller3.view.parent)
|
Assert.assertNull(controller3.view.parent)
|
||||||
Assert.assertNull(controller4.view.parent)
|
Assert.assertNull(controller4.view.parent)
|
||||||
Assert.assertNull(controller5.view.parent)
|
Assert.assertNull(controller5.view.parent)
|
||||||
|
verifyDetachingChildLogged(controller3, oldParent = controller2)
|
||||||
|
verifyDetachingChildLogged(controller4, oldParent = controller2)
|
||||||
|
verifyDetachingChildLogged(controller5, oldParent = controller2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRemovedGroupsWithKeepInParentAreKeptTogether() {
|
||||||
|
// GIVEN a preexisting tree with a group
|
||||||
|
// AND the group children supports keepInParent
|
||||||
|
applySpecAndCheck(
|
||||||
|
node(controller1),
|
||||||
|
node(controller2, node(controller3), node(controller4), node(controller5))
|
||||||
|
)
|
||||||
|
controller3.supportsKeepInParent = true
|
||||||
|
controller4.supportsKeepInParent = true
|
||||||
|
controller5.supportsKeepInParent = true
|
||||||
|
|
||||||
|
// WHEN the new spec removes the entire group
|
||||||
|
applySpecAndCheck(node(controller1))
|
||||||
|
|
||||||
|
// THEN the group children are still attached to their parent
|
||||||
|
Assert.assertEquals(controller2.view, controller3.view.parent)
|
||||||
|
Assert.assertEquals(controller2.view, controller4.view.parent)
|
||||||
|
Assert.assertEquals(controller2.view, controller5.view.parent)
|
||||||
|
verifySkipDetachingChildLogged(controller3, parent = controller2)
|
||||||
|
verifySkipDetachingChildLogged(controller4, parent = controller2)
|
||||||
|
verifySkipDetachingChildLogged(controller5, parent = controller2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testReuseRemovedGroupsWithKeepInParent() {
|
||||||
|
// GIVEN a preexisting tree with a dismissed group
|
||||||
|
// AND the group children supports keepInParent
|
||||||
|
controller3.supportsKeepInParent = true
|
||||||
|
controller4.supportsKeepInParent = true
|
||||||
|
controller5.supportsKeepInParent = true
|
||||||
|
applySpecAndCheck(
|
||||||
|
node(controller1),
|
||||||
|
node(controller2, node(controller3), node(controller4), node(controller5))
|
||||||
|
)
|
||||||
|
applySpecAndCheck(node(controller1))
|
||||||
|
|
||||||
|
// WHEN a new spec is applied which reuses the dismissed views
|
||||||
|
applySpecAndCheck(
|
||||||
|
node(controller1),
|
||||||
|
node(controller2),
|
||||||
|
node(controller3),
|
||||||
|
node(controller4),
|
||||||
|
node(controller5)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN the dismissed views can be reused
|
||||||
|
Assert.assertEquals(rootController.view, controller3.view.parent)
|
||||||
|
Assert.assertEquals(rootController.view, controller4.view.parent)
|
||||||
|
Assert.assertEquals(rootController.view, controller5.view.parent)
|
||||||
|
verifyDetachingChildLogged(controller3, oldParent = null)
|
||||||
|
verifyDetachingChildLogged(controller4, oldParent = null)
|
||||||
|
verifyDetachingChildLogged(controller5, oldParent = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -184,7 +247,30 @@ class ShadeViewDifferTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun verifySkipDetachingChildLogged(child: NodeController, parent: NodeController) {
|
||||||
|
verify(logger)
|
||||||
|
.logSkipDetachingChild(
|
||||||
|
key = matches(child.nodeLabel),
|
||||||
|
parentKey = matches(parent.nodeLabel),
|
||||||
|
anyBoolean(),
|
||||||
|
anyBoolean()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyDetachingChildLogged(child: NodeController, oldParent: NodeController?) {
|
||||||
|
verify(logger)
|
||||||
|
.logDetachingChild(
|
||||||
|
key = matches(child.nodeLabel),
|
||||||
|
isTransfer = anyBoolean(),
|
||||||
|
isParentRemoved = anyBoolean(),
|
||||||
|
oldParent = oldParent?.let { matches(it.nodeLabel) } ?: isNull(),
|
||||||
|
newParent = isNull()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private class FakeController(context: Context, label: String) : NodeController {
|
private class FakeController(context: Context, label: String) : NodeController {
|
||||||
|
var supportsKeepInParent: Boolean = false
|
||||||
|
|
||||||
override val view: FrameLayout = FrameLayout(context)
|
override val view: FrameLayout = FrameLayout(context)
|
||||||
override val nodeLabel: String = label
|
override val nodeLabel: String = label
|
||||||
override fun getChildCount(): Int = view.childCount
|
override fun getChildCount(): Int = view.childCount
|
||||||
@@ -209,6 +295,22 @@ class ShadeViewDifferTest : SysuiTestCase() {
|
|||||||
override fun onViewAdded() {}
|
override fun onViewAdded() {}
|
||||||
override fun onViewMoved() {}
|
override fun onViewMoved() {}
|
||||||
override fun onViewRemoved() {}
|
override fun onViewRemoved() {}
|
||||||
|
override fun offerToKeepInParentForAnimation(): Boolean {
|
||||||
|
return supportsKeepInParent
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeFromParentIfKeptForAnimation(): Boolean {
|
||||||
|
if (supportsKeepInParent) {
|
||||||
|
(view.parent as? ViewGroup)?.removeView(view)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetKeepInParentForAnimation() {
|
||||||
|
supportsKeepInParent = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SpecBuilder(
|
private class SpecBuilder(
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.reset;
|
|||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
@@ -458,4 +459,79 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
|
|||||||
verify(mNotificationTestHelper.mOnUserInteractionCallback, never())
|
verify(mNotificationTestHelper.mOnUserInteractionCallback, never())
|
||||||
.registerFutureDismissal(any(), anyInt());
|
.registerFutureDismissal(any(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddChildNotification() throws Exception {
|
||||||
|
ExpandableNotificationRow group = mNotificationTestHelper.createGroup(0);
|
||||||
|
ExpandableNotificationRow child = mNotificationTestHelper.createRow();
|
||||||
|
|
||||||
|
group.addChildNotification(child);
|
||||||
|
|
||||||
|
Assert.assertEquals(child, group.getChildNotificationAt(0));
|
||||||
|
Assert.assertEquals(group, child.getNotificationParent());
|
||||||
|
Assert.assertTrue(child.isChildInGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddChildNotification_childSkipped() throws Exception {
|
||||||
|
ExpandableNotificationRow group = mNotificationTestHelper.createGroup(0);
|
||||||
|
ExpandableNotificationRow child = mNotificationTestHelper.createRow();
|
||||||
|
child.setKeepInParentForDismissAnimation(true);
|
||||||
|
|
||||||
|
group.addChildNotification(child);
|
||||||
|
|
||||||
|
Assert.assertTrue(group.getAttachedChildren().isEmpty());
|
||||||
|
Assert.assertNotEquals(group, child.getNotificationParent());
|
||||||
|
verify(mNotificationTestHelper.getMockLogger()).logSkipAttachingKeepInParentChild(
|
||||||
|
/*child=*/ child.getEntry(),
|
||||||
|
/*newParent=*/ group.getEntry()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveChildNotification() throws Exception {
|
||||||
|
ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1);
|
||||||
|
ExpandableNotificationRow child = group.getAttachedChildren().get(0);
|
||||||
|
child.setKeepInParentForDismissAnimation(true);
|
||||||
|
|
||||||
|
group.removeChildNotification(child);
|
||||||
|
|
||||||
|
Assert.assertNull(child.getParent());
|
||||||
|
Assert.assertNull(child.getNotificationParent());
|
||||||
|
Assert.assertFalse(child.keepInParentForDismissAnimation());
|
||||||
|
verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveChildrenWithKeepInParent_removesChildWithKeepInParent() throws Exception {
|
||||||
|
ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1);
|
||||||
|
ExpandableNotificationRow child = group.getAttachedChildren().get(0);
|
||||||
|
child.setKeepInParentForDismissAnimation(true);
|
||||||
|
|
||||||
|
group.removeChildrenWithKeepInParent();
|
||||||
|
|
||||||
|
Assert.assertNull(child.getParent());
|
||||||
|
Assert.assertNull(child.getNotificationParent());
|
||||||
|
Assert.assertFalse(child.keepInParentForDismissAnimation());
|
||||||
|
verify(mNotificationTestHelper.getMockLogger()).logKeepInParentChildDetached(
|
||||||
|
/*child=*/ child.getEntry(),
|
||||||
|
/*oldParent=*/ group.getEntry()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveChildrenWithKeepInParent_skipsChildrenWithoutKeepInParent()
|
||||||
|
throws Exception {
|
||||||
|
ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1);
|
||||||
|
ExpandableNotificationRow child = group.getAttachedChildren().get(0);
|
||||||
|
|
||||||
|
group.removeChildrenWithKeepInParent();
|
||||||
|
|
||||||
|
Assert.assertEquals(group, child.getNotificationParent());
|
||||||
|
Assert.assertFalse(child.keepInParentForDismissAnimation());
|
||||||
|
verify(mNotificationTestHelper.getMockLogger(), never()).logKeepInParentChildDetached(
|
||||||
|
/*child=*/ any(),
|
||||||
|
/*oldParent=*/ any()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
|
|||||||
import com.android.systemui.statusbar.notification.icon.IconBuilder;
|
import com.android.systemui.statusbar.notification.icon.IconBuilder;
|
||||||
import com.android.systemui.statusbar.notification.icon.IconManager;
|
import com.android.systemui.statusbar.notification.icon.IconManager;
|
||||||
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
|
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
|
||||||
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
|
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
|
||||||
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
|
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
|
||||||
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
|
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
|
||||||
@@ -116,6 +116,7 @@ public class NotificationTestHelper {
|
|||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final TestableLooper mTestLooper;
|
private final TestableLooper mTestLooper;
|
||||||
private int mId;
|
private int mId;
|
||||||
|
private final ExpandableNotificationRowLogger mMockLogger;
|
||||||
private final GroupMembershipManager mGroupMembershipManager;
|
private final GroupMembershipManager mGroupMembershipManager;
|
||||||
private final GroupExpansionManager mGroupExpansionManager;
|
private final GroupExpansionManager mGroupExpansionManager;
|
||||||
private ExpandableNotificationRow mRow;
|
private ExpandableNotificationRow mRow;
|
||||||
@@ -139,6 +140,7 @@ public class NotificationTestHelper {
|
|||||||
dependency.injectMockDependency(NotificationMediaManager.class);
|
dependency.injectMockDependency(NotificationMediaManager.class);
|
||||||
dependency.injectMockDependency(NotificationShadeWindowController.class);
|
dependency.injectMockDependency(NotificationShadeWindowController.class);
|
||||||
dependency.injectMockDependency(MediaOutputDialogFactory.class);
|
dependency.injectMockDependency(MediaOutputDialogFactory.class);
|
||||||
|
mMockLogger = mock(ExpandableNotificationRowLogger.class);
|
||||||
mStatusBarStateController = mock(StatusBarStateController.class);
|
mStatusBarStateController = mock(StatusBarStateController.class);
|
||||||
mGroupMembershipManager = mock(GroupMembershipManager.class);
|
mGroupMembershipManager = mock(GroupMembershipManager.class);
|
||||||
mGroupExpansionManager = mock(GroupExpansionManager.class);
|
mGroupExpansionManager = mock(GroupExpansionManager.class);
|
||||||
@@ -197,6 +199,10 @@ public class NotificationTestHelper {
|
|||||||
mDefaultInflationFlags = defaultInflationFlags;
|
mDefaultInflationFlags = defaultInflationFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExpandableNotificationRowLogger getMockLogger() {
|
||||||
|
return mMockLogger;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a generic row with rounded border.
|
* Creates a generic row with rounded border.
|
||||||
*
|
*
|
||||||
@@ -527,7 +533,7 @@ public class NotificationTestHelper {
|
|||||||
mock(RemoteInputViewSubcomponent.Factory.class),
|
mock(RemoteInputViewSubcomponent.Factory.class),
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
entry.getKey(),
|
entry.getKey(),
|
||||||
mock(ExpansionLogger.class),
|
mMockLogger,
|
||||||
mock(KeyguardBypassController.class),
|
mock(KeyguardBypassController.class),
|
||||||
mGroupMembershipManager,
|
mGroupMembershipManager,
|
||||||
mGroupExpansionManager,
|
mGroupExpansionManager,
|
||||||
|
|||||||
Reference in New Issue
Block a user