Fix crash when adding a notification to a group while it is animating away.

This crash happens more often with the new pipeline where rows are removed and re-added quite often (rather than being set to GONE when redacted by visibility settings), and such removals also occasionally result in moving between the NSSL (which has disappear animations) and NotificationChildrenContainer.

Fixes: 210164657
Test: manual
Change-Id: I669d086a957b87762bfcb6b1ec00271282bd53fb
This commit is contained in:
Jeff DeCew
2021-12-17 21:09:51 +00:00
parent 7ce8fa3980
commit b10856228c
3 changed files with 47 additions and 9 deletions

View File

@@ -23,8 +23,10 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -517,6 +519,36 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
return mChangingPosition;
}
/**
* Called before adding this view to a group, which would always throw an exception if this view
* has a parent, so clean up the transient container and throw an exception if the parent isn't
* a transient container. Provide as much detail in the event of a crash as possible.
*/
public void removeFromTransientContainerForAdditionTo(ViewGroup newParent) {
final ViewParent parent = getParent();
if (parent == null) {
// If this view has no parent, the add will succeed, so do nothing.
return;
}
ViewGroup transientContainer = getTransientContainer();
if (transientContainer == null) {
throw new IllegalStateException(
"Can't add view " + this + " to container " + newParent + "; current parent "
+ parent + " is not a transient container");
}
if (transientContainer != parent) {
throw new IllegalStateException(
"Expandable view " + this + " has transient container " + transientContainer
+ " which is not the same as its parent " + parent);
}
if (parent != newParent) {
Log.w(TAG, "Moving view " + this + " from transient container "
+ transientContainer + " to parent " + newParent);
}
transientContainer.removeTransientView(this);
setTransientContainer(null);
}
public void setTransientContainer(ViewGroup transientContainer) {
mTransientContainer = transientContainer;
}

View File

@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.HybridGroupManager;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -272,6 +273,7 @@ public class NotificationChildrenContainer extends ViewGroup
* @param childIndex the index to add it at, if -1 it will be added at the end
*/
public void addNotification(ExpandableNotificationRow row, int childIndex) {
ensureRemovedFromTransientContainer(row);
int newIndex = childIndex < 0 ? mAttachedChildren.size() : childIndex;
mAttachedChildren.add(newIndex, row);
addView(row);
@@ -291,6 +293,16 @@ public class NotificationChildrenContainer extends ViewGroup
}
}
private void ensureRemovedFromTransientContainer(View v) {
if (v.getParent() != null && v instanceof ExpandableView) {
// If the child is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will
// happen automatically when the view's intro animation starts, but
// it's a fragile link.
((ExpandableView) v).removeFromTransientContainerForAdditionTo(this);
}
}
public void removeNotification(ExpandableNotificationRow row) {
int childIndex = mAttachedChildren.indexOf(row);
mAttachedChildren.remove(row);

View File

@@ -4654,18 +4654,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
}
private void ensureRemovedFromTransientContainer(View v) {
if (v.getParent() == this && v instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) v;
ViewGroup transientContainer = expandableView.getTransientContainer();
// If the child is animating away, it will still have a parent, so
// detach it first
if (v.getParent() != null && v instanceof ExpandableView) {
// If the child is animating away, it will still have a parent, so detach it first
// TODO: We should really cancel the active animations here. This will
// happen automatically when the view's intro animation starts, but
// it's a fragile link.
if (transientContainer != null) {
transientContainer.removeTransientView(v);
expandableView.setTransientContainer(null);
}
((ExpandableView) v).removeFromTransientContainerForAdditionTo(this);
}
}