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

Commit 1baaee6f authored by András Kurucz's avatar András Kurucz
Browse files

Tweak HUN intro/outro animations

Sync the height and alpha animation with the same interpolator, and
adjust the range of the alpha interpolation to avoid collision with the
height clipping and the content.
Add a translateY animation to move in from (and move out to) the start
of the screen.

Fixes: b/243302608
Test: atest StackStateAnimatorTest StackScrollAlgorithmTest
Test: post HUNs and observe the appear/disappear animations
Flag: LEGACY IMPROVED_HUN_ANIMATIONS DEVELOPMENT
Change-Id: I31a42f1e0af0c0ca5df5b8a53b260aaa79807d32
parent 305199f3
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -274,6 +274,10 @@
    <!-- Side padding on the side of notifications -->
    <dimen name="notification_side_paddings">16dp</dimen>

    <!-- Starting translateY offset of the HUN appear and disappear animations. Indicates
    the amount by the view is positioned above the screen before the animation starts. -->
    <dimen name="heads_up_appear_y_above_screen">32dp</dimen>

    <!-- padding between the heads up and the statusbar -->
    <dimen name="heads_up_status_bar_padding">8dp</dimen>

+12 −9
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.app.animation.Interpolators;
import com.android.internal.jank.InteractionJankMonitor;
@@ -68,7 +67,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
     * The content of the view should start showing at animation progress value of
     * #ALPHA_APPEAR_START_FRACTION.
     */
    private static final float ALPHA_APPEAR_START_FRACTION = .4f;

    private static final float ALPHA_APPEAR_START_FRACTION = .7f;
    /**
     * The content should show fully with progress at #ALPHA_APPEAR_END_FRACTION
     * The start of the animation is at #ALPHA_APPEAR_START_FRACTION
@@ -87,9 +87,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
     */
    private boolean mActivated;

    private final Interpolator mSlowOutFastInInterpolator;
    private Interpolator mCurrentAppearInterpolator;

    NotificationBackgroundView mBackgroundNormal;
    private float mAnimationTranslationY;
    private boolean mDrawingAppearAnimation;
@@ -117,7 +115,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView

    public ActivatableNotificationView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
        setClipChildren(false);
        setClipToPadding(false);
        updateColors();
@@ -401,12 +398,16 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
            mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
            targetValue = 1.0f;
        } else {
            mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
            mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN_REVERSE;
            targetValue = 0.0f;
        }
        mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
                targetValue);
        if (NotificationsImprovedHunAnimation.isEnabled()) {
            mAppearAnimator.setInterpolator(mCurrentAppearInterpolator);
        } else {
            mAppearAnimator.setInterpolator(Interpolators.LINEAR);
        }
        mAppearAnimator.setDuration(
                (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
        mAppearAnimator.addUpdateListener(animation -> {
@@ -503,8 +504,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
    }

    private void updateAppearRect() {
        float interpolatedFraction = mCurrentAppearInterpolator.getInterpolation(
                mAppearAnimationFraction);
        float interpolatedFraction =
                NotificationsImprovedHunAnimation.isEnabled() ? mAppearAnimationFraction
                        : mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
        mAppearAnimationTranslation = (1.0f - interpolatedFraction) * mAnimationTranslationY;
        final int actualHeight = getActualHeight();
        float bottom = actualHeight * interpolatedFraction;
@@ -525,6 +527,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
    }

    private float getInterpolatedAppearAnimationFraction() {

        if (mAppearAnimationFraction >= 0) {
            return mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
        }
+11 −6
Original line number Diff line number Diff line
@@ -115,6 +115,7 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
@@ -3328,8 +3329,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
                    logHunAnimationSkipped(row, "row has no viewState");
                    continue;
                }
                boolean shouldHunAppearFromTheBottom =
                        mStackScrollAlgorithm.shouldHunAppearFromBottom(mAmbientState, viewState);
                if (isHeadsUp && (mAddedHeadsUpChildren.contains(row) || pinnedAndClosed)) {
                    if (pinnedAndClosed || shouldHunAppearFromBottom(viewState)) {
                    if (pinnedAndClosed || shouldHunAppearFromTheBottom) {
                        // Our custom add animation
                        type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
                    } else {
@@ -3341,6 +3344,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
            }
            AnimationEvent event = new AnimationEvent(row, type);
            event.headsUpFromBottom = onBottom;
            if (NotificationsImprovedHunAnimation.isEnabled()) {
                // TODO(b/283084712) remove this with the flag and update the HUN filters at
                //  creation
                event.filter.animateHeight = false;
            }
            mAnimationEvents.add(event);
            if (SPEW) {
                Log.v(TAG, "Generating HUN animation event: "
@@ -3355,11 +3363,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
        mAddedHeadsUpChildren.clear();
    }

    private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
        return viewState.getYTranslation() + viewState.height
                >= mAmbientState.getMaxHeadsUpTranslation();
    }

    private void generateGroupExpansionEvent() {
        // Generate a group expansion/collapsing event if there is such a group at all
        if (mExpandedGroupView != null) {
@@ -4932,7 +4935,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
     */
    public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
        mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
        mStackScrollAlgorithm.setHeadsUpAppearHeightBottom(height);
        mStateAnimator.setHeadsUpAppearHeightBottom(height);
        mStateAnimator.setStackTopMargin(mAmbientState.getStackTopMargin());
        requestChildrenUpdate();
    }

+51 −8
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;

import java.util.ArrayList;
import java.util.List;
@@ -66,12 +67,15 @@ public class StackScrollAlgorithm {
    private boolean mClipNotificationScrollToTop;
    @VisibleForTesting
    float mHeadsUpInset;
    @VisibleForTesting
    float mHeadsUpAppearStartAboveScreen;
    private int mPinnedZTranslationExtra;
    private float mNotificationScrimPadding;
    private int mMarginBottom;
    private float mQuickQsOffsetHeight;
    private float mSmallCornerRadius;
    private float mLargeCornerRadius;
    private int mHeadsUpAppearHeightBottom;

    public StackScrollAlgorithm(
            Context context,
@@ -94,6 +98,8 @@ public class StackScrollAlgorithm {
        int statusBarHeight = SystemBarUtils.getStatusBarHeight(context);
        mHeadsUpInset = statusBarHeight + res.getDimensionPixelSize(
                R.dimen.heads_up_status_bar_padding);
        mHeadsUpAppearStartAboveScreen = res.getDimensionPixelSize(
                R.dimen.heads_up_appear_y_above_screen);
        mPinnedZTranslationExtra = res.getDimensionPixelSize(
                R.dimen.heads_up_pinned_elevation);
        mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
@@ -221,6 +227,25 @@ public class StackScrollAlgorithm {
        return getExpansionFractionWithoutShelf(mTempAlgorithmState, ambientState);
    }

    public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
    }

    /**
     * If the QuickSettings is showing full screen, we want to animate the HeadsUp Notifications
     * from the bottom of the screen.
     *
     * @param ambientState Current ambient state.
     * @param viewState The state of the HUN that is being queried to appear from the bottom.
     *
     * @return true if the HeadsUp Notifications should appear from the bottom
     */
    public boolean shouldHunAppearFromBottom(AmbientState ambientState,
            ExpandableViewState viewState) {
        return viewState.getYTranslation() + viewState.height
                >= ambientState.getMaxHeadsUpTranslation();
    }

    public static void log(String s) {
        if (DEBUG) {
            android.util.Log.i(TAG, s);
@@ -793,10 +818,16 @@ public class StackScrollAlgorithm {
                }
            }
            if (row.isPinned()) {
                if (NotificationsImprovedHunAnimation.isEnabled()) {
                    // Make sure row yTranslation is at the HUN yTranslation,
                    // which accounts for AmbientState.stackTopMargin in split-shade.
                    childState.setYTranslation(headsUpTranslation);
                } else {
                    // Make sure row yTranslation is at maximum the HUN yTranslation,
                    // which accounts for AmbientState.stackTopMargin in split-shade.
                    childState.setYTranslation(
                            Math.max(childState.getYTranslation(), headsUpTranslation));
                }
                childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                childState.hidden = false;
                ExpandableViewState topState =
@@ -819,10 +850,22 @@ public class StackScrollAlgorithm {
                }
            }
            if (row.isHeadsUpAnimatingAway()) {
                if (NotificationsImprovedHunAnimation.isEnabled()) {
                    if (shouldHunAppearFromBottom(ambientState, childState)) {
                        // move to the bottom of the screen
                        childState.setYTranslation(
                                mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
                    } else {
                        // move to the top of the screen
                        childState.setYTranslation(-ambientState.getStackTopMargin()
                                - mHeadsUpAppearStartAboveScreen);
                    }
                } else {
                    // Make sure row yTranslation is at maximum the HUN yTranslation,
                    // which accounts for AmbientState.stackTopMargin in split-shade.
                    childState.setYTranslation(
                            Math.max(childState.getYTranslation(), headsUpTranslation));
                }
                // keep it visible for the animation
                childState.hidden = false;
            }
+48 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar.notification.stack;

import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK;

@@ -26,6 +27,7 @@ import android.util.Property;
import android.view.View;

import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
@@ -33,6 +35,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;

import java.util.ArrayList;
import java.util.HashSet;
@@ -68,6 +71,8 @@ public class StackStateAnimator {

    private final int mGoToFullShadeAppearingTranslation;
    private final int mPulsingAppearingTranslation;
    @VisibleForTesting
    float mHeadsUpAppearStartAboveScreen;
    private final ExpandableViewState mTmpState = new ExpandableViewState();
    private final AnimationProperties mAnimationProperties;
    public NotificationStackScrollLayout mHostLayout;
@@ -85,21 +90,23 @@ public class StackStateAnimator {
    private ValueAnimator mTopOverScrollAnimator;
    private ValueAnimator mBottomOverScrollAnimator;
    private int mHeadsUpAppearHeightBottom;
    private int mStackTopMargin;
    private boolean mShadeExpanded;
    private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
    private NotificationShelf mShelf;
    private float mStatusBarIconLocation;
    private int[] mTmpLocation = new int[2];
    private StackStateLogger mLogger;

    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
        mHostLayout = hostLayout;
        // TODO(b/317061579) reload on configuration changes
        mGoToFullShadeAppearingTranslation =
                hostLayout.getContext().getResources().getDimensionPixelSize(
                        R.dimen.go_to_full_shade_appearing_translation);
        mPulsingAppearingTranslation =
                hostLayout.getContext().getResources().getDimensionPixelSize(
                        R.dimen.pulsing_notification_appear_translation);
        mHeadsUpAppearStartAboveScreen = hostLayout.getContext().getResources()
                .getDimensionPixelSize(R.dimen.heads_up_appear_y_above_screen);
        mAnimationProperties = new AnimationProperties() {
            @Override
            public AnimationFilter getAnimationFilter() {
@@ -455,8 +462,37 @@ public class StackStateAnimator {
                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
                ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
                row.prepareExpansionChanged();
            } else if (event.animationType == NotificationStackScrollLayout
                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
            } else if (NotificationsImprovedHunAnimation.isEnabled()
                    && (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR)) {
                mHeadsUpAppearChildren.add(changingView);

                mTmpState.copyFrom(changingView.getViewState());
                if (event.headsUpFromBottom) {
                    // start from the bottom of the screen
                    mTmpState.setYTranslation(
                            mHeadsUpAppearHeightBottom + mHeadsUpAppearStartAboveScreen);
                } else {
                    // start from the top of the screen
                    mTmpState.setYTranslation(
                            -mStackTopMargin - mHeadsUpAppearStartAboveScreen);
                }
                // set the height and the initial position
                mTmpState.applyToView(changingView);
                mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
                        Interpolators.FAST_OUT_SLOW_IN);

                Runnable onAnimationEnd = null;
                if (loggable) {
                    // This only captures HEADS_UP_APPEAR animations, but HUNs can appear with
                    // normal ADD animations, which would not be logged here.
                    String finalKey = key;
                    mLogger.logHUNViewAppearing(key);
                    onAnimationEnd = () -> mLogger.appearAnimationEnded(finalKey);
                }
                changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR,
                        /* isHeadsUpAppear= */ true, onAnimationEnd);
            } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR) {
                NotificationsImprovedHunAnimation.assertInLegacyMode();
                // This item is added, initialize its properties.
                ExpandableViewState viewState = changingView.getViewState();
                mTmpState.copyFrom(viewState);
@@ -536,6 +572,10 @@ public class StackStateAnimator {
                            changingView.setInRemovalAnimation(true);
                        };
                    }
                    if (NotificationsImprovedHunAnimation.isEnabled()) {
                        mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
                                Interpolators.FAST_OUT_SLOW_IN_REVERSE);
                    }
                    long removeAnimationDelay = changingView.performRemoveAnimation(
                            ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
                            0, 0.0f, true /* isHeadsUpAppear */,
@@ -601,6 +641,10 @@ public class StackStateAnimator {
        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
    }

    public void setStackTopMargin(int stackTopMargin) {
        mStackTopMargin = stackTopMargin;
    }

    public void setShadeExpanded(boolean shadeExpanded) {
        mShadeExpanded = shadeExpanded;
    }
Loading