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

Commit e9c9e16d authored by Lyn Han's avatar Lyn Han Committed by Android (Google) Code Review
Browse files

Merge "Update clear all animation"

parents ab1315c2 56e0d6c3
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -451,6 +451,12 @@ public class SwipeHelper implements Gefingerpoken {
        anim.addListener(new AnimatorListenerAdapter() {
            private boolean mCancelled;

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                mCallback.onBeginDrag(animView);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                mCancelled = true;
+22 −15
Original line number Diff line number Diff line
@@ -85,23 +85,26 @@ public abstract class StackScrollerDecorView extends ExpandableView {
    }

    /**
     * Set the content of this view to be visible in an animated way.
     *
     * @param contentVisible True if the content should be visible or false if it should be hidden.
     * @param visible True if we should animate contents visible
     */
    public void setContentVisible(boolean contentVisible) {
        setContentVisible(contentVisible, true /* animate */);
    public void setContentVisible(boolean visible) {
        setContentVisible(visible, true /* animate */, null /* runAfter */);
    }

    /**
     * Set the content of this view to be visible.
     * @param contentVisible True if the content should be visible or false if it should be hidden.
     * @param animate Should an animation be performed.
     * @param visible True if the contents should be visible
     * @param animate True if we should fade to new visibility
     * @param runAfter Runnable to run after visibility updates
     */
    private void setContentVisible(boolean contentVisible, boolean animate) {
        if (mContentVisible != contentVisible) {
    public void setContentVisible(boolean visible, boolean animate, Runnable runAfter) {
        if (mContentVisible != visible) {
            mContentAnimating = animate;
            mContentVisible = contentVisible;
            setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
            mContentVisible = visible;
            Runnable endRunnable = runAfter == null ? mContentVisibilityEndRunnable : () -> {
                mContentVisibilityEndRunnable.run();
                runAfter.run();
            };
            setViewVisible(mContent, visible, animate, endRunnable);
        }

        if (!mContentAnimating) {
@@ -113,6 +116,10 @@ public abstract class StackScrollerDecorView extends ExpandableView {
        return mContentVisible;
    }

    public void setVisible(boolean nowVisible, boolean animate) {
        setVisible(nowVisible, animate, null);
    }

    /**
     * Make this view visible. If {@code false} is passed, the view will fade out it's content
     * and set the view Visibility to GONE. If only the content should be changed
@@ -121,7 +128,7 @@ public abstract class StackScrollerDecorView extends ExpandableView {
     * @param nowVisible should the view be visible
     * @param animate should the change be animated.
     */
    public void setVisible(boolean nowVisible, boolean animate) {
    public void setVisible(boolean nowVisible, boolean animate, Runnable runAfter) {
        if (mIsVisible != nowVisible) {
            mIsVisible = nowVisible;
            if (animate) {
@@ -132,10 +139,10 @@ public abstract class StackScrollerDecorView extends ExpandableView {
                } else {
                    setWillBeGone(true);
                }
                setContentVisible(nowVisible, true /* animate */);
                setContentVisible(nowVisible, true /* animate */, runAfter);
            } else {
                setVisibility(nowVisible ? VISIBLE : GONE);
                setContentVisible(nowVisible, false /* animate */);
                setContentVisible(nowVisible, false /* animate */, runAfter);
                setWillBeGone(false);
                notifyHeightChanged(false /* needsAnimation */);
            }
+10 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ public class NotificationRoundnessManager {
    private ExpandableNotificationRow mTrackedHeadsUp;
    private float mAppearFraction;
    private boolean mRoundForPulsingViews;
    private boolean mIsDismissAllInProgress;

    private ExpandableView mSwipedView = null;
    private ExpandableView mViewBeforeSwipedView = null;
@@ -158,6 +159,10 @@ public class NotificationRoundnessManager {
        }
    }

    void setDismissAllInProgress(boolean isClearingAll) {
        mIsDismissAllInProgress = isClearingAll;
    }

    private float getRoundnessFraction(ExpandableView view, boolean top) {
        if (view == null) {
            return 0f;
@@ -167,6 +172,11 @@ public class NotificationRoundnessManager {
                || view == mViewAfterSwipedView) {
            return 1f;
        }
        if (view instanceof ExpandableNotificationRow
                && ((ExpandableNotificationRow) view).canViewBeDismissed()
                && mIsDismissAllInProgress) {
            return 1.0f;
        }
        if ((view.isPinned()
                || (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
            return 1.0f;
+124 −94
Original line number Diff line number Diff line
@@ -139,6 +139,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
            "persist.debug.nssl.dismiss", false /* default */);

    // Delay in milli-seconds before shade closes for clear all.
    private final int DELAY_BEFORE_SHADE_CLOSE = 200;
    private boolean mShadeNeedsToClose = false;

    private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
    private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
    private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
@@ -253,7 +257,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    protected FooterView mFooterView;
    protected EmptyShadeView mEmptyShadeView;
    private boolean mDismissAllInProgress;
    private boolean mFadeNotificationsOnDismiss;
    private FooterDismissListener mFooterDismissListener;
    private boolean mFlingAfterUpEvent;

@@ -1205,7 +1208,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    @ShadeViewRefactor(RefactorComponent.COORDINATOR)
    private void clampScrollPosition() {
        int scrollRange = getScrollRange();
        if (scrollRange < mOwnScrollY) {
        if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) {
            boolean animateStackY = false;
            if (scrollRange < getScrollAmountToScrollBoundary()
                    && mAnimateStackYForContentHeightChange) {
@@ -1721,6 +1724,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable

    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
    public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
        if (child instanceof SectionHeaderView) {
             ((StackScrollerDecorView) child).setContentVisible(
                     false /* visible */, true /* animate */, endRunnable);
             return;
        }
        mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
                true /* isDismissAll */);
    }
@@ -4062,6 +4070,18 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        runAnimationFinishedRunnables();
        clearTransient();
        clearHeadsUpDisappearRunning();

        if (mAmbientState.isDismissAllInProgress()) {
            setDismissAllInProgress(false);
            if (mShadeNeedsToClose) {
                mShadeNeedsToClose = false;
                postDelayed(
                        () -> {
                            mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
                        },
                        DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
            }
        }
    }

    @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4430,6 +4450,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
    public void setDismissAllInProgress(boolean dismissAllInProgress) {
        mDismissAllInProgress = dismissAllInProgress;
        mAmbientState.setDismissAllInProgress(dismissAllInProgress);
        mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress);
        handleDismissAllClipping();
    }

@@ -4973,129 +4994,137 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mHeadsUpAppearanceController = headsUpAppearanceController;
    }

    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
    @VisibleForTesting
    void clearNotifications(@SelectedRows int selection, boolean closeShade) {
        // animate-swipe all dismissable notifications, then animate the shade closed
        int numChildren = getChildCount();

        final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
        final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
        for (int i = 0; i < numChildren; i++) {
            final View child = getChildAt(i);
            if (child instanceof ExpandableNotificationRow) {
                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
                boolean parentVisible = false;
    private boolean isVisible(View child) {
        boolean hasClipBounds = child.getClipBounds(mTmpRect);
                if (includeChildInDismissAll(row, selection)) {
                    viewsToRemove.add(row);
                    if (child.getVisibility() == View.VISIBLE
                            && (!hasClipBounds || mTmpRect.height() > 0)) {
                        viewsToHide.add(child);
                        parentVisible = true;
        return child.getVisibility() == View.VISIBLE
                && (!hasClipBounds || mTmpRect.height() > 0);
    }
                } else if (child.getVisibility() == View.VISIBLE
                        && (!hasClipBounds || mTmpRect.height() > 0)) {
                    parentVisible = true;

    private boolean shouldHideParent(View view, @SelectedRows int selection) {
        final boolean silentSectionWillBeGone =
                !mController.hasNotifications(ROWS_GENTLE, false /* clearable */);

        // The only SectionHeaderView we have is the silent section header.
        if (view instanceof SectionHeaderView && silentSectionWillBeGone) {
            return true;
        }
                List<ExpandableNotificationRow> children = row.getAttachedChildren();
                if (children != null) {
                    for (ExpandableNotificationRow childRow : children) {
                        if (includeChildInDismissAll(row, selection)) {
                            viewsToRemove.add(childRow);
                            if (parentVisible && row.areChildrenExpanded()) {
                                hasClipBounds = childRow.getClipBounds(mTmpRect);
                                if (childRow.getVisibility() == View.VISIBLE
                                        && (!hasClipBounds || mTmpRect.height() > 0)) {
                                    viewsToHide.add(childRow);
        if (view instanceof ExpandableNotificationRow) {
            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
            if (isVisible(row) && includeChildInDismissAll(row, selection)) {
                return true;
            }
        }
        return false;
    }

    private boolean isChildrenVisible(ExpandableNotificationRow parent) {
        List<ExpandableNotificationRow> children = parent.getAttachedChildren();
        return isVisible(parent)
                && children != null
                && parent.areChildrenExpanded();
    }

    // Similar to #getRowsToDismissInBackend, but filters for visible views.
    private ArrayList<View> getVisibleViewsToAnimateAway(@SelectedRows int selection) {
        final int viewCount = getChildCount();
        final ArrayList<View> viewsToHide = new ArrayList<>(viewCount);

        for (int i = 0; i < viewCount; i++) {
            final View view = getChildAt(i);

            if (shouldHideParent(view, selection)) {
                viewsToHide.add(view);
            }
            if (view instanceof ExpandableNotificationRow) {
                ExpandableNotificationRow parent = (ExpandableNotificationRow) view;

                if (isChildrenVisible(parent)) {
                    for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
                        if (isVisible(child) && includeChildInDismissAll(child, selection)) {
                            viewsToHide.add(child);
                        }
                    }

        if (mDismissListener != null) {
            mDismissListener.onDismiss(selection);
                }

        if (viewsToRemove.isEmpty()) {
            if (closeShade && mShadeController != null) {
                mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
            }
            return;
        }

        performDismissAllAnimations(
                viewsToHide,
                closeShade,
                () -> onDismissAllAnimationsEnd(viewsToRemove, selection));
        return viewsToHide;
    }

    private boolean includeChildInDismissAll(
            ExpandableNotificationRow row,
    private ArrayList<ExpandableNotificationRow> getRowsToDismissInBackend(
            @SelectedRows int selection) {
        return canChildBeDismissed(row) && matchesSelection(row, selection);
        final int childCount = getChildCount();
        final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(childCount);

        for (int i = 0; i < childCount; i++) {
            final View view = getChildAt(i);
            if (!(view instanceof ExpandableNotificationRow)) {
                continue;
            }
            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
            if (includeChildInDismissAll(parent, selection)) {
                viewsToRemove.add(parent);
            }
            List<ExpandableNotificationRow> children = parent.getAttachedChildren();
            if (isVisible(parent) && children != null) {
                for (ExpandableNotificationRow child : children) {
                    if (includeChildInDismissAll(parent, selection)) {
                        viewsToRemove.add(child);
                    }
                }
            }
        }
        return viewsToRemove;
    }

    /**
     * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
     * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
     * handler.
     *
     * @param hideAnimatedList List of rows to animated away. Should only be views that are
     *                         currently visible, or else the stagger will look funky.
     * @param closeShade Whether to close the shade after the stagger animation completes.
     * @param onAnimationComplete Called after the entire animation completes (including the shade
     *                            closing if appropriate). The rows must be dismissed for real here.
     * Collects a list of visible rows, and animates them away in a staggered fashion as if they
     * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd.
     */
    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
    private void performDismissAllAnimations(
            final ArrayList<View> hideAnimatedList,
            final boolean closeShade,
            final Runnable onAnimationComplete) {

        final Runnable onSlideAwayAnimationComplete = () -> {
            if (closeShade) {
                mShadeController.addPostCollapseAction(() -> {
                    setDismissAllInProgress(false);
                    onAnimationComplete.run();
                });
                mShadeController.animateCollapsePanels(
                        CommandQueue.FLAG_EXCLUDE_NONE);
            } else {
                setDismissAllInProgress(false);
                onAnimationComplete.run();
    @VisibleForTesting
    void clearNotifications(@SelectedRows int selection, boolean closeShade) {
        // Animate-swipe all dismissable notifications, then animate the shade closed
        final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
        final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend =
                getRowsToDismissInBackend(selection);
        if (mDismissListener != null) {
            mDismissListener.onDismiss(selection);
        }
        final Runnable dismissInBackend = () -> {
            onDismissAllAnimationsEnd(rowsToDismissInBackend, selection);
        };

        if (hideAnimatedList.isEmpty()) {
            onSlideAwayAnimationComplete.run();
        if (viewsToAnimateAway.isEmpty()) {
            dismissInBackend.run();
            return;
        }

        // let's disable our normal animations
        // Disable normal animations
        setDismissAllInProgress(true);
        mShadeNeedsToClose = closeShade;

        // Decrease the delay for every row we animate to give the sense of
        // accelerating the swipes
        int rowDelayDecrement = 10;
        int currentDelay = 140;
        int totalDelay = 180;
        int numItems = hideAnimatedList.size();
        final int rowDelayDecrement = 5;
        int currentDelay = 60;
        int totalDelay = 0;
        final int numItems = viewsToAnimateAway.size();
        for (int i = numItems - 1; i >= 0; i--) {
            View view = hideAnimatedList.get(i);
            View view = viewsToAnimateAway.get(i);
            Runnable endRunnable = null;
            if (i == 0) {
                endRunnable = onSlideAwayAnimationComplete;
                endRunnable = dismissInBackend;
            }
            dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
            currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
            currentDelay = Math.max(30, currentDelay - rowDelayDecrement);
            totalDelay += currentDelay;
        }
    }

    private boolean includeChildInDismissAll(
            ExpandableNotificationRow row,
            @SelectedRows int selection) {
        return canChildBeDismissed(row) && matchesSelection(row, selection);
    }

    /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
    public void setManageButtonClickListener(@Nullable OnClickListener listener) {
        mManageButtonClickListener = listener;
@@ -5114,6 +5143,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
                mFooterDismissListener.onDismiss();
            }
            clearNotifications(ROWS_ALL, true /* closeShade */);
            footerView.setSecondaryVisible(false /* visible */, true /* animate */);
        });
        setFooterView(footerView);
    }
+13 −2
Original line number Diff line number Diff line
@@ -1157,6 +1157,10 @@ public class NotificationStackScrollLayoutController {
                mZenModeController.areNotificationsHiddenInShade());
    }

    public boolean areNotificationsHiddenInShade() {
        return mZenModeController.areNotificationsHiddenInShade();
    }

    public boolean isShowingEmptyShadeView() {
        return mShowEmptyShadeView;
    }
@@ -1205,6 +1209,10 @@ public class NotificationStackScrollLayoutController {
     * Return whether there are any clearable notifications
     */
    public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
        return hasNotifications(selection, true /* clearable */);
    }

    public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
        if (mDynamicPrivacyController.isInLockedDownShade()) {
            return false;
        }
@@ -1215,8 +1223,11 @@ public class NotificationStackScrollLayoutController {
                continue;
            }
            final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
            if (row.canViewBeDismissed() &&
                    NotificationStackScrollLayout.matchesSelection(row, selection)) {
            final boolean matchClearable =
                    isClearable ? row.canViewBeDismissed() : !row.canViewBeDismissed();
            final boolean inSection =
                    NotificationStackScrollLayout.matchesSelection(row, selection);
            if (matchClearable && inSection) {
                return true;
            }
        }
Loading