From b794ea64c72678ae1a94c2b3de130f4f5ce77a35 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Wed, 3 Feb 2021 18:00:30 +0000 Subject: [PATCH] Show TaskView as grid in large screens - Introduced gridProgress to RecentsView/TaskView ofr animating into grid - Introduced gridProgress dependant translation and scale properties in TaskView - Animate running task into grid with TaskViewSimulator - Remove overview actions (for now) but keep clear all button in large screens - Adjust ClearAllButton translation to acoomodate for grid - Use screen width +-50% to calculate task visibility - Use the position where TaskView is on screenEnd as pageScroll - TODO: Handle separate recents activity Doc: go/foldables-launcher-overview Video: http://dr/file/d/107Aydii1LoFCwP63nWG3Twr2PBDE5ZgD/view?resourcekey=0-aUjdnx8ezimS9tmAgao9ag Test: Test Launchering overview and launching overview from apps with folloiwng combination: - large / small screen sizes - portrait / landscape - thumbnails from different screen sizes / orientations Bug: 174464863 Fixes: 181509346 Change-Id: I4b691cde774f2e37532b68ba83c6eed399f2332e --- quickstep/res/values/dimens.xml | 1 + .../BaseRecentsViewStateController.java | 5 + .../RecentsViewStateController.java | 12 +- .../states/BackgroundAppState.java | 8 +- .../states/OverviewModalTaskState.java | 2 +- .../uioverrides/states/OverviewState.java | 9 +- .../NoButtonQuickSwitchTouchController.java | 4 +- .../PortraitOverviewStateTouchHelper.java | 2 +- .../android/quickstep/AbsSwipeUpHandler.java | 25 +- .../com/android/quickstep/TaskViewUtils.java | 7 +- .../quickstep/util/TaskViewSimulator.java | 33 +- .../quickstep/views/ClearAllButton.java | 63 +++- .../quickstep/views/LauncherRecentsView.java | 4 +- .../android/quickstep/views/RecentsView.java | 344 ++++++++++++++++-- .../android/quickstep/views/TaskMenuView.java | 8 +- .../com/android/quickstep/views/TaskView.java | 176 ++++++--- src/com/android/launcher3/LauncherState.java | 10 +- src/com/android/launcher3/PagedView.java | 23 +- src/com/android/launcher3/Workspace.java | 2 +- .../touch/LandscapePagedViewHandler.java | 7 - .../touch/PagedOrientationHandler.java | 2 - .../touch/PortraitPagedViewHandler.java | 7 - 22 files changed, 614 insertions(+), 140 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index e46eb9e12b..9773366247 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -29,6 +29,7 @@ 8dp 16dp + 48dp 16dp 70dp diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index aad7e17df0..0f13ef9c20 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -30,6 +30,7 @@ import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVER import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_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; @@ -73,6 +74,8 @@ public abstract class BaseRecentsViewStateController SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher)); SCRIM_MULTIPLIER.set(scrim, 1f); getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness()); + RECENTS_GRID_PROGRESS.set(mRecentsView, state.displayOverviewTasksAsGrid(mLauncher) + ? 1f : 0f); } @Override @@ -117,6 +120,8 @@ public abstract class BaseRecentsViewStateController mRecentsView, getTaskModalnessProperty(), toState.getOverviewModalness(), config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR)); + setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, + toState.displayOverviewTasksAsGrid(mLauncher) ? 1f : 0f, LINEAR); } abstract FloatProperty getTaskModalnessProperty(); diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 5ccc1e8681..c9de662784 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -15,7 +15,8 @@ */ package com.android.launcher3.uioverrides; -import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; +import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON; +import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; @@ -83,11 +84,14 @@ public final class RecentsViewStateController extends private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config, LauncherState state) { - float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0; + float clearAllButtonAlpha = (state.getVisibleElements(mLauncher) & CLEAR_ALL_BUTTON) != 0 + ? 1 : 0; propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA, - buttonAlpha, LINEAR); + clearAllButtonAlpha, LINEAR); + float overviewButtonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 + ? 1 : 0; propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(), - MultiValueAlpha.VALUE, buttonAlpha, config.getInterpolator( + MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator( ANIM_OVERVIEW_ACTIONS_FADE, LINEAR)); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 2cf65af3ed..2ad718b63f 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -69,11 +69,17 @@ public class BackgroundAppState extends OverviewState { @Override public int getVisibleElements(Launcher launcher) { return super.getVisibleElements(launcher) - & ~OVERVIEW_BUTTONS + & ~OVERVIEW_ACTIONS + & ~CLEAR_ALL_BUTTON & ~VERTICAL_SWIPE_INDICATOR | TASKBAR; } + @Override + public boolean displayOverviewTasksAsGrid(Launcher launcher) { + return false; + } + @Override public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) { if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java index 41c689d16d..bdba482c71 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java @@ -45,7 +45,7 @@ public class OverviewModalTaskState extends OverviewState { @Override public int getVisibleElements(Launcher launcher) { - return OVERVIEW_BUTTONS; + return OVERVIEW_ACTIONS | CLEAR_ALL_BUTTON; } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index b295e79d04..d480b6d472 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -28,6 +28,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Workspace; +import com.android.launcher3.config.FeatureFlags; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; @@ -122,7 +123,8 @@ public class OverviewState extends LauncherState { @Override public int getVisibleElements(Launcher launcher) { - return OVERVIEW_BUTTONS; + return displayOverviewTasksAsGrid(launcher) ? CLEAR_ALL_BUTTON + : CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS; } @Override @@ -130,6 +132,11 @@ public class OverviewState extends LauncherState { return 0.5f; } + @Override + public boolean displayOverviewTasksAsGrid(Launcher launcher) { + return launcher.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get(); + } + @Override public String getDescription(Launcher launcher) { return launcher.getString(R.string.accessibility_recent_apps); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index df433f8cc0..4766870984 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -18,7 +18,7 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static com.android.launcher3.LauncherAnimUtils.newCancelListener; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; +import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS; import static com.android.launcher3.LauncherState.QUICK_SWITCH; import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD; import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; @@ -222,7 +222,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, mRecentsView.setContentAlpha(1); mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress()); mLauncher.getActionsView().getVisibilityAlpha().setValue( - (fromState.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1f : 0f); + (fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f); float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher); // As we drag right, animate the following properties: diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java index 845699a761..4df0f63116 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java @@ -76,7 +76,7 @@ public final class PortraitOverviewStateTouchHelper { * @return the animation */ PendingAnimation createSwipeDownToTaskAppAnimation(long duration, Interpolator interpolator) { - mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen()); + mRecentsView.setCurrentPage(mRecentsView.getDestinationPage()); TaskView taskView = mRecentsView.getCurrentPageTaskView(); if (taskView == null) { throw new IllegalStateException("There is no task view to animate to."); diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5f8fc6d768..1bd7fe6593 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -25,7 +25,6 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; -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.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; @@ -46,12 +45,14 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI 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.views.RecentsView.RECENTS_GRID_PROGRESS; 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.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager; @@ -79,6 +80,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.statemanager.StatefulActivity; @@ -922,7 +924,7 @@ public abstract class AbsSwipeUpHandler, Q extends * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); duration = Math.min(MAX_SWIPE_DURATION, expectedDuration); startShift = currentShift; - interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; + interpolator = endTarget == RECENTS ? ACCEL_DEACCEL : DEACCEL; } else { startShift = Utilities.boundToRange(currentShift - velocity.y * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); @@ -945,7 +947,7 @@ public abstract class AbsSwipeUpHandler, Q extends duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); if (endTarget == RECENTS) { - interpolator = OVERSHOOT_1_2; + interpolator = ACCEL_DEACCEL; } } } @@ -958,7 +960,7 @@ public abstract class AbsSwipeUpHandler, Q extends duration = Math.max(MIN_OVERSHOOT_DURATION, duration); } else if (endTarget == RECENTS) { if (mRecentsView != null) { - int nearestPage = mRecentsView.getPageNearestToCenterOfScreen(); + int nearestPage = mRecentsView.getDestinationPage(); 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. @@ -1102,8 +1104,8 @@ public abstract class AbsSwipeUpHandler, Q extends homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y); mLauncherTransitionController = null; } else { + AnimatorSet animatorSet = new AnimatorSet(); ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end); - windowAnim.setDuration(duration).setInterpolator(interpolator); windowAnim.addUpdateListener(valueAnimator -> { computeRecentsScrollIfInvisible(); }); @@ -1138,8 +1140,15 @@ public abstract class AbsSwipeUpHandler, Q extends mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED); } }); - windowAnim.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim); + animatorSet.play(windowAnim); + if (mRecentsView != null && mDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get() + && mGestureState.getEndTarget() == RECENTS) { + animatorSet.play(ObjectAnimator.ofFloat(mRecentsView, RECENTS_GRID_PROGRESS, 1)); + animatorSet.play(mTaskViewSimulator.gridProgress.animateToValue(0, 1)); + } + animatorSet.setDuration(duration).setInterpolator(interpolator); + animatorSet.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animatorSet); } } @@ -1761,7 +1770,7 @@ public abstract class AbsSwipeUpHandler, Q extends mTaskViewSimulator.getCurrentRect(), mTaskViewSimulator.getCurrentCornerRadius()); LiveTileOverlay.INSTANCE.setRotation( - mRecentsView.getPagedViewOrientedState().getDisplayRotation()); + mTaskViewSimulator.getOrientationState().getDisplayRotation()); } ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate(); } diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 25c0928e8d..cd4f52f576 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -31,6 +31,7 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; @@ -307,7 +308,11 @@ public final class TaskViewUtils { Animator launcherAnim; final AnimatorListenerAdapter windowAnimEndListener; if (launcherClosing) { - launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); + Context context = v.getContext(); + DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile(); + launcherAnim = dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get() + ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0) + : recentsView.createAdjacentPageAnimForTaskLaunch(taskView); launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index edce1946eb..8c98480963 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -15,6 +15,7 @@ */ package com.android.quickstep.util; +import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.states.RotationHelper.deltaRotation; import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE; @@ -24,6 +25,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_ import android.animation.TimeInterpolator; import android.content.Context; +import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; @@ -34,6 +36,7 @@ import android.util.IntProperty; import androidx.annotation.NonNull; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; @@ -73,6 +76,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { @NonNull private RecentsOrientedState mOrientationState; + private final boolean mIsRecentsRtl; private final Rect mTaskRect = new Rect(); private boolean mDrawsBelowRecents; @@ -98,12 +102,15 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { public final AnimatedFloat recentsViewScale = new AnimatedFloat(); public final AnimatedFloat fullScreenProgress = new AnimatedFloat(); public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat(); + public final AnimatedFloat gridProgress = new AnimatedFloat(); private final ScrollState mScrollState = new ScrollState(); // Cached calculations private boolean mLayoutValid = false; private boolean mScrollValid = false; private int mOrientationStateId; + private final int mTaskThumbnailPadding; + private final int mRowSpacing; public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) { mContext = context; @@ -113,6 +120,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationState.setGestureActive(true); mCurrentFullscreenParams = new FullscreenDrawParams(context); mOrientationStateId = mOrientationState.getStateId(); + Resources resources = context.getResources(); + mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources); + mTaskThumbnailPadding = (int) resources.getDimension(R.dimen.task_thumbnail_top_margin); + mRowSpacing = (int) resources.getDimension(R.dimen.recents_row_spacing); } /** @@ -277,9 +288,10 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mScrollState.updateInterpolation(mDp, start); } - float progress = Utilities.boundToRange(fullScreenProgress.value, 0, 1); + float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1); mCurrentFullscreenParams.setProgress( - progress, recentsViewScale.value, mTaskRect.width(), mDp, mPositionHelper); + fullScreenProgress, recentsViewScale.value, mTaskRect.width(), mDp, + mPositionHelper); // Apply thumbnail matrix RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets; @@ -291,6 +303,23 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mMatrix.postTranslate(insets.left, insets.top); mMatrix.postScale(scale, scale); + float interpolatedGridProgress = ACCEL_DEACCEL.getInterpolation(gridProgress.value); + + // Apply TaskView matrix: gridProgress + final int boxLength = (int) Math.max(taskWidth, taskHeight); + float availableHeight = + mTaskThumbnailPadding + taskHeight + mSizeStrategy.getOverviewActionsHeight( + mContext); + float rowHeight = (availableHeight - mRowSpacing) / 2; + float gridScale = rowHeight / (boxLength + mTaskThumbnailPadding); + scale = Utilities.mapRange(interpolatedGridProgress, 1f, gridScale); + mMatrix.postScale(scale, scale, mIsRecentsRtl ? 0 : taskWidth, 0); + float taskWidthDiff = taskWidth * (1 - gridScale); + float taskWidthOffset = mIsRecentsRtl ? taskWidthDiff : -taskWidthDiff; + float translationPrimary = Utilities.mapRange(interpolatedGridProgress, 0, taskWidthOffset); + mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE, + translationPrimary); + // Apply TaskView matrix: translate, scroll mMatrix.postTranslate(mTaskRect.left, mTaskRect.top); mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE, diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java index 0837300a22..9af4d3054c 100644 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java @@ -43,9 +43,12 @@ public class ClearAllButton extends Button implements PageCallbacks { private float mScrollAlpha = 1; private float mContentAlpha = 1; private float mVisibilityAlpha = 1; + private float mGridProgress = 1; private boolean mIsRtl; private final float mOriginalTranslationX, mOriginalTranslationY; + private float mNormalTranslationPrimary; + private float mGridTranslationPrimary; private int mScrollOffset; @@ -100,10 +103,18 @@ public class ClearAllButton extends Button implements PageCallbacks { return; } - float shift = Math.min(scrollState.scrollFromEdge, orientationSize); - float translation = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift); - orientationHandler.setPrimaryAndResetSecondaryTranslate( - this, translation, mOriginalTranslationX, mOriginalTranslationY); + float shift; + if (mIsRtl) { + shift = Math.min(scrollState.scrollFromEdge, orientationSize); + } else { + shift = Math.min(scrollState.scrollFromEdge, + orientationSize + getGridTrans(mGridTranslationPrimary)) + - getGridTrans(mGridTranslationPrimary); + } + mNormalTranslationPrimary = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift); + applyPrimaryTranslation(); + orientationHandler.getSecondaryViewTranslate().set(this, + orientationHandler.getSecondaryValue(mOriginalTranslationX, mOriginalTranslationY)); mScrollAlpha = 1 - shift / orientationSize; updateAlpha(); } @@ -111,6 +122,48 @@ public class ClearAllButton extends Button implements PageCallbacks { private void updateAlpha() { final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha; setAlpha(alpha); - setClickable(alpha == 1); + setClickable(Math.min(alpha, 1) == 1); + } + + public void setGridTranslationPrimary(float gridTranslationPrimary) { + mGridTranslationPrimary = gridTranslationPrimary; + applyPrimaryTranslation(); + } + + public float getScrollAdjustment() { + float scrollAdjustment = 0; + if (mGridProgress > 0) { + scrollAdjustment += mGridTranslationPrimary; + } + return scrollAdjustment; + } + + public float getOffsetAdjustment() { + return getScrollAdjustment(); + } + + /** + * Moves ClearAllButton between carousel and 2 row grid. + * + * @param gridProgress 0 = carousel; 1 = 2 row grid. + */ + public void setGridProgress(float gridProgress) { + mGridProgress = gridProgress; + applyPrimaryTranslation(); + } + + private void applyPrimaryTranslation() { + RecentsView recentsView = getRecentsView(); + if (recentsView == null) { + return; + } + + PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler(); + orientationHandler.getPrimaryViewTranslate().set(this, + mNormalTranslationPrimary + getGridTrans(mGridTranslationPrimary)); + } + + private float getGridTrans(float endTranslation) { + return mGridProgress > 0 ? endTranslation : 0; } } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 52a7466429..84218fd454 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -16,9 +16,9 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA; +import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN; @@ -169,7 +169,7 @@ public class LauncherRecentsView extends RecentsView if (enabled) { LauncherState state = mActivity.getStateManager().getState(); boolean hasClearAllButton = (state.getVisibleElements(mActivity) - & OVERVIEW_BUTTONS) != 0; + & CLEAR_ALL_BUTTON) != 0; setDisallowScrollToClearAll(!hasClearAllButton); } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index b72e05c80d..e20540c8fc 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -107,6 +107,7 @@ import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; import com.android.launcher3.util.DynamicResource; +import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.ResourceBasedOverride.Overrides; @@ -239,6 +240,19 @@ public abstract class RecentsView extends PagedView } }; + public static final FloatProperty RECENTS_GRID_PROGRESS = + new FloatProperty("recentsGrid") { + @Override + public void setValue(RecentsView view, float gridProgress) { + view.setGridProgress(gridProgress); + } + + @Override + public Float get(RecentsView view) { + return view.mGridProgress; + } + }; + protected final RecentsOrientedState mOrientationState; protected final BaseActivityInterface mSizeStrategy; protected RecentsAnimationController mRecentsAnimationController; @@ -264,6 +278,7 @@ public abstract class RecentsView extends PagedView private final float mFastFlingVelocity; private final RecentsModel mModel; private final int mTaskTopMargin; + private final int mRowSpacing; private final ClearAllButton mClearAllButton; private final Rect mClearAllButtonDeadZoneRect = new Rect(); private final Rect mTaskViewDeadZoneRect = new Rect(); @@ -285,6 +300,9 @@ public abstract class RecentsView extends PagedView private float mAdjacentPageOffset = 0; private float mTaskViewsSecondaryTranslation = 0; + // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid. + private float mGridProgress = 0; + private boolean mShowAsGrid; /** * TODO: Call reloadIdNeeded in onTaskStackChanged. @@ -450,6 +468,7 @@ public abstract class RecentsView extends PagedView setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); mTaskTopMargin = getResources() .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); + mRowSpacing = (int) getResources().getDimension(R.dimen.recents_row_spacing); mSquaredTouchSlop = squaredTouchSlop(context); mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents); @@ -479,6 +498,11 @@ public abstract class RecentsView extends PagedView mLiveTileTaskViewSimulator.recentsViewScale.value = 1; mLiveTileTaskViewSimulator.setOrientationState(mOrientationState); mLiveTileTaskViewSimulator.setDrawsBelowRecents(true); + + mShowAsGrid = + mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get(); + mActivity.addOnDeviceProfileChangeListener(newDp -> + mShowAsGrid = newDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()); } public OverScroller getScroller() { @@ -640,8 +664,22 @@ public abstract class RecentsView extends PagedView } public boolean isTaskViewVisible(TaskView tv) { - // For now, just check if it's the active task or an adjacent task - return Math.abs(indexOfChild(tv) - getNextPage()) <= 1; + if (mShowAsGrid) { + int screenStart = mOrientationHandler.getPrimaryScroll(this); + int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this); + return isTaskViewWithinBounds(tv, screenStart, screenEnd); + } else { + // For now, just check if it's the active task or an adjacent task + return Math.abs(indexOfChild(tv) - getNextPage()) <= 1; + } + } + + private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) { + int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(); + int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment()); + int taskEnd = taskStart + taskSize; + return (taskStart >= start && taskStart <= end) || (taskEnd >= start + && taskEnd <= end); } public TaskView getTaskView(int taskId) { @@ -703,10 +741,21 @@ public abstract class RecentsView extends PagedView public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); - TaskView taskView = getCurrentPageTaskView(); - if (taskView != null && taskView.offerTouchToChildren(ev)) { - // Keep consuming events to pass to delegate - return true; + if (mShowAsGrid) { + int taskCount = getTaskViewCount(); + for (int i = 0; i < taskCount; i++) { + TaskView taskView = getTaskViewAt(i); + if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) { + // Keep consuming events to pass to delegate + return true; + } + } + } else { + TaskView taskView = getCurrentPageTaskView(); + if (taskView != null && taskView.offerTouchToChildren(ev)) { + // Keep consuming events to pass to delegate + return true; + } } final int x = (int) ev.getX(); @@ -757,6 +806,11 @@ public abstract class RecentsView extends PagedView return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev); } + @Override + protected boolean snapToPageInFreeScroll() { + return !mShowAsGrid; + } + @Override protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) { // Enables swiping to the left or right only if the task overlay is not modal. @@ -808,8 +862,8 @@ public abstract class RecentsView extends PagedView final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); taskView.bind(task, mOrientationState); - taskView.updateTaskSize(!taskView.hasTaskId(mRunningTaskId)); } + updateTaskSize(); if (mNextPage == INVALID_PAGE) { // Set the current page to the running task, but not if settling on new task. @@ -872,8 +926,6 @@ public abstract class RecentsView extends PagedView // Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need // to reset the params after it settles in Overview from swipe up so that we don't // render with obsolete param values. - mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0; - mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0; mLiveTileTaskViewSimulator.fullScreenProgress.value = 0; mLiveTileTaskViewSimulator.recentsViewScale.value = 1; @@ -899,12 +951,8 @@ public abstract class RecentsView extends PagedView public void setFullscreenProgress(float fullscreenProgress) { mFullscreenProgress = fullscreenProgress; int taskCount = getTaskViewCount(); - float accumulatedTranslationX = 0; for (int i = 0; i < taskCount; i++) { - TaskView taskView = getTaskViewAt(i); - taskView.setFullscreenProgress(mFullscreenProgress); - taskView.setAccumulatedTranslationX(accumulatedTranslationX); - accumulatedTranslationX += taskView.getFullscreenTranslationX(); + getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress); } // Fade out the actions view quickly (0.1 range) @@ -941,11 +989,22 @@ public abstract class RecentsView extends PagedView dp.widthPx - mInsets.right - mTempRect.right, dp.heightPx - mInsets.bottom - mTempRect.bottom); // Force TaskView to update size from thumbnail + updateTaskSize(); + } + + /** + * Updates TaskView scaling and translation required to support variable width. + */ + private void updateTaskSize() { + float accumulatedTranslationX = 0; final int taskCount = getTaskViewCount(); for (int i = 0; i < taskCount; i++) { TaskView taskView = getTaskViewAt(i); - taskView.updateTaskSize(!taskView.hasTaskId(mRunningTaskId)); + taskView.updateTaskSize(); + taskView.setAccumulatedFullscreenTranslationX(accumulatedTranslationX); + accumulatedTranslationX += taskView.getFullscreenTranslationX(); } + updateGridProperties(); } public void getTaskSize(Rect outRect) { @@ -1010,6 +1069,31 @@ public abstract class RecentsView extends PagedView } } + @Override + protected int getDestinationPage(int scaledScroll) { + if (mGridProgress == 0) { + return super.getDestinationPage(scaledScroll); + } + + final int childCount = getChildCount(); + if (mPageScrolls == null || childCount != mPageScrolls.length) { + return -1; + } + + // When in grid, return the page which scroll is closest to screenStart instead of page + // nearest to center of screen. + int minDistanceFromScreenStart = Integer.MAX_VALUE; + int minDistanceFromScreenStartIndex = -1; + for (int i = 0; i < childCount; ++i) { + int distanceFromScreenStart = Math.abs(mPageScrolls[i] - scaledScroll); + if (distanceFromScreenStart < minDistanceFromScreenStart) { + minDistanceFromScreenStart = distanceFromScreenStart; + minDistanceFromScreenStartIndex = i; + } + } + return minDistanceFromScreenStartIndex; + } + /** * Iterates through all the tasks, and loads the associated task data for newly visible tasks, * and unloads the associated task data for tasks that are no longer visible. @@ -1021,17 +1105,35 @@ public abstract class RecentsView extends PagedView return; } - int centerPageIndex = getPageNearestToCenterOfScreen(); - int numChildren = getChildCount(); - int lower = Math.max(0, centerPageIndex - 2); - int upper = Math.min(centerPageIndex + 2, numChildren - 1); + int lower = 0; + int upper = 0; + int visibleStart = 0; + int visibleEnd = 0; + if (mShowAsGrid) { + int screenStart = mOrientationHandler.getPrimaryScroll(this); + int pageOrientedSize = mOrientationHandler.getMeasuredSize(this); + int halfScreenSize = pageOrientedSize / 2; + // Use +/- 50% screen width as visible area. + visibleStart = screenStart - halfScreenSize; + visibleEnd = screenStart + pageOrientedSize + halfScreenSize; + } else { + int centerPageIndex = getPageNearestToCenterOfScreen(); + int numChildren = getChildCount(); + lower = Math.max(0, centerPageIndex - 2); + upper = Math.min(centerPageIndex + 2, numChildren - 1); + } // Update the task data for the in/visible children for (int i = 0; i < getTaskViewCount(); i++) { TaskView taskView = getTaskViewAt(i); Task task = taskView.getTask(); int index = indexOfChild(taskView); - boolean visible = lower <= index && index <= upper; + boolean visible; + if (mShowAsGrid) { + visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd); + } else { + visible = lower <= index && index <= upper; + } if (visible) { if (task == mTmpRunningTask) { // Skip loading if this is the task that we are animating into @@ -1229,7 +1331,11 @@ public abstract class RecentsView extends PagedView } setRunningTaskHidden(false); animateUpRunningTaskIconScale(); - animateActionsViewIn(); + + // TODO: This should be tied to whether there is a focus app on overview. + if (!mShowAsGrid) { + animateActionsViewIn(); + } } /** @@ -1258,7 +1364,6 @@ public abstract class RecentsView extends PagedView // gesture and the task list is loaded and applied mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false); taskView.bind(mTmpRunningTask, mOrientationState); - taskView.updateTaskSize(false); // Measure and layout immediately so that the scroll values is updated instantly // as the user might be quick-switching @@ -1272,6 +1377,8 @@ public abstract class RecentsView extends PagedView setCurrentPage(getRunningTaskIndex()); setRunningTaskViewShowScreenshot(false); setRunningTaskHidden(runningTaskTileHidden); + // Update task size after setting current task. + updateTaskSize(); // Reload the task list mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); @@ -1380,6 +1487,166 @@ public abstract class RecentsView extends PagedView } } + /** + * Updates TaskView and ClearAllButton scaling and translation required to turn into grid + * layout. + * This method only calculates the potential position and depends on {@link #setGridProgress} to + * apply the actual scaling and translation. + */ + private void updateGridProperties() { + int taskCount = getTaskViewCount(); + if (taskCount == 0) { + return; + } + + final int boxLength = Math.max(mTaskWidth, mTaskHeight); + + float availableHeight = + mTaskTopMargin + mTaskHeight + mSizeStrategy.getOverviewActionsHeight(mContext); + float rowHeight = (availableHeight - mRowSpacing) / 2; + float gridScale = rowHeight / (boxLength + mTaskTopMargin); + + TaskView firstTask = getTaskViewAt(0); + float firstTaskWidthOffset; + if (mIsRtl) { + // Move the first task to the right edge. + firstTaskWidthOffset = mTaskWidth - firstTask.getLayoutParams().width * gridScale; + } else { + // Move the first task to the left edge. + firstTaskWidthOffset = -firstTask.getLayoutParams().width * (1 - gridScale); + } + + int topRowWidth = 0; + int bottomRowWidth = 0; + float topAccumulatedTranslationX = 0; + float bottomAccumulatedTranslationX = 0; + IntSet topSet = new IntSet(); + float[] gridTranslations = new float[taskCount]; + for (int i = 0; i < taskCount; i++) { + TaskView taskView = getTaskViewAt(i); + taskView.setGridScale(gridScale); + + float taskWidthDiff = mTaskWidth - taskView.getLayoutParams().width * gridScale; + float taskWidthOffset = mIsRtl ? taskWidthDiff : -taskWidthDiff; + // Visually we want to move all task by firstTaskWidthOffset, but calculate page scroll + // according to right edge (or left in nonRtl) of TaskView. + gridTranslations[i] = firstTaskWidthOffset - taskWidthOffset; + taskView.setGridOffsetTranslationX(taskWidthOffset); + + // Off-set gap due to task scaling. + float widthDiff = taskView.getLayoutParams().width * (1 - gridScale); + float gridScaleTranslationX = mIsRtl ? widthDiff : -widthDiff; + gridTranslations[i] += gridScaleTranslationX; + + // Visible offset caused by having scaling pivot on top-right. + taskView.setNonRtlVisibleOffset(mIsRtl ? 0 : widthDiff); + + if (topRowWidth <= bottomRowWidth) { + gridTranslations[i] += topAccumulatedTranslationX; + topRowWidth += taskView.getLayoutParams().width * gridScale + mPageSpacing; + topSet.add(i); + + taskView.setGridTranslationY(0); + + // Move horizontally into empty space. + float widthOffset = 0; + for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) { + widthOffset += getTaskViewAt(j).getLayoutParams().width * gridScale + + mPageSpacing; + } + + float gridTranslationX = mIsRtl ? widthOffset : -widthOffset; + gridTranslations[i] += gridTranslationX; + topAccumulatedTranslationX += gridTranslationX + gridScaleTranslationX; + bottomAccumulatedTranslationX += gridScaleTranslationX; + } else { + gridTranslations[i] += bottomAccumulatedTranslationX; + bottomRowWidth += taskView.getLayoutParams().width * gridScale + mPageSpacing; + + // Move into bottom row. + float heightOffset = (boxLength + mTaskTopMargin) * gridScale + mRowSpacing; + taskView.setGridTranslationY(heightOffset); + + // Move horizontally into empty space. + float widthOffset = 0; + for (int j = i - 1; topSet.contains(j); j--) { + widthOffset += getTaskViewAt(j).getLayoutParams().width * gridScale + + mPageSpacing; + } + + float gridTranslationX = mIsRtl ? widthOffset : -widthOffset; + gridTranslations[i] += gridTranslationX; + topAccumulatedTranslationX += gridScaleTranslationX; + bottomAccumulatedTranslationX += gridTranslationX + gridScaleTranslationX; + } + } + + // Use the accumulated translation of the longer row. + float clearAllAccumulatedTranslation = mIsRtl ? Math.max(topAccumulatedTranslationX, + bottomAccumulatedTranslationX) : Math.min(topAccumulatedTranslationX, + bottomAccumulatedTranslationX); + + // If the last task is on the shorter row, ClearAllButton will embed into the shorter row + // which is not what we want. Compensate the width difference of the 2 rows in that case. + float shorterRowCompensation = 0; + if (topRowWidth <= bottomRowWidth) { + if (topSet.contains(taskCount - 1)) { + shorterRowCompensation = bottomRowWidth - topRowWidth; + } + } else { + if (!topSet.contains(taskCount - 1)) { + shorterRowCompensation = topRowWidth - bottomRowWidth; + } + } + float clearAllShorterRowCompensation = + mIsRtl ? -shorterRowCompensation : shorterRowCompensation; + + // If the total width is shorter than one task's width, move ClearAllButton further away + // accordingly. + float clearAllShortTotalCompensation = 0; + float longRowWidth = Math.max(topRowWidth, bottomRowWidth); + if (longRowWidth < mTaskWidth) { + float shortTotalCompensation = mTaskWidth - longRowWidth; + clearAllShortTotalCompensation = + mIsRtl ? -shortTotalCompensation : shortTotalCompensation; + } + + float clearAllTotalTranslationX = firstTaskWidthOffset + clearAllAccumulatedTranslation + + clearAllShorterRowCompensation + clearAllShortTotalCompensation; + + // We need to maintain first task's grid translation at 0, now shift translation of all + // the TaskViews to achieve that. + for (int i = 0; i < taskCount; i++) { + getTaskViewAt(i).setGridTranslationX(gridTranslations[i] - gridTranslations[0]); + } + mClearAllButton.setGridTranslationPrimary(clearAllTotalTranslationX - gridTranslations[0]); + + setGridProgress(mGridProgress); + } + + /** + * Moves TaskView and ClearAllButton between carousel and 2 row grid. + * + * @param gridProgress 0 = carousel; 1 = 2 row grid. + */ + public void setGridProgress(float gridProgress) { + int taskCount = getTaskViewCount(); + if (taskCount == 0) { + return; + } + + if (!mShowAsGrid) { + gridProgress = 0; + } + + mGridProgress = gridProgress; + + for (int i = 0; i < taskCount; i++) { + getTaskViewAt(i).setGridProgress(gridProgress); + } + mClearAllButton.setGridProgress(gridProgress); + } + private void enableLayoutTransitions() { if (mLayoutTransition == null) { mLayoutTransition = new LayoutTransition(); @@ -1515,7 +1782,7 @@ public abstract class RecentsView extends PagedView if (animateTaskView) { addDismissedTaskAnimations(taskView, duration, anim); } - } else { + } else if (!mShowAsGrid) { // Don't animate other tasks when dismissing in grid for now // If we just take newScroll - oldScroll, everything to the right of dragged task // translates to the left. We need to offset this in some cases: // - In RTL, add page offset to all pages, since we want pages to move to the right @@ -1608,6 +1875,8 @@ public abstract class RecentsView extends PagedView startHome(); } else { snapToPageImmediately(pageToSnapTo); + // Grid got messed up, reapply. + updateGridProperties(); } // Update the layout synchronously so that the position of next view is // immediately available. @@ -2383,17 +2652,16 @@ public abstract class RecentsView extends PagedView boolean pageScrollChanged = super.getPageScrolls(outPageScrolls, layoutChildren, scrollLogic); - final int taskCount = getTaskViewCount(); final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { - if (childCount < mTaskViewStartIndex) { - continue; + View child = getChildAt(i); + float scrollDiff = 0; + if (child instanceof TaskView) { + scrollDiff = ((TaskView) child).getScrollAdjustment(); + } else if (child instanceof ClearAllButton) { + scrollDiff = ((ClearAllButton) child).getScrollAdjustment(); } - final TaskView taskView = getTaskViewAt( - Utilities.boundToRange(i, mTaskViewStartIndex, taskCount - 1)); - float scrollDiff = - taskView.getFullscreenTranslationX() + taskView.getAccumulatedTranslationX(); if (scrollDiff != 0) { outPageScrolls[i] += scrollDiff; pageScrollChanged = true; @@ -2404,14 +2672,14 @@ public abstract class RecentsView extends PagedView @Override protected int getChildOffset(int index) { - if (index < mTaskViewStartIndex) { - return super.getChildOffset(index); + int childOffset = super.getChildOffset(index); + View child = getChildAt(index); + if (child instanceof TaskView) { + childOffset += ((TaskView) child).getOffsetAdjustment(); + } else if (child instanceof ClearAllButton) { + childOffset += ((ClearAllButton) child).getOffsetAdjustment(); } - - final TaskView taskView = getTaskViewAt( - Utilities.boundToRange(index, mTaskViewStartIndex, getTaskViewCount() - 1)); - return super.getChildOffset(index) + (int) taskView.getFullscreenTranslationX() - + (int) taskView.getAccumulatedTranslationX(); + return childOffset; } @Override @@ -2420,7 +2688,7 @@ public abstract class RecentsView extends PagedView if (taskView == null) { return super.getChildVisibleSize(index); } - return super.getChildVisibleSize(index) - (int) taskView.getFullscreenTranslationX(); + return (int) (super.getChildVisibleSize(index) * taskView.getSizeAdjustment()); } @Override diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java index e21bf7629e..f477ca7ea9 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -38,6 +38,7 @@ import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.Themes; @@ -113,7 +114,12 @@ public class TaskMenuView extends AbstractFloatingView { // NOTE: Changing the pivots means the rotated view gets rotated about the new pivots set, // which would render the X and Y position set here incorrect setPivotX(0); - setPivotY(0); + if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) { + // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment. + setPivotY(-mThumbnailTopMargin); + } else { + setPivotY(0); + } setRotation(pagedOrientationHandler.getDegreesRotated()); setX(pagedOrientationHandler.getTaskMenuX(x, mTaskView.getThumbnail())); setY(pagedOrientationHandler.getTaskMenuY(adjustedY, mTaskView.getThumbnail())); diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index dfbe6cea59..20a8444073 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -248,8 +248,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private IconView mIconView; private final DigitalWellBeingToast mDigitalWellBeingToast; private float mFullscreenProgress; - private float mScaleAtFullscreen = 1; + private float mGridProgress; private float mFullscreenScale = 1; + private float mGridScale = 1; private final FullscreenDrawParams mCurrentFullscreenParams; private final StatefulActivity mActivity; @@ -262,8 +263,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mTaskResistanceTranslationY; // The following translation variables should only be used in the same orientation as Launcher. private float mFullscreenTranslationX; - private float mAccumulatedTranslationX; + private float mAccumulatedFullscreenTranslationX; private float mBoxTranslationY; + // The following grid translations scales with mGridProgress. + private float mGridTranslationX; + private float mGridTranslationY; + // Offset translation does not affect scroll calculation. + private float mGridOffsetTranslationX; + private float mNonRtlVisibleOffset; private ObjectAnimator mIconAndDimAnimator; private float mIconScaleAnimStartProgress = 0; @@ -722,7 +729,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { @Override public void onRecycle() { - mFullscreenTranslationX = mAccumulatedTranslationX = mBoxTranslationY = 0f; + mFullscreenTranslationX = mAccumulatedFullscreenTranslationX = mGridTranslationX = + mGridTranslationY = + mGridOffsetTranslationX = mBoxTranslationY = mNonRtlVisibleOffset = 0f; resetViewTransforms(); // Clear any references to the thumbnail (it will be re-read either from the cache or the // system on next bind) @@ -807,7 +816,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { super.onLayout(changed, left, top, right, bottom); if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) { setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? (right - left) : 0); - setPivotY(0); + setPivotY(mSnapshotView.getTop()); } else { setPivotX((right - left) * 0.5f); setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f); @@ -834,9 +843,31 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { applyScale(); } + public void setGridScale(float gridScale) { + mGridScale = gridScale; + applyScale(); + } + + /** + * Moves TaskView between carousel and 2 row grid. + * + * @param gridProgress 0 = carousel; 1 = 2 row grid. + */ + public void setGridProgress(float gridProgress) { + mGridProgress = gridProgress; + applyTranslationX(); + applyTranslationY(); + applyScale(); + } + private void applyScale() { - setScaleX(mFullscreenScale); - setScaleY(mFullscreenScale); + float scale = 1; + float fullScreenProgress = EXAGGERATED_EASE.getInterpolation(mFullscreenProgress); + scale *= Utilities.mapRange(fullScreenProgress, 1f, mFullscreenScale); + float gridProgress = ACCEL_DEACCEL.getInterpolation(mGridProgress); + scale *= Utilities.mapRange(gridProgress, 1f, mGridScale); + setScaleX(scale); + setScaleY(scale); } private void setDismissTranslationX(float x) { @@ -878,13 +909,58 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { return mFullscreenTranslationX; } - public void setAccumulatedTranslationX(float accumulatedTranslationX) { - mAccumulatedTranslationX = accumulatedTranslationX; + public void setAccumulatedFullscreenTranslationX(float accumulatedFullscreenTranslationX) { + mAccumulatedFullscreenTranslationX = accumulatedFullscreenTranslationX; applyTranslationX(); } - public float getAccumulatedTranslationX() { - return mAccumulatedTranslationX; + public void setGridTranslationX(float gridTranslationX) { + mGridTranslationX = gridTranslationX; + applyTranslationX(); + } + + public void setGridTranslationY(float gridTranslationY) { + mGridTranslationY = gridTranslationY; + applyTranslationY(); + } + + public void setGridOffsetTranslationX(float gridOffsetTranslationX) { + mGridOffsetTranslationX = gridOffsetTranslationX; + applyTranslationX(); + } + + public void setNonRtlVisibleOffset(float nonRtlVisibleOffset) { + mNonRtlVisibleOffset = nonRtlVisibleOffset; + } + + public float getScrollAdjustment() { + float scrollAdjustment = 0; + if (mFullscreenProgress > 0) { + scrollAdjustment += mFullscreenTranslationX + mAccumulatedFullscreenTranslationX; + } + if (mGridProgress > 0) { + scrollAdjustment += mGridTranslationX; + } + return scrollAdjustment; + } + + public float getOffsetAdjustment() { + float offsetAdjustment = getScrollAdjustment(); + if (mGridProgress > 0) { + offsetAdjustment += mGridOffsetTranslationX + mNonRtlVisibleOffset; + } + return offsetAdjustment; + } + + public float getSizeAdjustment() { + float sizeAdjustment = 1; + if (mFullscreenProgress > 0) { + sizeAdjustment *= mFullscreenScale; + } + if (mGridProgress > 0) { + sizeAdjustment *= mGridScale; + } + return sizeAdjustment; } private void setBoxTranslationY(float boxTranslationY) { @@ -893,15 +969,20 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } private void applyTranslationX() { - setTranslationX( - mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX - + mFullscreenTranslationX + mAccumulatedTranslationX); + setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX + + getFullscreenTrans(mFullscreenTranslationX + mAccumulatedFullscreenTranslationX) + + getGridTrans(mGridTranslationX + mGridOffsetTranslationX)); } private void applyTranslationY() { setTranslationY( mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY - + mBoxTranslationY); + + getGridTrans(mGridTranslationY) + mBoxTranslationY); + } + + private float getGridTrans(float endTranslation) { + float progress = ACCEL_DEACCEL.getInterpolation(mGridProgress); + return Utilities.mapRange(progress, 0, endTranslation); } public FloatProperty getFillDismissGapTranslationProperty() { @@ -1057,7 +1138,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE); getThumbnail().getTaskOverlay().setFullscreenProgress(progress); - updateTaskScaling(); + applyTranslationX(); + applyTranslationY(); + applyScale(); TaskThumbnailView thumbnail = getThumbnail(); updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper()); @@ -1084,41 +1167,53 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { previewPositionHelper); } - void updateTaskSize(boolean variableWidth) { + /** + * Updates TaskView scaling and translation required to support variable width if enabled, while + * ensuring TaskView fits into screen in fullscreen. + */ + void updateTaskSize() { ViewGroup.LayoutParams params = getLayoutParams(); - float thumbnailRatio = mTask != null ? mTask.getVisibleThumbnailRatio() : 0f; - if (variableWidth && mActivity.getDeviceProfile().isTablet - && FeatureFlags.ENABLE_OVERVIEW_GRID.get() && thumbnailRatio != 0f) { + if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) { final int thumbnailPadding = (int) getResources().getDimension( R.dimen.task_thumbnail_top_margin); Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize(); int taskWidth = lastComputedTaskSize.width(); int taskHeight = lastComputedTaskSize.height(); - int boxLength = Math.max(taskWidth, taskHeight); int expectedWidth; int expectedHeight; - if (thumbnailRatio > 1) { - expectedWidth = boxLength; - expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding; + float thumbnailRatio = mTask != null ? mTask.getVisibleThumbnailRatio() : 0f; + if (isRunningTask() || thumbnailRatio == 0f) { + expectedWidth = taskWidth; + expectedHeight = taskHeight + thumbnailPadding; } else { - expectedWidth = (int) (boxLength * thumbnailRatio); - expectedHeight = boxLength + thumbnailPadding; + int boxLength = Math.max(taskWidth, taskHeight); + if (thumbnailRatio > 1) { + expectedWidth = boxLength; + expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding; + } else { + expectedWidth = (int) (boxLength * thumbnailRatio); + expectedHeight = boxLength + thumbnailPadding; + } } float heightDiff = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f; setBoxTranslationY(heightDiff); + float fullscreenScale = 1f; if (expectedWidth > taskWidth) { // In full screen, expectedWidth should not be larger than taskWidth. - mScaleAtFullscreen = taskWidth / (float) expectedWidth; + fullscreenScale = taskWidth / (float) expectedWidth; } else if (expectedHeight - thumbnailPadding > taskHeight) { // In full screen, expectedHeight should not be larger than taskHeight. - mScaleAtFullscreen = taskHeight / (float) (expectedHeight - thumbnailPadding); - } else { - mScaleAtFullscreen = 1f; + fullscreenScale = taskHeight / (float) (expectedHeight - thumbnailPadding); } + setFullscreenScale(fullscreenScale); + + float widthDiff = params.width * (1 - mFullscreenScale); + setFullscreenTranslationX( + getLayoutDirection() == LAYOUT_DIRECTION_RTL ? -widthDiff : widthDiff); if (params.width != expectedWidth || params.height != expectedHeight) { params.width = expectedWidth; @@ -1127,35 +1222,16 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } else { setBoxTranslationY(0); + setFullscreenTranslationX(0); + setFullscreenScale(1); if (params.width != ViewGroup.LayoutParams.MATCH_PARENT) { params.width = ViewGroup.LayoutParams.MATCH_PARENT; params.height = ViewGroup.LayoutParams.MATCH_PARENT; setLayoutParams(params); } } - updateTaskScaling(); } - private void updateTaskScaling() { - if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) { - ViewGroup.LayoutParams params = getLayoutParams(); - if (params.width == ViewGroup.LayoutParams.MATCH_PARENT - || params.height == ViewGroup.LayoutParams.MATCH_PARENT) { - // Snapshot is not loaded yet, skip. - return; - } - - float progress = EXAGGERATED_EASE.getInterpolation(mFullscreenProgress); - setFullscreenScale(Utilities.mapRange(progress, 1f, mScaleAtFullscreen)); - - float widthDiff = params.width * (1 - mFullscreenScale); - setFullscreenTranslationX(getFullscreenTrans( - getLayoutDirection() == LAYOUT_DIRECTION_RTL ? -widthDiff : widthDiff)); - } else { - setFullscreenScale(1); - setFullscreenTranslationX(0); - } - } private float getFullscreenTrans(float endTranslation) { float progress = ACCEL_DEACCEL.getInterpolation(mFullscreenProgress); diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index f9a1ded355..781f171698 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -56,8 +56,9 @@ public abstract class LauncherState implements BaseState { public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions public static final int ALL_APPS_CONTENT = 1 << 4; public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5; - public static final int OVERVIEW_BUTTONS = 1 << 6; + public static final int OVERVIEW_ACTIONS = 1 << 6; public static final int TASKBAR = 1 << 7; + public static final int CLEAR_ALL_BUTTON = 1 << 8; /** Mask of all the items that are contained in the apps view. */ public static final int APPS_VIEW_ITEM_MASK = @@ -220,6 +221,13 @@ public abstract class LauncherState implements BaseState { return 0; } + /** + * For this state, whether tasks should layout as a grid rather than a list. + */ + public boolean displayOverviewTasksAsGrid(Launcher launcher) { + return false; + } + /** * The amount of blur and wallpaper zoom to apply to the background of either the app * or Launcher surface in this state. Should be a number between 0 and 1, inclusive. diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index af2d94a278..c6766a470c 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -219,7 +219,7 @@ public abstract class PagedView extends ViewGrou /** * Returns the index of the currently displayed page. When in free scroll mode, this is the page * that the user was on before entering free scroll mode (e.g. the home screen page they - * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()} + * long-pressed on to enter the overview). Try using {@link #getDestinationPage()} * to get the page the user is currently scrolling over. */ public int getCurrentPage() { @@ -1289,7 +1289,7 @@ public abstract class PagedView extends ViewGrou if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) || ((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) { mScroller.springBack(initialScroll, minScroll, maxScroll); - mNextPage = getPageNearestToCenterOfScreen(); + mNextPage = getDestinationPage(); } else { mScroller.setInterpolator(mDefaultInterpolator); mScroller.fling(initialScroll, -velocity, @@ -1297,11 +1297,12 @@ public abstract class PagedView extends ViewGrou Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR)); int finalPos = mScroller.getFinalPos(); - mNextPage = getPageNearestToCenterOfScreen(finalPos); + mNextPage = getDestinationPage(finalPos); int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1); int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0); - if (finalPos > minScroll && finalPos < maxScroll) { + if (snapToPageInFreeScroll() && finalPos > minScroll + && finalPos < maxScroll) { // If scrolling ends in the half of the added space that is closer to // the end, settle to the end. Otherwise snap to the nearest page. // If flinging past one of the ends, don't change the velocity as it @@ -1347,6 +1348,10 @@ public abstract class PagedView extends ViewGrou return true; } + protected boolean snapToPageInFreeScroll() { + return true; + } + protected boolean shouldFlingForVelocity(int velocity) { float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity; return Math.abs(velocity) > threshold; @@ -1452,6 +1457,14 @@ public abstract class PagedView extends ViewGrou } } + public int getDestinationPage() { + return getDestinationPage(mOrientationHandler.getPrimaryScroll(this)); + } + + protected int getDestinationPage(int scaledScroll) { + return getPageNearestToCenterOfScreen(scaledScroll); + } + public int getPageNearestToCenterOfScreen() { return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this)); } @@ -1487,7 +1500,7 @@ public abstract class PagedView extends ViewGrou } protected void snapToDestination() { - snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration()); + snapToPage(getDestinationPage(), getPageSnapDuration()); } protected boolean isInOverScroll() { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index aca3d3c3c4..981ead97a8 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -417,7 +417,7 @@ public class Workspace extends PagedView // widgets as they cannot be placed inside a folder. // Start at the current page and search right (on LTR) until finding a page with // enough space. Since an empty screen is the furthest right, a page must be found. - int currentPage = getPageNearestToCenterOfScreen(); + int currentPage = getDestinationPage(); for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) { CellLayout page = (CellLayout) getPageAt(pageIndex); if (page.hasReorderSolution(dragObject.dragInfo)) { diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index f05f15e01d..8a64f3d7b5 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -146,13 +146,6 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { return VIEW_TRANSLATE_X; } - @Override - public void setPrimaryAndResetSecondaryTranslate( - View view, float translation, float defaultTranslationX, float defaultTranslationY) { - view.setTranslationX(defaultTranslationX); - view.setTranslationY(translation); - } - @Override public int getPrimaryScroll(View view) { return view.getScrollY(); diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index b9acfa3ca8..e1cec87df7 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -66,8 +66,6 @@ public interface PagedOrientationHandler { int getSecondaryDimension(View view); FloatProperty getPrimaryViewTranslate(); FloatProperty getSecondaryViewTranslate(); - void setPrimaryAndResetSecondaryTranslate( - View view, float translation, float defaultTranslationX, float defaultTranslationY); int getPrimaryScroll(View view); float getPrimaryScale(View view); int getChildStart(View view); diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 3663b5fcd3..bcaf5f444d 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -142,13 +142,6 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { return VIEW_TRANSLATE_Y; } - @Override - public void setPrimaryAndResetSecondaryTranslate( - View view, float translation, float defaultTranslationX, float defaultTranslationY) { - view.setTranslationX(translation); - view.setTranslationY(defaultTranslationY); - } - @Override public int getPrimaryScroll(View view) { return view.getScrollX();