diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java index 617794a9c..678c6cf7b 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java @@ -18,7 +18,9 @@ package com.hippo.ehviewer; import android.app.ActivityManager; import android.content.Context; +import android.os.Debug; import android.support.annotation.NonNull; +import android.util.Log; import android.util.SparseArray; import com.hippo.beerbelly.LruCacheEx; @@ -33,7 +35,9 @@ import com.hippo.okhttp.CookieDB; import com.hippo.scene.SceneApplication; import com.hippo.text.Html; import com.hippo.util.ReadableTime; +import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IntIdGenerator; +import com.hippo.yorozuya.SimpleHandler; import java.io.File; import java.util.Comparator; @@ -43,7 +47,10 @@ import okhttp3.OkHttpClient; public class EhApplication extends SceneApplication { + private static final String TAG = EhApplication.class.getSimpleName(); + private static final boolean DEBUG_CONACO = false; + private static final boolean DEBUG_NATIVE_MEMORY = false; private IntIdGenerator mIdGenerator = new IntIdGenerator(); private SparseArray mGlobalStuffMap = new SparseArray<>(); @@ -60,11 +67,27 @@ public class EhApplication extends SceneApplication { public void onCreate() { super.onCreate(); + GetText.initialize(this); CookieDB.initialize(this); StatusCodeException.initialize(this); Settings.initialize(this); ReadableTime.initialize(this); Html.initialize(this); + + if (DEBUG_NATIVE_MEMORY) { + debugNativeMemory(); + } + } + + private void debugNativeMemory() { + new Runnable() { + @Override + public void run() { + Log.i(TAG, "Native memory: " + FileUtils.humanReadableByteCount( + Debug.getNativeHeapAllocatedSize(), false)); + SimpleHandler.getInstance().postDelayed(this, 3000); + } + }.run(); } public int putGlobalStuff(@NonNull Object o) { diff --git a/app/src/main/java/com/hippo/ehviewer/GetText.java b/app/src/main/java/com/hippo/ehviewer/GetText.java new file mode 100644 index 000000000..02840d112 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/GetText.java @@ -0,0 +1,41 @@ +/* + * 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 index a51827bc6..81f4a269f 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java @@ -17,50 +17,68 @@ package com.hippo.ehviewer.gallery; import android.os.Process; +import android.support.annotation.Nullable; import android.util.Log; +import com.hippo.ehviewer.GetText; +import com.hippo.ehviewer.R; import com.hippo.image.Image; +import com.hippo.yorozuya.PriorityThread; +import com.hippo.yorozuya.StringUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.InputStream; +import java.util.Arrays; import java.util.Stack; +import java.util.concurrent.atomic.AtomicInteger; -public class DirGalleryProvider extends GalleryProvider { +public class DirGalleryProvider extends GalleryProvider implements Runnable { private static final String TAG = DirGalleryProvider.class.getSimpleName(); + private static final AtomicInteger sIdGenerator = new AtomicInteger(); - private File mDir; - private String[] mFiles; - + private final File mDir; private final Stack mRequests = new Stack<>(); - - private boolean mRun = true; + @Nullable + private Thread mBgThread; + private volatile int mSize = STATE_WAIT; + private String mError; + private boolean mStarted = false; public DirGalleryProvider(File dir) { mDir = dir; - mFiles = mDir.list(new ImageFilter()); } @Override public void start() { - mRun = true; - new Decoder().start(); - } + if (mStarted) { + throw new IllegalStateException("Can't start it twice"); + } + mStarted = true; - @Override - public void stop() { - mRun = false; - synchronized (mRequests) { - mRequests.notify(); + if (mBgThread == null || mBgThread.isInterrupted()) { + mBgThread = new PriorityThread(this, TAG + '-' + sIdGenerator.incrementAndGet(), + Process.THREAD_PRIORITY_BACKGROUND); + mBgThread.start(); } } + @Override + public void stop() { + if (mBgThread == null || mBgThread.isInterrupted()) { + return; + } + + mBgThread.interrupt(); + mBgThread = null; + } + @Override public int size() { - return mFiles != null ? mFiles.length : 0; + return mSize; } @Override @@ -77,69 +95,74 @@ public class DirGalleryProvider extends GalleryProvider { } } - private static final String[] ENDS = { - ".jpg", // Joint Photographic Experts Group - ".jpeg", - ".png", // Portable Network Graphics - ".gif", // Graphics Interchange Format - }; + @Override + public String getError() { + return mError; + } + + @Override + public void run() { + // It may take a long time, so run it in new thread + String[] files = mDir.list(new ImageFilter()); + + if (files == null) { + mSize = STATE_ERROR; + mError = GetText.getString(R.string.error_not_folder_path); + mBgThread = null; + + // Notify to to show error + notifyDataChanged(); + + Log.i(TAG, "ImageDecoder end with error"); + return; + } + + // Sort it + Arrays.sort(files); + + // Set state normal and notify + mSize = files.length; + notifyDataChanged(); + + while (!Thread.currentThread().isInterrupted()) { + int index; + synchronized (mRequests) { + if (mRequests.isEmpty()) { + try { + mRequests.wait(); + } catch (InterruptedException e) { + // Interrupted + break; + } + continue; + } + index = mRequests.pop(); + } + + try { + InputStream is = new FileInputStream(new File(mDir, files[index])); + Image image = Image.decode(is, false); + if (image != null) { + notifyPageSucceed(index, image); + } else { + notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed)); + } + } catch (FileNotFoundException e) { + notifyPageFailed(index, GetText.getString(R.string.error_not_found)); + } + } + + // Clear + mBgThread = null; + + Log.i(TAG, "ImageDecoder end"); + } 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"); + return StringUtils.endsWith(filename.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS); } } } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java new file mode 100644 index 000000000..c5baac195 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java @@ -0,0 +1,45 @@ +/* + * 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; + +public class EhGalleryProvider extends GalleryProvider { + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public int size() { + return 0; + } + + @Override + public int request(int index) { + return 0; + } + + @Override + public String getError() { + return null; + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java index 0362e0b8f..6bbf4a4c3 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider.java @@ -30,15 +30,22 @@ import java.util.List; public abstract class GalleryProvider { - @IntDef({RESULT_FAILED, RESULT_WAIT, RESULT_ERROR}) + @IntDef({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 RESULT_WAIT = 0; + public static final int RESULT_ERROR = 1; - public static final int SIZE_WAIT = -1; + public static final int STATE_WAIT = -1; + public static final int STATE_ERROR = -2; + + public static final String[] SUPPORT_IMAGE_EXTENSIONS = { + ".jpg", // Joint Photographic Experts Group + ".jpeg", + ".png", // Portable Network Graphics + ".gif", // Graphics Interchange Format + }; private List mGalleryProviderListeners = new ArrayList<>(2); private Pool mNotifyTaskPool = new Pool<>(5); @@ -49,13 +56,15 @@ public abstract class GalleryProvider { public abstract void stop(); /** - * @return {@link #SIZE_WAIT} for wait, 0 for empty + * @return {@link #STATE_WAIT} for wait, 0 for empty */ public abstract int size(); @Result public abstract int request(int index); + public abstract String getError(); + public void addGalleryProviderListener(GalleryProviderListener listener) { mGalleryProviderListeners.add(listener); } @@ -64,36 +73,41 @@ public abstract class GalleryProvider { mGalleryProviderListeners.remove(listener); } + public void notifyDataChanged() { + notify(NotifyTask.TYPE_DATA_CHANGED, 0, 0.0f, null, null); + } + public void notifyPagePercent(int index, float percent) { - notifyPage(NotifyTask.TYPE_PERCENT, index, percent, null, null); + notify(NotifyTask.TYPE_PERCENT, index, percent, null, null); } public void notifyPageSucceed(int index, Image image) { - notifyPage(NotifyTask.TYPE_SUCCEED, index, 0.0f, image, null); + notify(NotifyTask.TYPE_SUCCEED, index, 0.0f, image, null); } - public void notifyPageFailed(int index, Exception e) { - notifyPage(NotifyTask.TYPE_FAILED, index, 0.0f, null, e); + public void notifyPageFailed(int index, String error) { + notify(NotifyTask.TYPE_FAILED, index, 0.0f, null, error); } - private void notifyPage(@NotifyTask.Type int type, int index, float percent, Image image, Exception exception) { + private void notify(@NotifyTask.Type int type, int index, float percent, Image image, String error) { NotifyTask task = mNotifyTaskPool.pop(); if (task == null) { task = new NotifyTask(mGalleryProviderListeners, mNotifyTaskPool); } - task.setData(type, index, percent, image, exception); + task.setData(type, index, percent, image, error); mHandler.post(task); } private static class NotifyTask implements Runnable { - @IntDef({TYPE_PERCENT, TYPE_SUCCEED, TYPE_FAILED}) + @IntDef({TYPE_DATA_CHANGED, 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; + public static final int TYPE_DATA_CHANGED = 0; + public static final int TYPE_PERCENT = 1; + public static final int TYPE_SUCCEED = 2; + public static final int TYPE_FAILED = 3; private List mGalleryProviderListeners; private Pool mPool; @@ -103,24 +117,29 @@ public abstract class GalleryProvider { private int mIndex; private float mPercent; private Image mImage; - private Exception mException; + private String mError; public NotifyTask(List galleryProviderListeners, Pool pool) { mGalleryProviderListeners = galleryProviderListeners; mPool = pool; } - public void setData(@Type int type, int index, float percent, Image image, Exception exception) { + public void setData(@Type int type, int index, float percent, Image image, String error) { mType = type; mIndex = index; mPercent = percent; mImage = image; - mException = exception; + mError = error; } @Override public void run() { switch (mType) { + case TYPE_DATA_CHANGED: + for (int i = 0, n = mGalleryProviderListeners.size(); i < n; i++) { + mGalleryProviderListeners.get(i).onDataChanged(); + } + break; case TYPE_PERCENT: for (int i = 0, n = mGalleryProviderListeners.size(); i < n; i++) { mGalleryProviderListeners.get(i).onPagePercent(mIndex, mPercent); @@ -133,14 +152,14 @@ public abstract class GalleryProvider { break; case TYPE_FAILED: for (int i = 0, n = mGalleryProviderListeners.size(); i < n; i++) { - mGalleryProviderListeners.get(i).onPageFailed(mIndex, mException); + mGalleryProviderListeners.get(i).onPageFailed(mIndex, mError); } break; } // Clean data mImage = null; - mException = null; + mError = 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 index 84b5fe9c4..a2b3cd666 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProviderListener.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProviderListener.java @@ -20,9 +20,11 @@ import com.hippo.image.Image; public interface GalleryProviderListener { + void onDataChanged(); + void onPagePercent(int index, float percent); void onPageSucceed(int index, Image image); - void onPageFailed(int index, Exception e); + void onPageFailed(int index, String error); } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ZipGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/ZipGalleryProvider.java new file mode 100644 index 000000000..eb6620428 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/gallery/ZipGalleryProvider.java @@ -0,0 +1,194 @@ +/* + * 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.support.annotation.Nullable; +import android.util.Log; + +import com.hippo.ehviewer.GetText; +import com.hippo.ehviewer.R; +import com.hippo.image.Image; +import com.hippo.yorozuya.PriorityThread; +import com.hippo.yorozuya.StringUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Stack; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +public class ZipGalleryProvider extends GalleryProvider implements Runnable { + + private static final String TAG = ZipGalleryProvider.class.getSimpleName(); + private static final AtomicInteger sIdGenerator = new AtomicInteger(); + + private final File mFile; + private final Stack mRequests = new Stack<>(); + @Nullable + private Thread mBgThread; + private volatile int mSize = STATE_WAIT; + private String mError; + private boolean mStarted = false; + + public ZipGalleryProvider(File file) { + mFile = file; + } + + @Override + public void start() { + if (mStarted) { + throw new IllegalStateException("Can't start it twice"); + } + mStarted = true; + + if (mBgThread == null || mBgThread.isInterrupted()) { + mBgThread = new PriorityThread(this, TAG + '-' + sIdGenerator.incrementAndGet(), + Process.THREAD_PRIORITY_BACKGROUND); + mBgThread.start(); + } + } + + @Override + public void stop() { + if (mBgThread == null || mBgThread.isInterrupted()) { + return; + } + + mBgThread.interrupt(); + mBgThread = null; + } + + @Override + public int size() { + return mSize; + } + + @Override + public int request(int index) { + if (index < 0 || index >= mSize) { + return RESULT_ERROR; + } else { + synchronized (mRequests) { + mRequests.add(index); + mRequests.notify(); + } + return RESULT_WAIT; + } + } + + @Override + public String getError() { + return mError; + } + + @Override + public void run() { + ZipFile zipFile = null; + try { + zipFile = new ZipFile(mFile); + } catch (ZipException e) { + mError = GetText.getString(R.string.error_invalid_zip_file); + } catch (FileNotFoundException e) { + mError = GetText.getString(R.string.error_not_found); + } catch (IOException e) { + mError = GetText.getString(R.string.error_reading_failed); + } + + // Check zip file null + if (zipFile == null) { + mSize = STATE_ERROR; + if (mError == null) { + mError = GetText.getString(R.string.error_unknown); + } + mBgThread = null; + + // Notify to to show error + notifyDataChanged(); + + Log.i(TAG, "ImageDecoder end with error"); + return; + } + + // Get all image name + List filenames = new ArrayList<>(zipFile.size()); + Enumeration enumeration = zipFile.entries(); + while (enumeration.hasMoreElements()) { + ZipEntry zipEntry = enumeration.nextElement(); + String filename = zipEntry.getName(); + if (!zipEntry.isDirectory() && StringUtils.endsWith(filename, SUPPORT_IMAGE_EXTENSIONS)) { + filenames.add(filename); + } + } + Collections.sort(filenames); + + // Update size and notify changed + mSize = filenames.size(); + notifyDataChanged(); + + while (!Thread.currentThread().isInterrupted()) { + int index; + synchronized (mRequests) { + if (mRequests.isEmpty()) { + try { + mRequests.wait(); + } catch (InterruptedException e) { + // Interrupted + break; + } + continue; + } + index = mRequests.pop(); + } + + try { + ZipEntry zipEntry = zipFile.getEntry(filenames.get(index)); + if (zipEntry != null) { + InputStream is = zipFile.getInputStream(zipEntry); + Image image = Image.decode(is, false); + if (image != null) { + notifyPageSucceed(index, image); + } else { + notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed)); + } + } else { + notifyPageFailed(index, GetText.getString(R.string.error_reading_failed)); + } + } catch (IOException e) { + notifyPageFailed(index, GetText.getString(R.string.error_reading_failed)); + } + } + + // Clear + try { + zipFile.close(); + } catch (IOException e) { + // Ignore + } + mBgThread = null; + + Log.i(TAG, "ImageDecoder end"); + } +} 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 index 7759096f8..d10e78884 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryPageView.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryPageView.java @@ -16,6 +16,8 @@ package com.hippo.ehviewer.gallery.gl; +import android.content.Context; + import com.hippo.gl.glrenderer.BasicTexture; import com.hippo.gl.glrenderer.ImageTexture; import com.hippo.gl.glrenderer.MovableTextTexture; @@ -27,9 +29,11 @@ import com.hippo.gl.widget.GLMovableTextView; import com.hippo.gl.widget.GLProgressView; import com.hippo.gl.widget.GLTextureView; import com.hippo.image.Image; +import com.hippo.yorozuya.LayoutUtils; public class GalleryPageView extends GLFrameLayout { + private static final int INFO_INTERVAL = 24; public static final float PROGRESS_GONE = -1.0f; public static final float PROGRESS_INDETERMINATE = -2.0f; @@ -39,7 +43,7 @@ public class GalleryPageView extends GLFrameLayout { private GLTextureView mError; private GLProgressView mProgress; - public GalleryPageView(MovableTextTexture pageTextTexture, + public GalleryPageView(Context context, MovableTextTexture pageTextTexture, int progressColor, int progressSize) { // Add image mImage = new ImageView(); @@ -50,6 +54,7 @@ public class GalleryPageView extends GLFrameLayout { // Add other panel mInfo = new GLLinearLayout(); mInfo.setOrientation(GLLinearLayout.VERTICAL); + mInfo.setInterval(LayoutUtils.dp2pix(context, INFO_INTERVAL)); glp = new GravityLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); glp.gravity = Gravity.CENTER; diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java index d3397d9a3..abb9c22b3 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/GalleryView.java @@ -17,7 +17,6 @@ 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; @@ -144,8 +143,8 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { 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; + mErrorSize = context.getResources().getDimension(R.dimen.text_large); + mErrorColor = context.getResources().getColor(R.color.red_500); mPageMinHeight = LayoutUtils.dp2pix(context, PAGE_MIN_HEIGHT_IN_DP); mInterval = LayoutUtils.dp2pix(context, INTERVAL_IN_DP); @@ -154,7 +153,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { private void ensurePagerLayoutManager() { if (mPagerLayoutManager == null) { - mPagerLayoutManager = new PagerLayoutManager(mContext, this, mInterval); + mPagerLayoutManager = new PagerLayoutManager(mContext, this); } } @@ -436,7 +435,7 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { public GalleryPageView obtainPage() { GalleryPageView page = mRecycler.obtain(); if (page == null) { - page = new GalleryPageView(mPageTextTexture, mProgressColor, mProgressSize); + page = new GalleryPageView(mContext, mPageTextTexture, mProgressColor, mProgressSize); page.setMinimumHeight(mPageMinHeight); } return page; @@ -523,12 +522,16 @@ public class GalleryView extends GLView implements GestureRecognizer.Listener { public abstract void reset(); - public abstract boolean isBusy(); + /** + * @return True for waiting now, layout manager + * should show a progress bar or something like it + */ + public abstract boolean isWaiting(); /** - * Null for no error + * @return Null for no error */ - public abstract String isError(); + public abstract String getError(); public abstract boolean hasNext(); diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java index 456a1b741..9c46b0ee5 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/PagerLayoutManager.java @@ -29,6 +29,7 @@ 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.LayoutUtils; import com.hippo.yorozuya.MathUtils; import java.lang.annotation.Retention; @@ -43,6 +44,8 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { public static final int MODE_LEFT_TO_RIGHT = 0; public static final int MODE_RIGHT_TO_LEFT = 1; + private static final int INTERVAL = 48; + private static final Interpolator SMOOTH_SCROLLER_INTERPOLATOR = new Interpolator() { @Override public float getInterpolation(float t) { @@ -76,10 +79,9 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { private SmoothScroller mSmoothScroller; private SmoothScaler mSmoothScaler; - public PagerLayoutManager(Context context, @NonNull GalleryView galleryView, - int interval) { + public PagerLayoutManager(Context context, @NonNull GalleryView galleryView) { super(galleryView); - mInterval = interval; + mInterval = LayoutUtils.dp2pix(context, INTERVAL); mPageFling = new PageFling(context); mSmoothScroller = new SmoothScroller(); @@ -239,7 +241,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { int width = galleryView.getWidth(); int height = galleryView.getHeight(); - String errorStr = iterator.isError(); + String errorStr = iterator.getError(); if (errorStr != null) { // Remove progress and all pages @@ -260,7 +262,7 @@ public class PagerLayoutManager extends GalleryView.LayoutManager { // Place error view center placeCenter(mErrorView); - } else if (iterator.isBusy()) { + } else if (iterator.isWaiting()) { // Remove error view and all pages removeErrorView(); removeAllPages(); diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java b/app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java index cf5e8ada3..aea2da7dc 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/gl/ScrollLayoutManager.java @@ -138,7 +138,7 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { int width = galleryView.getWidth(); int height = galleryView.getHeight(); - String errorStr = iterator.isError(); + String errorStr = iterator.getError(); if (errorStr != null) { // Remove progress and all pages @@ -159,7 +159,7 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { // Place error view center placeCenter(mErrorView); - } else if (iterator.isBusy()) { + } else if (iterator.isWaiting()) { // Remove error view and all pages removeErrorView(); removeAllPages(); @@ -177,6 +177,12 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { removeProgress(); removeErrorView(); + + // TODO ensure first shown + + + + int minY = (int) (-height * RESERVATIONS); // Remove useless top view @@ -281,6 +287,10 @@ public class ScrollLayoutManager extends GalleryView.LayoutManager { } iterator.reset(); + + // TODO Avoid page not fill bottom, but has previous + + // Remove useless page while (mPages.size() > pageSize) { GalleryPageView page = mPages.removeLast(); 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 304ad9226..1464c7164 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -16,9 +16,10 @@ package com.hippo.ehviewer.ui; +import android.content.Intent; import android.graphics.Typeface; import android.os.Bundle; -import android.os.Environment; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.util.SparseIntArray; @@ -28,6 +29,7 @@ import com.hippo.ehviewer.R; import com.hippo.ehviewer.gallery.DirGalleryProvider; import com.hippo.ehviewer.gallery.GalleryProvider; import com.hippo.ehviewer.gallery.GalleryProviderListener; +import com.hippo.ehviewer.gallery.ZipGalleryProvider; import com.hippo.ehviewer.gallery.gl.GalleryPageView; import com.hippo.ehviewer.gallery.gl.GalleryView; import com.hippo.gl.glrenderer.ImageTexture; @@ -41,6 +43,20 @@ import java.io.File; public class GalleryActivity extends AppCompatActivity implements GalleryProviderListener { + public static final String ACTION_DIR = "dir"; + public static final String ACTION_ZIP = "zip"; + public static final String ACTION_EH = "eh"; + + public static final String KEY_ACTION = "action"; + public static final String KEY_FILENAME = "filename"; + public static final String KEY_GID = "gid"; + public static final String KEY_TOKEN = "token"; + + private String mAction; + private String mFilename; + private int mGid; + private String mToken; + @Nullable private GalleryView mGalleryView; @@ -53,26 +69,82 @@ public class GalleryActivity extends AppCompatActivity implements GalleryProvide @Nullable private SparseIntArray mIndexIdMap; + private void buildProvider() { + if (mGalleryProvider != null) { + return; + } + + if (ACTION_DIR.equals(mAction)) { + if (mFilename != null) { + mGalleryProvider = new DirGalleryProvider(new File(mFilename)); + } + } else if (ACTION_ZIP.equals(mAction)) { + if (mFilename != null) { + mGalleryProvider = new ZipGalleryProvider(new File(mFilename)); + } + } else if (ACTION_EH.equals(mAction)) { + + } + } + + private void onInit() { + Intent intent = getIntent(); + if (intent == null) { + return; + } + + mAction = intent.getAction(); + mFilename = intent.getStringExtra(KEY_FILENAME); + mGid = intent.getIntExtra(KEY_GID, -1); + mToken = intent.getStringExtra(KEY_TOKEN); + buildProvider(); + } + + private void onRestore(@NonNull Bundle savedInstanceState) { + mAction = savedInstanceState.getString(KEY_ACTION); + mFilename = savedInstanceState.getString(KEY_FILENAME); + mGid = savedInstanceState.getInt(KEY_GID, -1); + mToken = savedInstanceState.getString(KEY_TOKEN); + buildProvider(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(KEY_ACTION, mAction); + outState.putString(KEY_FILENAME, mFilename); + outState.putInt(KEY_GID, mGid); + outState.putString(KEY_TOKEN, mToken); + } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_gallery); - GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view); + if (savedInstanceState == null) { + onInit(); + } else { + onRestore(savedInstanceState); + } - mUploader = new ImageTexture.Uploader(glRootView); - mIndexIdMap = new SparseIntArray(); - mGalleryProvider = new DirGalleryProvider(new File(Environment.getExternalStorageDirectory(), "nmb/image")); + if (mGalleryProvider == null) { + finish(); + return; + } mGalleryProvider.addGalleryProviderListener(this); mGalleryProvider.start(); + setContentView(R.layout.activity_gallery); + GLRootView glRootView = (GLRootView) findViewById(R.id.gl_root_view); + mUploader = new ImageTexture.Uploader(glRootView); + mIndexIdMap = new SparseIntArray(); 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); + mPageTextTexture, null, GalleryView.LAYOUT_MODE_RIGHT_TO_LEFT); glRootView.setContentPane(mGalleryView); } @@ -182,6 +254,13 @@ public class GalleryActivity extends AppCompatActivity implements GalleryProvide return mGalleryView.findPageById(id); } + @Override + public void onDataChanged() { + if (mGalleryView != null) { + mGalleryView.onDataChanged(); + } + } + @Override public void onPagePercent(int index, float percent) { GalleryPageView page = findPageByIndex(index); @@ -203,18 +282,20 @@ public class GalleryActivity extends AppCompatActivity implements GalleryProvide page.setPage(index + 1); page.setProgress(GalleryPageView.PROGRESS_GONE); page.setError(null, null); + } else { + image.recycle(); } } @Override - public void onPageFailed(int index, Exception e) { + public void onPageFailed(int index, String error) { 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); + page.setError(error, mGalleryView); } } @@ -235,20 +316,23 @@ public class GalleryActivity extends AppCompatActivity implements GalleryProvide } @Override - public boolean isBusy() { - return mGalleryProvider == null || - mGalleryProvider.size() == GalleryProvider.SIZE_WAIT; + public boolean isWaiting() { + return mGalleryProvider != null && mGalleryProvider.size() == GalleryProvider.STATE_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 + public String getError() { + if (mGalleryProvider == null) { + return getString(R.string.error_no_provider); + } else if (mGalleryProvider.size() == GalleryProvider.STATE_ERROR) { + String error = mGalleryProvider.getError(); + if (error != null) { + return error; + } else { + return getString(R.string.error_unknown); } + } else if (mGalleryProvider.size() == 0) { + return getString(R.string.error_empty); } return null; } @@ -282,29 +366,12 @@ public class GalleryActivity extends AppCompatActivity implements GalleryProvide @Override 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; - } + mGalleryProvider.request(mIndex); + view.showInfo(); + view.setImage(null); + view.setPage(mIndex + 1); + view.setProgress(GalleryPageView.PROGRESS_INDETERMINATE); + view.setError(null, null); } int id = mIdGenerator.nextId(); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java index 8a595957c..ba303f248 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java @@ -128,6 +128,8 @@ public final class MainActivity extends StageActivity private void onRestore(Bundle savedInstanceState) { Log.d("TAG", "onRestore"); + Log.d("TAG", "action = " + getIntent().getAction()); + mNavCheckedItem = savedInstanceState.getInt(KEY_NAV_CHECKED_ITEM); } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java index ca9000bce..3de531eff 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java @@ -65,7 +65,9 @@ public class GalleryPreviewsScene extends ToolbarScene { public final static String KEY_HAS_FIRST_REFRESH = "has_first_refresh"; private EhClient mClient; + @Nullable private GalleryPreviewAdapter mAdapter; + @Nullable private GalleryPreviewHelper mHelper; private int mGid = -1; private String mToken = null; @@ -169,6 +171,9 @@ public class GalleryPreviewsScene extends ToolbarScene { int id = item.getItemId(); switch (id) { case R.id.action_go_to: + if (mHelper == null) { + return true; + } int pages = mHelper.getPages(); if (pages > 0 && mHelper.canGoTo()) { GoToDialogHelper helper = new GoToDialogHelper(pages, mHelper.getPageForTop()); @@ -211,14 +216,16 @@ public class GalleryPreviewsScene extends ToolbarScene { @Override public void onBindViewHolder(GalleryPreviewHolder holder, int position) { - GalleryPreview preview = mHelper.getDataAt(position); - holder.image.load(EhCacheKeyFactory.getLargePreviewKey(mGid, preview.index), preview.imageUrl); - holder.text.setText(String.format(Locale.US, "%d", preview.index + 1)); + if (mHelper != null) { + GalleryPreview preview = mHelper.getDataAt(position); + holder.image.load(EhCacheKeyFactory.getLargePreviewKey(mGid, preview.index), preview.imageUrl); + holder.text.setText(String.format(Locale.US, "%d", preview.index + 1)); + } } @Override public int getItemCount() { - return mHelper.size(); + return mHelper != null ? mHelper.size() : 0; } } @@ -260,17 +267,23 @@ public class GalleryPreviewsScene extends ToolbarScene { @Override protected void notifyDataSetChanged() { - mAdapter.notifyDataSetChanged(); + if (mAdapter != null) { + mAdapter.notifyDataSetChanged(); + } } @Override protected void notifyItemRangeRemoved(int positionStart, int itemCount) { - mAdapter.notifyItemRangeRemoved(positionStart, itemCount); + if (mAdapter != null) { + mAdapter.notifyItemRangeRemoved(positionStart, itemCount); + } } @Override protected void notifyItemRangeInserted(int positionStart, int itemCount) { - mAdapter.notifyItemRangeInserted(positionStart, itemCount); + if (mAdapter != null) { + mAdapter.notifyItemRangeInserted(positionStart, itemCount); + } } } @@ -376,7 +389,7 @@ public class GalleryPreviewsScene extends ToolbarScene { @Override public void onClick(View v) { int page = mSlider.getProgress() - 1; - if (page >= 0 && page < mPages) { + if (page >= 0 && page < mPages && mHelper != null) { mHelper.goTo(page); if (mDialog != null) { mDialog.dismiss(); 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 88cde4fe1..b4a6eac03 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 = true; + private static final boolean DEBUG_DRAW_BOUNDS = false; @IntDef({VISIBLE, INVISIBLE, GONE}) @Retention(RetentionPolicy.SOURCE) diff --git a/app/src/main/jni/giflib/CMakeLists.txt b/app/src/main/jni/giflib/CMakeLists.txt deleted file mode 100644 index 2c725df34..000000000 --- a/app/src/main/jni/giflib/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.3) - -project(libgif) - -include_directories(E:\\Android\\android-sdk\\ndk-bundle\\platforms\\android-14\\arch-arm\\usr\\include) - -set(SOURCE_FILES - dgif_lib.c - egif_lib.c - gif_hash.c - gifalloc.c - openbsd-reallocarray.c) - -add_executable(libgif ${SOURCE_FILES}) diff --git a/app/src/main/jni/image/CMakeLists.txt b/app/src/main/jni/image/CMakeLists.txt deleted file mode 100644 index b2c1718da..000000000 --- a/app/src/main/jni/image/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required(VERSION 3.3) - -project(image) - -include_directories(E:\\Android\\android-sdk\\ndk-bundle\\platforms\\android-14\\arch-arm\\usr\\include) -include_directories(..\\libjpeg-turbo) -include_directories(..\\libpng) -include_directories(..\\giflib) -include_directories(..\\stream) - -set(SOURCE_FILES - image.c - image_jpeg.c - image_png.c - image_gif.c - image_utils.c - java_wrapper.c) - -add_executable(image ${SOURCE_FILES}) diff --git a/app/src/main/jni/libjpeg-turbo/CMakeLists.txt b/app/src/main/jni/libjpeg-turbo/CMakeLists.txt deleted file mode 100644 index 54b36d39e..000000000 --- a/app/src/main/jni/libjpeg-turbo/CMakeLists.txt +++ /dev/null @@ -1,42 +0,0 @@ -cmake_minimum_required(VERSION 3.3) - -project(jpeg-turbo) -include_directories(E:\\Android\\android-sdk\\ndk-bundle\\platforms\\android-14\\arch-arm\\usr\\include) -set(SOURCE_FILES jaricom.c -jcomapi.c -jdapimin.c -jdapistd.c -jdarith.c -jdatasrc.c -jdatasrc-tj.c -jdcoefct.c -jdcolor.c -jddctmgr.c -jdhuff.c -jdinput.c -jdmainct.c -jdmarker.c -jdmaster.c -jdmerge.c -jdphuff.c -jdpostct.c -jdsample.c -jdtrans.c -jerror.c -jfdctflt.c -jfdctfst.c -jfdctint.c -jidctflt.c -jidctfst.c -jidctint.c -jidctred.c -jmemmgr.c -jmemnobs.c -jquant1.c -jquant2.c -jutils.c -jsimd_none.c -simd/jsimd_arm.c -simd/jsimd_arm64.c) - -add_executable(jpeg-turbo ${SOURCE_FILES}) diff --git a/app/src/main/jni/libpng/CMakeLists.txt b/app/src/main/jni/libpng/CMakeLists.txt deleted file mode 100644 index 3849f5567..000000000 --- a/app/src/main/jni/libpng/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -cmake_minimum_required(VERSION 3.3) - -project(libpng) - -include_directories(E:\\Android\\android-sdk\\ndk-bundle\\platforms\\android-14\\arch-arm\\usr\\include) - -set(SOURCE_FILES - png.c - pngerror.c - pngget.c - pngmem.c - pngpread.c - pngread.c - pngrio.c - pngrtran.c - pngrutil.c - pngset.c - pngtrans.c) - -add_executable(libpng ${SOURCE_FILES}) diff --git a/app/src/main/jni/log.h b/app/src/main/jni/log.h index e8bce8e5a..e31747389 100644 --- a/app/src/main/jni/log.h +++ b/app/src/main/jni/log.h @@ -34,7 +34,7 @@ #define STRINGIZE(L) #L #define MAKESTRING(M, L) M(L) #define FILE_LINE MAKESTRING(STRINGIZE, __LINE__ ) -#define EMSG(msg) __FILE__ "(" FILE_LINE ") : "msg +#define EMSG(msg) __FILE__ "(" FILE_LINE "): "msg #define OUT_OF_MEMORY "Out of memory" diff --git a/app/src/main/jni/stream/CMakeLists.txt b/app/src/main/jni/stream/CMakeLists.txt deleted file mode 100644 index 1324c2ffb..000000000 --- a/app/src/main/jni/stream/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.3) - -project(stream) - -include_directories(E:\\Android\\android-sdk\\ndk-bundle\\platforms\\android-14\\arch-arm\\usr\\include) - -set(SOURCE_FILES - input_stream.c - input_stream_pipe.c - output_stream.c - output_stream_pipe.c - patch_head_input_stream.c) - -add_executable(stream ${SOURCE_FILES}) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 07d77f761..0b4b4163b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -81,6 +81,13 @@ Weird Can\'t find the application Can\'t parse the url + Not folder path + No provider + Decoding failed + Not found + Empty + Invalid zip file + Reading Failed The content of this application is from the Internet. Some of it may do physical or mental harm to you. You have learnt the risks above and would like to undertake them. Do you want to use this application? Username cannot be an empty string