Merge "Avoid cutting off AVD on splash screen."

This commit is contained in:
Wei Sheng Shih
2022-02-14 12:01:21 +00:00
committed by Android (Google) Code Review
3 changed files with 129 additions and 5 deletions

View File

@@ -22,6 +22,9 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLAS
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.graphics.drawable.LayerDrawable;
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 @@ public class SplashscreenContentDrawer {
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 @@ public class SplashscreenContentDrawer {
* 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();
}
}
}

View File

@@ -41,6 +41,7 @@ import android.hardware.display.DisplayManager;
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;
@@ -120,6 +121,25 @@ public class StartingSurfaceDrawer {
private StartingSurface.SysuiProxy mSysuiProxy;
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.
*/
@@ -593,7 +613,8 @@ public class StartingSurfaceDrawer {
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 @@ public class StartingSurfaceDrawer {
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 @@ public class StartingSurfaceDrawer {
mBGColor = mTaskSnapshotWindow.getBackgroundColor();
}
mSuggestType = suggestType;
mCreateTime = SystemClock.uptimeMillis();
}
private void setSplashScreenView(SplashScreenView splashScreenView) {

View File

@@ -24,6 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
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 @@ public class StartingSurfaceDrawerTests {
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();