Loading quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +2 −17 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; Loading Loading @@ -79,7 +80,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; Loading Loading @@ -916,26 +916,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends isFling, isCancel); float endShift = endTarget.isLauncher ? 1 : 0; final float startShift; Interpolator interpolator = DEACCEL; if (!isFling) { long expectedDuration = Math.abs(Math.round((endShift - currentShift) * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); duration = Math.min(MAX_SWIPE_DURATION, expectedDuration); startShift = currentShift; interpolator = endTarget == RECENTS ? ACCEL_DEACCEL : DEACCEL; } else { startShift = Utilities.boundToRange(currentShift - velocity.y * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); if (mTransitionDragLength > 0) { if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) { Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams( startShift, endShift, endShift, endVelocity, mTransitionDragLength, mContext); endShift = overshoot.end; interpolator = overshoot.interpolator; duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION, MAX_SWIPE_DURATION); } else { float distanceToTravel = (endShift - currentShift) * mTransitionDragLength; // we want the page's snap velocity to approximately match the velocity at Loading @@ -943,13 +932,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends // derivative of the scroll interpolator at zero, ie. 2. long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y)); duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); if (endTarget == RECENTS) { interpolator = ACCEL_DEACCEL; } } } } Interpolator interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; if (endTarget.isLauncher) { mInputConsumerProxy.enable(); Loading quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +1 −7 Original line number Diff line number Diff line Loading @@ -94,13 +94,7 @@ public abstract class SwipeUpAnimationLogic { mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength( dp, mContext, TEMP_RECT, mTaskViewSimulator.getOrientationState().getOrientationHandler()); if (mDeviceState.isFullyGesturalNavMode()) { // We can drag all the way to the top of the screen. mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; } else { mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR; } PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR); Loading quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +26 −48 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.quickstep.util; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; Loading @@ -38,7 +37,6 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.views.RecentsView; /** Loading @@ -49,12 +47,6 @@ import com.android.quickstep.views.RecentsView; */ public class AnimatorControllerWithResistance { /** * How much farther we can drag past overview in 2-button mode, as a factor of the distance * it takes to drag from an app to overview. */ public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f; private enum RecentsResistanceParams { FROM_APP(0.75f, 0.5f, 1f), FROM_OVERVIEW(1f, 0.75f, 0.5f); Loading Loading @@ -161,12 +153,6 @@ public class AnimatorControllerWithResistance { LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect, orientationHandler); long distanceToCover = startRect.bottom; boolean isTwoButtonMode = SysUINavigationMode.getMode(params.context) == TWO_BUTTONS; if (isTwoButtonMode) { // We can only drag a small distance past overview, not to the top of the screen. distanceToCover = (long) ((params.dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR); } PendingAnimation resistAnim = params.resistAnim != null ? params.resistAnim : new PendingAnimation(distanceToCover * 2); Loading @@ -178,18 +164,13 @@ public class AnimatorControllerWithResistance { / (params.dp.heightPx - startRect.bottom); // This is what the scale would be at the end of the drag if we didn't apply resistance. float endScale = params.startScale - prevScaleRate * distanceToCover; final TimeInterpolator scaleInterpolator; if (isTwoButtonMode) { // We are bounded by the distance of the drag, so we don't need to apply resistance. scaleInterpolator = LINEAR; } else { // Create an interpolator that resists the scale so the scale doesn't get smaller than // RECENTS_SCALE_MAX_RESIST. float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist, params.startScale, endScale); float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist, params.startScale, endScale); scaleInterpolator = t -> { final TimeInterpolator scaleInterpolator = t -> { if (t < startResist) { return t; } Loading @@ -197,12 +178,10 @@ public class AnimatorControllerWithResistance { resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress); return startResist + resistProgress * (maxResist - startResist); }; } resistAnim.addFloat(params.scaleTarget, params.scaleProperty, params.startScale, endScale, scaleInterpolator); if (!isTwoButtonMode) { // Compute where the task view would be based on the end scale, if we didn't translate. // Compute where the task view would be based on the end scale. RectF endRectF = new RectF(startRect); Matrix temp = new Matrix(); temp.setScale(params.resistanceParams.scaleMaxResist, Loading @@ -214,7 +193,6 @@ public class AnimatorControllerWithResistance { * params.resistanceParams.translationFactor; resistAnim.addFloat(params.translationTarget, params.translationProperty, params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR); } return resistAnim; } Loading src/com/android/launcher3/anim/Interpolators.java +0 −78 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ package com.android.launcher3.anim; import static com.android.launcher3.util.DisplayController.getSingleFrameMs; import android.content.Context; import android.graphics.Path; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; Loading Loading @@ -67,9 +64,6 @@ public class Interpolators { */ public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1; private static final int MIN_SETTLE_DURATION = 200; private static final float OVERSHOOT_FACTOR = 0.9f; static { Path exaggeratedEase = new Path(); exaggeratedEase.moveTo(0, 0); Loading Loading @@ -175,76 +169,4 @@ public class Interpolators { float upperBound) { return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound); } /** * Computes parameters necessary for an overshoot effect. */ public static class OvershootParams { public Interpolator interpolator; public float start; public float end; public long duration; /** * Given the input params, sets OvershootParams variables to be used by the caller. * @param startProgress The progress from 0 to 1 that the overshoot starts from. * @param overshootPastProgress The progress from 0 to 1 where we overshoot past (should * either be equal to startProgress or endProgress, depending on if we want to * overshoot immediately or only once we reach the end). * @param endProgress The final progress from 0 to 1 that we will settle to. * @param velocityPxPerMs The initial velocity that causes this overshoot. * @param totalDistancePx The distance against which progress is calculated. */ public OvershootParams(float startProgress, float overshootPastProgress, float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) { velocityPxPerMs = Math.abs(velocityPxPerMs); overshootPastProgress = Math.max(overshootPastProgress, startProgress); start = startProgress; int startPx = (int) (start * totalDistancePx); // Overshoot by about half a frame. float overshootBy = OVERSHOOT_FACTOR * velocityPxPerMs * getSingleFrameMs(context) / totalDistancePx / 2; overshootBy = Utilities.boundToRange(overshootBy, 0.02f, 0.15f); end = overshootPastProgress + overshootBy; int endPx = (int) (end * totalDistancePx); int overshootDistance = endPx - startPx; // Calculate deceleration necessary to reach overshoot distance. // Formula: velocityFinal^2 = velocityInitial^2 + 2 * acceleration * distance // 0 = v^2 + 2ad (velocityFinal == 0) // a = v^2 / -2d float decelerationPxPerMs = velocityPxPerMs * velocityPxPerMs / (2 * overshootDistance); // Calculate time necessary to reach peak of overshoot. // Formula: acceleration = velocity / time // time = velocity / acceleration duration = (long) (velocityPxPerMs / decelerationPxPerMs); // Now that we're at the top of the overshoot, need to settle back to endProgress. float settleDistance = end - endProgress; int settleDistancePx = (int) (settleDistance * totalDistancePx); // Calculate time necessary for the settle. // Formula: distance = velocityInitial * time + 1/2 * acceleration * time^2 // d = 1/2at^2 (velocityInitial = 0, since we just stopped at the top) // t = sqrt(2d/a) // Above formula assumes constant acceleration. Since we use ACCEL_DEACCEL, we actually // have acceleration to halfway then deceleration the rest. So the formula becomes: // t = sqrt(d/a) * 2 (half the distance for accel, half for deaccel) long settleDuration = (long) Math.sqrt(settleDistancePx / decelerationPxPerMs) * 4; settleDuration = Math.max(MIN_SETTLE_DURATION, settleDuration); // How much of the animation to devote to playing the overshoot (the rest is for settle). float overshootFraction = (float) duration / (duration + settleDuration); duration += settleDuration; // Finally, create the interpolator, composed of two interpolators: an overshoot, which // reaches end > 1, and then a settle to endProgress. Interpolator overshoot = Interpolators.clampToProgress(DEACCEL, 0, overshootFraction); // The settle starts at 1, where 1 is the top of the overshoot, and maps to a fraction // such that final progress is endProgress. For example, if we overshot to 1.1 but want // to end at 1, we need to map to 1/1.1. Interpolator settle = Interpolators.clampToProgress(Interpolators.mapToProgress( ACCEL_DEACCEL, 1, (endProgress - start) / (end - start)), overshootFraction, 1); interpolator = t -> t <= overshootFraction ? overshoot.getInterpolation(t) : settle.getInterpolation(t); } } } Loading
quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +2 −17 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAG import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE; Loading Loading @@ -79,7 +80,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; Loading Loading @@ -916,26 +916,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends isFling, isCancel); float endShift = endTarget.isLauncher ? 1 : 0; final float startShift; Interpolator interpolator = DEACCEL; if (!isFling) { long expectedDuration = Math.abs(Math.round((endShift - currentShift) * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER)); duration = Math.min(MAX_SWIPE_DURATION, expectedDuration); startShift = currentShift; interpolator = endTarget == RECENTS ? ACCEL_DEACCEL : DEACCEL; } else { startShift = Utilities.boundToRange(currentShift - velocity.y * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); if (mTransitionDragLength > 0) { if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) { Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams( startShift, endShift, endShift, endVelocity, mTransitionDragLength, mContext); endShift = overshoot.end; interpolator = overshoot.interpolator; duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION, MAX_SWIPE_DURATION); } else { float distanceToTravel = (endShift - currentShift) * mTransitionDragLength; // we want the page's snap velocity to approximately match the velocity at Loading @@ -943,13 +932,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends // derivative of the scroll interpolator at zero, ie. 2. long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y)); duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); if (endTarget == RECENTS) { interpolator = ACCEL_DEACCEL; } } } } Interpolator interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL; if (endTarget.isLauncher) { mInputConsumerProxy.enable(); Loading
quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +1 −7 Original line number Diff line number Diff line Loading @@ -94,13 +94,7 @@ public abstract class SwipeUpAnimationLogic { mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength( dp, mContext, TEMP_RECT, mTaskViewSimulator.getOrientationState().getOrientationHandler()); if (mDeviceState.isFullyGesturalNavMode()) { // We can drag all the way to the top of the screen. mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; } else { mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR; } PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR); Loading
quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java +26 −48 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.quickstep.util; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; Loading @@ -38,7 +37,6 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.LauncherActivityInterface; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.views.RecentsView; /** Loading @@ -49,12 +47,6 @@ import com.android.quickstep.views.RecentsView; */ public class AnimatorControllerWithResistance { /** * How much farther we can drag past overview in 2-button mode, as a factor of the distance * it takes to drag from an app to overview. */ public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f; private enum RecentsResistanceParams { FROM_APP(0.75f, 0.5f, 1f), FROM_OVERVIEW(1f, 0.75f, 0.5f); Loading Loading @@ -161,12 +153,6 @@ public class AnimatorControllerWithResistance { LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect, orientationHandler); long distanceToCover = startRect.bottom; boolean isTwoButtonMode = SysUINavigationMode.getMode(params.context) == TWO_BUTTONS; if (isTwoButtonMode) { // We can only drag a small distance past overview, not to the top of the screen. distanceToCover = (long) ((params.dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR); } PendingAnimation resistAnim = params.resistAnim != null ? params.resistAnim : new PendingAnimation(distanceToCover * 2); Loading @@ -178,18 +164,13 @@ public class AnimatorControllerWithResistance { / (params.dp.heightPx - startRect.bottom); // This is what the scale would be at the end of the drag if we didn't apply resistance. float endScale = params.startScale - prevScaleRate * distanceToCover; final TimeInterpolator scaleInterpolator; if (isTwoButtonMode) { // We are bounded by the distance of the drag, so we don't need to apply resistance. scaleInterpolator = LINEAR; } else { // Create an interpolator that resists the scale so the scale doesn't get smaller than // RECENTS_SCALE_MAX_RESIST. float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist, params.startScale, endScale); float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist, params.startScale, endScale); scaleInterpolator = t -> { final TimeInterpolator scaleInterpolator = t -> { if (t < startResist) { return t; } Loading @@ -197,12 +178,10 @@ public class AnimatorControllerWithResistance { resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress); return startResist + resistProgress * (maxResist - startResist); }; } resistAnim.addFloat(params.scaleTarget, params.scaleProperty, params.startScale, endScale, scaleInterpolator); if (!isTwoButtonMode) { // Compute where the task view would be based on the end scale, if we didn't translate. // Compute where the task view would be based on the end scale. RectF endRectF = new RectF(startRect); Matrix temp = new Matrix(); temp.setScale(params.resistanceParams.scaleMaxResist, Loading @@ -214,7 +193,6 @@ public class AnimatorControllerWithResistance { * params.resistanceParams.translationFactor; resistAnim.addFloat(params.translationTarget, params.translationProperty, params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR); } return resistAnim; } Loading
src/com/android/launcher3/anim/Interpolators.java +0 −78 Original line number Diff line number Diff line Loading @@ -16,9 +16,6 @@ package com.android.launcher3.anim; import static com.android.launcher3.util.DisplayController.getSingleFrameMs; import android.content.Context; import android.graphics.Path; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; Loading Loading @@ -67,9 +64,6 @@ public class Interpolators { */ public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1; private static final int MIN_SETTLE_DURATION = 200; private static final float OVERSHOOT_FACTOR = 0.9f; static { Path exaggeratedEase = new Path(); exaggeratedEase.moveTo(0, 0); Loading Loading @@ -175,76 +169,4 @@ public class Interpolators { float upperBound) { return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound); } /** * Computes parameters necessary for an overshoot effect. */ public static class OvershootParams { public Interpolator interpolator; public float start; public float end; public long duration; /** * Given the input params, sets OvershootParams variables to be used by the caller. * @param startProgress The progress from 0 to 1 that the overshoot starts from. * @param overshootPastProgress The progress from 0 to 1 where we overshoot past (should * either be equal to startProgress or endProgress, depending on if we want to * overshoot immediately or only once we reach the end). * @param endProgress The final progress from 0 to 1 that we will settle to. * @param velocityPxPerMs The initial velocity that causes this overshoot. * @param totalDistancePx The distance against which progress is calculated. */ public OvershootParams(float startProgress, float overshootPastProgress, float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) { velocityPxPerMs = Math.abs(velocityPxPerMs); overshootPastProgress = Math.max(overshootPastProgress, startProgress); start = startProgress; int startPx = (int) (start * totalDistancePx); // Overshoot by about half a frame. float overshootBy = OVERSHOOT_FACTOR * velocityPxPerMs * getSingleFrameMs(context) / totalDistancePx / 2; overshootBy = Utilities.boundToRange(overshootBy, 0.02f, 0.15f); end = overshootPastProgress + overshootBy; int endPx = (int) (end * totalDistancePx); int overshootDistance = endPx - startPx; // Calculate deceleration necessary to reach overshoot distance. // Formula: velocityFinal^2 = velocityInitial^2 + 2 * acceleration * distance // 0 = v^2 + 2ad (velocityFinal == 0) // a = v^2 / -2d float decelerationPxPerMs = velocityPxPerMs * velocityPxPerMs / (2 * overshootDistance); // Calculate time necessary to reach peak of overshoot. // Formula: acceleration = velocity / time // time = velocity / acceleration duration = (long) (velocityPxPerMs / decelerationPxPerMs); // Now that we're at the top of the overshoot, need to settle back to endProgress. float settleDistance = end - endProgress; int settleDistancePx = (int) (settleDistance * totalDistancePx); // Calculate time necessary for the settle. // Formula: distance = velocityInitial * time + 1/2 * acceleration * time^2 // d = 1/2at^2 (velocityInitial = 0, since we just stopped at the top) // t = sqrt(2d/a) // Above formula assumes constant acceleration. Since we use ACCEL_DEACCEL, we actually // have acceleration to halfway then deceleration the rest. So the formula becomes: // t = sqrt(d/a) * 2 (half the distance for accel, half for deaccel) long settleDuration = (long) Math.sqrt(settleDistancePx / decelerationPxPerMs) * 4; settleDuration = Math.max(MIN_SETTLE_DURATION, settleDuration); // How much of the animation to devote to playing the overshoot (the rest is for settle). float overshootFraction = (float) duration / (duration + settleDuration); duration += settleDuration; // Finally, create the interpolator, composed of two interpolators: an overshoot, which // reaches end > 1, and then a settle to endProgress. Interpolator overshoot = Interpolators.clampToProgress(DEACCEL, 0, overshootFraction); // The settle starts at 1, where 1 is the top of the overshoot, and maps to a fraction // such that final progress is endProgress. For example, if we overshot to 1.1 but want // to end at 1, we need to map to 1/1.1. Interpolator settle = Interpolators.clampToProgress(Interpolators.mapToProgress( ACCEL_DEACCEL, 1, (endProgress - start) / (end - start)), overshootFraction, 1); interpolator = t -> t <= overshootFraction ? overshoot.getInterpolation(t) : settle.getInterpolation(t); } } }