Loading quickstep/res/values/dimens.xml +4 −1 Original line number Diff line number Diff line Loading @@ -32,8 +32,11 @@ <dimen name="overview_minimum_next_prev_size">50dp</dimen> <!-- Overview Task Views --> <!-- The primary task thumbnail uses up to this much of the total screen height/width --> <!-- The thumbnail uses up to this much of the total screen height/width in Overview --> <item name="overview_max_scale" format="float" type="dimen">0.7</item> <!-- The thumbnail should not go smaller than this much of the total screen height/width in tablet app to Overview carousel --> <item name="overview_carousel_min_scale" format="float" type="dimen">0.46</item> <!-- A touch target for icons, sometimes slightly larger than the icons themselves --> <dimen name="task_thumbnail_icon_size">48dp</dimen> <!-- The icon size for the focused task, placed in center of touch target --> Loading quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +5 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static com.android.launcher3.BaseActivity.EVENT_DESTROYED; import static com.android.launcher3.BaseActivity.EVENT_STARTED; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Flags.enableGridOnlyOverview; import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.launcher3.PagedView.INVALID_PAGE; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; Loading Loading @@ -2561,9 +2562,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } float scrollOffset = Math.abs(mRecentsView.getScrollOffset(mRecentsView.getCurrentPage())); Rect carouselTaskSize = enableGridOnlyOverview() ? mRecentsView.getLastComputedCarouselTaskSize() : mRecentsView.getLastComputedTaskSize(); int maxScrollOffset = mRecentsView.getPagedOrientationHandler().getPrimaryValue( mRecentsView.getLastComputedTaskSize().width(), mRecentsView.getLastComputedTaskSize().height()); carouselTaskSize.width(), carouselTaskSize.height()); maxScrollOffset += mRecentsView.getPageSpacing(); float maxScaleProgress = Loading quickstep/src/com/android/quickstep/BaseActivityInterface.java +19 −2 Original line number Diff line number Diff line Loading @@ -261,6 +261,23 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T } } /** * Calculates the taskView size for carousel during app to overview animation on tablets. */ public final void calculateCarouselTaskSize(Context context, DeviceProfile dp, Rect outRect, PagedOrientationHandler orientedState) { if (dp.isTablet && dp.isGestureMode) { Resources res = context.getResources(); float minScale = res.getFloat(R.dimen.overview_carousel_min_scale); Rect gridRect = new Rect(); calculateGridSize(dp, context, gridRect); calculateTaskSizeInternal(context, dp, gridRect, minScale, Gravity.CENTER | Gravity.TOP, outRect); } else { calculateTaskSize(context, dp, outRect, orientedState); } } private void calculateFocusTaskSize(Context context, DeviceProfile dp, Rect outRect) { Resources res = context.getResources(); float maxScale = res.getFloat(R.dimen.overview_max_scale); Loading @@ -286,13 +303,13 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T } private void calculateTaskSizeInternal(Context context, DeviceProfile dp, Rect potentialTaskRect, float maxScale, int gravity, Rect outRect) { Rect potentialTaskRect, float targetScale, int gravity, Rect outRect) { PointF taskDimension = getTaskDimension(context, dp); float scale = Math.min( potentialTaskRect.width() / taskDimension.x, potentialTaskRect.height() / taskDimension.y); scale = Math.min(scale, maxScale); scale = Math.min(scale, targetScale); int outWidth = Math.round(scale * taskDimension.x); int outHeight = Math.round(scale * taskDimension.y); Loading quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +7 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.quickstep.util; import static com.android.app.animation.Interpolators.DECELERATE; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Flags.enableGridOnlyOverview; import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; Loading Loading @@ -58,6 +59,7 @@ public class AnimatorControllerWithResistance { FROM_APP(0.75f, 0.5f, 1f, false), FROM_APP_TO_ALL_APPS(1f, 0.6f, 0.8f, false), FROM_APP_TABLET(1f, 0.7f, 1f, true), FROM_APP_TABLET_GRID_ONLY(1f, 1f, 1f, true), FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false), FROM_OVERVIEW(1f, 0.75f, 0.5f, false); Loading Loading @@ -239,10 +241,10 @@ public class AnimatorControllerWithResistance { float stopResist = params.resistanceParams.stopScalingAtTop ? 1f - startRect.top / endRectF.top : 1f; final TimeInterpolator scaleInterpolator = t -> { if (t < startResist) { if (t <= startResist) { return t; } if (t > stopResist) { if (t >= stopResist) { return maxResist; } float resistProgress = Utilities.getProgress(t, startResist, stopResist); Loading Loading @@ -304,6 +306,8 @@ public class AnimatorControllerWithResistance { resistanceParams = recentsOrientedState.getActivityInterface().allowAllAppsFromOverview() ? RecentsResistanceParams.FROM_APP_TO_ALL_APPS_TABLET : enableGridOnlyOverview() ? RecentsResistanceParams.FROM_APP_TABLET_GRID_ONLY : RecentsResistanceParams.FROM_APP_TABLET; } else { resistanceParams = Loading quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +96 −12 Original line number Diff line number Diff line Loading @@ -38,10 +38,12 @@ import android.graphics.Rect; import android.graphics.RectF; import android.util.Log; import android.view.RemoteAnimationTarget; import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; Loading Loading @@ -76,6 +78,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private final Rect mTaskRect = new Rect(); private final Rect mFullTaskSize = new Rect(); private final Rect mCarouselTaskSize = new Rect(); private PointF mPivotOverride = null; private final PointF mPivot = new PointF(); private DeviceProfile mDp; @StagePosition Loading @@ -95,6 +99,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat(); public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat(); // Carousel properties public final AnimatedFloat carouselScale = new AnimatedFloat(); public final AnimatedFloat carouselPrimaryTranslation = new AnimatedFloat(); public final AnimatedFloat carouselSecondaryTranslation = new AnimatedFloat(); // RecentsView properties public final AnimatedFloat recentsViewScale = new AnimatedFloat(); public final AnimatedFloat fullScreenProgress = new AnimatedFloat(); Loading @@ -109,9 +118,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private Boolean mDrawsBelowRecents = null; private boolean mIsGridTask; private boolean mIsDesktopTask; private boolean mScaleToCarouselTaskSize = false; private int mTaskRectTranslationX; private int mTaskRectTranslationY; private int mPivotOffsetX; public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) { mContext = context; Loading @@ -124,6 +133,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationStateId = mOrientationState.getStateId(); Resources resources = context.getResources(); mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources); carouselScale.value = 1f; } /** Loading @@ -149,6 +159,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationState.getOrientationHandler()); } if (enableGridOnlyOverview()) { mSizeStrategy.calculateCarouselTaskSize(mContext, mDp, mCarouselTaskSize, mOrientationState.getOrientationHandler()); } if (mSplitBounds != null) { // The task rect changes according to the staged split task sizes, but recents // fullscreen scale and pivot remains the same since the task fits into the existing Loading Loading @@ -193,9 +208,18 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { } // Copy mFullTaskSize instead of updating it directly so it could be reused next time // without recalculating Rect scaleRect = new Rect(mFullTaskSize); scaleRect.offset(mTaskRectTranslationX + mPivotOffsetX, mTaskRectTranslationY); return mOrientationState.getFullScreenScaleAndPivot(scaleRect, mDp, mPivot); Rect scaleRect = new Rect(); if (mScaleToCarouselTaskSize) { scaleRect.set(mCarouselTaskSize); } else { scaleRect.set(mFullTaskSize); } scaleRect.offset(mTaskRectTranslationX, mTaskRectTranslationY); float scale = mOrientationState.getFullScreenScaleAndPivot(scaleRect, mDp, mPivot); if (mPivotOverride != null) { mPivot.set(mPivotOverride); } return scale; } /** Loading Loading @@ -278,14 +302,64 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { /** * Adds animation for all the components corresponding to transition from an app to overview. */ public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) { public void addAppToOverviewAnim(PendingAnimation pa, Interpolator interpolator) { pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator); if (enableGridOnlyOverview() && mDp.isTablet) { int translationXToMiddle = mDp.widthPx / 2 - mFullTaskSize.centerX(); taskPrimaryTranslation.value = translationXToMiddle; mPivotOffsetX = translationXToMiddle; float fullScreenScale; if (enableGridOnlyOverview() && mDp.isTablet && mDp.isGestureMode) { // Move pivot to top right edge of the screen, to avoid task scaling down in opposite // direction of app window movement, otherwise the animation will wiggle left and right. // Also translate the app window to top right edge of the screen to simplify // calculations. taskPrimaryTranslation.value = mIsRecentsRtl ? mDp.widthPx - mFullTaskSize.right : -mFullTaskSize.left; taskSecondaryTranslation.value = -mFullTaskSize.top; mPivotOverride = new PointF(mIsRecentsRtl ? mDp.widthPx : 0, 0); // Scale down to the carousel and use the carousel Rect to calculate fullScreenScale. mScaleToCarouselTaskSize = true; carouselScale.value = mCarouselTaskSize.width() / (float) mFullTaskSize.width(); fullScreenScale = getFullScreenScale(); float carouselPrimaryTranslationTarget = mIsRecentsRtl ? mCarouselTaskSize.right - mDp.widthPx : mCarouselTaskSize.left; float carouselSecondaryTranslationTarget = mCarouselTaskSize.top; // Expected carousel position's center is in the middle, and invariant of // recentsViewScale. float exceptedCarouselCenterX = mCarouselTaskSize.centerX(); // Animating carousel translations linearly will result in a curved path, therefore // we'll need to calculate the expected translation at each recentsView scale. Luckily // primary and secondary follow the same translation, and primary is used here due to // it being simpler. Interpolator carouselTranslationInterpolator = t -> { // recentsViewScale is calculated rather than using recentsViewScale.value, so that // this interpolator works independently even if recentsViewScale don't animate. float recentsViewScale = Utilities.mapToRange(t, 0, 1, fullScreenScale, 1, Interpolators.LINEAR); // Without the translation, the app window will animate from fullscreen into top // right corner. float expectedTaskCenterX = mIsRecentsRtl ? mDp.widthPx - mCarouselTaskSize.width() * recentsViewScale / 2f : mCarouselTaskSize.width() * recentsViewScale / 2f; // Calculate the expected translation, then work back the animatedFraction that // results in this value. float carouselPrimaryTranslation = (exceptedCarouselCenterX - expectedTaskCenterX) / recentsViewScale; return carouselPrimaryTranslation / carouselPrimaryTranslationTarget; }; // Use addAnimatedFloat so this animation can later be canceled and animate to a // different value in RecentsView.onPrepareGestureEndAnimation. pa.addAnimatedFloat(carouselPrimaryTranslation, 0, carouselPrimaryTranslationTarget, carouselTranslationInterpolator); pa.addAnimatedFloat(carouselSecondaryTranslation, 0, carouselSecondaryTranslationTarget, carouselTranslationInterpolator); } else { fullScreenScale = getFullScreenScale(); } pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator); pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, fullScreenScale, 1, interpolator); } /** Loading Loading @@ -382,7 +456,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1); mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value, /* taskViewScale= */1f); carouselScale.value); // Apply thumbnail matrix float taskWidth = mTaskRect.width(); Loading @@ -396,6 +470,13 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { taskPrimaryTranslation.value); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, taskSecondaryTranslation.value); mMatrix.postScale(carouselScale.value, carouselScale.value, mPivot.x, mPivot.y); mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, carouselPrimaryTranslation.value); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, carouselSecondaryTranslation.value); mOrientationState.getOrientationHandler().setPrimary( mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value); Loading @@ -420,15 +501,18 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { return; } Log.d(TAG, "progress: " + fullScreenProgress + " carouselScale: " + carouselScale.value + " recentsViewScale: " + recentsViewScale.value + " crop: " + mTmpCropRect + " radius: " + getCurrentCornerRadius() + " taskW: " + taskWidth + " H: " + taskHeight + " taskRect: " + mTaskRect + " taskPrimaryT: " + taskPrimaryTranslation.value + " taskSecondaryT: " + taskSecondaryTranslation.value + " carouselPrimaryT: " + carouselPrimaryTranslation.value + " carouselSecondaryT: " + carouselSecondaryTranslation.value + " recentsPrimaryT: " + recentsViewPrimaryTranslation.value + " recentsSecondaryT: " + recentsViewSecondaryTranslation.value + " taskSecondaryT: " + taskSecondaryTranslation.value + " recentsScroll: " + recentsViewScroll.value + " pivot: " + mPivot ); Loading Loading
quickstep/res/values/dimens.xml +4 −1 Original line number Diff line number Diff line Loading @@ -32,8 +32,11 @@ <dimen name="overview_minimum_next_prev_size">50dp</dimen> <!-- Overview Task Views --> <!-- The primary task thumbnail uses up to this much of the total screen height/width --> <!-- The thumbnail uses up to this much of the total screen height/width in Overview --> <item name="overview_max_scale" format="float" type="dimen">0.7</item> <!-- The thumbnail should not go smaller than this much of the total screen height/width in tablet app to Overview carousel --> <item name="overview_carousel_min_scale" format="float" type="dimen">0.46</item> <!-- A touch target for icons, sometimes slightly larger than the icons themselves --> <dimen name="task_thumbnail_icon_size">48dp</dimen> <!-- The icon size for the focused task, placed in center of touch target --> Loading
quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +5 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static com.android.launcher3.BaseActivity.EVENT_DESTROYED; import static com.android.launcher3.BaseActivity.EVENT_STARTED; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Flags.enableGridOnlyOverview; import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.launcher3.PagedView.INVALID_PAGE; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; Loading Loading @@ -2561,9 +2562,11 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>, } float scrollOffset = Math.abs(mRecentsView.getScrollOffset(mRecentsView.getCurrentPage())); Rect carouselTaskSize = enableGridOnlyOverview() ? mRecentsView.getLastComputedCarouselTaskSize() : mRecentsView.getLastComputedTaskSize(); int maxScrollOffset = mRecentsView.getPagedOrientationHandler().getPrimaryValue( mRecentsView.getLastComputedTaskSize().width(), mRecentsView.getLastComputedTaskSize().height()); carouselTaskSize.width(), carouselTaskSize.height()); maxScrollOffset += mRecentsView.getPageSpacing(); float maxScaleProgress = Loading
quickstep/src/com/android/quickstep/BaseActivityInterface.java +19 −2 Original line number Diff line number Diff line Loading @@ -261,6 +261,23 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T } } /** * Calculates the taskView size for carousel during app to overview animation on tablets. */ public final void calculateCarouselTaskSize(Context context, DeviceProfile dp, Rect outRect, PagedOrientationHandler orientedState) { if (dp.isTablet && dp.isGestureMode) { Resources res = context.getResources(); float minScale = res.getFloat(R.dimen.overview_carousel_min_scale); Rect gridRect = new Rect(); calculateGridSize(dp, context, gridRect); calculateTaskSizeInternal(context, dp, gridRect, minScale, Gravity.CENTER | Gravity.TOP, outRect); } else { calculateTaskSize(context, dp, outRect, orientedState); } } private void calculateFocusTaskSize(Context context, DeviceProfile dp, Rect outRect) { Resources res = context.getResources(); float maxScale = res.getFloat(R.dimen.overview_max_scale); Loading @@ -286,13 +303,13 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T } private void calculateTaskSizeInternal(Context context, DeviceProfile dp, Rect potentialTaskRect, float maxScale, int gravity, Rect outRect) { Rect potentialTaskRect, float targetScale, int gravity, Rect outRect) { PointF taskDimension = getTaskDimension(context, dp); float scale = Math.min( potentialTaskRect.width() / taskDimension.x, potentialTaskRect.height() / taskDimension.y); scale = Math.min(scale, maxScale); scale = Math.min(scale, targetScale); int outWidth = Math.round(scale * taskDimension.x); int outHeight = Math.round(scale * taskDimension.y); Loading
quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +7 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.quickstep.util; import static com.android.app.animation.Interpolators.DECELERATE; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Flags.enableGridOnlyOverview; import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; Loading Loading @@ -58,6 +59,7 @@ public class AnimatorControllerWithResistance { FROM_APP(0.75f, 0.5f, 1f, false), FROM_APP_TO_ALL_APPS(1f, 0.6f, 0.8f, false), FROM_APP_TABLET(1f, 0.7f, 1f, true), FROM_APP_TABLET_GRID_ONLY(1f, 1f, 1f, true), FROM_APP_TO_ALL_APPS_TABLET(1f, 0.5f, 0.5f, false), FROM_OVERVIEW(1f, 0.75f, 0.5f, false); Loading Loading @@ -239,10 +241,10 @@ public class AnimatorControllerWithResistance { float stopResist = params.resistanceParams.stopScalingAtTop ? 1f - startRect.top / endRectF.top : 1f; final TimeInterpolator scaleInterpolator = t -> { if (t < startResist) { if (t <= startResist) { return t; } if (t > stopResist) { if (t >= stopResist) { return maxResist; } float resistProgress = Utilities.getProgress(t, startResist, stopResist); Loading Loading @@ -304,6 +306,8 @@ public class AnimatorControllerWithResistance { resistanceParams = recentsOrientedState.getActivityInterface().allowAllAppsFromOverview() ? RecentsResistanceParams.FROM_APP_TO_ALL_APPS_TABLET : enableGridOnlyOverview() ? RecentsResistanceParams.FROM_APP_TABLET_GRID_ONLY : RecentsResistanceParams.FROM_APP_TABLET; } else { resistanceParams = Loading
quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +96 −12 Original line number Diff line number Diff line Loading @@ -38,10 +38,12 @@ import android.graphics.Rect; import android.graphics.RectF; import android.util.Log; import android.view.RemoteAnimationTarget; import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.app.animation.Interpolators; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; Loading Loading @@ -76,6 +78,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private final Rect mTaskRect = new Rect(); private final Rect mFullTaskSize = new Rect(); private final Rect mCarouselTaskSize = new Rect(); private PointF mPivotOverride = null; private final PointF mPivot = new PointF(); private DeviceProfile mDp; @StagePosition Loading @@ -95,6 +99,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat(); public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat(); // Carousel properties public final AnimatedFloat carouselScale = new AnimatedFloat(); public final AnimatedFloat carouselPrimaryTranslation = new AnimatedFloat(); public final AnimatedFloat carouselSecondaryTranslation = new AnimatedFloat(); // RecentsView properties public final AnimatedFloat recentsViewScale = new AnimatedFloat(); public final AnimatedFloat fullScreenProgress = new AnimatedFloat(); Loading @@ -109,9 +118,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private Boolean mDrawsBelowRecents = null; private boolean mIsGridTask; private boolean mIsDesktopTask; private boolean mScaleToCarouselTaskSize = false; private int mTaskRectTranslationX; private int mTaskRectTranslationY; private int mPivotOffsetX; public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) { mContext = context; Loading @@ -124,6 +133,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationStateId = mOrientationState.getStateId(); Resources resources = context.getResources(); mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources); carouselScale.value = 1f; } /** Loading @@ -149,6 +159,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mOrientationState.getOrientationHandler()); } if (enableGridOnlyOverview()) { mSizeStrategy.calculateCarouselTaskSize(mContext, mDp, mCarouselTaskSize, mOrientationState.getOrientationHandler()); } if (mSplitBounds != null) { // The task rect changes according to the staged split task sizes, but recents // fullscreen scale and pivot remains the same since the task fits into the existing Loading Loading @@ -193,9 +208,18 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { } // Copy mFullTaskSize instead of updating it directly so it could be reused next time // without recalculating Rect scaleRect = new Rect(mFullTaskSize); scaleRect.offset(mTaskRectTranslationX + mPivotOffsetX, mTaskRectTranslationY); return mOrientationState.getFullScreenScaleAndPivot(scaleRect, mDp, mPivot); Rect scaleRect = new Rect(); if (mScaleToCarouselTaskSize) { scaleRect.set(mCarouselTaskSize); } else { scaleRect.set(mFullTaskSize); } scaleRect.offset(mTaskRectTranslationX, mTaskRectTranslationY); float scale = mOrientationState.getFullScreenScaleAndPivot(scaleRect, mDp, mPivot); if (mPivotOverride != null) { mPivot.set(mPivotOverride); } return scale; } /** Loading Loading @@ -278,14 +302,64 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { /** * Adds animation for all the components corresponding to transition from an app to overview. */ public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) { public void addAppToOverviewAnim(PendingAnimation pa, Interpolator interpolator) { pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator); if (enableGridOnlyOverview() && mDp.isTablet) { int translationXToMiddle = mDp.widthPx / 2 - mFullTaskSize.centerX(); taskPrimaryTranslation.value = translationXToMiddle; mPivotOffsetX = translationXToMiddle; float fullScreenScale; if (enableGridOnlyOverview() && mDp.isTablet && mDp.isGestureMode) { // Move pivot to top right edge of the screen, to avoid task scaling down in opposite // direction of app window movement, otherwise the animation will wiggle left and right. // Also translate the app window to top right edge of the screen to simplify // calculations. taskPrimaryTranslation.value = mIsRecentsRtl ? mDp.widthPx - mFullTaskSize.right : -mFullTaskSize.left; taskSecondaryTranslation.value = -mFullTaskSize.top; mPivotOverride = new PointF(mIsRecentsRtl ? mDp.widthPx : 0, 0); // Scale down to the carousel and use the carousel Rect to calculate fullScreenScale. mScaleToCarouselTaskSize = true; carouselScale.value = mCarouselTaskSize.width() / (float) mFullTaskSize.width(); fullScreenScale = getFullScreenScale(); float carouselPrimaryTranslationTarget = mIsRecentsRtl ? mCarouselTaskSize.right - mDp.widthPx : mCarouselTaskSize.left; float carouselSecondaryTranslationTarget = mCarouselTaskSize.top; // Expected carousel position's center is in the middle, and invariant of // recentsViewScale. float exceptedCarouselCenterX = mCarouselTaskSize.centerX(); // Animating carousel translations linearly will result in a curved path, therefore // we'll need to calculate the expected translation at each recentsView scale. Luckily // primary and secondary follow the same translation, and primary is used here due to // it being simpler. Interpolator carouselTranslationInterpolator = t -> { // recentsViewScale is calculated rather than using recentsViewScale.value, so that // this interpolator works independently even if recentsViewScale don't animate. float recentsViewScale = Utilities.mapToRange(t, 0, 1, fullScreenScale, 1, Interpolators.LINEAR); // Without the translation, the app window will animate from fullscreen into top // right corner. float expectedTaskCenterX = mIsRecentsRtl ? mDp.widthPx - mCarouselTaskSize.width() * recentsViewScale / 2f : mCarouselTaskSize.width() * recentsViewScale / 2f; // Calculate the expected translation, then work back the animatedFraction that // results in this value. float carouselPrimaryTranslation = (exceptedCarouselCenterX - expectedTaskCenterX) / recentsViewScale; return carouselPrimaryTranslation / carouselPrimaryTranslationTarget; }; // Use addAnimatedFloat so this animation can later be canceled and animate to a // different value in RecentsView.onPrepareGestureEndAnimation. pa.addAnimatedFloat(carouselPrimaryTranslation, 0, carouselPrimaryTranslationTarget, carouselTranslationInterpolator); pa.addAnimatedFloat(carouselSecondaryTranslation, 0, carouselSecondaryTranslationTarget, carouselTranslationInterpolator); } else { fullScreenScale = getFullScreenScale(); } pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator); pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, fullScreenScale, 1, interpolator); } /** Loading Loading @@ -382,7 +456,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1); mCurrentFullscreenParams.setProgress(fullScreenProgress, recentsViewScale.value, /* taskViewScale= */1f); carouselScale.value); // Apply thumbnail matrix float taskWidth = mTaskRect.width(); Loading @@ -396,6 +470,13 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { taskPrimaryTranslation.value); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, taskSecondaryTranslation.value); mMatrix.postScale(carouselScale.value, carouselScale.value, mPivot.x, mPivot.y); mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE, carouselPrimaryTranslation.value); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, carouselSecondaryTranslation.value); mOrientationState.getOrientationHandler().setPrimary( mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value); Loading @@ -420,15 +501,18 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { return; } Log.d(TAG, "progress: " + fullScreenProgress + " carouselScale: " + carouselScale.value + " recentsViewScale: " + recentsViewScale.value + " crop: " + mTmpCropRect + " radius: " + getCurrentCornerRadius() + " taskW: " + taskWidth + " H: " + taskHeight + " taskRect: " + mTaskRect + " taskPrimaryT: " + taskPrimaryTranslation.value + " taskSecondaryT: " + taskSecondaryTranslation.value + " carouselPrimaryT: " + carouselPrimaryTranslation.value + " carouselSecondaryT: " + carouselSecondaryTranslation.value + " recentsPrimaryT: " + recentsViewPrimaryTranslation.value + " recentsSecondaryT: " + recentsViewSecondaryTranslation.value + " taskSecondaryT: " + taskSecondaryTranslation.value + " recentsScroll: " + recentsViewScroll.value + " pivot: " + mPivot ); Loading