mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 19:38:21 +00:00
Merge "Swipe interaction changes on home screen" into ub-launcher3-master
This commit is contained in:
committed by
Android (Google) Code Review
commit
c8fd022bbe
@@ -89,7 +89,7 @@ public class AllAppsState extends LauncherState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getOverviewTranslationX(Launcher launcher) {
|
||||
public float getOverviewTranslationFactor(Launcher launcher) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getOverviewTranslationX(Launcher launcher) {
|
||||
public float getOverviewTranslationFactor(Launcher launcher) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user