Add transition view scale support, update ssiv

filtering
Floens 10 years ago
parent 463b7e5291
commit 4a948c591f
  1. 1
      Clover/app/build.gradle
  2. 55
      Clover/app/src/main/java/com/davemorrissey/labs/subscaleview/ImageViewState.java
  3. 1523
      Clover/app/src/main/java/com/davemorrissey/labs/subscaleview/ScaleImageView.java
  4. 1842
      Clover/app/src/main/java/com/davemorrissey/labs/subscaleview/SubsamplingScaleImageView.java
  5. 40
      Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java
  6. 74
      Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java
  7. 83
      Clover/app/src/main/java/org/floens/chan/ui/view/CustomScaleImageView.java
  8. 129
      Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java
  9. 76
      Clover/app/src/main/java/org/floens/chan/ui/view/TransitionImageView.java
  10. 1
      Clover/app/src/main/res/values/strings.xml

@ -80,6 +80,7 @@ dependencies {
compile 'com.j256.ormlite:ormlite-android:4.48' compile 'com.j256.ormlite:ormlite-android:4.48'
compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.0' compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.0'
compile 'com.squareup.okhttp:okhttp:2.2.0' compile 'com.squareup.okhttp:okhttp:2.2.0'
compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.1.3'
compile files('libs/httpclientandroidlib-1.2.1.jar') compile files('libs/httpclientandroidlib-1.2.1.jar')
} }

@ -1,55 +0,0 @@
/*
Copyright 2014 David Morrissey
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.davemorrissey.labs.subscaleview;
import android.graphics.PointF;
import java.io.Serializable;
/**
* Wraps the scale, center and orientation of a displayed image for easy restoration on screen rotate.
*/
public class ImageViewState implements Serializable {
private float scale;
private float centerX;
private float centerY;
private int orientation;
public ImageViewState(float scale, PointF center, int orientation) {
this.scale = scale;
this.centerX = center.x;
this.centerY = center.y;
this.orientation = orientation;
}
public float getScale() {
return scale;
}
public PointF getCenter() {
return new PointF(centerX, centerY);
}
public int getOrientation() {
return orientation;
}
}

@ -18,6 +18,7 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
private final Callback callback; private final Callback callback;
private final boolean imageAutoLoad = ChanSettings.imageAutoLoad.get(); private final boolean imageAutoLoad = ChanSettings.imageAutoLoad.get();
private final boolean movieAutoLoad = imageAutoLoad && ChanSettings.videoAutoLoad.get();
private boolean entering = true; private boolean entering = true;
private boolean exiting = false; private boolean exiting = false;
@ -137,18 +138,45 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.
// onModeLoaded when a unloaded image was swiped to the center earlier // onModeLoaded when a unloaded image was swiped to the center earlier
private void onLowResInCenter() { private void onLowResInCenter() {
PostImage postImage = images.get(selectedPosition); PostImage postImage = images.get(selectedPosition);
if (postImage.type == PostImage.Type.STATIC) {
callback.setImageMode(postImage, MultiImageView.Mode.BIGIMAGE); if (imageAutoLoad) {
} else { if (postImage.type == PostImage.Type.STATIC) {
// todo callback.setImageMode(postImage, MultiImageView.Mode.BIGIMAGE);
} else if (postImage.type == PostImage.Type.GIF) {
callback.setImageMode(postImage, MultiImageView.Mode.GIF);
} else if (postImage.type == PostImage.Type.MOVIE && movieAutoLoad) {
callback.setImageMode(postImage, MultiImageView.Mode.MOVIE);
}
} }
} }
@Override @Override
public void onTap(MultiImageView multiImageView) { public void onTap(MultiImageView multiImageView) {
// Don't mistake a swipe from a user when the pager is disabled as a tap // Don't mistake a swipe when the pager is disabled as a tap
if (viewPagerVisible) { if (viewPagerVisible) {
onExit(); PostImage postImage = images.get(selectedPosition);
if (imageAutoLoad) {
if (movieAutoLoad) {
onExit();
} else {
if (postImage.type == PostImage.Type.MOVIE) {
callback.setImageMode(postImage, MultiImageView.Mode.MOVIE);
} else {
onExit();
}
}
} else {
MultiImageView.Mode currentMode = callback.getImageMode(postImage);
if (postImage.type == PostImage.Type.STATIC && currentMode != MultiImageView.Mode.BIGIMAGE) {
callback.setImageMode(postImage, MultiImageView.Mode.BIGIMAGE);
} else if (postImage.type == PostImage.Type.GIF && currentMode != MultiImageView.Mode.GIF) {
callback.setImageMode(postImage, MultiImageView.Mode.GIF);
} else if (postImage.type == PostImage.Type.MOVIE && currentMode != MultiImageView.Mode.MOVIE) {
callback.setImageMode(postImage, MultiImageView.Mode.MOVIE);
} else {
onExit();
}
}
} }
} }

@ -10,6 +10,7 @@ import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
@ -20,6 +21,7 @@ import android.widget.ImageView;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.ImageLoader;
import com.davemorrissey.labs.subscaleview.ImageViewState;
import org.floens.chan.ChanApplication; import org.floens.chan.ChanApplication;
import org.floens.chan.R; import org.floens.chan.R;
@ -28,10 +30,12 @@ import org.floens.chan.core.model.PostImage;
import org.floens.chan.core.presenter.ImageViewerPresenter; import org.floens.chan.core.presenter.ImageViewerPresenter;
import org.floens.chan.ui.adapter.ImageViewerAdapter; import org.floens.chan.ui.adapter.ImageViewerAdapter;
import org.floens.chan.ui.toolbar.Toolbar; import org.floens.chan.ui.toolbar.Toolbar;
import org.floens.chan.ui.view.CustomScaleImageView;
import org.floens.chan.ui.view.MultiImageView; import org.floens.chan.ui.view.MultiImageView;
import org.floens.chan.ui.view.OptionalSwipeViewPager; import org.floens.chan.ui.view.OptionalSwipeViewPager;
import org.floens.chan.ui.view.TransitionImageView; import org.floens.chan.ui.view.TransitionImageView;
import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.AndroidUtils;
import org.floens.chan.utils.Logger;
import java.util.List; import java.util.List;
@ -40,10 +44,10 @@ import static org.floens.chan.utils.AndroidUtils.dp;
public class ImageViewerController extends Controller implements View.OnClickListener, ImageViewerPresenter.Callback { public class ImageViewerController extends Controller implements View.OnClickListener, ImageViewerPresenter.Callback {
private static final String TAG = "ImageViewerController"; private static final String TAG = "ImageViewerController";
private static final int TRANSITION_DURATION = 200; private static final int TRANSITION_DURATION = 200;
private static final float TRANSITION_FINAL_ALPHA = 0.80f; private static final float TRANSITION_FINAL_ALPHA = 0.85f;
private int statusBarColorPrevious; private int statusBarColorPrevious;
private AnimatorSet startPreviewAnimation; private AnimatorSet startAnimation;
private AnimatorSet endAnimation; private AnimatorSet endAnimation;
private PreviewCallback previewCallback; private PreviewCallback previewCallback;
@ -134,6 +138,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
ImageView startImageView = getTransitionImageView(postImage); ImageView startImageView = getTransitionImageView(postImage);
if (!setTransitionViewData(startImageView)) { if (!setTransitionViewData(startImageView)) {
Logger.test("Oops");
return; // TODO return; // TODO
} }
@ -141,7 +146,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
statusBarColorPrevious = getWindow().getStatusBarColor(); statusBarColorPrevious = getWindow().getStatusBarColor();
} }
startPreviewAnimation = new AnimatorSet(); startAnimation = new AnimatorSet();
ValueAnimator progress = ValueAnimator.ofFloat(0f, 1f); ValueAnimator progress = ValueAnimator.ofFloat(0f, 1f);
progress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { progress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@ -152,10 +157,10 @@ public class ImageViewerController extends Controller implements View.OnClickLis
} }
}); });
startPreviewAnimation.play(progress); startAnimation.play(progress);
startPreviewAnimation.setDuration(TRANSITION_DURATION); startAnimation.setDuration(TRANSITION_DURATION);
startPreviewAnimation.setInterpolator(new DecelerateInterpolator()); startAnimation.setInterpolator(new DecelerateInterpolator());
startPreviewAnimation.addListener(new AnimatorListenerAdapter() { startAnimation.addListener(new AnimatorListenerAdapter() {
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
previewCallback.onPreviewCreate(ImageViewerController.this); previewCallback.onPreviewCreate(ImageViewerController.this);
@ -163,15 +168,15 @@ public class ImageViewerController extends Controller implements View.OnClickLis
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
startPreviewAnimation = null; startAnimation = null;
presenter.onInTransitionEnd(); presenter.onInTransitionEnd();
} }
}); });
startPreviewAnimation.start(); startAnimation.start();
} }
public void startPreviewOutTransition(final PostImage postImage) { public void startPreviewOutTransition(final PostImage postImage) {
if (startPreviewAnimation != null || endAnimation != null) { if (startAnimation != null || endAnimation != null) {
return; return;
} }
@ -191,11 +196,22 @@ public class ImageViewerController extends Controller implements View.OnClickLis
} }
private void doPreviewOutAnimation(PostImage postImage, Bitmap bitmap) { private void doPreviewOutAnimation(PostImage postImage, Bitmap bitmap) {
// Find translation and scale if the current displayed image was a bigimage
MultiImageView multiImageView = ((ImageViewerAdapter) pager.getAdapter()).find(postImage);
CustomScaleImageView customScaleImageView = multiImageView.findScaleImageView();
if (customScaleImageView != null) {
ImageViewState state = customScaleImageView.getState();
if (state != null) {
PointF p = customScaleImageView.viewToSourceCoord(0f, 0f);
PointF bitmapSize = new PointF(customScaleImageView.getSWidth(), customScaleImageView.getSHeight());
previewImage.setState(state.getScale(), p, bitmapSize);
}
}
ImageView startImage = getTransitionImageView(postImage); ImageView startImage = getTransitionImageView(postImage);
endAnimation = new AnimatorSet();
if (!setTransitionViewData(startImage) || bitmap == null) { if (!setTransitionViewData(startImage) || bitmap == null) {
endAnimation = new AnimatorSet();
ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f); ValueAnimator backgroundAlpha = ValueAnimator.ofFloat(1f, 0f);
backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { backgroundAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override @Override
@ -209,18 +225,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis
.with(ObjectAnimator.ofFloat(previewImage, View.ALPHA, 1f, 0f)) .with(ObjectAnimator.ofFloat(previewImage, View.ALPHA, 1f, 0f))
.with(backgroundAlpha); .with(backgroundAlpha);
endAnimation.setDuration(TRANSITION_DURATION);
endAnimation.setInterpolator(new DecelerateInterpolator());
endAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
previewOutAnimationEnded();
}
});
endAnimation.start();
} else { } else {
endAnimation = new AnimatorSet();
ValueAnimator progress = ValueAnimator.ofFloat(1f, 0f); ValueAnimator progress = ValueAnimator.ofFloat(1f, 0f);
progress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { progress.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override @Override
@ -231,21 +236,16 @@ public class ImageViewerController extends Controller implements View.OnClickLis
}); });
endAnimation.play(progress); endAnimation.play(progress);
endAnimation.setDuration(TRANSITION_DURATION);
endAnimation.setInterpolator(new DecelerateInterpolator());
endAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
previewCallback.onPreviewCreate(ImageViewerController.this);
}
@Override
public void onAnimationEnd(Animator animation) {
previewOutAnimationEnded();
}
});
endAnimation.start();
} }
endAnimation.setDuration(TRANSITION_DURATION);
endAnimation.setInterpolator(new DecelerateInterpolator());
endAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
previewOutAnimationEnded();
}
});
endAnimation.start();
} }
private void previewOutAnimationEnded() { private void previewOutAnimationEnded() {

@ -1,18 +1,3 @@
/**
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.floens.chan.ui.view; package org.floens.chan.ui.view;
import android.content.Context; import android.content.Context;
@ -20,54 +5,72 @@ import android.util.AttributeSet;
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.Logger;
public class CustomScaleImageView extends SubsamplingScaleImageView { public class CustomScaleImageView extends SubsamplingScaleImageView {
private InitedCallback initCallback; private static final String TAG = "CustomScaleImageView";
private Callback callback;
public CustomScaleImageView(Context context, AttributeSet attr) { public CustomScaleImageView(Context context, AttributeSet attr) {
super(context, attr); super(context, attr);
init();
} }
public CustomScaleImageView(Context context) { public CustomScaleImageView(Context context) {
super(context); super(context);
init();
} }
public void setInitCallback(InitedCallback initCallback) { public void setCallback(Callback callback) {
this.initCallback = initCallback; this.callback = callback;
} }
@Override private void init() {
protected void onImageReady() { setOnImageEventListener(new OnImageEventListener() {
super.onImageReady();
AndroidUtils.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void onReady() {
if (initCallback != null) { float scale = Math.min(getWidth() / (float) getSWidth(), getHeight() / (float) getSHeight());
initCallback.onInit(); setMinScale(scale);
if (getMaxScale() < scale * 2f) {
setMaxScale(scale * 2f);
}
setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM);
if (callback != null) {
callback.onReady();
} }
} }
});
}
@Override @Override
protected void onOutOfMemory() { public void onImageLoaded() {
super.onOutOfMemory(); }
AndroidUtils.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void onPreviewLoadError(Exception e) {
if (initCallback != null) { }
initCallback.onOutOfMemory();
@Override
public void onImageLoadError(Exception e) {
Logger.w(TAG, "onImageLoadError", e);
if (callback != null) {
callback.onError(true);
}
}
@Override
public void onTileLoadError(Exception e) {
Logger.w(TAG, "onTileLoadError", e);
if (callback != null) {
callback.onError(false);
} }
} }
}); });
} }
public interface InitedCallback { public interface Callback {
public void onInit(); public void onReady();
public void onError(boolean wasInitial);
public void onOutOfMemory();
} }
} }

@ -33,6 +33,7 @@ import android.widget.VideoView;
import com.android.volley.VolleyError; import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader.ImageContainer; import com.android.volley.toolbox.ImageLoader.ImageContainer;
import com.davemorrissey.labs.subscaleview.ImageSource;
import org.floens.chan.ChanApplication; import org.floens.chan.ChanApplication;
import org.floens.chan.R; import org.floens.chan.R;
@ -51,7 +52,7 @@ import pl.droidsonroids.gif.GifImageView;
public class MultiImageView extends FrameLayout implements View.OnClickListener { public class MultiImageView extends FrameLayout implements View.OnClickListener {
public enum Mode { public enum Mode {
UNLOADED, LOWRES, BIGIMAGE UNLOADED, LOWRES, BIGIMAGE, GIF, MOVIE
} }
private static final String TAG = "MultiImageView"; private static final String TAG = "MultiImageView";
@ -97,32 +98,31 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
return postImage; return postImage;
} }
public void setMode(Mode mode) { public void setMode(final Mode newMode) {
if (this.mode != mode) { if (this.mode != newMode) {
final Mode previousTargetMode = this.mode; Logger.d(TAG, "Changing mode from " + this.mode + " to " + newMode + " for " + postImage.thumbnailUrl);
this.mode = mode; this.mode = newMode;
Logger.d(TAG, "Changing mode from " + previousTargetMode + "to " + mode + " for " + postImage.thumbnailUrl);
if (mode == Mode.LOWRES) { AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() {
AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() { @Override
@Override public boolean onMeasured(View view) {
public boolean onMeasured(View view) { switch (newMode) {
setThumbnail(postImage.thumbnailUrl); case LOWRES:
return false; setThumbnail(postImage.thumbnailUrl);
} break;
}); case BIGIMAGE:
} else if (mode == Mode.BIGIMAGE) {
if (postImage.type == PostImage.Type.STATIC) {
AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() {
@Override
public boolean onMeasured(View view) {
setBigImage(postImage.imageUrl); setBigImage(postImage.imageUrl);
return false; break;
} case GIF:
}); setGif(postImage.imageUrl);
} else { break;
Logger.e(TAG, "postImage type not STATIC, not changing to BIGIMAGE mode!"); case MOVIE:
setVideo(postImage.imageUrl);
break;
}
return false;
} }
} });
} }
} }
@ -134,6 +134,16 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
this.callback = callback; this.callback = callback;
} }
public CustomScaleImageView findScaleImageView() {
CustomScaleImageView bigImage = null;
for (int i = 0; i < getChildCount(); i++) {
if (getChildAt(i) instanceof CustomScaleImageView) {
bigImage = (CustomScaleImageView) getChildAt(i);
}
}
return bigImage;
}
public void setThumbnail(String thumbnailUrl) { public void setThumbnail(String thumbnailUrl) {
if (getWidth() == 0 || getHeight() == 0) { if (getWidth() == 0 || getHeight() == 0) {
Logger.e(TAG, "getWidth() or getHeight() returned 0, not loading"); Logger.e(TAG, "getWidth() or getHeight() returned 0, not loading");
@ -154,6 +164,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
if (response.getBitmap() != null && (!hasContent || mode == Mode.LOWRES)) { if (response.getBitmap() != null && (!hasContent || mode == Mode.LOWRES)) {
ImageView thumbnail = new ImageView(getContext()); ImageView thumbnail = new ImageView(getContext());
thumbnail.setImageBitmap(response.getBitmap()); thumbnail.setImageBitmap(response.getBitmap());
onModeLoaded(Mode.LOWRES, thumbnail); onModeLoaded(Mode.LOWRES, thumbnail);
} }
} }
@ -197,14 +208,14 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
public void setBigImageFile(File file) { public void setBigImageFile(File file) {
final CustomScaleImageView image = new CustomScaleImageView(getContext()); final CustomScaleImageView image = new CustomScaleImageView(getContext());
image.setImageFile(file.getAbsolutePath()); image.setImage(ImageSource.uri(file.getAbsolutePath()));
image.setOnClickListener(MultiImageView.this); image.setOnClickListener(MultiImageView.this);
addView(image); addView(image, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
image.setInitCallback(new CustomScaleImageView.InitedCallback() { image.setCallback(new CustomScaleImageView.Callback() {
@Override @Override
public void onInit() { public void onReady() {
if (!hasContent || mode == Mode.BIGIMAGE) { if (!hasContent || mode == Mode.BIGIMAGE) {
callback.setProgress(MultiImageView.this, false); callback.setProgress(MultiImageView.this, false);
onModeLoaded(Mode.BIGIMAGE, image); onModeLoaded(Mode.BIGIMAGE, image);
@ -212,8 +223,8 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
} }
@Override @Override
public void onOutOfMemory() { public void onError(boolean wasInitial) {
onOutOfMemoryError(); onBigImageError(wasInitial);
} }
}); });
} }
@ -239,7 +250,9 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
@Override @Override
public void onSuccess(File file) { public void onSuccess(File file) {
gifRequest = null; gifRequest = null;
setGifFile(file); if (!hasContent || mode == Mode.GIF) {
setGifFile(file);
}
} }
@Override @Override
@ -271,8 +284,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
GifImageView view = new GifImageView(getContext()); GifImageView view = new GifImageView(getContext());
view.setImageDrawable(drawable); view.setImageDrawable(drawable);
view.setLayoutParams(AndroidUtils.MATCH_PARAMS); onModeLoaded(Mode.GIF, view);
setView(view);
} }
public void setVideo(String videoUrl) { public void setVideo(String videoUrl) {
@ -291,7 +303,9 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
@Override @Override
public void onSuccess(File file) { public void onSuccess(File file) {
videoRequest = null; videoRequest = null;
setVideoFile(file); if (!hasContent || mode == Mode.MOVIE) {
setVideoFile(file);
}
} }
@Override @Override
@ -316,6 +330,8 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
} catch (ActivityNotFoundException e) { } catch (ActivityNotFoundException e) {
Toast.makeText(getContext(), R.string.open_link_failed, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), R.string.open_link_failed, Toast.LENGTH_SHORT).show();
} }
// TODO: check this
onModeLoaded(Mode.GIF, videoView);
} else { } else {
Context proxyContext = new NoMusicServiceCommandContext(getContext()); Context proxyContext = new NoMusicServiceCommandContext(getContext());
@ -332,7 +348,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
@Override @Override
public void onPrepared(MediaPlayer mp) { public void onPrepared(MediaPlayer mp) {
mp.setLooping(true); mp.setLooping(true);
callback.onVideoLoaded(MultiImageView.this); onModeLoaded(Mode.MOVIE, videoView);
} }
}); });
videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@ -346,7 +362,7 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
videoView.setVideoPath(file.getAbsolutePath()); videoView.setVideoPath(file.getAbsolutePath());
setView(videoView); addView(videoView, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER));
videoView.start(); videoView.start();
} }
@ -356,22 +372,32 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
return videoView; return videoView;
} }
public void onError() { private void onError() {
Toast.makeText(getContext(), R.string.image_preview_failed, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), R.string.image_preview_failed, Toast.LENGTH_SHORT).show();
callback.setProgress(this, false); callback.setProgress(this, false);
} }
public void onNotFoundError() { private void onNotFoundError() {
callback.setProgress(this, false); callback.setProgress(this, false);
Toast.makeText(getContext(), R.string.image_not_found, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), R.string.image_not_found, Toast.LENGTH_SHORT).show();
} }
public void onOutOfMemoryError() { private void onOutOfMemoryError() {
Toast.makeText(getContext(), R.string.image_preview_failed_oom, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), R.string.image_preview_failed_oom, Toast.LENGTH_SHORT).show();
callback.setProgress(this, false); callback.setProgress(this, false);
} }
private void onBigImageError(boolean wasInitial) {
if (wasInitial) {
Toast.makeText(getContext(), R.string.image_failed_big_image, Toast.LENGTH_SHORT).show();
callback.setProgress(this, false);
}
}
public void cancelLoad() { public void cancelLoad() {
if (thumbnailRequest != null) {
thumbnailRequest.cancelRequest();
}
if (bigImageRequest != null) { if (bigImageRequest != null) {
bigImageRequest.cancel(true); bigImageRequest.cancel(true);
} }
@ -394,14 +420,27 @@ public class MultiImageView extends FrameLayout implements View.OnClickListener
cancelLoad(); cancelLoad();
} }
private void setView(View view) { private void onModeLoaded(Mode mode) {
removeAllViews(); onModeLoaded(mode, null);
addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
} }
private void onModeLoaded(Mode mode, View view) { private void onModeLoaded(Mode mode, View view) {
removeAllViews(); if (view != null) {
addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); // Remove all other views
boolean alreadyAttached = false;
for (int i = getChildCount() - 1; i >= 0; i--) {
if (getChildAt(i) != view) {
removeViewAt(i);
} else {
alreadyAttached = true;
}
}
if (!alreadyAttached) {
addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
}
hasContent = true; hasContent = true;
callback.onModeLoaded(this, mode); callback.onModeLoaded(this, mode);
} }

@ -23,6 +23,10 @@ public class TransitionImageView extends View {
private PointF sourceOverlap = new PointF(); private PointF sourceOverlap = new PointF();
private RectF destClip = new RectF(); private RectF destClip = new RectF();
private float progress; private float progress;
private float stateScale;
private float stateBitmapScaleDiff;
private PointF stateBitmapSize;
private PointF statePos;
public TransitionImageView(Context context) { public TransitionImageView(Context context) {
super(context); super(context);
@ -43,15 +47,19 @@ public class TransitionImageView extends View {
this.bitmap = bitmap; this.bitmap = bitmap;
bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
if (stateBitmapSize != null) {
stateBitmapScaleDiff = stateBitmapSize.x / bitmap.getWidth();
}
int[] myLoc = new int[2]; int[] myLoc = new int[2];
getLocationInWindow(myLoc); getLocationInWindow(myLoc);
float globalOffsetX = windowLocation.x - myLoc[0]; float globalOffsetX = windowLocation.x - myLoc[0];
float globalOffsetY = windowLocation.y - myLoc[1]; float globalOffsetY = windowLocation.y - myLoc[1];
// Get the coords in the image view with the center crop method // Get the coords in the image view with the center crop method
float scaleX = (float) viewSize.x / (float) bitmap.getWidth(); float scale = Math.max(
float scaleY = (float) viewSize.y / (float) bitmap.getHeight(); (float) viewSize.x / (float) bitmap.getWidth(),
float scale = scaleX > scaleY ? scaleX : scaleY; (float) viewSize.y / (float) bitmap.getHeight());
float scaledX = bitmap.getWidth() * scale; float scaledX = bitmap.getWidth() * scale;
float scaledY = bitmap.getHeight() * scale; float scaledY = bitmap.getHeight() * scale;
float offsetX = (scaledX - viewSize.x) * 0.5f; float offsetX = (scaledX - viewSize.x) * 0.5f;
@ -66,30 +74,53 @@ public class TransitionImageView extends View {
scaledY - offsetY + globalOffsetY); scaledY - offsetY + globalOffsetY);
} }
public void setState(float stateScale, PointF statePos, PointF stateBitmapSize) {
this.stateScale = stateScale;
this.statePos = statePos;
this.stateBitmapSize = stateBitmapSize;
}
public void setProgress(float progress) { public void setProgress(float progress) {
this.progress = progress; this.progress = progress;
// Center inside method RectF output;
float destScale = Math.min( if (statePos != null) {
(float) getWidth() / (float) bitmap.getWidth(), // Use scale and translate from ssiv
(float) getHeight() / (float) bitmap.getHeight()); output = new RectF(-statePos.x * stateScale, -statePos.y * stateScale, 0, 0);
float destOffsetX = (getWidth() - bitmap.getWidth() * destScale) * 0.5f; output.right = output.left + bitmap.getWidth() * stateBitmapScaleDiff * stateScale;
float destOffsetY = (getHeight() - bitmap.getHeight() * destScale) * 0.5f; output.bottom = output.top + bitmap.getHeight() * stateBitmapScaleDiff * stateScale;
float destRight = bitmap.getWidth() * destScale + destOffsetX; } else {
float destBottom = bitmap.getHeight() * destScale + destOffsetY; // Center inside method
float selfWidth = getWidth();
float selfHeight = getHeight();
float destScale = Math.min(
selfWidth / (float) bitmap.getWidth(),
selfHeight / (float) bitmap.getHeight());
output = new RectF(
(selfWidth - bitmap.getWidth() * destScale) * 0.5f,
(selfHeight - bitmap.getHeight() * destScale) * 0.5f, 0, 0);
output.right = bitmap.getWidth() * destScale + output.left;
output.bottom = bitmap.getHeight() * destScale + output.top;
}
// Linear interpolate between start bounds and calculated final bounds
output.left = lerp(sourceImageRect.left, output.left, progress);
output.top = lerp(sourceImageRect.top, output.top, progress);
output.right = lerp(sourceImageRect.right, output.right, progress);
output.bottom = lerp(sourceImageRect.bottom, output.bottom, progress);
float left = sourceImageRect.left + (destOffsetX - sourceImageRect.left) * progress; destRect.set(output);
float top = sourceImageRect.top + (destOffsetY - sourceImageRect.top) * progress;
float right = sourceImageRect.right + (destRight - sourceImageRect.right) * progress;
float bottom = sourceImageRect.bottom + (destBottom - sourceImageRect.bottom) * progress;
destRect.set(left, top, right, bottom); matrix.setRectToRect(bitmapRect, destRect, Matrix.ScaleToFit.FILL);
destClip.set( destClip.set(
left + sourceOverlap.x * (1f - progress), output.left + sourceOverlap.x * (1f - progress),
top + sourceOverlap.y * (1f - progress), output.top + sourceOverlap.y * (1f - progress),
right - sourceOverlap.x * (1f - progress), output.right - sourceOverlap.x * (1f - progress),
bottom - sourceOverlap.y * (1f - progress) output.bottom - sourceOverlap.y * (1f - progress)
); );
invalidate(); invalidate();
@ -100,7 +131,6 @@ public class TransitionImageView extends View {
super.onDraw(canvas); super.onDraw(canvas);
if (bitmap != null) { if (bitmap != null) {
matrix.setRectToRect(bitmapRect, destRect, Matrix.ScaleToFit.FILL);
canvas.save(); canvas.save();
if (progress < 1f) { if (progress < 1f) {
canvas.clipRect(destClip); canvas.clipRect(destClip);
@ -114,4 +144,8 @@ public class TransitionImageView extends View {
paint.setAntiAlias(true); paint.setAntiAlias(true);
paint.setFilterBitmap(true); paint.setFilterBitmap(true);
} }
private float lerp(float a, float b, float x) {
return a + (b - a) * x;
}
} }

@ -67,6 +67,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<string name="image_preview_failed">Failed to show image</string> <string name="image_preview_failed">Failed to show image</string>
<string name="image_preview_failed_oom">Failed to show image, out of memory</string> <string name="image_preview_failed_oom">Failed to show image, out of memory</string>
<string name="image_failed_big_image">Deepzoom loading failed</string>
<string name="image_not_found">Image not found</string> <string name="image_not_found">Image not found</string>
<string name="image_open_failed">Failed to open image</string> <string name="image_open_failed">Failed to open image</string>

Loading…
Cancel
Save