|
|
@ -20,23 +20,24 @@ import org.floens.chan.R; |
|
|
|
import org.floens.chan.controller.Controller; |
|
|
|
import org.floens.chan.controller.Controller; |
|
|
|
import org.floens.chan.ui.view.ClippingImageView; |
|
|
|
import org.floens.chan.ui.view.ClippingImageView; |
|
|
|
import org.floens.chan.utils.AnimationUtils; |
|
|
|
import org.floens.chan.utils.AnimationUtils; |
|
|
|
|
|
|
|
import org.floens.chan.utils.Logger; |
|
|
|
|
|
|
|
|
|
|
|
import static org.floens.chan.utils.AndroidUtils.dp; |
|
|
|
import static org.floens.chan.utils.AndroidUtils.dp; |
|
|
|
import static org.floens.chan.utils.AnimationUtils.calculateBoundsAnimation; |
|
|
|
import static org.floens.chan.utils.AnimationUtils.calculateBoundsAnimation; |
|
|
|
|
|
|
|
|
|
|
|
public class ImageViewController extends Controller implements View.OnClickListener { |
|
|
|
public class ImageViewerController extends Controller implements View.OnClickListener { |
|
|
|
private static final int DURATION = 165; |
|
|
|
private static final int TRANSITION_DURATION = 200; //165;
|
|
|
|
private static final float CLIP_DURATION_PERCENTAGE = 0.40f; |
|
|
|
private static final int TRANSITION_CLIP_DURATION = (int) (TRANSITION_DURATION * 0.5f); |
|
|
|
private static final float FINAL_ALPHA = 0.80f; |
|
|
|
private static final float TRANSITION_FINAL_ALPHA = 0.80f; |
|
|
|
|
|
|
|
|
|
|
|
private ClippingImageView imageView; |
|
|
|
private ClippingImageView previewImage; |
|
|
|
private Callback callback; |
|
|
|
private Callback callback; |
|
|
|
|
|
|
|
|
|
|
|
private int statusBarColorPrevious; |
|
|
|
private int statusBarColorPrevious; |
|
|
|
private AnimatorSet startAnimation; |
|
|
|
private AnimatorSet startPreviewAnimation; |
|
|
|
private AnimatorSet endAnimation; |
|
|
|
private AnimatorSet endAnimation; |
|
|
|
|
|
|
|
|
|
|
|
public ImageViewController(Context context) { |
|
|
|
public ImageViewerController(Context context) { |
|
|
|
super(context); |
|
|
|
super(context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -44,62 +45,59 @@ public class ImageViewController extends Controller implements View.OnClickListe |
|
|
|
public void onCreate() { |
|
|
|
public void onCreate() { |
|
|
|
super.onCreate(); |
|
|
|
super.onCreate(); |
|
|
|
|
|
|
|
|
|
|
|
view = inflateRes(R.layout.controller_view_image); |
|
|
|
view = inflateRes(R.layout.controller_image_viewer); |
|
|
|
|
|
|
|
previewImage = (ClippingImageView) view.findViewById(R.id.image); |
|
|
|
imageView = (ClippingImageView) view.findViewById(R.id.image); |
|
|
|
|
|
|
|
view.setOnClickListener(this); |
|
|
|
view.setOnClickListener(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public boolean onBack() { |
|
|
|
public void onClick(View v) { |
|
|
|
removeImage(); |
|
|
|
startPreviewOutTransition(); |
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onClick(View v) { |
|
|
|
public boolean onBack() { |
|
|
|
removeImage(); |
|
|
|
startPreviewOutTransition(); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void setImage(Callback callback, final ImageView startImageView) { |
|
|
|
public void startPreviewInTransition(Callback callback, final ImageView previewImageView) { |
|
|
|
this.callback = callback; |
|
|
|
this.callback = callback; |
|
|
|
|
|
|
|
|
|
|
|
imageView.setImageDrawable(startImageView.getDrawable()); |
|
|
|
previewImage.setImageDrawable(previewImageView.getDrawable()); |
|
|
|
|
|
|
|
|
|
|
|
Rect startBounds = getStartImageViewBounds(startImageView); |
|
|
|
Rect startBounds = getImageViewBounds(previewImageView); |
|
|
|
final Rect endBounds = new Rect(); |
|
|
|
final Rect endBounds = new Rect(); |
|
|
|
final Point globalOffset = new Point(); |
|
|
|
final Point globalOffset = new Point(); |
|
|
|
view.getGlobalVisibleRect(endBounds, globalOffset); |
|
|
|
view.getGlobalVisibleRect(endBounds, globalOffset); |
|
|
|
float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset); |
|
|
|
float startScale = calculateBoundsAnimation(startBounds, endBounds, globalOffset); |
|
|
|
|
|
|
|
|
|
|
|
imageView.setPivotX(0f); |
|
|
|
previewImage.setPivotX(0f); |
|
|
|
imageView.setPivotY(0f); |
|
|
|
previewImage.setPivotY(0f); |
|
|
|
imageView.setX(startBounds.left); |
|
|
|
previewImage.setX(startBounds.left); |
|
|
|
imageView.setY(startBounds.top); |
|
|
|
previewImage.setY(startBounds.top); |
|
|
|
imageView.setScaleX(startScale); |
|
|
|
previewImage.setScaleX(startScale); |
|
|
|
imageView.setScaleY(startScale); |
|
|
|
previewImage.setScaleY(startScale); |
|
|
|
|
|
|
|
|
|
|
|
Rect clipStartBounds = new Rect(0, 0, (int) (startImageView.getWidth() / startScale), (int) (startImageView.getHeight() / startScale)); |
|
|
|
Rect clipStartBounds = new Rect(0, 0, (int) (previewImageView.getWidth() / startScale), (int) (previewImageView.getHeight() / startScale)); |
|
|
|
|
|
|
|
|
|
|
|
Window window = ((Activity) context).getWindow(); |
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 21) { |
|
|
|
if (Build.VERSION.SDK_INT >= 21) { |
|
|
|
statusBarColorPrevious = window.getStatusBarColor(); |
|
|
|
statusBarColorPrevious = getWindow().getStatusBarColor(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
startAnimation(startBounds, endBounds, startScale, clipStartBounds); |
|
|
|
doPreviewInTransition(startBounds, endBounds, startScale, clipStartBounds); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void removeImage() { |
|
|
|
public void startPreviewOutTransition() { |
|
|
|
if (startAnimation != null || endAnimation != null) { |
|
|
|
if (startPreviewAnimation != null || endAnimation != null) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
endAnimation(); |
|
|
|
doPreviewOutAnimation(); |
|
|
|
// endAnimationEmpty();
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void startAnimation(Rect startBounds, Rect finalBounds, float startScale, final Rect clipStartBounds) { |
|
|
|
private void doPreviewInTransition(Rect startBounds, Rect finalBounds, float startScale, final Rect clipStartBounds) { |
|
|
|
startAnimation = new AnimatorSet(); |
|
|
|
startPreviewAnimation = new AnimatorSet(); |
|
|
|
|
|
|
|
|
|
|
|
ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(0f, 1f); |
|
|
|
ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(0f, 1f); |
|
|
|
backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
|
|
|
backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
|
|
@ -114,42 +112,39 @@ public class ImageViewController extends Controller implements View.OnClickListe |
|
|
|
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
|
|
|
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onAnimationUpdate(ValueAnimator animation) { |
|
|
|
public void onAnimationUpdate(ValueAnimator animation) { |
|
|
|
AnimationUtils.getClippingBounds(clipStartBounds, imageView, clipRect, (float) animation.getAnimatedValue()); |
|
|
|
AnimationUtils.getClippingBounds(clipStartBounds, previewImage, clipRect, (float) animation.getAnimatedValue()); |
|
|
|
imageView.clip(clipRect); |
|
|
|
previewImage.clip(clipRect); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
startAnimation |
|
|
|
startPreviewAnimation |
|
|
|
.play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left, finalBounds.left).setDuration(DURATION)) |
|
|
|
.play(ObjectAnimator.ofFloat(previewImage, View.X, startBounds.left, finalBounds.left).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top, finalBounds.top).setDuration(DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.Y, startBounds.top, finalBounds.top).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, startScale, 1f).setDuration(DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_X, startScale, 1f).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, startScale, 1f).setDuration(DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_Y, startScale, 1f).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(backgroundAlpha.setDuration(DURATION)) |
|
|
|
.with(backgroundAlpha.setDuration(TRANSITION_DURATION)) |
|
|
|
.with(clip.setDuration((long) (DURATION * CLIP_DURATION_PERCENTAGE))); |
|
|
|
.with(clip.setDuration(TRANSITION_CLIP_DURATION)); |
|
|
|
|
|
|
|
|
|
|
|
startAnimation.setInterpolator(new DecelerateInterpolator()); |
|
|
|
startPreviewAnimation.setInterpolator(new DecelerateInterpolator()); |
|
|
|
startAnimation.addListener(new AnimatorListenerAdapter() { |
|
|
|
startPreviewAnimation.addListener(new AnimatorListenerAdapter() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onAnimationEnd(Animator animation) { |
|
|
|
public void onAnimationEnd(Animator animation) { |
|
|
|
startAnimationEnd(); |
|
|
|
previewImage.setX(0f); |
|
|
|
startAnimation = null; |
|
|
|
previewImage.setY(0f); |
|
|
|
|
|
|
|
previewImage.setScaleX(1f); |
|
|
|
|
|
|
|
previewImage.setScaleY(1f); |
|
|
|
|
|
|
|
previewImage.clip(null); |
|
|
|
|
|
|
|
startPreviewAnimation = null; |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
startAnimation.start(); |
|
|
|
startPreviewAnimation.start(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void startAnimationEnd() { |
|
|
|
private void doPreviewOutAnimation() { |
|
|
|
imageView.setX(0f); |
|
|
|
|
|
|
|
imageView.setY(0f); |
|
|
|
|
|
|
|
imageView.setScaleX(1f); |
|
|
|
|
|
|
|
imageView.setScaleY(1f); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void endAnimation() { |
|
|
|
|
|
|
|
ImageView startImage = getStartImageView(); |
|
|
|
ImageView startImage = getStartImageView(); |
|
|
|
Rect startBounds = null; |
|
|
|
Rect startBounds = null; |
|
|
|
if (startImage != null) { |
|
|
|
if (startImage != null) { |
|
|
|
startBounds = getStartImageViewBounds(startImage); |
|
|
|
startBounds = getImageViewBounds(startImage); |
|
|
|
} |
|
|
|
} |
|
|
|
if (startBounds == null) { |
|
|
|
if (startBounds == null) { |
|
|
|
endAnimation = new AnimatorSet(); |
|
|
|
endAnimation = new AnimatorSet(); |
|
|
@ -163,16 +158,16 @@ public class ImageViewController extends Controller implements View.OnClickListe |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
endAnimation |
|
|
|
endAnimation |
|
|
|
.play(ObjectAnimator.ofFloat(imageView, View.Y, imageView.getTop(), imageView.getTop() + dp(20))) |
|
|
|
.play(ObjectAnimator.ofFloat(previewImage, View.Y, previewImage.getTop(), previewImage.getTop() + dp(20))) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.ALPHA, 1f, 0f)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.ALPHA, 1f, 0f)) |
|
|
|
.with(backgroundAlpha); |
|
|
|
.with(backgroundAlpha); |
|
|
|
|
|
|
|
|
|
|
|
endAnimation.setDuration(DURATION); |
|
|
|
endAnimation.setDuration(TRANSITION_DURATION); |
|
|
|
endAnimation.setInterpolator(new DecelerateInterpolator()); |
|
|
|
endAnimation.setInterpolator(new DecelerateInterpolator()); |
|
|
|
endAnimation.addListener(new AnimatorListenerAdapter() { |
|
|
|
endAnimation.addListener(new AnimatorListenerAdapter() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onAnimationEnd(Animator animation) { |
|
|
|
public void onAnimationEnd(Animator animation) { |
|
|
|
endAnimationEnd(); |
|
|
|
previewOutAnimationEnded(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
endAnimation.start(); |
|
|
|
endAnimation.start(); |
|
|
@ -198,75 +193,84 @@ public class ImageViewController extends Controller implements View.OnClickListe |
|
|
|
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
|
|
|
clip.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onAnimationUpdate(ValueAnimator animation) { |
|
|
|
public void onAnimationUpdate(ValueAnimator animation) { |
|
|
|
AnimationUtils.getClippingBounds(clipStartBounds, imageView, clipRect, (float) animation.getAnimatedValue()); |
|
|
|
AnimationUtils.getClippingBounds(clipStartBounds, previewImage, clipRect, (float) animation.getAnimatedValue()); |
|
|
|
imageView.clip(clipRect); |
|
|
|
previewImage.clip(clipRect); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
long clipDuration = (long) (DURATION * CLIP_DURATION_PERCENTAGE); |
|
|
|
clip.setStartDelay(TRANSITION_DURATION - TRANSITION_CLIP_DURATION); |
|
|
|
clip.setStartDelay(DURATION - clipDuration); |
|
|
|
clip.setDuration(TRANSITION_CLIP_DURATION); |
|
|
|
clip.setDuration(clipDuration); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
endAnimation |
|
|
|
endAnimation |
|
|
|
.play(ObjectAnimator.ofFloat(imageView, View.X, startBounds.left).setDuration(DURATION)) |
|
|
|
.play(ObjectAnimator.ofFloat(previewImage, View.X, startBounds.left).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.Y, startBounds.top).setDuration(DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.Y, startBounds.top).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_X, 1f, startScale).setDuration(DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_X, 1f, startScale).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(imageView, View.SCALE_Y, 1f, startScale).setDuration(DURATION)) |
|
|
|
.with(ObjectAnimator.ofFloat(previewImage, View.SCALE_Y, 1f, startScale).setDuration(TRANSITION_DURATION)) |
|
|
|
.with(backgroundAlpha.setDuration(DURATION)) |
|
|
|
.with(backgroundAlpha.setDuration(TRANSITION_DURATION)) |
|
|
|
.with(clip); |
|
|
|
.with(clip); |
|
|
|
|
|
|
|
|
|
|
|
endAnimation.setInterpolator(new DecelerateInterpolator()); |
|
|
|
endAnimation.setInterpolator(new DecelerateInterpolator()); |
|
|
|
endAnimation.addListener(new AnimatorListenerAdapter() { |
|
|
|
endAnimation.addListener(new AnimatorListenerAdapter() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void onAnimationEnd(Animator animation) { |
|
|
|
public void onAnimationEnd(Animator animation) { |
|
|
|
endAnimationEnd(); |
|
|
|
previewOutAnimationEnded(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
endAnimation.start(); |
|
|
|
endAnimation.start(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void endAnimationEnd() { |
|
|
|
private void previewOutAnimationEnded() { |
|
|
|
Window window = ((Activity) context).getWindow(); |
|
|
|
setStatusBarColor(statusBarColorPrevious); |
|
|
|
if (Build.VERSION.SDK_INT >= 21) { |
|
|
|
|
|
|
|
window.setStatusBarColor(statusBarColorPrevious); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
callback.onImageViewLayoutDestroy(this); |
|
|
|
callback.onPreviewDestroy(this); |
|
|
|
stopPresenting(false); |
|
|
|
navigationController.stopPresenting(false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void setBackgroundAlpha(float alpha) { |
|
|
|
private void setBackgroundAlpha(float alpha) { |
|
|
|
alpha = alpha * FINAL_ALPHA; |
|
|
|
alpha *= TRANSITION_FINAL_ALPHA; |
|
|
|
view.setBackgroundColor(Color.argb((int) (alpha * 255f), 0, 0, 0)); |
|
|
|
view.setBackgroundColor(Color.argb((int) (alpha * 255f), 0, 0, 0)); |
|
|
|
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 21) { |
|
|
|
if (Build.VERSION.SDK_INT >= 21) { |
|
|
|
Window window = ((Activity) context).getWindow(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int r = (int) ((1f - alpha) * Color.red(statusBarColorPrevious)); |
|
|
|
int r = (int) ((1f - alpha) * Color.red(statusBarColorPrevious)); |
|
|
|
int g = (int) ((1f - alpha) * Color.green(statusBarColorPrevious)); |
|
|
|
int g = (int) ((1f - alpha) * Color.green(statusBarColorPrevious)); |
|
|
|
int b = (int) ((1f - alpha) * Color.blue(statusBarColorPrevious)); |
|
|
|
int b = (int) ((1f - alpha) * Color.blue(statusBarColorPrevious)); |
|
|
|
|
|
|
|
|
|
|
|
window.setStatusBarColor(Color.argb(255, r, g, b)); |
|
|
|
setStatusBarColor(Color.argb(255, r, g, b)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Rect getStartImageViewBounds(ImageView image) { |
|
|
|
private void setStatusBarColor(int color) { |
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= 21) { |
|
|
|
|
|
|
|
getWindow().setStatusBarColor(color); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Rect getImageViewBounds(ImageView image) { |
|
|
|
Rect startBounds = new Rect(); |
|
|
|
Rect startBounds = new Rect(); |
|
|
|
if (image.getGlobalVisibleRect(startBounds)) { |
|
|
|
if (image.getGlobalVisibleRect(startBounds) && !startBounds.isEmpty()) { |
|
|
|
AnimationUtils.adjustImageViewBoundsToDrawableBounds(image, startBounds); |
|
|
|
AnimationUtils.adjustImageViewBoundsToDrawableBounds(image, startBounds); |
|
|
|
return startBounds; |
|
|
|
if (!startBounds.isEmpty()) { |
|
|
|
|
|
|
|
Logger.test(startBounds.toShortString()); |
|
|
|
|
|
|
|
return startBounds; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private ImageView getStartImageView() { |
|
|
|
private ImageView getStartImageView() { |
|
|
|
return callback.getImageView(this); |
|
|
|
return callback.getPreviewImageStartView(this); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Window getWindow() { |
|
|
|
|
|
|
|
return ((Activity) context).getWindow(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public interface Callback { |
|
|
|
public interface Callback { |
|
|
|
public ImageView getImageView(ImageViewController imageViewController); |
|
|
|
public ImageView getPreviewImageStartView(ImageViewerController imageViewerController); |
|
|
|
|
|
|
|
|
|
|
|
public void onImageViewLayoutDestroy(ImageViewController imageViewController); |
|
|
|
public void onPreviewDestroy(ImageViewerController imageViewerController); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |