Cleanup quick switch logic

- Remove mFinishingRecentsAnimationTaskId and related logic;
  track mLastStartedTaskId and mLastAppearedTaskTarget instead.
- Don't finish controller if new gesture is in progress, only
  finish it when the last started task appears (and invalidate
  the handler at that point).
- Remove onTaskAppeared() from GestureState, handle it directly
  in BaseSwipeHandler.
- When the end target animation finishes, possibly change the
  end target from LAST_TASK to NEW_TASK or vice versa,
  depending on the last appeared task.

Bug: 154814771
Bug: 154727089
Bug: 155609695
Bug: 154812078
Change-Id: I38669515d064a131361359087f502783d875ddde
This commit is contained in:
Tony Wickham 2020-04-27 22:42:47 -05:00
parent d4f87007b6
commit cc9fe80323
8 changed files with 127 additions and 119 deletions

View File

@ -120,7 +120,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
protected MultiStateCallback mStateCallback;
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
protected int mLastStartedTaskId = -1;
private RecentsOrientedState mOrientedState;
@ -199,7 +199,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
mRecentsAnimationTargets));
}
protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
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.
@ -210,18 +210,17 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
if (!mCanceled) {
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
mLastStartedTaskId = taskId;
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
success -> {
resultCallback.accept(success);
if (!success) {
mActivityInterface.onLaunchTaskFailed();
nextTask.notifyTaskLaunchFailed(TAG);
} else {
mActivityInterface.onLaunchTaskSuccess();
mRecentsAnimationController.finish(true /* toRecents */, null);
}
}, MAIN_EXECUTOR.getHandler());
}
mStateCallback.setStateOnUiThread(successStateFlag);
}
mCanceled = false;
}
@ -241,6 +240,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
}
/**
* TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
* @return whether the recents animation has started and there are valid app targets.
*/
protected boolean hasTargets() {
@ -306,6 +306,30 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
}
}
@Override
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
if (mRecentsAnimationController != null) {
if (handleTaskAppeared(appearedTaskTarget)) {
mRecentsAnimationController.finish(false /* toRecents */,
null /* onFinishComplete */);
mActivityInterface.onLaunchTaskSuccess();
}
}
}
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
/**
* @return The index of the TaskView in RecentsView whose taskId matches the task that will
* resume if we finish the controller.
*/
protected int getLastAppearedTaskIndex() {
return mGestureState.getLastAppearedTaskId() != -1
? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
: mRecentsView.getRunningTaskIndex();
}
private Rect getStackBounds(DeviceProfile dp) {
if (mActivity != null) {
int loc[] = new int[2];

View File

@ -46,11 +46,11 @@ import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/**
* Handles the navigation gestures when a 3rd party launcher is the default home activity.
@ -320,15 +320,6 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
}
if (mRecentsView != null) {
if (mFinishingRecentsAnimationForNewTaskId != -1) {
TaskView newRunningTaskView = mRecentsView.getTaskView(
mFinishingRecentsAnimationForNewTaskId);
int newRunningTaskId = newRunningTaskView != null
? newRunningTaskView.getTask().key.id
: -1;
mRecentsView.setCurrentTask(newRunningTaskId);
mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
}
mRecentsView.setOnScrollChangeListener(null);
}
} else {
@ -401,7 +392,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
break;
}
case NEW_TASK: {
startNewTask(STATE_HANDLER_INVALIDATED, b -> {});
startNewTask(success -> { });
break;
}
}
@ -416,7 +407,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
if (mRecentsView == null || !hasTargets()) {
mGestureState.setEndTarget(LAST_TASK);
} else {
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
final int runningTaskIndex = getLastAppearedTaskIndex();
final int taskToLaunch = mRecentsView.getNextPage();
mGestureState.setEndTarget(
(runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
@ -494,6 +485,11 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
super.onRecentsAnimationCanceled(thumbnailData);
}
@Override
protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
return true;
}
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.

View File

@ -32,7 +32,6 @@ import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.GestureState.STATE_TASK_APPEARED_DURING_SWITCH;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
@ -264,8 +263,6 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
| STATE_RECENTS_SCROLLING_FINISHED,
this::onSettledOnEndTarget);
mGestureState.runOnceAtState(STATE_TASK_APPEARED_DURING_SWITCH, this::onTaskAppeared);
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
this::invalidateHandlerWithLauncher);
@ -771,20 +768,16 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
}
private void onTaskAppeared() {
RemoteAnimationTargetCompat app = mGestureState.getAnimationTarget();
if (mRecentsAnimationController != null && app != null) {
// TODO(b/152480470): Update Task target animation after onTaskAppeared holistically.
/* android.util.Log.d("LauncherSwipeHandler", "onTaskAppeared");
final boolean result = mRecentsAnimationController.removeTaskTarget(app);
mGestureState.setAnimationTarget(null);
android.util.Log.d("LauncherSwipeHandler", "removeTask, result=" + result); */
mRecentsAnimationController.finish(false /* toRecents */,
null /* onFinishComplete */);
@Override
protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
if (appearedTaskTarget.taskId == mLastStartedTaskId) {
reset();
return true;
}
return false;
}
private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
@ -1031,12 +1024,22 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
// skip doing any future work here for the current gesture.
return;
}
if (target == NEW_TASK && mRecentsView != null
&& mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
// We are about to launch the current running task, so use LAST_TASK state
// instead of NEW_TASK. This could happen, for example, if our scroll is
// aborted after we determined the target to be NEW_TASK.
mGestureState.setEndTarget(LAST_TASK);
if (mRecentsView != null) {
int taskToLaunch = mRecentsView.getNextPage();
int runningTask = getLastAppearedTaskIndex();
if (target == NEW_TASK && taskToLaunch == runningTask) {
// We are about to launch the current running task, so use LAST_TASK
// state instead of NEW_TASK. This could happen, for example, if our
// scroll is aborted after we determined the target to be NEW_TASK.
mGestureState.setEndTarget(LAST_TASK);
} else if (target == LAST_TASK && taskToLaunch != runningTask) {
// We are about to re-launch the previously running task, but we can't
// just finish the controller like we normally would because that would
// instead resume the last task that appeared. As a workaround, launch
// the task as if it were a new task.
// TODO: is this expected?
mGestureState.setEndTarget(NEW_TASK);
}
}
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
@ -1160,8 +1163,9 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
@UiThread
private void startNewTaskInternal() {
startNewTask(STATE_HANDLER_INVALIDATED, success -> {
startNewTask(success -> {
if (!success) {
reset();
// We couldn't launch the task, so take user to overview so they can
// decide what to do instead of staying in this broken state.
endLauncherTransitionController();
@ -1186,19 +1190,6 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
.getAnimationPlayer().isStarted()) {
mLauncherTransitionController.getAnimationPlayer().cancel();
}
if (mFinishingRecentsAnimationForNewTaskId != -1) {
// If we are canceling mid-starting a new task, switch to the screenshot since the
// recents animation has finished
switchToScreenshot();
TaskView newRunningTaskView = mRecentsView.getTaskView(
mFinishingRecentsAnimationForNewTaskId);
int newRunningTaskId = newRunningTaskView != null
? newRunningTaskView.getTask().key.id
: -1;
mRecentsView.setCurrentTask(newRunningTaskId);
mGestureState.setFinishingRecentsAnimationTaskId(newRunningTaskId);
}
}
private void invalidateHandler() {

View File

@ -30,8 +30,6 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYS
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.Service;
@ -518,8 +516,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
private GestureState createGestureState() {
GestureState gestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
gestureState.updateRunningTask(mGestureState.getRunningTask());
} else {
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
}
return gestureState;
}
@ -637,14 +639,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
runningComponent != null && runningComponent.equals(homeComponent);
}
if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
// If the finish animation was interrupted, then continue using the other activity input
// consumer but with the next task as the running task
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.id = previousGestureState.getFinishingRecentsAnimationTaskId();
gestureState.updateRunningTask(info);
return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
} else if (gestureState.getRunningTask() == null) {
if (gestureState.getRunningTask() == null) {
return mResetGestureInputConsumer;
} else if (previousGestureState.isRunningAnimationToLauncher()
|| gestureState.getActivityInterface().isResumed()
@ -658,25 +653,22 @@ public class TouchInteractionService extends Service implements PluginListener<O
} else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
return mResetGestureInputConsumer;
} else {
return createOtherActivityInputConsumer(previousGestureState, gestureState, event);
return createOtherActivityInputConsumer(gestureState, event);
}
}
private InputConsumer createOtherActivityInputConsumer(GestureState previousGestureState,
GestureState gestureState, MotionEvent event) {
private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
MotionEvent event) {
final boolean shouldDefer;
final BaseSwipeUpHandler.Factory factory;
if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
factory = mFallbackSwipeHandlerFactory;
} else {
shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
event);
factory = mLauncherSwipeHandlerFactory;
}
final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
|| gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, shouldDefer, this::onConsumerInactive,

View File

@ -965,7 +965,15 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
public int getRunningTaskIndex() {
TaskView tv = getRunningTaskView();
return getTaskIndexForId(mRunningTaskId);
}
/**
* Get the index of the task view whose id matches {@param taskId}.
* @return -1 if there is no task view for the task id, else the index of the task view.
*/
public int getTaskIndexForId(int taskId) {
TaskView tv = getTaskView(taskId);
return tv == null ? -1 : indexOfChild(tv);
}

View File

@ -15,6 +15,7 @@
*/
package com.android.quickstep;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import android.app.ActivityManager;
@ -110,10 +111,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
public static final int STATE_RECENTS_SCROLLING_FINISHED =
getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
// Called when the new task appeared from quick switching.
public static final int STATE_TASK_APPEARED_DURING_SWITCH =
getFlagForIndex("STATE_TASK_APPEARED_DURING_SWITCH");
// Needed to interact with the current activity
private final Intent mHomeIntent;
private final Intent mOverviewIntent;
@ -123,9 +120,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
private ActivityManager.RunningTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
private RemoteAnimationTargetCompat mAnimationTarget;
// TODO: This can be removed once we stop finishing the animation when starting a new task
private int mFinishingRecentsAnimationTaskId = -1;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
mHomeIntent = componentObserver.getHomeIntent();
@ -143,7 +138,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mGestureId = other.mGestureId;
mRunningTask = other.mRunningTask;
mEndTarget = other.mEndTarget;
mFinishingRecentsAnimationTaskId = other.mFinishingRecentsAnimationTaskId;
mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
}
public GestureState() {
@ -225,6 +220,20 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mRunningTask = runningTask;
}
/**
* Updates the last task that appeared during this gesture.
*/
public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
}
/**
* @return The id of the task that appeared during this gesture.
*/
public int getLastAppearedTaskId() {
return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
}
/**
* @return the end target for this gesture (if known).
*/
@ -232,14 +241,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
return mEndTarget;
}
public void setAnimationTarget(RemoteAnimationTargetCompat target) {
mAnimationTarget = target;
}
public RemoteAnimationTargetCompat getAnimationTarget() {
return mAnimationTarget;
}
/**
* Sets the end target of this gesture and immediately notifies the state changes.
*/
@ -259,30 +260,9 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
}
}
/**
* @return the id for the task that was about to be launched following the finish of the recents
* animation. Only defined between when the finish-recents call was made and the launch
* activity call is made.
*/
public int getFinishingRecentsAnimationTaskId() {
return mFinishingRecentsAnimationTaskId;
}
/**
* Sets the id for the task will be launched after the recents animation is finished. Once the
* animation has finished then the id will be reset to -1.
*/
public void setFinishingRecentsAnimationTaskId(int taskId) {
mFinishingRecentsAnimationTaskId = taskId;
mStateCallback.runOnceAtState(STATE_RECENTS_ANIMATION_FINISHED, () -> {
mFinishingRecentsAnimationTaskId = -1;
});
}
/**
* @return whether the current gesture is still running a recents animation to a state in the
* Launcher or Recents activity.
* Updates the running task for the gesture to be the given {@param runningTask}.
*/
public boolean isRunningAnimationToLauncher() {
return isRecentsAnimationRunning() && mEndTarget != null && mEndTarget.isLauncher;
@ -314,18 +294,12 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
}
@Override
public void onTaskAppeared(RemoteAnimationTargetCompat app) {
mAnimationTarget = app;
mStateCallback.setState(STATE_TASK_APPEARED_DURING_SWITCH);
}
public void dump(PrintWriter pw) {
pw.println("GestureState:");
pw.println(" gestureID=" + mGestureId);
pw.println(" runningTask=" + mRunningTask);
pw.println(" endTarget=" + mEndTarget);
pw.println(" finishingRecentsAnimationTaskId=" + mFinishingRecentsAnimationTaskId);
pw.println(" lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
}
}

View File

@ -159,6 +159,6 @@ public class RecentsAnimationCallbacks implements
/**
* Callback made when a task started from the recents is ready for an app transition.
*/
default void onTaskAppeared(RemoteAnimationTargetCompat app) {}
default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {}
}
}

View File

@ -18,6 +18,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.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
import android.content.Intent;
import android.util.Log;
@ -28,6 +29,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
@ -36,6 +38,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
private RecentsAnimationTargets mTargets;
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
/**
* Preloads the recents animation.
@ -79,6 +82,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
}
mController = controller;
mTargets = targets;
mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId());
mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
}
@Override
@ -96,6 +101,20 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
cleanUpRecentsAnimation(null /* canceledThumbnail */);
}
@Override
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
if (mController != null) {
if (mLastAppearedTaskTarget == null
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
if (mLastAppearedTaskTarget != null) {
mController.removeTaskTarget(mLastAppearedTaskTarget);
}
mLastAppearedTaskTarget = appearedTaskTarget;
mLastGestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
}
}
}
});
mCallbacks.addListener(gestureState);
mCallbacks.addListener(listener);
@ -112,6 +131,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mCallbacks.removeListener(mLastGestureState);
mLastGestureState = gestureState;
mCallbacks.addListener(gestureState);
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED
| STATE_RECENTS_ANIMATION_STARTED);
gestureState.updateLastAppearedTaskTarget(mLastAppearedTaskTarget);
return mCallbacks;
}
@ -171,6 +193,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mCallbacks = null;
mTargets = null;
mLastGestureState = null;
mLastAppearedTaskTarget = null;
}
public void dump() {