[Live Tile] Support launching another task (other than the current running task) in Overview
- Get rid of the defer cancelation logic
- Render animation on the task view of the task being launched upon task view appeared callback
- Finish the recents animation upon the end of the recents window animation
Fixes: 164926736
Test: manual
Change-Id: Ibffb6a9c74c235efc8615a22b0306551532c7b61
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 38adf39..ae4bd96 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 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 @@
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 5e05a7d..941a10b 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_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.TaskStackChangeListener;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.function.Consumer;
/**
@@ -1370,17 +1375,64 @@
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 @@
protected void startNewTask(Consumer<Boolean> 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 51f5e5d..21e6689 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -89,24 +89,6 @@
}
/**
- * 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 cad51f4..f38c1ea 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -17,6 +17,7 @@
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.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 @@
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
/**
* Preloads the recents animation.
@@ -88,22 +92,21 @@
@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) {
@@ -138,6 +141,15 @@
}
/**
+ * The passed-in handler is used to render side task launch animation in recents in live tile
+ * mode.
+ */
+ public void setLaunchOtherTaskInLiveTileModeHandler(
+ Consumer<RemoteAnimationTargetCompat> handler) {
+ mLaunchOtherTaskHandler = handler;
+ }
+
+ /**
* Finishes the running recents animation.
*/
public void finishRunningRecentsAnimation(boolean toHome) {
@@ -146,7 +158,7 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
: mController::finishAnimationToApp);
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
+ cleanUpRecentsAnimation();
}
}
@@ -173,12 +185,7 @@
/**
* 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 @@
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 a5af181..7299c38 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -17,15 +17,20 @@
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.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 @@
* 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 @@
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 @@
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);
+ }
}