Update interpolators and durations for state animations

When we enter overview (overview appears, workspace disappears):
- Workspace scales down from 1f to .8f with OvershootInterpolator(1.2f) at 200 ms
- Workspace fades from 1f to 0 with OvershootInterpolator(1.2f) at 200 ms
- Overview scales down from 1.33f to 1f with OvershootInterpolator(1.2f) at 200 ms
- Overview fades from 0 to 1f with OvershootInterpolator(1.2f) at 200 ms

When we exit overview (overview disappears, workspace appears):
- Workspace scales up from .92f to .1f with DecelerateInterpolator() at 200 ms
- Workspace fades from 0 to 1f with AccelerateInterpolator() at 200 ms
- Overview scales up from 1f to 1.1f with AccelerateInterpolator() at 180ms
- Overview fades from 1f to 0 with DecelerateInterpolator(1.7f) at 200 ms

Parallax while the finger moves: Workspace translates half the distance as the shelf

Bug: 79776746
Change-Id: I319d982cf202bcd6dbbcd68ffc5c0c7853629c7e
This commit is contained in:
Tony Wickham
2018-05-16 12:23:12 -07:00
parent 07b8b0b1f0
commit b2ddf10041
13 changed files with 119 additions and 41 deletions

View File

@@ -68,8 +68,10 @@ public class AllAppsState extends LauncherState {
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
// TODO: interpolate
return LauncherState.OVERVIEW.getWorkspaceScaleAndTranslation(launcher);
float[] scaleAndTranslation = LauncherState.OVERVIEW.getWorkspaceScaleAndTranslation(
launcher);
scaleAndTranslation[0] = 1;
return scaleAndTranslation;
}
@Override

View File

@@ -55,7 +55,7 @@ public class OverviewState extends LauncherState {
? workspacePage.getWidth() : launcher.getDeviceProfile().availableWidthPx;
recentsView.getTaskSize(sTempRect);
float scale = (float) sTempRect.width() / workspacePageWidth;
float parallaxFactor = 0.4f;
float parallaxFactor = 0.5f;
return new float[]{scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor};
}

View File

@@ -197,7 +197,8 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
}
};
animator.setFloatValues(0, 1);
animator.setDuration(Math.max(expectedDuration, 300)).setInterpolator(LINEAR);
animator.setDuration(Math.min(expectedDuration, ATOMIC_DURATION))
.setInterpolator(LINEAR);
}
} else {
mFinishFastOnSecondTouch = false;

View File

@@ -15,6 +15,10 @@
*/
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
@@ -72,16 +76,17 @@ public class RecentsViewStateController implements StateHandler {
}
PropertySetter setter = config.getPropertySetter(builder);
float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0], LINEAR);
Interpolator transYInterpolator = LINEAR;
if (toState == LauncherState.FAST_OVERVIEW) {
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0], scaleInterpolator);
Interpolator transYInterpolator = scaleInterpolator;
if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) {
transYInterpolator = Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
QUICK_SCRUB_TRANSLATION_Y_FACTOR);
}
setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
transYInterpolator);
setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
AGGRESSIVE_EASE_IN_OUT);
builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
if (!toState.overviewUi) {
builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);

View File

@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides;
import static android.view.View.VISIBLE;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -201,6 +202,13 @@ public class UiFactory {
return true;
}
public static void prepareToShowOverview(Launcher launcher) {
RecentsView overview = launcher.getOverviewPanel();
if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
overview.setAdjacentScale(1.33f);
}
}
private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
public LauncherTaskViewController(Launcher activity) {

View File

@@ -186,7 +186,7 @@ public class LauncherState {
* translationY factor where 0 is top aligned and 0.5 is centered vertically
*/
public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
return new float[] {1.2f, 0.2f};
return new float[] {1.1f, 0f};
}
public void onStateEnabled(Launcher launcher) {

View File

@@ -16,7 +16,17 @@
package com.android.launcher3;
import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import android.animation.Animator;
@@ -237,8 +247,10 @@ public class LauncherStateManager {
// transition plays in reverse and use the same duration as previous state.
mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration;
AnimatorSetBuilder builder = new AnimatorSetBuilder();
prepareForAtomicAnimation(mState, state, builder);
AnimatorSet animation = createAnimationToNewWorkspaceInternal(
state, new AnimatorSetBuilder(), onCompleteRunnable);
state, builder, onCompleteRunnable);
Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher));
if (delay > 0) {
mUiHandler.postDelayed(runnable, delay);
@@ -247,6 +259,43 @@ public class LauncherStateManager {
}
}
/**
* Prepares for a non-user controlled animation from fromState to toState. Preparations include:
* - Setting interpolators for various animations included in the state transition.
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/
public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
AnimatorSetBuilder builder) {
if (fromState == NORMAL && toState.overviewUi) {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
// Start from a higher overview scale, but only if we're invisible so we don't jump.
UiFactory.prepareToShowOverview(mLauncher);
} else if (fromState.overviewUi && toState == NORMAL) {
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
Workspace workspace = mLauncher.getWorkspace();
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE;
if (isWorkspaceVisible) {
CellLayout currentChild = (CellLayout) workspace.getChildAt(
workspace.getCurrentPage());
isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
&& currentChild.getShortcutsAndWidgets().getAlpha() > 0;
}
if (!isWorkspaceVisible) {
workspace.setScaleX(0.92f);
workspace.setScaleY(0.92f);
}
}
}
/**
* Creates a {@link AnimatorPlaybackController} that can be used for a controlled
* state transition.

View File

@@ -20,6 +20,10 @@ import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.HOTSEAT_SEARCH_BOX;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
@@ -30,7 +34,6 @@ import android.view.animation.Interpolator;
import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
@@ -50,12 +53,13 @@ public class WorkspaceStateTransitionAnimation {
}
public void setState(LauncherState toState) {
setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER, new AnimationConfig());
setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER, new AnimatorSetBuilder(),
new AnimationConfig());
}
public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
AnimationConfig config) {
setWorkspaceProperty(toState, config.getPropertySetter(builder), config);
setWorkspaceProperty(toState, config.getPropertySetter(builder), builder, config);
}
public float getFinalScale() {
@@ -66,26 +70,28 @@ public class WorkspaceStateTransitionAnimation {
* Starts a transition animation for the workspace.
*/
private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
AnimationConfig config) {
AnimatorSetBuilder builder, AnimationConfig config) {
float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
mNewScale = scaleAndTranslation[0];
PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
for (int i = 0; i < childCount; i++) {
applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, pageAlphaProvider,
propertySetter, config);
propertySetter, builder, config);
}
int elements = state.getVisibleElements(mLauncher);
Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
boolean playAtomicComponent = config.playAtomicComponent();
if (playAtomicComponent) {
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_OUT);
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
propertySetter.setViewAlpha(mLauncher.getHotseat().getLayout(), hotseatIconsAlpha,
pageAlphaProvider.interpolator);
fadeInterpolator);
propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
hotseatIconsAlpha, pageAlphaProvider.interpolator);
hotseatIconsAlpha, fadeInterpolator);
}
if (!config.playNonAtomicComponent()) {
@@ -93,43 +99,42 @@ public class WorkspaceStateTransitionAnimation {
return;
}
Interpolator translationInterpolator = !playAtomicComponent ? Interpolators.LINEAR
: Interpolators.ZOOM_OUT;
Interpolator translationInterpolator = !playAtomicComponent ? LINEAR : ZOOM_OUT;
propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
scaleAndTranslation[1], translationInterpolator);
propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
scaleAndTranslation[2], translationInterpolator);
propertySetter.setViewAlpha(mLauncher.getHotseatSearchBox(),
(elements & HOTSEAT_SEARCH_BOX) != 0 ? 1 : 0,
pageAlphaProvider.interpolator);
(elements & HOTSEAT_SEARCH_BOX) != 0 ? 1 : 0, fadeInterpolator);
// Set scrim
WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
Interpolators.LINEAR);
propertySetter.setFloat(scrim, SYSUI_PROGRESS, state.hasSysUiScrim ? 1 : 0,
Interpolators.LINEAR);
LINEAR);
propertySetter.setFloat(scrim, SYSUI_PROGRESS, state.hasSysUiScrim ? 1 : 0, LINEAR);
}
public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
applyChildState(state, cl, childIndex, state.getWorkspacePageAlphaProvider(mLauncher),
NO_ANIM_PROPERTY_SETTER, new AnimationConfig());
NO_ANIM_PROPERTY_SETTER, new AnimatorSetBuilder(), new AnimationConfig());
}
private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter,
AnimationConfig config) {
AnimatorSetBuilder builder, AnimationConfig config) {
float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
if (config.playNonAtomicComponent()) {
propertySetter.setInt(cl.getScrimBackground(),
DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_OUT);
DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
}
if (config.playAtomicComponent()) {
Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
pageAlpha, pageAlphaProvider.interpolator);
pageAlpha, fadeInterpolator);
}
}
}

View File

@@ -3,6 +3,8 @@ package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -172,7 +174,9 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
return;
}
Interpolator interpolator = config.userControlled ? LINEAR : FAST_OUT_SLOW_IN;
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN;
ObjectAnimator anim =
ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress);
anim.setDuration(config.duration);

View File

@@ -16,7 +16,6 @@
package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.util.SparseArray;
import android.view.animation.Interpolator;
@@ -32,6 +31,10 @@ import java.util.List;
public class AnimatorSetBuilder {
public static final int ANIM_VERTICAL_PROGRESS = 0;
public static final int ANIM_WORKSPACE_SCALE = 1;
public static final int ANIM_WORKSPACE_FADE = 2;
public static final int ANIM_OVERVIEW_SCALE = 3;
public static final int ANIM_OVERVIEW_FADE = 4;
protected final ArrayList<Animator> mAnims = new ArrayList<>();

View File

@@ -58,7 +58,7 @@ public class Interpolators {
EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
}
public static final Interpolator OVERSHOOT_0 = new OvershootInterpolator(0);
public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
new PathInterpolator(0.3f, 0f, 0.1f, 1f);

View File

@@ -64,8 +64,7 @@ public abstract class AbstractStateChangeTouchController
* Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this.
*/
public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 0.5f;
private static final long ATOMIC_NORMAL_TO_OVERVIEW_DURATION = 120;
private static final long ATOMIC_OVERVIEW_TO_NORMAL_DURATION = 200;
protected static final long ATOMIC_DURATION = 200;
protected final Launcher mLauncher;
protected final SwipeDetector mDetector;
@@ -213,7 +212,7 @@ public abstract class AbstractStateChangeTouchController
mAtomicComponentsStartProgress = mCurrentAnimation.getProgressFraction();
long duration = (long) (getShiftRange() * 2);
mAtomicComponentsController = AnimatorPlaybackController.wrap(
createAtomicAnimForState(mToState, duration), duration);
createAtomicAnimForState(mFromState, mToState, duration), duration);
mAtomicComponentsController.dispatchOnStart();
}
});
@@ -295,14 +294,13 @@ public abstract class AbstractStateChangeTouchController
: 1f - ATOMIC_OVERVIEW_ANIM_THRESHOLD;
boolean passedThreshold = progress >= threshold;
if (passedThreshold != mPassedOverviewAtomicThreshold) {
LauncherState targetState = passedThreshold ? toState : fromState;
LauncherState atomicFromState = passedThreshold ? fromState: toState;
LauncherState atomicToState = passedThreshold ? toState : fromState;
mPassedOverviewAtomicThreshold = passedThreshold;
if (mAtomicAnim != null) {
mAtomicAnim.cancel();
}
long duration = targetState == OVERVIEW ? ATOMIC_NORMAL_TO_OVERVIEW_DURATION
: ATOMIC_OVERVIEW_TO_NORMAL_DURATION;
mAtomicAnim = createAtomicAnimForState(targetState, duration);
mAtomicAnim = createAtomicAnimForState(atomicFromState, atomicToState, ATOMIC_DURATION);
mAtomicAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -314,8 +312,10 @@ public abstract class AbstractStateChangeTouchController
}
}
private AnimatorSet createAtomicAnimForState(LauncherState targetState, long duration) {
private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
long duration) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
mLauncher.getStateManager().prepareForAtomicAnimation(fromState, targetState, builder);
AnimationConfig config = new AnimationConfig();
config.animComponents = ATOMIC_COMPONENT;
config.duration = duration;

View File

@@ -55,4 +55,5 @@ public class UiFactory {
return false;
}
public static void prepareToShowOverview(Launcher launcher) { }
}