diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 38adf39444..ae4bd96997 100644 --- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,14 +16,10 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; @@ -32,11 +28,8 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.anim.PendingAnimation; +import com.android.quickstep.TaskViewUtils; import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** @@ -53,60 +46,16 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti protected boolean isLaunchingFromRecents(@NonNull View v, @Nullable RemoteAnimationTargetCompat[] targets) { return mLauncher.getStateManager().getState().overviewUi - && findTaskViewToLaunch(mLauncher, v, targets) != null; + && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null; } @Override protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) { - RecentsView recentsView = mLauncher.getOverviewPanel(); - boolean skipLauncherChanges = !launcherClosing; - - TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets); - PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); - createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, - mLauncher.getDepthController(), pa); - anim.play(pa.buildAnim()); - - Animator childStateAnimation = null; - // Found a visible recents task that matches the opening app, lets launch the app from there - Animator launcherAnim; - final AnimatorListenerAdapter windowAnimEndListener; - if (launcherClosing) { - launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); - launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); - launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); - - // Make sure recents gets fixed up by resetting task alphas and scales, etc. - windowAnimEndListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mLauncher.getStateManager().moveToRestState(); - mLauncher.getStateManager().reapplyState(); - } - }; - } else { - AnimatorPlaybackController controller = - mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, - RECENTS_LAUNCH_DURATION); - controller.dispatchOnStart(); - childStateAnimation = controller.getTarget(); - launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION); - windowAnimEndListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mLauncher.getStateManager().goToState(NORMAL, false); - } - }; - } - anim.play(launcherAnim); - - // Set the current animation first, before adding windowAnimEndListener. Setting current - // animation adds some listeners which need to be called before windowAnimEndListener - // (the ordering of listeners matter in this case). - mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation); - anim.addListener(windowAnimEndListener); + TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets, + launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(), + mLauncher.getDepthController()); } @Override diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5e05a7dbe1..941a10b6de 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -41,10 +41,14 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; +import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager; @@ -104,6 +108,7 @@ import com.android.systemui.shared.system.TaskInfoCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.function.Consumer; /** @@ -1370,17 +1375,64 @@ public abstract class AbsSwipeUpHandler, Q extends private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { endLauncherTransitionController(); mActivityInterface.onSwipeUpToRecentsComplete(); - if (mRecentsAnimationController != null) { - mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */, - true /* screenshot */); - } mRecentsView.onSwipeUpAnimationSuccess(); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler( + this::launchOtherTaskInLiveTileMode); + } SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView()); reset(); } + private void launchOtherTaskInLiveTileMode(RemoteAnimationTargetCompat appearedTaskTarget) { + TaskView taskView = mRecentsView.getTaskView(appearedTaskTarget.taskId); + if (taskView == null) { + return; + } + + RemoteAnimationTargetCompat[] apps = Arrays.copyOf( + mRecentsAnimationTargets.apps, + mRecentsAnimationTargets.apps.length + 1); + apps[apps.length - 1] = appearedTaskTarget; + boolean launcherClosing = + taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING); + + AnimatorSet anim = new AnimatorSet(); + TaskViewUtils.composeRecentsLaunchAnimator( + anim, taskView, apps, + mRecentsAnimationTargets.wallpapers, launcherClosing, + mActivity.getStateManager(), mRecentsView, + mActivityInterface.getDepthController()); + anim.addListener(new AnimatorListenerAdapter(){ + + @Override + public void onAnimationEnd(Animator animator) { + cleanUp(false); + } + + @Override + public void onAnimationCancel(Animator animator) { + cleanUp(true); + } + + private void cleanUp(boolean canceled) { + if (mRecentsAnimationController != null) { + mRecentsAnimationController.finish(false /* toRecents */, + null /* onFinishComplete */); + if (canceled) { + mRecentsAnimationController = null; + } else { + mActivityInterface.onLaunchTaskSuccess(); + } + ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false); + } + } + }); + anim.start(); + } + private void addLiveTileOverlay() { if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) { mRecentsView.setLiveTileOverlayAttached(true); @@ -1438,39 +1490,33 @@ public abstract class AbsSwipeUpHandler, Q extends protected void startNewTask(Consumer resultCallback) { // Launch the task user scrolled to (mRecentsView.getNextPage()). - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - // We finish recents animation inside launchTask() when live tile is enabled. - mRecentsView.getNextPageTaskView().launchTask(false /* animate */, - true /* freezeTaskList */); - } else { - if (!mCanceled) { - TaskView nextTask = mRecentsView.getNextPageTaskView(); - if (nextTask != null) { - int taskId = nextTask.getTask().key.id; - mGestureState.updateLastStartedTaskId(taskId); - boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() - .contains(taskId); - nextTask.launchTask(false /* animate */, true /* freezeTaskList */, - success -> { - resultCallback.accept(success); - if (success) { - if (hasTaskPreviouslyAppeared) { - onRestartPreviouslyAppearedTask(); - } - } else { - mActivityInterface.onLaunchTaskFailed(); - nextTask.notifyTaskLaunchFailed(TAG); - mRecentsAnimationController.finish(true /* toRecents */, null); + if (!mCanceled) { + TaskView nextTask = mRecentsView.getNextPageTaskView(); + if (nextTask != null) { + int taskId = nextTask.getTask().key.id; + mGestureState.updateLastStartedTaskId(taskId); + boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() + .contains(taskId); + nextTask.launchTask(false /* animate */, true /* freezeTaskList */, + success -> { + resultCallback.accept(success); + if (success) { + if (hasTaskPreviouslyAppeared) { + onRestartPreviouslyAppearedTask(); } - }, MAIN_EXECUTOR.getHandler()); - } else { - mActivityInterface.onLaunchTaskFailed(); - Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show(); - mRecentsAnimationController.finish(true /* toRecents */, null); - } + } else { + mActivityInterface.onLaunchTaskFailed(); + nextTask.notifyTaskLaunchFailed(TAG); + mRecentsAnimationController.finish(true /* toRecents */, null); + } + }, MAIN_EXECUTOR.getHandler()); + } else { + mActivityInterface.onLaunchTaskFailed(); + Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show(); + mRecentsAnimationController.finish(true /* toRecents */, null); } - mCanceled = false; } + mCanceled = false; } /** diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 51f5e5d785..21e6689072 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -88,24 +88,6 @@ public class RecentsAnimationController { } } - /** - * Notifies the controller that we want to defer cancel until the next app transition starts. - * If {@param screenshot} is set, then we will receive a screenshot on the next - * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call - * {@link #cleanupScreenshot()} when that screenshot is no longer used. - */ - public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { - mController.setDeferCancelUntilNextTransition(defer, screenshot); - } - - /** - * Cleans up the screenshot previously returned from - * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}. - */ - public void cleanupScreenshot() { - UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot()); - } - /** * Remove task remote animation target from * {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}. diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index cad51f4cc6..f38c1ea886 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED; @@ -31,6 +32,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import java.util.function.Consumer; + public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { private RecentsAnimationController mController; @@ -39,6 +42,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn // Temporary until we can hook into gesture state events private GestureState mLastGestureState; private RemoteAnimationTargetCompat mLastAppearedTaskTarget; + private Consumer mLaunchOtherTaskHandler; /** * Preloads the recents animation. @@ -88,22 +92,21 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @Override public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { - if (thumbnailData != null) { - // If a screenshot is provided, switch to the screenshot before cleaning up - activityInterface.switchRunningTaskViewToScreenshot(thumbnailData, - () -> cleanUpRecentsAnimation(thumbnailData)); - } else { - cleanUpRecentsAnimation(null /* canceledThumbnail */); - } + cleanUpRecentsAnimation(); } @Override public void onRecentsAnimationFinished(RecentsAnimationController controller) { - cleanUpRecentsAnimation(null /* canceledThumbnail */); + cleanUpRecentsAnimation(); } @Override public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) { + if (mLaunchOtherTaskHandler != null + && mLastGestureState.getEndTarget() == RECENTS) { + mLaunchOtherTaskHandler.accept(appearedTaskTarget); + return; + } if (mController != null) { if (mLastAppearedTaskTarget == null || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) { @@ -137,6 +140,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn return mCallbacks; } + /** + * The passed-in handler is used to render side task launch animation in recents in live tile + * mode. + */ + public void setLaunchOtherTaskInLiveTileModeHandler( + Consumer handler) { + mLaunchOtherTaskHandler = handler; + } + /** * Finishes the running recents animation. */ @@ -146,7 +158,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome ? mController::finishAnimationToHome : mController::finishAnimationToApp); - cleanUpRecentsAnimation(null /* canceledThumbnail */); + cleanUpRecentsAnimation(); } } @@ -173,12 +185,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn /** * Cleans up the recents animation entirely. */ - private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) { - // Clean up the screenshot if necessary - if (mController != null && canceledThumbnail != null) { - mController.cleanupScreenshot(); - } - + private void cleanUpRecentsAnimation() { // Release all the target leashes if (mTargets != null) { mTargets.release(); @@ -194,6 +201,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn mTargets = null; mLastGestureState = null; mLastAppearedTaskTarget = null; + mLaunchOtherTaskHandler = null; } public void dump() { diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index a5af1814d9..7299c3899e 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -17,15 +17,20 @@ package com.android.quickstep; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.statehandlers.DepthController.DEPTH; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; @@ -35,12 +40,16 @@ import android.graphics.RectF; import android.os.Build; import android.view.View; +import androidx.annotation.NonNull; + import com.android.launcher3.BaseActivity; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statehandlers.DepthController; +import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.util.DisplayController; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; @@ -67,8 +76,7 @@ public final class TaskViewUtils { * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView. */ public static TaskView findTaskViewToLaunch( - BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) { - RecentsView recentsView = activity.getOverviewPanel(); + RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) { if (v instanceof TaskView) { TaskView taskView = (TaskView) v; return recentsView.isTaskViewVisible(taskView) ? taskView : null; @@ -130,7 +138,8 @@ public final class TaskViewUtils { SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); final RemoteAnimationTargets targets = - new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); + new RemoteAnimationTargets(appTargets, wallpaperTargets, + ENABLE_QUICKSTEP_LIVE_TILE.get() ? MODE_CLOSING : MODE_OPENING); targets.addReleaseCheck(applier); TransformParams params = new TransformParams() @@ -235,4 +244,57 @@ public final class TaskViewUtils { TOUCH_RESPONSE_INTERPOLATOR); } } + + public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, + @NonNull RemoteAnimationTargetCompat[] appTargets, + @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing, + @NonNull StateManager stateManager, @NonNull RecentsView recentsView, + @NonNull DepthController depthController) { + boolean skipLauncherChanges = !launcherClosing; + + TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets); + PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); + createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, + depthController, pa); + anim.play(pa.buildAnim()); + + Animator childStateAnimation = null; + // Found a visible recents task that matches the opening app, lets launch the app from there + Animator launcherAnim; + final AnimatorListenerAdapter windowAnimEndListener; + if (launcherClosing) { + launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); + launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); + launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); + + // Make sure recents gets fixed up by resetting task alphas and scales, etc. + windowAnimEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + stateManager.moveToRestState(); + stateManager.reapplyState(); + } + }; + } else { + AnimatorPlaybackController controller = + stateManager.createAnimationToNewWorkspace(NORMAL, + RECENTS_LAUNCH_DURATION); + controller.dispatchOnStart(); + childStateAnimation = controller.getTarget(); + launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION); + windowAnimEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + stateManager.goToState(NORMAL, false); + } + }; + } + anim.play(launcherAnim); + + // Set the current animation first, before adding windowAnimEndListener. Setting current + // animation adds some listeners which need to be called before windowAnimEndListener + // (the ordering of listeners matter in this case). + stateManager.setCurrentAnimation(anim, childStateAnimation); + anim.addListener(windowAnimEndListener); + } }