From 7ad8a3afd1fca1ccf0a07fb179c92bfa728b5e23 Mon Sep 17 00:00:00 2001 From: seven332 Date: Sun, 14 Feb 2016 16:51:56 +0800 Subject: [PATCH] Add GalleryProvider --- .../com/hippo/ehviewer/EhApplication.java | 1 - .../main/java/com/hippo/ehviewer/GetText.java | 41 --- .../ehviewer/gallery/DirGalleryProvider.java | 145 ++++++++ .../ehviewer/gallery/GalleryProvider.java | 148 ++++++++ .../gallery/GalleryProviderListener.java | 28 ++ .../{ui => gallery}/gl/DownUpDetector.java | 2 +- .../ehviewer/{ui => gallery}/gl/Fling.java | 2 +- .../ehviewer/gallery/gl/GalleryPageView.java | 152 +++++++++ .../{ui => gallery}/gl/GalleryView.java | 319 +++++++++++++----- .../{ui => gallery}/gl/GestureRecognizer.java | 2 +- .../{ui => gallery}/gl/ImageView.java | 6 +- .../gl/PagerLayoutManager.java | 96 ++++-- .../gl/ScrollLayoutManager.java | 87 ++++- .../hippo/ehviewer/ui/GalleryActivity.java | 193 +++++++++-- .../hippo/ehviewer/ui/gl/GalleryPageView.java | 89 ----- .../com/hippo/gl/glrenderer/ImageTexture.java | 9 + .../main/java/com/hippo/gl/view/GLView.java | 2 +- .../com/hippo/gl/widget/GLTextureView.java | 13 +- app/src/main/res/values/dimens.xml | 2 + 19 files changed, 1045 insertions(+), 292 deletions(-) delete mode 100644 app/src/main/java/com/hippo/ehviewer/GetText.java create mode 100644 app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java create mode 100644 app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java create mode 100644 app/src/main/java/com/hippo/ehviewer/gallery/GalleryProviderListener.java rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/DownUpDetector.java (97%) rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/Fling.java (99%) create mode 100644 app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryPageView.java rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/GalleryView.java (55%) rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/GestureRecognizer.java (99%) rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/ImageView.java (99%) rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/PagerLayoutManager.java (90%) rename app/src/main/java/com/hippo/ehviewer/{ui => gallery}/gl/ScrollLayoutManager.java (87%) delete mode 100644 app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java index c787d68c6..617794a9c 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java @@ -60,7 +60,6 @@ public class EhApplication extends SceneApplication { public void onCreate() { super.onCreate(); - GetText.initialize(this); CookieDB.initialize(this); StatusCodeException.initialize(this); Settings.initialize(this); diff --git a/app/src/main/java/com/hippo/ehviewer/GetText.java b/app/src/main/java/com/hippo/ehviewer/GetText.java deleted file mode 100644 index 02840d112..000000000 --- a/app/src/main/java/com/hippo/ehviewer/GetText.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * 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.hippo.ehviewer; - -import android.content.Context; -import android.content.res.Resources; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; - -public class GetText { - - private static Resources sResources; - - public static void initialize(Context context) { - sResources = context.getApplicationContext().getResources(); - } - - @NonNull - public static String getString(@StringRes int id) { - return sResources.getString(id); - } - - @NonNull - public static String getString(@StringRes int id, Object... formatArgs) { - return sResources.getString(id, formatArgs); - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java new file mode 100644 index 000000000..a51827bc6 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java @@ -0,0 +1,145 @@ +/* + * Copyright 2016 Hippo Seven + * + * 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.hippo.ehviewer.gallery; + +import android.os.Process; +import android.util.Log; + +import com.hippo.image.Image; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.io.InputStream; +import java.util.Stack; + +public class DirGalleryProvider extends GalleryProvider { + + private static final String TAG = DirGalleryProvider.class.getSimpleName(); + + private File mDir; + private String[] mFiles; + + private final Stack mRequests = new Stack<>(); + + private boolean mRun = true; + + public DirGalleryProvider(File dir) { + mDir = dir; + mFiles = mDir.list(new ImageFilter()); + } + + @Override + public void start() { + mRun = true; + new Decoder().start(); + } + + @Override + public void stop() { + mRun = false; + synchronized (mRequests) { + mRequests.notify(); + } + } + + @Override + public int size() { + return mFiles != null ? mFiles.length : 0; + } + + @Override + @Result + public int request(int index) { + if (index < 0 || index >= size()) { + return RESULT_ERROR; + } else { + synchronized (mRequests) { + mRequests.add(index); + mRequests.notify(); + } + return RESULT_WAIT; + } + } + + private static final String[] ENDS = { + ".jpg", // Joint Photographic Experts Group + ".jpeg", + ".png", // Portable Network Graphics + ".gif", // Graphics Interchange Format + }; + + private static class ImageFilter implements FilenameFilter { + + private static boolean endsWith(String str, String[] ends) { + if (str == null) { + return false; + } + + for (int i = 0, n = ends.length; i < n; i++) { + if (str.endsWith(ends[i])) { + return true; + } + } + + return false; + } + + @Override + public boolean accept(File dir, String filename) { + return endsWith(filename.toLowerCase(), ENDS); + } + } + + private class Decoder extends Thread { + + @Override + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + + while (mRun) { + int index; + synchronized (mRequests) { + if (mRequests.isEmpty()) { + try { + mRequests.wait(); + } catch (InterruptedException e) { + // Ignore + } + continue; + } + index = mRequests.pop(); + } + + try { + InputStream is = new FileInputStream(new File(mDir, mFiles[index])); + Image image = Image.decode(is, false); + if (image != null) { + notifyPageSucceed(index, image); + } else { + notifyPageFailed(index, new Exception("Decode error")); + } + } catch (FileNotFoundException e) { + notifyPageFailed(index, e); + } + } + + Log.i(TAG, "ImageDecoder end"); + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java new file mode 100644 index 000000000..0362e0b8f --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java @@ -0,0 +1,148 @@ +/* + * Copyright 2016 Hippo Seven + * + * 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.hippo.ehviewer.gallery; + +import android.os.Handler; +import android.support.annotation.IntDef; + +import com.hippo.image.Image; +import com.hippo.yorozuya.Pool; +import com.hippo.yorozuya.SimpleHandler; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +public abstract class GalleryProvider { + + @IntDef({RESULT_FAILED, RESULT_WAIT, RESULT_ERROR}) + @Retention(RetentionPolicy.SOURCE) + public @interface Result {} + + public static final int RESULT_FAILED = 0; + public static final int RESULT_WAIT = 1; + public static final int RESULT_ERROR = 2; + + public static final int SIZE_WAIT = -1; + + private List mGalleryProviderListeners = new ArrayList<>(2); + private Pool mNotifyTaskPool = new Pool<>(5); + private Handler mHandler = SimpleHandler.getInstance(); + + public abstract void start(); + + public abstract void stop(); + + /** + * @return {@link #SIZE_WAIT} for wait, 0 for empty + */ + public abstract int size(); + + @Result + public abstract int request(int index); + + public void addGalleryProviderListener(GalleryProviderListener listener) { + mGalleryProviderListeners.add(listener); + } + + public void removeGalleryProviderListener(GalleryProviderListener listener) { + mGalleryProviderListeners.remove(listener); + } + + public void notifyPagePercent(int index, float percent) { + notifyPage(NotifyTask.TYPE_PERCENT, index, percent, null, null); + } + + public void notifyPageSucceed(int index, Image image) { + notifyPage(NotifyTask.TYPE_SUCCEED, index, 0.0f, image, null); + } + + public void notifyPageFailed(int index, Exception e) { + notifyPage(NotifyTask.TYPE_FAILED, index, 0.0f, null, e); + } + + private void notifyPage(@NotifyTask.Type int type, int index, float percent, Image image, Exception exception) { + NotifyTask task = mNotifyTaskPool.pop(); + if (task == null) { + task = new NotifyTask(mGalleryProviderListeners, mNotifyTaskPool); + } + task.setData(type, index, percent, image, exception); + mHandler.post(task); + } + + private static class NotifyTask implements Runnable { + + @IntDef({TYPE_PERCENT, TYPE_SUCCEED, TYPE_FAILED}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + public static final int TYPE_PERCENT = 0; + public static final int TYPE_SUCCEED = 1; + public static final int TYPE_FAILED = 2; + + private List mGalleryProviderListeners; + private Pool mPool; + + @Type + private int mType; + private int mIndex; + private float mPercent; + private Image mImage; + private Exception mException; + + public NotifyTask(List galleryProviderListeners, Pool pool) { + mGalleryProviderListeners = galleryProviderListeners; + mPool = pool; + } + + public void setData(@Type int type, int index, float percent, Image image, Exception exception) { + mType = type; + mIndex = index; + mPercent = percent; + mImage = image; + mException = exception; + } + + @Override + public void run() { + switch (mType) { + case TYPE_PERCENT: + for (int i = 0, n = mGalleryProviderListeners.size(); i < n; i++) { + mGalleryProviderListeners.get(i).onPagePercent(mIndex, mPercent); + } + break; + case TYPE_SUCCEED: + for (int i = 0, n = mGalleryProviderListeners.size(); i < n; i++) { + mGalleryProviderListeners.get(i).onPageSucceed(mIndex, mImage); + } + break; + case TYPE_FAILED: + for (int i = 0, n = mGalleryProviderListeners.size(); i < n; i++) { + mGalleryProviderListeners.get(i).onPageFailed(mIndex, mException); + } + break; + } + + // Clean data + mImage = null; + mException = null; + // Push back + mPool.push(this); + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProviderListener.java b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProviderListener.java new file mode 100644 index 000000000..84b5fe9c4 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProviderListener.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 Hippo Seven + * + * 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.hippo.ehviewer.gallery; + +import com.hippo.image.Image; + +public interface GalleryProviderListener { + + void onPagePercent(int index, float percent); + + void onPageSucceed(int index, Image image); + + void onPageFailed(int index, Exception e); +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/DownUpDetector.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/DownUpDetector.java similarity index 97% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/DownUpDetector.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/DownUpDetector.java index 19442fd68..e82b073bb 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/DownUpDetector.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/DownUpDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.view.MotionEvent; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/Fling.java similarity index 99% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/Fling.java index 6cd82e021..e39459af1 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/Fling.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/Fling.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.content.Context; import android.hardware.SensorManager; diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryPageView.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryPageView.java new file mode 100644 index 000000000..7759096f8 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryPageView.java @@ -0,0 +1,152 @@ +/* + * Copyright 2016 Hippo Seven + * + * 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.hippo.ehviewer.gallery.gl; + +import com.hippo.gl.glrenderer.BasicTexture; +import com.hippo.gl.glrenderer.ImageTexture; +import com.hippo.gl.glrenderer.MovableTextTexture; +import com.hippo.gl.glrenderer.Texture; +import com.hippo.gl.view.Gravity; +import com.hippo.gl.widget.GLFrameLayout; +import com.hippo.gl.widget.GLLinearLayout; +import com.hippo.gl.widget.GLMovableTextView; +import com.hippo.gl.widget.GLProgressView; +import com.hippo.gl.widget.GLTextureView; +import com.hippo.image.Image; + +public class GalleryPageView extends GLFrameLayout { + + public static final float PROGRESS_GONE = -1.0f; + public static final float PROGRESS_INDETERMINATE = -2.0f; + + private ImageView mImage; + private GLLinearLayout mInfo; + private GLMovableTextView mPage; + private GLTextureView mError; + private GLProgressView mProgress; + + public GalleryPageView(MovableTextTexture pageTextTexture, + int progressColor, int progressSize) { + // Add image + mImage = new ImageView(); + GravityLayoutParams glp = new GravityLayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + addComponent(mImage, glp); + + // Add other panel + mInfo = new GLLinearLayout(); + mInfo.setOrientation(GLLinearLayout.VERTICAL); + glp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + glp.gravity = Gravity.CENTER; + addComponent(mInfo, glp); + + // Add page + mPage = new GLMovableTextView(); + mPage.setTextTexture(pageTextTexture); + GLLinearLayout.LayoutParams lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + mInfo.addComponent(mPage, lp); + + // Add error + mError = new GLTextureView(); + lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + mInfo.addComponent(mError, lp); + + // Add progress + mProgress = new GLProgressView(); + mProgress.setBgColor(GalleryView.BACKGROUND_COLOR); + mProgress.setColor(progressColor); + mProgress.setMinimumWidth(progressSize); + mProgress.setMinimumHeight(progressSize); + lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + lp.gravity = Gravity.CENTER_HORIZONTAL; + mInfo.addComponent(mProgress, lp); + } + + public void showImage() { + mImage.setVisibility(VISIBLE); + mInfo.setVisibility(GONE); + } + + public void showInfo() { + mImage.setVisibility(GONE); + mInfo.setVisibility(VISIBLE); + } + + private void unbindImage() { + Texture texture = mImage.getTexture(); + if (texture != null) { + mImage.setTexture(null); + if (texture instanceof ImageTexture) { + ((ImageTexture) texture).recycle(); + } + } + } + + public void setImage(Image image) { + unbindImage(); + if (image != null) { + mImage.setTexture(new ImageTexture(image)); + } + } + + public void setPage(int page) { + mPage.setText(Integer.toString(page)); + } + + public void setProgress(float progress) { + if (progress == PROGRESS_GONE) { + mProgress.setVisibility(GONE); + } else if (progress == PROGRESS_INDETERMINATE) { + mProgress.setVisibility(VISIBLE); + mProgress.setIndeterminate(true); + } else { + mProgress.setVisibility(VISIBLE); + mProgress.setIndeterminate(false); + mProgress.setProgress(progress); + } + } + + private void unbindError() { + Texture texture = mError.getTexture(); + if (texture != null) { + mError.setTexture(null); + if (texture instanceof BasicTexture) { + ((BasicTexture) texture).recycle(); + } + } + } + + public void setError(String error, GalleryView galleryView) { + unbindError(); + if (error == null) { + mError.setVisibility(GONE); + } else { + mError.setVisibility(VISIBLE); + galleryView.bindErrorView(mError, error); + } + } + + public ImageView getImageView() { + return mImage; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java similarity index 55% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java index 22d07036f..d3397d9a3 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryView.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java @@ -14,26 +14,30 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.content.Context; +import android.graphics.Color; import android.graphics.Rect; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.MotionEvent; -import android.view.animation.Interpolator; -import android.view.animation.OvershootInterpolator; import com.hippo.ehviewer.R; import com.hippo.gl.annotation.RenderThread; +import com.hippo.gl.glrenderer.BasicTexture; import com.hippo.gl.glrenderer.GLCanvas; +import com.hippo.gl.glrenderer.MovableTextTexture; +import com.hippo.gl.glrenderer.StringTexture; +import com.hippo.gl.glrenderer.Texture; import com.hippo.gl.util.GalleryUtils; import com.hippo.gl.view.AnimationTime; import com.hippo.gl.view.GLRoot; import com.hippo.gl.view.GLView; import com.hippo.gl.widget.GLEdgeView; import com.hippo.gl.widget.GLProgressView; +import com.hippo.gl.widget.GLTextureView; import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.ResourcesUtils; @@ -43,10 +47,6 @@ import java.util.Stack; public class GalleryView extends GLView implements GestureRecognizer.Listener { - @IntDef({PAGE_STATE_UNKNOWN_SEEN, PAGE_STATE_SEEN, PAGE_STATE_UNSEEN}) - @Retention(RetentionPolicy.SOURCE) - private @interface PageState {} - @IntDef({SCALE_ORIGIN, SCALE_FIT_WIDTH, SCALE_FIT_HEIGHT, SCALE_FIT, SCALE_FIXED}) @Retention(RetentionPolicy.SOURCE) public @interface Scale {} @@ -60,17 +60,11 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { @Retention(RetentionPolicy.SOURCE) private @interface LayoutMode {} - private static final int NO_POSITION = -1; - private static final int MAX_SETTLE_DURATION = 600; // ms - private static final int BACKGROUND_COLOR = 0xff212121; + public static final int BACKGROUND_COLOR = 0xff212121; private static final int INTERVAL_IN_DP = 24; private static final int PROGRESS_SIZE_IN_DP = 48; private static final int PAGE_MIN_HEIGHT_IN_DP = 256; - private static final int PAGE_STATE_UNKNOWN_SEEN = 0; - private static final int PAGE_STATE_SEEN = 1; - private static final int PAGE_STATE_UNSEEN = 2; - public static final int SCALE_ORIGIN = 0; public static final int SCALE_FIT_WIDTH = 1; public static final int SCALE_FIT_HEIGHT = 2; @@ -87,27 +81,32 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { public static final int LAYOUT_MODE_RIGHT_TO_LEFT = 1; public static final int LAYOUT_MODE_TOP_TO_BOTTOM = 2; - private static final Interpolator SMOOTH_SCALER_INTERPOLATOR = new OvershootInterpolator(0.7f); - private static final float[] LEFT_AREA = {0.0f, 0.0f, 1.0f / 3.0f, 1f}; private static final float[] RIGHT_AREA = {2.0f / 3.0f, 0.0f, 1.0f, 1f}; private static final float[] CENTER_AREA = {1.0f / 3.0f, 2.0f / 5.0f, 2.0f / 3.0f, 3.0f / 5.0f}; private Context mContext; + private MovableTextTexture mPageTextTexture; private GestureRecognizer mGestureRecognizer; + private PageIterator mIterator; - private LayoutManager mPagerLayoutManager; - private LayoutManager mScrollLayoutManager; + private PagerLayoutManager mPagerLayoutManager; + private ScrollLayoutManager mScrollLayoutManager; private LayoutManager mLayoutManager; private Recycler mRecycler; private GLEdgeView mEdgeView; private GLProgressView mProgressCache; + private GLTextureView mErrorViewCache; private int mProgressColor; + private int mProgressSize; + private float mErrorSize; + private int mErrorColor; private int mPageMinHeight; + private int mInterval; - private boolean mEnableRequestLayout = true; + private boolean mEnableRequestFill = true; private boolean mScale = false; private boolean mScroll = false; @@ -120,8 +119,6 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { @LayoutMode private int mLayoutMode = LAYOUT_MODE_RIGHT_TO_LEFT; - @LayoutMode - private int mLastLayoutMode = mLayoutMode; @Scale private int mScaleMode = SCALE_FIT; @@ -132,64 +129,131 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { private ActionListener mListener; - public GalleryView(Context context, PageIterator iterator, ActionListener listener) { + public GalleryView(Context context, PageIterator iterator, MovableTextTexture pageTextTexture, + ActionListener listener, @LayoutMode int layoutMode) { mContext = context; + mIterator = iterator; + mPageTextTexture = pageTextTexture; mListener = listener; mEdgeView = new GLEdgeView(context); mGestureRecognizer = new GestureRecognizer(context, this); mRecycler = new Recycler(); - mProgressColor = ResourcesUtils.getAttrColor(context, R.attr.colorPrimary); - mPageMinHeight = LayoutUtils.dp2pix(context, PAGE_MIN_HEIGHT_IN_DP); + iterator.setGalleryView(this); - int interval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP); - int progressSize = LayoutUtils.dp2pix(context, PROGRESS_SIZE_IN_DP); - mPagerLayoutManager = new PagerLayoutManager(context, this, interval, progressSize); - mScrollLayoutManager = new ScrollLayoutManager(context, this, interval, progressSize); - mScrollLayoutManager.onAttach(iterator); - mLayoutManager = mScrollLayoutManager; + mLayoutMode = layoutMode; + mProgressColor = ResourcesUtils.getAttrColor(context, R.attr.colorPrimary); + mProgressSize = LayoutUtils.dp2pix(context, PROGRESS_SIZE_IN_DP); + mErrorSize = context.getResources().getDimension(R.dimen.text_medium); + mErrorColor = Color.RED; + mPageMinHeight = LayoutUtils.dp2pix(context, PAGE_MIN_HEIGHT_IN_DP); + mInterval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP); setBackgroundColor(BACKGROUND_COLOR); } + private void ensurePagerLayoutManager() { + if (mPagerLayoutManager == null) { + mPagerLayoutManager = new PagerLayoutManager(mContext, this, mInterval); + } + } + + private void ensureScrollLayoutManager() { + if (mScrollLayoutManager == null) { + mScrollLayoutManager = new ScrollLayoutManager(mContext, this, mInterval); + } + } + @Override public void onAttachToRoot(GLRoot root) { super.onAttachToRoot(root); - mEdgeView.onAttachToRoot(root); + + switch (mLayoutMode) { + case LAYOUT_MODE_LEFT_TO_RIGHT: + ensurePagerLayoutManager(); + mPagerLayoutManager.setMode(PagerLayoutManager.MODE_LEFT_TO_RIGHT); + mPagerLayoutManager.onAttach(mIterator); + mIterator = null; + mLayoutManager = mPagerLayoutManager; + break; + case LAYOUT_MODE_RIGHT_TO_LEFT: + ensurePagerLayoutManager(); + mPagerLayoutManager.setMode(PagerLayoutManager.MODE_RIGHT_TO_LEFT); + mPagerLayoutManager.onAttach(mIterator); + mIterator = null; + mLayoutManager = mPagerLayoutManager; + break; + case LAYOUT_MODE_TOP_TO_BOTTOM: + ensureScrollLayoutManager(); + mScrollLayoutManager.onAttach(mIterator); + mIterator = null; + mLayoutManager = mScrollLayoutManager; + break; + } } @Override public void onDetachFromRoot() { super.onDetachFromRoot(); - mEdgeView.onDetachFromRoot(); + + mIterator = mLayoutManager.onDetach(); + mLayoutManager = null; } - /* - public void setMode(Mode mode) { - if (mMode != mode) { - Mode oldMode = mMode; - mMode = mode; - resetPageMoving(); - superRequestLayout(); + public void setLayoutMode(@LayoutMode int layoutMode) { + if (mLayoutMode == layoutMode) { + return; + } + mLayoutMode = layoutMode; - mListener.onSetMode(mode); - if (oldMode == Mode.NONE) { - mFirstShownIndex = MathUtils.clamp(mFirstShownIndex, 0, mAdapter.getPages() - 1); - mListener.onScrollToPage(mFirstShownIndex, true); - } + if (mLayoutManager == null) { + return; + } + + switch (mLayoutMode) { + case LAYOUT_MODE_LEFT_TO_RIGHT: + if (mLayoutManager == mPagerLayoutManager) { + // mPagerLayoutManager already attached, just change mode + mPagerLayoutManager.setMode(PagerLayoutManager.MODE_LEFT_TO_RIGHT); + } else { + ensurePagerLayoutManager(); + mPagerLayoutManager.setMode(PagerLayoutManager.MODE_LEFT_TO_RIGHT); + mPagerLayoutManager.onAttach(mLayoutManager.onDetach()); + mLayoutManager = mPagerLayoutManager; + } + break; + case LAYOUT_MODE_RIGHT_TO_LEFT: + if (mLayoutManager == mPagerLayoutManager) { + // mPagerLayoutManager already attached, just change mode + mPagerLayoutManager.setMode(PagerLayoutManager.MODE_RIGHT_TO_LEFT); + } else { + ensurePagerLayoutManager(); + mPagerLayoutManager.setMode(PagerLayoutManager.MODE_RIGHT_TO_LEFT); + mPagerLayoutManager.onAttach(mLayoutManager.onDetach()); + mLayoutManager = mPagerLayoutManager; + } + break; + case LAYOUT_MODE_TOP_TO_BOTTOM: + ensureScrollLayoutManager(); + mScrollLayoutManager.onAttach(mLayoutManager.onDetach()); + mLayoutManager = mScrollLayoutManager; + break; } } - */ - public void superRequestLayout() { - super.requestLayout(); + @Override + public void requestLayout() { + // Do not need requestLayout, because the size will not change + requestFill(); } public void requestFill() { - mRequestFill = true; - invalidate(); + if (mEnableRequestFill) { + mRequestFill = true; + invalidate(); + } } @Override @@ -220,11 +284,12 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { return false; } - mLayoutManager.onDoubleTapConfirmed(x, y); + if (mLayoutManager != null) { + mLayoutManager.onDoubleTapConfirmed(x, y); + } return true; } - GLEdgeView getEdgeView() { return mEdgeView; } @@ -242,14 +307,18 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } mScroll = true; - mLayoutManager.onScroll(dx, dy, totalX, totalY, x, y); + if (mLayoutManager != null) { + mLayoutManager.onScroll(dx, dy, totalX, totalY, x, y); + } return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - mLayoutManager.onFling(e1, e2, velocityX, velocityY); + if (mLayoutManager != null) { + mLayoutManager.onFling(e1, e2, velocityX, velocityY); + } return true; } @@ -260,12 +329,14 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { @Override public boolean onScale(float focusX, float focusY, float scale) { - if (mScroll || !mLayoutManager.canScale()) { + if (mScroll || (mLayoutManager != null && !mLayoutManager.canScale())) { return false; } mScale = true; - mLayoutManager.onScale(focusX, focusY, scale); + if (mLayoutManager != null) { + mLayoutManager.onScale(focusX, focusY, scale); + } return true; } @@ -280,18 +351,22 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { mScale = false; mScroll = false; mFirstScroll = true; - mLayoutManager.onDown(); + if (mLayoutManager != null) { + mLayoutManager.onDown(); + } } @Override public void onUp() { mTouched = false; - mLayoutManager.onUp(); + if (mLayoutManager != null) { + mLayoutManager.onUp(); + } } @Override public void onPointerDown(float x, float y) { - if (!mScroll && mLayoutManager.canScale()) { + if (!mScroll && (mLayoutManager != null && mLayoutManager.canScale())) { mScale = true; } } @@ -301,13 +376,6 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { } - @Override - public void requestLayout() { - if (mEnableRequestLayout) { - super.requestLayout(); - } - } - @Override protected void onLayout(boolean changeSize, int left, int top, int right, int bottom) { @@ -333,16 +401,17 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { GalleryUtils.assertInRenderThread(); // Disable request layout - mEnableRequestLayout = false; - mLayoutManager.onFill(); - mEnableRequestLayout = true; + mEnableRequestFill = false; + if (mLayoutManager != null) { + mLayoutManager.onFill(); + } + mEnableRequestFill = true; } @Override public void render(GLCanvas canvas) { long time = AnimationTime.get(); - boolean invalidate = mLayoutManager.onUpdateAnimation(time); - if (invalidate) { + if (mLayoutManager != null && mLayoutManager.onUpdateAnimation(time)) { invalidate(); } @@ -356,10 +425,18 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { mEdgeView.render(canvas); } + public GalleryPageView findPageById(int id) { + if (mLayoutManager != null) { + return mLayoutManager.findPageById(id); + } else { + return null; + } + } + public GalleryPageView obtainPage() { GalleryPageView page = mRecycler.obtain(); if (page == null) { - page = new GalleryPageView(mContext); + page = new GalleryPageView(mPageTextTexture, mProgressColor, mProgressSize); page.setMinimumHeight(mPageMinHeight); } return page; @@ -382,6 +459,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { progress.setColor(mProgressColor); progress.setBgColor(BACKGROUND_COLOR); progress.setIndeterminate(true); + progress.setMinimumWidth(mProgressSize); + progress.setMinimumHeight(mProgressSize); } return progress; } @@ -393,25 +472,88 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { mProgressCache = progress; } - public interface PageIterator { + public GLTextureView obtainErrorView() { + GLTextureView errorView; + if (mErrorViewCache != null) { + errorView = mErrorViewCache; + mErrorViewCache = null; + } else { + errorView = new GLTextureView(); + } + return errorView; + } - void mark(); + public void unbindErrorView(GLTextureView errorView) { + Texture texture = errorView.getTexture(); + if (texture != null) { + errorView.setTexture(null); + if (texture instanceof BasicTexture) { + ((BasicTexture) texture).recycle(); + } + } + } - void reset(); + public void bindErrorView(GLTextureView errorView, String error) { + unbindErrorView(errorView); - boolean hasNext(); + Texture texture = StringTexture.newInstance(error, mErrorSize, mErrorColor); + errorView.setTexture(texture); + } - boolean hasPrevious(); + public void releaseErrorView(GLTextureView errorView) { + unbindErrorView(errorView); + mErrorViewCache = errorView; + } - boolean isValid(); + public void onDataChanged() { + if (mLayoutManager != null){ + mLayoutManager.onDataChanged(); + } + } - void next(); + public static abstract class PageIterator { - void previous(); + protected GalleryView mGalleryView; - void bind(GalleryPageView view); + private void setGalleryView(@NonNull GalleryView galleryView) { + mGalleryView = galleryView; + } - void unbind(GalleryPageView view); + public abstract void mark(); + + public abstract void reset(); + + public abstract boolean isBusy(); + + /** + * Null for no error + */ + public abstract String isError(); + + public abstract boolean hasNext(); + + public abstract boolean hasPrevious(); + + public abstract void next(); + + public abstract void previous(); + + public void bind(GalleryPageView view) { + view.setId(onBind(view)); + } + + public void unbind(GalleryPageView view) { + onUnbind(view); + view.setId(NO_ID); + } + + public abstract int onBind(GalleryPageView view); + + public abstract void onUnbind(GalleryPageView view); + + public void notifyDataChanged() { + mGalleryView.onDataChanged(); + } } public static abstract class LayoutManager { @@ -443,6 +585,21 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { public abstract void onScale(float focusX, float focusY, float scale); public abstract boolean onUpdateAnimation(long time); + + public abstract void onDataChanged(); + + public abstract GalleryPageView findPageById(int id); + + protected void placeCenter(GLView view) { + int spec = GLView.MeasureSpec.makeMeasureSpec(GLView.LayoutParams.WRAP_CONTENT, + GLView.LayoutParams.WRAP_CONTENT); + view.measure(spec, spec); + int viewWidth = view.getMeasuredWidth(); + int viewHeight = view.getMeasuredHeight(); + int viewLeft = mGalleryView.getWidth() / 2 - viewWidth / 2; + int viewTop = mGalleryView.getHeight() / 2 - viewHeight / 2; + view.layout(viewLeft, viewTop, viewLeft + viewWidth, viewTop + viewHeight); + } } private static class Recycler { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GestureRecognizer.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GestureRecognizer.java similarity index 99% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/GestureRecognizer.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/GestureRecognizer.java index b958ced62..84fc1aa17 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GestureRecognizer.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GestureRecognizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.content.Context; import android.os.SystemClock; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/ImageView.java similarity index 99% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/ImageView.java index 1ddd90ed5..ed2ac92dc 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/ImageView.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/ImageView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.graphics.Rect; import android.graphics.RectF; @@ -178,6 +178,10 @@ public class ImageView extends GLView { mPositionInRootDirty = true; } + public Texture getTexture() { + return mTexture; + } + public boolean isLoaded() { return mTexture != null; } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java similarity index 90% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java index 5a772e21d..456a1b741 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/PagerLayoutManager.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java @@ -14,12 +14,11 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.content.Context; import android.support.annotation.IntDef; import android.support.annotation.NonNull; -import android.util.Log; import android.view.MotionEvent; import android.view.animation.Interpolator; @@ -28,6 +27,7 @@ import com.hippo.gl.anim.Animation; import com.hippo.gl.view.GLView; import com.hippo.gl.widget.GLEdgeView; import com.hippo.gl.widget.GLProgressView; +import com.hippo.gl.widget.GLTextureView; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.MathUtils; @@ -40,8 +40,8 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { @Retention(RetentionPolicy.SOURCE) private @interface Mode {} - private static final int MODE_LEFT_TO_RIGHT = 0; - private static final int MODE_RIGHT_TO_LEFT = 1; + public static final int MODE_LEFT_TO_RIGHT = 0; + public static final int MODE_RIGHT_TO_LEFT = 1; private static final Interpolator SMOOTH_SCROLLER_INTERPOLATOR = new Interpolator() { @Override @@ -54,6 +54,8 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private GalleryView.PageIterator mIterator; private GLProgressView mProgress; + private String mErrorStr; + private GLTextureView mErrorView; private GalleryPageView mPrevious; private GalleryPageView mCurrent; private GalleryPageView mNext; @@ -66,7 +68,6 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private boolean mCanScrollBetweenPages = false; private int mInterval; - private int mProgressSpec; private int[] mScrollRemain = new int[2]; private float[] mScaleDefault = new float[3]; @@ -76,10 +77,9 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private SmoothScaler mSmoothScaler; public PagerLayoutManager(Context context, @NonNull GalleryView galleryView, - int interval, int progressSize) { + int interval) { super(galleryView); mInterval = interval; - mProgressSpec = GLView.MeasureSpec.makeMeasureSpec(progressSize, GLView.MeasureSpec.EXACTLY); mPageFling = new PageFling(context); mSmoothScroller = new SmoothScroller(); @@ -111,6 +111,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { cancelAllAnimations(); // Remove all view removeProgress(); + removeErrorView(); removeAllPages(); // Reset parameters resetParameters(); @@ -122,6 +123,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { @Override public void onAttach(GalleryView.PageIterator iterator) { AssertUtils.assertEquals("The PagerLayoutManager is attached", mIterator, null); + AssertUtils.assertNotEquals("The iterator is null", iterator, null); mIterator = iterator; // Reset parameters resetParameters(); @@ -130,10 +132,20 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private void removeProgress() { if (mProgress != null) { mGalleryView.removeComponent(mProgress); + mGalleryView.releaseProgress(mProgress); mProgress = null; } } + private void removeErrorView() { + if (mErrorView != null) { + mGalleryView.removeComponent(mErrorView); + mGalleryView.releaseErrorView(mErrorView); + mErrorView = null; + mErrorStr = null; + } + } + private void removePage(@NonNull GalleryPageView page) { mGalleryView.removeComponent(page); mIterator.unbind(page); @@ -165,6 +177,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { // Remove all view removeProgress(); + removeErrorView(); removeAllPages(); // Clear iterator @@ -226,9 +239,30 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { int width = galleryView.getWidth(); int height = galleryView.getHeight(); + String errorStr = iterator.isError(); - if (!iterator.isValid()) { - // Remove all pages + if (errorStr != null) { + // Remove progress and all pages + removeProgress(); + removeAllPages(); + + // Ensure error view + if (mErrorView == null) { + mErrorView = galleryView.obtainErrorView(); + galleryView.addComponent(mErrorView); + } + + // Update error string + if (!errorStr.equals(mErrorStr)) { + mErrorStr = errorStr; + galleryView.bindErrorView(mErrorView, errorStr); + } + + // Place error view center + placeCenter(mErrorView); + } else if (iterator.isBusy()) { + // Remove error view and all pages + removeErrorView(); removeAllPages(); // Ensure progress @@ -237,16 +271,12 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { galleryView.addComponent(mProgress); } - // Measure and layout progress - mProgress.measure(mProgressSpec, mProgressSpec); - int progressWidth = mProgress.getMeasuredWidth(); - int progressHeight = mProgress.getMeasuredHeight(); - int progressLeft = width / 2 - progressWidth / 2; - int progressTop = height / 2 - progressHeight / 2; - mProgress.layout(progressLeft, progressTop, progressLeft + progressWidth, progressTop + progressHeight); + // Place progress center + placeCenter(mProgress); } else { - // Remove progress + // Remove progress and error view removeProgress(); + removeErrorView(); // Ensure pages if (mCurrent == null) { @@ -474,9 +504,6 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { mDeltaX += dx; mDeltaY += dy; - - Log.d("TAG", "mDeltaX = " + mDeltaX); - if (mDeltaX < 0) { edgeView.onPull(-mDeltaX, y, GLEdgeView.LEFT); if (!edgeView.isFinished(GLEdgeView.RIGHT)) { @@ -593,6 +620,35 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { return invalidate; } + @Override + public void onDataChanged() { + AssertUtils.assertNotEquals("The PagerLayoutManager is not attached", mIterator, null); + + // Cancel all animations + cancelAllAnimations(); + // Remove all views + removeProgress(); + removeErrorView(); + removeAllPages(); + // Reset parameters + resetParameters(); + mGalleryView.requestFill(); + } + + @Override + public GalleryPageView findPageById(int id) { + if (mCurrent != null && mCurrent.getId() == id) { + return mCurrent; + } + if (mPrevious != null && mPrevious.getId() == id) { + return mPrevious; + } + if (mNext != null && mNext.getId() == id) { + return mNext; + } + return null; + } + class SmoothScroller extends Animation { private int mDx; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java similarity index 87% rename from app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java rename to app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java index 82e228616..cf5e8ada3 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/ScrollLayoutManager.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.hippo.ehviewer.ui.gl; +package com.hippo.ehviewer.gallery.gl; import android.content.Context; import android.support.annotation.NonNull; @@ -24,6 +24,7 @@ import android.view.MotionEvent; import com.hippo.gl.view.GLView; import com.hippo.gl.widget.GLEdgeView; import com.hippo.gl.widget.GLProgressView; +import com.hippo.gl.widget.GLTextureView; import com.hippo.yorozuya.AssertUtils; import java.util.Iterator; @@ -38,6 +39,8 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { private GalleryView.PageIterator mIterator; private GLProgressView mProgress; + private String mErrorStr; + private GLTextureView mErrorView; private LinkedList mPages = new LinkedList<>(); private int mOffset; @@ -50,11 +53,12 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { private PageFling mPageFling; public ScrollLayoutManager(Context context, @NonNull GalleryView galleryView, - int interval, int progressSize) { + int interval) { super(galleryView); mInterval = interval; - mProgressSpec = GLView.MeasureSpec.makeMeasureSpec(progressSize, GLView.MeasureSpec.EXACTLY); + mProgressSpec = GLView.MeasureSpec.makeMeasureSpec(GLView.LayoutParams.WRAP_CONTENT, + GLView.LayoutParams.WRAP_CONTENT); mPageFling = new PageFling(context); } @@ -70,7 +74,8 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { @Override public void onAttach(GalleryView.PageIterator iterator) { - AssertUtils.assertEquals("The PagerLayoutManager is attached", mIterator, null); + AssertUtils.assertEquals("The ScrollLayoutManager is attached", mIterator, null); + AssertUtils.assertNotEquals("The iterator is null", iterator, null); mIterator = iterator; // Reset parameters resetParameters(); @@ -79,10 +84,20 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { private void removeProgress() { if (mProgress != null) { mGalleryView.removeComponent(mProgress); + mGalleryView.releaseProgress(mProgress); mProgress = null; } } + private void removeErrorView() { + if (mErrorView != null) { + mGalleryView.removeComponent(mErrorView); + mGalleryView.releaseErrorView(mErrorView); + mErrorView = null; + mErrorStr = null; + } + } + private void removePage(@NonNull GalleryPageView page) { mGalleryView.removeComponent(page); mIterator.unbind(page); @@ -105,6 +120,7 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { // Remove all view removeProgress(); + removeErrorView(); removeAllPages(); // Clear iterator @@ -122,9 +138,30 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { int width = galleryView.getWidth(); int height = galleryView.getHeight(); + String errorStr = iterator.isError(); - if (!iterator.isValid()) { - // Remove all pages + if (errorStr != null) { + // Remove progress and all pages + removeProgress(); + removeAllPages(); + + // Ensure error view + if (mErrorView == null) { + mErrorView = galleryView.obtainErrorView(); + galleryView.addComponent(mErrorView); + } + + // Update error string + if (!errorStr.equals(mErrorStr)) { + mErrorStr = errorStr; + galleryView.bindErrorView(mErrorView, errorStr); + } + + // Place error view center + placeCenter(mErrorView); + } else if (iterator.isBusy()) { + // Remove error view and all pages + removeErrorView(); removeAllPages(); // Ensure progress @@ -133,16 +170,12 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { galleryView.addComponent(mProgress); } - // Measure and layout progress - mProgress.measure(mProgressSpec, mProgressSpec); - int progressWidth = mProgress.getMeasuredWidth(); - int progressHeight = mProgress.getMeasuredHeight(); - int progressLeft = width / 2 - progressWidth / 2; - int progressTop = height / 2 - progressHeight / 2; - mProgress.layout(progressLeft, progressTop, progressLeft + progressWidth, progressTop + progressHeight); + // Place progress center + placeCenter(mProgress); } else { - // Remove progress + // Remove progress and error view removeProgress(); + removeErrorView(); int minY = (int) (-height * RESERVATIONS); @@ -498,6 +531,32 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { return invalidate; } + @Override + public void onDataChanged() { + AssertUtils.assertNotEquals("The PagerLayoutManager is not attached", mIterator, null); + + // Cancel all animations + cancelAllAnimations(); + // Remove all views + removeProgress(); + removeErrorView(); + removeAllPages(); + // Reset parameters + resetParameters(); + mGalleryView.requestFill(); + } + + @Override + public GalleryPageView findPageById(int id) { + for (int i = 0, n = mPages.size(); i < n; i++) { + GalleryPageView page = mPages.get(i); + if (page.getId() == id) { + return page; + } + } + return null; + } + class PageFling extends Fling { private int mVelocityX; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java index 153fb4261..304ad9226 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -16,26 +16,42 @@ package com.hippo.ehviewer.ui; +import android.graphics.Typeface; import android.os.Bundle; +import android.os.Environment; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; +import android.util.SparseIntArray; import android.view.KeyEvent; import com.hippo.ehviewer.R; -import com.hippo.ehviewer.ui.gl.GalleryPageView; -import com.hippo.ehviewer.ui.gl.GalleryView; +import com.hippo.ehviewer.gallery.DirGalleryProvider; +import com.hippo.ehviewer.gallery.GalleryProvider; +import com.hippo.ehviewer.gallery.GalleryProviderListener; +import com.hippo.ehviewer.gallery.gl.GalleryPageView; +import com.hippo.ehviewer.gallery.gl.GalleryView; import com.hippo.gl.glrenderer.ImageTexture; +import com.hippo.gl.glrenderer.MovableTextTexture; import com.hippo.gl.view.GLRootView; +import com.hippo.gl.view.GLView; import com.hippo.image.Image; +import com.hippo.yorozuya.IntIdGenerator; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.File; -public class GalleryActivity extends AppCompatActivity { +public class GalleryActivity extends AppCompatActivity implements GalleryProviderListener { + @Nullable private GalleryView mGalleryView; + @Nullable + private MovableTextTexture mPageTextTexture; + @Nullable private ImageTexture.Uploader mUploader; + @Nullable + private GalleryProvider mGalleryProvider; + @Nullable + private SparseIntArray mIndexIdMap; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -43,16 +59,41 @@ public class GalleryActivity extends AppCompatActivity { setContentView(R.layout.activity_gallery); GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view); - mGalleryView = new GalleryView(this, new InvalidPageIterator(), null); - glRootView.setContentPane(mGalleryView); + mUploader = new ImageTexture.Uploader(glRootView); + mIndexIdMap = new SparseIntArray(); + mGalleryProvider = new DirGalleryProvider(new File(Environment.getExternalStorageDirectory(), "nmb/image")); + mGalleryProvider.addGalleryProviderListener(this); + mGalleryProvider.start(); + + mPageTextTexture = MovableTextTexture.create(Typeface.DEFAULT, + getResources().getDimensionPixelSize(R.dimen.gallery_page_text), + getResources().getColor(R.color.secondary_text_dark), + new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}); + + mGalleryView = new GalleryView(this, new GalleryPageIterator(), + mPageTextTexture, null, GalleryView.LAYOUT_MODE_LEFT_TO_RIGHT); + glRootView.setContentPane(mGalleryView); } @Override protected void onDestroy() { super.onDestroy(); mGalleryView = null; - mUploader = null; + if (mUploader != null) { + mUploader.clear(); + mUploader = null; + } + if (mPageTextTexture != null) { + mPageTextTexture.recycle(); + mPageTextTexture = null; + } + if (mGalleryProvider != null) { + mGalleryProvider.removeGalleryProviderListener(this); + mGalleryProvider.stop(); + mGalleryProvider = null; + } + mIndexIdMap = null; } @Override @@ -128,24 +169,61 @@ public class GalleryActivity extends AppCompatActivity { return super.onKeyUp(keyCode, event); } + private GalleryPageView findPageByIndex(int index) { + if (mIndexIdMap == null || mGalleryView == null) { + return null; + } - private static final int MAX_SIZE = 2; + int id = mIndexIdMap.get(index, GLView.NO_ID); + if (id == GLView.NO_ID) { + return null; + } - private class InvalidPageIterator implements GalleryView.PageIterator { + return mGalleryView.findPageById(id); + } + @Override + public void onPagePercent(int index, float percent) { + GalleryPageView page = findPageByIndex(index); + if (page != null) { + page.showInfo(); + page.setImage(null); + page.setPage(index + 1); + page.setProgress(percent); + page.setError(null, null); + } + } + + @Override + public void onPageSucceed(int index, Image image) { + GalleryPageView page = findPageByIndex(index); + if (page != null) { + page.showImage(); + page.setImage(image); + page.setPage(index + 1); + page.setProgress(GalleryPageView.PROGRESS_GONE); + page.setError(null, null); + } + } + + @Override + public void onPageFailed(int index, Exception e) { + GalleryPageView page = findPageByIndex(index); + if (page != null && mGalleryView != null) { + page.showInfo(); + page.setImage(null); + page.setPage(index + 1); + page.setProgress(GalleryPageView.PROGRESS_GONE); + page.setError("Error", mGalleryView); + } + } + + private class GalleryPageIterator extends GalleryView.PageIterator { + + private IntIdGenerator mIdGenerator = new IntIdGenerator(); private int mIndex = 0; private int mBackup = mIndex; - public ImageTexture mImageTexture; - - public InvalidPageIterator() { - try { - mImageTexture = new ImageTexture(Image.decode(new FileInputStream("/sdcard/1.jpg"), false)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - } - @Override public void mark() { mBackup = mIndex; @@ -156,9 +234,28 @@ public class GalleryActivity extends AppCompatActivity { mIndex = mBackup; } + @Override + public boolean isBusy() { + return mGalleryProvider == null || + mGalleryProvider.size() == GalleryProvider.SIZE_WAIT; + } + + @Override + public String isError() { + if (mGalleryProvider != null) { + int size = mGalleryProvider.size(); + if (size == 0) { + return "Empty Gallery"; // TODO hardcode + } else if (size < 0 && size != GalleryProvider.SIZE_WAIT) { + return "Weird"; // TODO hardcode + } + } + return null; + } + @Override public boolean hasNext() { - return mIndex < MAX_SIZE - 1; + return mGalleryProvider != null && mIndex < mGalleryProvider.size() - 1; } @Override @@ -166,14 +263,9 @@ public class GalleryActivity extends AppCompatActivity { return mIndex > 0; } - @Override - public boolean isValid() { - return mIndex >= 0 && mIndex < MAX_SIZE; - } - @Override public void next() { - if (mIndex >= MAX_SIZE - 1) { + if (mGalleryProvider != null && mIndex >= mGalleryProvider.size() - 1) { throw new IndexOutOfBoundsException(); } mIndex++; @@ -188,14 +280,51 @@ public class GalleryActivity extends AppCompatActivity { } @Override - public void bind(GalleryPageView view) { - view.showImage(); - view.getImageView().setTexture(mImageTexture); + public int onBind(GalleryPageView view) { + if (mGalleryProvider != null && mGalleryView != null) { + switch (mGalleryProvider.request(mIndex)) { + case GalleryProvider.RESULT_WAIT: + view.showInfo(); + view.setImage(null); + view.setPage(mIndex + 1); + view.setProgress(GalleryPageView.PROGRESS_INDETERMINATE); + view.setError(null, null); + break; + case GalleryProvider.RESULT_FAILED: + view.showInfo(); + view.setImage(null); + view.setPage(mIndex + 1); + view.setProgress(GalleryPageView.PROGRESS_GONE); + view.setError("Failed", mGalleryView); + break; + case GalleryProvider.RESULT_ERROR: + view.showInfo(); + view.setImage(null); + view.setPage(mIndex + 1); + view.setProgress(GalleryPageView.PROGRESS_GONE); + view.setError("Error", mGalleryView); + break; + } + } + + int id = mIdGenerator.nextId(); + if (mIndexIdMap != null) { + mIndexIdMap.put(mIndex, id); + } + return id; } @Override - public void unbind(GalleryPageView view) { - view.getImageView().setTexture(null); + public void onUnbind(GalleryPageView view) { + if (mIndexIdMap != null) { + int index = mIndexIdMap.indexOfKey(view.getId()); + if (index >= 0) { + mIndexIdMap.removeAt(index); + } + } + + view.setImage(null); + view.setError(null, null); } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java b/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java deleted file mode 100644 index b08485c1e..000000000 --- a/app/src/main/java/com/hippo/ehviewer/ui/gl/GalleryPageView.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * 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.hippo.ehviewer.ui.gl; - -import android.content.Context; - -import com.hippo.gl.glrenderer.TextTexture; -import com.hippo.gl.view.Gravity; -import com.hippo.gl.widget.GLFrameLayout; -import com.hippo.gl.widget.GLLinearLayout; -import com.hippo.gl.widget.GLProgressView; -import com.hippo.gl.widget.GLTextView; -import com.hippo.gl.widget.GLTextureView; -import com.hippo.yorozuya.LayoutUtils; - -public class GalleryPageView extends GLFrameLayout { - - private ImageView mImage; - private GLLinearLayout mInfo; - private GLTextView mPage; - private GLTextureView mError; - private GLProgressView mProgress; - - // TODO - private static TextTexture sPageTextTexture; - - public GalleryPageView(Context context) { - // Add image - mImage = new ImageView(); - GravityLayoutParams glp = new GravityLayoutParams(LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - addComponent(mImage, glp); - - // Add other panel - mInfo = new GLLinearLayout(); - mInfo.setOrientation(GLLinearLayout.VERTICAL); - glp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - glp.gravity = Gravity.CENTER; - addComponent(mInfo, glp); - - // Add page - mPage = new GLTextView(); - mPage.setTextTexture(sPageTextTexture); - GLLinearLayout.LayoutParams lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; - mInfo.addComponent(mPage, lp); - - // Add error - mError = new GLTextureView(); - lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; - mInfo.addComponent(mError, lp); - - // Add progress - mProgress = new GLProgressView(); - mProgress.setMinimumWidth(LayoutUtils.dp2pix(context, 56)); // 56dp - mProgress.setMinimumHeight(LayoutUtils.dp2pix(context, 56)); // 56dp - lp = new GLLinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; - mInfo.addComponent(mProgress, lp); - } - - public void showImage() { - mImage.setVisibility(VISIBLE); - mInfo.setVisibility(INVISIBLE); - } - - public ImageView getImageView() { - return mImage; - } -} diff --git a/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java b/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java index 9320cf7f1..426ad7233 100644 --- a/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java +++ b/app/src/main/java/com/hippo/gl/glrenderer/ImageTexture.java @@ -536,4 +536,13 @@ public class ImageTexture implements Texture { public boolean isReady() { return mUploadIndex == mTiles.length; } + + public void recycle() { + for (int i = 0, n = mTiles.length; i < n; ++i) { + mTiles[i].free(); + } + synchronized (mImage) { + mImage.recycle(); + } + } } diff --git a/app/src/main/java/com/hippo/gl/view/GLView.java b/app/src/main/java/com/hippo/gl/view/GLView.java index b4a6eac03..88cde4fe1 100644 --- a/app/src/main/java/com/hippo/gl/view/GLView.java +++ b/app/src/main/java/com/hippo/gl/view/GLView.java @@ -52,7 +52,7 @@ import java.util.ArrayList; public class GLView implements TouchOwner { private static final String TAG = "GLView"; - private static final boolean DEBUG_DRAW_BOUNDS = false; + private static final boolean DEBUG_DRAW_BOUNDS = true; @IntDef({VISIBLE, INVISIBLE, GONE}) @Retention(RetentionPolicy.SOURCE) diff --git a/app/src/main/java/com/hippo/gl/widget/GLTextureView.java b/app/src/main/java/com/hippo/gl/widget/GLTextureView.java index 954007b1c..760cd159b 100644 --- a/app/src/main/java/com/hippo/gl/widget/GLTextureView.java +++ b/app/src/main/java/com/hippo/gl/widget/GLTextureView.java @@ -38,6 +38,10 @@ public class GLTextureView extends GLView { } } + public Texture getTexture() { + return mTexture; + } + @Override protected int getSuggestedMinimumWidth() { return mTexture == null ? super.getSuggestedMinimumWidth() : mTexture.getWidth() + mPaddings.width(); @@ -61,13 +65,4 @@ public class GLTextureView extends GLView { mTexture.draw(canvas, mSrc, mDst); } - - - - - - - - - } diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index b2a15b267..8758f3fe2 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -73,4 +73,6 @@ 128dp 192dp + 56sp +