package io.github.mthli.Ninja.View;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.view.*;
public class SwipeToDismissListener implements View.OnTouchListener {
public interface DismissCallback {
boolean canDismiss(Object token);
void onDismiss(View view, Object token);
}
private View view;
private Object token;
private DismissCallback callback;
private int viewHeight = 1;
private int slop;
private int minFlingVelocity;
private int maxFlingVelocity;
private long animTime;
private float downY;
private float translationY;
private boolean swiping;
private int swipingSlop;
private VelocityTracker velocityTracker;
public SwipeToDismissListener(View view, Object token, DismissCallback callback) {
this.view = view;
this.token = token;
this.callback = callback;
ViewConfiguration configuration = ViewConfiguration.get(view.getContext());
this.slop = configuration.getScaledTouchSlop();
this.minFlingVelocity = configuration.getScaledMinimumFlingVelocity();
this.maxFlingVelocity = configuration.getScaledMaximumFlingVelocity();
this.animTime = view.getContext().getResources().getInteger(android.R.integer.config_shortAnimTime);
}
@Override
public boolean onTouch(View view, MotionEvent event) {
event.offsetLocation(0, translationY);
if (viewHeight < 2) {
viewHeight = this.view.getHeight();
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
downY = event.getRawY();
if (callback.canDismiss(token)) {
velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
}
return false;
} case MotionEvent.ACTION_UP: {
if (velocityTracker == null) {
break;
}
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
float deltaY = event.getRawY() - downY;
float velocityY = velocityTracker.getYVelocity();
float absVelocityY = Math.abs(velocityTracker.getYVelocity());
boolean dismiss = false;
boolean dismissDown = false;
if ((Math.abs(deltaY) > viewHeight / 2) && swiping) {
dismiss = true;
dismissDown = (deltaY > 0);
} else if (minFlingVelocity <= absVelocityY && absVelocityY <= maxFlingVelocity && swiping) {
dismiss = ((velocityY < 0) == (deltaY < 0));
dismissDown = velocityTracker.getYVelocity() > 0;
}
if (dismiss) {
this.view.animate()
.translationY(dismissDown ? viewHeight : -viewHeight)
.alpha(0)
.setDuration(animTime)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
performDismiss();
}
});
} else if (swiping) {
this.view.animate()
.translationY(0f)
.alpha(1f)
.setDuration(animTime)
.setListener(null);
}
downY = 0;
translationY = 0;
swiping = false;
velocityTracker.recycle();
velocityTracker = null;
break;
} case MotionEvent.ACTION_CANCEL: {
if (velocityTracker == null) {
break;
}
this.view.animate()
.translationY(0f)
.alpha(1f)
.setDuration(animTime)
.setListener(null);
downY = 0;
translationY = 0;
swiping = false;
velocityTracker.recycle();
velocityTracker = null;
break;
} case MotionEvent.ACTION_MOVE: {
if (velocityTracker == null) {
break;
}
velocityTracker.addMovement(event);
float deltaY = event.getRawY() - downY;
if (Math.abs(deltaY) > slop) {
swiping = true;
swipingSlop = (deltaY > 0 ? slop : -slop);
this.view.getParent().requestDisallowInterceptTouchEvent(true);
MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event);
cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
this.view.onTouchEvent(cancelEvent);
cancelEvent.recycle();
}
if (swiping) {
translationY = deltaY;
this.view.setTranslationY(deltaY - swipingSlop);
this.view.setAlpha(Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaY) / viewHeight)));
return true;
}
break;
}
}
return false;
}
private void performDismiss() {
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
final int originalWidth = view.getWidth();
ValueAnimator animator = ValueAnimator.ofInt(originalWidth, 1).setDuration(animTime);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
callback.onDismiss(view, token);
view.setAlpha(1f);
view.setTranslationY(0f);
layoutParams.width = originalWidth;
view.setLayoutParams(layoutParams);
}
});
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
layoutParams.width = (Integer) animation.getAnimatedValue();
view.setLayoutParams(layoutParams);
}
});
animator.start();
}
}