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

Commit 6b9d8251 authored by András Kurucz's avatar András Kurucz
Browse files

Fix HUN disappear flickering by canceling animators after clipping

Root cause: With physicalNotificationMovement enabled, the HUN y-position
spring animation lasts longer than its content/clipping animation. This
causes the clipping rectangle to be reset prematurely while the NPV is still
visible due to the ongoing y-translation, resulting in a visual glitch.

Fix: Advance any running animators to the end for the HUN disappearing,
after its content animation has finished, because at this point the view is
fully clipped off, and it shouldn't be visible anyways.
This results in calling NSSL#onChildAnimationFinished(), which triggers
the natural way of finalizing the HUN disappear animation, which is
setting headsUpAnimationAway to false, which hides the Notification Panel.

Fixes: 406254389
Test: observe the HUN appear/disappear animations
Flag: com.android.systemui.physical_notification_movement
Change-Id: I6cdac864fd58db13ca8996f7d4425a740f366351
parent 70ba5e41
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

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

import static com.android.systemui.Flags.physicalNotificationMovement;
import static com.android.systemui.statusbar.notification.row.ExpandableView.HEIGHT_PROPERTY;
import static com.android.systemui.statusbar.notification.row.ExpandableView.TAG_ANIMATOR_HEIGHT;

@@ -24,15 +23,11 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.util.FloatProperty;
import android.view.View;

import androidx.annotation.NonNull;

import com.android.app.animation.Interpolators;
import com.android.internal.dynamicanimation.animation.DynamicAnimation;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.PhysicsProperty;
import com.android.systemui.statusbar.notification.PhysicsPropertyAnimator;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -416,4 +411,11 @@ public class ExpandableViewState extends ViewState {
        abortAnimation(view, TAG_ANIMATOR_HEIGHT);
        abortAnimation(view, TAG_ANIMATOR_TOP_INSET);
    }

    @Override
    public void finishAnimations(View view) {
        super.finishAnimations(view);
        skipAnimationToEnd(view, TAG_ANIMATOR_HEIGHT);
        skipAnimationToEnd(view, TAG_ANIMATOR_TOP_INSET);
    }
}
+31 −23
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.stack;

import static com.android.systemui.Flags.physicalNotificationMovement;
import static com.android.systemui.statusbar.notification.row.ExpandableView.HEIGHT_PROPERTY;
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_CYCLING_IN;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_CYCLING_OUT;
@@ -693,8 +692,8 @@ public class StackStateAnimator {
                    // running anymore, the panel will instantly hide itself. We need to wait until
                    // the animation is fully finished for this though.
                    final Runnable tmpEndRunnable = endRunnable;
                    Runnable postAnimation;
                    Runnable startAnimation;
                    final Runnable logAnimationStart;
                    final Runnable logAnimationEnd;
                    if (loggable) {
                        String finalKey1 = key;
                        final boolean finalIsHeadsUp = isHeadsUp;
@@ -702,28 +701,37 @@ public class StackStateAnimator {
                                event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR
                                        ? "ANIMATION_TYPE_HEADS_UP_DISAPPEAR"
                                        : "ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK";
                        startAnimation = () -> {
                            mLogger.animationStart(finalKey1, type, finalIsHeadsUp);
                            changingView.setInRemovalAnimation(true);
                        };
                        postAnimation = () -> {
                            mLogger.animationEnd(finalKey1, type, finalIsHeadsUp);
                            changingView.setInRemovalAnimation(false);
                            if (tmpEndRunnable != null) {
                                tmpEndRunnable.run();
                            }
                        };
                        logAnimationStart = () -> mLogger.animationStart(finalKey1, type,
                                finalIsHeadsUp);
                        logAnimationEnd = () -> mLogger.animationEnd(finalKey1, type,
                                finalIsHeadsUp);
                    } else {
                        startAnimation = () -> {
                        logAnimationStart = null;
                        logAnimationEnd = null;
                    }

                    Runnable startAnimation = () -> {
                        if (logAnimationStart != null) {
                            logAnimationStart.run();
                        }
                        changingView.setInRemovalAnimation(true);
                    };
                        postAnimation = () -> {
                    Runnable postAnimation = () -> {
                        if (logAnimationEnd != null) {
                            logAnimationEnd.run();
                        }
                        changingView.setInRemovalAnimation(false);
                        if (physicalNotificationMovement()) {
                            // Finish any running animations, in case the content disappear
                            // animation would finish earlier.
                            // Note: a cancellation would not be finalized by onAnimationFinished().
                            changingView.getViewState().finishAnimations(changingView);
                        }
                        if (tmpEndRunnable != null) {
                            tmpEndRunnable.run();
                        }
                    };
                    }

                    long removeAnimationDelay = changingView.performRemoveAnimation(
                            ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
                            0, 0.0f, true /* isHeadsUpAppear */,
+32 −0
Original line number Diff line number Diff line
@@ -807,6 +807,23 @@ public class ViewState implements Dumpable {
        }
    }

    protected void skipAnimationToEnd(View child, int animatorTag) {
        Object storedTag = getChildTag(child, animatorTag);
        if (storedTag != null) {
            if (storedTag instanceof Animator animator) {
                animator.end();
            } else if (storedTag instanceof PropertyData propertyData) {
                // Physics based animation!
                Runnable delayRunnable = propertyData.getDelayRunnable();
                child.removeCallbacks(delayRunnable);
                SpringAnimation animator = propertyData.getAnimator();
                if (animator != null) {
                    animator.skipToEnd();
                }
            }
        }
    }

    /**
     * Cancel the previous animator and get the duration of the new animation.
     *
@@ -848,6 +865,10 @@ public class ViewState implements Dumpable {
        return isAnimating(child, TAG_ANIMATOR_TRANSLATION_Y);
    }

    /**
     * Abort the animations. Unlike {@link #finishAnimations(View)} this causes the animators
     * to stop in their tracks.
     */
    public void cancelAnimations(View view) {
        abortAnimation(view, TAG_ANIMATOR_TRANSLATION_X);
        abortAnimation(view, TAG_ANIMATOR_TRANSLATION_Y);
@@ -855,6 +876,17 @@ public class ViewState implements Dumpable {
        abortAnimation(view, TAG_ANIMATOR_ALPHA);
    }

    /**
     * Finish the animations. This causes any currently running animators to jump to the end
     * value of the property being animated, and to trigger any animation end listeners.
     */
    public void finishAnimations(View view) {
        skipAnimationToEnd(view, TAG_ANIMATOR_TRANSLATION_X);
        skipAnimationToEnd(view, TAG_ANIMATOR_TRANSLATION_Y);
        skipAnimationToEnd(view, TAG_ANIMATOR_TRANSLATION_Z);
        skipAnimationToEnd(view, TAG_ANIMATOR_ALPHA);
    }

    @Override
    public void dump(PrintWriter pw, String[] args) {
        StringBuilder result = new StringBuilder();