NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/WaveDrawable.java

368 lines
13 KiB
Java

package org.telegram.ui.Components;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Build;
import android.os.SystemClock;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import org.telegram.messenger.AndroidUtilities;
public class WaveDrawable {
public final static float MAX_AMPLITUDE = 1800f;
private final static float ROTATION_SPEED = 0.36f * 0.1f;
public final static float SINE_WAVE_SPEED = 0.81f;
public final static float SMALL_WAVE_RADIUS = 0.55f;
public final static float SMALL_WAVE_SCALE = 0.40f;
public final static float SMALL_WAVE_SCALE_SPEED = 0.60f;
public final static float FLING_DISTANCE = 0.50f;
private final static float WAVE_ANGLE = 0.03f;
private final static float RANDOM_RADIUS_SIZE = 0.3f;
private final static float ANIMATION_SPEED_CIRCLE = 0.45f;
public final static float CIRCLE_ALPHA_1 = 0.30f;
public final static float CIRCLE_ALPHA_2 = 0.15f;
private final static float IDLE_ROTATION_SPEED = 0.2f;
private final static float IDLE_WAVE_ANGLE = 0.5f;
private final static float IDLE_SCALE_SPEED = 0.3f;
private final static float IDLE_RADIUS = 0.56f;
private final static float IDLE_ROTATE_DIF = 0.1f * IDLE_ROTATION_SPEED;
private final static float ANIMATION_SPEED_WAVE_HUGE = 0.65f;
private final static float ANIMATION_SPEED_WAVE_SMALL = 0.45f;
private final static float animationSpeed = 1f - ANIMATION_SPEED_WAVE_HUGE;
private final static float animationSpeedTiny = 1f - ANIMATION_SPEED_WAVE_SMALL;
public final static float animationSpeedCircle = 1f - ANIMATION_SPEED_CIRCLE;
private Paint paintRecordWaveBig = new Paint();
private Paint paintRecordWaveTin = new Paint();
public float fling;
private float animateToAmplitude;
private float amplitude;
private float slowAmplitude;
private float animateAmplitudeDiff;
private float animateAmplitudeSlowDiff;
float lastRadius;
float radiusDiff;
float waveDif;
double waveAngle;
private boolean incRandomAdditionals;
public float rotation;
float idleRotation;
private float circleRadius;
private Interpolator linearInterpolator = new LinearInterpolator();
public float amplitudeWaveDif;
private final CircleBezierDrawable circleBezierDrawable;
public float amplitudeRadius;
private float idleRadius = 0;
private float idleRadiusK = 0.15f * IDLE_WAVE_ANGLE;
private boolean expandIdleRadius;
private boolean expandScale;
private boolean isBig;
private boolean isIdle = true;
private float scaleIdleDif;
private float scaleDif;
public float scaleSpeed = 0.00008f;
public float scaleSpeedIdle = 0.0002f * IDLE_SCALE_SPEED;
public float maxScale;
private float flingRadius;
private Animator flingAnimator;
private ValueAnimator animator;
float randomAdditions = AndroidUtilities.dp(8) * RANDOM_RADIUS_SIZE;
private final ValueAnimator.AnimatorUpdateListener flingUpdateListener = animation -> flingRadius = (float) animation.getAnimatedValue();
private float idleGlobalRadius = AndroidUtilities.dp(10f) * IDLE_RADIUS;
private float sineAngleMax;
private WaveDrawable tinyWaveDrawable;
private long lastUpdateTime;
private View parentView;
public WaveDrawable(View parent, int n, float rotateDif, float radius, WaveDrawable tinyDrawable) {
parentView = parent;
circleBezierDrawable = new CircleBezierDrawable(n);
amplitudeRadius = radius;
isBig = tinyDrawable != null;
tinyWaveDrawable = tinyDrawable;
expandIdleRadius = isBig;
radiusDiff = AndroidUtilities.dp(34) * 0.0012f;
if (Build.VERSION.SDK_INT >= 26) {
paintRecordWaveBig.setAntiAlias(true);
paintRecordWaveTin.setAntiAlias(true);
}
}
public void setValue(float value) {
animateToAmplitude = value;
if (isBig) {
if (animateToAmplitude > amplitude) {
animateAmplitudeDiff = (animateToAmplitude - amplitude) / (100f + 300f * animationSpeed);
} else {
animateAmplitudeDiff = (animateToAmplitude - amplitude) / (100 + 500f * animationSpeed);
}
animateAmplitudeSlowDiff = (animateToAmplitude - slowAmplitude) / (100f + 500 * animationSpeed);
} else {
if (animateToAmplitude > amplitude) {
animateAmplitudeDiff = (animateToAmplitude - amplitude) / (100f + 400f * animationSpeedTiny);
} else {
animateAmplitudeDiff = (animateToAmplitude - amplitude) / (100f + 500f * animationSpeedTiny);
}
animateAmplitudeSlowDiff = (animateToAmplitude - slowAmplitude) / (100f + 500 * animationSpeedTiny);
}
boolean idle = value < 0.1f;
if (isIdle != idle && idle && isBig) {
float bRotation = rotation;
int k = 60;
float animateToBRotation = Math.round(rotation / k) * k + k / 2;
float tRotation = tinyWaveDrawable.rotation;
float animateToTRotation = Math.round(tRotation / k) * k;
float bWaveDif = waveDif;
float tWaveDif = tinyWaveDrawable.waveDif;
animator = ValueAnimator.ofFloat(1f, 0f);
animator.addUpdateListener(animation -> {
float v = (float) animation.getAnimatedValue();
rotation = animateToBRotation + (bRotation - animateToBRotation) * v;
tinyWaveDrawable.rotation = animateToTRotation + (tRotation - animateToTRotation) * v;
waveDif = 1f + (bWaveDif - 1f) * v;
tinyWaveDrawable.waveDif = 1 + (tWaveDif - 1f) * v;
waveAngle = (float) Math.acos(waveDif);
tinyWaveDrawable.waveAngle = (float) Math.acos(-tinyWaveDrawable.waveDif);
});
animator.setDuration(1200);
animator.start();
}
isIdle = idle;
if (!isIdle && animator != null) {
animator.cancel();
animator = null;
}
}
private void startFling(float delta) {
if (flingAnimator != null) {
flingAnimator.cancel();
}
float fling = this.fling * 2;
float flingDistance = delta * amplitudeRadius * (isBig ? 8 : 20) * 16 * fling;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(flingRadius, flingDistance);
valueAnimator.addUpdateListener(flingUpdateListener);
valueAnimator.setDuration((long) ((isBig ? 200 : 350) * fling));
valueAnimator.setInterpolator(linearInterpolator);
ValueAnimator valueAnimator1 = ValueAnimator.ofFloat(flingDistance, 0);
valueAnimator1.addUpdateListener(flingUpdateListener);
valueAnimator1.setInterpolator(linearInterpolator);
valueAnimator1.setDuration((long) ((isBig ? 220 : 380) * fling));
AnimatorSet animatorSet = new AnimatorSet();
flingAnimator = animatorSet;
animatorSet.playSequentially(valueAnimator, valueAnimator1);
animatorSet.start();
}
boolean wasFling;
public void tick(float circleRadius) {
long newTime = SystemClock.elapsedRealtime();
long dt = newTime - lastUpdateTime;
lastUpdateTime = newTime;
if (dt > 20) {
dt = 17;
}
if (animateToAmplitude != amplitude) {
amplitude += animateAmplitudeDiff * dt;
if (animateAmplitudeDiff > 0) {
if (amplitude > animateToAmplitude) {
amplitude = animateToAmplitude;
}
} else {
if (amplitude < animateToAmplitude) {
amplitude = animateToAmplitude;
}
}
if (Math.abs(amplitude - animateToAmplitude) * amplitudeRadius < AndroidUtilities.dp(4)) {
if (!wasFling) {
startFling(animateAmplitudeDiff);
wasFling = true;
}
} else {
wasFling = false;
}
}
if (animateToAmplitude != slowAmplitude) {
slowAmplitude += animateAmplitudeSlowDiff * dt;
if (Math.abs(slowAmplitude - amplitude) > 0.2f) {
slowAmplitude = amplitude + (slowAmplitude > amplitude ?
0.2f : -0.2f);
}
if (animateAmplitudeSlowDiff > 0) {
if (slowAmplitude > animateToAmplitude) {
slowAmplitude = animateToAmplitude;
}
} else {
if (slowAmplitude < animateToAmplitude) {
slowAmplitude = animateToAmplitude;
}
}
}
idleRadius = circleRadius * idleRadiusK;
if (expandIdleRadius) {
scaleIdleDif += scaleSpeedIdle * dt;
if (scaleIdleDif >= 0.05f) {
scaleIdleDif = 0.05f;
expandIdleRadius = false;
}
} else {
scaleIdleDif -= scaleSpeedIdle * dt;
if (scaleIdleDif < 0f) {
scaleIdleDif = 0f;
expandIdleRadius = true;
}
}
if (maxScale > 0) {
if (expandScale) {
scaleDif += scaleSpeed * dt;
if (scaleDif >= maxScale) {
scaleDif = maxScale;
expandScale = false;
}
} else {
scaleDif -= scaleSpeed * dt;
if (scaleDif < 0f) {
scaleDif = 0f;
expandScale = true;
}
}
}
if (sineAngleMax > animateToAmplitude) {
sineAngleMax -= 0.25f;
if (sineAngleMax < animateToAmplitude) {
sineAngleMax = animateToAmplitude;
}
} else if (sineAngleMax < animateToAmplitude) {
sineAngleMax += 0.25f;
if (sineAngleMax > animateToAmplitude) {
sineAngleMax = animateToAmplitude;
}
}
if (!isIdle) {
rotation += (ROTATION_SPEED * 0.5f + ROTATION_SPEED * 4f * (amplitude > 0.5f ? 1 : amplitude / 0.5f)) * dt;
if (rotation > 360) rotation %= 360;
} else {
idleRotation += IDLE_ROTATE_DIF * dt;
if (idleRotation > 360) idleRotation %= 360;
}
if (lastRadius < circleRadius) {
lastRadius = circleRadius;
} else {
lastRadius -= radiusDiff * dt;
if (lastRadius < circleRadius) {
lastRadius = circleRadius;
}
}
lastRadius = circleRadius;
if (!isIdle) {
waveAngle += (amplitudeWaveDif * sineAngleMax) * dt;
if (isBig) {
waveDif = (float) Math.cos(waveAngle);
} else {
waveDif = -(float) Math.cos(waveAngle);
}
if (waveDif > 0f && incRandomAdditionals) {
circleBezierDrawable.calculateRandomAdditionals();
incRandomAdditionals = false;
} else if (waveDif < 0f && !incRandomAdditionals) {
circleBezierDrawable.calculateRandomAdditionals();
incRandomAdditionals = true;
}
}
parentView.invalidate();
}
public void draw(float cx, float cy, float scale, Canvas canvas) {
float waveAmplitude = amplitude < 0.3f ? amplitude / 0.3f : 1f;
float radiusDiff = AndroidUtilities.dp(10) + AndroidUtilities.dp(50) * WAVE_ANGLE * animateToAmplitude;
circleBezierDrawable.idleStateDiff = idleRadius * (1f - waveAmplitude);
float kDiff = 0.35f * waveAmplitude * waveDif;
circleBezierDrawable.radiusDiff = radiusDiff * kDiff;
circleBezierDrawable.cubicBezierK = 1f + Math.abs(kDiff) * waveAmplitude + (1f - waveAmplitude) * idleRadiusK;
circleBezierDrawable.radius = (lastRadius + amplitudeRadius * amplitude) + idleGlobalRadius + (flingRadius * waveAmplitude);
if (circleBezierDrawable.radius + circleBezierDrawable.radiusDiff < circleRadius) {
circleBezierDrawable.radiusDiff = circleRadius - circleBezierDrawable.radius;
}
if (isBig) {
circleBezierDrawable.globalRotate = rotation + idleRotation;
} else {
circleBezierDrawable.globalRotate = -rotation + idleRotation;
}
canvas.save();
float s = scale + scaleIdleDif * (1f - waveAmplitude) + scaleDif * waveAmplitude;
canvas.scale(s, s, cx, cy);
circleBezierDrawable.setRandomAdditions(waveAmplitude * waveDif * randomAdditions);
circleBezierDrawable.draw(cx, cy, canvas, isBig ? paintRecordWaveBig : paintRecordWaveTin);
canvas.restore();
}
public void setCircleRadius(float radius) {
circleRadius = radius;
}
public void setColor(int color, int alpha) {
paintRecordWaveBig.setColor(color);
paintRecordWaveTin.setColor(color);
paintRecordWaveBig.setAlpha(alpha);
paintRecordWaveTin.setAlpha(alpha);
}
}