diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java index d189c504cd..bcb1f5c3b4 100644 --- a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java +++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java @@ -1,16 +1,20 @@ package com.android.launcher3; +import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.app.ActivityOptions; import android.content.Context; +import android.os.Handler; import android.view.View; import com.android.quickstep.views.IconRecentsView; +import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** @@ -28,6 +32,12 @@ public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransi return mLauncher.getStateManager().getState().overviewUi; } + @Override + RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) { + return new GoWallpaperOpenLauncherAnimationRunner(mHandler, + false /* startAtFrontOfQueue */, fromUnlock); + } + @Override protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v, RemoteAnimationTargetCompat[] targets, boolean launcherClosing) { @@ -51,4 +61,34 @@ public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransi return mLauncher.getStateManager()::reapplyState; } + + /** + * Remote animation runner for animation from app to Launcher. For Go, when going to recents, + * we need to ensure that the recents view is ready for remote animation before starting. + */ + private final class GoWallpaperOpenLauncherAnimationRunner extends + WallpaperOpenLauncherAnimationRunner { + public GoWallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue, + boolean fromUnlock) { + super(handler, startAtFrontOfQueue, fromUnlock); + } + + @Override + public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, + AnimationResult result) { + boolean isGoingToRecents = + taskIsATargetWithMode(targetCompats, mLauncher.getTaskId(), MODE_OPENING) + && (mLauncher.getStateManager().getState() == LauncherState.OVERVIEW); + if (isGoingToRecents) { + IconRecentsView recentsView = mLauncher.getOverviewPanel(); + if (!recentsView.isReadyForRemoteAnim()) { + recentsView.setOnReadyForRemoteAnimCallback(() -> + postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result)) + ); + return; + } + } + super.onCreateAnimation(targetCompats, result); + } + } } diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 7225e572ba..07faa4bbdd 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -378,6 +378,36 @@ public final class IconRecentsView extends FrameLayout implements Insettable { return view.getThumbnailView(); } + /** + * Whether this view has processed all data changes and is ready to animate from the app to + * the overview. + * + * @return true if ready to animate app to overview, false otherwise + */ + public boolean isReadyForRemoteAnim() { + return !mTaskRecyclerView.hasPendingAdapterUpdates(); + } + + /** + * Set a callback for whenever this view is ready to do a remote animation from the app to + * overview. See {@link #isReadyForRemoteAnim()}. + * + * @param callback callback to run when view is ready to animate + */ + public void setOnReadyForRemoteAnimCallback(onReadyForRemoteAnimCallback callback) { + mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (isReadyForRemoteAnim()) { + callback.onReadyForRemoteAnim(); + mTaskRecyclerView.getViewTreeObserver(). + removeOnGlobalLayoutListener(this); + } + } + }); + } + /** * Clear all tasks and animate out. */ @@ -557,4 +587,12 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); mTaskRecyclerView.invalidateItemDecorations(); } + + /** + * Callback for when this view is ready for a remote animation from app to overview. + */ + public interface onReadyForRemoteAnimCallback { + + void onReadyForRemoteAnim(); + } } diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index e1a115ad3d..3b75304b7c 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -132,7 +132,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private final DragLayer mDragLayer; private final AlphaProperty mDragLayerAlpha; - private final Handler mHandler; + final Handler mHandler; private final boolean mIsRtl; private final float mContentTransY; @@ -573,70 +573,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans * @return Runner that plays when user goes to Launcher * ie. pressing home, swiping up from nav bar. */ - private RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) { - return new LauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */) { - @Override - public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, - AnimationResult result) { - if (!mLauncher.hasBeenResumed()) { - // If launcher is not resumed, wait until new async-frame after resume - mLauncher.setOnResumeCallback(() -> - postAsyncCallback(mHandler, () -> - onCreateAnimation(targetCompats, result))); - return; - } - - if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) { - mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS); - mLauncher.getStateManager().moveToRestState(); - } - - AnimatorSet anim = null; - RemoteAnimationProvider provider = mRemoteAnimationProvider; - if (provider != null) { - anim = provider.createWindowAnimation(targetCompats); - } - - if (anim == null) { - anim = new AnimatorSet(); - anim.play(fromUnlock - ? getUnlockWindowAnimator(targetCompats) - : getClosingWindowAnimators(targetCompats)); - - // Normally, we run the launcher content animation when we are transitioning - // home, but if home is already visible, then we don't want to animate the - // contents of launcher unless we know that we are animating home as a result - // of the home button press with quickstep, which will result in launcher being - // started on touch down, prior to the animation home (and won't be in the - // targets list because it is already visible). In that case, we force - // invisibility on touch down, and only reset it after the animation to home - // is initialized. - if (launcherIsATargetWithMode(targetCompats, MODE_OPENING) - || mLauncher.isForceInvisible()) { - // Only register the content animation for cancellation when state changes - mLauncher.getStateManager().setCurrentAnimation(anim); - if (fromUnlock) { - Pair contentAnimator = - getLauncherContentAnimator(false /* isAppOpening */, - new float[] {mContentTransY, 0}); - contentAnimator.first.setStartDelay(0); - anim.play(contentAnimator.first); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - contentAnimator.second.run(); - } - }); - } else { - createLauncherResumeAnimation(anim); - } - } - } - - mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); - result.setAnimation(anim); - } - }; + RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) { + return new WallpaperOpenLauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */, + fromUnlock); } /** @@ -773,4 +712,79 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION) == PackageManager.PERMISSION_GRANTED; } + + /** + * Remote animation runner for animation from the app to Launcher, including recents. + */ + class WallpaperOpenLauncherAnimationRunner extends LauncherAnimationRunner { + private final boolean mFromUnlock; + + public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue, + boolean fromUnlock) { + super(handler, startAtFrontOfQueue); + mFromUnlock = fromUnlock; + } + + @Override + public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, + LauncherAnimationRunner.AnimationResult result) { + if (!mLauncher.hasBeenResumed()) { + // If launcher is not resumed, wait until new async-frame after resume + mLauncher.setOnResumeCallback(() -> + postAsyncCallback(mHandler, () -> + onCreateAnimation(targetCompats, result))); + return; + } + + if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) { + mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS); + mLauncher.getStateManager().moveToRestState(); + } + + AnimatorSet anim = null; + RemoteAnimationProvider provider = mRemoteAnimationProvider; + if (provider != null) { + anim = provider.createWindowAnimation(targetCompats); + } + + if (anim == null) { + anim = new AnimatorSet(); + anim.play(mFromUnlock + ? getUnlockWindowAnimator(targetCompats) + : getClosingWindowAnimators(targetCompats)); + + // Normally, we run the launcher content animation when we are transitioning + // home, but if home is already visible, then we don't want to animate the + // contents of launcher unless we know that we are animating home as a result + // of the home button press with quickstep, which will result in launcher being + // started on touch down, prior to the animation home (and won't be in the + // targets list because it is already visible). In that case, we force + // invisibility on touch down, and only reset it after the animation to home + // is initialized. + if (launcherIsATargetWithMode(targetCompats, MODE_OPENING) + || mLauncher.isForceInvisible()) { + // Only register the content animation for cancellation when state changes + mLauncher.getStateManager().setCurrentAnimation(anim); + if (mFromUnlock) { + Pair contentAnimator = + getLauncherContentAnimator(false /* isAppOpening */, + new float[] {mContentTransY, 0}); + contentAnimator.first.setStartDelay(0); + anim.play(contentAnimator.first); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + contentAnimator.second.run(); + } + }); + } else { + createLauncherResumeAnimation(anim); + } + } + } + + mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); + result.setAnimation(anim); + } + } }