Address LAUNCHER_APP_LAUNCH_FROM_ICON jank.

- Delay app launch animations by a frame, and skip logic to skip the first frame.
- Note the icon pressed state animation still occurs, so there is still some
  visual feedback for the user that something is happening.

Bug: 181901105
Test: ensure animation still looks smooth (using window animation scale & record in slow mo)
Change-Id: Ia904b8b96301042c900e0589f33fc625c1c1148b
Merged-In: Ia904b8b96301042c900e0589f33fc625c1c1148b
This commit is contained in:
Jon Miranda
2021-06-11 16:30:47 -07:00
committed by Jonathan Miranda
parent 2f346b8666
commit 0750f03c96
8 changed files with 59 additions and 25 deletions

View File

@@ -38,6 +38,7 @@ import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
@@ -340,12 +341,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final int rotationChange = getRotationChange(appTargets);
// Note: the targetBounds are relative to the launcher
int startDelay = getSingleFrameMs(mLauncher);
Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, nonAppTargets,
windowTargetBounds, areAllTargetsTranslucent(appTargets), rotationChange));
Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets,
nonAppTargets, windowTargetBounds, areAllTargetsTranslucent(appTargets),
rotationChange);
windowAnimator.setStartDelay(startDelay);
anim.play(windowAnimator);
if (launcherClosing) {
// Delay animation by a frame to avoid jank.
Pair<AnimatorSet, Runnable> launcherContentAnimator =
getLauncherContentAnimator(true /* isAppOpening */);
getLauncherContentAnimator(true /* isAppOpening */, startDelay);
anim.play(launcherContentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -436,8 +442,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
*
* @param isAppOpening True when this is called when an app is opening.
* False when this is called when an app is closing.
* @param startDelay Start delay duration.
*/
private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening) {
private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
int startDelay) {
AnimatorSet launcherAnimator = new AnimatorSet();
Runnable endListener;
@@ -528,6 +536,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
};
}
launcherAnimator.setStartDelay(startDelay);
return new Pair<>(launcherAnimator, endListener);
}
@@ -633,7 +643,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
? 0 : getWindowCornerRadius(mLauncher.getResources());
final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
@@ -662,7 +672,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
@Override
public void onUpdate(float percent) {
public void onUpdate(float percent, boolean initOnly) {
// Calculate the size of the scaled icon.
float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
@@ -707,6 +717,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
floatingIconBounds.right += offsetX;
floatingIconBounds.bottom += offsetY;
if (initOnly) {
floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
return;
}
ArrayList<SurfaceParams> params = new ArrayList<>();
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
@@ -779,7 +795,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
}
});
};
appAnimator.addUpdateListener(listener);
// Since we added a start delay, call update here to init the FloatingIconView properly.
listener.onUpdate(0, true /* initOnly */);
animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
return animatorSet;
@@ -869,7 +888,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
@Override
public void onUpdate(float percent) {
public void onUpdate(float percent, boolean initOnly) {
widgetBackgroundBounds.set(mDx.value - mWidth.value / 2f,
mDy.value - mHeight.value / 2f, mDx.value + mWidth.value / 2f,
mDy.value + mHeight.value / 2f);
@@ -1128,7 +1147,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
DEACCEL_1_7);
@Override
public void onUpdate(float percent) {
public void onUpdate(float percent, boolean initOnly) {
SurfaceParams[] params = new SurfaceParams[appTargets.length];
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
@@ -1278,8 +1297,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false /* isAppOpening */);
contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -1328,27 +1346,32 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
final boolean skipFirstFrame;
if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
wallpaperTargets, nonAppTargets);
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
skipFirstFrame = true;
} else if (launchingFromRecents) {
composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
addCujInstrumentation(
anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
skipFirstFrame = true;
} else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);
skipFirstFrame = false;
}
if (launcherClosing) {
anim.addListener(mForceInvisibleListener);
}
result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy);
result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
skipFirstFrame);
}
}