package architect;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
import architect.view.HandlesBack;
import architect.view.HandlesViewTransition;
/**
* @author Lukasz Piliszczuk - lukasz.pili@gmail.com
*/
public class NavigatorView extends FrameLayout implements HandlesBack {
int sessionId;
private boolean interactionsDisabled;
public NavigatorView(Context context) {
super(context);
}
public NavigatorView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return !interactionsDisabled && super.dispatchTouchEvent(ev);
}
public boolean hasCurrentView() {
return getChildCount() > 0;
}
/**
* Current view is the last one
*/
public View getCurrentView() {
return hasCurrentView() ? getChildAt(getChildCount() - 1) : null;
}
void show(final Presentation presentation, final Presenter.PresentationCallback callback) {
Preconditions.checkArgument(!interactionsDisabled, "Start presentation but previous one did not end");
Preconditions.checkArgument(sessionId > 0, "Cannot show while session is not valid");
interactionsDisabled = true;
Logger.d("## Views before");
for (int i = 0; i < getChildCount(); ++i) {
Logger.d("%d = %s", i, getChildAt(i));
}
loadTransition(presentation, 1, new Callback() {
@Override
public void onAnimatorReady(Animator animator) {
if (animator != null) {
animator.start();
} else {
end(callback);
}
}
@Override
public void onAnimatorEnd() {
end(callback);
}
});
}
void showModals(final List<Presentation> presentations, final Presenter.PresentationCallback callback) {
Preconditions.checkArgument(!interactionsDisabled, "Start presentation but previous one did not end");
Preconditions.checkArgument(sessionId > 0, "Cannot show while session is not valid");
Preconditions.checkArgument(presentations != null && !presentations.isEmpty(), "Presentations cannot be null nor empty");
interactionsDisabled = true;
Logger.d("## Views before");
for (int i = 0; i < getChildCount(); ++i) {
Logger.d("%d = %s", i, getChildAt(i));
}
final List<Animator> animators = new ArrayList<>(presentations.size());
Callback getCallback = new Callback() {
int readyCount;
@Override
public void onAnimatorReady(Animator animator) {
animators.add(animator);
markReady();
}
@Override
public void onAnimatorEnd() {
}
private void markReady() {
readyCount++;
Preconditions.checkArgument(readyCount <= presentations.size(), "Ready count cannot exceed presentations size");
if (readyCount == presentations.size()) {
if (!animators.isEmpty()) {
AnimatorSet set = new AnimatorSet();
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
end(callback);
}
});
set.playTogether(animators);
set.start();
} else {
end(callback);
}
}
}
};
for (int i = 0; i < presentations.size(); i++) {
loadTransition(presentations.get(i), i + 1, getCallback);
}
}
void loadTransition(final Presentation presentation, int previousDecount, final Callback callback) {
Logger.d("Load transition for: %s", presentation.view.getClass());
final View currentView = getCurrentView();
if (currentView == null) {
Preconditions.checkNotNull(presentation.view, "New view cannot be null if current view is null");
// no previous view, add and show directly
addView(presentation.view);
sendTransitionEvents(presentation.view, null);
callback.onAnimatorReady(null);
return;
}
if (presentation.addView) {
addView(presentation.view);
Logger.d("Add view %s", presentation.view.getClass());
}
if (presentation.transition == null) {
if (presentation.removePreviousView) {
removeView(currentView);
Logger.d("Remove view %s", currentView.getClass());
}
sendTransitionEvents(presentation.view, null);
callback.onAnimatorReady(null);
} else {
measureAndGetTransition(presentation.view, getChildAt(getChildCount() - previousDecount - (presentation.addView ? 1 : 0)), presentation.removePreviousView, presentation.direction, presentation.transition, callback);
}
}
private void sendTransitionEvents(View destinationView, AnimatorSet set) {
if (destinationView instanceof HandlesViewTransition) {
((HandlesViewTransition) destinationView).onViewTransition(set);
}
}
private void end(Presenter.PresentationCallback callback) {
Logger.d("## Views after");
for (int i = 0; i < getChildCount(); ++i) {
Logger.d("%d = %s", i, getChildAt(i));
}
interactionsDisabled = false;
callback.onPresentationFinished(sessionId);
}
private void measureAndGetTransition(final View newView, final View previousView, final boolean removePreviousView, final ViewTransitionDirection direction, final ViewTransition transition, final Callback callback) {
int width = newView.getWidth();
int height = newView.getHeight();
if (width > 0 && height > 0) {
callback.onAnimatorReady(getAnimator(previousView, newView, removePreviousView, direction, transition, callback));
return;
}
newView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
final ViewTreeObserver observer = newView.getViewTreeObserver();
if (observer.isAlive()) {
observer.removeOnPreDrawListener(this);
}
callback.onAnimatorReady(getAnimator(previousView, newView, removePreviousView, direction, transition, callback));
return true;
}
});
}
private Animator getAnimator(final View originView, final View destinationView, final boolean removePreviousView, final ViewTransitionDirection direction, final ViewTransition transition, final Callback callback) {
AnimatorSet set = new AnimatorSet();
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (removePreviousView) {
removeView(originView);
Logger.d("Remove view %s", originView.getClass());
}
callback.onAnimatorEnd();
}
});
transition.transition(destinationView, originView, direction, set);
sendTransitionEvents(destinationView, set);
return set;
}
// HandlesBack
@Override
public boolean onBackPressed() {
if (interactionsDisabled) {
return true;
}
if (hasCurrentView() && getCurrentView() instanceof HandlesBack) {
return ((HandlesBack) getCurrentView()).onBackPressed();
}
return false;
}
static class Presentation {
final View view;
final boolean addView;
final boolean removePreviousView;
final ViewTransitionDirection direction;
final ViewTransition transition;
public Presentation(View view, boolean addView, boolean removePreviousView, ViewTransitionDirection direction, ViewTransition transition) {
this.view = view;
this.addView = addView;
this.removePreviousView = removePreviousView;
this.direction = direction;
this.transition = transition;
}
}
private interface Callback {
void onAnimatorReady(Animator animator);
void onAnimatorEnd();
}
}