diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java index 1cf686b8..405a820e 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/ImageViewerPresenter.java @@ -1,16 +1,20 @@ package org.floens.chan.core.presenter; import android.support.v4.view.ViewPager; +import android.util.Log; import org.floens.chan.core.model.PostImage; import org.floens.chan.core.settings.ChanSettings; import org.floens.chan.ui.view.MultiImageView; +import org.floens.chan.utils.Logger; import java.io.File; import java.util.ArrayList; import java.util.List; public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager.OnPageChangeListener { + private static final String TAG = "ImageViewerPresenter"; + private final Callback callback; private final boolean imageAutoLoad = ChanSettings.imageAutoLoad.get(); @@ -18,21 +22,25 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. private boolean entering = true; private boolean exiting = false; private List images; - private int selectedIndex; - private boolean initalLowResLoaded = false; + private int selectedPosition; + + // Disables swiping until the view pager is visible + private boolean viewPagerVisible = false; private boolean changeViewsOnInTransitionEnd = false; public ImageViewerPresenter(Callback callback) { this.callback = callback; } - public void showImages(List images, int index) { + public void showImages(List images, int position) { this.images = images; - selectedIndex = index; + selectedPosition = position; + + Logger.test("showImages position " + position); // Do this before the view is measured, to avoid it to always loading the first two pages - callback.setPagerItems(images, selectedIndex); - callback.setImageMode(images.get(selectedIndex), MultiImageView.Mode.LOWRES); + callback.setPagerItems(images, selectedPosition); + callback.setImageMode(images.get(selectedPosition), MultiImageView.Mode.LOWRES); } public void onViewMeasured() { @@ -60,14 +68,15 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. @Override public void onPageSelected(int position) { - selectedIndex = position; - if (initalLowResLoaded) { - for (PostImage other : getOther(selectedIndex)) { - callback.setImageMode(other, MultiImageView.Mode.LOWRES); - } - callback.setImageMode(images.get(selectedIndex), MultiImageView.Mode.LOWRES); + Logger.test("onPageSelected " + selectedPosition + ", " + position); + + if (!viewPagerVisible) { + return; } - // onModeLoaded will handle the else case + + selectedPosition = position; + + onPageSwipedTo(position); } @Override @@ -83,8 +92,8 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. if (mode == MultiImageView.Mode.LOWRES) { // lowres is requested at the beginning of the transition, // the lowres is loaded before the in transition or after - if (!initalLowResLoaded) { - initalLowResLoaded = true; + if (!viewPagerVisible) { + viewPagerVisible = true; if (!entering) { // Entering transition was already ended, switch now callback.setPreviewVisibility(false); @@ -94,24 +103,49 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. changeViewsOnInTransitionEnd = true; } // Transition ended or not, request loading the other side views to lowres - for (PostImage other : getOther(selectedIndex)) { + for (PostImage other : getOther(selectedPosition, false)) { callback.setImageMode(other, MultiImageView.Mode.LOWRES); } - // selectedIndex can be different than the initial one because of page changes before onModeLoaded was called, - // request a load of the current selectedIndex one here - callback.setImageMode(images.get(selectedIndex), MultiImageView.Mode.LOWRES); + onLowResInCenter(); + } else { + if (multiImageView.getPostImage() == images.get(selectedPosition)) { + Log.i(TAG, "Loading high res from a onModeLoaded"); + onLowResInCenter(); + } } + } + } - // Initial load or not, transitioning or not, load the high res when the user setting says so after the low res - if (imageAutoLoad) { - multiImageView.setMode(MultiImageView.Mode.BIGIMAGE); - } + private void onPageSwipedTo(int position) { + for (PostImage other : getOther(position, false)) { + callback.setImageMode(other, MultiImageView.Mode.LOWRES); + } + + // Already in LOWRES mode + if (callback.getImageMode(images.get(selectedPosition)) == MultiImageView.Mode.LOWRES) { + Log.i(TAG, "Loading high res from a swipe"); + onLowResInCenter(); + } + // Else let onModeChange handle it + } + + // Called from either a page swipe caused a lowres image to the center or an + // onModeLoaded when a unloaded image was swiped to the center earlier + private void onLowResInCenter() { + PostImage postImage = images.get(selectedPosition); + if (postImage.type == PostImage.Type.STATIC) { + callback.setImageMode(postImage, MultiImageView.Mode.BIGIMAGE); + } else { + // todo } } @Override public void onTap(MultiImageView multiImageView) { - onExit(); + // Don't mistake a swipe from a user when the pager is disabled as a tap + if (viewPagerVisible) { + onExit(); + } } @Override @@ -134,11 +168,14 @@ public class ImageViewerPresenter implements MultiImageView.Callback, ViewPager. } - private List getOther(int position) { - List other = new ArrayList<>(2); + private List getOther(int position, boolean all) { + List other = new ArrayList<>(3); if (position - 1 >= 0) { other.add(images.get(position - 1)); } + if (all) { + other.add(images.get(position)); + } if (position + 1 < images.size()) { other.add(images.get(position + 1)); } 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 6e5da6d6..bc0e00e4 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 @@ -143,7 +143,7 @@ public class ThreadPresenter implements ChanLoader.ChanLoaderCallback, PostAdapt if (item.hasImage) { images.add(new PostImage(item.thumbnailUrl, item.imageUrl, item.filename, item.ext, item.imageWidth, item.imageHeight)); if (item.no == post.no) { - index = i; + index = images.size() - 1; } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java index 34dc72a4..7de137f5 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ImageViewerController.java @@ -11,7 +11,6 @@ import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.Build; -import android.support.v4.view.ViewPager; import android.view.View; import android.view.Window; import android.view.animation.DecelerateInterpolator; @@ -24,6 +23,7 @@ import org.floens.chan.core.presenter.ImageViewerPresenter; import org.floens.chan.ui.adapter.ImageViewerAdapter; import org.floens.chan.ui.toolbar.Toolbar; import org.floens.chan.ui.view.ClippingImageView; +import org.floens.chan.ui.view.OptionalSwipeViewPager; import org.floens.chan.ui.view.MultiImageView; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.AnimationUtils; @@ -47,7 +47,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis private final Toolbar toolbar; private ClippingImageView previewImage; - private ViewPager pager; + private OptionalSwipeViewPager pager; public ImageViewerController(Context context, Toolbar toolbar) { super(context); @@ -65,7 +65,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis view = inflateRes(R.layout.controller_image_viewer); view.setOnClickListener(this); previewImage = (ClippingImageView) view.findViewById(R.id.preview_image); - pager = (ViewPager) view.findViewById(R.id.pager); + pager = (OptionalSwipeViewPager) view.findViewById(R.id.pager); pager.setOnPageChangeListener(presenter); AndroidUtils.waitForMeasure(view, new AndroidUtils.OnMeasuredCallback() { @@ -102,6 +102,7 @@ public class ImageViewerController extends Controller implements View.OnClickLis public void setPagerVisiblity(boolean visible) { pager.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + pager.setSwipingEnabled(visible); } public void setPagerItems(List images, int initialIndex) { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java index 3ca58706..664cb2de 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/MultiImageView.java @@ -26,6 +26,7 @@ import android.net.Uri; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Toast; import android.widget.VideoView; @@ -33,6 +34,7 @@ import android.widget.VideoView; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader.ImageContainer; import com.koushikdutta.async.future.Future; +import com.koushikdutta.ion.Response; import org.floens.chan.ChanApplication; import org.floens.chan.R; @@ -48,7 +50,7 @@ import java.io.IOException; import pl.droidsonroids.gif.GifDrawable; import pl.droidsonroids.gif.GifImageView; -public class MultiImageView extends LoadView implements View.OnClickListener { +public class MultiImageView extends FrameLayout implements View.OnClickListener { public enum Mode { UNLOADED, LOWRES, BIGIMAGE } @@ -61,7 +63,11 @@ public class MultiImageView extends LoadView implements View.OnClickListener { private Mode mode = Mode.UNLOADED; private boolean thumbnailNeeded = true; - private Future request; + private boolean hasContent = false; + private ImageContainer thumbnailRequest; + private Future> bigImageRequest; + private Future> gifRequest; + private Future> videoRequest; private VideoView videoView; @@ -95,9 +101,10 @@ public class MultiImageView extends LoadView implements View.OnClickListener { public void setMode(Mode mode) { if (this.mode != mode) { + final Mode previousTargetMode = this.mode; this.mode = mode; + Logger.d(TAG, "Changing mode from " + previousTargetMode + "to " + mode + " for " + postImage.thumbnailUrl); if (mode == Mode.LOWRES) { - Logger.d(TAG, "Changing mode to LOWRES for " + postImage.thumbnailUrl); AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() { @Override public boolean onMeasured(View view) { @@ -106,10 +113,14 @@ public class MultiImageView extends LoadView implements View.OnClickListener { } }); } else if (mode == Mode.BIGIMAGE) { - Logger.d(TAG, "Changing mode to BIGIMAGE for " + postImage.thumbnailUrl); - // Always done after at least LOWRES, so the view is measured if (postImage.type == PostImage.Type.STATIC) { - setBigImage(postImage.imageUrl); + AndroidUtils.waitForMeasure(this, new AndroidUtils.OnMeasuredCallback() { + @Override + public boolean onMeasured(View view) { + setBigImage(postImage.imageUrl); + return false; + } + }); } else { Logger.e(TAG, "postImage type not STATIC, not changing to BIGIMAGE mode!"); } @@ -132,25 +143,32 @@ public class MultiImageView extends LoadView implements View.OnClickListener { } // Also use volley for the thumbnails - ChanApplication.getVolleyImageLoader().get(thumbnailUrl, new com.android.volley.toolbox.ImageLoader.ImageListener() { + thumbnailRequest = ChanApplication.getVolleyImageLoader().get(thumbnailUrl, new com.android.volley.toolbox.ImageLoader.ImageListener() { @Override public void onErrorResponse(VolleyError error) { + thumbnailRequest = null; onError(); } @Override public void onResponse(ImageContainer response, boolean isImmediate) { - if (response.getBitmap() != null && thumbnailNeeded) { + thumbnailRequest = null; + if (response.getBitmap() != null && (!hasContent || mode == Mode.LOWRES)) { ImageView thumbnail = new ImageView(getContext()); thumbnail.setImageBitmap(response.getBitmap()); - thumbnail.setLayoutParams(AndroidUtils.MATCH_PARAMS); - setView(thumbnail, false); - callback.onModeLoaded(MultiImageView.this, Mode.LOWRES); + onModeLoaded(Mode.LOWRES, thumbnail); } } }, getWidth(), getHeight()); } + private void cancelThumbnail() { + if (thumbnailRequest != null) { + thumbnailRequest.cancelRequest(); + thumbnailRequest = null; + } + } + public void setBigImage(String imageUrl) { if (getWidth() == 0 || getHeight() == 0) { Logger.e(TAG, "getWidth() or getHeight() returned 0, not loading big image"); @@ -158,7 +176,7 @@ public class MultiImageView extends LoadView implements View.OnClickListener { } callback.setProgress(this, true); - request = ChanApplication.getFileCache().downloadFile(getContext(), imageUrl, new FileCache.DownloadedCallback() { + bigImageRequest = ChanApplication.getFileCache().downloadFile(getContext(), imageUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { if (done) { @@ -171,11 +189,13 @@ public class MultiImageView extends LoadView implements View.OnClickListener { @Override public void onSuccess(File file) { + bigImageRequest = null; setBigImageFile(file); } @Override public void onFail(boolean notFound) { + bigImageRequest = null; if (notFound) { onNotFoundError(); } else { @@ -195,10 +215,10 @@ public class MultiImageView extends LoadView implements View.OnClickListener { image.setInitCallback(new CustomScaleImageView.InitedCallback() { @Override public void onInit() { - removeAllViews(); - addView(image); - callback.setProgress(MultiImageView.this, false); - callback.onModeLoaded(MultiImageView.this, Mode.BIGIMAGE); + if (!hasContent || mode == Mode.BIGIMAGE) { + callback.setProgress(MultiImageView.this, false); + onModeLoaded(Mode.BIGIMAGE, image); + } } @Override @@ -208,6 +228,13 @@ public class MultiImageView extends LoadView implements View.OnClickListener { }); } + private void cancelBigImage() { + if (bigImageRequest != null) { + bigImageRequest.cancel(); + bigImageRequest = null; + } + } + public void setGif(String gifUrl) { if (getWidth() == 0 || getHeight() == 0) { Logger.e(TAG, "getWidth() or getHeight() returned 0, not loading"); @@ -215,7 +242,7 @@ public class MultiImageView extends LoadView implements View.OnClickListener { } callback.setProgress(this, true); - request = ChanApplication.getFileCache().downloadFile(getContext(), gifUrl, new FileCache.DownloadedCallback() { + gifRequest = ChanApplication.getFileCache().downloadFile(getContext(), gifUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { if (done) { @@ -229,11 +256,13 @@ public class MultiImageView extends LoadView implements View.OnClickListener { @Override public void onSuccess(File file) { + gifRequest = null; setGifFile(file); } @Override public void onFail(boolean notFound) { + gifRequest = null; if (notFound) { onNotFoundError(); } else { @@ -261,12 +290,12 @@ public class MultiImageView extends LoadView implements View.OnClickListener { GifImageView view = new GifImageView(getContext()); view.setImageDrawable(drawable); view.setLayoutParams(AndroidUtils.MATCH_PARAMS); - setView(view, false); + setView(view); } public void setVideo(String videoUrl) { callback.setProgress(this, true); - request = ChanApplication.getFileCache().downloadFile(getContext(), videoUrl, new FileCache.DownloadedCallback() { + videoRequest = ChanApplication.getFileCache().downloadFile(getContext(), videoUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { if (done) { @@ -280,11 +309,13 @@ public class MultiImageView extends LoadView implements View.OnClickListener { @Override public void onSuccess(File file) { + videoRequest = null; setVideoFile(file); } @Override public void onFail(boolean notFound) { + videoRequest = null; if (notFound) { onNotFoundError(); } else { @@ -334,7 +365,7 @@ public class MultiImageView extends LoadView implements View.OnClickListener { videoView.setVideoPath(file.getAbsolutePath()); - setView(videoView, false); + setView(videoView); videoView.start(); } @@ -360,8 +391,14 @@ public class MultiImageView extends LoadView implements View.OnClickListener { } public void cancelLoad() { - if (request != null) { - request.cancel(true); + if (bigImageRequest != null) { + bigImageRequest.cancel(); + } + if (gifRequest != null) { + gifRequest.cancel(); + } + if (videoRequest != null) { + videoRequest.cancel(); } } @@ -370,6 +407,24 @@ public class MultiImageView extends LoadView implements View.OnClickListener { callback.onTap(this); } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + cancelLoad(); + } + + private void setView(View view) { + removeAllViews(); + addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + } + + private void onModeLoaded(Mode mode, View view) { + removeAllViews(); + addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + hasContent = true; + callback.onModeLoaded(this, mode); + } + public static interface Callback { public void onTap(MultiImageView multiImageView); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java b/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java new file mode 100644 index 00000000..ded4fde1 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/OptionalSwipeViewPager.java @@ -0,0 +1,32 @@ +package org.floens.chan.ui.view; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; + +public class OptionalSwipeViewPager extends ViewPager { + private boolean swipingEnabled; + + public OptionalSwipeViewPager(Context context) { + super(context); + } + + public OptionalSwipeViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return swipingEnabled && super.onTouchEvent(ev); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return swipingEnabled && super.onInterceptTouchEvent(ev); + } + + public void setSwipingEnabled(boolean swipingEnabled) { + this.swipingEnabled = swipingEnabled; + } +} diff --git a/Clover/app/src/main/res/layout/controller_image_viewer.xml b/Clover/app/src/main/res/layout/controller_image_viewer.xml index 0593486d..a145038f 100644 --- a/Clover/app/src/main/res/layout/controller_image_viewer.xml +++ b/Clover/app/src/main/res/layout/controller_image_viewer.xml @@ -8,7 +8,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> -