package com.gospelware.liquidbutton.controller;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.util.Log;
import android.view.animation.LinearInterpolator;
import com.gospelware.liquidbutton.LiquidButton;
import java.util.Random;
/**
* Created by ricogao on 30/06/2016.
*/
public class WaveController extends PourBaseController {
private float liquidProgress;
private float nextLiquidProgress;
private float liquidLevel;
private int left;
private int pourLength;
private Path wavePath;
private Path circlePath;
private final static int LIQUID_COLOR_BLUE = 24;
private static final long LIQUID_ANIMATION_DURATION = 5000;
//control shift-x on sin wave
private float phi;
private float amplitude;
private float currAmplitude;
private static final int FAI_FACTOR = 6;
private static final float APTITUDE_RATIO = 0.2f;
private static final float ANGLE_VELOCITY = 0.5f;
private ObjectAnimator liquidLevelAnimator;
private Random random;
private LiquidButton.PourFinishListener listener;
public WaveController() {
super();
random = new Random();
wavePath = new Path();
circlePath = new Path();
}
public interface OnProgressUpdateListener {
void onProgressUpdate(float progress);
}
public void setPourFinishListener(LiquidButton.PourFinishListener listener) {
this.listener = listener;
}
public float getLiquidProgress() {
return this.liquidProgress;
}
@Override
public Animator buildAnimator() {
ObjectAnimator animator = (ObjectAnimator) getBaseAnimator(LIQUID_ANIMATION_DURATION, new FastOutLinearInInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setRepeatMode(ValueAnimator.RESTART);
return animator;
}
@Override
public void reset() {
super.reset();
phi = 0;
liquidLevel = 0;
liquidProgress = 0;
nextLiquidProgress = 0;
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
drawLiquid(canvas);
}
@Override
protected void setRender(float progress) {
super.setRender(liquidProgress);
computeLiquid(liquidProgress);
computePhi();
}
@Override
public void getMeasure(int width, int height) {
super.getMeasure(width, height);
pourLength = width;
amplitude = radius * APTITUDE_RATIO;
left = centerX - radius;
circlePath.addCircle(centerX, centerY, radius, Path.Direction.CW);
}
private void computePhi() {
// scroll x by the fai factor
//slowly reduce the wave frequency
if (liquidLevel < bottom) {
float faiFactor;
if (liquidLevel > centerY) {
faiFactor = 0.4f + (liquidLevel - centerY) / (float) radius;
} else {
faiFactor = 0.4f + ((float) centerY - liquidLevel) / (float) radius;
}
phi += FAI_FACTOR * (faiFactor);
if (phi >= 360) {
//Generate Bubbles when phi is larger than 360
int count = random.nextInt(4);
for (int i = 0; i < count; i++) {
generateBubble(centerX, liquidLevel);
}
Log.i(PourStartController.class.getSimpleName(), "Bubble Generated");
phi = 0;
}
}
}
private void computeColor() {
int red = (liquidLevel >= centerY) ? 255 : Math.round((1 - ((float) centerY - liquidLevel) / (float) radius) * 255);
int green = (liquidLevel <= centerY) ? 255 : Math.round((1 - (liquidLevel - centerY) / (float) radius) * 255);
int liquidColor = Color.rgb(red, green, LIQUID_COLOR_BLUE);
pourPaint.setColor(liquidColor);
liquidPaint.setColor(liquidColor);
bubblePaint.setColor(liquidColor);
}
@Override
protected void computePour(float progress) {
pourBottom.y = pourLength + frameTop;
}
private void computeLiquid(float progress) {
liquidLevel = computeLiquidLevel(progress);
computeColor();
computeWave();
}
private float computeLiquidLevel(float progress) {
return bottom - (2 * radius * progress);
}
private void computeWave() {
float reduceRatio = 1.4f + (liquidLevel - top) / (float) (2 * radius);
//slowly reduce the amplitude when filling comes to end
currAmplitude = amplitude * reduceRatio;
computeWavePath();
}
private void computeWavePath() {
//clear the path for next render
wavePath.reset();
for (int i = 0; i < 2 * radius; i++) {
int dx = left + i;
// y = a * sin( w * x + fai ) + h
int dy = (int) (currAmplitude * Math.sin((i * ANGLE_VELOCITY + phi) * Math.PI / 180) + liquidLevel);
if (i == 0) {
wavePath.moveTo(dx, dy);
}
wavePath.quadTo(dx, dy, dx + 1, dy);
}
wavePath.lineTo(centerX + radius, bottom);
wavePath.lineTo(left, bottom);
wavePath.close();
}
private void drawLiquid(Canvas canvas) {
//save the canvas status
canvas.save();
//clip the canvas to circle
if (liquidLevel < bottom) {
canvas.clipPath(circlePath);
canvas.drawPath(wavePath, liquidPaint);
}//restore the canvas status~
canvas.restore();
}
/**
* This will only works when the progress is larger than the current Progress
*
* @param progress
*/
public void changeProgress(float progress) {
if (progress > nextLiquidProgress) {
nextLiquidProgress = progress;
if (liquidLevelAnimator != null) {
liquidLevelAnimator.cancel();
startLiquidChange(liquidProgress, nextLiquidProgress);
} else {
startLiquidChange(liquidProgress, nextLiquidProgress);
}
}
}
private void startLiquidChange(float current, float target) {
liquidLevelAnimator = ObjectAnimator.ofFloat(this, "liquidProgress", current, target);
liquidLevelAnimator.setDuration(computeDuration(current, target));
liquidLevelAnimator.setInterpolator(new LinearInterpolator());
if (target >= 1f) {
liquidLevelAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
getCheckView().finishPour();
getAnimator().cancel();
reset();
}
});
}
liquidLevelAnimator.start();
}
private void setLiquidProgress(float liquidProgress) {
if (liquidProgress > 1f) {
this.liquidProgress = 1f;
} else if (liquidProgress < 0f) {
this.liquidProgress = 0f;
} else {
this.liquidProgress = liquidProgress;
}
if (listener != null) {
listener.onProgressUpdate(liquidProgress);
}
}
private int computeDuration(float current, float target) {
return Math.round((target - current) * LIQUID_ANIMATION_DURATION);
}
}