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
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index e46eb9e..9773366 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -29,6 +29,7 @@
<dimen name="overview_actions_bottom_margin_three_button">8dp</dimen>
<dimen name="overview_actions_horizontal_margin">16dp</dimen>
+ <dimen name="recents_row_spacing">48dp</dimen>
<dimen name="recents_page_spacing">16dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index aad7e17..0f13ef9 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_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 @@
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 @@
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 5ccc1e8..c9de662 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 @@
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 2cf65af..2ad718b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -69,12 +69,18 @@
@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) {
// Translate hotseat offscreen if we show it in overview.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 41c689d..bdba482 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 @@
@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 b295e79..d480b6d 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.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 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return OVERVIEW_BUTTONS;
+ return displayOverviewTasksAsGrid(launcher) ? CLEAR_ALL_BUTTON
+ : CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
}
@Override
@@ -131,6 +133,11 @@
}
@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 df433f8..4766870 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 @@
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 @@
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 845699a..4df0f63 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 @@
* @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 5f8fc6d..1bd7fe6 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.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_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.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 @@
* 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 @@
duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
if (endTarget == RECENTS) {
- interpolator = OVERSHOOT_1_2;
+ interpolator = ACCEL_DEACCEL;
}
}
}
@@ -958,7 +960,7 @@
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 @@
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 @@
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 @@
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 25c0928..cd4f52f 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -31,6 +31,7 @@
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 @@
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 edce194..8c98480 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 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 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 @@
@NonNull
private RecentsOrientedState mOrientationState;
+ private final boolean mIsRecentsRtl;
private final Rect mTaskRect = new Rect();
private boolean mDrawsBelowRecents;
@@ -98,12 +102,15 @@
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 @@
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 @@
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 @@
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 0837300..9af4d30 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -43,9 +43,12 @@
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 @@
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 @@
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 52a7466..84218fd 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 @@
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 b72e05c..e20540c 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.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 static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
+ new FloatProperty<RecentsView>("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 @@
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 @@
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 @@
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 @@
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 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 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();
@@ -758,6 +807,11 @@
}
@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.
if (!isModal()) {
@@ -808,8 +862,8 @@
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 @@
// 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 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 @@
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 @@
}
}
+ @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 @@
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 @@
}
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 @@
// 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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 @@
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 @@
@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 @@
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 e21bf76..f477ca7 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.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 @@
// 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 dfbe6ce..20a8444 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -248,8 +248,9 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
}
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<TaskView> getFillDismissGapTranslationProperty() {
@@ -1057,7 +1138,9 @@
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
getThumbnail().getTaskOverlay().setFullscreenProgress(progress);
- updateTaskScaling();
+ applyTranslationX();
+ applyTranslationY();
+ applyScale();
TaskThumbnailView thumbnail = getThumbnail();
updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper());
@@ -1084,41 +1167,53 @@
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 @@
}
} 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 f9a1ded..781f171 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -56,8 +56,9 @@
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 =
@@ -221,6 +222,13 @@
}
/**
+ * 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 af2d94a..c6766a4 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -219,7 +219,7 @@
/**
* 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 @@
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 @@
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 @@
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 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 @@
}
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 aca3d3c..981ead9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -417,7 +417,7 @@
// 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 f05f15e..8a64f3d 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -147,13 +147,6 @@
}
@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 b9acfa3..e1cec87 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -66,8 +66,6 @@
int getSecondaryDimension(View view);
FloatProperty<View> getPrimaryViewTranslate();
FloatProperty<View> 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 3663b5f..bcaf5f4 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -143,13 +143,6 @@
}
@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();
}