mirror of https://github.com/NekoX-Dev/NekoX.git
274 lines
9.4 KiB
Java
274 lines
9.4 KiB
Java
|
package org.telegram.ui.Components;
|
||
|
|
||
|
import android.animation.Animator;
|
||
|
import android.animation.AnimatorListenerAdapter;
|
||
|
import android.animation.ObjectAnimator;
|
||
|
import android.content.Context;
|
||
|
import android.graphics.Canvas;
|
||
|
import android.graphics.Color;
|
||
|
import android.graphics.Paint;
|
||
|
import android.text.Layout;
|
||
|
import android.text.StaticLayout;
|
||
|
import android.text.TextPaint;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.TypedValue;
|
||
|
|
||
|
import androidx.annotation.Keep;
|
||
|
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||
|
import androidx.dynamicanimation.animation.SpringForce;
|
||
|
|
||
|
import org.telegram.messenger.AndroidUtilities;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.List;
|
||
|
import java.util.Objects;
|
||
|
|
||
|
public class AnimatedPhoneNumberEditText extends HintEditText {
|
||
|
private final static float SPRING_MULTIPLIER = 100f;
|
||
|
|
||
|
private final static boolean USE_NUMBERS_ANIMATION = false;
|
||
|
|
||
|
private ArrayList<StaticLayout> letters = new ArrayList<>();
|
||
|
private ArrayList<StaticLayout> oldLetters = new ArrayList<>();
|
||
|
private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||
|
private ObjectAnimator animator;
|
||
|
private float progress;
|
||
|
private String oldText = "";
|
||
|
|
||
|
private HintFadeProperty hintFadeProperty = new HintFadeProperty();
|
||
|
private List<Float> hintAnimationValues = new ArrayList<>();
|
||
|
private List<SpringAnimation> hintAnimations = new ArrayList<>();
|
||
|
|
||
|
private Boolean wasHintVisible;
|
||
|
private String wasHint;
|
||
|
|
||
|
private Runnable hintAnimationCallback;
|
||
|
|
||
|
public AnimatedPhoneNumberEditText(Context context) {
|
||
|
super(context);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setHintText(String value) {
|
||
|
boolean show = !TextUtils.isEmpty(value);
|
||
|
boolean runAnimation = false;
|
||
|
|
||
|
if (wasHintVisible == null || wasHintVisible != show) {
|
||
|
hintAnimationValues.clear();
|
||
|
for (SpringAnimation a : hintAnimations) {
|
||
|
a.cancel();
|
||
|
}
|
||
|
hintAnimations.clear();
|
||
|
wasHintVisible = show;
|
||
|
runAnimation = TextUtils.isEmpty(getText());
|
||
|
}
|
||
|
|
||
|
String str = show ? value : wasHint;
|
||
|
if (str == null) str = "";
|
||
|
wasHint = value;
|
||
|
|
||
|
if (show || !runAnimation) {
|
||
|
super.setHintText(value);
|
||
|
}
|
||
|
|
||
|
if (runAnimation) {
|
||
|
runHintAnimation(str.length(), show, () -> {
|
||
|
hintAnimationValues.clear();
|
||
|
for (SpringAnimation a : hintAnimations) {
|
||
|
a.cancel();
|
||
|
}
|
||
|
|
||
|
if (!show) {
|
||
|
super.setHintText(value);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String getHintText() {
|
||
|
return wasHint;
|
||
|
}
|
||
|
|
||
|
private void runHintAnimation(int length, boolean show, Runnable callback) {
|
||
|
if (hintAnimationCallback != null) {
|
||
|
removeCallbacks(hintAnimationCallback);
|
||
|
}
|
||
|
for (int i = 0; i < length; i++) {
|
||
|
float startValue = show ? 0 : 1, finalValue = show ? 1 : 0;
|
||
|
|
||
|
SpringAnimation springAnimation = new SpringAnimation(i, hintFadeProperty)
|
||
|
.setSpring(new SpringForce(finalValue * SPRING_MULTIPLIER)
|
||
|
.setStiffness(500)
|
||
|
.setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)
|
||
|
.setFinalPosition(finalValue * SPRING_MULTIPLIER))
|
||
|
.setStartValue(startValue * SPRING_MULTIPLIER);
|
||
|
hintAnimations.add(springAnimation);
|
||
|
hintAnimationValues.add(startValue);
|
||
|
postDelayed(springAnimation::start, i * 5L);
|
||
|
}
|
||
|
postDelayed(hintAnimationCallback = callback, length * 5L + 150L);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setTextSize(int unit, float size) {
|
||
|
super.setTextSize(unit, size);
|
||
|
|
||
|
textPaint.setTextSize(TypedValue.applyDimension(unit, size, getResources().getDisplayMetrics()));
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setTextColor(int color) {
|
||
|
super.setTextColor(color);
|
||
|
|
||
|
textPaint.setColor(color);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
|
||
|
super.onTextChanged(text, start, lengthBefore, lengthAfter);
|
||
|
|
||
|
if (USE_NUMBERS_ANIMATION && !isTextWatchersSuppressed()) {
|
||
|
setNewText(text.toString().trim());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onDraw(Canvas canvas) {
|
||
|
if (USE_NUMBERS_ANIMATION) {
|
||
|
int color = getCurrentTextColor();
|
||
|
setTextColor(Color.TRANSPARENT);
|
||
|
super.onDraw(canvas);
|
||
|
setTextColor(color);
|
||
|
|
||
|
if (letters.isEmpty() && oldLetters.isEmpty()) {
|
||
|
return;
|
||
|
}
|
||
|
float height = letters.isEmpty() ? oldLetters.get(0).getHeight() : letters.get(0).getHeight();
|
||
|
|
||
|
float x = 0;
|
||
|
float oldDx = 0;
|
||
|
canvas.save();
|
||
|
canvas.translate(getPaddingLeft() + x, (getMeasuredHeight() - height) / 2);
|
||
|
int count = Math.max(letters.size(), oldLetters.size());
|
||
|
for (int a = 0; a < count; a++) {
|
||
|
canvas.save();
|
||
|
StaticLayout old = a < oldLetters.size() ? oldLetters.get(a) : null;
|
||
|
StaticLayout layout = a < letters.size() ? letters.get(a) : null;
|
||
|
if (progress < 0) {
|
||
|
if (old != null) {
|
||
|
textPaint.setAlpha((int) (255 * -progress));
|
||
|
canvas.save();
|
||
|
canvas.translate(oldDx, (1f + progress) * height);
|
||
|
old.draw(canvas);
|
||
|
canvas.restore();
|
||
|
}
|
||
|
if (layout != null) {
|
||
|
if (a == count - 1 || old != null) {
|
||
|
textPaint.setAlpha((int) (255 * (1f + progress)));
|
||
|
canvas.translate(0, -progress * height);
|
||
|
} else {
|
||
|
textPaint.setAlpha(255);
|
||
|
}
|
||
|
}
|
||
|
} else if (layout != null) {
|
||
|
textPaint.setAlpha(255);
|
||
|
}
|
||
|
if (layout != null) {
|
||
|
layout.draw(canvas);
|
||
|
}
|
||
|
canvas.restore();
|
||
|
canvas.translate(layout != null ? layout.getLineWidth(0) : old.getLineWidth(0) + AndroidUtilities.dp(1), 0);
|
||
|
if (layout != null && old != null) {
|
||
|
oldDx += old.getLineWidth(0) - layout.getLineWidth(0);
|
||
|
}
|
||
|
}
|
||
|
canvas.restore();
|
||
|
} else super.onDraw(canvas);
|
||
|
}
|
||
|
|
||
|
public void setNewText(String text) {
|
||
|
if (oldLetters == null || letters == null || Objects.equals(oldText, text)) return;
|
||
|
|
||
|
if (animator != null) {
|
||
|
animator.cancel();
|
||
|
animator = null;
|
||
|
}
|
||
|
oldLetters.clear();
|
||
|
oldLetters.addAll(letters);
|
||
|
letters.clear();
|
||
|
boolean replace = TextUtils.isEmpty(oldText) && !TextUtils.isEmpty(text);
|
||
|
|
||
|
progress = 0;
|
||
|
for (int a = 0; a < text.length(); a++) {
|
||
|
String ch = text.substring(a, a + 1);
|
||
|
String oldCh = !oldLetters.isEmpty() && a < oldText.length() ? oldText.substring(a, a + 1) : null;
|
||
|
if (!replace && oldCh != null && oldCh.equals(ch)) {
|
||
|
letters.add(oldLetters.get(a));
|
||
|
oldLetters.set(a, null);
|
||
|
} else {
|
||
|
if (replace && oldCh == null) {
|
||
|
oldLetters.add(new StaticLayout("", textPaint, 0, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false));
|
||
|
}
|
||
|
StaticLayout layout = new StaticLayout(ch, textPaint, (int) Math.ceil(textPaint.measureText(ch)), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||
|
letters.add(layout);
|
||
|
}
|
||
|
}
|
||
|
if (!oldLetters.isEmpty()) {
|
||
|
animator = ObjectAnimator.ofFloat(this, "progress", -1, 0);
|
||
|
animator.setDuration(150);
|
||
|
animator.addListener(new AnimatorListenerAdapter() {
|
||
|
@Override
|
||
|
public void onAnimationEnd(Animator animation) {
|
||
|
animator = null;
|
||
|
oldLetters.clear();
|
||
|
}
|
||
|
});
|
||
|
animator.start();
|
||
|
}
|
||
|
oldText = text;
|
||
|
invalidate();
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onPreDrawHintCharacter(int index, Canvas canvas, float pivotX, float pivotY) {
|
||
|
if (index < hintAnimationValues.size()) {
|
||
|
hintPaint.setAlpha((int) (hintAnimationValues.get(index) * 0xFF));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Keep
|
||
|
public void setProgress(float value) {
|
||
|
if (progress == value) {
|
||
|
return;
|
||
|
}
|
||
|
progress = value;
|
||
|
invalidate();
|
||
|
}
|
||
|
|
||
|
@Keep
|
||
|
public float getProgress() {
|
||
|
return progress;
|
||
|
}
|
||
|
|
||
|
private final class HintFadeProperty extends FloatPropertyCompat<Integer> {
|
||
|
public HintFadeProperty() {
|
||
|
super("hint_fade");
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public float getValue(Integer object) {
|
||
|
return object < hintAnimationValues.size() ? hintAnimationValues.get(object) * SPRING_MULTIPLIER : 0;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void setValue(Integer object, float value) {
|
||
|
if (object < hintAnimationValues.size()) {
|
||
|
hintAnimationValues.set((int) object, value / SPRING_MULTIPLIER);
|
||
|
invalidate();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|