From c9bf6d45ac8964ccd307c1095abfe160304e35ff Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 4 Oct 2019 15:33:18 -0700 Subject: [PATCH] 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 --- .../uioverrides/RecentsUiFactory.java | 2 - .../uioverrides/RecentsUiFactory.java | 13 -- .../quickstep/LauncherActivityInterface.java | 21 +-- .../android/quickstep/SwipeSharedState.java | 128 +------------ .../quickstep/TouchInteractionService.java | 49 +++-- .../WindowTransformSwipeHandler.java | 26 ++- .../DeviceLockedInputConsumer.java | 17 +- .../OtherActivityInputConsumer.java | 45 ++--- .../ResetGestureInputConsumer.java | 14 +- .../quickstep/views/LauncherRecentsView.java | 5 +- .../android/quickstep/views/RecentsView.java | 20 +- .../quickstep/BaseActivityInterface.java | 8 +- .../com/android/quickstep/GestureState.java | 19 +- .../quickstep/RecentsAnimationCallbacks.java | 5 +- .../quickstep/RecentsAnimationTargets.java | 9 - .../quickstep/TaskAnimationManager.java | 173 ++++++++++++++++++ src/com/android/launcher3/Launcher.java | 17 +- .../launcher3/uioverrides/UiFactory.java | 3 - 18 files changed, 338 insertions(+), 236 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/TaskAnimationManager.java 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; }