Make workspace and hotseat scale animations interruptible.

This affects app launches and returns to home. When launching
immediately after returning to home (before the return animation is
complete), the two animations could overlap, causing the two property
setters to conflict.

Bug: 367591368
Flag: com.android.launcher3.coordinate_workspace_scale
Test: tested with flag off and on, see before and after in the bug
Change-Id: Ie1483fa0bf589caee315ae298e9e68ca361709d8
This commit is contained in:
Luca Zuccarini
2024-09-18 11:08:56 +00:00
parent e3dc1c5185
commit 2831d1eaf7
3 changed files with 85 additions and 15 deletions
@@ -116,6 +116,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.app.animation.Animations;
import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
@@ -574,23 +575,45 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
} else {
List<View> viewsToAnimate = new ArrayList<>();
Workspace<?> workspace = mLauncher.getWorkspace();
workspace.forEachVisiblePage(
view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
if (Flags.coordinateWorkspaceScale()) {
viewsToAnimate.add(workspace);
} else {
workspace.forEachVisiblePage(
view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
}
Hotseat hotseat = mLauncher.getHotseat();
// Do not scale hotseat as a whole when taskbar is present, and scale QSB only if it's
// not inline.
if (mDeviceProfile.isTaskbarPresent) {
if (!mDeviceProfile.isQsbInline) {
viewsToAnimate.add(mLauncher.getHotseat().getQsb());
viewsToAnimate.add(hotseat.getQsb());
}
} else {
viewsToAnimate.add(mLauncher.getHotseat());
viewsToAnimate.add(hotseat);
}
viewsToAnimate.forEach(view -> {
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scales)
float[] scale = scales;
if (Flags.coordinateWorkspaceScale()) {
// Start the animation from the current value, instead of assuming the views are
// in their resting state, so interrupted animations merge seamlessly.
// TODO(b/367591368): ideally these animations would be refactored to be
// controlled centrally so each instances doesn't need to care about this
// coordination.
scale = new float[]{view.getScaleX(), scales[1]};
// Cancel any ongoing animations. This is necessary to avoid a conflict between
// e.g. the unfinished animation triggered when closing an app back to Home and
// this animation caused by a launch.
Animations.Companion.cancelOngoingAnimation(view);
// Make sure to cache the current animation, so it can be properly interrupted.
Animations.Companion.setOngoingAnimation(view, launcherAnimator);
}
ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scale)
.setDuration(CONTENT_SCALE_DURATION);
scaleAnim.setInterpolator(DECELERATE_1_5);
launcherAnimator.play(scaleAnim);
@@ -600,6 +623,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
viewsToAnimate.forEach(view -> {
SCALE_PROPERTY.set(view, 1f);
view.setLayerType(View.LAYER_TYPE_NONE, null);
if (Flags.coordinateWorkspaceScale()) {
// Reset the cached animation.
Animations.Companion.setOngoingAnimation(view, null /* animation */);
}
});
mLauncher.resumeExpensiveViewUpdates();
};