diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index f2aa842d12..d5ea1ecc18 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 abstract class RecentsUiFactory { 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 ff73679a91..2a22e9d5ae 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 @@ package com.android.launcher3.uioverrides; 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; 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 @@ public abstract class RecentsUiFactory { 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.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 f6b3654e69..c5289355b8 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 @@ public final class LauncherActivityInterface implements BaseActivityInterface { - 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 class SwipeSharedState implements RecentsAnimationListener { 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 @@ public class SwipeSharedState implements RecentsAnimationListener { 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 e244e848fc..cc7eb9bbb4 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 @@ public class TouchInteractionService extends Service implements 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 @@ public class TouchInteractionService extends Service implements 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 @@ public class TouchInteractionService extends Service implements @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 @@ public class TouchInteractionService extends Service implements 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 @@ public class TouchInteractionService extends Service implements 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 @@ public class TouchInteractionService extends Service implements } 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 @@ public class TouchInteractionService extends Service implements return; } - // Pass null animation handler to indicate this start is preload. - startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), - null); + mTaskAnimationManager.preloadRecentsAnimation( + mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState()); } @Override @@ -725,9 +736,9 @@ public class TouchInteractionService extends Service implements 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 35f8be7461..29f431d40f 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 @@ public class WindowTransformSwipeHandler 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 @@ public class WindowTransformSwipeHandler 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 @@ public class WindowTransformSwipeHandler // 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 @@ public class WindowTransformSwipeHandler 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 12b7c2622d..980cfad4e9 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 android.view.MotionEvent.ACTION_UP; 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.RecentsAnimationDeviceState; 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 @@ public class DeviceLockedInputConsumer implements InputConsumer, 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 @@ public class DeviceLockedInputConsumer implements InputConsumer, 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 @@ public class DeviceLockedInputConsumer implements InputConsumer, 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 02f4c4032d..6ba326c650 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 android.view.MotionEvent.INVALID_POINTER_ID; 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.RecentsAnimationDeviceState; 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 class OtherActivityInputConsumer extends ContextWrapper implements InputC 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 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final Consumer mOnCompleteCallback; private final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; + private VelocityTracker mVelocityTracker; private BaseSwipeUpHandler mInteractionHandler; @@ -126,13 +129,15 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private int mLogId; public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState, - GestureState gestureState, RunningTaskInfo runningTaskInfo, - boolean isDeferredDownTarget, Consumer onCompleteCallback, + TaskAnimationManager taskAnimationManager, GestureState gestureState, + RunningTaskInfo runningTaskInfo, boolean isDeferredDownTarget, + Consumer 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 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mVelocityTracker = VelocityTracker.obtain(); mInputMonitorCompat = inputMonitorCompat; - boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null; + boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning(); mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget; mSwipeSharedState = swipeSharedState; @@ -329,25 +334,22 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC 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 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } 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 e04c0c741c..b22a75b7c7 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 @@ package com.android.quickstep.inputconsumers; 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 @@ public class ResetGestureInputConsumer implements InputConsumer { @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 0655c733ba..5a65c15376 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 @@ public class LauncherRecentsView extends RecentsView implements StateL @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 6120b9e9f9..5d4665d48a 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 @@ public abstract class RecentsView extends PagedView impl 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 409bec6c70..fdf16a1057 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -60,6 +60,11 @@ public interface BaseActivityInterface { ActivityInitListener createActivityInitListener(BiPredicate 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 @@ public interface BaseActivityInterface { 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 de64227c26..4bd962a5ff 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 class GestureState { public BaseActivityInterface 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 2918879d74..acf61b4142 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -127,7 +127,7 @@ public class RecentsAnimationCallbacks implements */ 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 @@ public class RecentsAnimationCallbacks implements */ 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 93537597d5..718c5baa2c 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java @@ -41,13 +41,4 @@ public class RecentsAnimationTargets extends RemoteAnimationTargets { 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 0000000000..557be5b554 --- /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 4b4d7939e6..67c1a04163 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -267,6 +267,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private ArrayList 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 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // 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; } @@ -1947,6 +1954,14 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mOnResumeCallbacks.add(callback); } + /** + * 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. */ diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index 6d9ed88e08..606c9905f1 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 class UiFactory { 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; }