Merge "Swipe interaction changes on home screen" into ub-launcher3-master

This commit is contained in:
TreeHugger Robot
2018-03-22 01:15:51 +00:00
committed by Android (Google) Code Review
10 changed files with 266 additions and 98 deletions

View File

@@ -89,7 +89,7 @@ public class AllAppsState extends LauncherState {
}
@Override
public float getOverviewTranslationX(Launcher launcher) {
public float getOverviewTranslationFactor(Launcher launcher) {
return 0;
}

View File

@@ -58,7 +58,7 @@ public class OverviewState extends LauncherState {
}
@Override
public float getOverviewTranslationX(Launcher launcher) {
public float getOverviewTranslationFactor(Launcher launcher) {
return 0;
}

View File

@@ -18,16 +18,24 @@ package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
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;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.SysuiEventLogger;
@@ -36,6 +44,47 @@ import com.android.quickstep.util.SysuiEventLogger;
*/
public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
private static final float TOTAL_DISTANCE_MULTIPLIER = 2f;
private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER;
// Much be greater than LINEAR_SCALE_LIMIT;
private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f;
// Maximum amount to overshoot.
private static final float MAX_OVERSHOOT = 0.3f;
private static final double PI_BY_2 = Math.PI / 2;
private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
// If > 0, the animation progress is clamped at that value as long as user is dragging.
private float mClampProgressUpdate = -1;
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
private final Interpolator mAllAppsDampedInterpolator = new Interpolator() {
private final double mAngleMultiplier = Math.PI /
(2 * (MAXIMUM_DISTANCE_FACTOR - LINEAR_SCALE_LIMIT));
@Override
public float getInterpolation(float v) {
if (v <= LINEAR_SCALE_LIMIT) {
return v * TOTAL_DISTANCE_MULTIPLIER;
}
float overshoot = (v - LINEAR_SCALE_LIMIT);
return (float) (1 + MAX_OVERSHOOT * Math.sin(overshoot * mAngleMultiplier));
}
};
private final Interpolator mOverviewBoundInterpolator = (v) -> {
if (v >= MAXIMUM_DISTANCE_FACTOR) {
return 1;
}
return FAST_OUT_SLOW_IN.getInterpolation(v / MAXIMUM_DISTANCE_FACTOR);
};
public PortraitStatesTouchController(Launcher l) {
super(l, SwipeDetector.VERTICAL);
}
@@ -43,6 +92,11 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
// TODO: Animate to finish instead.
mCurrentAnimation.getAnimationPlayer().end();
}
// If we are already animating from a previous state, we can intercept.
return true;
}
@@ -100,17 +154,49 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
}
}
private AnimatorSetBuilder getNormalToOverviewAnimation() {
mAllAppsInterpolatorWrapper.baseInterpolator = mAllAppsDampedInterpolator;
AnimatorSetBuilder builder = new AnimatorSetBuilder();
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATION, mOverviewBoundInterpolator);
return builder;
}
@Override
protected void updateProgress(float fraction) {
if (mClampProgressUpdate > 0) {
mCurrentAnimation.setPlayFraction(Math.min(fraction, mClampProgressUpdate));
} else {
super.updateProgress(fraction);
}
}
@Override
protected float initCurrentAnimation() {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, maxAccuracy);
float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
float totalShift = endVerticalShift - startVerticalShift;
final AnimatorSetBuilder builder;
if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) {
builder = getNormalToOverviewAnimation();
totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER;
mClampProgressUpdate = MAXIMUM_DISTANCE_FACTOR;
} else {
builder = new AnimatorSetBuilder();
mClampProgressUpdate = -1;
}
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, builder, maxAccuracy);
if (totalShift == 0) {
totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
* OverviewState.getDefaultSwipeHeight(mLauncher);
@@ -118,6 +204,73 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return 1 / totalShift;
}
@Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
// Update all apps interpolator
float currentFraction = mCurrentAnimation.getProgressFraction();
float absVelocity = Math.abs(velocity);
float currentValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction);
if (isFling && absVelocity > 1 && currentFraction < LINEAR_SCALE_LIMIT) {
// TODO: Clean up these magic calculations
// Linearly interpolate the max value based on the velocity.
float maxValue = Math.max(absVelocity > 4 ? 1 + MAX_OVERSHOOT :
1 + (absVelocity - 1) * MAX_OVERSHOOT / 3,
currentValue);
double angleToPeak = PI_BY_2 - Math.asin(currentValue / maxValue);
if (expectedDuration != 0 && angleToPeak != 0) {
float distanceLeft = 1 - currentFraction;
mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> {
float scaledF = (f - currentFraction) / distanceLeft;
if (scaledF < 0.5f) {
double angle = PI_BY_2 - angleToPeak + scaledF * angleToPeak / 0.5f;
return (float) (maxValue * Math.sin(angle));
}
scaledF = ((scaledF - .5f) / .5f);
double angle = PI_BY_2 + 3 * scaledF * PI_BY_2;
float amplitude = (1 - scaledF) * (1 - scaledF) * (maxValue - 1);
return 1 + (float) (amplitude * Math.sin(angle));
};
animator.setDuration(expectedDuration).setInterpolator(LINEAR);
return;
}
}
if (currentFraction < LINEAR_SCALE_LIMIT) {
mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
velocity, isFling);
return;
}
float extraValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction) - 1;
float distanceLeft = 1 - currentFraction;
animator.setFloatValues(currentFraction, 1);
mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> {
float scaledF = (f - currentFraction) / distanceLeft;
double angle = scaledF * 1.5 * Math.PI;
float amplitude = (1 - scaledF) * (1 - scaledF) * extraValue;
return 1 + (float) (amplitude * Math.sin(angle));
};
animator.setDuration(200).setInterpolator(LINEAR);
return;
}
mFinishFastOnSecondTouch = false;
super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
velocity, isFling);
}
@Override
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
super.onSwipeInteractionCompleted(targetState, logAction);
@@ -125,4 +278,14 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
SysuiEventLogger.writeDummyRecentsTransition(0);
}
}
private static class InterpolatorWrapper implements Interpolator {
public TimeInterpolator baseInterpolator = LINEAR;
@Override
public float getInterpolation(float v) {
return baseInterpolator.getInterpolation(v);
}
}
}

View File

@@ -16,32 +16,30 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_FACTOR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.view.View;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.PagedView;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.views.RecentsView;
import com.android.launcher3.anim.PropertySetter;
import com.android.quickstep.views.LauncherRecentsView;
@TargetApi(Build.VERSION_CODES.O)
public class RecentsViewStateController implements StateHandler {
private final Launcher mLauncher;
private final RecentsView mRecentsView;
private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress);
// The fraction representing the visibility of the RecentsView. This allows delaying the
// overall transition while the RecentsView is being shown or hidden.
private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress);
private final LauncherRecentsView mRecentsView;
public RecentsViewStateController(Launcher launcher) {
mLauncher = launcher;
@@ -50,14 +48,12 @@ public class RecentsViewStateController implements StateHandler {
@Override
public void setState(LauncherState state) {
setVisibility(state.overviewUi);
setTransitionProgress(state.overviewUi ? 1 : 0);
mRecentsView.setAlpha(state.overviewUi ? 1 : 0);
updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher));
mRecentsView.setTranslationFactor(state.getOverviewTranslationFactor(mLauncher));
if (state.overviewUi) {
mRecentsView.resetTaskVisuals();
}
float overviewTranslationX = state.getOverviewTranslationX(mLauncher);
int direction = mRecentsView.isRtl() ? -1 : 1;
mRecentsView.setTranslationX(overviewTranslationX * direction);
}
@Override
@@ -76,73 +72,20 @@ public class RecentsViewStateController implements StateHandler {
builder.setStartDelay(snapDuration / 4);
}
ObjectAnimator progressAnim =
mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0);
progressAnim.setDuration(config.duration);
progressAnim.setInterpolator(Interpolators.LINEAR);
builder.play(progressAnim);
PropertySetter setter = config.getProperSetter(builder);
setter.setFloat(mRecentsView, TRANSLATION_FACTOR,
toState.getOverviewTranslationFactor(mLauncher),
builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
setter.setViewAlpha(mRecentsView, toState.overviewUi ? 1 : 0, LINEAR);
ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi);
visibilityAnim.setDuration(config.duration);
visibilityAnim.setInterpolator(Interpolators.LINEAR);
builder.play(visibilityAnim);
int direction = mRecentsView.isRtl() ? -1 : 1;
float fromTranslationX = fromState.getOverviewTranslationX(mLauncher) * direction;
float toTranslationX = toState.getOverviewTranslationX(mLauncher) * direction;
ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(mRecentsView, View.TRANSLATION_X,
fromTranslationX, toTranslationX);
translationXAnim.setDuration(config.duration);
translationXAnim.setInterpolator(Interpolators.ACCEL);
if (toState.overviewUi) {
translationXAnim.addUpdateListener(valueAnimator -> {
ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
updateAnim.addUpdateListener(valueAnimator -> {
// While animating into recents, update the visible task data as needed
mRecentsView.loadVisibleTaskData();
});
updateAnim.setDuration(config.duration);
builder.play(updateAnim);
}
builder.play(translationXAnim);
}
public void setVisibility(boolean isVisible) {
mVisibilityMultiplier.cancelAnimation();
mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
mVisibilityMultiplier.updateValue(isVisible ? 1 : 0);
}
public ObjectAnimator animateVisibility(boolean isVisible) {
ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0);
if (isVisible) {
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mRecentsView.setVisibility(View.VISIBLE);
}
});
} else {
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mRecentsView.setVisibility(View.GONE);
}
});
}
return anim;
}
public void setTransitionProgress(float progress) {
mTransitionProgress.cancelAnimation();
mTransitionProgress.updateValue(progress);
}
private void onTransitionProgress() {
applyProgress();
}
private void onVisibilityProgress() {
applyProgress();
}
private void applyProgress() {
mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value);
}
}

View File

@@ -17,6 +17,7 @@ package com.android.quickstep.views;
import static com.android.launcher3.LauncherState.NORMAL;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
@@ -30,7 +31,9 @@ import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.widget.FrameLayout;
import com.android.launcher3.DeviceProfile;
@@ -41,14 +44,31 @@ import com.android.launcher3.R;
/**
* {@link RecentsView} used in Launcher activity
*/
@TargetApi(Build.VERSION_CODES.O)
public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable {
public static final FloatProperty<LauncherRecentsView> TRANSLATION_FACTOR =
new FloatProperty<LauncherRecentsView>("translationFactor") {
@Override
public void setValue(LauncherRecentsView view, float v) {
view.setTranslationFactor(v);
}
@Override
public Float get(LauncherRecentsView view) {
return view.mTranslationFactor;
}
};
private Bitmap mScrim;
private Paint mFadePaint;
private Shader mFadeShader;
private Matrix mFadeMatrix;
private boolean mScrimOnLeft;
private float mTranslationFactor;
public LauncherRecentsView(Context context) {
this(context, null);
}
@@ -131,4 +151,21 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements Insett
protected void onAllTasksRemoved() {
mActivity.getStateManager().goToState(NORMAL);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int width = right - left;
setTranslationX(mTranslationFactor * (mIsRtl ? -width : width));
}
public void setTranslationFactor(float translationFactor) {
mTranslationFactor = translationFactor;
setTranslationX(translationFactor * (mIsRtl ? -getWidth() : getWidth()));
}
public float getTranslationFactor() {
return mTranslationFactor;
}
}