diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index dc1046bd6e..f2205097a4 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -23,16 +23,22 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; 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.launcher3.testing.shared.TestProtocol.BAD_STATE; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; +import static com.android.quickstep.views.RecentsView.FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN; import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS; 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.SPLIT_INSTRUCTIONS_FADE; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; +import android.graphics.Rect; +import android.graphics.RectF; import android.util.FloatProperty; import android.util.Log; @@ -40,9 +46,12 @@ import androidx.annotation.NonNull; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; +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; /** @@ -106,6 +115,49 @@ public abstract class BaseRecentsViewStateController setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f, config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR)); + if (mRecentsView.isSplitSelectionActive()) { + // TODO (b/238651489): Refactor state management to avoid need for double check + FloatingTaskView floatingTask = mRecentsView.getFirstFloatingTaskView(); + if (floatingTask != null) { + 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, + FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN, + mRecentsView.getPagedOrientationHandler() + .getFloatingTaskOffscreenTranslationTarget( + floatingTask, + onScreenRectF, + floatingTask.getStagePosition(), + mLauncher.getDeviceProfile() + ), + config.getInterpolator( + ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, + LINEAR + )); + setter.setFloat( + mRecentsView, + SPLIT_INSTRUCTIONS_FADE, + 1, + config.getInterpolator( + ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, + LINEAR + )); + } + } + float recentsAlpha = toState.overviewUi ? 1 : 0; Log.d(BAD_STATE, "BaseRecentsViewStateController setStateWithAnimationInternal toState=" + toState + ", alpha=" + recentsAlpha); diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index c4c903815e..6f07568cf5 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -37,6 +37,10 @@ import com.android.quickstep.views.TaskView; */ public class OverviewState extends LauncherState { + private static final int OVERVIEW_SLIDE_IN_DURATION = 380; + private static final int OVERVIEW_POP_IN_DURATION = 250; + private static final int OVERVIEW_EXIT_DURATION = 250; + protected static final Rect sTempRect = new Rect(); private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED @@ -57,8 +61,15 @@ public class OverviewState extends LauncherState { @Override public int getTransitionDuration(Context context, boolean isToState) { - // In gesture modes, overview comes in all the way from the side, so give it more time. - return DisplayController.getNavigationMode(context).hasGestures ? 380 : 250; + if (isToState) { + // In gesture modes, overview comes in all the way from the side, so give it more time. + return DisplayController.getNavigationMode(context).hasGestures + ? OVERVIEW_SLIDE_IN_DURATION + : OVERVIEW_POP_IN_DURATION; + } else { + // When exiting Overview, exit quickly. + return OVERVIEW_EXIT_DURATION; + } } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 4d2f9652ad..872e64a510 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -22,13 +22,15 @@ import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -39,6 +41,8 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; 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.ANIM_SCRIM_FADE; @@ -87,9 +91,16 @@ public class QuickstepAtomicAnimationFactory extends public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState, StateAnimationConfig config) { RecentsView overview = mActivity.getOverviewPanel(); - if (toState == NORMAL && fromState == OVERVIEW) { + if ((fromState == OVERVIEW || fromState == OVERVIEW_SPLIT_SELECT) && toState == NORMAL) { + if (fromState == OVERVIEW_SPLIT_SELECT) { + config.setInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_FLOATING_TASK_TRANSLATE_OFFSCREEN, + clampToProgress(EMPHASIZED_ACCELERATE, 0, 0.4f)); + config.setInterpolator(ANIM_OVERVIEW_SPLIT_SELECT_INSTRUCTIONS_FADE, + clampToProgress(LINEAR, 0, 0.33f)); + } + config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f)); - config.setInterpolator(ANIM_SCRIM_FADE, LINEAR); + config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(LINEAR, 0.33f, 1)); config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); @@ -98,8 +109,7 @@ public class QuickstepAtomicAnimationFactory extends // Overview is going offscreen, so keep it at its current scale and opacity. config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME); config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME); - config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, - clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f)); + config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, EMPHASIZED_DECELERATE); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME); } else { config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL); diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java index 835c9f7e8e..7a66ea00e1 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -260,6 +260,10 @@ public class FloatingTaskView extends FrameLayout { mActivity.getDeviceProfile(), mStagePosition); } + public int getStagePosition() { + return mStagePosition; + } + private static class SplitOverlayProperties { private final float finalTaskViewScaleX; diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 3aa24ac825..5975106ea5 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -397,6 +397,39 @@ public abstract class RecentsView FIRST_FLOATING_TASK_TRANSLATE_OFFSCREEN = + new FloatProperty("firstFloatingTaskTranslateOffscreen") { + @Override + public void setValue(RecentsView view, float translation) { + view.getPagedOrientationHandler().setFloatingTaskPrimaryTranslation( + view.mFirstFloatingTaskView, + translation, + view.mActivity.getDeviceProfile() + ); + } + + @Override + public Float get(RecentsView view) { + return view.getPagedOrientationHandler().getFloatingTaskPrimaryTranslation( + view.mFirstFloatingTaskView, + view.mActivity.getDeviceProfile() + ); + } + }; + + public static final FloatProperty SPLIT_INSTRUCTIONS_FADE = + new FloatProperty("splitInstructionsFade") { + @Override + public void setValue(RecentsView view, float fade) { + view.mSplitInstructionsView.setAlpha(1 - fade); + } + + @Override + public Float get(RecentsView view) { + return 1 - view.mSplitInstructionsView.getAlpha(); + } + }; + // OverScroll constants private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270; @@ -5291,6 +5324,11 @@ public abstract class RecentsView(primary, secondary); } + + @Override + public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect, + @StagePosition int stagePosition, DeviceProfile dp) { + float currentTranslationY = floatingTask.getTranslationY(); + return currentTranslationY - onScreenRect.height(); + } + + @Override + public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, + DeviceProfile dp) { + floatingTask.setTranslationY(translation); + } + + @Override + public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) { + return floatingTask.getTranslationY(); + } } diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index 6bc021b515..1a8d355ffc 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -34,9 +34,9 @@ import android.widget.LinearLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.util.SplitConfigurationOptions; +import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; -import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds; import java.util.List; @@ -242,6 +242,41 @@ public interface PagedOrientationHandler { */ void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile); + /** + * Determine the target translation for animating the FloatingTaskView out. This value could + * either be an x-coordinate or a y-coordinate, depending on which way the FloatingTaskView was + * docked. + * + * @param floatingTask The FloatingTaskView. + * @param onScreenRect The current on-screen dimensions of the FloatingTaskView. + * @param stagePosition STAGE_POSITION_TOP_OR_LEFT or STAGE_POSITION_BOTTOM_OR_RIGHT. + * @param dp The device profile. + * @return A float. When an animation translates the FloatingTaskView to this position, it will + * appear to tuck away off the edge of the screen. + */ + float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect, + @StagePosition int stagePosition, DeviceProfile dp); + + /** + * Sets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be + * either x or y), depending on how the view is oriented. + * + * @param floatingTask The FloatingTaskView to be translated. + * @param translation The target translation value. + * @param dp The current device profile. + */ + void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, DeviceProfile dp); + + /** + * Gets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be + * either x or y), depending on how the view is oriented. + * + * @param floatingTask The FloatingTaskView in question. + * @param dp The current device profile. + * @return The current translation value. + */ + Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp); + class ChildBounds { public final int primaryDimension; diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 738e6b1886..dd9f64266e 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -757,4 +757,36 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { return new Pair<>(secondary, primary); } } + + @Override + public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect, + @StagePosition int stagePosition, DeviceProfile dp) { + if (dp.isLandscape) { + float currentTranslationX = floatingTask.getTranslationX(); + return stagePosition == STAGE_POSITION_TOP_OR_LEFT + ? currentTranslationX - onScreenRect.width() + : currentTranslationX + onScreenRect.width(); + } else { + float currentTranslationY = floatingTask.getTranslationY(); + return currentTranslationY - onScreenRect.height(); + } + } + + @Override + public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, + DeviceProfile dp) { + if (dp.isLandscape) { + floatingTask.setTranslationX(translation); + } else { + floatingTask.setTranslationY(translation); + } + + } + + @Override + public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) { + return dp.isLandscape + ? floatingTask.getTranslationX() + : floatingTask.getTranslationY(); + } }