package com.byoutline.kickmaterial.activities;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.databinding.DataBindingUtil;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.SharedElementCallback;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.OvershootInterpolator;
import butterknife.ButterKnife;
import butterknife.OnClick;
import com.byoutline.cachedfield.CachedFieldWithArg;
import com.byoutline.kickmaterial.KickMaterialApp;
import com.byoutline.kickmaterial.R;
import com.byoutline.kickmaterial.adapters.CategoryClickListener;
import com.byoutline.kickmaterial.databinding.ActivityCategoryListBinding;
import com.byoutline.kickmaterial.managers.CategoriesListViewModel;
import com.byoutline.kickmaterial.model.Category;
import com.byoutline.kickmaterial.model.DiscoverQuery;
import com.byoutline.kickmaterial.model.DiscoverResponse;
import com.byoutline.kickmaterial.utils.AnimatorUtils;
import com.byoutline.kickmaterial.utils.LUtils;
import com.byoutline.kickmaterial.views.CategoriesListSeparator;
import com.byoutline.secretsauce.utils.ViewUtils;
import org.parceler.Parcels;
import timber.log.Timber;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.List;
/**
* @author Pawel Karczewski <pawel.karczewski at byoutline.com> on 2015-01-03
*/
public class CategoriesListActivity extends KickMaterialBaseActivity implements CategoryClickListener {
public static final String ARG_CATEGORY = "ARG_CATEGORY";
public static final int REVEAL_ANIM_DURATION = 400;
public static final int FINISH_ANIMATION_DURATION = REVEAL_ANIM_DURATION + 100;
public static final int RESULT_CATEGORY_SELECTED = 13;
public static final int RESULT_CATEGORY_SELECTION_CANCELED = 17;
public static final int DEFAULT_REQUEST_CODE = 101;
private static final String INSTANCE_STATE_SUMMARY_SCROLLED = "INSTANCE_STATE_SUMMARY_SCROLLED";
@Inject
CachedFieldWithArg<DiscoverResponse, DiscoverQuery> discoverField;
@Inject
CategoriesListViewModel viewModel;
private Animator revealAnimation;
private Category category;
private int summaryScrolledValue;
private ActivityCategoryListBinding binding;
public static void launch(@Nonnull Activity context, @Nonnull Category category, View sharedElement) {
final Bundle options = KickMaterialBaseActivity.getSharedElementsBundle(context, sharedElement);
Intent intent = new Intent(context, CategoriesListActivity.class);
intent.putExtra(ARG_CATEGORY, Parcels.wrap(category));
ActivityCompat.startActivityForResult(context, intent, DEFAULT_REQUEST_CODE, options);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_category_list);
injectViewsAndSetUpToolbar();
KickMaterialApp.component.inject(this);
ButterKnife.bind(this);
handleArguments();
setUpAdapters(binding);
setUpListeners();
launchPostTransitionAnimations();
}
private void setUpListeners() {
binding.categoriesRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
summaryScrolledValue += dy;
binding.circleImageContainer.setTranslationY(-0.5f * summaryScrolledValue);
binding.categoriesHeaderLl.setTranslationY(-summaryScrolledValue);
}
});
}
private void launchPostTransitionAnimations() {
if (category != null) {
int color = ContextCompat.getColor(this, category.colorResId);
binding.categoryCircleIv.setColorFilter(color);
binding.selectedCategoryIv.setImageResource(category.drawableResId);
binding.selectCategoryTv.setBackgroundColor(color);
binding.selectCategoryTv.getBackground().setAlpha(85);
}
if (LUtils.hasL()) {
binding.closeCategoriesIv.setScaleX(0);
binding.closeCategoriesIv.setScaleY(0);
ActivityCompat.setEnterSharedElementCallback(this, new SharedElementCallback() {
@Override
public void onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots) {
binding.closeCategoriesIv.postDelayed(() -> {
// remove listener, we do not want to trigger this animation on exit
ActivityCompat.setEnterSharedElementCallback(CategoriesListActivity.this, null);
if (isFinishing()) {
return;
}
AnimatorSet closeCategoryAnim = AnimatorUtils.getScaleAnimator(binding.closeCategoriesIv, 0, 1);
closeCategoryAnim.setInterpolator(new OvershootInterpolator());
closeCategoryAnim.start();
}, 160);
}
});
}
binding.categoriesRv.post(() -> binding.categoriesRv.startAnimation(LUtils.loadAnimationWithLInterpolator(getApplicationContext(), R.anim.slide_from_bottom)));
}
private void handleArguments() {
Bundle args = getIntent().getExtras();
if (args != null && args.containsKey(ARG_CATEGORY)) {
category = Parcels.unwrap(args.getParcelable(ARG_CATEGORY));
} else {
Timber.e("Category not passed");
}
}
@Override
public void onResume() {
super.onResume();
showActionbar(false, false);
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(INSTANCE_STATE_SUMMARY_SCROLLED, summaryScrolledValue);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
summaryScrolledValue = savedInstanceState.getInt(INSTANCE_STATE_SUMMARY_SCROLLED);
}
super.onRestoreInstanceState(savedInstanceState);
}
private void setUpAdapters(ActivityCategoryListBinding binding) {
binding.categoriesRv.addItemDecoration(new CategoriesListSeparator(this.getApplicationContext()));
int itemColor = R.color.green_primary;
if (category != null) {
itemColor = category.colorResId;
}
int bgColor = ContextCompat.getColor(this, itemColor);
viewModel.setAllCategoriesBgColor(bgColor);
binding.setCategoryClickListener(this);
binding.setViewModel(viewModel);
}
@Override
public void categoryClicked(View view, Category category) {
View checkedView = view.findViewById(R.id.checked_view);
ViewUtils.showView(checkedView, true);
categoryClicked(category);
}
public void categoryClicked(Category category) {
animateCategoryColor(category);
// start loading data from API during animation
discoverField.postValue(DiscoverQuery.getDiscoverQuery(category, 1));
}
private Category animateCategoryColor(Category clickedCategory) {
final int color = ContextCompat.getColor(this, clickedCategory.colorResId);
binding.selectedCategoryIv.setImageResource(clickedCategory.drawableResId);
binding.categoryCircleRevealIv.setColorFilter(color);
animateCircleReveal(color, clickedCategory);
return clickedCategory;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void animateCircleReveal(final int color, Category category) {
// get the center for the clipping circle
int cx = (binding.categoryCircleRevealIv.getLeft() + binding.categoryCircleRevealIv.getRight()) / 2;
int cy = (binding.categoryCircleRevealIv.getTop() + binding.categoryCircleRevealIv.getBottom());
int finalRadius = Math.max(binding.categoryCircleRevealIv.getWidth(), binding.categoryCircleRevealIv.getHeight());
if (LUtils.hasL()) {
if (revealAnimation != null && revealAnimation.isRunning()) {
revealAnimation.end();
}
revealAnimation = ViewAnimationUtils.createCircularReveal(binding.categoryCircleRevealIv, cx, cy, 0.4f * finalRadius, finalRadius);
revealAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
ViewUtils.showView(binding.categoryCircleRevealIv, true);
binding.selectCategoryTv.setBackgroundColor(Color.TRANSPARENT);
LUtils.setStatusBarColor(CategoriesListActivity.this, color);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (binding.categoryCircleIv != null) {
binding.categoryCircleIv.setColorFilter(color);
binding.selectCategoryTv.setBackgroundColor(color);
binding.selectCategoryTv.getBackground().setAlpha(85);
binding.categoryCircleRevealIv.setVisibility(View.INVISIBLE);
}
}
});
revealAnimation.setDuration(REVEAL_ANIM_DURATION);
revealAnimation.setInterpolator(new FastOutSlowInInterpolator());
revealAnimation.start();
} else {
binding.categoryCircleIv.setColorFilter(color);
}
if (category != null) {
finishWithResult(category);
}
}
private void finishWithResult(Category category) {
runFinishAnimation(() -> {
Intent intent = new Intent();
intent.putExtra(ARG_CATEGORY, Parcels.wrap(category));
setResult(RESULT_CATEGORY_SELECTED, intent);
ActivityCompat.finishAfterTransition(CategoriesListActivity.this);
});
}
private void finishWithoutResult() {
runFinishAnimation(() -> {
setResult(RESULT_CATEGORY_SELECTION_CANCELED);
ActivityCompat.finishAfterTransition(CategoriesListActivity.this);
});
}
private void runFinishAnimation(Runnable finishAction) {
if (summaryScrolledValue > 0) {
binding.categoriesRv.smoothScrollToPosition(0);
}
ViewUtils.showView(binding.selectCategoryTv, false);
ObjectAnimator imageFade = ObjectAnimator.ofFloat(binding.selectedCategoryIv, View.ALPHA, 1, 0);
AnimatorSet set = new AnimatorSet();
AnimatorSet closeButtonScale = AnimatorUtils.getScaleAnimator(binding.closeCategoriesIv, 1, 0.1f);
set.playTogether(closeButtonScale, imageFade);
set.setDuration(FINISH_ANIMATION_DURATION);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (revealAnimation != null) {
revealAnimation.cancel();
}
ViewUtils.showView(binding.categoryCircleRevealIv, false);
binding.closeCategoriesIv.setScaleX(0);
binding.closeCategoriesIv.setScaleY(0);
finishAction.run();
}
});
binding.categoriesRv.startAnimation(LUtils.loadAnimationWithLInterpolator(getApplicationContext(), R.anim.slide_to_bottom));
set.start();
}
@Override
public void onBackPressed() {
// overwrite back to cancel reveal animation and launch transition.
finishWithoutResult();
}
@OnClick(R.id.close_categories_iv)
public void onCloseCategories() {
finishWithoutResult();
}
@Override
public void setToolbarAlpha(float alpha) {
}
}