package com.konifar.materialcat; import android.app.Activity; import android.app.ActivityOptions; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.support.design.widget.AppBarLayout; import android.support.design.widget.CollapsingToolbarLayout; import android.support.design.widget.FloatingActionButton; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.support.v4.view.animation.LinearOutSlowInInterpolator; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.transition.Slide; import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; import android.view.animation.Interpolator; import android.widget.ImageView; import com.konifar.materialcat.models.pojo.Photo; import com.konifar.materialcat.utils.FabAnimationUtils; import com.konifar.materialcat.views.ColorPalleteView; import com.konifar.materialcat.views.CustomKenBurnsView; import com.konifar.materialcat.views.PhotoInfoView; import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.AnimatorListenerAdapter; import com.nineoldandroids.view.ViewPropertyAnimator; import com.nineoldandroids.view.animation.AnimatorProxy; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import io.codetail.animation.SupportAnimator; import io.codetail.animation.ViewAnimationUtils; public class PhotoDetailActivity extends AppCompatActivity { private static final String EXTRA_ORIENTATION = "extra_orientation"; private static final String EXTRA_LEFT = "extra_left"; private static final String EXTRA_TOP = "extra_top"; private static final String EXTRA_WIDTH = "extra_width"; private static final String EXTRA_HEIGHT = "extra_height"; private static final long ANIMATION_DURATION = 250; private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator(); @Bind(R.id.app_bar) AppBarLayout appBarLayout; @Bind(R.id.collapsing_toolbar) CollapsingToolbarLayout collapsingToolbarLayout; @Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.img_preview) CustomKenBurnsView imgPreview; @Bind(R.id.img_preview_cover) ImageView imgPreviewCover; @Bind(R.id.img_preview_dummy) ImageView imgPreviewDummy; @Bind(R.id.fab) FloatingActionButton fab; @Bind(R.id.color_pallete) ColorPalleteView colorPalleteView; @Bind(R.id.photo_info_id) PhotoInfoView photoInfoId; @Bind(R.id.photo_info_owner) PhotoInfoView photoInfoOwner; @Bind(R.id.photo_info_secret) PhotoInfoView photoInfoSecret; @Bind(R.id.photo_info_server) PhotoInfoView photoInfoServer; @Bind(R.id.photo_info_title) PhotoInfoView photoInfoTitle; @Bind(R.id.photo_info_public) PhotoInfoView photoInfoPublic; @Bind(R.id.photo_info_friend) PhotoInfoView photoInfoFriend; @Bind(R.id.photo_info_family) PhotoInfoView photoInfoFamily; private int leftDelta; private int topDelta; private float widthScale; private float heightScale; private int originalOrientation; public static void start(Activity activity, View transitionView, Photo photo) { Intent intent = new Intent(activity, PhotoDetailActivity.class); intent.putExtra(Photo.class.getSimpleName(), photo); int[] screenLocation = new int[2]; transitionView.getLocationOnScreen(screenLocation); int orientation = activity.getResources().getConfiguration().orientation; intent.putExtra(EXTRA_ORIENTATION, orientation); intent.putExtra(EXTRA_LEFT, screenLocation[0]); intent.putExtra(EXTRA_TOP, screenLocation[1]); intent.putExtra(EXTRA_WIDTH, transitionView.getWidth()); intent.putExtra(EXTRA_HEIGHT, transitionView.getHeight()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity); activity.startActivity(intent, options.toBundle()); } else { activity.startActivity(intent); } activity.overridePendingTransition(0, R.anim.activity_fade_out); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_photo_detail); ButterKnife.bind(this); Bundle bundle = getIntent().getExtras(); final Photo photo = (Photo) bundle.getSerializable(Photo.class.getSimpleName()); initToolbar(photo); bindPhotoInfo(photo); Picasso.with(this) .load(photo.getImageUrl()) .placeholder(R.color.theme50) .into(imgPreviewDummy); // For circular reveal animation Picasso.with(this) .load(photo.getImageUrl()) .placeholder(R.color.theme50) .into(imgPreviewCover); initPallete(photo); if (savedInstanceState == null) { final int thumbnailTop = bundle.getInt(EXTRA_TOP); final int thumbnailLeft = bundle.getInt(EXTRA_LEFT); final int thumbnailWidth = bundle.getInt(EXTRA_WIDTH); final int thumbnailHeight = bundle.getInt(EXTRA_HEIGHT); originalOrientation = bundle.getInt(EXTRA_ORIENTATION); ViewTreeObserver observer = imgPreviewDummy.getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { // TODO setVisibility(View.gone) does not work... // fab.setVisibility(View.GONE); FabAnimationUtils.scaleOut(fab, 50, null); imgPreviewDummy.getViewTreeObserver().removeOnPreDrawListener(this); int[] screenLocation = new int[2]; imgPreviewDummy.getLocationOnScreen(screenLocation); leftDelta = thumbnailLeft - screenLocation[0]; topDelta = thumbnailTop - screenLocation[1]; widthScale = (float) thumbnailWidth / (imgPreviewDummy.getWidth()); heightScale = (float) thumbnailHeight / imgPreviewDummy.getHeight(); startEnterAnimation(); return true; } }); } onClickFab(); initActivityTransitions(); } private void initActivityTransitions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Slide transition = new Slide(); transition.excludeTarget(android.R.id.statusBarBackground, true); transition.excludeTarget(R.id.img_preview_dummy, true); transition.setInterpolator(new LinearOutSlowInInterpolator()); transition.setDuration(300); getWindow().setEnterTransition(transition); } } private void initPallete(Photo photo) { Picasso.with(this) .load(photo.getImageUrl()) .placeholder(R.color.theme50) .into(new Target() { @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { imgPreview.setImageBitmap(bitmap); colorPalleteView.initColors(bitmap); } @Override public void onBitmapFailed(Drawable errorDrawable) { // } @Override public void onPrepareLoad(Drawable placeHolderDrawable) { // } }); } private void bindPhotoInfo(Photo photo) { photoInfoId.setTitleText(photo.getId()); photoInfoOwner.setTitleText(photo.getOwner()); photoInfoSecret.setTitleText(photo.getSecret()); photoInfoServer.setTitleText(photo.getServer()); photoInfoTitle.setTitleText(photo.getTitle()); photoInfoPublic.setTitleText(String.valueOf(photo.isPublic())); photoInfoFriend.setTitleText(String.valueOf(photo.isFriend())); photoInfoFamily.setTitleText(String.valueOf(photo.isFamily())); } private void initToolbar(Photo photo) { setSupportActionBar(toolbar); ActionBar bar = getSupportActionBar(); if (bar != null) bar.setDisplayHomeAsUpEnabled(true); collapsingToolbarLayout.setTitle(photo.getTitle()); } public void startEnterAnimation() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { imgPreviewDummy.setPivotX(0); imgPreviewDummy.setPivotY(0); imgPreviewDummy.setScaleX(widthScale); imgPreviewDummy.setScaleY(heightScale); imgPreviewDummy.setTranslationX(leftDelta); imgPreviewDummy.setTranslationY(topDelta); } else { AnimatorProxy proxy = AnimatorProxy.wrap(imgPreviewDummy); proxy.setPivotX(0); proxy.setPivotY(0); proxy.setScaleX(widthScale); proxy.setScaleY(heightScale); proxy.setTranslationX(leftDelta); proxy.setTranslationY(topDelta); } ViewPropertyAnimator.animate(imgPreviewDummy) .setDuration(ANIMATION_DURATION) .scaleX(1).scaleY(1) .translationX(0).translationY(0) .setInterpolator(INTERPOLATOR) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // AppBarLayout is set invisible, because AppbarLayout has default background color. appBarLayout.setVisibility(View.VISIBLE); imgPreview.setVisibility(View.VISIBLE); revealPreview(); } }); } @Override public void finish() { super.finish(); overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out); } @Override public void onBackPressed() { finish(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; } return super.onOptionsItemSelected(item); } private void revealPreview() { int cx = (imgPreview.getLeft() + imgPreview.getRight()) / 2; int cy = (imgPreview.getTop() + imgPreview.getBottom()) / 2; float finalRadius = (float) Math.hypot(cx, cy); SupportAnimator animator = ViewAnimationUtils.createCircularReveal(imgPreview, cx, cy, 0, finalRadius); animator.setInterpolator(INTERPOLATOR); animator.setDuration(300); animator.addListener(new SupportAnimator.AnimatorListener() { @Override public void onAnimationStart() { imgPreviewDummy.setVisibility(View.GONE); imgPreviewCover.setVisibility(View.VISIBLE); imgPreview.setVisibility(View.VISIBLE); } @Override public void onAnimationEnd() { FabAnimationUtils.scaleIn(fab); } @Override public void onAnimationCancel() { // } @Override public void onAnimationRepeat() { // } }); animator.start(); } @OnClick(R.id.fab) void onClickFab() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Drawable drawable = fab.getDrawable(); if (drawable instanceof AnimatedVectorDrawable) { if (((AnimatedVectorDrawable) drawable).isRunning()) return; int drawableResId = fab.isSelected() ? R.drawable.ic_pause_to_play : R.drawable.ic_play_to_pause; fab.setImageResource(drawableResId); drawable = fab.getDrawable(); ((AnimatedVectorDrawable) drawable).start(); } } if (fab.isSelected()) { imgPreview.pause(); } else { imgPreview.resume(); } fab.setSelected(!fab.isSelected()); } }