Merge "Avoid cutting off AVD on splash screen."
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index e255e44..025bcad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -22,6 +22,9 @@
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION;
+
import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -46,6 +49,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -322,6 +326,33 @@
private int mAnimationDuration = 0;
}
+ /**
+ * Get an optimal animation duration to keep the splash screen from showing.
+ *
+ * @param animationDuration The animation duration defined from app.
+ * @param appReadyDuration The real duration from the starting the app to the first app window
+ * drawn.
+ */
+ @VisibleForTesting
+ static long getShowingDuration(long animationDuration, long appReadyDuration) {
+ if (animationDuration <= appReadyDuration) {
+ // app window ready took longer time than animation, it can be removed ASAP.
+ return appReadyDuration;
+ }
+ if (appReadyDuration < MAX_ANIMATION_DURATION) {
+ if (animationDuration > MAX_ANIMATION_DURATION
+ || appReadyDuration < MINIMAL_ANIMATION_DURATION) {
+ // animation is too long or too short, cut off with minimal duration
+ return MINIMAL_ANIMATION_DURATION;
+ }
+ // animation is longer than dOpt but shorter than max, allow it to play till finish
+ return MAX_ANIMATION_DURATION;
+ }
+ // the shortest duration is longer than dMax, cut off no matter how long the animation
+ // will be.
+ return appReadyDuration;
+ }
+
private class StartingWindowViewBuilder {
private final Context mContext;
private final ActivityInfo mActivityInfo;
@@ -977,9 +1008,27 @@
* Create and play the default exit animation for splash screen view.
*/
void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
- Rect frame, Runnable finishCallback) {
- final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(mContext, view,
- leash, frame, mMainWindowShiftLength, mTransactionPool, finishCallback);
- animation.startAnimations();
+ Rect frame, Runnable finishCallback, long createTime) {
+ final Runnable playAnimation = () -> {
+ final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(mContext,
+ view, leash, frame, mMainWindowShiftLength, mTransactionPool, finishCallback);
+ animation.startAnimations();
+ };
+ if (view.getIconView() == null) {
+ playAnimation.run();
+ return;
+ }
+ final long appReadyDuration = SystemClock.uptimeMillis() - createTime;
+ final long animDuration = view.getIconAnimationDuration() != null
+ ? view.getIconAnimationDuration().toMillis() : 0;
+ final long minimumShowingDuration = getShowingDuration(animDuration, appReadyDuration);
+ final long delayed = minimumShowingDuration - appReadyDuration;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "applyExitAnimation delayed: %s", delayed);
+ if (delayed > 0) {
+ view.postDelayed(playAnimation, delayed);
+ } else {
+ playAnimation.run();
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 9a966b8..3442699 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -41,6 +41,7 @@
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
@@ -121,6 +122,25 @@
private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
/**
+ * The minimum duration during which the splash screen is shown when the splash screen icon is
+ * animated.
+ */
+ static final long MINIMAL_ANIMATION_DURATION = 400L;
+
+ /**
+ * Allow the icon style splash screen to be displayed for longer to give time for the animation
+ * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly
+ * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration.
+ */
+ static final long TIME_WINDOW_DURATION = 100L;
+
+ /**
+ * The maximum duration during which the splash screen will be shown if the application is ready
+ * to show before the icon animation finishes.
+ */
+ static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION;
+
+ /**
* @param splashScreenExecutor The thread used to control add and remove starting window.
*/
public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
@@ -593,7 +613,8 @@
if (removalInfo.playRevealAnimation) {
mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
removalInfo.windowAnimationLeash, removalInfo.mainFrame,
- () -> removeWindowInner(record.mDecorView, true));
+ () -> removeWindowInner(record.mDecorView, true),
+ record.mCreateTime);
} else {
// the SplashScreenView has been copied to client, hide the view to skip
// default exit animation
@@ -641,6 +662,7 @@
private boolean mSetSplashScreen;
private @StartingWindowType int mSuggestType;
private int mBGColor;
+ private final long mCreateTime;
StartingWindowRecord(IBinder appToken, View decorView,
TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) {
@@ -651,6 +673,7 @@
mBGColor = mTaskSnapshotWindow.getBackgroundColor();
}
mSuggestType = suggestType;
+ mCreateTime = SystemClock.uptimeMillis();
}
private void setSplashScreenView(SplashScreenView splashScreenView) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index d92b12e..630d0d2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -24,6 +24,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION;
+import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@@ -297,6 +299,56 @@
assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0);
}
+ @Test
+ public void testMinimumAnimationDuration() {
+ final long maxDuration = MAX_ANIMATION_DURATION;
+ final long minDuration = MINIMAL_ANIMATION_DURATION;
+
+ final long shortDuration = minDuration - 1;
+ final long medianShortDuration = minDuration + 1;
+ final long medianLongDuration = maxDuration - 1;
+ final long longAppDuration = maxDuration + 1;
+
+ // static icon
+ assertEquals(shortDuration, SplashscreenContentDrawer.getShowingDuration(
+ 0, shortDuration));
+ // median launch + static icon
+ assertEquals(medianShortDuration, SplashscreenContentDrawer.getShowingDuration(
+ 0, medianShortDuration));
+ // long launch + static icon
+ assertEquals(longAppDuration, SplashscreenContentDrawer.getShowingDuration(
+ 0, longAppDuration));
+
+ // fast launch + animatable icon
+ assertEquals(shortDuration, SplashscreenContentDrawer.getShowingDuration(
+ shortDuration, shortDuration));
+ assertEquals(minDuration, SplashscreenContentDrawer.getShowingDuration(
+ medianShortDuration, shortDuration));
+ assertEquals(minDuration, SplashscreenContentDrawer.getShowingDuration(
+ longAppDuration, shortDuration));
+
+ // median launch + animatable icon
+ assertEquals(medianShortDuration, SplashscreenContentDrawer.getShowingDuration(
+ shortDuration, medianShortDuration));
+ assertEquals(medianShortDuration, SplashscreenContentDrawer.getShowingDuration(
+ medianShortDuration, medianShortDuration));
+ assertEquals(minDuration, SplashscreenContentDrawer.getShowingDuration(
+ longAppDuration, medianShortDuration));
+ // between min < max launch + animatable icon
+ assertEquals(medianLongDuration, SplashscreenContentDrawer.getShowingDuration(
+ medianShortDuration, medianLongDuration));
+ assertEquals(maxDuration, SplashscreenContentDrawer.getShowingDuration(
+ medianLongDuration, medianShortDuration));
+
+ // long launch + animatable icon
+ assertEquals(longAppDuration, SplashscreenContentDrawer.getShowingDuration(
+ shortDuration, longAppDuration));
+ assertEquals(longAppDuration, SplashscreenContentDrawer.getShowingDuration(
+ medianShortDuration, longAppDuration));
+ assertEquals(longAppDuration, SplashscreenContentDrawer.getShowingDuration(
+ longAppDuration, longAppDuration));
+ }
+
private StartingWindowInfo createWindowInfo(int taskId, int themeResId) {
StartingWindowInfo windowInfo = new StartingWindowInfo();
final ActivityInfo info = new ActivityInfo();