Merge "Estimate animation duration for AVD and AnimationDrawable" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
172ea326de
@@ -65,6 +65,7 @@ import com.android.internal.util.ContrastColorUtil;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
/**
|
||||
* <p>The view which allows an activity to customize its splash screen exit animation.</p>
|
||||
@@ -234,7 +235,7 @@ public final class SplashScreenView extends FrameLayout {
|
||||
/**
|
||||
* Set the animation duration if icon is animatable.
|
||||
*/
|
||||
public Builder setAnimationDurationMillis(int duration) {
|
||||
public Builder setAnimationDurationMillis(long duration) {
|
||||
mIconAnimationDuration = Duration.ofMillis(duration);
|
||||
return this;
|
||||
}
|
||||
@@ -521,8 +522,11 @@ public final class SplashScreenView extends FrameLayout {
|
||||
});
|
||||
}
|
||||
|
||||
private void animationStartCallback() {
|
||||
private void animationStartCallback(long animDuration) {
|
||||
mIconAnimationStart = Instant.now();
|
||||
if (animDuration > 0) {
|
||||
mIconAnimationDuration = Duration.ofMillis(animDuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,9 +697,8 @@ public final class SplashScreenView extends FrameLayout {
|
||||
* Prepare the animation if this drawable also be animatable.
|
||||
* @param duration The animation duration.
|
||||
* @param startListener The callback listener used to receive the start of the animation.
|
||||
* @return true if this drawable object can also be animated and it can be played now.
|
||||
*/
|
||||
boolean prepareAnimate(long duration, Runnable startListener);
|
||||
void prepareAnimate(long duration, LongConsumer startListener);
|
||||
|
||||
/**
|
||||
* Stop animation.
|
||||
|
||||
@@ -690,6 +690,14 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total duration of the animation
|
||||
* @hide
|
||||
*/
|
||||
public long getTotalDuration() {
|
||||
return mAnimatorSet.getTotalDuration();
|
||||
}
|
||||
|
||||
private static class AnimatedVectorDrawableState extends ConstantState {
|
||||
@Config int mChangingConfigurations;
|
||||
VectorDrawable mVectorDrawable;
|
||||
@@ -1074,6 +1082,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
boolean isInfinite();
|
||||
void pause();
|
||||
void resume();
|
||||
long getTotalDuration();
|
||||
}
|
||||
|
||||
private static class VectorDrawableAnimatorUI implements VectorDrawableAnimator {
|
||||
@@ -1085,6 +1094,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
// setup by init().
|
||||
private ArrayList<AnimatorListener> mListenerArray = null;
|
||||
private boolean mIsInfinite = false;
|
||||
private long mTotalDuration;
|
||||
|
||||
VectorDrawableAnimatorUI(@NonNull AnimatedVectorDrawable drawable) {
|
||||
mDrawable = drawable;
|
||||
@@ -1100,7 +1110,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
// Keep a deep copy of the set, such that set can be still be constantly representing
|
||||
// the static content from XML file.
|
||||
mSet = set.clone();
|
||||
mIsInfinite = mSet.getTotalDuration() == Animator.DURATION_INFINITE;
|
||||
mTotalDuration = mSet.getTotalDuration();
|
||||
mIsInfinite = mTotalDuration == Animator.DURATION_INFINITE;
|
||||
|
||||
// If there are listeners added before calling init(), now they should be setup.
|
||||
if (mListenerArray != null && !mListenerArray.isEmpty()) {
|
||||
@@ -1219,6 +1230,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
private void invalidateOwningView() {
|
||||
mDrawable.invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalDuration() {
|
||||
return mTotalDuration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1249,6 +1265,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
private int mLastListenerId = 0;
|
||||
private final IntArray mPendingAnimationActions = new IntArray();
|
||||
private final AnimatedVectorDrawable mDrawable;
|
||||
private long mTotalDuration;
|
||||
|
||||
VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
|
||||
mDrawable = drawable;
|
||||
@@ -1270,7 +1287,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
.getNativeTree();
|
||||
nSetVectorDrawableTarget(mSetPtr, vectorDrawableTreePtr);
|
||||
mInitialized = true;
|
||||
mIsInfinite = set.getTotalDuration() == Animator.DURATION_INFINITE;
|
||||
mTotalDuration = set.getTotalDuration();
|
||||
mIsInfinite = mTotalDuration == Animator.DURATION_INFINITE;
|
||||
|
||||
// Check reversible.
|
||||
mIsReversible = true;
|
||||
@@ -1796,6 +1814,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
}
|
||||
mPendingAnimationActions.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalDuration() {
|
||||
return mTotalDuration;
|
||||
}
|
||||
}
|
||||
|
||||
private static native long nCreateAnimatorSet();
|
||||
|
||||
@@ -424,6 +424,17 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
|
||||
System.arraycopy(mDurations, 0, newDurations, 0, oldSize);
|
||||
mDurations = newDurations;
|
||||
}
|
||||
|
||||
public long getTotalDuration() {
|
||||
if (mDurations != null) {
|
||||
int total = 0;
|
||||
for (int dur : mDurations) {
|
||||
total += dur;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -435,6 +446,14 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total duration of the animation
|
||||
* @hide
|
||||
*/
|
||||
public long getTotalDuration() {
|
||||
return mAnimationState.getTotalDuration();
|
||||
}
|
||||
|
||||
private AnimationDrawable(AnimationState state, Resources res) {
|
||||
final AnimationState as = new AnimationState(state, this, res);
|
||||
setConstantState(as);
|
||||
|
||||
@@ -69,6 +69,7 @@ import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.launcher3.icons.BaseIconFactory;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
|
||||
@@ -115,8 +116,10 @@ public class SplashscreenContentDrawer {
|
||||
private final Handler mSplashscreenWorkerHandler;
|
||||
@VisibleForTesting
|
||||
final ColorCache mColorCache;
|
||||
private final ShellExecutor mSplashScreenExecutor;
|
||||
|
||||
SplashscreenContentDrawer(Context context, IconProvider iconProvider, TransactionPool pool) {
|
||||
SplashscreenContentDrawer(Context context, IconProvider iconProvider, TransactionPool pool,
|
||||
ShellExecutor splashScreenExecutor) {
|
||||
mContext = context;
|
||||
mIconProvider = iconProvider;
|
||||
mTransactionPool = pool;
|
||||
@@ -129,6 +132,7 @@ public class SplashscreenContentDrawer {
|
||||
shellSplashscreenWorkerThread.start();
|
||||
mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler();
|
||||
mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler);
|
||||
mSplashScreenExecutor = splashScreenExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,7 +401,7 @@ public class SplashscreenContentDrawer {
|
||||
|
||||
SplashScreenView build() {
|
||||
Drawable iconDrawable;
|
||||
final int animationDuration;
|
||||
final long animationDuration;
|
||||
if (mSuggestType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
|
||||
|| mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
|
||||
// empty or legacy splash screen case
|
||||
@@ -455,8 +459,8 @@ public class SplashscreenContentDrawer {
|
||||
iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
|
||||
} else {
|
||||
mFinalIconDrawables = SplashscreenIconDrawableFactory.makeIconDrawable(
|
||||
mTmpAttrs.mIconBgColor, mThemeColor,
|
||||
iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
|
||||
mTmpAttrs.mIconBgColor, mThemeColor, iconDrawable, mDefaultIconSize,
|
||||
mFinalIconSize, mSplashscreenWorkerHandler, mSplashScreenExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +520,7 @@ public class SplashscreenContentDrawer {
|
||||
}
|
||||
|
||||
private SplashScreenView fillViewWithIcon(int iconSize, @Nullable Drawable[] iconDrawable,
|
||||
int animationDuration, Consumer<Runnable> uiThreadInitTask) {
|
||||
long animationDuration, Consumer<Runnable> uiThreadInitTask) {
|
||||
Drawable foreground = null;
|
||||
Drawable background = null;
|
||||
if (iconDrawable != null) {
|
||||
|
||||
@@ -18,9 +18,7 @@ package com.android.wm.shell.startingsurface;
|
||||
|
||||
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -36,6 +34,8 @@ import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Trace;
|
||||
@@ -44,6 +44,9 @@ import android.util.PathParser;
|
||||
import android.window.SplashScreenView;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
/**
|
||||
* Creating a lightweight Drawable object used for splash screen.
|
||||
@@ -60,14 +63,15 @@ public class SplashscreenIconDrawableFactory {
|
||||
*/
|
||||
static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
|
||||
@NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
|
||||
Handler splashscreenWorkerHandler) {
|
||||
Handler splashscreenWorkerHandler, ShellExecutor splashScreenExecutor) {
|
||||
Drawable foreground;
|
||||
Drawable background = null;
|
||||
boolean drawBackground =
|
||||
backgroundColor != Color.TRANSPARENT && backgroundColor != themeColor;
|
||||
|
||||
if (foregroundDrawable instanceof Animatable) {
|
||||
foreground = new AnimatableIconAnimateListener(foregroundDrawable);
|
||||
foreground = new AnimatableIconAnimateListener(foregroundDrawable,
|
||||
splashScreenExecutor);
|
||||
} else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
|
||||
// If the icon is Adaptive, we already use the icon background.
|
||||
drawBackground = false;
|
||||
@@ -266,14 +270,37 @@ public class SplashscreenIconDrawableFactory {
|
||||
*/
|
||||
public static class AnimatableIconAnimateListener extends AdaptiveForegroundDrawable
|
||||
implements SplashScreenView.IconAnimateListener {
|
||||
private Animatable mAnimatableIcon;
|
||||
private Animator mIconAnimator;
|
||||
private final Animatable mAnimatableIcon;
|
||||
private boolean mAnimationTriggered;
|
||||
private AnimatorListenerAdapter mJankMonitoringListener;
|
||||
private boolean mRunning;
|
||||
private long mDuration;
|
||||
private LongConsumer mStartListener;
|
||||
private final ShellExecutor mSplashScreenExecutor;
|
||||
|
||||
AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable) {
|
||||
AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable,
|
||||
ShellExecutor splashScreenExecutor) {
|
||||
super(foregroundDrawable);
|
||||
mForegroundDrawable.setCallback(mCallback);
|
||||
Callback callback = new Callback() {
|
||||
@Override
|
||||
public void invalidateDrawable(@NonNull Drawable who) {
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what,
|
||||
long when) {
|
||||
scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
|
||||
unscheduleSelf(what);
|
||||
}
|
||||
};
|
||||
mForegroundDrawable.setCallback(callback);
|
||||
mSplashScreenExecutor = splashScreenExecutor;
|
||||
mAnimatableIcon = (Animatable) mForegroundDrawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -282,83 +309,68 @@ public class SplashscreenIconDrawableFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prepareAnimate(long duration, Runnable startListener) {
|
||||
mAnimatableIcon = (Animatable) mForegroundDrawable;
|
||||
mIconAnimator = ValueAnimator.ofInt(0, 1);
|
||||
mIconAnimator.setDuration(duration);
|
||||
mIconAnimator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (startListener != null) {
|
||||
startListener.run();
|
||||
}
|
||||
try {
|
||||
if (mJankMonitoringListener != null) {
|
||||
mJankMonitoringListener.onAnimationStart(animation);
|
||||
}
|
||||
mAnimatableIcon.start();
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Error while running the splash screen animated icon", ex);
|
||||
animation.cancel();
|
||||
}
|
||||
}
|
||||
public void prepareAnimate(long duration, LongConsumer startListener) {
|
||||
stopAnimation();
|
||||
mDuration = duration;
|
||||
mStartListener = startListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mAnimatableIcon.stop();
|
||||
if (mJankMonitoringListener != null) {
|
||||
mJankMonitoringListener.onAnimationEnd(animation);
|
||||
}
|
||||
private void startAnimation() {
|
||||
if (mJankMonitoringListener != null) {
|
||||
mJankMonitoringListener.onAnimationStart(null);
|
||||
}
|
||||
try {
|
||||
mAnimatableIcon.start();
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Error while running the splash screen animated icon", ex);
|
||||
mRunning = false;
|
||||
if (mJankMonitoringListener != null) {
|
||||
mJankMonitoringListener.onAnimationCancel(null);
|
||||
}
|
||||
if (mStartListener != null) {
|
||||
mStartListener.accept(mDuration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
long animDuration = mDuration;
|
||||
if (mAnimatableIcon instanceof AnimatedVectorDrawable
|
||||
&& ((AnimatedVectorDrawable) mAnimatableIcon).getTotalDuration() > 0) {
|
||||
animDuration = ((AnimatedVectorDrawable) mAnimatableIcon).getTotalDuration();
|
||||
} else if (mAnimatableIcon instanceof AnimationDrawable
|
||||
&& ((AnimationDrawable) mAnimatableIcon).getTotalDuration() > 0) {
|
||||
animDuration = ((AnimationDrawable) mAnimatableIcon).getTotalDuration();
|
||||
}
|
||||
mRunning = true;
|
||||
mSplashScreenExecutor.executeDelayed(this::stopAnimation, animDuration);
|
||||
if (mStartListener != null) {
|
||||
mStartListener.accept(Math.max(animDuration, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mAnimatableIcon.stop();
|
||||
if (mJankMonitoringListener != null) {
|
||||
mJankMonitoringListener.onAnimationCancel(animation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
// do not repeat
|
||||
mAnimatableIcon.stop();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
private void onAnimationEnd() {
|
||||
mAnimatableIcon.stop();
|
||||
if (mJankMonitoringListener != null) {
|
||||
mJankMonitoringListener.onAnimationEnd(null);
|
||||
}
|
||||
mStartListener = null;
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAnimation() {
|
||||
if (mIconAnimator != null && mIconAnimator.isRunning()) {
|
||||
mIconAnimator.end();
|
||||
if (mRunning) {
|
||||
mSplashScreenExecutor.removeCallbacks(this::stopAnimation);
|
||||
onAnimationEnd();
|
||||
mJankMonitoringListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
private final Callback mCallback = new Callback() {
|
||||
@Override
|
||||
public void invalidateDrawable(@NonNull Drawable who) {
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
|
||||
scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
|
||||
unscheduleSelf(what);
|
||||
}
|
||||
};
|
||||
|
||||
private void ensureAnimationStarted() {
|
||||
if (mAnimationTriggered) {
|
||||
return;
|
||||
}
|
||||
if (mIconAnimator != null && !mIconAnimator.isRunning()) {
|
||||
mIconAnimator.start();
|
||||
if (!mRunning) {
|
||||
startAnimation();
|
||||
}
|
||||
mAnimationTriggered = true;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,8 @@ public class StartingSurfaceDrawer {
|
||||
mContext = context;
|
||||
mDisplayManager = mContext.getSystemService(DisplayManager.class);
|
||||
mSplashScreenExecutor = splashScreenExecutor;
|
||||
mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool);
|
||||
mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool,
|
||||
mSplashScreenExecutor);
|
||||
mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
|
||||
mWindowManagerGlobal = WindowManagerGlobal.getInstance();
|
||||
mDisplayManager.getDisplay(DEFAULT_DISPLAY);
|
||||
|
||||
Reference in New Issue
Block a user