diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index a0af79743b..131fcbfbf1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -17,7 +17,6 @@ package com.android.launcher3.uioverrides.states; import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; @@ -52,6 +51,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_S import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.animation.Animator; import android.animation.AnimatorSet; @@ -212,7 +212,7 @@ public class QuickstepAtomicAnimationFactory extends // Scale up the recents, if it is not coming from the side RecentsView overview = mActivity.getOverviewPanel(); if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { - SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); + RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); } } config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 1b439d1d31..821ada400e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -47,6 +46,7 @@ import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.animation.Animator; @@ -244,7 +244,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, final LauncherState toState = OVERVIEW; // Set RecentView's initial properties. - SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); + RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f); mRecentsView.setContentAlpha(1); mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress()); @@ -266,7 +266,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // - RecentsView scale // - RecentsView fullscreenProgress PendingAnimation yAnim = new PendingAnimation((long) (mYRange * 2)); - yAnim.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], SCALE_DOWN_INTERPOLATOR); + yAnim.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0], + SCALE_DOWN_INTERPOLATOR); yAnim.setFloat(mRecentsView, FULLSCREEN_PROGRESS, toState.getOverviewFullscreenProgress(), SCALE_DOWN_INTERPOLATOR); mYOverviewAnim = yAnim.createPlaybackController(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index 3f1e7bad9b..163c23268b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -15,7 +15,6 @@ */ package com.android.quickstep.fallback; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; @@ -25,6 +24,7 @@ import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVER 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.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; import com.android.launcher3.anim.PendingAnimation; @@ -82,7 +82,7 @@ public class FallbackRecentsStateController implements StateHandler extends PagedView } }; + /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */ + public static final FloatProperty RECENTS_SCALE_PROPERTY = + new FloatProperty("recentsScale") { + @Override + public void setValue(RecentsView view, float scale) { + view.setScaleX(scale); + view.setScaleY(scale); + view.mLastComputedTaskPushOutDistance = null; + view.updatePageOffsets(); + } + + @Override + public Float get(RecentsView view) { + return view.getScaleX(); + } + }; + protected RecentsOrientedState mOrientationState; protected final BaseActivityInterface mSizeStrategy; protected RecentsAnimationController mRecentsAnimationController; @@ -219,8 +236,12 @@ public abstract class RecentsView extends PagedView protected int mTaskHeight; protected final TransformParams mLiveTileParams = new TransformParams(); protected final TaskViewSimulator mLiveTileTaskViewSimulator; + protected final Rect mLastComputedTaskSize = new Rect(); + // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot. + protected Float mLastComputedTaskPushOutDistance = null; protected boolean mEnableDrawingLiveTile = false; protected final Rect mTempRect = new Rect(); + protected final RectF mTempRectF = new RectF(); private final PointF mTempPointF = new PointF(); private static final int DISMISS_TASK_DURATION = 300; @@ -887,6 +908,7 @@ public abstract class RecentsView extends PagedView public void getTaskSize(Rect outRect) { mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect, mOrientationHandler); + mLastComputedTaskSize.set(outRect); } /** Gets the task size for modal state. */ @@ -934,8 +956,8 @@ public abstract class RecentsView extends PagedView final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - mScrollState.updateInterpolation(mOrientationHandler.getChildStartWithTranslation(page), - mPageSpacing); + mScrollState.updateInterpolation( + mOrientationHandler.getChildStartWithTranslation(page)); ((PageCallbacks) page).onPageScroll(mScrollState); } } @@ -1357,10 +1379,14 @@ public abstract class RecentsView extends PagedView /** * Updates linearInterpolation for the provided child position */ - public void updateInterpolation(float childStart, int pageSpacing) { - float pageCenter = childStart + halfPageSize; + public void updateInterpolation(float childStart) { + float scaledHalfPageSize = halfPageSize / pageParentScale; + float pageCenter = childStart + scaledHalfPageSize; float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; + // How far the page has to move from the center to be offscreen, taking into account + // the EDGE_SCALE_DOWN_FACTOR that will be applied at that position. + float distanceToReachEdge = halfScreenSize + + scaledHalfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR); linearInterpolation = Math.min(1, Math.abs(distanceFromScreenCenter) / distanceToReachEdge); } @@ -1802,14 +1828,15 @@ public abstract class RecentsView extends PagedView setPivotX(mTempPointF.x); setPivotY(mTempPointF.y); setTaskModalness(mTaskModalness); + mLastComputedTaskPushOutDistance = null; updatePageOffsets(); setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_AUTO); } private void updatePageOffsets() { - float offset = mAdjacentPageOffset * getWidth(); - float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness) * getWidth(); + float offset = mAdjacentPageOffset; + float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness); if (mIsRtl) { offset = -offset; modalOffset = -modalOffset; @@ -1818,18 +1845,89 @@ public abstract class RecentsView extends PagedView TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden ? null : getTaskView(mRunningTaskId); - int midPoint = runningTask == null ? -1 : indexOfChild(runningTask); - int currentPage = getCurrentPage(); + int midpoint = runningTask == null ? -1 : indexOfChild(runningTask); + int modalMidpoint = getCurrentPage(); + + float midpointOffsetSize = 0; + float leftOffsetSize = midpoint - 1 >= 0 + ? -getOffsetSize(midpoint - 1, midpoint, offset) + : 0; + float rightOffsetSize = midpoint + 1 < count + ? getOffsetSize(midpoint + 1, midpoint, offset) + : 0; + + float modalMidpointOffsetSize = 0; + float modalLeftOffsetSize = modalMidpoint - 1 >= 0 + ? -getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset) + : 0; + float modalRightOffsetSize = modalMidpoint + 1 < count + ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset) + : 0; for (int i = 0; i < count; i++) { - float translation = i == midPoint ? 0 : (i < midPoint ? -offset : offset); - float modalTranslation = - i == currentPage ? 0 : (i < currentPage ? -modalOffset : modalOffset); + float translation = i == midpoint + ? midpointOffsetSize + : i < midpoint + ? leftOffsetSize + : rightOffsetSize; + float modalTranslation = i == modalMidpoint + ? modalMidpointOffsetSize + : i < modalMidpoint + ? modalLeftOffsetSize + : modalRightOffsetSize; getChildAt(i).setTranslationX(translation + modalTranslation); } updateCurveProperties(); } + /** + * Computes the distance to offset the given child such that it is completely offscreen when + * translating away from the given midpoint. + * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen. + */ + private float getOffsetSize(int childIndex, int midpointIndex, float offsetProgress) { + if (offsetProgress == 0) { + // Don't bother calculating everything below if we won't offset anyway. + return 0; + } + // First, get the position of the task relative to the midpoint. If there is no midpoint + // then we just use the normal (centered) task position. + mTempRectF.set(mLastComputedTaskSize); + RectF taskPosition = mTempRectF; + float desiredLeft = getWidth(); + float distanceToOffscreen = desiredLeft - taskPosition.left; + // Used to calculate the scale of the task view based on its new offset. + float centerToOffscreenProgress = Math.abs(offsetProgress); + if (midpointIndex > -1) { + // When there is a midpoint reference task, adjacent tasks have less distance to travel + // to reach offscreen. Offset the task position to the task's starting point. + View child = getChildAt(childIndex); + View midpointChild = getChildAt(midpointIndex); + int distanceFromMidpoint = Math.abs(mOrientationHandler.getChildStart(child) + - mOrientationHandler.getChildStart(midpointChild) + + getDisplacementFromScreenCenter(midpointIndex)); + taskPosition.offset(distanceFromMidpoint, 0); + centerToOffscreenProgress = Utilities.mapRange(centerToOffscreenProgress, + distanceFromMidpoint / distanceToOffscreen, 1); + } + // Find the task's scale based on its offscreen progress, then see how far it still needs to + // move to be completely offscreen. + Utilities.scaleRectFAboutCenter(taskPosition, + TaskView.getCurveScaleForInterpolation(centerToOffscreenProgress)); + distanceToOffscreen = desiredLeft - taskPosition.left; + // Finally, we need to account for RecentsView scale, because it moves tasks based on its + // pivot. To do this, we move the task position to where it would be offscreen at scale = 1 + // (computed above), then we apply the scale via getMatrix() to determine how much that + // moves the task from its desired position, and adjust the computed distance accordingly. + if (mLastComputedTaskPushOutDistance == null) { + taskPosition.offsetTo(desiredLeft, 0); + getMatrix().mapRect(taskPosition); + mLastComputedTaskPushOutDistance = (taskPosition.left - desiredLeft) / getScaleX(); + } + distanceToOffscreen -= mLastComputedTaskPushOutDistance; + return distanceToOffscreen * offsetProgress; + } + /** * TODO: Do not assume motion across X axis for adjacent page */ @@ -1928,7 +2026,7 @@ public abstract class RecentsView extends PagedView float toScale = getMaxScaleForFullScreen(); if (launchingCenterTask) { RecentsView recentsView = tv.getRecentsView(); - anim.play(ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, toScale)); + anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale)); anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1)); } else { // We are launching an adjacent task, so parallax the center and other adjacent task. diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index ec3a4905f5..6e120e8bd8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -16,7 +16,6 @@ package com.android.launcher3.uioverrides; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; @@ -29,6 +28,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_SCALE_PROPERTY; import android.util.FloatProperty; @@ -61,7 +61,7 @@ public abstract class BaseRecentsViewStateController @Override public void setState(@NonNull LauncherState state) { float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher); - SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]); + RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]); ADJACENT_PAGE_OFFSET.set(mRecentsView, scaleAndOffset[1]); getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); @@ -93,7 +93,7 @@ public abstract class BaseRecentsViewStateController void setStateWithAnimationInternal(@NonNull final LauncherState toState, @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) { float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher); - setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], + setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0], config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR)); setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR)); diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 0ae386f39a..9dc2132683 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -15,7 +15,6 @@ */ package com.android.quickstep; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -28,6 +27,7 @@ import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_REC import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.animation.Animator; import android.annotation.TargetApi; @@ -393,7 +393,7 @@ public abstract class BaseActivityInterface extends ViewGrou int minDistanceFromScreenCenterIndex = -1; final int childCount = getChildCount(); for (int i = 0; i < childCount; ++i) { - View layout = getPageAt(i); - int childSize = mOrientationHandler.getMeasuredSize(layout); - int halfChildSize = (childSize / 2); - int childCenter = getChildOffset(i) + halfChildSize; - int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); + int distanceFromScreenCenter = Math.abs( + getDisplacementFromScreenCenter(i, screenCenter)); if (distanceFromScreenCenter < minDistanceFromScreenCenter) { minDistanceFromScreenCenter = distanceFromScreenCenter; minDistanceFromScreenCenterIndex = i; @@ -1461,6 +1458,20 @@ public abstract class PagedView extends ViewGrou return minDistanceFromScreenCenterIndex; } + private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) { + View layout = getPageAt(childIndex); + int childSize = mOrientationHandler.getMeasuredSize(layout); + int halfChildSize = (childSize / 2); + int childCenter = getChildOffset(childIndex) + halfChildSize; + return childCenter - screenCenter; + } + + protected int getDisplacementFromScreenCenter(int childIndex) { + int pageOrientationSize = mOrientationHandler.getMeasuredSize(this); + int screenCenter = mOrientationHandler.getPrimaryScroll(this) + (pageOrientationSize / 2); + return getDisplacementFromScreenCenter(childIndex, screenCenter); + } + protected void snapToDestination() { snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration()); } diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index ac1ade260d..1aaa608657 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -72,6 +72,7 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { out.halfPageSize = view.getNormalChildHeight() / 2; out.halfScreenSize = view.getMeasuredHeight() / 2; out.screenCenter = insets.top + view.getPaddingTop() + out.scroll + out.halfPageSize; + out.pageParentScale = view.getScaleY(); } @Override diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index d4f5cba40e..f88cdb3b0d 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -109,6 +109,7 @@ public interface PagedOrientationHandler { public int halfPageSize; public int screenCenter; public int halfScreenSize; + public float pageParentScale; } class ChildBounds { diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 3341996383..f18b109515 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -70,6 +70,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { out.halfPageSize = view.getNormalChildWidth() / 2; out.halfScreenSize = view.getMeasuredWidth() / 2; out.screenCenter = insets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize; + out.pageParentScale = view.getScaleX(); } @Override