diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index bcad478eae..721c831a42 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -21,6 +21,7 @@ import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.app.animation.Interpolators.FINAL_FRAME; import static com.android.app.animation.Interpolators.INSTANT; import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile; import static com.android.launcher3.LauncherState.QUICK_SWITCH_FROM_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; @@ -31,6 +32,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TR 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.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; +import static com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_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.TASK_SECONDARY_TRANSLATION; @@ -76,6 +78,10 @@ public abstract class BaseRecentsViewStateController RECENTS_GRID_PROGRESS.set(mRecentsView, state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f); TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, state.showTaskThumbnailSplash() ? 1f : 0f); + if (enableLargeDesktopWindowingTile()) { + DESKTOP_CAROUSEL_DETACH_PROGRESS.set(mRecentsView, + state.detachDesktopCarousel() ? 1f : 0f); + } } @Override @@ -142,6 +148,12 @@ public abstract class BaseRecentsViewStateController setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f, getOverviewInterpolator(fromState, toState)); + + if (enableLargeDesktopWindowingTile()) { + setter.setFloat(mRecentsView, DESKTOP_CAROUSEL_DETACH_PROGRESS, + toState.detachDesktopCarousel() ? 1f : 0f, + getOverviewInterpolator(fromState, toState)); + } } private Interpolator getOverviewInterpolator(LauncherState fromState, LauncherState toState) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 18d717f096..e87ac2fec6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -89,6 +89,11 @@ public class BackgroundAppState extends OverviewState { return true; } + @Override + public boolean detachDesktopCarousel() { + return true; + } + @Override protected float getDepthUnchecked(Context context) { if (Launcher.getLauncher(context).areDesktopTasksVisible()) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index b165cddfb2..c48ba4f11b 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -165,6 +165,11 @@ public class OverviewState extends LauncherState { return deviceProfile.isTablet; } + @Override + public boolean detachDesktopCarousel() { + return false; + } + @Override public boolean disallowTaskbarGlobalDrag() { // Disable global drag in overview diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index 83794fbd6c..daac9fb013 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -18,6 +18,7 @@ package com.android.quickstep.fallback; import static com.android.app.animation.Interpolators.FINAL_FRAME; import static com.android.app.animation.Interpolators.INSTANT; import static com.android.app.animation.Interpolators.LINEAR; +import static com.android.launcher3.Flags.enableLargeDesktopWindowingTile; 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_TRANSLATE_X; @@ -26,6 +27,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET; +import static com.android.quickstep.views.RecentsView.DESKTOP_CAROUSEL_DETACH_PROGRESS; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; @@ -116,6 +118,11 @@ public class FallbackRecentsStateController implements StateHandler { private static final int FLAG_LIVE_TILE = BaseState.getFlag(6); private static final int FLAG_RECENTS_VIEW_VISIBLE = BaseState.getFlag(7); private static final int FLAG_TASK_THUMBNAIL_SPLASH = BaseState.getFlag(8); + private static final int FLAG_DETACH_DESKTOP_CAROUSEL = BaseState.getFlag(9); public static final RecentsState DEFAULT = new RecentsState(0, FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID @@ -51,8 +52,8 @@ public class RecentsState implements BaseState { | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE | FLAG_RECENTS_VIEW_VISIBLE); public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2, FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN - | FLAG_RECENTS_VIEW_VISIBLE - | FLAG_TASK_THUMBNAIL_SPLASH); + | FLAG_RECENTS_VIEW_VISIBLE | FLAG_TASK_THUMBNAIL_SPLASH + | FLAG_DETACH_DESKTOP_CAROUSEL); public static final RecentsState HOME = new RecentsState(3, 0); public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0); public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5, @@ -149,6 +150,11 @@ public class RecentsState implements BaseState { return hasFlag(FLAG_TASK_THUMBNAIL_SPLASH); } + @Override + public boolean detachDesktopCarousel() { + return hasFlag(FLAG_DETACH_DESKTOP_CAROUSEL); + } + /** * True if the state has overview panel visible. */ diff --git a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt index e70372fb79..aa0fd88573 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt +++ b/quickstep/src/com/android/quickstep/util/RecentsViewUtils.kt @@ -83,22 +83,22 @@ class RecentsViewUtils { /** Returns the first [TaskView], with some tasks possibly hidden in the carousel. */ fun getFirstTaskViewInCarousel( - nonRunningTaskCategoryHidden: Boolean, + nonRunningTaskCarouselHidden: Boolean, taskViews: Iterable, runningTaskView: TaskView?, ): TaskView? = taskViews.firstOrNull { - it.isVisibleInCarousel(runningTaskView, nonRunningTaskCategoryHidden) + it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden) } /** Returns the last [TaskView], with some tasks possibly hidden in the carousel. */ fun getLastTaskViewInCarousel( - nonRunningTaskCategoryHidden: Boolean, + nonRunningTaskCarouselHidden: Boolean, taskViews: Iterable, runningTaskView: TaskView?, ): TaskView? = taskViews.lastOrNull { - it.isVisibleInCarousel(runningTaskView, nonRunningTaskCategoryHidden) + it.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden) } /** Returns the current list of [TaskView] children. */ @@ -109,28 +109,33 @@ class RecentsViewUtils { fun applyAttachAlpha( taskViews: Iterable, runningTaskView: TaskView?, - runningTaskTileHidden: Boolean, - nonRunningTaskCategoryHidden: Boolean, + runningTaskAttachAlpha: Float, + nonRunningTaskCarouselHidden: Boolean, ) { taskViews.forEach { taskView -> - val isVisible = - if (taskView == runningTaskView) !runningTaskTileHidden - else taskView.isVisibleInCarousel(runningTaskView, nonRunningTaskCategoryHidden) - taskView.attachAlpha = if (isVisible) 1f else 0f + taskView.attachAlpha = + if (taskView == runningTaskView) { + runningTaskAttachAlpha + } else { + if (taskView.isVisibleInCarousel(runningTaskView, nonRunningTaskCarouselHidden)) + 1f + else 0f + } } } - private fun TaskView.isVisibleInCarousel( + fun TaskView.isVisibleInCarousel( runningTaskView: TaskView?, - nonRunningTaskCategoryHidden: Boolean, + nonRunningTaskCarouselHidden: Boolean, ): Boolean = - if (!nonRunningTaskCategoryHidden) true - else if (runningTaskView == null) true else getCategory() == runningTaskView.getCategory() + if (!nonRunningTaskCarouselHidden) true + else getCarouselType() == runningTaskView.getCarouselType() - private fun TaskView.getCategory(): TaskViewCategory = - if (this is DesktopTaskView) TaskViewCategory.DESKTOP else TaskViewCategory.FULL_SCREEN + /** Returns the carousel type of the TaskView, and default to fullscreen if it's null. */ + private fun TaskView?.getCarouselType(): TaskViewCarousel = + if (this is DesktopTaskView) TaskViewCarousel.DESKTOP else TaskViewCarousel.FULL_SCREEN - private enum class TaskViewCategory { + private enum class TaskViewCarousel { FULL_SCREEN, DESKTOP, } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 3ae9bc823b..a075c58a00 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -324,20 +324,13 @@ public abstract class RecentsView< new FloatProperty("runningTaskAttachAlpha") { @Override public void setValue(RecentsView recentsView, float v) { - TaskView runningTask = recentsView.getRunningTaskView(); - if (runningTask == null) { - return; - } - runningTask.setAttachAlpha(v); + recentsView.mRunningTaskAttachAlpha = v; + recentsView.applyAttachAlpha(); } @Override public Float get(RecentsView recentsView) { - TaskView runningTask = recentsView.getRunningTaskView(); - if (runningTask == null) { - return null; - } - return runningTask.getAttachAlpha(); + return recentsView.mRunningTaskAttachAlpha; } }; @@ -463,6 +456,21 @@ public abstract class RecentsView< } }; + public static final FloatProperty DESKTOP_CAROUSEL_DETACH_PROGRESS = + new FloatProperty<>("desktopCarouselDetachProgress") { + @Override + public void setValue(RecentsView view, float offset) { + view.mDesktopCarouselDetachProgress = offset; + view.applyAttachAlpha(); + view.updatePageOffsets(); + } + + @Override + public Float get(RecentsView view) { + return view.mDesktopCarouselDetachProgress; + } + }; + /** * Alpha of the task thumbnail splash, where being in BackgroundAppState has a value of 1, and * being in any other state has a value of 0. @@ -574,6 +582,7 @@ public abstract class RecentsView< private int mClampedScrollOffsetBound; private float mAdjacentPageHorizontalOffset = 0; + private float mDesktopCarouselDetachProgress = 0; protected float mTaskViewsSecondaryTranslation = 0; protected float mTaskViewsPrimarySplitTranslation = 0; protected float mTaskViewsSecondarySplitTranslation = 0; @@ -680,13 +689,13 @@ public abstract class RecentsView< protected int mRunningTaskViewId = -1; private int mTaskViewIdCount; protected boolean mRunningTaskTileHidden; - private boolean mNonRunningTaskCategoryHidden; @Nullable private Task[] mTmpRunningTasks; protected int mFocusedTaskViewId = INVALID_TASK_ID; private boolean mTaskIconScaledDown = false; private boolean mRunningTaskShowScreenshot = false; + private float mRunningTaskAttachAlpha; private boolean mOverviewStateEnabled; private boolean mHandleTaskStackChanges; @@ -2733,9 +2742,6 @@ public abstract class RecentsView< setEnableFreeScroll(false); setEnableDrawingLiveTile(false); setRunningTaskHidden(true); - if (enableLargeDesktopWindowingTile()) { - setNonRunningTaskCategoryHidden(true); - } setTaskIconScaledDown(true); } @@ -2859,6 +2865,14 @@ public abstract class RecentsView< animatorSet.play( ObjectAnimator.ofFloat(this, TASK_THUMBNAIL_SPLASH_ALPHA, splashAlpha)); } + if (enableLargeDesktopWindowingTile()) { + if (animatorSet != null) { + animatorSet.play( + ObjectAnimator.ofFloat(this, DESKTOP_CAROUSEL_DETACH_PROGRESS, 0f)); + } else { + DESKTOP_CAROUSEL_DETACH_PROGRESS.set(this, 0f); + } + } } /** @@ -2874,9 +2888,6 @@ public abstract class RecentsView< setEnableDrawingLiveTile(mCurrentGestureEndTarget == GestureState.GestureEndTarget.RECENTS); Log.d(TAG, "onGestureAnimationEnd - mEnableDrawingLiveTile: " + mEnableDrawingLiveTile); setRunningTaskHidden(false); - if (enableLargeDesktopWindowingTile()) { - setNonRunningTaskCategoryHidden(false); - } animateUpTaskIconScale(); animateActionsViewIn(); @@ -3028,6 +3039,9 @@ public abstract class RecentsView< */ public void setRunningTaskHidden(boolean isHidden) { mRunningTaskTileHidden = isHidden; + // mRunningTaskAttachAlpha can be changed by RUNNING_TASK_ATTACH_ALPHA animation without + // changing mRunningTaskTileHidden. + mRunningTaskAttachAlpha = isHidden ? 0f : 1f; TaskView runningTask = getRunningTaskView(); if (runningTask == null) { return; @@ -3039,18 +3053,11 @@ public abstract class RecentsView< } } - /** - * Hides the tasks that has a different category (Fullscreen/Desktop) from the running task. - */ - public void setNonRunningTaskCategoryHidden(boolean isHidden) { - mNonRunningTaskCategoryHidden = isHidden; - updateMinAndMaxScrollX(); - applyAttachAlpha(); - } - private void applyAttachAlpha() { - mUtils.applyAttachAlpha(getTaskViews(), getRunningTaskView(), mRunningTaskTileHidden, - mNonRunningTaskCategoryHidden); + // Only hide non running task carousel when it's fully off screen, otherwise it needs to + // be visible to move to on screen. + mUtils.applyAttachAlpha(getTaskViews(), getRunningTaskView(), mRunningTaskAttachAlpha, + /*nonRunningTaskCarouselHidden=*/mDesktopCarouselDetachProgress == 1f); } private void setRunningTaskViewShowScreenshot(boolean showScreenshot) { @@ -4693,6 +4700,10 @@ public abstract class RecentsView< ? (runningTask == null ? INVALID_PAGE : indexOfChild(runningTask)) : mOffsetMidpointIndexOverride; int modalMidpoint = getCurrentPage(); + TaskView carouselHiddenMidpointTask = runningTask != null ? runningTask + : mUtils.getFirstTaskViewInCarousel(/*nonRunningTaskCarouselHidden=*/true, + getTaskViews(), null); + int carouselHiddenMidpoint = indexOfChild(carouselHiddenMidpointTask); boolean shouldCalculateOffsetForAllTasks = showAsGrid && (enableGridOnlyOverview() || enableLargeDesktopWindowingTile()) && mTaskModalness > 0; @@ -4712,6 +4723,7 @@ public abstract class RecentsView< float modalLeftOffsetSize = 0; float modalRightOffsetSize = 0; float gridOffsetSize = 0; + float carouselHiddenOffsetSize = 0; if (showAsGrid) { // In grid, we only focus the task on the side. The reference index used for offset @@ -4729,7 +4741,10 @@ public abstract class RecentsView< : 0; } + int primarySize = getPagedOrientationHandler().getPrimaryValue(getWidth(), getHeight()); + float maxOverscroll = primarySize * OverScroll.OVERSCROLL_DAMP_FACTOR; for (int i = 0; i < count; i++) { + View child = getChildAt(i); float translation = i == midpoint ? midpointOffsetSize : i < midpoint @@ -4739,16 +4754,31 @@ public abstract class RecentsView< gridOffsetSize = getHorizontalOffsetSize(i, modalMidpoint, modalOffset); gridOffsetSize = Math.abs(gridOffsetSize) * (i <= modalMidpoint ? 1 : -1); } + if (enableLargeDesktopWindowingTile()) { + if (child instanceof TaskView + && !mUtils.isVisibleInCarousel((TaskView) child, + runningTask, /*nonRunningTaskCarouselHidden=*/true)) { + // Increment carouselHiddenOffsetSize by maxOverscroll so it won't be on screen + // even when user overscroll. + carouselHiddenOffsetSize = (Math.abs(getMaxHorizontalOffsetSize(i, + carouselHiddenMidpoint)) + maxOverscroll) + * mDesktopCarouselDetachProgress; + carouselHiddenOffsetSize = carouselHiddenOffsetSize * ( + i <= carouselHiddenMidpoint ? 1 : -1); + } else { + carouselHiddenOffsetSize = 0; + } + } float modalTranslation = i == modalMidpoint ? modalMidpointOffsetSize : showAsGrid ? gridOffsetSize : i < modalMidpoint ? modalLeftOffsetSize : modalRightOffsetSize; - View child = getChildAt(i); boolean skipTranslationOffset = enableDesktopTaskAlphaAnimation() && i == getRunningTaskIndex() && child instanceof DesktopTaskView; - float totalTranslationX = (skipTranslationOffset ? 0f : translation) + modalTranslation; + float totalTranslationX = (skipTranslationOffset ? 0f : translation) + modalTranslation + + carouselHiddenOffsetSize; FloatProperty translationPropertyX = child instanceof TaskView ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty() : getPagedOrientationHandler().getPrimaryViewTranslate(); @@ -4805,6 +4835,14 @@ public abstract class RecentsView< return 0; } + return getMaxHorizontalOffsetSize(childIndex, midpointIndex) * offsetProgress; + } + + /** + * Computes the distance to offset the given child such that it is completely offscreen when + * translating away from the given midpoint. + */ + private float getMaxHorizontalOffsetSize(int childIndex, int midpointIndex) { // First, get the position of the task relative to the midpoint. If there is no midpoint // then we just use the normal (centered) task position. RectF taskPosition = mTempRectF; @@ -4864,7 +4902,7 @@ public abstract class RecentsView< } distanceToOffscreen -= mLastComputedTaskEndPushOutDistance; } - return distanceToOffscreen * offsetProgress; + return distanceToOffscreen; } /** @@ -5817,7 +5855,7 @@ public abstract class RecentsView< private int getFirstViewIndex() { final TaskView firstView; if (mShowAsGridLastOnLayout) { - // For grid Overivew, it always start if a large tile (focused task or desktop task) if + // For grid Overview, it always start if a large tile (focused task or desktop task) if // they exist, otherwise it start with the first task. TaskView firstLargeTaskView = mUtils.getFirstLargeTaskView(getTaskViews()); if (firstLargeTaskView != null) { @@ -5826,7 +5864,8 @@ public abstract class RecentsView< firstView = getTaskViewAt(0); } } else { - firstView = mUtils.getFirstTaskViewInCarousel(mNonRunningTaskCategoryHidden, + firstView = mUtils.getFirstTaskViewInCarousel( + /*nonRunningTaskCarouselHidden=*/mDesktopCarouselDetachProgress > 0, getTaskViews(), getRunningTaskView()); } return indexOfChild(firstView); @@ -5847,7 +5886,8 @@ public abstract class RecentsView< lastView = mUtils.getLastLargeTaskView(getTaskViews()); } } else { - lastView = mUtils.getLastTaskViewInCarousel(mNonRunningTaskCategoryHidden, + lastView = mUtils.getLastTaskViewInCarousel( + /*nonRunningTaskCarouselHidden=*/mDesktopCarouselDetachProgress > 0, getTaskViews(), getRunningTaskView()); } return indexOfChild(lastView); diff --git a/src/com/android/launcher3/statemanager/BaseState.java b/src/com/android/launcher3/statemanager/BaseState.java index b81729a712..f6b610c9bf 100644 --- a/src/com/android/launcher3/statemanager/BaseState.java +++ b/src/com/android/launcher3/statemanager/BaseState.java @@ -71,6 +71,13 @@ public interface BaseState { return false; } + /** + * For this state, whether fullscreen and desktop quickswitch carousel are detached. + */ + default boolean detachDesktopCarousel() { + return true; + } + /** * For this state, whether member variables and other forms of data state should be preserved * or wiped when the state is reapplied. (See {@link StateManager#reapplyState()})