mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-02 17:06:49 +00:00
Setting locale to english so that the numbers don't get reverted Bug: 188552618 Test: set language to arabic, swipe up to home Change-Id: I736c83984b472d9ad09207927743c87b18637db4
371 lines
13 KiB
Java
371 lines
13 KiB
Java
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.quickstep.util;
|
|
|
|
import static com.android.launcher3.Utilities.dpToPx;
|
|
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.ObjectAnimator;
|
|
import android.animation.ValueAnimator;
|
|
import android.content.Context;
|
|
import android.graphics.PointF;
|
|
import android.graphics.RectF;
|
|
import android.util.PathParser;
|
|
import android.util.Property;
|
|
import android.view.animation.Interpolator;
|
|
|
|
import androidx.core.view.animation.PathInterpolatorCompat;
|
|
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
|
import androidx.dynamicanimation.animation.SpringForce;
|
|
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.Utilities;
|
|
import com.android.launcher3.util.DynamicResource;
|
|
import com.android.systemui.plugins.ResourceProvider;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
/**
|
|
* Applies spring forces to animate from a starting rect to a target rect,
|
|
* while providing update callbacks to the caller.
|
|
*/
|
|
public class RectFSpringAnim2 extends RectFSpringAnim {
|
|
|
|
private static final FloatPropertyCompat<RectFSpringAnim2> RECT_CENTER_X =
|
|
new FloatPropertyCompat<RectFSpringAnim2>("rectCenterXSpring") {
|
|
@Override
|
|
public float getValue(RectFSpringAnim2 anim) {
|
|
return anim.mCurrentCenterX;
|
|
}
|
|
|
|
@Override
|
|
public void setValue(RectFSpringAnim2 anim, float currentCenterX) {
|
|
anim.mCurrentCenterX = currentCenterX;
|
|
anim.onUpdate();
|
|
}
|
|
};
|
|
|
|
private static final FloatPropertyCompat<RectFSpringAnim2> RECT_Y =
|
|
new FloatPropertyCompat<RectFSpringAnim2>("rectYSpring") {
|
|
@Override
|
|
public float getValue(RectFSpringAnim2 anim) {
|
|
return anim.mCurrentCenterY;
|
|
}
|
|
|
|
@Override
|
|
public void setValue(RectFSpringAnim2 anim, float y) {
|
|
anim.mCurrentCenterY = y;
|
|
anim.onUpdate();
|
|
}
|
|
};
|
|
|
|
private static final Property<RectFSpringAnim2, Float> PROGRESS =
|
|
new Property<RectFSpringAnim2, Float>(Float.class, "rectFProgress") {
|
|
@Override
|
|
public Float get(RectFSpringAnim2 rectFSpringAnim) {
|
|
return rectFSpringAnim.mProgress;
|
|
}
|
|
|
|
@Override
|
|
public void set(RectFSpringAnim2 rectFSpringAnim, Float progress) {
|
|
rectFSpringAnim.mProgress = progress;
|
|
rectFSpringAnim.onUpdate();
|
|
}
|
|
};
|
|
|
|
private final RectF mStartRect;
|
|
private final RectF mTargetRect;
|
|
private final RectF mCurrentRect = new RectF();
|
|
private final List<OnUpdateListener> mOnUpdateListeners = new ArrayList<>();
|
|
private final List<Animator.AnimatorListener> mAnimatorListeners = new ArrayList<>();
|
|
|
|
private float mCurrentCenterX;
|
|
private float mCurrentCenterY;
|
|
|
|
private float mTargetX;
|
|
private float mTargetY;
|
|
|
|
// If true, tracking the bottom of the rects, else tracking the top.
|
|
private float mProgress;
|
|
private SpringAnimation mRectXAnim;
|
|
private SpringAnimation mRectYAnim;
|
|
private ValueAnimator mRectScaleAnim;
|
|
private boolean mAnimsStarted;
|
|
private boolean mRectXAnimEnded;
|
|
private boolean mRectYAnimEnded;
|
|
private boolean mRectScaleAnimEnded;
|
|
|
|
private final float mXDamping;
|
|
private final float mXStiffness;
|
|
|
|
private final float mYDamping;
|
|
private float mYStiffness;
|
|
|
|
private long mDuration;
|
|
|
|
private final Interpolator mCloseInterpolator;
|
|
|
|
private AppCloseConfig mValues;
|
|
final float mStartRadius;
|
|
final float mEndRadius;
|
|
|
|
final float mHomeTransYEnd;
|
|
final float mScaleStart;
|
|
|
|
public RectFSpringAnim2(RectF startRect, RectF targetRect, Context context, float startRadius,
|
|
float endRadius) {
|
|
super(startRect, targetRect, context);
|
|
mStartRect = startRect;
|
|
mTargetRect = targetRect;
|
|
|
|
mCurrentCenterY = mStartRect.centerY();
|
|
mCurrentCenterX = mStartRect.centerX();
|
|
|
|
mTargetY = mTargetRect.centerY();
|
|
mTargetX = mTargetRect.centerX();
|
|
|
|
ResourceProvider rp = DynamicResource.provider(context);
|
|
mXDamping = rp.getFloat(R.dimen.swipe_up_rect_2_x_damping_ratio);
|
|
mXStiffness = rp.getFloat(R.dimen.swipe_up_rect_2_x_stiffness);
|
|
|
|
mYDamping = rp.getFloat(R.dimen.swipe_up_rect_2_y_damping_ratio);
|
|
mYStiffness = rp.getFloat(R.dimen.swipe_up_rect_2_y_stiffness);
|
|
mDuration = Math.round(rp.getFloat(R.dimen.swipe_up_duration));
|
|
|
|
mHomeTransYEnd = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
|
|
mScaleStart = rp.getFloat(R.dimen.swipe_up_scale_start);
|
|
|
|
mCloseInterpolator = getAppCloseInterpolator(context);
|
|
|
|
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
|
|
// rounding at the end of the animation.
|
|
mStartRadius = startRadius;
|
|
mEndRadius = endRadius;
|
|
|
|
setCanRelease(true);
|
|
}
|
|
|
|
public void onTargetPositionChanged() {
|
|
if (mRectXAnim != null && mTargetX != mTargetRect.centerX()) {
|
|
mTargetX = mTargetRect.centerX();
|
|
mRectXAnim.animateToFinalPosition(mTargetX);
|
|
}
|
|
|
|
if (mRectYAnim != null) {
|
|
if (mTargetY != mTargetRect.centerY()) {
|
|
mTargetY = mTargetRect.centerY();
|
|
mRectYAnim.animateToFinalPosition(mTargetY);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void addOnUpdateListener(OnUpdateListener onUpdateListener) {
|
|
mOnUpdateListeners.add(onUpdateListener);
|
|
}
|
|
|
|
public void addAnimatorListener(Animator.AnimatorListener animatorListener) {
|
|
mAnimatorListeners.add(animatorListener);
|
|
}
|
|
|
|
/**
|
|
* Starts the fling/spring animation.
|
|
* @param context The activity context.
|
|
* @param velocityPxPerMs Velocity of swipe in px/ms.
|
|
*/
|
|
public void start(Context context, PointF velocityPxPerMs) {
|
|
mRectXAnim = new SpringAnimation(this, RECT_CENTER_X)
|
|
.setStartValue(mCurrentCenterX)
|
|
.setStartVelocity(velocityPxPerMs.x * 1000)
|
|
.setSpring(new SpringForce(mTargetX)
|
|
.setStiffness(mXStiffness)
|
|
.setDampingRatio(mXDamping));
|
|
mRectXAnim.addEndListener(((animation, canceled, centerX, velocityX) -> {
|
|
mRectXAnimEnded = true;
|
|
maybeOnEnd();
|
|
}));
|
|
|
|
mRectYAnim = new SpringAnimation(this, RECT_Y)
|
|
.setStartValue(mCurrentCenterY)
|
|
.setStartVelocity(velocityPxPerMs.y * 1000)
|
|
.setSpring(new SpringForce(mTargetY)
|
|
.setStiffness(mYStiffness)
|
|
.setDampingRatio(mYDamping));
|
|
mRectYAnim.addEndListener(((animation, canceled, centerY, velocityY) -> {
|
|
mRectYAnimEnded = true;
|
|
maybeOnEnd();
|
|
}));
|
|
|
|
mRectScaleAnim = ObjectAnimator.ofFloat(this, PROGRESS, 0, 1f)
|
|
.setDuration(mDuration);
|
|
mRectScaleAnim.setInterpolator(mCloseInterpolator);
|
|
mRectScaleAnim.addListener(new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
mRectScaleAnimEnded = true;
|
|
maybeOnEnd();
|
|
}
|
|
});
|
|
|
|
mValues = buildConfig();
|
|
mRectScaleAnim.addUpdateListener(mValues);
|
|
|
|
setCanRelease(false);
|
|
mAnimsStarted = true;
|
|
|
|
mRectXAnim.start();
|
|
mRectYAnim.start();
|
|
mRectScaleAnim.start();
|
|
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
|
|
animatorListener.onAnimationStart(null);
|
|
}
|
|
}
|
|
|
|
private AppCloseConfig buildConfig() {
|
|
return new AppCloseConfig() {
|
|
FloatProp mHomeTransY = new FloatProp(0, mHomeTransYEnd, 0, mDuration, LINEAR);
|
|
FloatProp mHomeScale = new FloatProp(mScaleStart, 1f, 0, mDuration, LINEAR);
|
|
FloatProp mWindowFadeOut = new FloatProp(1f, 0f, 0, 116, LINEAR);
|
|
// There should be a slight overlap b/w window fading out and fg fading in.
|
|
// (fg startDelay < window fade out duration)
|
|
FloatProp mFgFadeIn = new FloatProp(0, 255f, 100, mDuration - 100, LINEAR);
|
|
FloatProp mRadius = new FloatProp(mStartRadius, mEndRadius, 0, mDuration, LINEAR);
|
|
FloatProp mThreePointInterpolation = new FloatProp(0, 1, 0, mDuration, LINEAR);
|
|
|
|
@Override
|
|
public float getWorkspaceTransY() {
|
|
return mHomeTransY.value;
|
|
}
|
|
|
|
@Override
|
|
public float getWorkspaceScale() {
|
|
return mHomeScale.value;
|
|
}
|
|
|
|
@Override
|
|
public float getWindowAlpha() {
|
|
return mWindowFadeOut.value;
|
|
}
|
|
|
|
@Override
|
|
public int getFgAlpha() {
|
|
return (int) mFgFadeIn.value;
|
|
}
|
|
|
|
@Override
|
|
public float getCornerRadius() {
|
|
return mRadius.value;
|
|
}
|
|
|
|
@Override
|
|
public float getInterpolatedProgress() {
|
|
return mThreePointInterpolation.value;
|
|
}
|
|
|
|
@Override
|
|
public void onUpdate(float percent) {}
|
|
};
|
|
}
|
|
|
|
public void end() {
|
|
if (mAnimsStarted) {
|
|
if (mRectXAnim.canSkipToEnd()) {
|
|
mRectXAnim.skipToEnd();
|
|
}
|
|
if (mRectYAnim.canSkipToEnd()) {
|
|
mRectYAnim.skipToEnd();
|
|
}
|
|
mRectScaleAnim.end();
|
|
}
|
|
mRectXAnimEnded = true;
|
|
mRectYAnimEnded = true;
|
|
mRectScaleAnimEnded = true;
|
|
maybeOnEnd();
|
|
}
|
|
|
|
private boolean isEnded() {
|
|
return mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded;
|
|
}
|
|
|
|
private void onUpdate() {
|
|
if (isEnded()) {
|
|
// Prevent further updates from being called. This can happen between callbacks for
|
|
// ending the x/y/scale animations.
|
|
return;
|
|
}
|
|
|
|
if (!mOnUpdateListeners.isEmpty()) {
|
|
float rectProgress = mProgress;
|
|
float currentWidth = Utilities.mapRange(rectProgress, mStartRect.width(),
|
|
mTargetRect.width());
|
|
float currentHeight = Utilities.mapRange(rectProgress, mStartRect.height(),
|
|
mTargetRect.height());
|
|
|
|
mCurrentRect.set(mCurrentCenterX - currentWidth / 2,
|
|
mCurrentCenterY - currentHeight / 2,
|
|
mCurrentCenterX + currentWidth / 2,
|
|
mCurrentCenterY + currentHeight / 2);
|
|
|
|
float currentPlayTime = mRectScaleAnimEnded ? mRectScaleAnim.getDuration()
|
|
: mRectScaleAnim.getCurrentPlayTime();
|
|
float linearProgress = Math.min(1f, currentPlayTime / mRectScaleAnim.getDuration());
|
|
for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
|
|
onUpdateListener.onUpdate(mValues, mCurrentRect, linearProgress);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void maybeOnEnd() {
|
|
if (mAnimsStarted && isEnded()) {
|
|
mAnimsStarted = false;
|
|
setCanRelease(true);
|
|
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
|
|
animatorListener.onAnimationEnd(null);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void cancel() {
|
|
if (mAnimsStarted) {
|
|
for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
|
|
onUpdateListener.onCancel();
|
|
}
|
|
}
|
|
end();
|
|
}
|
|
|
|
private Interpolator getAppCloseInterpolator(Context context) {
|
|
ResourceProvider rp = DynamicResource.provider(context);
|
|
String path = String.format(Locale.ENGLISH,
|
|
"M 0,0 C %f, %f, %f, %f, %f, %f C %f, %f, %f, %f, 1, 1",
|
|
rp.getFloat(R.dimen.c1_a),
|
|
rp.getFloat(R.dimen.c1_b),
|
|
rp.getFloat(R.dimen.c1_c),
|
|
rp.getFloat(R.dimen.c1_d),
|
|
rp.getFloat(R.dimen.mp_x),
|
|
rp.getFloat(R.dimen.mp_y),
|
|
rp.getFloat(R.dimen.c2_a),
|
|
rp.getFloat(R.dimen.c2_b),
|
|
rp.getFloat(R.dimen.c2_c),
|
|
rp.getFloat(R.dimen.c2_d));
|
|
return PathInterpolatorCompat.create(PathParser.createPathFromPathData(path));
|
|
}
|
|
}
|