Tuned the heads up animations more

The previous animation was using the linear interpolator
for the shape change, which is now properly using the
existing interpolator. Also the roundedness is now using
the interpolator to feel more connected to the rest of
the animation.
This also fixes some bugs where the heads up was animating
it's corner roundedness immediately, before it was
decided whether we will animating away, leading to some issues.

Lastly, this also adds a failsafe for the roundedness, which
could become really large if the roundness was updated without
an animation, while an animation was running already.

Fixes: 187175147
Test: add hun, observe nice animation
Change-Id: I4561eed97387e3465e7ea61066bffa11d8e6bda0
This commit is contained in:
Selim Cinek
2021-06-07 20:02:10 +02:00
parent 00630da0cf
commit d2ad623032
7 changed files with 50 additions and 106 deletions

View File

@@ -60,12 +60,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
*/
private static final float HORIZONTAL_ANIMATION_END = 0.2f;
/**
* At which point from [0,1] does the alpha animation end (or start when
* expanding)? 1.0 meaning that it ends immediately and 0.0 that it is continuously animated.
*/
private static final float ALPHA_ANIMATION_END = 0.0f;
/**
* At which point from [0,1] does the horizontal collapse animation start (or start when
* expanding)? 1.0 meaning that it starts immediately and 0.0 that it is animated at all.
@@ -497,10 +491,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
float targetValue;
if (isAppearing) {
mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
targetValue = 1.0f;
} else {
mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
targetValue = 0.0f;
}
mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
@@ -584,19 +578,21 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
private void updateAppearRect() {
float inverseFraction = (1.0f - mAppearAnimationFraction);
float translationFraction = mCurrentAppearInterpolator.getInterpolation(inverseFraction);
float translateYTotalAmount = translationFraction * mAnimationTranslationY;
mAppearAnimationTranslation = translateYTotalAmount;
float interpolatedFraction = mCurrentAppearInterpolator.getInterpolation(
mAppearAnimationFraction);
mAppearAnimationTranslation = (1.0f - interpolatedFraction) * mAnimationTranslationY;
final int actualHeight = getActualHeight();
float bottom = actualHeight * mAppearAnimationFraction;
float bottom = actualHeight * interpolatedFraction;
setOutlineRect(0, mAppearAnimationTranslation,
getWidth(), bottom + mAppearAnimationTranslation);
setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
bottom + mAppearAnimationTranslation);
}
private float getAppearAnimationFraction() {
return mAppearAnimationFraction >= 0 ? mAppearAnimationFraction : 1;
private float getInterpolatedAppearAnimationFraction() {
if (mAppearAnimationFraction >= 0) {
return mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
}
return 1.0f;
}
private void updateAppearAnimationAlpha() {
@@ -629,18 +625,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
@Override
public float getCurrentBackgroundRadiusTop() {
float fraction = getAppearAnimationFraction();
return isHeadsUpAnimatingAway() || isHeadsUp()
? mOutlineRadius * fraction
: super.getCurrentBackgroundRadiusTop();
float fraction = getInterpolatedAppearAnimationFraction();
return MathUtils.lerp(0, super.getCurrentBackgroundRadiusTop(), fraction);
}
@Override
public float getCurrentBackgroundRadiusBottom() {
float fraction = getAppearAnimationFraction();
return isHeadsUpAnimatingAway() || isHeadsUp()
? mOutlineRadius * fraction
: super.getCurrentBackgroundRadiusBottom();
float fraction = getInterpolatedAppearAnimationFraction();
return MathUtils.lerp(0, super.getCurrentBackgroundRadiusBottom(), fraction);
}
private void applyBackgroundRoundness(float topRadius, float bottomRadius) {

View File

@@ -249,9 +249,18 @@ public abstract class ExpandableOutlineView extends ExpandableView {
@Override
public boolean setTopRoundness(float topRoundness, boolean animate) {
if (mTopRoundness != topRoundness) {
float diff = Math.abs(topRoundness - mTopRoundness);
mTopRoundness = topRoundness;
boolean shouldAnimate = animate;
if (PropertyAnimator.isAnimating(this, TOP_ROUNDNESS) && diff > 0.5f) {
// Fail safe:
// when we've been animating previously and we're now getting an update in the
// other direction, make sure to animate it too, otherwise, the localized updating
// may make the start larger than 1.0.
shouldAnimate = true;
}
PropertyAnimator.setProperty(this, TOP_ROUNDNESS, topRoundness,
ROUNDNESS_PROPERTIES, animate);
ROUNDNESS_PROPERTIES, shouldAnimate);
return true;
}
return false;
@@ -286,9 +295,18 @@ public abstract class ExpandableOutlineView extends ExpandableView {
@Override
public boolean setBottomRoundness(float bottomRoundness, boolean animate) {
if (mBottomRoundness != bottomRoundness) {
float diff = Math.abs(bottomRoundness - mBottomRoundness);
mBottomRoundness = bottomRoundness;
boolean shouldAnimate = animate;
if (PropertyAnimator.isAnimating(this, BOTTOM_ROUNDNESS) && diff > 0.5f) {
// Fail safe:
// when we've been animating previously and we're now getting an update in the
// other direction, make sure to animate it too, otherwise, the localized updating
// may make the start larger than 1.0.
shouldAnimate = true;
}
PropertyAnimator.setProperty(this, BOTTOM_ROUNDNESS, bottomRoundness,
ROUNDNESS_PROPERTIES, animate);
ROUNDNESS_PROPERTIES, shouldAnimate);
return true;
}
return false;

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.notification.stack;
import android.graphics.Path;
import android.view.animation.PathInterpolator;
/**
* An interpolator specifically designed for the appear animation of heads up notifications.
*/
public class HeadsUpAppearInterpolator extends PathInterpolator {
private static float X1 = 250f;
private static float X2 = 200f;
private static float XTOT = (X1 + X2);;
public HeadsUpAppearInterpolator() {
super(getAppearPath());
}
private static Path getAppearPath() {
Path path = new Path();
path.moveTo(0, 0);
float y1 = 90f;
float y2 = 80f;
path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
X1 * 0.8f / XTOT, y1 / y2,
X1 / XTOT, y1 / y2);
path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
(X1 + X2 * 0.2f) / XTOT, 1.0f,
1.0f , 1.0f);
return path;
}
public static float getFractionUntilOvershoot() {
return X1 / XTOT;
}
}

View File

@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.stack;
import android.content.res.Resources;
import android.util.MathUtils;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -199,7 +198,7 @@ public class NotificationRoundnessManager {
mExpanded = expandedHeight != 0.0f;
mAppearFraction = appearFraction;
if (mTrackedHeadsUp != null) {
updateView(mTrackedHeadsUp, true);
updateView(mTrackedHeadsUp, false /* animate */);
}
}

View File

@@ -544,7 +544,11 @@ public class NotificationStackScrollLayoutController {
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
mNotificationRoundnessManager.updateView(entry.getRow(), true /* animate */);
ExpandableNotificationRow row = entry.getRow();
// update the roundedness posted, because we might be animating away the
// headsup soon, so no need to set the roundedness to 0 and then back to 1.
row.post(() -> mNotificationRoundnessManager.updateView(row,
true /* animate */));
}
@Override
@@ -553,7 +557,9 @@ public class NotificationStackScrollLayoutController {
NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
mView.setNumHeadsUp(numEntries);
mView.setTopHeadsUpEntry(topEntry);
mNotificationRoundnessManager.updateView(entry.getRow(), false /* animate */);
generateHeadsUpAnimation(entry, isHeadsUp);
ExpandableNotificationRow row = entry.getRow();
mNotificationRoundnessManager.updateView(row, true /* animate */);
}
};
@@ -1238,7 +1244,7 @@ public class NotificationStackScrollLayoutController {
return mView.getFirstChildNotGone();
}
public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
private void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
mView.generateHeadsUpAnimation(entry, isHeadsUp);
}

View File

@@ -21,7 +21,6 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.R;
@@ -50,11 +49,8 @@ public class StackStateAnimator {
public static final int ANIMATION_DURATION_SWIPE = 260;
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
= (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
* HeadsUpAppearInterpolator.getFractionUntilOvershoot());
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 400;
public static final int ANIMATION_DURATION_PULSE_APPEAR =
KeyguardSliceView.DEFAULT_ANIM_DURATION;
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
@@ -64,8 +60,6 @@ public class StackStateAnimator {
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
private static final int MAX_STAGGER_COUNT = 5;
private static final HeadsUpAppearInterpolator HEADS_UP_APPEAR_INTERPOLATOR =
new HeadsUpAppearInterpolator();
private final int mGoToFullShadeAppearingTranslation;
private final int mPulsingAppearingTranslation;
@@ -115,14 +109,6 @@ public class StackStateAnimator {
public boolean wasAdded(View view) {
return mNewAddChildren.contains(view);
}
@Override
public Interpolator getCustomInterpolator(View child, Property property) {
if (mHeadsUpAppearChildren.contains(child) && View.TRANSLATION_Y.equals(property)) {
return HEADS_UP_APPEAR_INTERPOLATOR;
}
return null;
}
};
}
@@ -422,7 +408,7 @@ public class StackStateAnimator {
if (event.headsUpFromBottom) {
mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
} else {
changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR,
true /* isHeadsUpAppear */);
}
mHeadsUpAppearChildren.add(changingView);

View File

@@ -4182,11 +4182,6 @@ public class NotificationPanelViewController extends PanelViewController {
entry.setHeadsUpIsVisible();
}
}
@Override
public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, isHeadsUp);
}
}
private class HeightListener implements QS.HeightListener {