package com.ctech.reaction.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import com.ctech.reaction.util.Constants;
import java.util.Arrays;
import java.util.List;
/**
* Created by KenZira on 3/10/17.
*/
public class ReactionView extends View {
private static final int SCALE_DURATION = 200;
private static final int TRANSLATION_DURATION = 800;
private static final int CHILD_TRANSLATION_DURATION = 300;
private static final int CHILD_DELAY = 100;
private static final int DRAW_DELAY = 50;
private RoundedBoard board;
private List<Emotion> emotions;
private int selectedIndex = -1;
private SelectingAnimation selectingAnimation;
private DeselectAnimation deselectAnimation;
private Runnable runnable = new Runnable() {
@Override public void run() {
invalidate();
}
};
public ReactionView(Context context) {
super(context);
init();
}
public ReactionView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ReactionView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
board = new RoundedBoard();
setLayerType(LAYER_TYPE_SOFTWARE, null);
emotions = Arrays.asList(
new Emotion(getContext(), "Like", "Like.json"),
new Emotion(getContext(), "Love", "Love.json"),
new Emotion(getContext(), "Haha", "Haha.json"),
new Emotion(getContext(), "Wow", "Wow.json"),
new Emotion(getContext(), "Sorry", "Sorry.json"),
new Emotion(getContext(), "Angry", "Anger.json")
);
selectingAnimation = new SelectingAnimation();
deselectAnimation = new DeselectAnimation();
deselectAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override public void onAnimationStart(Animation animation) {}
@Override public void onAnimationEnd(Animation animation) {
selectedIndex = -1;
}
@Override public void onAnimationRepeat(Animation animation) {}
});
startAnimation(new TranslationAnimation());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
for (int i = 0; i < emotions.size(); i++) {
if (event.getX() > emotions.get(i).x &&
event.getX() < emotions.get(i).x + emotions.get(i).size) {
onSelect(i);
break;
}
}
return true;
case MotionEvent.ACTION_UP:
onDeselect();
return true;
}
return super.onTouchEvent(event);
}
private void onSelect(int index) {
if (selectedIndex == index) {
return;
}
selectedIndex = index;
selectingAnimation.prepare();
startAnimation(selectingAnimation);
}
private void onDeselect() {
deselectAnimation.prepare();
startAnimation(deselectAnimation);
}
@Override
protected void onDraw(final Canvas canvas) {
board.draw(canvas);
for (Emotion emotion : emotions) {
emotion.draw(canvas);
}
postDelayed(runnable, DRAW_DELAY);
}
private void animateEmotions(float interpolatedTime) {
for (Emotion emotion : emotions) {
animateEmotionSize(emotion, interpolatedTime);
animateEmotionPosition(emotion);
}
}
private void animateEmotionPosition(Emotion emotion) {
emotion.y = RoundedBoard.BASE_LINE - emotion.size;
emotions.get(0).x = RoundedBoard.LEFT + Constants.HORIZONTAL_SPACING;
emotions.get(emotions.size() - 1).x =
RoundedBoard.LEFT + RoundedBoard.WIDTH - Constants.HORIZONTAL_SPACING
- emotions.get(emotions.size() - 1).size;
for (int i = 1; i < selectedIndex; i++) {
emotions.get(i).x = emotions.get(i - 1).x + emotions.get(i - 1).size
+ Constants.HORIZONTAL_SPACING;
}
for (int i = emotions.size() - 2; i > selectedIndex; i--) {
emotions.get(i).x = emotions.get(i + 1).x - emotions.get(i).size
- Constants.HORIZONTAL_SPACING;
}
if (selectedIndex > 0) {
emotions.get(selectedIndex).x = emotions.get(selectedIndex - 1).x
+ emotions.get(selectedIndex - 1).size + Constants.HORIZONTAL_SPACING;
}
}
private void animateEmotionSize(Emotion emotion, float interpolatedTime) {
emotion.setCurrentSize(emotion.startAnimatedSize +
(int) (interpolatedTime * (emotion.endAnimatedSize - emotion.startAnimatedSize)));
}
private void animateRoundedBoard(float interpolatedTime) {
board.setCurrentHeight(board.startAnimatedHeight + (interpolatedTime *
(board.endAnimatedHeight - board.startAnimatedHeight)));
}
private class SelectingAnimation extends Animation {
SelectingAnimation() {
setDuration(SCALE_DURATION);
}
void prepare(){
prepareEmotions();
prepareRoundedBoard();
}
private void prepareEmotions() {
for (int i = 0; i < emotions.size(); i++) {
emotions.get(i).startAnimatedSize = emotions.get(i).size;
if (i == selectedIndex) {
emotions.get(i).endAnimatedSize = Emotion.LARGE_SIZE;
} else {
emotions.get(i).endAnimatedSize = Emotion.SMALL_SIZE;
}
}
}
private void prepareRoundedBoard() {
board.startAnimatedHeight = board.height;
board.endAnimatedHeight = RoundedBoard.SCALED_DOWN_HEIGHT;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
animateRoundedBoard(interpolatedTime);
animateEmotions(interpolatedTime);
}
}
private class DeselectAnimation extends Animation {
DeselectAnimation() {
setDuration(SCALE_DURATION);
}
void prepare(){
prepareRoundedBoard();
prepareEmotions();
}
private void prepareEmotions() {
for (Emotion emotion : emotions) {
emotion.startAnimatedSize = emotion.size;
emotion.endAnimatedSize = Emotion.MEDIUM_SIZE;
}
}
private void prepareRoundedBoard() {
board.startAnimatedHeight = board.height;
board.endAnimatedHeight = RoundedBoard.HEIGHT;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
animateRoundedBoard(interpolatedTime);
animateEmotions(interpolatedTime);
}
}
private class TranslationAnimation extends Animation {
private static final int TRANSLATION_DISTANCE = 150;
private final int EMOTION_RADIUS = Emotion.MEDIUM_SIZE / 2;
TranslationAnimation() {
setDuration(TRANSLATION_DURATION);
prepareRoundedBoard();
prepareEmotions();
}
private void prepareEmotions() {
for (int i = 0; i < emotions.size(); i++) {
emotions.get(i).endAnimatedY = RoundedBoard.TOP + Constants.VERTICAL_SPACING;
emotions.get(i).startAnimatedY =
emotions.get(i).y = RoundedBoard.BOTTOM + TRANSLATION_DISTANCE;
emotions.get(i).startAnimatedX
= emotions.get(i).x = i == 0 ? RoundedBoard.LEFT
+ Constants.HORIZONTAL_SPACING + (Emotion.MEDIUM_SIZE / 2)
: emotions.get(i - 1).x + Emotion.MEDIUM_SIZE + Constants.HORIZONTAL_SPACING;
}
}
private void prepareRoundedBoard() {
board.startAnimatedY = board.y = RoundedBoard.BOTTOM + TRANSLATION_DISTANCE;
board.endAnimatedY = RoundedBoard.TOP;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
translateEmotions(interpolatedTime);
translateRoundedBoard();
}
private void translateEmotions(float interpolatedTime) {
float currentTime = interpolatedTime * TRANSLATION_DURATION;
for (int i = 0; i < emotions.size(); i++) {
int delayOfChild = CHILD_DELAY * i;
Emotion view = emotions.get(i);
if ((currentTime > delayOfChild)) {
if ((currentTime - delayOfChild) <= CHILD_TRANSLATION_DURATION) {
float progressOfChild = ((currentTime - delayOfChild) / CHILD_TRANSLATION_DURATION);
view.y = view.startAnimatedY +
progressOfChild * (view.endAnimatedY - view.startAnimatedY);
view.x = view.startAnimatedX - progressOfChild * EMOTION_RADIUS;
view.setCurrentSize((int) (progressOfChild * Emotion.MEDIUM_SIZE));
} else {
view.x = view.startAnimatedX - EMOTION_RADIUS;
view.y = view.endAnimatedY;
view.setCurrentSize(Emotion.MEDIUM_SIZE);
}
}
}
}
private void translateRoundedBoard() {
Emotion firstEmoticon = emotions.get(0);
float d =
(firstEmoticon.y - firstEmoticon.startAnimatedY) / (firstEmoticon.endAnimatedY
- firstEmoticon.startAnimatedY) * (board.endAnimatedY - board.startAnimatedY);
board.y = board.startAnimatedY + d;
}
}
}