package com.zhuinden.simplestackdemomultistack.application;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import com.zhuinden.simplestack.Backstack;
import com.zhuinden.simplestack.BackstackDelegate;
import com.zhuinden.simplestack.KeyContextWrapper;
import com.zhuinden.simplestack.StateChange;
import com.zhuinden.simplestack.StateChanger;
import com.zhuinden.simplestackdemomultistack.R;
import com.zhuinden.simplestackdemomultistack.presentation.paths.main.chromecast.ChromeCastKey;
import com.zhuinden.simplestackdemomultistack.presentation.paths.main.cloudsync.CloudSyncKey;
import com.zhuinden.simplestackdemomultistack.presentation.paths.main.list.ListKey;
import com.zhuinden.simplestackdemomultistack.presentation.paths.main.mail.MailKey;
import com.zhuinden.simplestackdemomultistack.util.Multistack;
import com.zhuinden.simplestackdemomultistack.util.ViewUtils;
import butterknife.BindView;
import butterknife.ButterKnife;
import it.sephiroth.android.library.bottomnavigation.BottomNavigation;
import static com.zhuinden.simplestackdemomultistack.application.MainActivity.StackType.CHROMECAST;
import static com.zhuinden.simplestackdemomultistack.application.MainActivity.StackType.CLOUDSYNC;
import static com.zhuinden.simplestackdemomultistack.application.MainActivity.StackType.LIST;
import static com.zhuinden.simplestackdemomultistack.application.MainActivity.StackType.MAIL;
public class MainActivity
extends AppCompatActivity
implements StateChanger {
public enum StackType {
CLOUDSYNC,
CHROMECAST,
MAIL,
LIST;
}
@BindView(R.id.root)
RelativeLayout root;
@BindView(R.id.coordinator_root)
CoordinatorLayout coordinatorLayout;
@BindView(R.id.bottom_navigation)
BottomNavigation bottomNavigation;
Multistack multistack;
private boolean isAnimating; // unfortunately, we must manually ensure that you can't navigate while you're animating.
@Override
protected void onCreate(Bundle savedInstanceState) {
this.multistack = new Multistack();
multistack.add(CLOUDSYNC.name(), new BackstackDelegate(null));
multistack.add(CHROMECAST.name(), new BackstackDelegate(null));
multistack.add(MAIL.name(), new BackstackDelegate(null));
multistack.add(LIST.name(), new BackstackDelegate(null));
Multistack.NonConfigurationInstance nonConfigurationInstance = (Multistack.NonConfigurationInstance) getLastCustomNonConfigurationInstance();
multistack.onCreate(savedInstanceState);
multistack.onCreate(CLOUDSYNC.name(), savedInstanceState, nonConfigurationInstance, CloudSyncKey.create());
multistack.onCreate(CHROMECAST.name(), savedInstanceState, nonConfigurationInstance, ChromeCastKey.create());
multistack.onCreate(MAIL.name(), savedInstanceState, nonConfigurationInstance, MailKey.create());
multistack.onCreate(LIST.name(), savedInstanceState, nonConfigurationInstance, ListKey.create());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
bottomNavigation.setOnMenuItemClickListener(new BottomNavigation.OnMenuItemSelectionListener() {
@Override
public void onMenuItemSelect(@IdRes int menuItemId, int itemIndex, boolean b) {
multistack.setSelectedStack(StackType.values()[itemIndex].name());
}
@Override
public void onMenuItemReselect(@IdRes int menuItemId, int itemIndex, boolean b) {
}
});
multistack.setStateChanger(this);
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
return multistack.onRetainCustomNonConfigurationInstance();
}
@Override
protected void onPostResume() {
super.onPostResume();
multistack.onPostResume();
}
@Override
public void onBackPressed() {
if(!multistack.onBackPressed()) {
super.onBackPressed();
}
}
@Override
protected void onPause() {
multistack.onPause();
super.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
multistack.persistViewToState(root.getChildAt(0));
multistack.onSaveInstanceState(outState);
}
@Override
protected void onDestroy() {
multistack.onDestroy();
super.onDestroy();
}
@Override
public Object getSystemService(String name) {
if(multistack != null) {
BackstackDelegate stack = multistack.get(name);
if(stack != null) {
return stack;
}
}
return super.getSystemService(name);
}
private void exchangeViewForKey(Key newKey, int direction) {
multistack.persistViewToState(root.getChildAt(0));
multistack.setSelectedStack(newKey.stackIdentifier());
Context newContext = new KeyContextWrapper(this, newKey);
View previousView = root.getChildAt(0);
View newView = LayoutInflater.from(newContext).inflate(newKey.layout(), root, false);
multistack.restoreViewFromState(newView);
root.addView(newView);
if(direction == StateChange.REPLACE) {
finishStateChange(previousView);
} else {
isAnimating = true;
ViewUtils.waitForMeasure(newView, (view, width, height) -> {
runAnimation(previousView, newView, direction, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
isAnimating = false;
finishStateChange(previousView);
}
});
});
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return !isAnimating && super.dispatchTouchEvent(ev); // unfortunately, we must manually make sure you can't navigate while you're animating.
}
@Override
public void handleStateChange(StateChange stateChange, Callback completionCallback) {
if(stateChange.topNewState().equals(stateChange.topPreviousState())) {
// no-op
completionCallback.stateChangeComplete();
return;
}
int direction = StateChange.REPLACE;
if(root.getChildAt(0) != null) {
Key previousKey = Backstack.getKey(root.getChildAt(0).getContext());
StackType previousStack = StackType.valueOf(previousKey.stackIdentifier());
StackType newStack = StackType.valueOf(((Key) stateChange.topNewState()).stackIdentifier());
direction = previousStack.ordinal() < newStack.ordinal() ? StateChange.FORWARD : previousStack.ordinal() > newStack.ordinal() ? StateChange.BACKWARD : StateChange.REPLACE;
}
exchangeViewForKey(stateChange.topNewState(), direction);
completionCallback.stateChangeComplete();
}
private void finishStateChange(View previousView) {
root.removeView(previousView);
}
// animation
private void runAnimation(final View previousView, final View newView, int direction, AnimatorListenerAdapter animatorListenerAdapter) {
Animator animator = createSegue(previousView, newView, direction);
animator.addListener(animatorListenerAdapter);
animator.start();
}
private Animator createSegue(View from, View to, int direction) {
boolean backward = direction == StateChange.BACKWARD;
int fromTranslation = backward ? from.getWidth() : -from.getWidth();
int toTranslation = backward ? -to.getWidth() : to.getWidth();
AnimatorSet set = new AnimatorSet();
set.play(ObjectAnimator.ofFloat(from, View.TRANSLATION_X, fromTranslation));
set.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, toTranslation, 0));
return set;
}
}