Loading core/java/android/window/SplashScreenView.java +7 −4 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; } Loading Loading @@ -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); } } /** Loading Loading @@ -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. Loading graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +25 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading @@ -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; Loading @@ -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()) { Loading Loading @@ -1219,6 +1230,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private void invalidateOwningView() { mDrawable.invalidateSelf(); } @Override public long getTotalDuration() { return mTotalDuration; } } /** Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -1796,6 +1814,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } mPendingAnimationActions.clear(); } @Override public long getTotalDuration() { return mTotalDuration; } } private static native long nCreateAnimatorSet(); Loading graphics/java/android/graphics/drawable/AnimationDrawable.java +19 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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); Loading libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +9 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -129,6 +132,7 @@ public class SplashscreenContentDrawer { shellSplashscreenWorkerThread.start(); mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler(); mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler); mSplashScreenExecutor = splashScreenExecutor; } /** Loading Loading @@ -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 Loading Loading @@ -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); } } Loading Loading @@ -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) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java +81 −69 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading @@ -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; Loading Loading @@ -266,99 +270,107 @@ 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 setAnimationJankMonitoring(AnimatorListenerAdapter listener) { mJankMonitoringListener = listener; public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { scheduleSelf(what, when); } @Override public boolean prepareAnimate(long duration, Runnable startListener) { public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { unscheduleSelf(what); } }; mForegroundDrawable.setCallback(callback); mSplashScreenExecutor = splashScreenExecutor; 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(); public void setAnimationJankMonitoring(AnimatorListenerAdapter listener) { mJankMonitoringListener = listener; } try { @Override public void prepareAnimate(long duration, LongConsumer startListener) { stopAnimation(); mDuration = duration; mStartListener = startListener; } private void startAnimation() { if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationStart(animation); mJankMonitoringListener.onAnimationStart(null); } try { mAnimatableIcon.start(); } catch (Exception ex) { Log.e(TAG, "Error while running the splash screen animated icon", ex); animation.cancel(); mRunning = false; if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationCancel(null); } if (mStartListener != null) { mStartListener.accept(mDuration); } @Override public void onAnimationEnd(Animator animation) { mAnimatableIcon.stop(); if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationEnd(animation); 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(); } @Override public void onAnimationCancel(Animator animation) { mAnimatableIcon.stop(); if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationCancel(animation); mRunning = true; mSplashScreenExecutor.executeDelayed(this::stopAnimation, animDuration); if (mStartListener != null) { mStartListener.accept(Math.max(animDuration, 0)); } } @Override public void onAnimationRepeat(Animator animation) { // do not repeat private void onAnimationEnd() { mAnimatableIcon.stop(); if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationEnd(null); } }); return true; 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; } Loading Loading
core/java/android/window/SplashScreenView.java +7 −4 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; } Loading Loading @@ -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); } } /** Loading Loading @@ -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. Loading
graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +25 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 { Loading @@ -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; Loading @@ -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()) { Loading Loading @@ -1219,6 +1230,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { private void invalidateOwningView() { mDrawable.invalidateSelf(); } @Override public long getTotalDuration() { return mTotalDuration; } } /** Loading Loading @@ -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; Loading @@ -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; Loading Loading @@ -1796,6 +1814,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { } mPendingAnimationActions.clear(); } @Override public long getTotalDuration() { return mTotalDuration; } } private static native long nCreateAnimatorSet(); Loading
graphics/java/android/graphics/drawable/AnimationDrawable.java +19 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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); Loading
libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +9 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -129,6 +132,7 @@ public class SplashscreenContentDrawer { shellSplashscreenWorkerThread.start(); mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler(); mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler); mSplashScreenExecutor = splashScreenExecutor; } /** Loading Loading @@ -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 Loading Loading @@ -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); } } Loading Loading @@ -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) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java +81 −69 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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. Loading @@ -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; Loading Loading @@ -266,99 +270,107 @@ 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 setAnimationJankMonitoring(AnimatorListenerAdapter listener) { mJankMonitoringListener = listener; public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { scheduleSelf(what, when); } @Override public boolean prepareAnimate(long duration, Runnable startListener) { public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { unscheduleSelf(what); } }; mForegroundDrawable.setCallback(callback); mSplashScreenExecutor = splashScreenExecutor; 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(); public void setAnimationJankMonitoring(AnimatorListenerAdapter listener) { mJankMonitoringListener = listener; } try { @Override public void prepareAnimate(long duration, LongConsumer startListener) { stopAnimation(); mDuration = duration; mStartListener = startListener; } private void startAnimation() { if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationStart(animation); mJankMonitoringListener.onAnimationStart(null); } try { mAnimatableIcon.start(); } catch (Exception ex) { Log.e(TAG, "Error while running the splash screen animated icon", ex); animation.cancel(); mRunning = false; if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationCancel(null); } if (mStartListener != null) { mStartListener.accept(mDuration); } @Override public void onAnimationEnd(Animator animation) { mAnimatableIcon.stop(); if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationEnd(animation); 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(); } @Override public void onAnimationCancel(Animator animation) { mAnimatableIcon.stop(); if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationCancel(animation); mRunning = true; mSplashScreenExecutor.executeDelayed(this::stopAnimation, animDuration); if (mStartListener != null) { mStartListener.accept(Math.max(animDuration, 0)); } } @Override public void onAnimationRepeat(Animator animation) { // do not repeat private void onAnimationEnd() { mAnimatableIcon.stop(); if (mJankMonitoringListener != null) { mJankMonitoringListener.onAnimationEnd(null); } }); return true; 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; } Loading