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

Commit 1e2fb5a7 authored by Ioana Alexandru's avatar Ioana Alexandru
Browse files

Re-structure StackScrollerDecorView.

This is not a behavior change, just moving some code around and renaming
some things to make them more readable and consistent.

More specifically:
- re-ordering methods a bit so that related ones are in the same place,
  and the order and parameter naming is consistent
- renamed things that were called Runnable without actually being a
  runnable, and turned a couple of consumers into methods for simplicity
- made isSecondaryVisible/setSecondaryVisible protected, because it
  tends to be confusing when you don't know what the secondary view
  actually is (e.g. in the footer, it's the Clear all button); it makes
  sense for the child class to define these methods more clearly

Bug: 293167744
Test: presubmit + manually checked that the footer view, empty shade and
silent section behave normally
Flag: NONE

Change-Id: I3b7d70a97472493fc00a741848e0aa36e62edc88
parent 7a17d789
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ public class EmptyShadeView extends StackScrollerDecorView {
            if (view instanceof EmptyShadeView) {
                EmptyShadeView emptyShadeView = (EmptyShadeView) view;
                boolean visible = this.clipTopAmount <= mEmptyText.getPaddingTop() * 0.6f;
                emptyShadeView.setContentVisible(visible && emptyShadeView.isVisible());
                emptyShadeView.setContentVisibleAnimated(visible && emptyShadeView.isVisible());
            }
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ class SectionHeaderNodeControllerImpl @Inject constructor(
    }

    override fun onViewAdded() {
        headerView?.isContentVisible = true
        headerView?.setContentVisibleAnimated(true)
    }

    override val view: View
+14 −1
Original line number Diff line number Diff line
@@ -79,6 +79,19 @@ public class FooterView extends StackScrollerDecorView {
        return findViewById(R.id.dismiss_text);
    }

    /** Whether the "Clear all" button is currently visible. */
    public boolean isClearAllButtonVisible() {
        return isSecondaryVisible();
    }

    /**
     * Set the visibility of the "Clear all" button to {@code visible}. Animate the change if
     * {@code animate} is true.
     */
    public void setClearAllButtonVisible(boolean visible, boolean animate) {
        setSecondaryVisible(visible, animate);
    }

    @Override
    public void dump(PrintWriter pwOriginal, String[] args) {
        IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
@@ -293,7 +306,7 @@ public class FooterView extends StackScrollerDecorView {
            super.applyToView(view);
            if (view instanceof FooterView) {
                FooterView footerView = (FooterView) view;
                footerView.setContentVisible(!hideContent);
                footerView.setContentVisibleAnimated(!hideContent);
            }
        }
    }
+106 −101
Original line number Diff line number Diff line
@@ -39,28 +39,9 @@ public abstract class StackScrollerDecorView extends ExpandableView {
    private boolean mIsVisible = true;
    private boolean mContentVisible = true;
    private boolean mIsSecondaryVisible = true;
    private int mDuration = 260;
    private int mAnimationDuration = 260;
    private boolean mContentAnimating;
    private final Runnable mContentVisibilityEndRunnable = () -> {
        mContentAnimating = false;
        if (getVisibility() != View.GONE && !mIsVisible) {
            setVisibility(GONE);
            setWillBeGone(false);
            notifyHeightChanged(false /* needsAnimation */);
        }
    };

    private boolean mSecondaryAnimating = false;
    private final Consumer<Boolean> mSecondaryVisibilityEndRunnable = (cancelled) -> {
        mSecondaryAnimating = false;
        // If we were on screen, become GONE to avoid touches
        if (mSecondaryView == null) return;
        if (getVisibility() != View.GONE
                && mSecondaryView.getVisibility() != View.GONE
                && !mIsSecondaryVisible) {
            mSecondaryView.setVisibility(View.GONE);
        }
    };

    public StackScrollerDecorView(Context context, AttributeSet attrs) {
        super(context, attrs);
@@ -72,8 +53,8 @@ public abstract class StackScrollerDecorView extends ExpandableView {
        super.onFinishInflate();
        mContent = findContentView();
        mSecondaryView = findSecondaryView();
        setVisible(false /* nowVisible */, false /* animate */);
        setSecondaryVisible(false /* nowVisible */, false /* animate */);
        setVisible(false /* visible */, false /* animate */);
        setSecondaryVisible(false /* visible */, false /* animate */);
        setOutlineProvider(null);
    }

@@ -83,35 +64,39 @@ public abstract class StackScrollerDecorView extends ExpandableView {
    }

    /**
     * @param visible True if we should animate contents visible
     * Is this view visible. If a view is currently animating to gone, it will
     * return {@code false}.
     */
    public void setContentVisible(boolean visible) {
        setContentVisible(visible, true /* animate */, null /* runAfter */);
    public boolean isVisible() {
        return mIsVisible;
    }

    /**
     * @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
     * Make this view visible. If {@code false} is passed, the view will fade out its content
     * and set the view Visibility to GONE. If only the content should be changed,
     * {@link #setContentVisibleAnimated(boolean)} can be used.
     *
     * @param visible True if the contents should be visible.
     * @param animate True if we should fade to new visibility.
     */
    public void setContentVisible(boolean visible, boolean animate, Consumer<Boolean> runAfter) {
        if (mContentVisible != visible) {
            mContentAnimating = animate;
            mContentVisible = visible;
            Consumer<Boolean> endRunnable = (cancelled) -> {
                mContentVisibilityEndRunnable.run();
                if (runAfter != null) {
                    runAfter.accept(cancelled);
    public void setVisible(boolean visible, boolean animate) {
        if (mIsVisible != visible) {
            mIsVisible = visible;
            if (animate) {
                if (visible) {
                    setVisibility(VISIBLE);
                    setWillBeGone(false);
                    notifyHeightChanged(false /* needsAnimation */);
                } else {
                    setWillBeGone(true);
                }
            };
            setViewVisible(mContent, visible, animate, endRunnable);
        } else if (runAfter != null) {
            // Execute the runAfter runnable immediately if there's no animation to perform.
            runAfter.accept(true);
                setContentVisible(visible, true /* animate */, null /* runAfter */);
            } else {
                setVisibility(visible ? VISIBLE : GONE);
                setContentVisible(visible, false /* animate */, null /* runAfter */);
                setWillBeGone(false);
                notifyHeightChanged(false /* needsAnimation */);
            }

        if (!mContentAnimating) {
            mContentVisibilityEndRunnable.run();
        }
    }

@@ -120,79 +105,94 @@ public abstract class StackScrollerDecorView extends ExpandableView {
    }

    /**
     * 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
     * {@link #setContentVisible(boolean)} can be used.
     *
     * @param nowVisible should the view be visible
     * @param animate should the change be animated.
     * Change content visibility to {@code visible}, animated.
     */
    public void setVisible(boolean nowVisible, boolean animate) {
        if (mIsVisible != nowVisible) {
            mIsVisible = nowVisible;
            if (animate) {
                if (nowVisible) {
                    setVisibility(VISIBLE);
                    setWillBeGone(false);
                    notifyHeightChanged(false /* needsAnimation */);
                } else {
                    setWillBeGone(true);
    public void setContentVisibleAnimated(boolean visible) {
        setContentVisible(visible, true /* animate */, null /* runAfter */);
    }
                setContentVisible(nowVisible, true /* animate */, null /* runAfter */);
            } else {
                setVisibility(nowVisible ? VISIBLE : GONE);
                setContentVisible(nowVisible, false /* animate */, null /* runAfter */);

    /**
     * @param visible          True if the contents should be visible.
     * @param animate          True if we should fade to new visibility.
     * @param onAnimationEnded Callback to run after visibility updates, takes a boolean as a
     *                         parameter that represents whether the animation was cancelled.
     */
    public void setContentVisible(boolean visible, boolean animate,
            Consumer<Boolean> onAnimationEnded) {
        if (mContentVisible != visible) {
            mContentAnimating = animate;
            mContentVisible = visible;
            Consumer<Boolean> onAnimationEndedWrapper = (cancelled) -> {
                onContentVisibilityAnimationEnd();
                if (onAnimationEnded != null) {
                    onAnimationEnded.accept(cancelled);
                }
            };
            setViewVisible(mContent, visible, animate, onAnimationEndedWrapper);
        } else if (onAnimationEnded != null) {
            // Execute onAnimationEnded immediately if there's no animation to perform.
            onAnimationEnded.accept(true /* cancelled */);
        }

        if (!mContentAnimating) {
            onContentVisibilityAnimationEnd();
        }
    }

    private void onContentVisibilityAnimationEnd() {
        mContentAnimating = false;
        if (getVisibility() != View.GONE && !mIsVisible) {
            setVisibility(GONE);
            setWillBeGone(false);
            notifyHeightChanged(false /* needsAnimation */);
        }
    }

    protected boolean isSecondaryVisible() {
        return mIsSecondaryVisible;
    }

    /**
     * Set the secondary view of this layout to visible.
     *
     * @param nowVisible should the secondary view be visible
     * @param visible should the secondary view be visible
     * @param animate should the change be animated
     */
    public void setSecondaryVisible(boolean nowVisible, boolean animate) {
        if (mIsSecondaryVisible != nowVisible) {
    protected void setSecondaryVisible(boolean visible, boolean animate) {
        if (mIsSecondaryVisible != visible) {
            mSecondaryAnimating = animate;
            mIsSecondaryVisible = nowVisible;
            setViewVisible(mSecondaryView, nowVisible, animate, mSecondaryVisibilityEndRunnable);
            mIsSecondaryVisible = visible;
            setViewVisible(mSecondaryView, visible, animate,
                    (cancelled) -> onSecondaryVisibilityAnimationEnd());
        }

        if (!mSecondaryAnimating) {
            mSecondaryVisibilityEndRunnable.accept(true /* cancelled */);
            onSecondaryVisibilityAnimationEnd();
        }
    }

    @VisibleForTesting
    public boolean isSecondaryVisible() {
        return mIsSecondaryVisible;
    }

    /**
     * Is this view visible. If a view is currently animating to gone, it will
     * return {@code false}.
     */
    public boolean isVisible() {
        return mIsVisible;
    private void onSecondaryVisibilityAnimationEnd() {
        mSecondaryAnimating = false;
        // If we were on screen, become GONE to avoid touches
        if (mSecondaryView == null) return;
        if (getVisibility() != View.GONE
                && mSecondaryView.getVisibility() != View.GONE
                && !mIsSecondaryVisible) {
            mSecondaryView.setVisibility(View.GONE);
        }

    @VisibleForTesting
    public void setDuration(int duration) {
        mDuration = duration;
    }

    /**
     * Animate a view to a new visibility.
     *
     * @param view             Target view, maybe content view or dismiss view.
     * @param nowVisible Should it now be visible.
     * @param visible          Should it now be visible.
     * @param animate          Should this be done in an animated way.
     * @param endRunnable A runnable that is run when the animation is done.
     * @param onAnimationEnded Callback to run after visibility updates, takes a boolean as a
     *                         parameter that represents whether the animation was cancelled.
     */
    private void setViewVisible(View view, boolean nowVisible,
            boolean animate, Consumer<Boolean> endRunnable) {
    private void setViewVisible(View view, boolean visible,
            boolean animate, Consumer<Boolean> onAnimationEnded) {
        if (view == null) {
            return;
        }
@@ -204,21 +204,21 @@ public abstract class StackScrollerDecorView extends ExpandableView {

        // cancel any previous animations
        view.animate().cancel();
        float endValue = nowVisible ? 1.0f : 0.0f;
        float endValue = visible ? 1.0f : 0.0f;
        if (!animate) {
            view.setAlpha(endValue);
            if (endRunnable != null) {
                endRunnable.accept(true);
            if (onAnimationEnded != null) {
                onAnimationEnded.accept(true);
            }
            return;
        }

        // Animate the view alpha
        Interpolator interpolator = nowVisible ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT;
        Interpolator interpolator = visible ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT;
        view.animate()
                .alpha(endValue)
                .setInterpolator(interpolator)
                .setDuration(mDuration)
                .setDuration(mAnimationDuration)
                .setListener(new AnimatorListenerAdapter() {
                    boolean mCancelled;

@@ -229,11 +229,16 @@ public abstract class StackScrollerDecorView extends ExpandableView {

                    @Override
                    public void onAnimationEnd(Animator animation) {
                        endRunnable.accept(mCancelled);
                        onAnimationEnded.accept(mCancelled);
                    }
                });
    }

    @VisibleForTesting
    public void setAnimationDuration(int animationDuration) {
        mAnimationDuration = animationDuration;
    }

    @Override
    public long performRemoveAnimation(long duration, long delay,
            float translationDirection, boolean isHeadsUpAnimation,
@@ -251,14 +256,14 @@ public abstract class StackScrollerDecorView extends ExpandableView {
    @Override
    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
        // TODO: use delay and duration
        setContentVisible(true);
        setContentVisibleAnimated(true);
    }

    @Override
    public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear,
            Runnable endRunnable) {
        // TODO: use delay and duration
        setContentVisible(true);
        setContentVisibleAnimated(true);
    }

    @Override
+2 −2
Original line number Diff line number Diff line
@@ -4573,7 +4573,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
                mFooterClearAllListener.onClearAll();
            }
            clearNotifications(ROWS_ALL, true /* closeShade */);
            footerView.setSecondaryVisible(false /* visible */, true /* animate */);
            footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
        });
        if (FooterViewRefactor.isEnabled()) {
            updateFooter();
@@ -4638,7 +4638,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        }
        boolean animate = mIsExpanded && mAnimationsEnabled;
        mFooterView.setVisible(visible, animate);
        mFooterView.setSecondaryVisible(showDismissView, animate);
        mFooterView.setClearAllButtonVisible(showDismissView, animate);
        mFooterView.showHistory(showHistory);
        if (!FooterViewRefactor.isEnabled()) {
            mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
Loading