9/ Clean up swipe shared state
- Add TaskAnimationManager which keeps track of the animation state whose
lifecycle can be longer than the gesture. Move some of the logic related
to cleaning up old animations into this class (called when the state is
shared across gestures).
- Instead of calling into the shared state directly via UIFactory, add
callback to cleanup the animation and shared state from Launcher
Bug: 141886704
Change-Id: Ib6140b37162f7460a20fa1046cfd4f4068e4a1c6
Signed-off-by: Winson Chung <winsonc@google.com>
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index f2aa842..d5ea1ec 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -88,6 +88,4 @@
public static RotationMode getRotationMode(DeviceProfile dp) {
return RotationMode.NORMAL;
}
-
- public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index ff73679..2a22e9d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
@@ -45,7 +44,6 @@
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.views.RecentsView;
@@ -188,17 +186,6 @@
return new RecentsViewStateController(launcher);
}
- /** Clears the swipe shared state for the current swipe gesture. */
- public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- launcher.<RecentsView>getOverviewPanel().switchToScreenshot(
- () -> TouchInteractionService.getSwipeSharedState().clearAllState(
- finishAnimation));
- } else {
- TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation);
- }
- }
-
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
*
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index f6b3654..c528935 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -493,22 +493,21 @@
}
@Override
- public void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
+ public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
+ Runnable onFinishRunnable) {
Launcher launcher = getCreatedActivity();
RecentsView recentsView = launcher.getOverviewPanel();
if (recentsView == null) {
- if (runnable != null) {
- runnable.run();
+ if (onFinishRunnable != null) {
+ onFinishRunnable.run();
}
return;
}
- TaskView taskView = recentsView.getRunningTaskView();
- if (taskView != null) {
- taskView.setShowScreenshot(true);
- taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
- ViewUtils.postDraw(taskView, runnable);
- } else if (runnable != null) {
- runnable.run();
- }
+ recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
+ }
+
+ @Override
+ public void setOnDeferredActivityLaunchCallback(Runnable r) {
+ getCreatedActivity().setOnDeferredActivityLaunchCallback(r);
}
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index cd8e1a4..a9cfa27 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -15,32 +15,12 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
-import android.util.Log;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
import java.io.PrintWriter;
/**
* Utility class used to store state information shared across multiple transitions.
*/
-public class SwipeSharedState implements RecentsAnimationListener {
-
- private OverviewComponentObserver mOverviewComponentObserver;
-
- private RecentsAnimationCallbacks mRecentsAnimationListener;
- private RecentsAnimationController mLastRecentsAnimationController;
- private RecentsAnimationTargets mLastAnimationTarget;
-
- private boolean mLastAnimationCancelled = false;
- private boolean mLastAnimationRunning = false;
+public class SwipeSharedState {
public boolean canGestureBeContinued;
public boolean goingToLauncher;
@@ -48,106 +28,6 @@
public int nextRunningTaskId = -1;
private int mLogId;
- public void setOverviewComponentObserver(OverviewComponentObserver observer) {
- mOverviewComponentObserver = observer;
- }
-
- @Override
- public final void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targets) {
- mLastRecentsAnimationController = controller;
- mLastAnimationTarget = targets;
-
- mLastAnimationCancelled = false;
- mLastAnimationRunning = true;
- }
-
- @Override
- public final void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- if (thumbnailData != null) {
- mOverviewComponentObserver.getActivityInterface().switchToScreenshot(thumbnailData,
- () -> {
- mLastRecentsAnimationController.cleanupScreenshot();
- clearAnimationState();
- });
- } else {
- clearAnimationState();
- }
- }
-
- @Override
- public final void onRecentsAnimationFinished(RecentsAnimationController controller) {
- if (mLastRecentsAnimationController == controller) {
- mLastAnimationRunning = false;
- }
- }
-
- private void clearAnimationTarget() {
- if (mLastAnimationTarget != null) {
- mLastAnimationTarget.release();
- mLastAnimationTarget = null;
- }
- }
-
- private void clearAnimationState() {
- clearAnimationTarget();
-
- mLastAnimationCancelled = true;
- mLastAnimationRunning = false;
- }
-
- private void clearListenerState(boolean finishAnimation) {
- if (mRecentsAnimationListener != null) {
- mRecentsAnimationListener.removeListener(this);
- mRecentsAnimationListener.notifyAnimationCanceled();
- if (mLastAnimationRunning && mLastRecentsAnimationController != null) {
- Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
- finishAnimation
- ? mLastRecentsAnimationController::finishAnimationToHome
- : mLastRecentsAnimationController::finishAnimationToApp);
- mLastRecentsAnimationController = null;
- mLastAnimationTarget = null;
- }
- }
- mRecentsAnimationListener = null;
- clearAnimationTarget();
- mLastAnimationCancelled = false;
- mLastAnimationRunning = false;
- }
-
- public RecentsAnimationCallbacks newRecentsAnimationCallbacks() {
- Preconditions.assertUIThread();
-
- if (mLastAnimationRunning) {
- String msg = "New animation started before completing old animation";
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- throw new IllegalArgumentException(msg);
- } else {
- Log.e("SwipeSharedState", msg, new Exception());
- }
- }
-
- clearListenerState(false /* finishAnimation */);
- boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false
- : mOverviewComponentObserver.getActivityInterface().shouldMinimizeSplitScreen();
- mRecentsAnimationListener = new RecentsAnimationCallbacks(shouldMinimiseSplitScreen);
- mRecentsAnimationListener.addListener(this);
- return mRecentsAnimationListener;
- }
-
- public RecentsAnimationCallbacks getActiveListener() {
- return mRecentsAnimationListener;
- }
-
- public void applyActiveRecentsAnimationState(RecentsAnimationListener listener) {
- if (mLastRecentsAnimationController != null) {
- listener.onRecentsAnimationStart(mLastRecentsAnimationController,
- mLastAnimationTarget);
- } else if (mLastAnimationCancelled) {
- listener.onRecentsAnimationCanceled(null);
- }
- }
-
/**
* Called when a recents animation has finished, but was interrupted before the next task was
* launched. The given {@param runningTaskId} should be used as the running task for the
@@ -156,11 +36,9 @@
public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
recentsAnimationFinishInterrupted = true;
nextRunningTaskId = runningTaskId;
- mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
}
- public void clearAllState(boolean finishAnimation) {
- clearListenerState(finishAnimation);
+ public void clearAllState() {
canGestureBeContinued = false;
recentsAnimationFinishInterrupted = false;
nextRunningTaskId = -1;
@@ -172,8 +50,6 @@
pw.println(prefix + "canGestureBeContinued=" + canGestureBeContinued);
pw.println(prefix + "recentsAnimationFinishInterrupted=" + recentsAnimationFinishInterrupted);
pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId);
- pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled);
- pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning);
pw.println(prefix + "logTraceId=" + mLogId);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index e244e84..cc7eb9b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -250,10 +250,7 @@
return sSwipeSharedState;
}
- private final InputConsumer mResetGestureInputConsumer =
- new ResetGestureInputConsumer(sSwipeSharedState);
-
- private final BaseSwipeUpHandler.Factory mWindowTreansformFactory =
+ private final BaseSwipeUpHandler.Factory mWindowTransformFactory =
this::createWindowTransformSwipeHandler;
private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory =
this::createFallbackNoButtonSwipeHandler;
@@ -264,10 +261,12 @@
private OverviewComponentObserver mOverviewComponentObserver;
private InputConsumerController mInputConsumer;
private RecentsAnimationDeviceState mDeviceState;
+ private TaskAnimationManager mTaskAnimationManager;
private InputConsumer mUncheckedConsumer = InputConsumer.NO_OP;
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
+ private InputConsumer mResetGestureInputConsumer;
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
@@ -338,13 +337,13 @@
@UiThread
public void onUserUnlocked() {
+ mTaskAnimationManager = new TaskAnimationManager();
mRecentsModel = RecentsModel.INSTANCE.get(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
mOverviewComponentObserver);
+ mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
-
- sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver);
mInputConsumer.registerInputConsumer();
onSystemUiFlagsChanged();
onAssistantVisibilityChanged();
@@ -356,6 +355,18 @@
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
+ private void onDeferredActivityLaunch() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
+ null, () -> {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ sSwipeSharedState.clearAllState();
+ });
+ } else {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ }
+ }
+
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (!mDeviceState.isUserUnlocked() || !mMode.hasGestures) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
@@ -509,7 +520,8 @@
RunningTaskInfo runningTaskInfo = TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(0));
if (!useSharedState) {
- sSwipeSharedState.clearAllState(false /* finishAnimation */);
+ mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+ sSwipeSharedState.clearAllState();
}
if (mDeviceState.isKeyguardShowingOccluded()) {
// This handles apps showing over the lockscreen (e.g. camera)
@@ -572,20 +584,20 @@
} else {
shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
event);
- factory = mWindowTreansformFactory;
+ factory = mWindowTransformFactory;
}
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
- return new OtherActivityInputConsumer(this, mDeviceState, gestureState, runningTaskInfo,
- shouldDefer, this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat,
- disableHorizontalSwipe, factory, mLogId);
+ return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
+ gestureState, runningTaskInfo, shouldDefer, this::onConsumerInactive,
+ sSwipeSharedState, mInputMonitorCompat, disableHorizontalSwipe, factory, mLogId);
}
private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState,
RunningTaskInfo taskInfo) {
if (mMode == Mode.NO_BUTTON && taskInfo != null) {
- return new DeviceLockedInputConsumer(this, mDeviceState, gestureState,
- sSwipeSharedState, mInputMonitorCompat, taskInfo.taskId, mLogId);
+ return new DeviceLockedInputConsumer(this, mDeviceState, mTaskAnimationManager,
+ gestureState, sSwipeSharedState, mInputMonitorCompat, taskInfo.taskId, mLogId);
} else {
return mResetGestureInputConsumer;
}
@@ -647,9 +659,8 @@
return;
}
- // Pass null animation handler to indicate this start is preload.
- startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(),
- null);
+ mTaskAnimationManager.preloadRecentsAnimation(
+ mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
}
@Override
@@ -725,9 +736,9 @@
private BaseSwipeUpHandler createWindowTransformSwipeHandler(GestureState gestureState,
RunningTaskInfo runningTask, long touchTimeMs, boolean continuingLastGesture,
boolean isLikelyToStartNewTask) {
- return new WindowTransformSwipeHandler(this, mDeviceState, gestureState, runningTask,
- touchTimeMs, mOverviewComponentObserver, continuingLastGesture, mInputConsumer,
- mRecentsModel);
+ return new WindowTransformSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ gestureState, runningTask, touchTimeMs, mOverviewComponentObserver,
+ continuingLastGesture, mInputConsumer, mRecentsModel);
}
private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(GestureState gestureState,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 35f8be7..29f431d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -192,6 +192,7 @@
private static final int LOG_NO_OP_PAGE_INDEX = -1;
private final RecentsAnimationDeviceState mDeviceState;
+ private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private GestureEndTarget mGestureEndTarget;
@@ -225,13 +226,17 @@
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
+ private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
+
public WindowTransformSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, RunningTaskInfo runningTaskInfo, long touchTimeMs,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ RunningTaskInfo runningTaskInfo, long touchTimeMs,
OverviewComponentObserver overviewComponentObserver, boolean continuingLastGesture,
InputConsumerController inputConsumer, RecentsModel recentsModel) {
super(context, gestureState, overviewComponentObserver, recentsModel, inputConsumer,
runningTaskInfo.id);
mDeviceState = deviceState;
+ mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -401,9 +406,26 @@
// that time by a previous window transition.
setupRecentsViewUi();
+ // For the duration of the gesture, in cases where an activity is launched while the
+ // activity is not yet resumed, finish the animation to ensure we get resumed
+ mGestureState.getActivityInterface().setOnDeferredActivityLaunchCallback(
+ mOnDeferredActivityLaunch);
+
notifyGestureStartedAsync();
}
+ private void onDeferredActivityLaunch() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mOverviewComponentObserver.getActivityInterface().switchRunningTaskViewToScreenshot(
+ null, () -> {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ TouchInteractionService.getSwipeSharedState().clearAllState();
+ });
+ } else {
+ mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
+ }
+ }
+
private void setupRecentsViewUi() {
if (mContinuingLastGesture) {
updateSysUiFlags(mCurrentShift.value);
@@ -1091,6 +1113,8 @@
mRecentsView.onGestureAnimationEnd();
+ // Reset the callback for deferred activity launches
+ mActivityInterface.setOnDeferredActivityLaunchCallback(null);
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
removeLiveTileOverlay();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 12b7c26..980cfad 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -48,6 +47,7 @@
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationTargets;
+import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -76,6 +76,7 @@
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
+ private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private final float mTouchSlopSquared;
private final SwipeSharedState mSwipeSharedState;
@@ -98,10 +99,12 @@
private RecentsAnimationTargets mRecentsAnimationTargets;
public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, SwipeSharedState swipeSharedState,
- InputMonitorCompat inputMonitorCompat, int runningTaskId, int logId) {
+ TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
+ int runningTaskId, int logId) {
mContext = context;
mDeviceState = deviceState;
+ mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mTouchSlopSquared = squaredTouchSlop(context);
mSwipeSharedState = swipeSharedState;
@@ -207,16 +210,14 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
- callbacks.addListener(this);
+ mInputMonitorCompat.pilferPointers();
+
Intent intent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_DEFAULT)
.setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
-
- mInputMonitorCompat.pilferPointers();
- startRecentsActivityAsync(intent, callbacks);
+ mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 02f4c40..6ba326c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -26,7 +26,6 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
-import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -58,6 +57,7 @@
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
@@ -80,7 +80,9 @@
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
private final RecentsAnimationDeviceState mDeviceState;
+ private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
+ private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final RunningTaskInfo mRunningTask;
private final SwipeSharedState mSwipeSharedState;
@@ -95,6 +97,7 @@
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
+
private VelocityTracker mVelocityTracker;
private BaseSwipeUpHandler mInteractionHandler;
@@ -126,13 +129,15 @@
private int mLogId;
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, RunningTaskInfo runningTaskInfo,
- boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
+ TaskAnimationManager taskAnimationManager, GestureState gestureState,
+ RunningTaskInfo runningTaskInfo, boolean isDeferredDownTarget,
+ Consumer<OtherActivityInputConsumer> onCompleteCallback,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
boolean disableHorizontalSwipe, Factory handlerFactory, int logId) {
super(base);
mLogId = logId;
mDeviceState = deviceState;
+ mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
mRunningTask = runningTaskInfo;
@@ -147,7 +152,7 @@
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
- boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
+ boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
mSwipeSharedState = swipeSharedState;
@@ -329,25 +334,22 @@
long touchTimeMs, boolean isLikelyToStartNewTask) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
- RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
- final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mGestureState, mRunningTask,
- touchTimeMs, listenerSet != null, isLikelyToStartNewTask);
+ mInteractionHandler = mHandlerFactory.newHandler(mGestureState, mRunningTask, touchTimeMs,
+ mTaskAnimationManager.isRecentsAnimationRunning(), isLikelyToStartNewTask);
+ mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
+ mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
+ mInteractionHandler.initWhenReady();
- mInteractionHandler = handler;
- handler.setGestureEndCallback(this::onInteractionGestureFinished);
- mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
- handler.initWhenReady();
-
- if (listenerSet != null) {
- listenerSet.addListener(handler);
- mSwipeSharedState.applyActiveRecentsAnimationState(handler);
+ if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+ mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
+ mActiveCallbacks.addListener(mInteractionHandler);
+ mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
notifyGestureStarted();
} else {
- RecentsAnimationCallbacks callbacks = mSwipeSharedState.newRecentsAnimationCallbacks();
- callbacks.addListener(handler);
- Intent intent = handler.getLaunchIntent();
+ Intent intent = mInteractionHandler.getLaunchIntent();
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId);
- startRecentsActivityAsync(intent, callbacks);
+ mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
+ mInteractionHandler);
}
}
@@ -415,9 +417,8 @@
}
private void removeListener() {
- RecentsAnimationCallbacks listenerSet = mSwipeSharedState.getActiveListener();
- if (listenerSet != null) {
- listenerSet.removeListener(mInteractionHandler);
+ if (mActiveCallbacks != null) {
+ mActiveCallbacks.removeListener(mInteractionHandler);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
index e04c0c7..b22a75b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
@@ -18,17 +18,18 @@
import android.view.MotionEvent;
import com.android.quickstep.InputConsumer;
-import com.android.quickstep.SwipeSharedState;
+import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.TouchInteractionService;
/**
* A NO_OP input consumer which also resets any pending gesture
*/
public class ResetGestureInputConsumer implements InputConsumer {
- private final SwipeSharedState mSwipeSharedState;
+ private final TaskAnimationManager mTaskAnimationManager;
- public ResetGestureInputConsumer(SwipeSharedState swipeSharedState) {
- mSwipeSharedState = swipeSharedState;
+ public ResetGestureInputConsumer(TaskAnimationManager taskAnimationManager) {
+ mTaskAnimationManager = taskAnimationManager;
}
@Override
@@ -39,8 +40,9 @@
@Override
public void onMotionEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN
- && mSwipeSharedState.getActiveListener() != null) {
- mSwipeSharedState.clearAllState(false /* finishAnimation */);
+ && mTaskAnimationManager.isRecentsAnimationRunning()) {
+ mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */);
+ TouchInteractionService.getSwipeSharedState().clearAllState();
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 0655c73..5a65c15 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -102,8 +102,9 @@
@Override
public void startHome() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */,
- () -> mActivity.getStateManager().goToState(NORMAL)));
+ switchToScreenshot(null,
+ () -> finishRecentsAnimation(true /* toRecents */,
+ () -> mActivity.getStateManager().goToState(NORMAL)));
} else {
mActivity.getStateManager().goToState(NORMAL);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 6120b9e..5d4665d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -1851,20 +1851,20 @@
return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight());
}
-
/** If it's in the live tile mode, switch the running task into screenshot mode. */
- public void switchToScreenshot(Runnable onFinishRunnable) {
+ public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
- if (taskView == null) {
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
+ if (taskView != null) {
+ taskView.setShowScreenshot(true);
+ if (thumbnailData != null) {
+ taskView.getThumbnail().setThumbnail(taskView.getTask(), thumbnailData);
+ } else {
+ taskView.getThumbnail().refresh();
}
- return;
+ ViewUtils.postDraw(taskView, onFinishRunnable);
+ } else {
+ onFinishRunnable.run();
}
-
- taskView.setShowScreenshot(true);
- taskView.getThumbnail().refresh();
- ViewUtils.postDraw(taskView, onFinishRunnable);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 409bec6..fdf16a1 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -60,6 +60,11 @@
ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
+ /**
+ * Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
+ */
+ default void setOnDeferredActivityLaunchCallback(Runnable r) {}
+
@Nullable
T getCreatedActivity();
@@ -96,7 +101,8 @@
default void closeOverlay() { }
- default void switchToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {}
+ default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
+ Runnable runnable) {}
interface AnimationFactory {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index de64227..4bd962a 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -16,12 +16,13 @@
package com.android.quickstep;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* Manages the state for an active system gesture, listens for events from the system and Launcher,
* and fires events when the states change.
*/
-public class GestureState {
+public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
// Needed to interact with the current activity
private BaseActivityInterface mActivityInterface;
@@ -33,4 +34,20 @@
public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
return mActivityInterface;
}
+
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ // To be implemented
+ }
+
+ @Override
+ public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+ // To be implemented
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ // To be implemented
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 2918879..acf61b4 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -127,7 +127,7 @@
*/
public interface RecentsAnimationListener {
default void onRecentsAnimationStart(RecentsAnimationController controller,
- RecentsAnimationTargets targetSet) {}
+ RecentsAnimationTargets targets) {}
/**
* Callback from the system when the recents animation is canceled. {@param thumbnailData}
@@ -135,6 +135,9 @@
*/
default void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {}
+ /**
+ * Callback made whenever the recents animation is finished.
+ */
default void onRecentsAnimationFinished(RecentsAnimationController controller) {}
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 9353759..718c5ba 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -41,13 +41,4 @@
public boolean hasTargets() {
return unfilteredApps.length != 0;
}
-
- /**
- * Clones the target set without any actual targets. Used only when continuing a gesture after
- * the actual recents animation has finished.
- */
- public RecentsAnimationTargets cloneWithoutTargets() {
- return new RecentsAnimationTargets(new RemoteAnimationTargetCompat[0],
- new RemoteAnimationTargetCompat[0], homeContentInsets, minimizedHomeBounds);
- }
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
new file mode 100644
index 0000000..557be5b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.UiThread;
+
+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;
+
+public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
+
+ private RecentsAnimationController mController;
+ private RecentsAnimationCallbacks mCallbacks;
+ private RecentsAnimationTargets mTargets;
+ // Temporary until we can hook into gesture state events
+ private GestureState mLastGestureState;
+ private ThumbnailData mCanceledThumbnail;
+
+ /**
+ * Preloads the recents animation.
+ */
+ public void preloadRecentsAnimation(Intent intent) {
+ // Pass null animation handler to indicate this start is for preloading
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, null, null, null, null));
+ }
+
+ /**
+ * Starts a new recents animation for the activity with the given {@param intent}.
+ */
+ @UiThread
+ public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
+ Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ // Notify if recents animation is still running
+ if (mController != null) {
+ String msg = "New recents animation started before old animation completed";
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ throw new IllegalArgumentException(msg);
+ } else {
+ Log.e("TaskAnimationManager", msg, new Exception());
+ }
+ }
+ // But force-finish it anyways
+ finishRunningRecentsAnimation(false /* toHome */);
+
+ final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
+ mLastGestureState = gestureState;
+ mCallbacks = new RecentsAnimationCallbacks(activityInterface.shouldMinimizeSplitScreen());
+ mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
+ @Override
+ public void onRecentsAnimationStart(RecentsAnimationController controller,
+ RecentsAnimationTargets targets) {
+ mController = controller;
+ mTargets = targets;
+ }
+
+ @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());
+ } else {
+ cleanUpRecentsAnimation();
+ }
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ cleanUpRecentsAnimation();
+ }
+ });
+ mCallbacks.addListener(gestureState);
+ mCallbacks.addListener(listener);
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, null, mCallbacks, null, null));
+ return mCallbacks;
+ }
+
+ /**
+ * Continues the existing running recents animation for a new gesture.
+ */
+ public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
+ mCallbacks.removeListener(mLastGestureState);
+ mLastGestureState = gestureState;
+ mCallbacks.addListener(gestureState);
+ return mCallbacks;
+ }
+
+ /**
+ * Finishes the running recents animation.
+ */
+ public void finishRunningRecentsAnimation(boolean toHome) {
+ if (mController != null) {
+ mCallbacks.notifyAnimationCanceled();
+ Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
+ ? mController::finishAnimationToHome
+ : mController::finishAnimationToApp);
+ cleanUpRecentsAnimation();
+ }
+ }
+
+ /**
+ * Used to notify a listener of the current recents animation state (used if the listener was
+ * not yet added to the callbacks at the point that the listener callbacks would have been
+ * made).
+ */
+ public void notifyRecentsAnimationState(
+ RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ if (isRecentsAnimationRunning()) {
+ listener.onRecentsAnimationStart(mController, mTargets);
+ }
+ // TODO: Do we actually need to report canceled/finished?
+ }
+
+ /**
+ * @return whether there is a recents animation running.
+ */
+ public boolean isRecentsAnimationRunning() {
+ return mController != null;
+ }
+
+ /**
+ * Cleans up the recents animation entirely.
+ */
+ private void cleanUpRecentsAnimation() {
+ // Clean up the screenshot if necessary
+ if (mController != null && mCanceledThumbnail != null) {
+ mController.cleanupScreenshot();
+ }
+
+ // Release all the target leashes
+ if (mTargets != null) {
+ mTargets.release();
+ }
+
+ // Remove gesture state from callbacks
+ if (mCallbacks != null && mLastGestureState != null) {
+ mCallbacks.removeListener(mLastGestureState);
+ }
+
+ mController = null;
+ mCallbacks = null;
+ mTargets = null;
+ mCanceledThumbnail = null;
+ mLastGestureState = null;
+ }
+
+ public void dump() {
+ // TODO
+ }
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4b4d793..67c1a04 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -267,6 +267,10 @@
private ArrayList<OnResumeCallback> mOnResumeCallbacks = new ArrayList<>();
+ // Used to notify when an activity launch has been deferred because launcher is not yet resumed
+ // TODO: See if we can remove this later
+ private Runnable mOnDeferredActivityLaunchCallback;
+
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
@@ -1886,7 +1890,10 @@
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
- UiFactory.clearSwipeSharedState(this, true /* finishAnimation */);
+ if (mOnDeferredActivityLaunchCallback != null) {
+ mOnDeferredActivityLaunchCallback.run();
+ mOnDeferredActivityLaunchCallback = null;
+ }
return true;
}
@@ -1948,6 +1955,14 @@
}
/**
+ * Persistant callback which notifies when an activity launch is deferred because the activity
+ * was not yet resumed.
+ */
+ public void setOnDeferredActivityLaunchCallback(Runnable callback) {
+ mOnDeferredActivityLaunchCallback = callback;
+ }
+
+ /**
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 6d9ed88..606c990 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -96,9 +96,6 @@
public static void resetPendingActivityResults(Launcher launcher, int requestCode) { }
- /** No-op. */
- public static void clearSwipeSharedState(Launcher launcher, boolean finishAnimation) { }
-
public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}