diff --git a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java index fe6bea3a..e20e0d45 100644 --- a/Clover/app/src/main/java/org/floens/chan/controller/Controller.java +++ b/Clover/app/src/main/java/org/floens/chan/controller/Controller.java @@ -38,6 +38,7 @@ public abstract class Controller { // Controller (for presenting) members public Controller presentingController; + public Controller presentedController; public Controller(Context context) { this.context = context; @@ -67,32 +68,49 @@ public abstract class Controller { } public void presentController(Controller controller) { + presentController(controller, true); + } + + public void presentController(Controller controller, boolean animated) { ViewGroup contentView = ((BoardActivity) context).getContentView(); + presentedController = controller; controller.presentingController = this; - ControllerTransition transition = new FadeInTransition(); - transition.setCallback(new ControllerTransition.Callback() { - @Override - public void onControllerTransitionCompleted(ControllerTransition transition) { - ControllerLogic.finishTransition(transition); - } - }); - ControllerLogic.startTransition(null, controller, false, true, contentView, transition); + if (animated) { + ControllerTransition transition = new FadeInTransition(); + transition.setCallback(new ControllerTransition.Callback() { + @Override + public void onControllerTransitionCompleted(ControllerTransition transition) { + ControllerLogic.finishTransition(transition); + } + }); + ControllerLogic.startTransition(null, controller, false, true, contentView, transition); + } else { + ControllerLogic.transition(null, controller, false, true, contentView); + } ((BoardActivity) context).addController(controller); } public void stopPresenting() { + stopPresenting(true); + } + + public void stopPresenting(boolean animated) { ViewGroup contentView = ((BoardActivity) context).getContentView(); - ControllerTransition transition = new FadeOutTransition(); - transition.setCallback(new ControllerTransition.Callback() { - @Override - public void onControllerTransitionCompleted(ControllerTransition transition) { - ControllerLogic.finishTransition(transition); - } - }); - ControllerLogic.startTransition(this, null, true, false, contentView, transition); - ((BoardActivity) context).removeController(Controller.this); + if (animated) { + ControllerTransition transition = new FadeOutTransition(); + transition.setCallback(new ControllerTransition.Callback() { + @Override + public void onControllerTransitionCompleted(ControllerTransition transition) { + ControllerLogic.finishTransition(transition); + } + }); + ControllerLogic.startTransition(this, null, true, false, contentView, transition); + } else { + ControllerLogic.transition(this, null, true, false, contentView); + } + ((BoardActivity) context).removeController(this); } public View inflateRes(int resId) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java index a47ad29e..6497cbb8 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ThreadPresenter.java @@ -19,6 +19,7 @@ package org.floens.chan.core.presenter; import android.text.TextUtils; import android.view.Menu; +import android.widget.ImageView; import com.android.volley.VolleyError; @@ -134,7 +135,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt } @Override - public void onThumbnailClicked(Post post) { + public void onThumbnailClicked(Post post, ImageView thumbnail) { List images = new ArrayList<>(); int index = -1; for (int i = 0; i < chanLoader.getThread().posts.size(); i++) { @@ -147,7 +148,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt } } - threadPresenterCallback.showImages(images, index); + threadPresenterCallback.showImages(images, index, thumbnail); } @Override @@ -326,6 +327,6 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt public void showPostsPopup(Post forPost, List posts); - public void showImages(List images, int index); + public void showImages(List images, int index, ImageView thumbnail); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java index 86e1ae66..aaf8a86b 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BrowseController.java @@ -28,11 +28,9 @@ import android.widget.TextView; import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.chan.ChanUrls; -import org.floens.chan.controller.Controller; import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.PostImage; import org.floens.chan.ui.layout.ThreadLayout; import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; @@ -43,14 +41,13 @@ import org.floens.chan.utils.AndroidUtils; import java.util.ArrayList; import java.util.List; -public class BrowseController extends Controller implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, FloatingMenu.FloatingMenuCallback, BoardManager.BoardChangeListener { +public class BrowseController extends ThreadController implements ToolbarMenuItem.ToolbarMenuItemCallback, ThreadLayout.ThreadLayoutCallback, FloatingMenu.FloatingMenuCallback, BoardManager.BoardChangeListener { private static final int REFRESH_ID = 1; private static final int POST_ID = 2; private static final int SEARCH_ID = 101; private static final int SHARE_ID = 102; private static final int SETTINGS_ID = 103; - private ThreadLayout threadLayout; private List boardItems; public BrowseController(Context context) { @@ -84,11 +81,6 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool overflow.setSubMenu(new FloatingMenu(context, overflow.getView(), items)); - threadLayout = new ThreadLayout(context); - threadLayout.setCallback(this); - - view = threadLayout; - loadBoard(ChanApplication.getBoardManager().getSavedBoards().get(0)); } @@ -147,12 +139,6 @@ public class BrowseController extends Controller implements ToolbarMenuItem.Tool navigationController.pushController(viewThreadController); } - @Override - public void showImages(List images, int index) { - ImageViewController imageViewController = new ImageViewController(context); - presentController(imageViewController); - } - @Override public void onBoardsChanged() { loadBoards(); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewController.java index aca3cc4a..db6394d5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewController.java @@ -1,14 +1,40 @@ package org.floens.chan.ui.controller; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; import android.content.Context; +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Build; import android.view.View; -import android.widget.Button; +import android.view.Window; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; import org.floens.chan.R; import org.floens.chan.controller.Controller; +import org.floens.chan.ui.view.ClippingImageView; +import org.floens.chan.utils.AnimationUtils; + +import static org.floens.chan.utils.AndroidUtils.dp; +import static org.floens.chan.utils.AnimationUtils.calculateBoundsAnimation; public class ImageViewController extends Controller implements View.OnClickListener { - private Button button; + private static final int DURATION = 165; + private static final float CLIP_DURATION_PERCENTAGE = 0.40f; + private static final float FINAL_ALPHA = 0.80f; + + private ClippingImageView imageView; + private Callback callback; + + private int statusBarColorPrevious; + private AnimatorSet startAnimation; + private AnimatorSet endAnimation; public ImageViewController(Context context) { super(context); @@ -20,20 +46,227 @@ public class ImageViewController extends Controller implements View.OnClickListe view = inflateRes(R.layout.controller_view_image); - button = (Button) view.findViewById(R.id.button); - button.setOnClickListener(this); + imageView = (ClippingImageView) view.findViewById(R.id.image); + view.setOnClickListener(this); } @Override public boolean onBack() { - stopPresenting(); + removeImage(); return true; } @Override public void onClick(View v) { - if (v == button) { - stopPresenting(); + removeImage(); + } + + public void setImage(Callback callback, final ImageView startImageView) { + this.callback = callback; + + imageView.setImageDrawable(startImageView.getDrawable()); + + Rect startBounds = getStartImageViewBounds(startImageView); + final Rect endBounds = new Rect(); + final Point globalOffset = new Point(); + view.getGlobalVisibleRect(endBounds, globalOffset); + float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset); + + imageView.setPivotX(0f); + imageView.setPivotY(0f); + imageView.setX(startBounds.left); + imageView.setY(startBounds.top); + imageView.setScaleX(startScale); + imageView.setScaleY(startScale); + + Rect clipStartBounds = new Rect(0, 0, (int) (startImageView.getWidth() / startScale), (int) (startImageView.getHeight() / startScale)); + + Window window = ((Activity) context).getWindow(); + if (Build.VERSION.SDK_INT >= 21) { + statusBarColorPrevious = window.getStatusBarColor(); + } + + startAnimation(startBounds, endBounds, startScale, clipStartBounds); + } + + public void removeImage() { + if (startAnimation != null || endAnimation != null) { + return; } + + endAnimation(); +// endAnimationEmpty(); + } + + private void startAnimation(Rect startBounds, Rect finalBounds, float startScale, final Rect clipStartBounds) { + startAnimation = new AnimatorSet(); + + ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(0f, 1f); + backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setBackgroundAlpha((float) animation.getAnimatedValue()); + } + }); + + final Rect clipRect = new Rect(); + ValueAnimator clip = ValueAnimator.ofFloat(1f, 0f); + clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + AnimationUtils.getClippingBounds(clipStartBounds, imageView, clipRect, (float) animation.getAnimatedValue()); + imageView.clip(clipRect); + } + }); + + startAnimation + .play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left, finalBounds.left).setDuration(DURATION)) + .with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top, finalBounds.top).setDuration(DURATION)) + .with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, startScale, 1f).setDuration(DURATION)) + .with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, startScale, 1f).setDuration(DURATION)) + .with(backgroundAlpha.setDuration(DURATION)) + .with(clip.setDuration((long) (DURATION * CLIP_DURATION_PERCENTAGE))); + + startAnimation.setInterpolator(new DecelerateInterpolator()); + startAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + startAnimationEnd(); + startAnimation = null; + } + }); + startAnimation.start(); + } + + private void startAnimationEnd() { + imageView.setX(0f); + imageView.setY(0f); + imageView.setScaleX(1f); + imageView.setScaleY(1f); + } + + private void endAnimation() { + ImageView startImage = getStartImageView(); + Rect startBounds = null; + if (startImage != null) { + startBounds = getStartImageViewBounds(startImage); + } + if (startBounds == null) { + endAnimation = new AnimatorSet(); + + ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f); + backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setBackgroundAlpha((float) animation.getAnimatedValue()); + } + }); + + endAnimation + .play(ObjectAnimator.ofFloat(imageView, View.Y, imageView.getTop(), imageView.getTop() + dp(20))) + .with(ObjectAnimator.ofFloat(imageView, View.ALPHA, 1f, 0f)) + .with(backgroundAlpha); + + endAnimation.setDuration(DURATION); + endAnimation.setInterpolator(new DecelerateInterpolator()); + endAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + endAnimationEnd(); + } + }); + endAnimation.start(); + } else { + final Rect endBounds = new Rect(); + final Point globalOffset = new Point(); + view.getGlobalVisibleRect(endBounds, globalOffset); + float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset); + + endAnimation = new AnimatorSet(); + + ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f); + backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setBackgroundAlpha((float) animation.getAnimatedValue()); + } + }); + + final Rect clipStartBounds = new Rect(0, 0, (int) (startImage.getWidth() / startScale), (int) (startImage.getHeight() / startScale)); + final Rect clipRect = new Rect(); + ValueAnimator clip = ValueAnimator.ofFloat(0f, 1f); + clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + AnimationUtils.getClippingBounds(clipStartBounds, imageView, clipRect, (float) animation.getAnimatedValue()); + imageView.clip(clipRect); + } + }); + long clipDuration = (long) (DURATION * CLIP_DURATION_PERCENTAGE); + clip.setStartDelay(DURATION - clipDuration); + clip.setDuration(clipDuration); + + endAnimation + .play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left).setDuration(DURATION)) + .with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top).setDuration(DURATION)) + .with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, 1f, startScale).setDuration(DURATION)) + .with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, 1f, startScale).setDuration(DURATION)) + .with(backgroundAlpha.setDuration(DURATION)) + .with(clip); + + endAnimation.setInterpolator(new DecelerateInterpolator()); + endAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + endAnimationEnd(); + } + }); + endAnimation.start(); + } + } + + private void endAnimationEnd() { + Window window = ((Activity) context).getWindow(); + if (Build.VERSION.SDK_INT >= 21) { + window.setStatusBarColor(statusBarColorPrevious); + } + + callback.onImageViewLayoutDestroy(this); + stopPresenting(false); + } + + private void setBackgroundAlpha(float alpha) { + alpha = alpha * FINAL_ALPHA; + view.setBackgroundColor(Color.argb((int) (alpha * 255f), 0, 0, 0)); + + if (Build.VERSION.SDK_INT >= 21) { + Window window = ((Activity) context).getWindow(); + + int r = (int) ((1f - alpha) * Color.red(statusBarColorPrevious)); + int g = (int) ((1f - alpha) * Color.green(statusBarColorPrevious)); + int b = (int) ((1f - alpha) * Color.blue(statusBarColorPrevious)); + + window.setStatusBarColor(Color.argb(255, r, g, b)); + } + } + + private Rect getStartImageViewBounds(ImageView image) { + Rect startBounds = new Rect(); + if (image.getGlobalVisibleRect(startBounds)) { + AnimationUtils.adjustImageViewBoundsToDrawableBounds(image, startBounds); + return startBounds; + } else { + return null; + } + } + + private ImageView getStartImageView() { + return callback.getImageView(this); + } + + public interface Callback { + public ImageView getImageView(ImageViewController imageViewController); + + public void onImageViewLayoutDestroy(ImageViewController imageViewController); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java new file mode 100644 index 00000000..c0e3a50b --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThreadController.java @@ -0,0 +1,52 @@ +package org.floens.chan.ui.controller; + +import android.content.Context; +import android.view.View; +import android.widget.ImageView; + +import org.floens.chan.controller.Controller; +import org.floens.chan.core.model.PostImage; +import org.floens.chan.ui.layout.ThreadLayout; +import org.floens.chan.utils.AndroidUtils; + +import java.util.List; + +public abstract class ThreadController extends Controller implements ThreadLayout.ThreadLayoutCallback, ImageViewController.Callback { + protected ThreadLayout threadLayout; + private ImageView presentingImageView; + + public ThreadController(Context context) { + super(context); + + threadLayout = new ThreadLayout(context); + threadLayout.setCallback(this); + view = threadLayout; + } + + @Override + public void showImages(List images, int index, final ImageView thumbnail) { + presentingImageView = thumbnail; + presentingImageView.setVisibility(View.INVISIBLE); + + final ImageViewController imageViewController = new ImageViewController(context); + presentController(imageViewController, false); + AndroidUtils.waitForMeasure(imageViewController.view, new AndroidUtils.OnMeasuredCallback() { + @Override + public boolean onMeasured(View view) { + imageViewController.setImage(ThreadController.this, thumbnail); + return true; + } + }); + } + + @Override + public ImageView getImageView(ImageViewController imageViewController) { + return presentingImageView; + } + + @Override + public void onImageViewLayoutDestroy(ImageViewController imageViewController) { + presentingImageView.setVisibility(View.VISIBLE); + presentingImageView = null; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java index d6dce614..8db6f9dd 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ViewThreadController.java @@ -22,15 +22,10 @@ import android.content.Context; import android.content.DialogInterface; import org.floens.chan.R; -import org.floens.chan.controller.Controller; import org.floens.chan.core.model.Loadable; -import org.floens.chan.core.model.PostImage; import org.floens.chan.ui.layout.ThreadLayout; -import java.util.List; - -public class ViewThreadController extends Controller implements ThreadLayout.ThreadLayoutCallback { - private ThreadLayout threadLayout; +public class ViewThreadController extends ThreadController implements ThreadLayout.ThreadLayoutCallback { private Loadable loadable; public ViewThreadController(Context context) { @@ -45,9 +40,6 @@ public class ViewThreadController extends Controller implements ThreadLayout.Thr public void onCreate() { super.onCreate(); - threadLayout = new ThreadLayout(context); - threadLayout.setCallback(this); - view = threadLayout; view.setBackgroundColor(0xffffffff); threadLayout.getPresenter().bindLoadable(loadable); @@ -71,9 +63,4 @@ public class ViewThreadController extends Controller implements ThreadLayout.Thr .setMessage("/" + threadLoadable.board + "/" + threadLoadable.no) .show(); } - - @Override - public void showImages(List images, int index) { - - } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ImageViewLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ImageViewLayout.java deleted file mode 100644 index 2eb111c7..00000000 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ImageViewLayout.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Clover - 4chan browser https://github.com/Floens/Clover/ - * Copyright (C) 2014 Floens - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.floens.chan.ui.layout; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.app.Activity; -import android.content.Context; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.animation.DecelerateInterpolator; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import org.floens.chan.R; - -import static org.floens.chan.utils.AndroidUtils.dp; -import static org.floens.chan.utils.AnimationUtils.calculateBoundsAnimation; - - -public class ImageViewLayout extends FrameLayout implements View.OnClickListener { - private ImageView imageView; - - private Callback callback; - private Drawable drawable; - - private int statusBarColorPrevious; - private AnimatorSet startAnimation; - private AnimatorSet endAnimation; - - public static ImageViewLayout attach(Window window) { - ImageViewLayout imageViewLayout = (ImageViewLayout) LayoutInflater.from(window.getContext()).inflate(R.layout.image_view_layout, null); - window.addContentView(imageViewLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - return imageViewLayout; - } - - public ImageViewLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - this.imageView = (ImageView) findViewById(R.id.image); - setOnClickListener(this); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - super.onTouchEvent(event); - return true; - } - - @Override - public void onClick(View v) { - removeImage(); - } - - public void setImage(Callback callback, final Drawable drawable) { - this.callback = callback; - this.drawable = drawable; - - this.imageView.setImageDrawable(drawable); - - Rect startBounds = callback.getImageViewLayoutStartBounds(); - final Rect endBounds = new Rect(); - final Point globalOffset = new Point(); - getGlobalVisibleRect(endBounds, globalOffset); - float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset); - - imageView.setPivotX(0f); - imageView.setPivotY(0f); - imageView.setX(startBounds.left); - imageView.setY(startBounds.top); - imageView.setScaleX(startScale); - imageView.setScaleY(startScale); - - Window window = ((Activity) getContext()).getWindow(); - if (Build.VERSION.SDK_INT >= 21) { - statusBarColorPrevious = window.getStatusBarColor(); - } - - startAnimation(startBounds, endBounds, startScale); - } - - public void removeImage() { - if (startAnimation != null || endAnimation != null) { - return; - } - - endAnimation(); -// endAnimationEmpty(); - } - - private void startAnimation(Rect startBounds, Rect finalBounds, float startScale) { - startAnimation = new AnimatorSet(); - - ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(0f, 1f); - backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - setBackgroundAlpha((float) animation.getAnimatedValue()); - } - }); - - startAnimation - .play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left, finalBounds.left)) - .with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top, finalBounds.top)) - .with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, startScale, 1f)) - .with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, startScale, 1f)) - .with(backgroundAlpha); - - startAnimation.setDuration(200); - startAnimation.setInterpolator(new DecelerateInterpolator()); - startAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - startAnimationEnd(); - startAnimation = null; - } - }); - startAnimation.start(); - } - - private void startAnimationEnd() { - imageView.setX(0f); - imageView.setY(0f); - imageView.setScaleX(1f); - imageView.setScaleY(1f); -// controller.setVisibility(false); - } - - private void endAnimation() { -// controller.setVisibility(true); - - Rect startBounds = callback.getImageViewLayoutStartBounds(); - final Rect endBounds = new Rect(); - final Point globalOffset = new Point(); - getGlobalVisibleRect(endBounds, globalOffset); - float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset); - - endAnimation = new AnimatorSet(); - - ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f); - backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - setBackgroundAlpha((float) animation.getAnimatedValue()); - } - }); - - endAnimation - .play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left)) - .with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top)) - .with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, 1f, startScale)) - .with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, 1f, startScale)) - .with(backgroundAlpha); - - endAnimation.setDuration(200); - endAnimation.setInterpolator(new DecelerateInterpolator()); - endAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - endAnimationEnd(); - } - }); - endAnimation.start(); - } - - private void endAnimationEmpty() { - endAnimation = new AnimatorSet(); - - ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f); - backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - setBackgroundAlpha((float) animation.getAnimatedValue()); - } - }); - endAnimation - .play(ObjectAnimator.ofFloat(imageView, View.Y, imageView.getTop(), imageView.getTop() + dp(20))) - .with(ObjectAnimator.ofFloat(imageView, View.ALPHA, 1f, 0f)) - .with(backgroundAlpha); - - endAnimation.setDuration(200); - endAnimation.setInterpolator(new DecelerateInterpolator()); - endAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - endAnimationEnd(); - } - }); - endAnimation.start(); - } - - private void endAnimationEnd() { - Window window = ((Activity) getContext()).getWindow(); - if (Build.VERSION.SDK_INT >= 21) { - window.setStatusBarColor(statusBarColorPrevious); - } - - callback.onImageViewLayoutDestroy(); - } - - private void setBackgroundAlpha(float alpha) { - setBackgroundColor(Color.argb((int) (alpha * 255f), 0, 0, 0)); - - if (Build.VERSION.SDK_INT >= 21) { - Window window = ((Activity) getContext()).getWindow(); - - int r = (int) ((1f - alpha) * Color.red(statusBarColorPrevious)); - int g = (int) ((1f - alpha) * Color.green(statusBarColorPrevious)); - int b = (int) ((1f - alpha) * Color.blue(statusBarColorPrevious)); - - window.setStatusBarColor(Color.argb(255, r, g, b)); - } - } - - public interface Callback { - public Rect getImageViewLayoutStartBounds(); - - public void onImageViewLayoutDestroy(); - } -} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java index f35b4386..24121f2a 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadLayout.java @@ -23,18 +23,19 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.util.AttributeSet; +import android.widget.ImageView; import android.widget.Toast; import com.android.volley.VolleyError; import org.floens.chan.R; -import org.floens.chan.core.model.PostImage; -import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.core.model.ChanThread; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; +import org.floens.chan.core.model.PostImage; import org.floens.chan.core.model.PostLinkable; import org.floens.chan.core.presenter.ThreadPresenter; +import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.helper.PostPopupHelper; import org.floens.chan.ui.view.LoadView; import org.floens.chan.utils.AndroidUtils; @@ -163,8 +164,8 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres } @Override - public void showImages(List images, int index) { - callback.showImages(images, index); + public void showImages(List images, int index, ImageView thumbnail) { + callback.showImages(images, index, thumbnail); } private void switchVisible(boolean visible) { @@ -177,6 +178,6 @@ public class ThreadLayout extends LoadView implements ThreadPresenter.ThreadPres public interface ThreadLayoutCallback { public void openThread(Loadable threadLoadable); - public void showImages(List images, int index); + public void showImages(List images, int index, ImageView thumbnail); } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/transition/ImageTransition.java b/Clover/app/src/main/java/org/floens/chan/ui/transition/ImageTransition.java new file mode 100644 index 00000000..e92fd6aa --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/transition/ImageTransition.java @@ -0,0 +1,10 @@ +package org.floens.chan.ui.transition; + +import org.floens.chan.controller.ControllerTransition; + +public class ImageTransition extends ControllerTransition { + @Override + public void perform() { + + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/ClippingImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/ClippingImageView.java new file mode 100644 index 00000000..cddb703f --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/ClippingImageView.java @@ -0,0 +1,37 @@ +package org.floens.chan.ui.view; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.ImageView; + +public class ClippingImageView extends ImageView { + private Rect clipRect = new Rect(); + + public ClippingImageView(Context context) { + super(context); + } + + public ClippingImageView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ClippingImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onDraw(Canvas canvas) { + if (!clipRect.isEmpty() && (clipRect.width() < getWidth() || clipRect.height() < getHeight())) { + canvas.clipRect(clipRect); + } + + super.onDraw(canvas); + } + + public void clip(Rect rect) { + clipRect.set(rect); + invalidate(); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java index b4569c67..5d7df31a 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/PostView.java @@ -323,7 +323,7 @@ public class PostView extends LinearLayout implements View.OnClickListener { imageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - callback.onThumbnailClicked(post); + callback.onThumbnailClicked(post, imageView); } }); @@ -491,7 +491,7 @@ public class PostView extends LinearLayout implements View.OnClickListener { public void onPostClicked(Post post); - public void onThumbnailClicked(Post post); + public void onThumbnailClicked(Post post, ImageView thumbnail); public void onShowPostReplies(Post post); diff --git a/Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java b/Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java index 41d49fe1..45de5745 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java @@ -61,6 +61,23 @@ public class AnimationUtils { return startScale; } + public static void getClippingBounds(Rect clipStartBounds, ImageView view, Rect out, float progress) { + float[] f = new float[9]; + view.getImageMatrix().getValues(f); + float imageWidth = view.getDrawable().getIntrinsicWidth() * f[Matrix.MSCALE_X]; + float imageHeight = view.getDrawable().getIntrinsicHeight() * f[Matrix.MSCALE_Y]; + float imageWidthDiff = (view.getWidth() - imageWidth) / 2f; + float imageHeightDiff = (view.getHeight() - imageHeight) / 2f; + + float offsetWidth = ((imageWidth - clipStartBounds.right) / 2f) * progress; + float offsetHeight = ((imageHeight - clipStartBounds.bottom) / 2f) * progress; + + out.set((int) (offsetWidth + imageWidthDiff), + (int) (offsetHeight + imageHeightDiff), + (int) (view.getWidth() - offsetWidth - imageWidthDiff), + (int) (view.getHeight() - offsetHeight - imageHeightDiff)); + } + public static void adjustImageViewBoundsToDrawableBounds(ImageView imageView, Rect bounds) { float[] f = new float[9]; imageView.getImageMatrix().getValues(f); diff --git a/Clover/app/src/main/res/layout/controller_view_image.xml b/Clover/app/src/main/res/layout/controller_view_image.xml index 0eeae2ee..868ad5ca 100644 --- a/Clover/app/src/main/res/layout/controller_view_image.xml +++ b/Clover/app/src/main/res/layout/controller_view_image.xml @@ -1,29 +1,12 @@ - + android:layout_height="match_parent"> - + android:layout_height="match_parent" /> - - -