diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java index feaa8b9afb..5a3449b62c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2018 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. @@ -17,83 +17,1418 @@ package com.android.quickstep; import static android.widget.Toast.LENGTH_SHORT; +import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; +import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; +import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; +import static com.android.quickstep.GestureState.GestureEndTarget.HOME; +import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK; +import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK; +import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; +import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED; +import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; +import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; +import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; +import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; +import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; +import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; import android.annotation.TargetApi; +import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.graphics.PointF; import android.graphics.Rect; import android.os.Build; +import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnApplyWindowInsetsListener; +import android.view.ViewTreeObserver.OnDrawListener; +import android.view.WindowInsets; +import android.view.animation.Interpolator; import android.widget.Toast; -import androidx.annotation.CallSuper; +import androidx.annotation.Nullable; import androidx.annotation.UiThread; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.logging.StatsLogManager.StatsLogger; +import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.WindowBounds; -import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; +import com.android.quickstep.BaseActivityInterface.AnimationFactory; +import com.android.quickstep.GestureState.GestureEndTarget; +import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.InputConsumerProxy; import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.ShelfPeekAnim; +import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; +import com.android.quickstep.views.LiveTileOverlay; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; +import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.TaskInfoCompat; +import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; import java.util.function.Consumer; /** - * Base class for swipe up handler with some utility methods + * Handles the navigation gestures when Launcher is the default home activity. */ -@TargetApi(Build.VERSION_CODES.Q) +@TargetApi(Build.VERSION_CODES.R) public abstract class AbsSwipeUpHandler, Q extends RecentsView> - extends SwipeUpAnimationLogic implements RecentsAnimationListener { - + extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener, + RecentsAnimationCallbacks.RecentsAnimationListener { private static final String TAG = "AbsSwipeUpHandler"; + private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null; + protected final BaseActivityInterface mActivityInterface; protected final InputConsumerProxy mInputConsumerProxy; - protected final ActivityInitListener mActivityInitListener; - - protected RecentsAnimationController mRecentsAnimationController; - protected RecentsAnimationTargets mRecentsAnimationTargets; - // Callbacks to be made once the recents animation starts private final ArrayList mRecentsAnimationStartCallbacks = new ArrayList<>(); - + protected RecentsAnimationController mRecentsAnimationController; + protected RecentsAnimationTargets mRecentsAnimationTargets; protected T mActivity; protected Q mRecentsView; - protected Runnable mGestureEndCallback; - protected MultiStateCallback mStateCallback; - protected boolean mCanceled; - private boolean mRecentsViewScrollLinked = false; - protected AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState, - GestureState gestureState, InputConsumerController inputConsumer) { + private static int getFlagForIndex(int index, String name) { + if (DEBUG_STATES) { + STATE_NAMES[index] = name; + } + return 1 << index; + } + + // Launcher UI related states + protected static final int STATE_LAUNCHER_PRESENT = + getFlagForIndex(0, "STATE_LAUNCHER_PRESENT"); + protected static final int STATE_LAUNCHER_STARTED = + getFlagForIndex(1, "STATE_LAUNCHER_STARTED"); + protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN"); + + // Internal initialization states + private static final int STATE_APP_CONTROLLER_RECEIVED = + getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED"); + + // Interaction finish states + private static final int STATE_SCALED_CONTROLLER_HOME = + getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME"); + private static final int STATE_SCALED_CONTROLLER_RECENTS = + getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS"); + + protected static final int STATE_HANDLER_INVALIDATED = + getFlagForIndex(6, "STATE_HANDLER_INVALIDATED"); + private static final int STATE_GESTURE_STARTED = + getFlagForIndex(7, "STATE_GESTURE_STARTED"); + private static final int STATE_GESTURE_CANCELLED = + getFlagForIndex(8, "STATE_GESTURE_CANCELLED"); + private static final int STATE_GESTURE_COMPLETED = + getFlagForIndex(9, "STATE_GESTURE_COMPLETED"); + + private static final int STATE_CAPTURE_SCREENSHOT = + getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT"); + protected static final int STATE_SCREENSHOT_CAPTURED = + getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED"); + private static final int STATE_SCREENSHOT_VIEW_SHOWN = + getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN"); + + private static final int STATE_RESUME_LAST_TASK = + getFlagForIndex(13, "STATE_RESUME_LAST_TASK"); + private static final int STATE_START_NEW_TASK = + getFlagForIndex(14, "STATE_START_NEW_TASK"); + private static final int STATE_CURRENT_TASK_FINISHED = + getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED"); + + private static final int LAUNCHER_UI_STATES = + STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED; + + public static final long MAX_SWIPE_DURATION = 350; + public static final long MIN_OVERSHOOT_DURATION = 120; + + public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; + private static final float SWIPE_DURATION_MULTIPLIER = + Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW)); + private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured"; + + public static final long RECENTS_ATTACH_DURATION = 300; + + /** + * Used as the page index for logging when we return to the last task at the end of the gesture. + */ + private static final int LOG_NO_OP_PAGE_INDEX = -1; + + protected final TaskAnimationManager mTaskAnimationManager; + + // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise + private RunningWindowAnim mRunningWindowAnim; + private boolean mIsShelfPeeking; + + private boolean mContinuingLastGesture; + + private ThumbnailData mTaskSnapshot; + + // Used to control launcher components throughout the swipe gesture. + private AnimatorPlaybackController mLauncherTransitionController; + private boolean mHasLauncherTransitionControllerStarted; + + private AnimationFactory mAnimationFactory = (t) -> { }; + + private boolean mWasLauncherAlreadyVisible; + + private boolean mPassedOverviewThreshold; + private boolean mGestureStarted; + private int mLogAction = Touch.SWIPE; + private int mLogDirection = Direction.UP; + private PointF mDownPos; + private boolean mIsLikelyToStartNewTask; + + private final long mTouchTimeMs; + private long mLauncherFrameDrawnTime; + + private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch; + + public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState, + TaskAnimationManager taskAnimationManager, GestureState gestureState, + long touchTimeMs, boolean continuingLastGesture, + InputConsumerController inputConsumer) { super(context, deviceState, gestureState, new TransformParams()); mActivityInterface = gestureState.getActivityInterface(); mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit); mInputConsumerProxy = new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler); + mTaskAnimationManager = taskAnimationManager; + mTouchTimeMs = touchTimeMs; + mContinuingLastGesture = continuingLastGesture; + + initAfterSubclassConstructor(); + initStateCallbacks(); + } + + private void initStateCallbacks() { + mStateCallback = new MultiStateCallback(STATE_NAMES); + + mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED, + this::onLauncherPresentAndGestureStarted); + + mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED, + this::initializeLauncherAnimationController); + + mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN, + this::launcherFrameDrawn); + + mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED + | STATE_GESTURE_CANCELLED, + this::resetStateForAnimationCancel); + + mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED, + this::resumeLastTask); + mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED, + this::startNewTask); + + mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED + | STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT, + this::switchToScreenshot); + + mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED + | STATE_SCALED_CONTROLLER_RECENTS, + this::finishCurrentTransitionToRecents); + + mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED + | STATE_SCALED_CONTROLLER_HOME, + this::finishCurrentTransitionToHome); + mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED, + this::reset); + + mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED + | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS + | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED + | STATE_GESTURE_STARTED, + this::setupLauncherUiAfterSwipeUpToRecentsAnimation); + + mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, + this::continueComputingRecentsScrollIfNecessary); + mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED + | STATE_RECENTS_SCROLLING_FINISHED, + this::onSettledOnEndTarget); + + mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler); + mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, + this::invalidateHandlerWithLauncher); + mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK, + this::notifyTransitionCancelled); + + if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT + | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT, + (b) -> mRecentsView.setRunningTaskHidden(!b)); + } + } + + protected boolean onActivityInit(Boolean alreadyOnHome) { + T createdActivity = mActivityInterface.getCreatedActivity(); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1"); + } + if (createdActivity != null) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2"); + } + initTransitionEndpoints(createdActivity.getDeviceProfile()); + } + final T activity = mActivityInterface.getCreatedActivity(); + if (mActivity == activity) { + return true; + } + + if (mActivity != null) { + // The launcher may have been recreated as a result of device rotation. + int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES; + initStateCallbacks(); + mStateCallback.setState(oldState); + } + mWasLauncherAlreadyVisible = alreadyOnHome; + mActivity = activity; + // Override the visibility of the activity until the gesture actually starts and we swipe + // up, or until we transition home and the home animation is composed + if (alreadyOnHome) { + mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); + } else { + mActivity.addForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); + } + + mRecentsView = activity.getOverviewPanel(); + mRecentsView.setOnPageTransitionEndCallback(null); + addLiveTileOverlay(); + + mStateCallback.setState(STATE_LAUNCHER_PRESENT); + if (alreadyOnHome) { + onLauncherStart(); + } else { + activity.runOnceOnStart(this::onLauncherStart); + } + + setupRecentsViewUi(); + linkRecentsViewScroll(); + + return true; + } + + /** + * Return true if the window should be translated horizontally if the recents view scrolls + */ + protected boolean moveWindowWithRecentsScroll() { + return mGestureState.getEndTarget() != HOME; + } + + private void onLauncherStart() { + final T activity = mActivityInterface.getCreatedActivity(); + if (mActivity != activity) { + return; + } + if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { + return; + } + mTaskViewSimulator.setRecentsRotation(mActivity.getDisplay().getRotation()); + + // If we've already ended the gesture and are going home, don't prepare recents UI, + // as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL. + if (mGestureState.getEndTarget() != HOME) { + Runnable initAnimFactory = () -> { + mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState, + mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated); + maybeUpdateRecentsAttachedState(false /* animate */); + }; + if (mWasLauncherAlreadyVisible) { + // Launcher is visible, but might be about to stop. Thus, if we prepare recents + // now, it might get overridden by moveToRestState() in onStop(). To avoid this, + // wait until the next gesture (and possibly launcher) starts. + mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, initAnimFactory); + } else { + initAnimFactory.run(); + } + } + AbstractFloatingView.closeAllOpenViewsExcept(activity, mWasLauncherAlreadyVisible, + AbstractFloatingView.TYPE_LISTENER); + + if (mWasLauncherAlreadyVisible) { + mStateCallback.setState(STATE_LAUNCHER_DRAWN); + } else { + Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init"); + View dragLayer = activity.getDragLayer(); + dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { + boolean mHandled = false; + + @Override + public void onDraw() { + if (mHandled) { + return; + } + mHandled = true; + + TraceHelper.INSTANCE.endSection(traceToken); + dragLayer.post(() -> + dragLayer.getViewTreeObserver().removeOnDrawListener(this)); + if (activity != mActivity) { + return; + } + + mStateCallback.setState(STATE_LAUNCHER_DRAWN); + } + }); + } + + activity.getRootView().setOnApplyWindowInsetsListener(this); + mStateCallback.setState(STATE_LAUNCHER_STARTED); + } + + private void onLauncherPresentAndGestureStarted() { + // Re-setup the recents UI when gesture starts, as the state could have been changed during + // 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); + + mGestureState.runOnceAtState(STATE_END_TARGET_SET, + () -> mDeviceState.getRotationTouchHelper(). + onEndTargetCalculated(mGestureState.getEndTarget(), + mActivityInterface)); + + notifyGestureStartedAsync(); + } + + private void onDeferredActivityLaunch() { + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mActivityInterface.switchRunningTaskViewToScreenshot( + null, () -> { + mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */); + }); + } else { + mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */); + } + } + + private void setupRecentsViewUi() { + if (mContinuingLastGesture) { + updateSysUiFlags(mCurrentShift.value); + return; + } + notifyGestureAnimationStartToRecents(); + } + + protected void notifyGestureAnimationStartToRecents() { + mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask()); + } + + private void launcherFrameDrawn() { + mLauncherFrameDrawnTime = SystemClock.uptimeMillis(); + } + + private void initializeLauncherAnimationController() { + buildAnimationController(); + + Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents", + TraceHelper.FLAG_IGNORE_BINDERS); + // Only used in debug builds + if (LatencyTrackerCompat.isEnabled(mContext)) { + LatencyTrackerCompat.logToggleRecents( + (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); + } + TraceHelper.INSTANCE.endSection(traceToken); + + // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the + // high-res thumbnail loader here once we are sure that we will end up in an overview state + RecentsModel.INSTANCE.get(mContext).getThumbnailCache() + .getHighResLoadingState().setVisible(true); + } + + /** + * Called when motion pause is detected + */ + public void onMotionPauseChanged(boolean isPaused) { + setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION); + } + + public void maybeUpdateRecentsAttachedState() { + maybeUpdateRecentsAttachedState(true /* animate */); + } + + /** + * Determines whether to show or hide RecentsView. The window is always + * synchronized with its corresponding TaskView in RecentsView, so if + * RecentsView is shown, it will appear to be attached to the window. + * + * Note this method has no effect unless the navigation mode is NO_BUTTON. + */ + private void maybeUpdateRecentsAttachedState(boolean animate) { + if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) { + return; + } + RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null + ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) + : null; + final boolean recentsAttachedToAppWindow; + if (mGestureState.getEndTarget() != null) { + recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow; + } else if (mContinuingLastGesture + && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) { + recentsAttachedToAppWindow = true; + } else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) { + // The window is going away so make sure recents is always visible in this case. + recentsAttachedToAppWindow = true; + } else { + recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask; + } + mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate); + } + + public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { + setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */); + } + + private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) { + if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) { + mIsLikelyToStartNewTask = isLikelyToStartNewTask; + maybeUpdateRecentsAttachedState(animate); + } + } + + @UiThread + public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) { + mAnimationFactory.setShelfState(shelfState, interpolator, duration); + boolean wasShelfPeeking = mIsShelfPeeking; + mIsShelfPeeking = shelfState == PEEK; + if (mIsShelfPeeking != wasShelfPeeking) { + maybeUpdateRecentsAttachedState(); + } + if (shelfState.shouldPreformHaptic) { + performHapticFeedback(); + } + } + + private void buildAnimationController() { + if (!canCreateNewOrUpdateExistingLauncherTransitionController()) { + return; + } + initTransitionEndpoints(mActivity.getDeviceProfile()); + mAnimationFactory.createActivityInterface(mTransitionDragLength); + } + + /** + * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME + * (it has its own animation) or if we're already animating the current controller. + * @return Whether we can create the launcher controller or update its progress. + */ + private boolean canCreateNewOrUpdateExistingLauncherTransitionController() { + return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted; + } + + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { + WindowInsets result = view.onApplyWindowInsets(windowInsets); + buildAnimationController(); + return result; + } + + private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { + mLauncherTransitionController = anim; + mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor); + mLauncherTransitionController.dispatchOnStart(); + updateLauncherTransitionProgress(); + } + + public Intent getLaunchIntent() { + return mGestureState.getOverviewIntent(); + } + + /** + * Called when the value of {@link #mCurrentShift} changes + */ + @UiThread + @Override + public void updateFinalShift() { + final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; + if (passed != mPassedOverviewThreshold) { + mPassedOverviewThreshold = passed; + if (!mDeviceState.isFullyGesturalNavMode()) { + performHapticFeedback(); + } + } + + updateSysUiFlags(mCurrentShift.value); + applyWindowTransform(); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + if (mRecentsAnimationTargets != null) { + LiveTileOverlay.INSTANCE.update( + mTaskViewSimulator.getCurrentRect(), + mTaskViewSimulator.getCurrentCornerRadius()); + } + } + + updateLauncherTransitionProgress(); + } + + private void updateLauncherTransitionProgress() { + if (mLauncherTransitionController == null + || !canCreateNewOrUpdateExistingLauncherTransitionController()) { + return; + } + // Normalize the progress to 0 to 1, as the animation controller will clamp it to that + // anyway. The controller mimics the drag length factor by applying it to its interpolators. + float progress = mCurrentShift.value / mDragLengthFactor; + mLauncherTransitionController.setPlayFraction(progress); + } + + /** + * @param windowProgress 0 == app, 1 == overview + */ + private void updateSysUiFlags(float windowProgress) { + if (mRecentsAnimationController != null && mRecentsView != null) { + TaskView runningTask = mRecentsView.getRunningTaskView(); + TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen(); + int centermostTaskFlags = centermostTask == null ? 0 + : centermostTask.getThumbnail().getSysUiStatusNavFlags(); + boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD; + boolean quickswitchThresholdPassed = centermostTask != runningTask; + + // We will handle the sysui flags based on the centermost task view. + mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed + || (quickswitchThresholdPassed && centermostTaskFlags != 0)); + mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed); + + int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags; + mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags); + } + } + + @Override + public void onRecentsAnimationStart(RecentsAnimationController controller, + RecentsAnimationTargets targets) { + ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length); + mRecentsAnimationController = controller; + mRecentsAnimationTargets = targets; + mTransformParams.setTargetSet(mRecentsAnimationTargets); + RemoteAnimationTargetCompat runningTaskTarget = targets.findTask( + mGestureState.getRunningTaskId()); + + if (runningTaskTarget != null) { + mTaskViewSimulator.setPreview(runningTaskTarget); + } + + // Only initialize the device profile, if it has not been initialized before, as in some + // configurations targets.homeContentInsets may not be correct. + if (mActivity == null) { + DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile(); + if (targets.minimizedHomeBounds != null && runningTaskTarget != null) { + Rect overviewStackBounds = mActivityInterface + .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget); + dp = dp.getMultiWindowProfile(mContext, + new WindowBounds(overviewStackBounds, targets.homeContentInsets)); + } else { + // If we are not in multi-window mode, home insets should be same as system insets. + dp = dp.copy(mContext); + } + dp.updateInsets(targets.homeContentInsets); + dp.updateIsSeascape(mContext); + initTransitionEndpoints(dp); + } + + // Notify when the animation starts + if (!mRecentsAnimationStartCallbacks.isEmpty()) { + for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) { + action.run(); + } + mRecentsAnimationStartCallbacks.clear(); + } + + // Only add the callback to enable the input consumer after we actually have the controller + mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED, + mRecentsAnimationController::enableInputConsumer); + mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); + + mPassedOverviewThreshold = false; + } + + @Override + public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { + ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation"); + mActivityInitListener.unregister(); + mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED); + + // Defer clearing the controller and the targets until after we've updated the state + mRecentsAnimationController = null; + mRecentsAnimationTargets = null; + if (mRecentsView != null) { + mRecentsView.setRecentsAnimationTargets(null, null); + } + } + + @UiThread + public void onGestureStarted(boolean isLikelyToStartNewTask) { + notifyGestureStartedAsync(); + setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */); + mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED); + mGestureStarted = true; + } + + /** + * Notifies the launcher that the swipe gesture has started. This can be called multiple times. + */ + @UiThread + private void notifyGestureStartedAsync() { + final T curActivity = mActivity; + if (curActivity != null) { + // Once the gesture starts, we can no longer transition home through the button, so + // reset the force override of the activity visibility + mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); + } + } + + /** + * Called as a result on ACTION_CANCEL to return the UI to the start state. + */ + @UiThread + public void onGestureCancelled() { + updateDisplacement(0); + mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED); + mLogAction = Touch.SWIPE_NOOP; + handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */); + } + + /** + * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen. + * @param velocity The x and y components of the velocity when the gesture ends. + * @param downPos The x and y value of where the gesture started. + */ + @UiThread + public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { + float flingThreshold = mContext.getResources() + .getDimension(R.dimen.quickstep_fling_threshold_velocity); + boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold; + mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED); + + mLogAction = isFling ? Touch.FLING : Touch.SWIPE; + boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x); + if (isVelocityVertical) { + mLogDirection = velocity.y < 0 ? Direction.UP : Direction.DOWN; + } else { + mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT; + } + mDownPos = downPos; + handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */); + } + + /** + * Called to create a input proxy for the running task + */ + @UiThread + protected InputConsumer createNewInputProxyHandler() { + endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */); + endLauncherTransitionController(); + + StatefulActivity activity = mActivityInterface.getCreatedActivity(); + return activity == null ? InputConsumer.NO_OP + : new OverviewInputConsumer(mGestureState, activity, null, true); + } + + private void endRunningWindowAnim(boolean cancel) { + if (mRunningWindowAnim != null) { + if (cancel) { + mRunningWindowAnim.cancel(); + } else { + mRunningWindowAnim.end(); + } + } + } + + private void onSettledOnEndTarget() { + switch (mGestureState.getEndTarget()) { + case HOME: + mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); + // Notify swipe-to-home (recents animation) is finished + SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished(); + break; + case RECENTS: + mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT + | STATE_SCREENSHOT_VIEW_SHOWN); + break; + case NEW_TASK: + mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT); + break; + case LAST_TASK: + mStateCallback.setState(STATE_RESUME_LAST_TASK); + break; + } + ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget()); + } + + /** @return Whether this was the task we were waiting to appear, and thus handled it. */ + protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) { + if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { + return false; + } + if (mStateCallback.hasStates(STATE_START_NEW_TASK) + && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) { + reset(); + return true; + } + return false; + } + + private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling, + boolean isCancel) { + final GestureEndTarget endTarget; + final boolean goingToNewTask; + if (mRecentsView != null) { + if (!hasTargets()) { + // If there are no running tasks, then we can assume that this is a continuation of + // the last gesture, but after the recents animation has finished + goingToNewTask = true; + } else { + final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); + final int taskToLaunch = mRecentsView.getNextPage(); + goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex; + } + } else { + goingToNewTask = false; + } + final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; + if (!isFling) { + if (isCancel) { + endTarget = LAST_TASK; + } else if (mDeviceState.isFullyGesturalNavMode()) { + if (mIsShelfPeeking) { + endTarget = RECENTS; + } else if (goingToNewTask) { + endTarget = NEW_TASK; + } else { + endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME; + } + } else { + endTarget = reachedOverviewThreshold && mGestureStarted + ? RECENTS + : goingToNewTask + ? NEW_TASK + : LAST_TASK; + } + } else { + // If swiping at a diagonal, base end target on the faster velocity. + boolean isSwipeUp = endVelocity < 0; + boolean willGoToNewTaskOnSwipeUp = + goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity); + + if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) { + endTarget = HOME; + } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) { + // If swiping at a diagonal, base end target on the faster velocity. + endTarget = NEW_TASK; + } else if (isSwipeUp) { + endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp + ? NEW_TASK : RECENTS; + } else { + endTarget = goingToNewTask ? NEW_TASK : LAST_TASK; + } + } + + if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) { + return LAST_TASK; + } + return endTarget; + } + + @UiThread + private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity, + boolean isCancel) { + PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000); + long duration = MAX_SWIPE_DURATION; + float currentShift = mCurrentShift.value; + final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity, + isFling, isCancel); + float endShift = endTarget.isLauncher ? 1 : 0; + final float startShift; + Interpolator interpolator = DEACCEL; + if (!isFling) { + long expectedDuration = Math.abs(Math.round((endShift - currentShift) + * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); + duration = Math.min(MAX_SWIPE_DURATION, expectedDuration); + startShift = currentShift; + interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; + } else { + startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y + * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); + float minFlingVelocity = mContext.getResources() + .getDimension(R.dimen.quickstep_fling_min_velocity); + if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) { + if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) { + Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams( + startShift, endShift, endShift, endVelocity / 1000, + mTransitionDragLength, mContext); + endShift = overshoot.end; + interpolator = overshoot.interpolator; + duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION, + MAX_SWIPE_DURATION); + } else { + float distanceToTravel = (endShift - currentShift) * mTransitionDragLength; + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 2. + long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); + duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); + + if (endTarget == RECENTS) { + interpolator = OVERSHOOT_1_2; + } + } + } + } + + if (endTarget.isLauncher) { + mInputConsumerProxy.enable(); + } + if (endTarget == HOME) { + setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); + duration = Math.max(MIN_OVERSHOOT_DURATION, duration); + } else if (endTarget == RECENTS) { + LiveTileOverlay.INSTANCE.startIconAnimation(); + if (mRecentsView != null) { + int nearestPage = mRecentsView.getPageNearestToCenterOfScreen(); + if (mRecentsView.getNextPage() != nearestPage) { + // We shouldn't really scroll to the next page when swiping up to recents. + // Only allow settling on the next page if it's nearest to the center. + mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration)); + } + if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) { + mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION); + } + duration = Math.max(duration, mRecentsView.getScroller().getDuration()); + } + if (mDeviceState.isFullyGesturalNavMode()) { + setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration); + } + } + + // Let RecentsView handle the scrolling to the task, which we launch in startNewTask() + // or resumeLastTask(). + if (mRecentsView != null) { + mRecentsView.setOnPageTransitionEndCallback( + () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED)); + } else { + mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED); + } + + animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs); + } + + private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) { + StatsLogManager.EventEnum event; + switch (endTarget) { + case HOME: + event = LAUNCHER_HOME_GESTURE; + break; + case RECENTS: + event = LAUNCHER_OVERVIEW_GESTURE; + break; + case LAST_TASK: + case NEW_TASK: + event = (mLogDirection == Direction.LEFT) + ? LAUNCHER_QUICKSWITCH_LEFT + : LAUNCHER_QUICKSWITCH_RIGHT; + break; + default: + event = IGNORE; + } + StatsLogger logger = StatsLogManager.newInstance(mContext).logger() + .withSrcState(LAUNCHER_STATE_BACKGROUND) + .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType)); + if (targetTask != null) { + logger.withItemInfo(targetTask.getItemInfo()); + } + logger.log(event); + + + DeviceProfile dp = mDp; + if (dp == null || mDownPos == null) { + // We probably never received an animation controller, skip logging. + return; + } + int pageIndex = endTarget == LAST_TASK + ? LOG_NO_OP_PAGE_INDEX + : mRecentsView.getNextPage(); + UserEventDispatcher.newInstance(mContext).logStateChangeAction( + mLogAction, mLogDirection, + (int) mDownPos.x, (int) mDownPos.y, + ContainerType.NAVBAR, ContainerType.APP, + endTarget.containerType, + pageIndex); + } + + /** Animates to the given progress, where 0 is the current app and 1 is overview. */ + @UiThread + private void animateToProgress(float start, float end, long duration, Interpolator interpolator, + GestureEndTarget target, PointF velocityPxPerMs) { + runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration, + interpolator, target, velocityPxPerMs)); + } + + protected abstract HomeAnimationFactory createHomeAnimationFactory(long duration); + + private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() { + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + if (task.taskId == mGestureState.getRunningTaskId() + && TaskInfoCompat.getActivityType(task) != ACTIVITY_TYPE_HOME) { + // Since this is an edge case, just cancel and relaunch with default activity + // options (since we don't know if there's an associated app icon to launch from) + endRunningWindowAnim(true /* cancel */); + ActivityManagerWrapper.getInstance().unregisterTaskStackListener( + mActivityRestartListener); + ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null); + } + } + }; + + @UiThread + private void animateToProgressInternal(float start, float end, long duration, + Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) { + // Set the state, but don't notify until the animation completes + mGestureState.setEndTarget(target, false /* isAtomic */); + maybeUpdateRecentsAttachedState(); + + // If we are transitioning to launcher, then listen for the activity to be restarted while + // the transition is in progress + if (mGestureState.getEndTarget().isLauncher) { + ActivityManagerWrapper.getInstance().registerTaskStackListener( + mActivityRestartListener); + } + + if (mGestureState.getEndTarget() == HOME) { + HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration); + RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory); + windowAnim.addAnimatorListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (mRecentsAnimationController == null) { + // If the recents animation is interrupted, we still end the running + // animation (not canceled) so this is still called. In that case, we can + // skip doing any future work here for the current gesture. + return; + } + // Finalize the state and notify of the change + mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); + } + }); + getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); + windowAnim.start(mContext, velocityPxPerMs); + homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); + mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); + mLauncherTransitionController = null; + } else { + ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end); + windowAnim.setDuration(duration).setInterpolator(interpolator); + windowAnim.addUpdateListener(valueAnimator -> { + computeRecentsScrollIfInvisible(); + }); + windowAnim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (mRecentsAnimationController == null) { + // If the recents animation is interrupted, we still end the running + // animation (not canceled) so this is still called. In that case, we can + // skip doing any future work here for the current gesture. + return; + } + if (mRecentsView != null) { + int taskToLaunch = mRecentsView.getNextPage(); + int runningTask = getLastAppearedTaskIndex(); + boolean hasStartedNewTask = hasStartedNewTask(); + if (target == NEW_TASK && taskToLaunch == runningTask + && !hasStartedNewTask) { + // We are about to launch the current running task, so use LAST_TASK + // state instead of NEW_TASK. This could happen, for example, if our + // scroll is aborted after we determined the target to be NEW_TASK. + mGestureState.setEndTarget(LAST_TASK); + } else if (target == LAST_TASK && hasStartedNewTask) { + // We are about to re-launch the previously running task, but we can't + // just finish the controller like we normally would because that would + // instead resume the last task that appeared, and not ensure that this + // task is restored to the top. To address this, re-launch the task as + // if it were a new task. + mGestureState.setEndTarget(NEW_TASK); + } + } + mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); + } + }); + windowAnim.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); + } + // Always play the entire launcher animation when going home, since it is separate from + // the animation that has been controlled thus far. + if (mGestureState.getEndTarget() == HOME) { + start = 0; + } + + // We want to use the same interpolator as the window, but need to adjust it to + // interpolate over the remaining progress (end - start). + TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress( + interpolator, start, end); + if (mLauncherTransitionController == null) { + return; + } + if (start == end || duration <= 0) { + mLauncherTransitionController.dispatchSetInterpolator(t -> end); + } else { + mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator); + } + mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration)); + + if (UNSTABLE_SPRINGS.get()) { + mLauncherTransitionController.dispatchOnStart(); + } + mLauncherTransitionController.getAnimationPlayer().start(); + mHasLauncherTransitionControllerStarted = true; + } + + private void computeRecentsScrollIfInvisible() { + if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) { + // Views typically don't compute scroll when invisible as an optimization, + // but in our case we need to since the window offset depends on the scroll. + mRecentsView.computeScroll(); + } + } + + private void continueComputingRecentsScrollIfNecessary() { + if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED) + && !mStateCallback.hasStates(STATE_HANDLER_INVALIDATED) + && !mCanceled) { + computeRecentsScrollIfInvisible(); + mRecentsView.postOnAnimation(this::continueComputingRecentsScrollIfNecessary); + } + } + + /** + * Creates an animation that transforms the current app window into the home app. + * @param startProgress The progress of {@link #mCurrentShift} to start the window from. + * @param homeAnimationFactory The home animation factory. + */ + @Override + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, + HomeAnimationFactory homeAnimationFactory) { + RectFSpringAnim anim = + super.createWindowAnimationToHome(startProgress, homeAnimationFactory); + anim.addOnUpdateListener((r, p) -> { + updateSysUiFlags(Math.max(p, mCurrentShift.value)); + }); + anim.addAnimatorListener(new AnimationSuccessListener() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + if (mActivity != null) { + removeLiveTileOverlay(); + } + } + + @Override + public void onAnimationSuccess(Animator animator) { + if (mRecentsView != null) { + mRecentsView.post(mRecentsView::resetTaskVisuals); + } + // Make sure recents is in its final state + maybeUpdateRecentsAttachedState(false); + mActivityInterface.onSwipeUpToHomeComplete(mDeviceState); + } + }); + if (mRecentsAnimationTargets != null) { + mRecentsAnimationTargets.addReleaseCheck(anim); + } + return anim; + } + + public void onConsumerAboutToBeSwitched() { + if (mActivity != null) { + // In the off chance that the gesture ends before Launcher is started, we should clear + // the callback here so that it doesn't update with the wrong state + mActivity.clearRunOnceOnStartCallback(); + resetLauncherListenersAndOverlays(); + } + if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) { + cancelCurrentAnimation(); + } else { + reset(); + } + } + + public boolean isCanceled() { + return mCanceled; + } + + @UiThread + private void resumeLastTask() { + mRecentsAnimationController.finish(false /* toRecents */, null); + ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false); + doLogGesture(LAST_TASK, null); + reset(); + } + + @UiThread + private void startNewTask() { + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal); + } else { + startNewTaskInternal(); + } + } + + @UiThread + private void startNewTaskInternal() { + TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView(); + startNewTask(success -> { + if (!success) { + reset(); + // We couldn't launch the task, so take user to overview so they can + // decide what to do instead of staying in this broken state. + endLauncherTransitionController(); + updateSysUiFlags(1 /* windowProgress == overview */); + } + doLogGesture(NEW_TASK, taskToLaunch); + }); + } + + /** + * Called when we successfully startNewTask() on the task that was previously running. Normally + * we call resumeLastTask() when returning to the previously running task, but this handles a + * specific edge case: if we switch from A to B, and back to A before B appears, we need to + * start A again to ensure it stays on top. + */ + @androidx.annotation.CallSuper + protected void onRestartPreviouslyAppearedTask() { + // Finish the controller here, since we won't get onTaskAppeared() for a task that already + // appeared. + if (mRecentsAnimationController != null) { + mRecentsAnimationController.finish(false, null); + } + reset(); + } + + private void reset() { + mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED); + } + + /** + * Cancels any running animation so that the active target can be overriden by a new swipe + * handle (in case of quick switch). + */ + private void cancelCurrentAnimation() { + mCanceled = true; + mCurrentShift.cancelAnimation(); + if (mLauncherTransitionController != null && mLauncherTransitionController + .getAnimationPlayer().isStarted()) { + mLauncherTransitionController.getAnimationPlayer().cancel(); + } + } + + private void invalidateHandler() { + mInputConsumerProxy.destroy(); + endRunningWindowAnim(false /* cancel */); + + if (mGestureEndCallback != null) { + mGestureEndCallback.run(); + } + + mActivityInitListener.unregister(); + ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener); + mTaskSnapshot = null; + } + + private void invalidateHandlerWithLauncher() { + endLauncherTransitionController(); + + mRecentsView.onGestureAnimationEnd(); + resetLauncherListenersAndOverlays(); + } + + private void endLauncherTransitionController() { + setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); + if (mLauncherTransitionController != null) { + mLauncherTransitionController.getAnimationPlayer().end(); + mLauncherTransitionController = null; + } + } + + private void resetLauncherListenersAndOverlays() { + // Reset the callback for deferred activity launches + if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mActivityInterface.setOnDeferredActivityLaunchCallback(null); + } + mActivity.getRootView().setOnApplyWindowInsetsListener(null); + removeLiveTileOverlay(); + } + + private void notifyTransitionCancelled() { + mAnimationFactory.onTransitionCancelled(); + } + + private void resetStateForAnimationCancel() { + boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted; + mActivityInterface.onTransitionCancelled(wasVisible); + + // Leave the pending invisible flag, as it may be used by wallpaper open animation. + mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER); + } + + protected void switchToScreenshot() { + final int runningTaskId = mGestureState.getRunningTaskId(); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + if (mRecentsAnimationController != null) { + mRecentsAnimationController.getController().setWillFinishToHome(true); + // Update the screenshot of the task + if (mTaskSnapshot == null) { + mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId); + } + mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, false /* refreshNow */); + } + mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); + } else if (!hasTargets()) { + // If there are no targets, then we don't need to capture anything + mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); + } else { + boolean finishTransitionPosted = false; + if (mRecentsAnimationController != null) { + // Update the screenshot of the task + if (mTaskSnapshot == null) { + mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId); + } + final TaskView taskView; + if (mGestureState.getEndTarget() == HOME) { + // Capture the screenshot before finishing the transition to home to ensure it's + // taken in the correct orientation, but no need to update the thumbnail. + taskView = null; + } else { + taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot); + } + if (taskView != null && !mCanceled) { + // Defer finishing the animation until the next launcher frame with the + // new thumbnail + finishTransitionPosted = ViewUtils.postDraw(taskView, + () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), + this::isCanceled); + } + } + if (!finishTransitionPosted) { + // If we haven't posted a draw callback, set the state immediately. + Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT, + TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS); + mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); + TraceHelper.INSTANCE.endSection(traceToken); + } + } + } + + private void finishCurrentTransitionToRecents() { + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); + } else if (!hasTargets() || mRecentsAnimationController == null) { + // If there are no targets or the animation not started, then there is nothing to finish + mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); + } else { + mRecentsAnimationController.finish(true /* toRecents */, + () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED)); + } + ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true); + } + + private void finishCurrentTransitionToHome() { + if (!hasTargets() || mRecentsAnimationController == null) { + // If there are no targets or the animation not started, then there is nothing to finish + mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); + } else { + finishRecentsControllerToHome( + () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED)); + } + ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true); + doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView()); + } + + protected abstract void finishRecentsControllerToHome(Runnable callback); + + private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { + endLauncherTransitionController(); + mActivityInterface.onSwipeUpToRecentsComplete(); + if (mRecentsAnimationController != null) { + mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */, + true /* screenshot */); + } + mRecentsView.onSwipeUpAnimationSuccess(); + + SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); + doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView()); + reset(); + } + + private void addLiveTileOverlay() { + if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) { + mRecentsView.setLiveTileOverlayAttached(true); + } + } + + private void removeLiveTileOverlay() { + LiveTileOverlay.INSTANCE.detach(mActivity.getRootView().getOverlay()); + mRecentsView.setLiveTileOverlayAttached(false); + } + + private static boolean isNotInRecents(RemoteAnimationTargetCompat app) { + return app.isNotInRecents + || app.activityType == ACTIVITY_TYPE_HOME; } /** @@ -117,8 +1452,6 @@ public abstract class AbsSwipeUpHandler, Q extends mGestureEndCallback = gestureEndCallback; } - public abstract Intent getLaunchIntent(); - protected void linkRecentsViewScroll() { SurfaceTransactionApplier.create(mRecentsView, applier -> { mTransformParams.setSyncTransactionApplier(applier); @@ -174,21 +1507,6 @@ public abstract class AbsSwipeUpHandler, Q extends } } - /** - * Called when we successfully startNewTask() on the task that was previously running. Normally - * we call resumeLastTask() when returning to the previously running task, but this handles a - * specific edge case: if we switch from A to B, and back to A before B appears, we need to - * start A again to ensure it stays on top. - */ - @CallSuper - protected void onRestartPreviouslyAppearedTask() { - // Finish the controller here, since we won't get onTaskAppeared() for a task that already - // appeared. - if (mRecentsAnimationController != null) { - mRecentsAnimationController.finish(false, null); - } - } - /** * Runs the given {@param action} if the recents animation has already started, or queues it to * be run when it is next started. @@ -209,55 +1527,6 @@ public abstract class AbsSwipeUpHandler, Q extends return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets(); } - @Override - public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController, - RecentsAnimationTargets targets) { - mRecentsAnimationController = recentsAnimationController; - mRecentsAnimationTargets = targets; - mTransformParams.setTargetSet(mRecentsAnimationTargets); - RemoteAnimationTargetCompat runningTaskTarget = targets.findTask( - mGestureState.getRunningTaskId()); - - if (runningTaskTarget != null) { - mTaskViewSimulator.setPreview(runningTaskTarget); - } - - // Only initialize the device profile, if it has not been initialized before, as in some - // configurations targets.homeContentInsets may not be correct. - if (mActivity == null) { - DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile(); - if (targets.minimizedHomeBounds != null && runningTaskTarget != null) { - Rect overviewStackBounds = mActivityInterface - .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget); - dp = dp.getMultiWindowProfile(mContext, - new WindowBounds(overviewStackBounds, targets.homeContentInsets)); - } else { - // If we are not in multi-window mode, home insets should be same as system insets. - dp = dp.copy(mContext); - } - dp.updateInsets(targets.homeContentInsets); - dp.updateIsSeascape(mContext); - initTransitionEndpoints(dp); - } - - // Notify when the animation starts - if (!mRecentsAnimationStartCallbacks.isEmpty()) { - for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) { - action.run(); - } - mRecentsAnimationStartCallbacks.clear(); - } - } - - @Override - public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { - mRecentsAnimationController = null; - mRecentsAnimationTargets = null; - if (mRecentsView != null) { - mRecentsView.setRecentsAnimationTargets(null, null); - } - } - @Override public void onRecentsAnimationFinished(RecentsAnimationController controller) { mRecentsAnimationController = null; @@ -279,9 +1548,6 @@ public abstract class AbsSwipeUpHandler, Q extends } } - /** @return Whether this was the task we were waiting to appear, and thus handled it. */ - protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget); - /** * @return The index of the TaskView in RecentsView whose taskId matches the task that will * resume if we finish the controller. @@ -300,55 +1566,6 @@ public abstract class AbsSwipeUpHandler, Q extends return mGestureState.getLastStartedTaskId() != -1; } - /** - * Return true if the window should be translated horizontally if the recents view scrolls - */ - protected abstract boolean moveWindowWithRecentsScroll(); - - protected boolean onActivityInit(Boolean alreadyOnHome) { - T createdActivity = mActivityInterface.getCreatedActivity(); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1"); - } - if (createdActivity != null) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2"); - } - initTransitionEndpoints(createdActivity.getDeviceProfile()); - } - return true; - } - - /** - * Called to create a input proxy for the running task - */ - @UiThread - protected abstract InputConsumer createNewInputProxyHandler(); - - /** - * Called when the value of {@link #mCurrentShift} changes - */ - @UiThread - public abstract void updateFinalShift(); - - /** - * Called when motion pause is detected - */ - public abstract void onMotionPauseChanged(boolean isPaused); - - @UiThread - public void onGestureStarted(boolean isLikelyToStartNewTask) { } - - @UiThread - public abstract void onGestureCancelled(); - - @UiThread - public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos); - - public abstract void onConsumerAboutToBeSwitched(); - - public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { } - /** * Registers a callback to run when the activity is ready. * @param intent The intent that will be used to start the activity if it doesn't exist already. @@ -376,20 +1593,9 @@ public abstract class AbsSwipeUpHandler, Q extends } } - @Override - protected RectFSpringAnim createWindowAnimationToHome(float startProgress, - HomeAnimationFactory homeAnimationFactory) { - RectFSpringAnim anim = - super.createWindowAnimationToHome(startProgress, homeAnimationFactory); - if (mRecentsAnimationTargets != null) { - mRecentsAnimationTargets.addReleaseCheck(anim); - } - return anim; - } - public interface Factory { - AbsSwipeUpHandler newHandler( + AbsSwipeUpHandler, RecentsView> newHandler( GestureState gestureState, long touchTimeMs, boolean continuingLastGesture); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java deleted file mode 100644 index 434fed29a1..0000000000 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java +++ /dev/null @@ -1,1330 +0,0 @@ -/* - * Copyright (C) 2018 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.BaseActivity.INVISIBLE_BY_STATE_HANDLER; -import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; -import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; -import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS; -import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT; -import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; -import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; -import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; -import static com.android.quickstep.GestureState.GestureEndTarget.HOME; -import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK; -import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK; -import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; -import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED; -import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; -import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; -import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; -import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; -import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; -import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; - -import android.animation.Animator; -import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; -import android.annotation.TargetApi; -import android.app.ActivityManager; -import android.content.Context; -import android.content.Intent; -import android.graphics.PointF; -import android.os.Build; -import android.os.SystemClock; -import android.view.View; -import android.view.View.OnApplyWindowInsetsListener; -import android.view.ViewTreeObserver.OnDrawListener; -import android.view.WindowInsets; -import android.view.animation.Interpolator; - -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; - -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.logging.StatsLogManager; -import com.android.launcher3.logging.StatsLogManager.StatsLogger; -import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.statemanager.StatefulActivity; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.util.TraceHelper; -import com.android.quickstep.BaseActivityInterface.AnimationFactory; -import com.android.quickstep.GestureState.GestureEndTarget; -import com.android.quickstep.inputconsumers.OverviewInputConsumer; -import com.android.quickstep.util.ActiveGestureLog; -import com.android.quickstep.util.RectFSpringAnim; -import com.android.quickstep.util.ShelfPeekAnim; -import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState; -import com.android.quickstep.views.LiveTileOverlay; -import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskView; -import com.android.systemui.shared.recents.model.ThumbnailData; -import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.shared.system.InputConsumerController; -import com.android.systemui.shared.system.LatencyTrackerCompat; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.TaskInfoCompat; -import com.android.systemui.shared.system.TaskStackChangeListener; - -/** - * Handles the navigation gestures when Launcher is the default home activity. - * TODO: Merge this with BaseSwipeUpHandler - */ -@TargetApi(Build.VERSION_CODES.O) -public abstract class BaseSwipeUpHandlerV2, Q extends RecentsView> - extends AbsSwipeUpHandler implements OnApplyWindowInsetsListener { - private static final String TAG = BaseSwipeUpHandlerV2.class.getSimpleName(); - - private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null; - - private static int getFlagForIndex(int index, String name) { - if (DEBUG_STATES) { - STATE_NAMES[index] = name; - } - return 1 << index; - } - - // Launcher UI related states - protected static final int STATE_LAUNCHER_PRESENT = - getFlagForIndex(0, "STATE_LAUNCHER_PRESENT"); - protected static final int STATE_LAUNCHER_STARTED = - getFlagForIndex(1, "STATE_LAUNCHER_STARTED"); - protected static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN"); - - // Internal initialization states - private static final int STATE_APP_CONTROLLER_RECEIVED = - getFlagForIndex(3, "STATE_APP_CONTROLLER_RECEIVED"); - - // Interaction finish states - private static final int STATE_SCALED_CONTROLLER_HOME = - getFlagForIndex(4, "STATE_SCALED_CONTROLLER_HOME"); - private static final int STATE_SCALED_CONTROLLER_RECENTS = - getFlagForIndex(5, "STATE_SCALED_CONTROLLER_RECENTS"); - - protected static final int STATE_HANDLER_INVALIDATED = - getFlagForIndex(6, "STATE_HANDLER_INVALIDATED"); - private static final int STATE_GESTURE_STARTED = - getFlagForIndex(7, "STATE_GESTURE_STARTED"); - private static final int STATE_GESTURE_CANCELLED = - getFlagForIndex(8, "STATE_GESTURE_CANCELLED"); - private static final int STATE_GESTURE_COMPLETED = - getFlagForIndex(9, "STATE_GESTURE_COMPLETED"); - - private static final int STATE_CAPTURE_SCREENSHOT = - getFlagForIndex(10, "STATE_CAPTURE_SCREENSHOT"); - protected static final int STATE_SCREENSHOT_CAPTURED = - getFlagForIndex(11, "STATE_SCREENSHOT_CAPTURED"); - private static final int STATE_SCREENSHOT_VIEW_SHOWN = - getFlagForIndex(12, "STATE_SCREENSHOT_VIEW_SHOWN"); - - private static final int STATE_RESUME_LAST_TASK = - getFlagForIndex(13, "STATE_RESUME_LAST_TASK"); - private static final int STATE_START_NEW_TASK = - getFlagForIndex(14, "STATE_START_NEW_TASK"); - private static final int STATE_CURRENT_TASK_FINISHED = - getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED"); - - private static final int LAUNCHER_UI_STATES = - STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED; - - public static final long MAX_SWIPE_DURATION = 350; - public static final long MIN_OVERSHOOT_DURATION = 120; - - public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; - private static final float SWIPE_DURATION_MULTIPLIER = - Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW)); - private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured"; - - public static final long RECENTS_ATTACH_DURATION = 300; - - /** - * Used as the page index for logging when we return to the last task at the end of the gesture. - */ - private static final int LOG_NO_OP_PAGE_INDEX = -1; - - protected final TaskAnimationManager mTaskAnimationManager; - - // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise - private RunningWindowAnim mRunningWindowAnim; - private boolean mIsShelfPeeking; - - private boolean mContinuingLastGesture; - - private ThumbnailData mTaskSnapshot; - - // Used to control launcher components throughout the swipe gesture. - private AnimatorPlaybackController mLauncherTransitionController; - private boolean mHasLauncherTransitionControllerStarted; - - private AnimationFactory mAnimationFactory = (t) -> { }; - - private boolean mWasLauncherAlreadyVisible; - - private boolean mPassedOverviewThreshold; - private boolean mGestureStarted; - private int mLogAction = Touch.SWIPE; - private int mLogDirection = Direction.UP; - private PointF mDownPos; - private boolean mIsLikelyToStartNewTask; - - private final long mTouchTimeMs; - private long mLauncherFrameDrawnTime; - - private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch; - - public BaseSwipeUpHandlerV2(Context context, RecentsAnimationDeviceState deviceState, - TaskAnimationManager taskAnimationManager, GestureState gestureState, - long touchTimeMs, boolean continuingLastGesture, - InputConsumerController inputConsumer) { - super(context, deviceState, gestureState, inputConsumer); - mTaskAnimationManager = taskAnimationManager; - mTouchTimeMs = touchTimeMs; - mContinuingLastGesture = continuingLastGesture; - - initAfterSubclassConstructor(); - initStateCallbacks(); - } - - private void initStateCallbacks() { - mStateCallback = new MultiStateCallback(STATE_NAMES); - - mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED, - this::onLauncherPresentAndGestureStarted); - - mStateCallback.runOnceAtState(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED, - this::initializeLauncherAnimationController); - - mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN, - this::launcherFrameDrawn); - - mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED - | STATE_GESTURE_CANCELLED, - this::resetStateForAnimationCancel); - - mStateCallback.runOnceAtState(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED, - this::resumeLastTask); - mStateCallback.runOnceAtState(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED, - this::startNewTask); - - mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED - | STATE_LAUNCHER_DRAWN | STATE_CAPTURE_SCREENSHOT, - this::switchToScreenshot); - - mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED - | STATE_SCALED_CONTROLLER_RECENTS, - this::finishCurrentTransitionToRecents); - - mStateCallback.runOnceAtState(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED - | STATE_SCALED_CONTROLLER_HOME, - this::finishCurrentTransitionToHome); - mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED, - this::reset); - - mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED - | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS - | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED - | STATE_GESTURE_STARTED, - this::setupLauncherUiAfterSwipeUpToRecentsAnimation); - - mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, - this::continueComputingRecentsScrollIfNecessary); - mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED - | STATE_RECENTS_SCROLLING_FINISHED, - this::onSettledOnEndTarget); - - mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler); - mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED, - this::invalidateHandlerWithLauncher); - mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK, - this::notifyTransitionCancelled); - - if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { - mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT - | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT, - (b) -> mRecentsView.setRunningTaskHidden(!b)); - } - } - - @Override - protected boolean onActivityInit(Boolean alreadyOnHome) { - super.onActivityInit(alreadyOnHome); - final T activity = mActivityInterface.getCreatedActivity(); - if (mActivity == activity) { - return true; - } - - if (mActivity != null) { - // The launcher may have been recreated as a result of device rotation. - int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES; - initStateCallbacks(); - mStateCallback.setState(oldState); - } - mWasLauncherAlreadyVisible = alreadyOnHome; - mActivity = activity; - // Override the visibility of the activity until the gesture actually starts and we swipe - // up, or until we transition home and the home animation is composed - if (alreadyOnHome) { - mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); - } else { - mActivity.addForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); - } - - mRecentsView = activity.getOverviewPanel(); - mRecentsView.setOnPageTransitionEndCallback(null); - addLiveTileOverlay(); - - mStateCallback.setState(STATE_LAUNCHER_PRESENT); - if (alreadyOnHome) { - onLauncherStart(); - } else { - activity.runOnceOnStart(this::onLauncherStart); - } - - setupRecentsViewUi(); - linkRecentsViewScroll(); - - return true; - } - - @Override - protected boolean moveWindowWithRecentsScroll() { - return mGestureState.getEndTarget() != HOME; - } - - private void onLauncherStart() { - final T activity = mActivityInterface.getCreatedActivity(); - if (mActivity != activity) { - return; - } - if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { - return; - } - mTaskViewSimulator.setRecentsRotation(mActivity.getDisplay().getRotation()); - - // If we've already ended the gesture and are going home, don't prepare recents UI, - // as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL. - if (mGestureState.getEndTarget() != HOME) { - Runnable initAnimFactory = () -> { - mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState, - mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated); - maybeUpdateRecentsAttachedState(false /* animate */); - }; - if (mWasLauncherAlreadyVisible) { - // Launcher is visible, but might be about to stop. Thus, if we prepare recents - // now, it might get overridden by moveToRestState() in onStop(). To avoid this, - // wait until the next gesture (and possibly launcher) starts. - mStateCallback.runOnceAtState(STATE_GESTURE_STARTED, initAnimFactory); - } else { - initAnimFactory.run(); - } - } - AbstractFloatingView.closeAllOpenViewsExcept(activity, mWasLauncherAlreadyVisible, - AbstractFloatingView.TYPE_LISTENER); - - if (mWasLauncherAlreadyVisible) { - mStateCallback.setState(STATE_LAUNCHER_DRAWN); - } else { - Object traceToken = TraceHelper.INSTANCE.beginSection("WTS-init"); - View dragLayer = activity.getDragLayer(); - dragLayer.getViewTreeObserver().addOnDrawListener(new OnDrawListener() { - boolean mHandled = false; - - @Override - public void onDraw() { - if (mHandled) { - return; - } - mHandled = true; - - TraceHelper.INSTANCE.endSection(traceToken); - dragLayer.post(() -> - dragLayer.getViewTreeObserver().removeOnDrawListener(this)); - if (activity != mActivity) { - return; - } - - mStateCallback.setState(STATE_LAUNCHER_DRAWN); - } - }); - } - - activity.getRootView().setOnApplyWindowInsetsListener(this); - mStateCallback.setState(STATE_LAUNCHER_STARTED); - } - - private void onLauncherPresentAndGestureStarted() { - // Re-setup the recents UI when gesture starts, as the state could have been changed during - // 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); - - mGestureState.runOnceAtState(STATE_END_TARGET_SET, - () -> mDeviceState.getRotationTouchHelper(). - onEndTargetCalculated(mGestureState.getEndTarget(), - mActivityInterface)); - - notifyGestureStartedAsync(); - } - - private void onDeferredActivityLaunch() { - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - mActivityInterface.switchRunningTaskViewToScreenshot( - null, () -> { - mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */); - }); - } else { - mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */); - } - } - - private void setupRecentsViewUi() { - if (mContinuingLastGesture) { - updateSysUiFlags(mCurrentShift.value); - return; - } - notifyGestureAnimationStartToRecents(); - } - - protected void notifyGestureAnimationStartToRecents() { - mRecentsView.onGestureAnimationStart(mGestureState.getRunningTask()); - } - - private void launcherFrameDrawn() { - mLauncherFrameDrawnTime = SystemClock.uptimeMillis(); - } - - private void initializeLauncherAnimationController() { - buildAnimationController(); - - Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents", - TraceHelper.FLAG_IGNORE_BINDERS); - // Only used in debug builds - if (LatencyTrackerCompat.isEnabled(mContext)) { - LatencyTrackerCompat.logToggleRecents( - (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); - } - TraceHelper.INSTANCE.endSection(traceToken); - - // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the - // high-res thumbnail loader here once we are sure that we will end up in an overview state - RecentsModel.INSTANCE.get(mContext).getThumbnailCache() - .getHighResLoadingState().setVisible(true); - } - - @Override - public void onMotionPauseChanged(boolean isPaused) { - setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION); - } - - public void maybeUpdateRecentsAttachedState() { - maybeUpdateRecentsAttachedState(true /* animate */); - } - - /** - * Determines whether to show or hide RecentsView. The window is always - * synchronized with its corresponding TaskView in RecentsView, so if - * RecentsView is shown, it will appear to be attached to the window. - * - * Note this method has no effect unless the navigation mode is NO_BUTTON. - */ - private void maybeUpdateRecentsAttachedState(boolean animate) { - if (!mDeviceState.isFullyGesturalNavMode() || mRecentsView == null) { - return; - } - RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null - ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) - : null; - final boolean recentsAttachedToAppWindow; - if (mGestureState.getEndTarget() != null) { - recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow; - } else if (mContinuingLastGesture - && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) { - recentsAttachedToAppWindow = true; - } else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) { - // The window is going away so make sure recents is always visible in this case. - recentsAttachedToAppWindow = true; - } else { - recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask; - } - mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate); - } - - @Override - public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { - setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */); - } - - private void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask, boolean animate) { - if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) { - mIsLikelyToStartNewTask = isLikelyToStartNewTask; - maybeUpdateRecentsAttachedState(animate); - } - } - - @UiThread - public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) { - mAnimationFactory.setShelfState(shelfState, interpolator, duration); - boolean wasShelfPeeking = mIsShelfPeeking; - mIsShelfPeeking = shelfState == PEEK; - if (mIsShelfPeeking != wasShelfPeeking) { - maybeUpdateRecentsAttachedState(); - } - if (shelfState.shouldPreformHaptic) { - performHapticFeedback(); - } - } - - private void buildAnimationController() { - if (!canCreateNewOrUpdateExistingLauncherTransitionController()) { - return; - } - initTransitionEndpoints(mActivity.getDeviceProfile()); - mAnimationFactory.createActivityInterface(mTransitionDragLength); - } - - /** - * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME - * (it has its own animation) or if we're already animating the current controller. - * @return Whether we can create the launcher controller or update its progress. - */ - private boolean canCreateNewOrUpdateExistingLauncherTransitionController() { - return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted; - } - - @Override - public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { - WindowInsets result = view.onApplyWindowInsets(windowInsets); - buildAnimationController(); - return result; - } - - private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { - mLauncherTransitionController = anim; - mLauncherTransitionController.dispatchSetInterpolator(t -> t * mDragLengthFactor); - mLauncherTransitionController.dispatchOnStart(); - updateLauncherTransitionProgress(); - } - - @Override - public Intent getLaunchIntent() { - return mGestureState.getOverviewIntent(); - } - - @Override - public void updateFinalShift() { - final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; - if (passed != mPassedOverviewThreshold) { - mPassedOverviewThreshold = passed; - if (!mDeviceState.isFullyGesturalNavMode()) { - performHapticFeedback(); - } - } - - updateSysUiFlags(mCurrentShift.value); - applyWindowTransform(); - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - if (mRecentsAnimationTargets != null) { - LiveTileOverlay.INSTANCE.update( - mTaskViewSimulator.getCurrentRect(), - mTaskViewSimulator.getCurrentCornerRadius()); - } - } - - updateLauncherTransitionProgress(); - } - - private void updateLauncherTransitionProgress() { - if (mLauncherTransitionController == null - || !canCreateNewOrUpdateExistingLauncherTransitionController()) { - return; - } - // Normalize the progress to 0 to 1, as the animation controller will clamp it to that - // anyway. The controller mimics the drag length factor by applying it to its interpolators. - float progress = mCurrentShift.value / mDragLengthFactor; - mLauncherTransitionController.setPlayFraction(progress); - } - - /** - * @param windowProgress 0 == app, 1 == overview - */ - private void updateSysUiFlags(float windowProgress) { - if (mRecentsAnimationController != null && mRecentsView != null) { - TaskView runningTask = mRecentsView.getRunningTaskView(); - TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen(); - int centermostTaskFlags = centermostTask == null ? 0 - : centermostTask.getThumbnail().getSysUiStatusNavFlags(); - boolean swipeUpThresholdPassed = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD; - boolean quickswitchThresholdPassed = centermostTask != runningTask; - - // We will handle the sysui flags based on the centermost task view. - mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed - || (quickswitchThresholdPassed && centermostTaskFlags != 0)); - mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed); - - int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags; - mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags); - } - } - - @Override - public void onRecentsAnimationStart(RecentsAnimationController controller, - RecentsAnimationTargets targets) { - ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length); - super.onRecentsAnimationStart(controller, targets); - - // Only add the callback to enable the input consumer after we actually have the controller - mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED, - mRecentsAnimationController::enableInputConsumer); - mStateCallback.setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED); - - mPassedOverviewThreshold = false; - } - - @Override - public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { - ActiveGestureLog.INSTANCE.addLog("cancelRecentsAnimation"); - mActivityInitListener.unregister(); - mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED); - - // Defer clearing the controller and the targets until after we've updated the state - super.onRecentsAnimationCanceled(thumbnailData); - } - - @Override - public void onGestureStarted(boolean isLikelyToStartNewTask) { - notifyGestureStartedAsync(); - setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */); - mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED); - mGestureStarted = true; - } - - /** - * Notifies the launcher that the swipe gesture has started. This can be called multiple times. - */ - @UiThread - private void notifyGestureStartedAsync() { - final T curActivity = mActivity; - if (curActivity != null) { - // Once the gesture starts, we can no longer transition home through the button, so - // reset the force override of the activity visibility - mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS); - } - } - - /** - * Called as a result on ACTION_CANCEL to return the UI to the start state. - */ - @Override - public void onGestureCancelled() { - updateDisplacement(0); - mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED); - mLogAction = Touch.SWIPE_NOOP; - handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */); - } - - /** - * @param endVelocity The velocity in the direction of the nav bar to the middle of the screen. - * @param velocity The x and y components of the velocity when the gesture ends. - * @param downPos The x and y value of where the gesture started. - */ - @Override - public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) { - float flingThreshold = mContext.getResources() - .getDimension(R.dimen.quickstep_fling_threshold_velocity); - boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold; - mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED); - - mLogAction = isFling ? Touch.FLING : Touch.SWIPE; - boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x); - if (isVelocityVertical) { - mLogDirection = velocity.y < 0 ? Direction.UP : Direction.DOWN; - } else { - mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT; - } - mDownPos = downPos; - handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */); - } - - @Override - protected InputConsumer createNewInputProxyHandler() { - endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */); - endLauncherTransitionController(); - - StatefulActivity activity = mActivityInterface.getCreatedActivity(); - return activity == null ? InputConsumer.NO_OP - : new OverviewInputConsumer(mGestureState, activity, null, true); - } - - private void endRunningWindowAnim(boolean cancel) { - if (mRunningWindowAnim != null) { - if (cancel) { - mRunningWindowAnim.cancel(); - } else { - mRunningWindowAnim.end(); - } - } - } - - private void onSettledOnEndTarget() { - switch (mGestureState.getEndTarget()) { - case HOME: - mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); - // Notify swipe-to-home (recents animation) is finished - SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished(); - break; - case RECENTS: - mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT - | STATE_SCREENSHOT_VIEW_SHOWN); - break; - case NEW_TASK: - mStateCallback.setState(STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT); - break; - case LAST_TASK: - mStateCallback.setState(STATE_RESUME_LAST_TASK); - break; - } - ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget()); - } - - @Override - protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) { - if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) { - return false; - } - if (mStateCallback.hasStates(STATE_START_NEW_TASK) - && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) { - reset(); - return true; - } - return false; - } - - private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling, - boolean isCancel) { - final GestureEndTarget endTarget; - final boolean goingToNewTask; - if (mRecentsView != null) { - if (!hasTargets()) { - // If there are no running tasks, then we can assume that this is a continuation of - // the last gesture, but after the recents animation has finished - goingToNewTask = true; - } else { - final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); - final int taskToLaunch = mRecentsView.getNextPage(); - goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex; - } - } else { - goingToNewTask = false; - } - final boolean reachedOverviewThreshold = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW; - if (!isFling) { - if (isCancel) { - endTarget = LAST_TASK; - } else if (mDeviceState.isFullyGesturalNavMode()) { - if (mIsShelfPeeking) { - endTarget = RECENTS; - } else if (goingToNewTask) { - endTarget = NEW_TASK; - } else { - endTarget = !reachedOverviewThreshold ? LAST_TASK : HOME; - } - } else { - endTarget = reachedOverviewThreshold && mGestureStarted - ? RECENTS - : goingToNewTask - ? NEW_TASK - : LAST_TASK; - } - } else { - // If swiping at a diagonal, base end target on the faster velocity. - boolean isSwipeUp = endVelocity < 0; - boolean willGoToNewTaskOnSwipeUp = - goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity); - - if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) { - endTarget = HOME; - } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) { - // If swiping at a diagonal, base end target on the faster velocity. - endTarget = NEW_TASK; - } else if (isSwipeUp) { - endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp - ? NEW_TASK : RECENTS; - } else { - endTarget = goingToNewTask ? NEW_TASK : LAST_TASK; - } - } - - if (mDeviceState.isOverviewDisabled() && (endTarget == RECENTS || endTarget == LAST_TASK)) { - return LAST_TASK; - } - return endTarget; - } - - @UiThread - private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity, - boolean isCancel) { - PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000); - long duration = MAX_SWIPE_DURATION; - float currentShift = mCurrentShift.value; - final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity, - isFling, isCancel); - float endShift = endTarget.isLauncher ? 1 : 0; - final float startShift; - Interpolator interpolator = DEACCEL; - if (!isFling) { - long expectedDuration = Math.abs(Math.round((endShift - currentShift) - * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); - duration = Math.min(MAX_SWIPE_DURATION, expectedDuration); - startShift = currentShift; - interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; - } else { - startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y - * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); - float minFlingVelocity = mContext.getResources() - .getDimension(R.dimen.quickstep_fling_min_velocity); - if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) { - if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) { - Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams( - startShift, endShift, endShift, endVelocity / 1000, - mTransitionDragLength, mContext); - endShift = overshoot.end; - interpolator = overshoot.interpolator; - duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION, - MAX_SWIPE_DURATION); - } else { - float distanceToTravel = (endShift - currentShift) * mTransitionDragLength; - - // we want the page's snap velocity to approximately match the velocity at - // which the user flings, so we scale the duration by a value near to the - // derivative of the scroll interpolator at zero, ie. 2. - long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); - duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); - - if (endTarget == RECENTS) { - interpolator = OVERSHOOT_1_2; - } - } - } - } - - if (endTarget.isLauncher) { - mInputConsumerProxy.enable(); - } - if (endTarget == HOME) { - setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); - duration = Math.max(MIN_OVERSHOOT_DURATION, duration); - } else if (endTarget == RECENTS) { - LiveTileOverlay.INSTANCE.startIconAnimation(); - if (mRecentsView != null) { - int nearestPage = mRecentsView.getPageNearestToCenterOfScreen(); - if (mRecentsView.getNextPage() != nearestPage) { - // We shouldn't really scroll to the next page when swiping up to recents. - // Only allow settling on the next page if it's nearest to the center. - mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration)); - } - if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) { - mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION); - } - duration = Math.max(duration, mRecentsView.getScroller().getDuration()); - } - if (mDeviceState.isFullyGesturalNavMode()) { - setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration); - } - } - - // Let RecentsView handle the scrolling to the task, which we launch in startNewTask() - // or resumeLastTask(). - if (mRecentsView != null) { - mRecentsView.setOnPageTransitionEndCallback( - () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED)); - } else { - mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED); - } - - animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs); - } - - private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) { - StatsLogManager.EventEnum event; - switch (endTarget) { - case HOME: - event = LAUNCHER_HOME_GESTURE; - break; - case RECENTS: - event = LAUNCHER_OVERVIEW_GESTURE; - break; - case LAST_TASK: - case NEW_TASK: - event = (mLogDirection == Direction.LEFT) - ? LAUNCHER_QUICKSWITCH_LEFT - : LAUNCHER_QUICKSWITCH_RIGHT; - break; - default: - event = IGNORE; - } - StatsLogger logger = StatsLogManager.newInstance(mContext).logger() - .withSrcState(LAUNCHER_STATE_BACKGROUND) - .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType)); - if (targetTask != null) { - logger.withItemInfo(targetTask.getItemInfo()); - } - logger.log(event); - - - DeviceProfile dp = mDp; - if (dp == null || mDownPos == null) { - // We probably never received an animation controller, skip logging. - return; - } - int pageIndex = endTarget == LAST_TASK - ? LOG_NO_OP_PAGE_INDEX - : mRecentsView.getNextPage(); - UserEventDispatcher.newInstance(mContext).logStateChangeAction( - mLogAction, mLogDirection, - (int) mDownPos.x, (int) mDownPos.y, - ContainerType.NAVBAR, ContainerType.APP, - endTarget.containerType, - pageIndex); - } - - /** Animates to the given progress, where 0 is the current app and 1 is overview. */ - @UiThread - private void animateToProgress(float start, float end, long duration, Interpolator interpolator, - GestureEndTarget target, PointF velocityPxPerMs) { - runOnRecentsAnimationStart(() -> animateToProgressInternal(start, end, duration, - interpolator, target, velocityPxPerMs)); - } - - protected abstract HomeAnimationFactory createHomeAnimationFactory(long duration); - - private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() { - @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { - if (task.taskId == mGestureState.getRunningTaskId() - && TaskInfoCompat.getActivityType(task) != ACTIVITY_TYPE_HOME) { - // Since this is an edge case, just cancel and relaunch with default activity - // options (since we don't know if there's an associated app icon to launch from) - endRunningWindowAnim(true /* cancel */); - ActivityManagerWrapper.getInstance().unregisterTaskStackListener( - mActivityRestartListener); - ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null); - } - } - }; - - @UiThread - private void animateToProgressInternal(float start, float end, long duration, - Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) { - // Set the state, but don't notify until the animation completes - mGestureState.setEndTarget(target, false /* isAtomic */); - maybeUpdateRecentsAttachedState(); - - // If we are transitioning to launcher, then listen for the activity to be restarted while - // the transition is in progress - if (mGestureState.getEndTarget().isLauncher) { - ActivityManagerWrapper.getInstance().registerTaskStackListener( - mActivityRestartListener); - } - - if (mGestureState.getEndTarget() == HOME) { - HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration); - RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory); - windowAnim.addAnimatorListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - if (mRecentsAnimationController == null) { - // If the recents animation is interrupted, we still end the running - // animation (not canceled) so this is still called. In that case, we can - // skip doing any future work here for the current gesture. - return; - } - // Finalize the state and notify of the change - mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); - } - }); - getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); - windowAnim.start(mContext, velocityPxPerMs); - homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); - mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); - mLauncherTransitionController = null; - } else { - ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end); - windowAnim.setDuration(duration).setInterpolator(interpolator); - windowAnim.addUpdateListener(valueAnimator -> { - computeRecentsScrollIfInvisible(); - }); - windowAnim.addListener(new AnimationSuccessListener() { - @Override - public void onAnimationSuccess(Animator animator) { - if (mRecentsAnimationController == null) { - // If the recents animation is interrupted, we still end the running - // animation (not canceled) so this is still called. In that case, we can - // skip doing any future work here for the current gesture. - return; - } - if (mRecentsView != null) { - int taskToLaunch = mRecentsView.getNextPage(); - int runningTask = getLastAppearedTaskIndex(); - boolean hasStartedNewTask = hasStartedNewTask(); - if (target == NEW_TASK && taskToLaunch == runningTask - && !hasStartedNewTask) { - // We are about to launch the current running task, so use LAST_TASK - // state instead of NEW_TASK. This could happen, for example, if our - // scroll is aborted after we determined the target to be NEW_TASK. - mGestureState.setEndTarget(LAST_TASK); - } else if (target == LAST_TASK && hasStartedNewTask) { - // We are about to re-launch the previously running task, but we can't - // just finish the controller like we normally would because that would - // instead resume the last task that appeared, and not ensure that this - // task is restored to the top. To address this, re-launch the task as - // if it were a new task. - mGestureState.setEndTarget(NEW_TASK); - } - } - mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); - } - }); - windowAnim.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); - } - // Always play the entire launcher animation when going home, since it is separate from - // the animation that has been controlled thus far. - if (mGestureState.getEndTarget() == HOME) { - start = 0; - } - - // We want to use the same interpolator as the window, but need to adjust it to - // interpolate over the remaining progress (end - start). - TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress( - interpolator, start, end); - if (mLauncherTransitionController == null) { - return; - } - if (start == end || duration <= 0) { - mLauncherTransitionController.dispatchSetInterpolator(t -> end); - } else { - mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator); - } - mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration)); - - if (UNSTABLE_SPRINGS.get()) { - mLauncherTransitionController.dispatchOnStart(); - } - mLauncherTransitionController.getAnimationPlayer().start(); - mHasLauncherTransitionControllerStarted = true; - } - - private void computeRecentsScrollIfInvisible() { - if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) { - // Views typically don't compute scroll when invisible as an optimization, - // but in our case we need to since the window offset depends on the scroll. - mRecentsView.computeScroll(); - } - } - - private void continueComputingRecentsScrollIfNecessary() { - if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED) - && !mStateCallback.hasStates(STATE_HANDLER_INVALIDATED) - && !mCanceled) { - computeRecentsScrollIfInvisible(); - mRecentsView.postOnAnimation(this::continueComputingRecentsScrollIfNecessary); - } - } - - /** - * Creates an animation that transforms the current app window into the home app. - * @param startProgress The progress of {@link #mCurrentShift} to start the window from. - * @param homeAnimationFactory The home animation factory. - */ - @Override - protected RectFSpringAnim createWindowAnimationToHome(float startProgress, - HomeAnimationFactory homeAnimationFactory) { - RectFSpringAnim anim = - super.createWindowAnimationToHome(startProgress, homeAnimationFactory); - anim.addOnUpdateListener((r, p) -> { - updateSysUiFlags(Math.max(p, mCurrentShift.value)); - }); - anim.addAnimatorListener(new AnimationSuccessListener() { - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - if (mActivity != null) { - removeLiveTileOverlay(); - } - } - - @Override - public void onAnimationSuccess(Animator animator) { - if (mRecentsView != null) { - mRecentsView.post(mRecentsView::resetTaskVisuals); - } - // Make sure recents is in its final state - maybeUpdateRecentsAttachedState(false); - mActivityInterface.onSwipeUpToHomeComplete(mDeviceState); - } - }); - return anim; - } - - @Override - public void onConsumerAboutToBeSwitched() { - if (mActivity != null) { - // In the off chance that the gesture ends before Launcher is started, we should clear - // the callback here so that it doesn't update with the wrong state - mActivity.clearRunOnceOnStartCallback(); - resetLauncherListenersAndOverlays(); - } - if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) { - cancelCurrentAnimation(); - } else { - reset(); - } - } - - public boolean isCanceled() { - return mCanceled; - } - - @UiThread - private void resumeLastTask() { - mRecentsAnimationController.finish(false /* toRecents */, null); - ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false); - doLogGesture(LAST_TASK, null); - reset(); - } - - @UiThread - private void startNewTask() { - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal); - } else { - startNewTaskInternal(); - } - } - - @UiThread - private void startNewTaskInternal() { - TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView(); - startNewTask(success -> { - if (!success) { - reset(); - // We couldn't launch the task, so take user to overview so they can - // decide what to do instead of staying in this broken state. - endLauncherTransitionController(); - updateSysUiFlags(1 /* windowProgress == overview */); - } - doLogGesture(NEW_TASK, taskToLaunch); - }); - } - - @Override - protected void onRestartPreviouslyAppearedTask() { - super.onRestartPreviouslyAppearedTask(); - reset(); - } - - private void reset() { - mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED); - } - - /** - * Cancels any running animation so that the active target can be overriden by a new swipe - * handle (in case of quick switch). - */ - private void cancelCurrentAnimation() { - mCanceled = true; - mCurrentShift.cancelAnimation(); - if (mLauncherTransitionController != null && mLauncherTransitionController - .getAnimationPlayer().isStarted()) { - mLauncherTransitionController.getAnimationPlayer().cancel(); - } - } - - private void invalidateHandler() { - mInputConsumerProxy.destroy(); - endRunningWindowAnim(false /* cancel */); - - if (mGestureEndCallback != null) { - mGestureEndCallback.run(); - } - - mActivityInitListener.unregister(); - ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener); - mTaskSnapshot = null; - } - - private void invalidateHandlerWithLauncher() { - endLauncherTransitionController(); - - mRecentsView.onGestureAnimationEnd(); - resetLauncherListenersAndOverlays(); - } - - private void endLauncherTransitionController() { - setShelfState(ShelfAnimState.CANCEL, LINEAR, 0); - if (mLauncherTransitionController != null) { - mLauncherTransitionController.getAnimationPlayer().end(); - mLauncherTransitionController = null; - } - } - - private void resetLauncherListenersAndOverlays() { - // Reset the callback for deferred activity launches - if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { - mActivityInterface.setOnDeferredActivityLaunchCallback(null); - } - mActivity.getRootView().setOnApplyWindowInsetsListener(null); - removeLiveTileOverlay(); - } - - private void notifyTransitionCancelled() { - mAnimationFactory.onTransitionCancelled(); - } - - private void resetStateForAnimationCancel() { - boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted; - mActivityInterface.onTransitionCancelled(wasVisible); - - // Leave the pending invisible flag, as it may be used by wallpaper open animation. - mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER); - } - - protected void switchToScreenshot() { - final int runningTaskId = mGestureState.getRunningTaskId(); - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - if (mRecentsAnimationController != null) { - mRecentsAnimationController.getController().setWillFinishToHome(true); - // Update the screenshot of the task - if (mTaskSnapshot == null) { - mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId); - } - mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot, false /* refreshNow */); - } - mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); - } else if (!hasTargets()) { - // If there are no targets, then we don't need to capture anything - mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); - } else { - boolean finishTransitionPosted = false; - if (mRecentsAnimationController != null) { - // Update the screenshot of the task - if (mTaskSnapshot == null) { - mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId); - } - final TaskView taskView; - if (mGestureState.getEndTarget() == HOME) { - // Capture the screenshot before finishing the transition to home to ensure it's - // taken in the correct orientation, but no need to update the thumbnail. - taskView = null; - } else { - taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot); - } - if (taskView != null && !mCanceled) { - // Defer finishing the animation until the next launcher frame with the - // new thumbnail - finishTransitionPosted = ViewUtils.postDraw(taskView, - () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), - this::isCanceled); - } - } - if (!finishTransitionPosted) { - // If we haven't posted a draw callback, set the state immediately. - Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT, - TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS); - mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); - TraceHelper.INSTANCE.endSection(traceToken); - } - } - } - - private void finishCurrentTransitionToRecents() { - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); - } else if (!hasTargets() || mRecentsAnimationController == null) { - // If there are no targets or the animation not started, then there is nothing to finish - mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); - } else { - mRecentsAnimationController.finish(true /* toRecents */, - () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED)); - } - ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true); - } - - private void finishCurrentTransitionToHome() { - if (!hasTargets() || mRecentsAnimationController == null) { - // If there are no targets or the animation not started, then there is nothing to finish - mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); - } else { - finishRecentsControllerToHome( - () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED)); - } - ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true); - doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView()); - } - - protected abstract void finishRecentsControllerToHome(Runnable callback); - - private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { - endLauncherTransitionController(); - mActivityInterface.onSwipeUpToRecentsComplete(); - if (mRecentsAnimationController != null) { - mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */, - true /* screenshot */); - } - mRecentsView.onSwipeUpAnimationSuccess(); - - SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); - doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView()); - reset(); - } - - private void addLiveTileOverlay() { - if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) { - mRecentsView.setLiveTileOverlayAttached(true); - } - } - - private void removeLiveTileOverlay() { - LiveTileOverlay.INSTANCE.detach(mActivity.getRootView().getOverlay()); - mRecentsView.setLiveTileOverlayAttached(false); - } - - private static boolean isNotInRecents(RemoteAnimationTargetCompat app) { - return app.isNotInRecents - || app.activityType == ACTIVITY_TYPE_HOME; - } -} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java index f60a50bb43..ffb05dfed7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java @@ -71,7 +71,7 @@ import java.util.function.Consumer; */ @TargetApi(Build.VERSION_CODES.R) public class FallbackSwipeHandler extends - BaseSwipeUpHandlerV2 { + AbsSwipeUpHandler { /** * Message used for receiving gesture nav contract information. We use a static messenger to diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java index 052d0a68a6..44114558e5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -39,7 +39,7 @@ import com.android.systemui.shared.system.InputConsumerController; * Temporary class to allow easier refactoring */ public class LauncherSwipeHandlerV2 extends - BaseSwipeUpHandlerV2 { + AbsSwipeUpHandler { public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, 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 a676390ce3..db1948b9f3 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 @@ -21,7 +21,7 @@ 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.BaseSwipeUpHandlerV2.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index f4a394a4f6..0ae386f39a 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -20,7 +20,7 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; -import static com.android.quickstep.BaseSwipeUpHandlerV2.RECENTS_ATTACH_DURATION; +import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION; import static com.android.quickstep.SysUINavigationMode.getMode; import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java index 4110b33d24..044e010b3c 100644 --- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -18,7 +18,7 @@ package com.android.quickstep.interaction; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; -import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; +import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;