Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab2d473f authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Fix ExpandableView's transient container tracking; prevent crash when mismatched."

parents e74fe455 27630150
Loading
Loading
Loading
Loading
+47 −7
Original line number Diff line number Diff line
@@ -519,27 +519,67 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
        return mChangingPosition;
    }

    /**
     * Called when removing a view from its transient container, such as at the end of an animation.
     * Generally, when operating on ExpandableView instances, this should be used rather than
     * {@link ExpandableView#removeTransientView(View)} to ensure that the
     * {@link #getTransientContainer() transient container} is correctly reset.
     */
    public void removeFromTransientContainer() {
        final ViewGroup transientContainer = getTransientContainer();
        if (transientContainer == null) {
            return;
        }
        final ViewParent parent = getParent();
        if (parent != transientContainer) {
            Log.w(TAG, "Expandable view " + this
                    + " has transient container " + transientContainer
                    + " but different parent " + parent);
            setTransientContainer(null);
            return;
        }
        transientContainer.removeTransientView(this);
        setTransientContainer(null);
    }

    /**
     * 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.
     * has a different parent, so clean up the transient container and throw an exception if the
     * parent isn't a transient container.  Provide as much detail as possible in the crash.
     */
    public void removeFromTransientContainerForAdditionTo(ViewGroup newParent) {
        final ViewParent parent = getParent();
        final ViewGroup transientContainer = getTransientContainer();
        if (parent == null) {
            // If this view has no parent, the add will succeed, so do nothing.
            // If this view has no parent, the add will succeed, so just make sure the tracked
            // transient container is in sync with the lack of a parent.
            if (transientContainer != null) {
                Log.w(TAG, "Expandable view " + this
                        + " has transient container " + transientContainer
                        + " but no parent");
                setTransientContainer(null);
            }
            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);
            String transientContainerOutOfSyncError = "Expandable view " + this
                    + " has transient container " + transientContainer
                    + " but different parent " + parent;
            if (parent != newParent) {
                // Crash with details before addView() crashes without any; the view is being added
                // to a different parent, and the transient container isn't the parent, so we can't
                // even (safely) clean that up.
                throw new IllegalStateException(transientContainerOutOfSyncError);
            } else {
                Log.w(TAG, transientContainerOutOfSyncError);
                setTransientContainer(null);
                return;
            }
        }
        if (parent != newParent) {
            Log.w(TAG, "Moving view " + this + " from transient container "
+1 −2
Original line number Diff line number Diff line
@@ -215,8 +215,7 @@ class NotificationSectionsManager @Inject internal constructor(
                            // 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.
                            header.transientContainer?.removeTransientView(header)
                            header.transientContainer = null
                            header.removeFromTransientContainer()
                            parent.addView(header, target)
                        } else {
                            parent.changeViewPosition(header, target)
+7 −6
Original line number Diff line number Diff line
@@ -3213,10 +3213,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
                    // Clean up any potential transient views if the child has already been swiped
                    // out, as we won't be animating it further (due to its height already being
                    // clipped to 0.
                    ViewGroup transientContainer = child.getTransientContainer();
                    if (transientContainer != null) {
                        transientContainer.removeTransientView(child);
                    }
                    child.removeFromTransientContainer();
                }
            }
            int animationType = childWasSwipedOut
@@ -3933,7 +3930,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
    private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
        while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
            viewGroup.removeTransientView(viewGroup.getTransientView(0));
            final View transientView = viewGroup.getTransientView(0);
            viewGroup.removeTransientView(transientView);
            if (transientView instanceof ExpandableView) {
                ((ExpandableView) transientView).setTransientContainer(null);
            }
        }
    }

@@ -4102,7 +4103,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
    private void clearTransient() {
        for (ExpandableView view : mClearTransientViewsWhenFinished) {
            StackStateAnimator.removeTransientView(view);
            view.removeFromTransientContainer();
        }
        mClearTransientViewsWhenFinished.clear();
    }
+1 −4
Original line number Diff line number Diff line
@@ -455,10 +455,7 @@ public class NotificationStackScrollLayoutController {
                    if (!row.isDismissed()) {
                        handleChildViewDismissed(view);
                    }
                    ViewGroup transientContainer = row.getTransientContainer();
                    if (transientContainer != null) {
                        transientContainer.removeTransientView(view);
                    }
                    row.removeFromTransientContainer();
                }

                /**
+7 −15
Original line number Diff line number Diff line
@@ -322,9 +322,8 @@ public class StackStateAnimator {
    private void onAnimationFinished() {
        mHostLayout.onChildAnimationFinished();

        for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
            transientViewsToRemove.getTransientContainer()
                    .removeTransientView(transientViewsToRemove);
        for (ExpandableView transientViewToRemove : mTransientViewsToRemove) {
            transientViewToRemove.removeFromTransientContainer();
        }
        mTransientViewsToRemove.clear();
    }
@@ -353,7 +352,7 @@ public class StackStateAnimator {
            } else if (event.animationType ==
                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
                if (changingView.getVisibility() != View.VISIBLE) {
                    removeTransientView(changingView);
                    changingView.removeFromTransientContainer();
                    continue;
                }

@@ -390,12 +389,11 @@ public class StackStateAnimator {
                }
                changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
                        0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
                        0, () -> removeTransientView(changingView), null);
                        0, changingView::removeFromTransientContainer, null);
            } else if (event.animationType ==
                NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
                if (mHostLayout.isFullySwipedOut(changingView)
                        && changingView.getTransientContainer() != null) {
                    changingView.getTransientContainer().removeTransientView(changingView);
                if (mHostLayout.isFullySwipedOut(changingView)) {
                    changingView.removeFromTransientContainer();
                }
            } else if (event.animationType == NotificationStackScrollLayout
                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
@@ -425,7 +423,7 @@ public class StackStateAnimator {
                    mHostLayout.addTransientView(changingView, 0);
                    changingView.setTransientContainer(mHostLayout);
                    mTmpState.initFrom(changingView);
                    endRunnable = () -> removeTransientView(changingView);
                    endRunnable = changingView::removeFromTransientContainer;
                }
                float targetLocation = 0;
                boolean needsAnimation = true;
@@ -468,12 +466,6 @@ public class StackStateAnimator {
        }
    }

    public static void removeTransientView(ExpandableView viewToRemove) {
        if (viewToRemove.getTransientContainer() != null) {
            viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
        }
    }

    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
            final boolean isRubberbanded) {
        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
Loading