diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index d241260082..9fcadeaf3d 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -24,33 +24,26 @@ import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; -import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; -import static com.android.quickstep.views.FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import static com.android.quickstep.views.RecentsView.TASK_THUMBNAIL_SPLASH_ALPHA; -import android.graphics.Rect; -import android.graphics.RectF; import android.util.FloatProperty; import android.view.animation.Interpolator; import androidx.annotation.NonNull; import com.android.launcher3.LauncherState; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; -import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.RecentsView; /** @@ -115,49 +108,18 @@ public abstract class BaseRecentsViewStateController config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR)); boolean exitingOverview = !FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() - || !toState.overviewUi; + && !toState.overviewUi; if (mRecentsView.isSplitSelectionActive() && exitingOverview) { - // TODO (b/238651489): Refactor state management to avoid need for double check - FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView(); - if (floatingTask != null) { - // We are in split selection state currently, transitioning to another state - DragLayer dragLayer = mLauncher.getDragLayer(); - RectF onScreenRectF = new RectF(); - Utilities.getBoundsForViewInDragLayer(mLauncher.getDragLayer(), floatingTask, - new Rect(0, 0, floatingTask.getWidth(), floatingTask.getHeight()), - false, null, onScreenRectF); - // Get the part of the floatingTask that intersects with the DragLayer (i.e. the - // on-screen portion) - onScreenRectF.intersect( - dragLayer.getLeft(), - dragLayer.getTop(), - dragLayer.getRight(), - dragLayer.getBottom() - ); - - setter.setFloat( - mRecentsView.getFirstFloatingTaskView(), - PRIMARY_TRANSLATE_OFFSCREEN, - mRecentsView.getPagedOrientationHandler() - .getFloatingTaskOffscreenTranslationTarget( - floatingTask, - onScreenRectF, - floatingTask.getStagePosition(), - mLauncher.getDeviceProfile() - ), - config.getInterpolator( - ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, - LINEAR - )); - setter.setViewAlpha( - mRecentsView.getSplitInstructionsView(), - 0, - config.getInterpolator( - ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, - LINEAR - ) - ); - } + setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController() + .animateAwayPlaceholder(mLauncher)); + setter.setViewAlpha( + mRecentsView.getSplitInstructionsView(), + 0, + config.getInterpolator( + ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, + LINEAR + ) + ); } setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0, diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 1ba11fd01d..3b53e8a7b6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -193,6 +193,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -406,7 +407,8 @@ public class QuickstepLauncher extends Launcher { private List> getSplitShortcuts() { - if (!ENABLE_SPLIT_FROM_WORKSPACE.get() || !mDeviceProfile.isTablet) { + if (!ENABLE_SPLIT_FROM_WORKSPACE.get() || !mDeviceProfile.isTablet || + mSplitSelectStateController.isSplitSelectActive()) { return Collections.emptyList(); } RecentsView recentsView = getOverviewPanel(); @@ -545,11 +547,22 @@ public class QuickstepLauncher extends Launcher { ArrayList list = new ArrayList<>(); list.add(getDragController()); + Consumer splitAnimator = animatorSet -> { + AnimatorSet anim = mSplitSelectStateController.getSplitAnimationController() + .animateAwayPlaceholder(QuickstepLauncher.this); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSplitSelectStateController.resetState(); + } + }); + animatorSet.play(anim); + }; switch (mode) { case NO_BUTTON: list.add(new NoButtonQuickSwitchTouchController(this)); - list.add(new NavBarToHomeTouchController(this)); - list.add(new NoButtonNavbarToOverviewTouchController(this)); + list.add(new NavBarToHomeTouchController(this, splitAnimator)); + list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator)); break; case TWO_BUTTONS: list.add(new TwoButtonNavbarTouchController(this)); @@ -560,8 +573,8 @@ public class QuickstepLauncher extends Launcher { break; case THREE_BUTTONS: list.add(new NoButtonQuickSwitchTouchController(this)); - list.add(new NavBarToHomeTouchController(this)); - list.add(new NoButtonNavbarToOverviewTouchController(this)); + list.add(new NavBarToHomeTouchController(this, splitAnimator)); + list.add(new NoButtonNavbarToOverviewTouchController(this, splitAnimator)); list.add(new PortraitStatesTouchController(this)); break; default: diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index be532206e9..b266bcd246 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -30,6 +30,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.view.MotionEvent; import android.view.animation.Interpolator; @@ -43,6 +44,7 @@ import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.TouchController; @@ -51,6 +53,8 @@ import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; +import java.util.function.Consumer; + /** * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps. */ @@ -62,6 +66,7 @@ public class NavBarToHomeTouchController implements TouchController, private static final float OVERVIEW_TO_HOME_SCRIM_MULTIPLIER = 0.5f; private final Launcher mLauncher; + private final Consumer mCancelSplitRunnable; private final SingleAxisSwipeDetector mSwipeDetector; private final float mPullbackDistance; @@ -70,8 +75,14 @@ public class NavBarToHomeTouchController implements TouchController, private LauncherState mEndState = NORMAL; private AnimatorPlaybackController mCurrentAnimation; - public NavBarToHomeTouchController(Launcher launcher) { + /** + * @param cancelSplitRunnable Called when split placeholder view needs to be cancelled. + * Animation should be added to the provided AnimatorSet + */ + public NavBarToHomeTouchController(Launcher launcher, + Consumer cancelSplitRunnable) { mLauncher = launcher; + mCancelSplitRunnable = cancelSplitRunnable; mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this, SingleAxisSwipeDetector.VERTICAL); mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance); @@ -183,7 +194,10 @@ public class NavBarToHomeTouchController implements TouchController, recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation(true /* toRecents */, null)); if (mStartState.overviewUi) { - new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState)) + new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState), + FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get() + ? mCancelSplitRunnable + : null) .animateWithVelocity(velocity); } else { mLauncher.getStateManager().goToState(mEndState, true, diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 2f5467ea75..40753882d4 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -31,6 +31,7 @@ import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.graphics.PointF; @@ -53,6 +54,8 @@ import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.OverviewToHomeAnim; import com.android.quickstep.views.RecentsView; +import java.util.function.Consumer; + /** * Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above * the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the @@ -67,6 +70,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f; private final VibratorWrapper mVibratorWrapper; + private final Consumer mCancelSplitRunnable; private final RecentsView mRecentsView; private final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; @@ -82,12 +86,18 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch // Normal to Hint animation has flag SKIP_OVERVIEW, so we update this scrim with this animator. private ObjectAnimator mNormalToHintOverviewScrimAnimator; - public NoButtonNavbarToOverviewTouchController(Launcher l) { + /** + * @param cancelSplitRunnable Called when split placeholder view needs to be cancelled. + * Animation should be added to the provided AnimatorSet + */ + public NoButtonNavbarToOverviewTouchController(Launcher l, + Consumer cancelSplitRunnable) { super(l); mRecentsView = l.getOverviewPanel(); mMotionPauseDetector = new MotionPauseDetector(l); mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop(); mVibratorWrapper = VibratorWrapper.INSTANCE.get(l.getApplicationContext()); + mCancelSplitRunnable = cancelSplitRunnable; } @Override @@ -197,6 +207,9 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch // state, but since the hint state tracks the entire screen without a clear endpoint, we // need to manually set the duration to a reasonable value. animator.setDuration(HINT_STATE.getTransitionDuration(mLauncher, true /* isToState */)); + AnimatorSet animatorSet = new AnimatorSet(); + mCancelSplitRunnable.accept(animatorSet); + animatorSet.start(); } if (FeatureFlags.ENABLE_PREMIUM_HAPTICS_ALL_APPS.get() && ((mFromState == NORMAL && mToState == ALL_APPS) @@ -268,7 +281,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch private void goToOverviewOrHomeOnDragEnd(float velocity) { boolean goToHomeInsteadOfOverview = !mMotionPauseDetector.isPaused(); if (goToHomeInsteadOfOverview) { - new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(NORMAL)) + new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(NORMAL), null) .animateWithVelocity(velocity); } if (mReachedOverview) { diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 7cb6eb63ab..c58d6cc7a4 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -58,6 +58,7 @@ import com.android.launcher3.R; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.compat.AccessibilityManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; @@ -117,6 +118,7 @@ public final class RecentsActivity extends StatefulActivity { // animation callback private final Handler mHandler = new Handler(); private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout; + private SplitSelectStateController mSplitSelectStateController; /** * Init drag layer and overview panel views. @@ -129,13 +131,12 @@ public final class RecentsActivity extends StatefulActivity { mFallbackRecentsView = findViewById(R.id.overview_panel); mActionsView = findViewById(R.id.overview_actions_view); getRootView().getSysUiScrim().getSysUIProgress().updateValue(0); - - SplitSelectStateController controller = + mSplitSelectStateController = new SplitSelectStateController(this, mHandler, getStateManager(), - null /* depthController */, getStatsLogManager(), + null /* depthController */, getStatsLogManager(), SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this)); mDragLayer.recreateControllers(); - mFallbackRecentsView.init(mActionsView, controller); + mFallbackRecentsView.init(mActionsView, mSplitSelectStateController); mTISBindHelper = new TISBindHelper(this, this::onTISConnected); } diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java index 3cec1a4f1a..6d9ecd9534 100644 --- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java +++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java @@ -22,12 +22,16 @@ import android.animation.Animator; import android.animation.AnimatorSet; import android.util.Log; +import androidx.annotation.Nullable; + import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.states.StateAnimationConfig; +import java.util.function.Consumer; + /** * Runs an animation from overview to home. Currently, this animation is just a wrapper around the * normal state transition and may play a {@link WorkspaceRevealAnim} if we're starting from an @@ -39,14 +43,18 @@ public class OverviewToHomeAnim { private final Launcher mLauncher; private final Runnable mOnReachedHome; + @Nullable + private final Consumer mSplitCancelConsumer; // Only run mOnReachedHome when both of these are true. private boolean mIsHomeStaggeredAnimFinished; private boolean mIsOverviewHidden; - public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome) { + public OverviewToHomeAnim(Launcher launcher, Runnable onReachedHome, + @Nullable Consumer splitCancelConsumer) { mLauncher = launcher; mOnReachedHome = onReachedHome; + mSplitCancelConsumer = splitCancelConsumer; } /** @@ -92,6 +100,11 @@ public class OverviewToHomeAnim { maybeOverviewToHomeAnimComplete(); } }); + + if (mSplitCancelConsumer != null) { + // Clear split state when swiping to home + mSplitCancelConsumer.accept(anim); + } anim.play(stateAnim); stateManager.setCurrentAnimation(anim, NORMAL); anim.start(); diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index b76fe5cb03..2e8af4c64b 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -17,14 +17,22 @@ package com.android.quickstep.util +import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.graphics.Bitmap +import android.graphics.Rect +import android.graphics.RectF import android.graphics.drawable.Drawable import android.view.View import com.android.launcher3.DeviceProfile +import com.android.launcher3.Launcher +import com.android.launcher3.Utilities import com.android.launcher3.anim.PendingAnimation +import com.android.launcher3.dragndrop.DragLayer import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource +import com.android.quickstep.views.FloatingTaskView import com.android.quickstep.views.IconView +import com.android.quickstep.views.RecentsView import com.android.quickstep.views.TaskThumbnailView import com.android.quickstep.views.TaskView import com.android.quickstep.views.TaskView.TaskIdAttributeContainer @@ -176,4 +184,38 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC TaskThumbnailView.SPLIT_SELECT_TRANSLATE_X, 0f)) } } + + + fun animateAwayPlaceholder(mLauncher: Launcher) : AnimatorSet { + val animatorSet = AnimatorSet() + val recentsView : RecentsView<*, *> = mLauncher.getOverviewPanel() + val floatingTask: FloatingTaskView = splitSelectStateController.firstFloatingTaskView + ?: return animatorSet + + // We are in split selection state currently, transitioning to another state + val dragLayer: DragLayer = mLauncher.dragLayer + val onScreenRectF = RectF() + Utilities.getBoundsForViewInDragLayer(mLauncher.dragLayer, floatingTask, + Rect(0, 0, floatingTask.width, floatingTask.height), + false, null, onScreenRectF) + // Get the part of the floatingTask that intersects with the DragLayer (i.e. the + // on-screen portion) + onScreenRectF.intersect( + dragLayer.left.toFloat(), + dragLayer.top.toFloat(), + dragLayer.right.toFloat(), + dragLayer.bottom + .toFloat() + ) + animatorSet.play(ObjectAnimator.ofFloat(floatingTask, + FloatingTaskView.PRIMARY_TRANSLATE_OFFSCREEN, + recentsView.pagedOrientationHandler + .getFloatingTaskOffscreenTranslationTarget( + floatingTask, + onScreenRectF, + floatingTask.stagePosition, + mLauncher.deviceProfile + ))) + return animatorSet + } } diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index ff701e756b..970cf64906 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -895,6 +895,7 @@ public class SplitSelectStateController { mFirstFloatingTaskView = floatingTaskView; } + @Nullable public FloatingTaskView getFirstFloatingTaskView() { return mFirstFloatingTaskView; } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index ae744d48a3..b8ec5f0e33 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -6022,11 +6022,6 @@ public abstract class RecentsView