更换 cache,更换图片获取方式,建立 previewlist 类,移除不需要的类
This commit is contained in:
1 parent
4e266c160c
commit
ce2907610a
30 files changed
+580
-3083
No files matched your search
@@ -1,6 +1,7 @@
|
||||
Version 0.5.4 (2014-??-??)
|
||||
* 用户信息中显示用户名改为显示昵称
|
||||
* 用户信息中显示头像
|
||||
* 显示登陆中的错误
|
||||
|
||||
Version 0.5.3 (2014-06-15)
|
||||
* 添加热门
|
||||
|
||||
@@ -22,10 +22,10 @@ import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import com.hippo.ehviewer.cache.ImageCache;
|
||||
import com.hippo.ehviewer.data.Data;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Crash;
|
||||
import com.hippo.ehviewer.util.Download;
|
||||
@@ -46,7 +46,6 @@ public class AppContext extends Application implements UncaughtExceptionHandler
|
||||
|
||||
private Data mData;
|
||||
private EhClient mEhClient;
|
||||
private ImageGeterManager mImageGeterManager;
|
||||
private ThreadPool mNetworkThreadPool;
|
||||
|
||||
@Override
|
||||
@@ -58,15 +57,13 @@ public class AppContext extends Application implements UncaughtExceptionHandler
|
||||
|
||||
Config.init(this);
|
||||
Ui.init(this);
|
||||
Cache.init(this);
|
||||
Crash.init(this);
|
||||
mEhClient = new EhClient(this);
|
||||
Download.init(this);
|
||||
Favorite.init(this);
|
||||
|
||||
mData = new Data(this);
|
||||
mImageGeterManager = new ImageGeterManager(this, Cache.memoryCache, Cache.diskCache);
|
||||
LoadImageView.setImageGeterManager(mImageGeterManager);
|
||||
ImageLoader.createHandler();
|
||||
HttpHelper.createHandler();
|
||||
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||
@@ -96,14 +93,19 @@ public class AppContext extends Application implements UncaughtExceptionHandler
|
||||
return mData;
|
||||
}
|
||||
|
||||
public ImageGeterManager getImageGeterManager() {
|
||||
return mImageGeterManager;
|
||||
}
|
||||
|
||||
public DateFormat getDateFormat() {
|
||||
return mFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
ImageCache.getInstance(this).evictAll();
|
||||
super.onLowMemory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
if (!handleException(ex) && mDefaultHandler != null) {
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
import com.jakewharton.disklrucache.DiskLruCache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Movie;
|
||||
import android.os.Environment;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
// For Gallery cover key is gid,
|
||||
// for example , gid is 618395, token is 0439fa3666,
|
||||
// so key is 618395
|
||||
|
||||
// For Gallery preview key is gid-preview-num-row,
|
||||
// for example , gid is 618395, token is 0439fa3666, page2
|
||||
// so key is 618395-preview-2-0
|
||||
|
||||
// For Gallery image key is gid-preview-num,
|
||||
// for example , gid is 618395, token is 0439fa3666, image2
|
||||
// so key is 618395-image-2
|
||||
|
||||
public class DiskCache {
|
||||
private static String TAG = "ImageDiskCache";
|
||||
|
||||
private static final int VALUE_IDX = 0;
|
||||
|
||||
private Context mContext;
|
||||
private BitmapFactory.Options opt;
|
||||
private final DiskLruCache diskLruCache;
|
||||
|
||||
public DiskCache(Context context, String path, long maxSize) throws NameNotFoundException, IOException {
|
||||
this.mContext = context;
|
||||
|
||||
opt = Ui.getBitmapOpt();
|
||||
if (!Cache.hasSdCard()) {
|
||||
throw new IllegalStateException("Has no SdCard.");
|
||||
}
|
||||
|
||||
File dir = new File(Environment.getExternalStorageDirectory() + path);
|
||||
int version = mContext.getPackageManager().getPackageInfo(
|
||||
mContext.getPackageName(), 0).versionCode;
|
||||
version = 1;
|
||||
diskLruCache = DiskLruCache.open(dir, version, 1, maxSize);
|
||||
}
|
||||
|
||||
public long size() {
|
||||
return diskLruCache.size();
|
||||
}
|
||||
|
||||
public long maxSize() {
|
||||
return diskLruCache.getMaxSize();
|
||||
}
|
||||
|
||||
public void setMaxSize(long maxSize) {
|
||||
diskLruCache.setMaxSize(maxSize);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
diskLruCache.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "diskLruCache close error !");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
DiskLruCache.Snapshot snapshot = null;
|
||||
try {
|
||||
snapshot = diskLruCache.get(key);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if(snapshot==null)
|
||||
return false;
|
||||
snapshot.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object get(String key, int type) {
|
||||
DiskLruCache.Snapshot snapshot = null;
|
||||
try {
|
||||
snapshot = diskLruCache.get(key);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if (snapshot == null)
|
||||
return null;
|
||||
InputStream is = snapshot.getInputStream(VALUE_IDX);
|
||||
if (type == Util.BITMAP) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opt);
|
||||
return bitmap;
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
Movie movie = Movie.decodeStream(is);
|
||||
return movie;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean put(String key, InputStream is) throws IOException {
|
||||
DiskLruCache.Editor editor = diskLruCache.edit(key);
|
||||
if (editor == null)
|
||||
return false;
|
||||
BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX));
|
||||
OutputStream os = new CacheOutputStream(bos, editor);
|
||||
Util.copy(is, os);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
diskLruCache.delete();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private class CacheOutputStream extends FilterOutputStream {
|
||||
|
||||
private final DiskLruCache.Editor editor;
|
||||
private boolean failed = false;
|
||||
|
||||
private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) {
|
||||
super(os);
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
IOException closeException = null;
|
||||
try {
|
||||
super.close();
|
||||
} catch (IOException e) {
|
||||
closeException = e;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
editor.abort();
|
||||
} else {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
if (closeException != null) throw closeException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
try {
|
||||
super.flush();
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) throws IOException {
|
||||
try {
|
||||
super.write(oneByte);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer) throws IOException {
|
||||
try {
|
||||
super.write(buffer);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
try {
|
||||
super.write(buffer, offset, length);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
private Context context = null;
|
||||
private File dir = null;
|
||||
int maxSize = 1024 * 10;
|
||||
|
||||
public DiskCache(Context context, String path, int maxSize) {
|
||||
this.context = context;
|
||||
this.maxSize = maxSize;
|
||||
if (maxSize <= 0)
|
||||
maxSize = 1024 * 10;
|
||||
|
||||
File rootdir = Environment.getExternalStorageDirectory();
|
||||
dir = new File(rootdir + path);
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
synchronized (key) {
|
||||
|
||||
File file = new File(dir, key);
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory())
|
||||
file.delete();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Object get(String key, int type) {
|
||||
synchronized (key) {
|
||||
File file = new File(dir, key);
|
||||
if (!file.isFile())
|
||||
return null;
|
||||
try {
|
||||
InputStream is = new FileInputStream(file);
|
||||
if (type == Util.BITMAP) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, Util.opt);
|
||||
return bitmap;
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
Movie movie = Movie.decodeStream(is);
|
||||
return movie;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.d(TAG, "WTF, I have checked that the file does exists.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean put(String key, InputStream is) throws IOException {
|
||||
synchronized (key) {
|
||||
File file = new File(dir, key);
|
||||
try {
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
dir.mkdirs();
|
||||
file.delete();
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e1) {
|
||||
Log.d(TAG, "WTF, mkdirs didn't work? Path is " + file.getPath());
|
||||
e1.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Util.inputStream2File(is, file);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Put " + key + " error.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
clearDirectory(dir);
|
||||
}
|
||||
|
||||
private void clearDirectory(File dir) {
|
||||
|
||||
for (File file: dir.listFiles()) {
|
||||
if (file.isDirectory()) clearDirectory(file);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
}*/
|
||||
/*
|
||||
* private static String getStingMD5(String s) { String re = null; try {
|
||||
* MessageDigest messageDigest =MessageDigest.getInstance(Algorithm); byte[]
|
||||
* inputByteArray = s.getBytes(); messageDigest.update(inputByteArray);
|
||||
* byte[] resultByteArray = messageDigest.digest(); re =
|
||||
* byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) {
|
||||
* e.printStackTrace(); Log.d(TAG, e.getMessage()); } return re; }
|
||||
*
|
||||
* private static String byteArrayToHex(byte[] byteArray) { char[] hexDigits
|
||||
* = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
|
||||
* char[] resultCharArray =new char[byteArray.length * 2]; int index = 0;
|
||||
* for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b>>> 4 &
|
||||
* 0xf]; resultCharArray[index++] = hexDigits[b& 0xf]; } return new
|
||||
* String(resultCharArray); }
|
||||
*/
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 java.util.Stack;
|
||||
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
import com.hippo.ehviewer.util.Constants;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
import com.hippo.ehviewer.widget.LoadImageView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.v4.util.LruCache;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
public class ImageGeterManager {
|
||||
private static final String TAG = "ImageGeterManager";
|
||||
|
||||
public static final int MEMORY_CACHE = 0x1;
|
||||
public static final int DISK_CACHE = 0x2;
|
||||
public static final int DOWNLOAD = 0x4;
|
||||
|
||||
private class LoadTask {
|
||||
public String url;
|
||||
public String key;
|
||||
public int mode;
|
||||
public OnGetImageListener listener;
|
||||
public Bitmap bitmap;
|
||||
|
||||
public LoadTask(String url, String key, int mode, OnGetImageListener listener) {
|
||||
this.url = url;
|
||||
this.key = key;
|
||||
this.mode = mode;
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
private Context mContext;
|
||||
|
||||
private final Stack<LoadTask> mLoadCacheTask;
|
||||
private ImageDownloadManager mImageDownloadTask;
|
||||
|
||||
private LruCache<String, Bitmap> mMemoryCache;
|
||||
private DiskCache mDiskCache;
|
||||
|
||||
private Object mLock;
|
||||
|
||||
private static final Handler mHandler =
|
||||
new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
LoadTask task = (LoadTask)msg.obj;
|
||||
if (msg.what == Constants.TRUE) {
|
||||
task.listener.onGetImageSuccess(task.key, task.bitmap, msg.arg1);
|
||||
} else if (msg.what == Constants.FALSE){
|
||||
task.listener.onGetImageFail(task.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ImageGeterManager(Context context, LruCache<String, Bitmap> memoryCache,
|
||||
DiskCache diskCache) {
|
||||
mLoadCacheTask = new Stack<LoadTask>();
|
||||
mImageDownloadTask = new ImageDownloadManager();
|
||||
|
||||
mContext = context;
|
||||
mMemoryCache = memoryCache;
|
||||
mDiskCache = diskCache;
|
||||
|
||||
mLock = new Object();
|
||||
new Thread(new LoadFromCacheTask()).start();
|
||||
}
|
||||
|
||||
public void add(String url, String key, int mode, OnGetImageListener listener) {
|
||||
synchronized (mLock) {
|
||||
mLoadCacheTask.push(new LoadTask(url, key, mode, listener));
|
||||
mLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadFromCacheTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LoadTask loadTask;
|
||||
while (true) {
|
||||
synchronized (mLock) {
|
||||
if (mLoadCacheTask.isEmpty()) {
|
||||
try {
|
||||
mLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
loadTask = mLoadCacheTask.pop();
|
||||
}
|
||||
|
||||
// Continue if do not need any more
|
||||
if (!loadTask.listener.onGetImageStart(loadTask.key))
|
||||
continue;
|
||||
|
||||
String key = loadTask.key;
|
||||
int mode = loadTask.mode;
|
||||
int getMode = MEMORY_CACHE;
|
||||
if ((mode & MEMORY_CACHE) != 0 && mMemoryCache != null
|
||||
&& (loadTask.bitmap = mMemoryCache.get(key)) != null)
|
||||
getMode = MEMORY_CACHE;
|
||||
else if ((mode & DISK_CACHE) != 0 && mDiskCache != null
|
||||
&& (loadTask.bitmap = (Bitmap)mDiskCache.get(key, Util.BITMAP)) != null) {
|
||||
getMode = DISK_CACHE;
|
||||
if (mMemoryCache != null)
|
||||
mMemoryCache.put(key, loadTask.bitmap);
|
||||
}
|
||||
|
||||
if (loadTask.bitmap != null) { // Get bitmap
|
||||
Message msg = new Message();
|
||||
msg.obj = loadTask;
|
||||
msg.what = Constants.TRUE;
|
||||
msg.arg1 = getMode;
|
||||
mHandler.sendMessage(msg);
|
||||
} else { // get from cache miss
|
||||
if ((mode & DOWNLOAD) != 0) {
|
||||
mImageDownloadTask.add(loadTask);
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.obj = loadTask;
|
||||
msg.what = Constants.FALSE;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ImageDownloadManager {
|
||||
|
||||
private static final int MAX_DOWNLOAD_THREADS = 3;
|
||||
|
||||
private final Stack<LoadTask> mDownloadTask;
|
||||
|
||||
private int workingDownloadThreads = 0;
|
||||
|
||||
public ImageDownloadManager() {
|
||||
mDownloadTask = new Stack<LoadTask>();
|
||||
}
|
||||
|
||||
public synchronized void add(LoadTask loadTask) {
|
||||
mDownloadTask.push(loadTask);
|
||||
if (workingDownloadThreads < MAX_DOWNLOAD_THREADS) {
|
||||
new Thread(new DownloadImageTask()).start();
|
||||
workingDownloadThreads++;
|
||||
}
|
||||
}
|
||||
|
||||
private class DownloadImageTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LoadTask loadTask;
|
||||
HttpHelper httpHelper = new HttpHelper(mContext);
|
||||
while (true) {
|
||||
synchronized (ImageDownloadManager.this) {
|
||||
if (mDownloadTask.isEmpty()) {
|
||||
loadTask = null;
|
||||
workingDownloadThreads--;
|
||||
break;
|
||||
}
|
||||
loadTask = mDownloadTask.pop();
|
||||
}
|
||||
|
||||
loadTask.bitmap = httpHelper.getImage(loadTask.url,
|
||||
loadTask.key, mMemoryCache, mDiskCache, true);
|
||||
|
||||
Message msg = new Message();
|
||||
msg.obj = loadTask;
|
||||
if (loadTask.bitmap == null) {
|
||||
msg.what = Constants.FALSE;
|
||||
} else {
|
||||
msg.what = Constants.TRUE;
|
||||
msg.arg1 = DOWNLOAD;
|
||||
}
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnGetImageListener {
|
||||
/**
|
||||
* @return False if you wanna stop get
|
||||
*/
|
||||
boolean onGetImageStart(String key);
|
||||
void onGetImageSuccess(String key, Bitmap bmp, int mode);
|
||||
void onGetImageFail(String key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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 java.util.Stack;
|
||||
|
||||
import com.hippo.ehviewer.cache.ImageCache;
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public class ImageLoader {
|
||||
private static final String TAG = ImageLoader.class.getSimpleName();
|
||||
|
||||
private static ImageLoader sInstance;
|
||||
private static Handler mHandler;
|
||||
|
||||
private class LoadTask {
|
||||
public String url;
|
||||
public String key;
|
||||
public OnGetImageListener listener;
|
||||
public Bitmap bitmap;
|
||||
|
||||
public LoadTask(String url, String key, OnGetImageListener listener) {
|
||||
this.url = url;
|
||||
this.key = key;
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private final Stack<LoadTask> mLoadTasks;
|
||||
private ImageCache mImageCache;
|
||||
private ImageDownloader mImageDownloader;
|
||||
private Object mLock;
|
||||
|
||||
|
||||
public static final void createHandler() {
|
||||
if (mHandler != null)
|
||||
return;
|
||||
mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
LoadTask task = (LoadTask)msg.obj;
|
||||
task.listener.onGetImage(task.key, task.bitmap);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ImageLoader(Context context) {
|
||||
mLoadTasks = new Stack<LoadTask>();
|
||||
mImageDownloader = new ImageDownloader();
|
||||
|
||||
mContext = context;
|
||||
mImageCache = ImageCache.getInstance(mContext);
|
||||
|
||||
mLock = new Object();
|
||||
new Thread(new LoadFromCacheTask()).start();
|
||||
}
|
||||
|
||||
public final static ImageLoader getInstance(final Context context) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ImageLoader(context.getApplicationContext());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public void add(String url, String key, OnGetImageListener listener) {
|
||||
synchronized (mLock) {
|
||||
mLoadTasks.push(new LoadTask(url, key, listener));
|
||||
mLock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadFromCacheTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LoadTask loadTask;
|
||||
while (true) {
|
||||
synchronized (mLock) {
|
||||
if (mLoadTasks.isEmpty()) {
|
||||
try {
|
||||
mLock.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
continue;
|
||||
}
|
||||
loadTask = mLoadTasks.pop();
|
||||
}
|
||||
|
||||
String key = loadTask.key;
|
||||
loadTask.bitmap = mImageCache.getCachedBitmap(key);
|
||||
|
||||
if (loadTask.bitmap != null) {
|
||||
Message msg = new Message();
|
||||
msg.obj = loadTask;
|
||||
mHandler.sendMessage(msg);
|
||||
} else {
|
||||
mImageDownloader.add(loadTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ImageDownloader {
|
||||
private static final int MAX_DOWNLOAD_THREADS = 3;
|
||||
private final Stack<LoadTask> mDownloadTasks;
|
||||
private int mWorkingThreadNum = 0;
|
||||
private Object mLock;
|
||||
|
||||
public ImageDownloader() {
|
||||
mLock = new Object();
|
||||
mDownloadTasks = new Stack<LoadTask>();
|
||||
}
|
||||
|
||||
public void add(LoadTask loadTask) {
|
||||
synchronized (mLock) {
|
||||
mDownloadTasks.push(loadTask);
|
||||
if (mWorkingThreadNum < MAX_DOWNLOAD_THREADS) {
|
||||
new Thread(new DownloadImageTask()).start();
|
||||
mWorkingThreadNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DownloadImageTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LoadTask loadTask;
|
||||
HttpHelper httpHelper = new HttpHelper(mContext);
|
||||
while (true) {
|
||||
synchronized (mLock) {
|
||||
if (mDownloadTasks.isEmpty()) {
|
||||
loadTask = null;
|
||||
mWorkingThreadNum--;
|
||||
break;
|
||||
}
|
||||
loadTask = mDownloadTasks.pop();
|
||||
}
|
||||
|
||||
loadTask.bitmap = httpHelper.getImage(loadTask.url);
|
||||
mImageCache.addBitmapToCache(loadTask.key, loadTask.bitmap);
|
||||
|
||||
Message msg = new Message();
|
||||
msg.obj = loadTask;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnGetImageListener {
|
||||
/**
|
||||
* bmp is null for fail
|
||||
* @param key
|
||||
* @param bmp
|
||||
*/
|
||||
void onGetImage(String key, Bitmap bmp);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import com.hippo.ehviewer.ui.MangaListActivity;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Crash;
|
||||
import com.hippo.ehviewer.widget.AlertButton;
|
||||
@@ -252,7 +251,7 @@ public class StartActivity extends Activity {
|
||||
return;
|
||||
}
|
||||
case CHECK_EXTERNAL_STORAGE:
|
||||
if (!Cache.hasSdCard()) {
|
||||
if (false) {
|
||||
createNoSdCardDialog().show();
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-68
@@ -48,12 +48,7 @@ import com.hippo.ehviewer.util.Util;
|
||||
public final class ImageCache {
|
||||
|
||||
private static final String TAG = ImageCache.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* The {@link Uri} used to retrieve album art
|
||||
*/
|
||||
private static final Uri mArtworkUri;
|
||||
|
||||
|
||||
/**
|
||||
* Default memory cache size as a percent of device memory class
|
||||
*/
|
||||
@@ -96,11 +91,7 @@ public final class ImageCache {
|
||||
*/
|
||||
public boolean mPauseDiskAccess = false;
|
||||
private Object mPauseLock = new Object();
|
||||
|
||||
static {
|
||||
mArtworkUri = Uri.parse("content://media/external/audio/albumart");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor of <code>ImageCache</code>
|
||||
*
|
||||
@@ -413,63 +404,7 @@ public final class ImageCache {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to return the album art from memory cache and disk cache, before
|
||||
* calling {@code #getArtworkFromFile(Context, String)} again
|
||||
*
|
||||
* @param context The {@link Context} to use
|
||||
* @param data The name of the album art
|
||||
* @param id The ID of the album to find artwork for
|
||||
* @return The artwork for an album
|
||||
*/
|
||||
public final Bitmap getCachedArtwork(final Context context, final String data, final long id) {
|
||||
if (context == null || data == null) {
|
||||
return null;
|
||||
}
|
||||
Bitmap cachedImage = getCachedBitmap(data);
|
||||
if (cachedImage == null && id >= 0) {
|
||||
cachedImage = getArtworkFromFile(context, id);
|
||||
}
|
||||
if (cachedImage != null) {
|
||||
addBitmapToMemCache(data, cachedImage);
|
||||
return cachedImage;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to fetch the artwork for an album locally from the user's device
|
||||
*
|
||||
* @param context The {@link Context} to use
|
||||
* @param albumID The ID of the album to find artwork for
|
||||
* @return The artwork for an album
|
||||
*/
|
||||
public final Bitmap getArtworkFromFile(final Context context, final long albumId) {
|
||||
if (albumId < 0) {
|
||||
return null;
|
||||
}
|
||||
Bitmap artwork = null;
|
||||
waitUntilUnpaused();
|
||||
try {
|
||||
final Uri uri = ContentUris.withAppendedId(mArtworkUri, albumId);
|
||||
final ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver()
|
||||
.openFileDescriptor(uri, "r");
|
||||
if (parcelFileDescriptor != null) {
|
||||
final FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
|
||||
artwork = BitmapFactory.decodeFileDescriptor(fileDescriptor);
|
||||
}
|
||||
} catch (final IllegalStateException e) {
|
||||
// Log.e(TAG, "IllegalStateExcetpion - getArtworkFromFile - ", e);
|
||||
} catch (final FileNotFoundException e) {
|
||||
// Log.e(TAG, "FileNotFoundException - getArtworkFromFile - ", e);
|
||||
} catch (final OutOfMemoryError evict) {
|
||||
// Log.e(TAG, "OutOfMemoryError - getArtworkFromFile - ", evict);
|
||||
evictAll();
|
||||
}
|
||||
return artwork;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* flush() is called to synchronize up other methods that are accessing the
|
||||
* cache first
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
||||
import com.hippo.ehviewer.ImageLoader;
|
||||
import com.hippo.ehviewer.ui.MangaActivity;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.widget.AutoWrapLayout;
|
||||
|
||||
public class NormalPreviewList extends PreviewList{
|
||||
|
||||
public class Item {
|
||||
public int xOffset;
|
||||
public int yOffset;
|
||||
public int width;
|
||||
public int height;
|
||||
public String url;
|
||||
|
||||
public Item(int xOffset, int yOffset, int width, int height, String url) {
|
||||
this.xOffset = xOffset;
|
||||
this.yOffset = yOffset;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public class Row {
|
||||
public String imageUrl;
|
||||
public ArrayList<Item> itemArray = new ArrayList<Item>();
|
||||
public int startIndex;
|
||||
public Row(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
public void addItem(int xOffset, int yOffset, int width, int height,
|
||||
String url) {
|
||||
itemArray.add(new Item(xOffset, yOffset, width, height, url));
|
||||
}
|
||||
}
|
||||
|
||||
private Row curRow;
|
||||
public ArrayList<Row> rowArray = new ArrayList<Row>();
|
||||
|
||||
public void addItem(String imageUrl, String xOffset, String yOffset, String width, String height,
|
||||
String url) {
|
||||
if (curRow == null) {
|
||||
curRow = new Row(imageUrl);
|
||||
curRow.startIndex = 0;
|
||||
rowArray.add(curRow);
|
||||
} else if (!curRow.imageUrl.equals(imageUrl)) {
|
||||
Row lastRow = curRow;
|
||||
curRow = new Row(imageUrl);
|
||||
curRow.startIndex = lastRow.startIndex + lastRow.itemArray.size();
|
||||
rowArray.add(curRow);
|
||||
}
|
||||
|
||||
curRow.addItem(Integer.parseInt(xOffset),
|
||||
Integer.parseInt(yOffset), Integer.parseInt(width),
|
||||
Integer.parseInt(height), url);
|
||||
}
|
||||
|
||||
public int getSum() {
|
||||
int sum = 0;
|
||||
for (Row row : rowArray)
|
||||
sum += row.itemArray.size();
|
||||
return sum;
|
||||
}
|
||||
|
||||
// TODO reuse the TextView in AutoWrapLayout
|
||||
@Override
|
||||
public void addPreview(AutoWrapLayout viewGroup) {
|
||||
if (mTargetPage != mHolder.getCurPreviewPage())
|
||||
return;
|
||||
|
||||
int index = mTargetPage * mGi.previewPerPage + 1; // it is display index
|
||||
int rowIndex = 0;
|
||||
for (NormalPreviewList.Row row : rowArray) {
|
||||
for (NormalPreviewList.Item item : row.itemArray) {
|
||||
TextViewWithUrl tvu = new TextViewWithUrl(mActivity);
|
||||
tvu.url = item.url;
|
||||
tvu.setGravity(Gravity.CENTER);
|
||||
tvu.setText(String.valueOf(index));
|
||||
final int finalIndex = index;
|
||||
tvu.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Add to read in Data
|
||||
Intent intent = new Intent(mActivity,
|
||||
MangaActivity.class);
|
||||
intent.putExtra("url", ((TextViewWithUrl)v).url);
|
||||
intent.putExtra("gid", mGi.gid);
|
||||
intent.putExtra("title", mGi.title);
|
||||
intent.putExtra("firstPage", finalIndex - 1);
|
||||
intent.putExtra("pageSum", mGi.pages);
|
||||
mActivity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
// Set transport drawable for temp
|
||||
ColorDrawable white = new ColorDrawable(Color.TRANSPARENT);
|
||||
white.setBounds(0, 0, item.width, item.height);
|
||||
tvu.setCompoundDrawables(null, white, null, null);
|
||||
|
||||
AutoWrapLayout.LayoutParams lp = new AutoWrapLayout.LayoutParams();
|
||||
lp.leftMargin = Ui.dp2pix(8);
|
||||
lp.topMargin = Ui.dp2pix(8);
|
||||
lp.rightMargin = Ui.dp2pix(8);
|
||||
lp.bottomMargin = Ui.dp2pix(8);
|
||||
viewGroup.addView(tvu, lp);
|
||||
|
||||
index++;
|
||||
}
|
||||
ImageLoader.getInstance(mActivity).add(row.imageUrl, mGi.gid + "preview" + "m" + rowIndex,
|
||||
new PreviewImageGetListener(viewGroup, rowIndex));
|
||||
rowIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private class PreviewImageGetListener implements ImageLoader.OnGetImageListener {
|
||||
private AutoWrapLayout mViewGroup;
|
||||
private int mRowIndex;
|
||||
|
||||
public PreviewImageGetListener(AutoWrapLayout viewGroup, int rowIndex) {
|
||||
mViewGroup = viewGroup;
|
||||
mRowIndex = rowIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetImage(String key, Bitmap bmp) {
|
||||
if (mTargetPage != mHolder.getCurPreviewPage())
|
||||
return;
|
||||
|
||||
if (bmp == null) {
|
||||
mHolder.onGetPreviewImageFailure();
|
||||
} else {
|
||||
int maxWidth = bmp.getWidth();
|
||||
int maxHeight = bmp.getHeight();
|
||||
|
||||
NormalPreviewList.Row row = rowArray.get(mRowIndex);
|
||||
|
||||
int i = row.startIndex;
|
||||
for(Item item : row.itemArray) {
|
||||
if (item.xOffset + item.width > maxWidth)
|
||||
item.width = maxWidth - item.xOffset;
|
||||
if (item.yOffset + item.height > maxHeight)
|
||||
item.height = maxHeight - item.yOffset;
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(
|
||||
mActivity.getResources(), Bitmap.createBitmap(bmp,
|
||||
item.xOffset, item.yOffset, item.width, item.height));
|
||||
bitmapDrawable.setBounds(0, 0, item.width, item.height);
|
||||
|
||||
TextViewWithUrl tvu = (TextViewWithUrl)mViewGroup.getChildAt(i);
|
||||
tvu.setCompoundDrawables(null, bitmapDrawable, null, null);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,66 +16,54 @@
|
||||
|
||||
package com.hippo.ehviewer.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class PreviewList {
|
||||
import com.hippo.ehviewer.ui.DetailSectionFragment;
|
||||
import com.hippo.ehviewer.widget.AutoWrapLayout;
|
||||
|
||||
/**
|
||||
* This interface describe the previews in one page
|
||||
*
|
||||
* @author Hippo
|
||||
*
|
||||
*/
|
||||
public abstract class PreviewList {
|
||||
|
||||
public class Row {
|
||||
protected PreviewHolder mHolder;
|
||||
protected Activity mActivity;
|
||||
protected GalleryDetail mGi;
|
||||
protected int mTargetPage;
|
||||
|
||||
/**
|
||||
* Add preview to view group
|
||||
* @param viewGroup
|
||||
* @return
|
||||
*/
|
||||
public abstract void addPreview(AutoWrapLayout viewGroup);
|
||||
|
||||
public void setData(PreviewHolder holder, Activity activity, GalleryDetail gi, int targetPage) {
|
||||
mHolder = holder;
|
||||
mActivity = activity;
|
||||
mGi = gi;
|
||||
mTargetPage = targetPage;
|
||||
}
|
||||
|
||||
public void setTargetPage(int targetPage) {
|
||||
mTargetPage = targetPage;
|
||||
}
|
||||
|
||||
protected class TextViewWithUrl extends TextView {
|
||||
public String url;
|
||||
|
||||
public class Item {
|
||||
public int xOffset;
|
||||
public int yOffset;
|
||||
public int width;
|
||||
public int height;
|
||||
public String url;
|
||||
|
||||
public Item(int xOffset, int yOffset, int width, int height, String url) {
|
||||
this.xOffset = xOffset;
|
||||
this.yOffset = yOffset;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
public String imageUrl;
|
||||
public ArrayList<Item> itemArray = new ArrayList<Item>();
|
||||
public int startIndex;
|
||||
public Row(String imageUrl) {
|
||||
this.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
public void addItem(int xOffset, int yOffset, int width, int height,
|
||||
String url) {
|
||||
itemArray.add(new Item(xOffset, yOffset, width, height, url));
|
||||
public TextViewWithUrl(Context context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
|
||||
private Row curRow;
|
||||
public ArrayList<Row> rowArray = new ArrayList<Row>();
|
||||
|
||||
public void addItem(String imageUrl, String xOffset, String yOffset, String width, String height,
|
||||
String url) {
|
||||
if (curRow == null) {
|
||||
curRow = new Row(imageUrl);
|
||||
curRow.startIndex = 0;
|
||||
rowArray.add(curRow);
|
||||
} else if (!curRow.imageUrl.equals(imageUrl)) {
|
||||
Row lastRow = curRow;
|
||||
curRow = new Row(imageUrl);
|
||||
curRow.startIndex = lastRow.startIndex + lastRow.itemArray.size();
|
||||
rowArray.add(curRow);
|
||||
}
|
||||
|
||||
curRow.addItem(Integer.parseInt(xOffset),
|
||||
Integer.parseInt(yOffset), Integer.parseInt(width),
|
||||
Integer.parseInt(height), url);
|
||||
}
|
||||
|
||||
public int getSum() {
|
||||
int sum = 0;
|
||||
for (Row row : rowArray)
|
||||
sum += row.itemArray.size();
|
||||
return sum;
|
||||
public interface PreviewHolder {
|
||||
public int getCurPreviewPage();
|
||||
public void onGetPreviewImageFailure();
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import com.hippo.ehviewer.ListUrls;
|
||||
import com.hippo.ehviewer.data.Comment;
|
||||
import com.hippo.ehviewer.data.PreviewList;
|
||||
import com.hippo.ehviewer.data.NormalPreviewList;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
public class DetailParser {
|
||||
@@ -66,23 +66,23 @@ public class DetailParser {
|
||||
public int previewPerPage;
|
||||
public int previewSum;
|
||||
public LinkedHashMap<String, LinkedList<SimpleEntry<String, Integer>>> tags;
|
||||
public PreviewList previewList;
|
||||
public NormalPreviewList previewList;
|
||||
public LinkedList<Comment> comments;
|
||||
|
||||
public void setMode(int mode) {
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
public int parser(String pageContent) {
|
||||
public int parser(String body) {
|
||||
int re = 0;
|
||||
Pattern p;
|
||||
Matcher m;
|
||||
|
||||
if (pageContent.contains(OFFENSIVE_STRING)) {
|
||||
if (body.contains(OFFENSIVE_STRING)) {
|
||||
return OFFENSIVE;
|
||||
}
|
||||
|
||||
if (pageContent.contains(PINING_STRING)) {
|
||||
if (body.contains(PINING_STRING)) {
|
||||
return PINING;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class DetailParser {
|
||||
+ "<tr><td[^<>]*>([^<>]+)</td>" // rating
|
||||
+ ".+"
|
||||
+ "<div id=\"gdt\"><div[^<>]*>(?:<div[^<>]*>)?<a[^<>]*href=\"([^<>\"]+)\"[^<>]*>"); // get firstPage
|
||||
m = p.matcher(pageContent);
|
||||
m = p.matcher(body);
|
||||
if (m.find()) {
|
||||
re |= DETAIL;
|
||||
|
||||
@@ -144,7 +144,7 @@ public class DetailParser {
|
||||
tags = new LinkedHashMap<String, LinkedList<SimpleEntry<String, Integer>>>();
|
||||
p = Pattern
|
||||
.compile("<tr><td[^<>]*>([^<>]+):</td><td>(?:<div[^<>]*><a[^<>]*>[^<>]*</a>[^<>]*<span[^<>]*>\\d+</span>[^<>]*</div>)+</td></tr>");
|
||||
m = p.matcher(pageContent);
|
||||
m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
re |= TAG;
|
||||
String groupName = m.group(1);
|
||||
@@ -158,7 +158,7 @@ public class DetailParser {
|
||||
// Get preview info
|
||||
if ((mMode & PREVIEW_INFO) != 0) {
|
||||
p = Pattern.compile("<p class=\"ip\">Showing ([\\d|,]+) - ([\\d|,]+) of ([\\d|,]+) images</p>");
|
||||
m = p.matcher(pageContent);
|
||||
m = p.matcher(body);
|
||||
if (m.find()) {
|
||||
re |= PREVIEW_INFO;
|
||||
previewPerPage = Integer.parseInt(m.group(2).replace(",",
|
||||
@@ -173,11 +173,11 @@ public class DetailParser {
|
||||
if ((mMode & PREVIEW) != 0) {
|
||||
p = Pattern
|
||||
.compile("<div[^<>]*class=\"gdtm\"[^<>]*><div[^<>]*width:(\\d+)[^<>]*height:(\\d+)[^<>]*\\((.+?)\\)[^<>]*-(\\d+)px[^<>]*><a[^<>]*href=\"(.+?)\"[^<>]*>");
|
||||
m = p.matcher(pageContent);
|
||||
m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
if (previewList == null) {
|
||||
re |= PREVIEW;
|
||||
previewList = new PreviewList();
|
||||
previewList = new NormalPreviewList();
|
||||
}
|
||||
previewList.addItem(m.group(3), m.group(4), "0", m.group(1),
|
||||
m.group(2), m.group(5));
|
||||
@@ -187,7 +187,7 @@ public class DetailParser {
|
||||
if ((mMode & COMMENT) != 0) {
|
||||
p = Pattern
|
||||
.compile("<div class=\"c3\">Posted on ([^<>]+) by <a[^<>]+>([^<>]+)</a>.*?<div class=\"c6\">(.*?)</div>");
|
||||
m = p.matcher(pageContent);
|
||||
m = p.matcher(body);
|
||||
comments = new LinkedList<Comment>();
|
||||
while (m.find()) {
|
||||
re |= COMMENT;
|
||||
|
||||
@@ -16,37 +16,27 @@
|
||||
|
||||
package com.hippo.ehviewer.ehclient;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.http.conn.ConnectTimeoutException;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.hippo.ehviewer.Analytics;
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.DiskCache;
|
||||
import com.hippo.ehviewer.ListUrls;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.data.Comment;
|
||||
import com.hippo.ehviewer.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.data.GalleryPopular;
|
||||
import com.hippo.ehviewer.data.PreviewList;
|
||||
import com.hippo.ehviewer.data.NormalPreviewList;
|
||||
import com.hippo.ehviewer.gallery.data.ImageSet;
|
||||
import com.hippo.ehviewer.network.Downloader;
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
@@ -56,11 +46,8 @@ import com.hippo.ehviewer.util.ThreadPool.Job;
|
||||
import com.hippo.ehviewer.util.ThreadPool.JobContext;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Movie;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.v4.util.LruCache;
|
||||
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Download;
|
||||
@@ -68,7 +55,6 @@ import com.hippo.ehviewer.util.Future;
|
||||
import com.hippo.ehviewer.util.FutureListener;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.ThreadPool;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
|
||||
public class EhClient {
|
||||
@@ -188,11 +174,6 @@ public class EhClient {
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnGetPageListListener {
|
||||
public void onSuccess(Object checkFlag, PreviewList pageList);
|
||||
public void onFailure(Object checkFlag, String eMsg);
|
||||
}
|
||||
|
||||
public interface OnGetMangaUrlListener {
|
||||
public void onSuccess(Object checkFlag, String[] arg);
|
||||
public void onFailure(Object checkFlag, String eMsg);
|
||||
@@ -264,15 +245,6 @@ public class EhClient {
|
||||
|
||||
switch (msg.what) {
|
||||
|
||||
case GET_PAGE_LIST:
|
||||
GetPageListPackage getPageListPackage = (GetPageListPackage) msg.obj;
|
||||
OnGetPageListListener listener6 = getPageListPackage.listener;
|
||||
if (getPageListPackage.pageList != null)
|
||||
listener6.onSuccess(getPageListPackage.checkFlag, getPageListPackage.pageList);
|
||||
else
|
||||
listener6.onFailure(getPageListPackage.checkFlag, getPageListPackage.eMsg);
|
||||
break;
|
||||
|
||||
case GET_MANGA_URL:
|
||||
GetMangaUrlPackage getMangaUrlPackage = (GetMangaUrlPackage) msg.obj;
|
||||
OnGetMangaUrlListener listener7 = getMangaUrlPackage.listener;
|
||||
@@ -360,7 +332,7 @@ public class EhClient {
|
||||
}
|
||||
String avatarUrl = m.group(1);
|
||||
// Get avatar
|
||||
Bitmap avatar = hp.getImage(avatarUrl, null, null, null, true);
|
||||
Bitmap avatar = hp.getImage(avatarUrl);
|
||||
// Set avatar if not null
|
||||
if (avatar != null)
|
||||
mInfo.setAvatar(avatar);
|
||||
@@ -409,7 +381,7 @@ public class EhClient {
|
||||
}).start();
|
||||
}
|
||||
|
||||
// Get Manga Detail
|
||||
// Get gallery Detail
|
||||
public interface OnGetGDetailListener {
|
||||
public void onSuccess(GalleryDetail md);
|
||||
public void onFailure(String eMsg);
|
||||
@@ -456,7 +428,7 @@ public class EhClient {
|
||||
md.previewSum = parser.previewSum;
|
||||
|
||||
md.tags = parser.tags;
|
||||
md.previewLists = new PreviewList[md.previewSum];
|
||||
md.previewLists = new NormalPreviewList[md.previewSum];
|
||||
md.previewLists[0] = parser.previewList;
|
||||
md.comments = parser.comments;
|
||||
listener.onSuccess(md);
|
||||
@@ -474,83 +446,39 @@ public class EhClient {
|
||||
}).start();
|
||||
}
|
||||
|
||||
// Get page list
|
||||
private class GetPageListPackage {
|
||||
public Object checkFlag;
|
||||
public PreviewList pageList;
|
||||
public OnGetPageListListener listener;
|
||||
public String eMsg;
|
||||
|
||||
public GetPageListPackage(Object checkFlag, PreviewList pageList, OnGetPageListListener listener,
|
||||
String eMsg) {
|
||||
this.checkFlag = checkFlag;
|
||||
this.pageList = pageList;
|
||||
this.listener = listener;
|
||||
this.eMsg = eMsg;
|
||||
}
|
||||
// Get preview list
|
||||
public interface OnGetPreviewListListener {
|
||||
public void onSuccess(Object checkFlag, NormalPreviewList pageList);
|
||||
public void onFailure(Object checkFlag, String eMsg);
|
||||
}
|
||||
|
||||
private class GetPageListRunnable implements Runnable {
|
||||
private String url;
|
||||
|
||||
public PreviewList pageList;
|
||||
public String eMsg;
|
||||
|
||||
public GetPageListRunnable(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
pageList = null;
|
||||
|
||||
HttpHelper hp = new HttpHelper(mAppContext);
|
||||
String pageContent = hp.get(url);
|
||||
if (pageContent != null) {
|
||||
|
||||
pageList = getPageList(pageContent);
|
||||
if (pageList == null)
|
||||
eMsg = mAppContext.getString(R.string.em_parser_error);
|
||||
} else
|
||||
eMsg = hp.getEMsg();
|
||||
}
|
||||
}
|
||||
|
||||
public void getPageList(String url, final Object checkFlag,
|
||||
final OnGetPageListListener listener) {
|
||||
|
||||
final GetPageListRunnable task = new GetPageListRunnable(url);
|
||||
mThreadPool.submit(new Job<Object>() {
|
||||
public void getPreviewList(final String url, final Object checkFlag,
|
||||
final OnGetPreviewListListener listener) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public Object run(JobContext jc) {
|
||||
task.run();
|
||||
return null;
|
||||
public void run() {
|
||||
HttpHelper hp = new HttpHelper(mAppContext);
|
||||
hp.setOnRespondListener(new HttpHelper.OnRespondListener() {
|
||||
@Override
|
||||
public void onSuccess(String body) {
|
||||
DetailParser parser = new DetailParser();
|
||||
parser.setMode(DetailParser.PREVIEW);
|
||||
if (parser.parser(body) == DetailParser.PREVIEW) {
|
||||
listener.onSuccess(checkFlag, parser.previewList);
|
||||
} else {
|
||||
listener.onFailure(checkFlag, "Parser error");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onFailure(String eMsg) {
|
||||
listener.onFailure(checkFlag, eMsg);
|
||||
}
|
||||
});
|
||||
hp.get(url);
|
||||
}
|
||||
}, new FutureListener<Object>() {
|
||||
@Override
|
||||
public void onFutureDone(Future<Object> future) {
|
||||
Message msg = new Message();
|
||||
msg.what = GET_PAGE_LIST;
|
||||
msg.obj = new GetPageListPackage(checkFlag, task.pageList, listener, task.eMsg);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
private PreviewList getPageList(String pageContent) {
|
||||
PreviewList pageList = null;
|
||||
Pattern p = Pattern
|
||||
.compile("<div[^<>]*class=\"gdtm\"[^<>]*><div[^<>]*width:(\\d+)[^<>]*height:(\\d+)[^<>]*\\((.+?)\\)[^<>]*-(\\d+)px[^<>]*><a[^<>]*href=\"(.+?)\"[^<>]*>");
|
||||
Matcher m = p.matcher(pageContent);
|
||||
while (m.find()) {
|
||||
if (pageList == null)
|
||||
pageList = new PreviewList();
|
||||
pageList.addItem(m.group(3), m.group(4), "0", m.group(1),
|
||||
m.group(2), m.group(5));
|
||||
}
|
||||
return pageList;
|
||||
}
|
||||
|
||||
|
||||
// Get Manga url and next page
|
||||
private class GetMangaUrlPackage {
|
||||
public Object checkFlag;
|
||||
@@ -624,162 +552,6 @@ public class EhClient {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Get image
|
||||
private static class GetImagePackage {
|
||||
public Object res;
|
||||
public Object checkFlag;
|
||||
public OnGetImageListener listener;
|
||||
|
||||
public GetImagePackage(Object res, Object checkFlag,
|
||||
OnGetImageListener listener) {
|
||||
this.res = res;
|
||||
this.checkFlag = checkFlag;
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
private static Handler getGetImageHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
OnGetImageListener listener = ((GetImagePackage) msg.obj).listener;
|
||||
Object checkFlag = ((GetImagePackage) msg.obj).checkFlag;
|
||||
Object res = ((GetImagePackage) msg.obj).res;
|
||||
if (res == null) {
|
||||
listener.onFailure(msg.what);
|
||||
}
|
||||
else
|
||||
listener.onSuccess(checkFlag, res);
|
||||
};
|
||||
};
|
||||
|
||||
private static class GetImageRunnable implements Runnable {
|
||||
private String url;
|
||||
private String key;
|
||||
private int type;
|
||||
private LruCache<String, Bitmap> memoryCache;
|
||||
private DiskCache diskCache;
|
||||
private Object checkFlag;
|
||||
private OnGetImageListener listener;
|
||||
|
||||
public GetImageRunnable(String url, String key, int type,
|
||||
LruCache<String, Bitmap> memoryCache, DiskCache diskCache,
|
||||
Object checkFlag, OnGetImageListener listener) {
|
||||
this.url = url;
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
this.memoryCache = memoryCache;
|
||||
this.diskCache = diskCache;
|
||||
this.checkFlag = checkFlag;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int errorMessageId = R.string.em_unknown_error;
|
||||
Object res = null;
|
||||
synchronized (url) {
|
||||
// Check memory cache
|
||||
if (type == Util.BITMAP && memoryCache != null
|
||||
&& (res = memoryCache.get(key)) != null) {
|
||||
sendMessage(res, errorMessageId);
|
||||
return;
|
||||
}
|
||||
// If not find in memory cache or do not have memory cache
|
||||
// Check disk cache
|
||||
if (diskCache != null
|
||||
&& (res = diskCache.get(key, type)) != null) {
|
||||
sendMessage(res, errorMessageId);
|
||||
if (memoryCache != null && res instanceof Bitmap)
|
||||
memoryCache.put(key, (Bitmap) res);
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "Download image " + url);
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
ByteArrayOutputStream baos = null;
|
||||
try {
|
||||
URL url = new URL(this.url);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(5 * 1000);
|
||||
conn.setReadTimeout(5 * 1000);
|
||||
conn.connect();
|
||||
InputStream is = conn.getInputStream();
|
||||
if (diskCache != null) {
|
||||
|
||||
int length = conn.getContentLength();
|
||||
if (length >= 0)
|
||||
baos = new ByteArrayOutputStream(length);
|
||||
else
|
||||
baos = new ByteArrayOutputStream();
|
||||
Util.copy(is, baos);
|
||||
byte[] bytes = baos.toByteArray();
|
||||
// To disk cache
|
||||
is = new ByteArrayInputStream(bytes);
|
||||
diskCache.put(key, is);
|
||||
// Read
|
||||
is = new ByteArrayInputStream(bytes);
|
||||
is.reset();
|
||||
if (type == Util.BITMAP) {
|
||||
res = BitmapFactory.decodeStream(is, null,
|
||||
Ui.getBitmapOpt());
|
||||
if (memoryCache != null && res != null)
|
||||
memoryCache.put(key, (Bitmap) res);
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
res = Movie.decodeStream(is);
|
||||
}
|
||||
is.close();
|
||||
} else {
|
||||
if (type == Util.BITMAP) {
|
||||
res = BitmapFactory.decodeStream(is, null,
|
||||
Ui.getBitmapOpt());
|
||||
if (memoryCache != null && res != null)
|
||||
memoryCache.put(key, (Bitmap) res);
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
res = Movie.decodeStream(is);
|
||||
}
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
errorMessageId = R.string.em_url_error;
|
||||
e.printStackTrace();
|
||||
} catch (ConnectTimeoutException e) {
|
||||
errorMessageId = R.string.em_timeout;
|
||||
e.printStackTrace();
|
||||
} catch (UnknownHostException e) {
|
||||
errorMessageId = R.string.em_no_network_2;
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
errorMessageId = R.string.em_network_error;
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (conn != null)
|
||||
conn.disconnect();
|
||||
if (baos != null)
|
||||
Util.closeStreamQuietly(baos);
|
||||
}
|
||||
sendMessage(res, errorMessageId);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(Object res, int errorMessageId) {
|
||||
Message msg = new Message();
|
||||
msg.what = errorMessageId;
|
||||
msg.obj = new GetImagePackage(res, checkFlag, listener);
|
||||
getGetImageHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void getImage(String url, String key, int type,
|
||||
LruCache<String, Bitmap> memoryCache, DiskCache diskCache,
|
||||
Object checkFlag, OnGetImageListener listener) {
|
||||
new Thread(new GetImageRunnable(url, key, type, memoryCache, diskCache,
|
||||
checkFlag, listener)).start();
|
||||
}
|
||||
|
||||
|
||||
public interface OnDownloadMangaListener {
|
||||
public void onDownloadMangaStart(String id);
|
||||
|
||||
@@ -171,9 +171,11 @@ public class EhInfo {
|
||||
public void storeCookie(HttpURLConnection conn) {
|
||||
String ipb_member_id;
|
||||
String ipb_pass_hash;
|
||||
SharedPreferences.Editor editor = mInfoPref.edit();
|
||||
|
||||
List<String> cookieList = conn.getHeaderFields().get("Set-Cookie");
|
||||
if (cookieList == null)
|
||||
return;
|
||||
SharedPreferences.Editor editor = mInfoPref.edit();
|
||||
for (String str : cookieList) {
|
||||
ipb_member_id = getCookie(str, "ipb_member_id");
|
||||
ipb_pass_hash = getCookie(str, "ipb_pass_hash");
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
package com.hippo.ehviewer.exception;
|
||||
|
||||
public class ParserException extends Exception{
|
||||
|
||||
@@ -17,14 +17,10 @@
|
||||
package com.hippo.ehviewer.network;
|
||||
|
||||
public class Constant {
|
||||
|
||||
public static final int HTTP_TEMP_REDIRECT = 307;
|
||||
|
||||
public static String defaultUserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36";
|
||||
public static String userAgent = System.getProperty("http.agent", defaultUserAgent);
|
||||
public static final int DEFAULT_TIMEOUT = 5 * 1000;
|
||||
public static final int DEFAULT_TIMEOUT = 10 * 1000;
|
||||
public static final int MAX_REDIRECTS = 5;
|
||||
|
||||
public static final int BUFFER_SIZE = 4096;
|
||||
|
||||
}
|
||||
@@ -31,15 +31,14 @@ import java.net.UnknownHostException;
|
||||
import org.apache.http.conn.ConnectTimeoutException;
|
||||
|
||||
import android.content.Context;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.exception.FileException;
|
||||
import com.hippo.ehviewer.exception.NoSdCardException;
|
||||
import com.hippo.ehviewer.exception.RedirectionException;
|
||||
import com.hippo.ehviewer.exception.ResponseCodeException;
|
||||
import com.hippo.ehviewer.exception.StopRequestException;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
|
||||
// TODO 添加检查次磁盘空间
|
||||
@@ -170,7 +169,7 @@ public class Downloader implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
int retryTimes = 0;
|
||||
if (Cache.hasSdCard()) {
|
||||
if (true) {
|
||||
|
||||
boolean checkFolder = false;
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ import java.util.zip.GZIPInputStream;
|
||||
import org.apache.http.conn.ConnectTimeoutException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.hippo.ehviewer.DiskCache;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.ehclient.EhInfo;
|
||||
@@ -55,7 +54,6 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.support.v4.util.LruCache;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
@@ -441,68 +439,29 @@ public class HttpHelper {
|
||||
return requst(new PostJsonHelper(json), url);
|
||||
}
|
||||
|
||||
public Bitmap getImage(String urlStr, String key,
|
||||
LruCache<String, Bitmap> memoryCache,
|
||||
DiskCache diskCache, boolean getImage) {
|
||||
public Bitmap getImage(String urlStr) {
|
||||
mException = null;
|
||||
|
||||
int redirectionCount = 0;
|
||||
URL url = null;
|
||||
HttpURLConnection conn = null;
|
||||
ByteArrayOutputStream baos = null;
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
|
||||
Log.d(TAG, "Http get bitmap " + urlStr);
|
||||
|
||||
Log.d(TAG, "Http get image " + urlStr);
|
||||
url = new URL(urlStr);
|
||||
while (redirectionCount++ < Constant.MAX_REDIRECTS) {
|
||||
conn = (HttpURLConnection)url.openConnection();
|
||||
conn.setRequestProperty("User-Agent", Constant.userAgent);
|
||||
conn.setConnectTimeout(Constant.DEFAULT_TIMEOUT);
|
||||
conn.setReadTimeout(Constant.DEFAULT_TIMEOUT);
|
||||
conn.setRequestProperty("Connection", "close");
|
||||
conn.setRequestMethod("GET");
|
||||
conn.connect();
|
||||
|
||||
final int responseCode = conn.getResponseCode();
|
||||
switch (responseCode) {
|
||||
case HttpURLConnection.HTTP_OK:
|
||||
case HttpURLConnection.HTTP_PARTIAL:
|
||||
|
||||
Bitmap bitmap = null;
|
||||
is = conn.getInputStream();
|
||||
if (diskCache == null) {
|
||||
if (getImage
|
||||
&& (bitmap = BitmapFactory.decodeStream(is, null, Ui.getBitmapOpt())) != null) {
|
||||
if (memoryCache != null)
|
||||
memoryCache.put(key, bitmap);
|
||||
}
|
||||
} else {
|
||||
boolean twice = memoryCache != null || getImage;
|
||||
if (twice) {
|
||||
baos = new ByteArrayOutputStream();
|
||||
Util.copy(is, baos);
|
||||
byte[] bytes = baos.toByteArray();
|
||||
// To disk cache
|
||||
is = new ByteArrayInputStream(bytes);
|
||||
diskCache.put(key, is);
|
||||
is.close();
|
||||
//
|
||||
is = new ByteArrayInputStream(bytes);
|
||||
Bitmap bmp = BitmapFactory.decodeStream(is, null, Ui.getBitmapOpt());
|
||||
if (bmp != null) {
|
||||
if (memoryCache != null)
|
||||
memoryCache.put(key, bmp);
|
||||
if (getImage)
|
||||
bitmap = bmp;
|
||||
}
|
||||
is.close();
|
||||
baos.close();
|
||||
} else {
|
||||
diskCache.put(key, is);
|
||||
}
|
||||
}
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(conn.getInputStream(), null, Ui.getBitmapOpt());
|
||||
return bitmap;
|
||||
|
||||
case HttpURLConnection.HTTP_MOVED_PERM:
|
||||
@@ -522,10 +481,6 @@ public class HttpHelper {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (baos != null)
|
||||
Util.closeStreamQuietly(baos);
|
||||
if (is != null)
|
||||
Util.closeStreamQuietly(is);
|
||||
if (conn != null)
|
||||
conn.disconnect();
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.network;
|
||||
|
||||
import java.net.CookieStore;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class PrefCookieHelper implements CookieStore {
|
||||
private static final String TAG = "PrefCookieHelper";
|
||||
private final String PREF_NAME = "cookies";
|
||||
|
||||
private final SharedPreferences cookiePref;
|
||||
private Map<String, String> cookies;
|
||||
|
||||
public PrefCookieHelper(Context context) {
|
||||
cookiePref = context.getSharedPreferences(PREF_NAME, 0);
|
||||
|
||||
cookies = new HashMap<String, String>();
|
||||
Map<String, ?> all = cookiePref.getAll();
|
||||
for (Entry<String, ?> item : all.entrySet()) {
|
||||
Object value = item.getValue();
|
||||
if (value instanceof String)
|
||||
cookies.put(item.getKey(), (String)value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void add(URI uri, HttpCookie cookie) {
|
||||
uri.getHost();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<HttpCookie> get(URI uri) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<HttpCookie> getCookies() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<URI> getURIs() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean remove(URI uri, HttpCookie cookie) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean removeAll() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.network;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OptionalDataException;
|
||||
import java.net.CookieStore;
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
public class ShapreCookieStore implements CookieStore {
|
||||
//private static final String TAG = "ShapreCookieStore";
|
||||
private final CookieDataSource datasource;
|
||||
private final Map<URI, List<HttpCookie>> map;
|
||||
|
||||
public ShapreCookieStore(Context context) {
|
||||
datasource = new CookieDataSource(context);
|
||||
map = datasource.getAll();
|
||||
}
|
||||
|
||||
public synchronized void add(URI uri, HttpCookie cookie) {
|
||||
if (cookie == null) {
|
||||
throw new NullPointerException("cookie == null");
|
||||
}
|
||||
|
||||
uri = cookiesUri(uri);
|
||||
List<HttpCookie> cookies = map.get(uri);
|
||||
if (cookies == null) {
|
||||
cookies = new ArrayList<HttpCookie>();
|
||||
map.put(uri, cookies);
|
||||
} else {
|
||||
cookies.remove(cookie);
|
||||
}
|
||||
cookies.add(cookie);
|
||||
|
||||
// For sql
|
||||
if (cookies != null) {
|
||||
datasource.add(uri.getHost(), cookie);
|
||||
}
|
||||
}
|
||||
|
||||
private URI cookiesUri(URI uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new URI("http", uri.getHost(), null, null);
|
||||
} catch (URISyntaxException e) {
|
||||
return uri; // probably a URI with no host
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized List<HttpCookie> get(URI uri) {/*
|
||||
if (uri == null) {
|
||||
throw new NullPointerException("uri == null");
|
||||
}
|
||||
|
||||
List<HttpCookie> result = new ArrayList<HttpCookie>();
|
||||
|
||||
// get cookies associated with given URI. If none, returns an empty list
|
||||
List<HttpCookie> cookiesForUri = map.get(uri);
|
||||
if (cookiesForUri != null) {
|
||||
for (Iterator<HttpCookie> i = cookiesForUri.iterator(); i.hasNext(); ) {
|
||||
HttpCookie cookie = i.next();
|
||||
if (cookie.hasExpired()) {
|
||||
i.remove(); // remove expired cookies
|
||||
// For sql
|
||||
datasource.remove(uri.getHost(), cookie);
|
||||
} else {
|
||||
result.add(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get all cookies that domain matches the URI
|
||||
for (Map.Entry<URI, List<HttpCookie>> entry : map.entrySet()) {
|
||||
if (uri.equals(entry.getKey())) {
|
||||
continue; // skip the given URI; we've already handled it
|
||||
}
|
||||
|
||||
List<HttpCookie> entryCookies = entry.getValue();
|
||||
for (Iterator<HttpCookie> i = entryCookies.iterator(); i.hasNext(); ) {
|
||||
HttpCookie cookie = i.next();
|
||||
if (!HttpCookie.domainMatches(cookie.getDomain(), uri.getHost())) {
|
||||
continue;
|
||||
}
|
||||
if (cookie.hasExpired()) {
|
||||
i.remove(); // remove expired cookies
|
||||
// For sql
|
||||
datasource.remove(uri.getHost(), cookie);
|
||||
} else if (!result.contains(cookie)) {
|
||||
result.add(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);*/
|
||||
|
||||
|
||||
List<HttpCookie> result = new ArrayList<HttpCookie>();
|
||||
for (URI uri1 : map.keySet()) {
|
||||
List<HttpCookie> list = map.get(uri1);
|
||||
for (Iterator<HttpCookie> i = list.iterator(); i.hasNext(); ) {
|
||||
HttpCookie cookie = i.next();
|
||||
if (cookie.hasExpired()) {
|
||||
i.remove(); // remove expired cookies
|
||||
// For sql
|
||||
datasource.remove(uri1.getHost(), cookie);
|
||||
} else if (!result.contains(cookie)) {
|
||||
result.add(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
public synchronized List<HttpCookie> getCookies() {
|
||||
List<HttpCookie> result = new ArrayList<HttpCookie>();
|
||||
for (URI uri : map.keySet()) {
|
||||
List<HttpCookie> list = map.get(uri);
|
||||
for (Iterator<HttpCookie> i = list.iterator(); i.hasNext(); ) {
|
||||
HttpCookie cookie = i.next();
|
||||
if (cookie.hasExpired()) {
|
||||
i.remove(); // remove expired cookies
|
||||
// For sql
|
||||
datasource.remove(uri.getHost(), cookie);
|
||||
} else if (!result.contains(cookie)) {
|
||||
result.add(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
public synchronized List<URI> getURIs() {
|
||||
List<URI> result = new ArrayList<URI>(map.keySet());
|
||||
result.remove(null); // sigh
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
public synchronized boolean remove(URI uri, HttpCookie cookie) {
|
||||
if (cookie == null) {
|
||||
throw new NullPointerException("cookie == null");
|
||||
}
|
||||
|
||||
// For sql
|
||||
datasource.remove(uri.getHost(), cookie);
|
||||
|
||||
List<HttpCookie> cookies = map.get(cookiesUri(uri));
|
||||
if (cookies != null) {
|
||||
return cookies.remove(cookie);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean removeAll() {
|
||||
boolean result = !map.isEmpty();
|
||||
map.clear();
|
||||
|
||||
// For sql
|
||||
datasource.removeAll();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class CookieDataSource {
|
||||
|
||||
private static final String COOKIE_PREFS = "cookie";
|
||||
private static final String COOKIE_URI_LIST = "cookie_uri_list";
|
||||
private static final String COOKIE_URI_PREFIX = "uri_";
|
||||
private static final String COOKIE_NAME_PREFIX = "name_";
|
||||
private final SharedPreferences cookiePrefs;
|
||||
private final Map<String, Set<String>> data;
|
||||
|
||||
public CookieDataSource(Context context) {
|
||||
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
|
||||
data = new HashMap<String, Set<String>>();
|
||||
}
|
||||
|
||||
public Map<URI, List<HttpCookie>> getAll() {
|
||||
Map<URI, List<HttpCookie>> map = new HashMap<URI, List<HttpCookie>>();
|
||||
String rawUris = cookiePrefs.getString(COOKIE_URI_LIST, null);
|
||||
if (rawUris == null)
|
||||
return map;
|
||||
String[] strUris = decodeStrings(rawUris);
|
||||
|
||||
for (String strUri : strUris) {
|
||||
URI uri = null;
|
||||
try {
|
||||
uri = new URI("http", strUri, null, null);
|
||||
} catch (URISyntaxException e) {continue;}
|
||||
|
||||
// Add to map
|
||||
data.put(strUri, new HashSet<String>());
|
||||
String rawNames = cookiePrefs.getString(COOKIE_URI_PREFIX + strUri, null);
|
||||
if (rawNames == null)
|
||||
continue;
|
||||
String[] names = decodeStrings(rawNames);
|
||||
List<HttpCookie> cookies = new ArrayList<HttpCookie>();
|
||||
for (String name : names) {
|
||||
String strCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + strUri + name, null);
|
||||
if (strCookie == null)
|
||||
continue;
|
||||
|
||||
// Add to map
|
||||
data.get(strUri).add(name);
|
||||
cookies.add(decodeCookie(strCookie));
|
||||
}
|
||||
map.put(uri, cookies);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public void add(String uri, HttpCookie cookie) {
|
||||
Set<String> names = data.get(uri);
|
||||
if (names == null) {
|
||||
names = new HashSet<String>();
|
||||
data.put(uri, names);
|
||||
putStrings(COOKIE_URI_LIST, data.keySet());
|
||||
}
|
||||
|
||||
if (!names.contains(cookie.getName())) {
|
||||
names.add(cookie.getName());
|
||||
putStrings(COOKIE_URI_PREFIX + uri, names);
|
||||
}
|
||||
|
||||
cookiePrefs.edit().putString(COOKIE_NAME_PREFIX
|
||||
+ uri + cookie.getName(),
|
||||
encodeCookie(cookie))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void remove(String uri, HttpCookie cookie) {
|
||||
cookiePrefs.edit().remove(COOKIE_NAME_PREFIX
|
||||
+ uri + cookie.getName())
|
||||
.apply();
|
||||
|
||||
Set<String> names = data.get(uri);
|
||||
names.remove(cookie.getName());
|
||||
if (names.size() != 0)
|
||||
putStrings(COOKIE_URI_PREFIX + uri, names);
|
||||
else {
|
||||
cookiePrefs.edit().remove(COOKIE_URI_PREFIX + uri)
|
||||
.apply();
|
||||
data.remove(uri);
|
||||
putStrings(COOKIE_URI_LIST, data.keySet());
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAll() {
|
||||
cookiePrefs.edit().clear().apply();
|
||||
}
|
||||
|
||||
public void putStrings(String key, Set<String> values) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (String value : values) {
|
||||
sb.append(value).append('\n');
|
||||
}
|
||||
int length = sb.length();
|
||||
sb.delete(length - 1, length);
|
||||
cookiePrefs.edit()
|
||||
.putString(key, Util.byteArrayToHexString(
|
||||
sb.toString().getBytes())).apply();
|
||||
}
|
||||
|
||||
public String[] decodeStrings(String rawStrings) {
|
||||
return new String(Util.hexStringToByteArray(rawStrings)).split("\n");
|
||||
}
|
||||
|
||||
protected String encodeCookie(HttpCookie cookie) {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
ObjectOutputStream outputStream = new ObjectOutputStream(os);
|
||||
writeObject(cookie, outputStream);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return Util.byteArrayToHexString(os.toByteArray());
|
||||
}
|
||||
|
||||
protected HttpCookie decodeCookie(String cookieStr) {
|
||||
byte[] bytes = Util.hexStringToByteArray(cookieStr);
|
||||
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
|
||||
HttpCookie cookie = null;
|
||||
try {
|
||||
ObjectInputStream ois = new ObjectInputStream(is);
|
||||
cookie = readObject(ois);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void writeObject(HttpCookie cookie, ObjectOutputStream out) throws IOException {
|
||||
out.writeObject(cookie.getName());
|
||||
out.writeObject(cookie.getValue());
|
||||
out.writeObject(cookie.getComment());
|
||||
out.writeObject(cookie.getCommentURL());
|
||||
out.writeObject((Boolean)cookie.getDiscard());
|
||||
out.writeObject(cookie.getDomain());
|
||||
out.writeObject((Long)cookie.getMaxAge());
|
||||
out.writeObject(cookie.getPath());
|
||||
out.writeObject(cookie.getPortlist());
|
||||
out.writeObject((Boolean)cookie.getSecure());
|
||||
out.writeObject((Integer)cookie.getVersion());
|
||||
}
|
||||
|
||||
public static HttpCookie readObject(ObjectInputStream in) {
|
||||
HttpCookie clientCookie = null;
|
||||
try {
|
||||
String name = (String)in.readObject();
|
||||
String value = (String)in.readObject();
|
||||
clientCookie = new HttpCookie(name, value);
|
||||
clientCookie.setComment((String)in.readObject());
|
||||
clientCookie.setCommentURL((String)in.readObject());
|
||||
clientCookie.setDiscard((Boolean)in.readObject());
|
||||
clientCookie.setDomain((String)in.readObject());
|
||||
clientCookie.setMaxAge((Long)in.readObject());
|
||||
clientCookie.setPath((String)in.readObject());
|
||||
clientCookie.setPortlist((String)in.readObject());
|
||||
clientCookie.setSecure((Boolean)in.readObject());
|
||||
clientCookie.setVersion((Integer)in.readObject());
|
||||
} catch (OptionalDataException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return clientCookie;
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import com.google.analytics.tracking.android.EasyTracker;
|
||||
import com.hippo.ehviewer.cache.ImageCache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
|
||||
public abstract class AbstractFragmentActivity extends FragmentActivity {
|
||||
@@ -53,5 +54,7 @@ public abstract class AbstractFragmentActivity extends FragmentActivity {
|
||||
super.onStop();
|
||||
if (Config.getAllowAnalyics())
|
||||
EasyTracker.getInstance(this).activityStop(this);
|
||||
|
||||
ImageCache.getInstance(this).flush();
|
||||
}
|
||||
}
|
||||
@@ -20,14 +20,12 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.ImageGeterManager;
|
||||
import com.hippo.ehviewer.ImageLoader;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.cache.ImageCache;
|
||||
import com.hippo.ehviewer.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.data.GalleryPopular;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.widget.FswView;
|
||||
import com.hippo.ehviewer.widget.HfListView;
|
||||
@@ -35,7 +33,6 @@ import com.hippo.ehviewer.widget.LoadImageView;
|
||||
import com.hippo.ehviewer.widget.OnFitSystemWindowsListener;
|
||||
import com.hippo.ehviewer.widget.SuperToast;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
@@ -46,8 +43,6 @@ import android.widget.AbsListView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ImageView.ScaleType;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RatingBar;
|
||||
@@ -67,7 +62,7 @@ public abstract class AbstractGalleryActivity extends AbstractSlidingActivity
|
||||
|
||||
protected AppContext mAppContext;
|
||||
protected EhClient mClient;
|
||||
protected ImageGeterManager mImageGeterManager;
|
||||
private ImageLoader mImageLoader;
|
||||
|
||||
private List<GalleryInfo> mGiList;
|
||||
private GalleryAdapter mGalleryAdapter;
|
||||
@@ -273,7 +268,7 @@ public abstract class AbstractGalleryActivity extends AbstractSlidingActivity
|
||||
|
||||
mAppContext = (AppContext)getApplication();
|
||||
mClient = mAppContext.getEhClient();
|
||||
mImageGeterManager = mAppContext.getImageGeterManager();
|
||||
mImageLoader = ImageLoader.getInstance(this);
|
||||
|
||||
mGiList = new ArrayList<GalleryInfo>();
|
||||
|
||||
@@ -440,20 +435,10 @@ public abstract class AbstractGalleryActivity extends AbstractSlidingActivity
|
||||
final LoadImageView thumb = (LoadImageView)convertView.findViewById(R.id.cover);
|
||||
if (!String.valueOf(gi.gid).equals(thumb.getKey())) {
|
||||
|
||||
Bitmap bmp = null;
|
||||
if (Cache.memoryCache != null &&
|
||||
(bmp = Cache.memoryCache.get(String.valueOf(gi.gid))) != null) {
|
||||
thumb.setLoadInfo(gi.thumb, String.valueOf(gi.gid));
|
||||
thumb.setImageBitmap(bmp);
|
||||
thumb.setState(LoadImageView.LOADED);
|
||||
} else {
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(gi.thumb, String.valueOf(gi.gid));
|
||||
thumb.setState(LoadImageView.NONE);
|
||||
mImageGeterManager.add(gi.thumb, String.valueOf(gi.gid),
|
||||
ImageGeterManager.DISK_CACHE | ImageGeterManager.DOWNLOAD,
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
}
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(gi.thumb, String.valueOf(gi.gid));
|
||||
mImageLoader.add(gi.thumb, String.valueOf(gi.gid),
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
}
|
||||
// Set manga name
|
||||
TextView name = (TextView) convertView.findViewById(R.id.name);
|
||||
@@ -510,7 +495,16 @@ public abstract class AbstractGalleryActivity extends AbstractSlidingActivity
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {}
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
||||
switch (scrollState) {
|
||||
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
|
||||
ImageCache.getInstance(mAppContext).setPauseDiskCache(true);
|
||||
break;
|
||||
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
|
||||
ImageCache.getInstance(mAppContext).setPauseDiskCache(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setListPosition(int position) {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.hippo.ehviewer.ui;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.analytics.tracking.android.EasyTracker;
|
||||
import com.hippo.ehviewer.cache.ImageCache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.jeremyfeinstein.slidingmenu.lib.app.SlidingActivity;
|
||||
|
||||
@@ -53,5 +54,7 @@ public class AbstractSlidingActivity extends SlidingActivity {
|
||||
super.onStop();
|
||||
if (Config.getAllowAnalyics())
|
||||
EasyTracker.getInstance(this).activityStop(this);
|
||||
|
||||
ImageCache.getInstance(this).flush();
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.hippo.ehviewer.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
@@ -24,21 +23,19 @@ import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.ImageGeterManager;
|
||||
import com.hippo.ehviewer.ImageLoader;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.data.Comment;
|
||||
import com.hippo.ehviewer.data.Data;
|
||||
import com.hippo.ehviewer.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.data.NormalPreviewList;
|
||||
import com.hippo.ehviewer.data.PreviewList;
|
||||
import com.hippo.ehviewer.ehclient.DetailParser;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Theme;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
import com.hippo.ehviewer.widget.AlertButton;
|
||||
import com.hippo.ehviewer.widget.AutoWrapLayout;
|
||||
import com.hippo.ehviewer.widget.ButtonsDialogBuilder;
|
||||
@@ -53,15 +50,10 @@ import com.hippo.ehviewer.widget.SuperToast;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.StateListDrawable;
|
||||
import android.graphics.drawable.shapes.PathShape;
|
||||
@@ -86,7 +78,7 @@ import android.widget.TextView;
|
||||
import android.widget.ViewSwitcher.ViewFactory;
|
||||
|
||||
public class DetailSectionFragment extends Fragment
|
||||
implements View.OnClickListener {
|
||||
implements View.OnClickListener, PreviewList.PreviewHolder {
|
||||
private static final String TAG = "DetailSectionFragment";
|
||||
|
||||
private AppContext mAppContext;
|
||||
@@ -140,7 +132,6 @@ public class DetailSectionFragment extends Fragment
|
||||
}
|
||||
ShapeDrawable d = new ShapeDrawable(new PathShape(path, 50, 100));
|
||||
d.getPaint().setColor(color);
|
||||
//d.setBounds(0, 0, Ui.dp2pix(24), Ui.dp2pix(48));
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -154,10 +145,20 @@ public class DetailSectionFragment extends Fragment
|
||||
sld.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_focused}, dark);
|
||||
sld.addState(new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed}, dark);
|
||||
sld.addState(new int[]{}, normal);
|
||||
//sld.setBounds(0, 0, Ui.dp2pix(24), Ui.dp2pix(48));
|
||||
return sld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurPreviewPage() {
|
||||
return mCurPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGetPreviewImageFailure() {
|
||||
mWaitPreviewList.setVisibility(View.GONE);
|
||||
mPreviewRefreshButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@@ -168,12 +169,7 @@ public class DetailSectionFragment extends Fragment
|
||||
mData = mAppContext.getData();
|
||||
|
||||
GalleryInfo galleryInfo = mActivity.getGalleryInfo();
|
||||
boolean getFromCache = true;
|
||||
mActivity.mGalleryDetail = Cache.mdCache.get(String.valueOf(galleryInfo.gid));
|
||||
if (mActivity.mGalleryDetail == null) {
|
||||
getFromCache = false;
|
||||
mActivity.mGalleryDetail = new GalleryDetail(galleryInfo);
|
||||
}
|
||||
mActivity.mGalleryDetail = new GalleryDetail(galleryInfo);
|
||||
mGalleryDetail = mActivity.mGalleryDetail;
|
||||
mUrl = EhClient.getDetailUrl(
|
||||
mGalleryDetail.gid, mGalleryDetail.token);
|
||||
@@ -257,20 +253,10 @@ public class DetailSectionFragment extends Fragment
|
||||
});
|
||||
|
||||
LoadImageView thumb = (LoadImageView)mRootView.findViewById(R.id.detail_cover);
|
||||
Bitmap bmp = null;
|
||||
if (Cache.memoryCache != null &&
|
||||
(bmp = Cache.memoryCache.get(String.valueOf(mGalleryDetail.gid))) != null) {
|
||||
thumb.setLoadInfo(mGalleryDetail.thumb, String.valueOf(mGalleryDetail.gid));
|
||||
thumb.setImageBitmap(bmp);
|
||||
thumb.setState(LoadImageView.LOADED);
|
||||
} else {
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(mGalleryDetail.thumb, String.valueOf(mGalleryDetail.gid));
|
||||
thumb.setState(LoadImageView.NONE);
|
||||
mAppContext.getImageGeterManager().add(mGalleryDetail.thumb, String.valueOf(mGalleryDetail.gid),
|
||||
ImageGeterManager.DISK_CACHE | ImageGeterManager.DOWNLOAD,
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
}
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(mGalleryDetail.thumb, String.valueOf(mGalleryDetail.gid));
|
||||
ImageLoader.getInstance(mActivity).add(mGalleryDetail.thumb, String.valueOf(mGalleryDetail.gid),
|
||||
new LoadImageView.SimpleImageGetListener(thumb).setTransitabled(false));
|
||||
|
||||
TextView title = (TextView)mRootView.findViewById(R.id.detail_title);
|
||||
title.setText(mGalleryDetail.title);
|
||||
@@ -296,103 +282,41 @@ public class DetailSectionFragment extends Fragment
|
||||
mReadButton.setEnabled(false);
|
||||
mRateButton.setEnabled(false);
|
||||
|
||||
// get from cache
|
||||
if (getFromCache)
|
||||
layout(mGalleryDetail);
|
||||
else
|
||||
mClient.getGDetail(mUrl, mGalleryDetail, new GDetailGetListener());
|
||||
mClient.getGDetail(mUrl, mGalleryDetail, new GDetailGetListener());
|
||||
|
||||
return mRootView;
|
||||
}
|
||||
|
||||
private void addPageItem(int page) {
|
||||
if (mActivity.isFinishing())
|
||||
return;
|
||||
|
||||
if (page != mCurPage)
|
||||
return;
|
||||
|
||||
mWaitPreviewList.setVisibility(View.GONE);
|
||||
mPreviewRefreshButton.setVisibility(View.GONE);
|
||||
|
||||
PreviewList pageList = mGalleryDetail.previewLists[page];
|
||||
if (pageList == null) {
|
||||
Log.d(TAG, "WTF, I may check mangaDetail.pageLists[page] is not null");
|
||||
new SuperToast(mActivity).setIcon(R.drawable.ic_warning)
|
||||
.setMessage("WTF, I may check mangaDetail.pageLists[page] is not null")
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
int index = page * mGalleryDetail.previewPerPage + 1;
|
||||
int rowIndex = 0;
|
||||
for (PreviewList.Row row : pageList.rowArray) {
|
||||
for (PreviewList.Row.Item item : row.itemArray) {
|
||||
TextViewWithUrl tvu = new TextViewWithUrl(mActivity,
|
||||
item.url);
|
||||
tvu.setGravity(Gravity.CENTER);
|
||||
tvu.setText(String.valueOf(index));
|
||||
final int index1 = index;
|
||||
tvu.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Add to read in Data
|
||||
mData.addRead(mGalleryDetail);
|
||||
|
||||
Intent intent = new Intent(mActivity,
|
||||
MangaActivity.class);
|
||||
intent.putExtra("url", ((TextViewWithUrl) v).url);
|
||||
intent.putExtra("gid", mGalleryDetail.gid);
|
||||
intent.putExtra("title", mGalleryDetail.title);
|
||||
intent.putExtra("firstPage", index1 - 1);
|
||||
intent.putExtra("pageSum", mGalleryDetail.pages);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
// Set white drawable for temp
|
||||
ColorDrawable white = new ColorDrawable(Color.TRANSPARENT);
|
||||
white.setBounds(0, 0, item.width, item.height);
|
||||
tvu.setCompoundDrawables(null, white, null, null);
|
||||
|
||||
AutoWrapLayout.LayoutParams lp = new AutoWrapLayout.LayoutParams();
|
||||
lp.leftMargin = Ui.dp2pix(8);
|
||||
lp.topMargin = Ui.dp2pix(8);
|
||||
lp.rightMargin = Ui.dp2pix(8);
|
||||
lp.bottomMargin = Ui.dp2pix(8);
|
||||
|
||||
mPreviewListLayout.addView(tvu, lp);
|
||||
index++;
|
||||
}
|
||||
// Download pic
|
||||
EhClient.getImage(row.imageUrl, mGalleryDetail.gid + "-preview-"
|
||||
+ mCurPage + "-" + rowIndex, Util.getResourcesType(row.imageUrl), Cache.memoryCache,
|
||||
Cache.diskCache, new int[]{page, rowIndex}, new PageImageGetListener());
|
||||
rowIndex ++;
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshPageList() {
|
||||
mPreviewListLayout.removeAllViews();
|
||||
mWaitPreviewList.setVisibility(View.VISIBLE);
|
||||
mPreviewRefreshButton.setVisibility(View.GONE);
|
||||
|
||||
mPreviewNumText.setText(String.format("%d / %d",
|
||||
mCurPage+1, mGalleryDetail.previewSum));
|
||||
|
||||
|
||||
PreviewList pageList = mGalleryDetail.previewLists[mCurPage];
|
||||
|
||||
|
||||
if (pageList == null) {
|
||||
// Update layout
|
||||
mWaitPreviewList.setVisibility(View.VISIBLE);
|
||||
mPreviewRefreshButton.setVisibility(View.GONE);
|
||||
|
||||
mUrl = EhClient.getDetailUrl(
|
||||
mGalleryDetail.gid, mGalleryDetail.token, mCurPage);
|
||||
mClient.getPageList(mUrl, mCurPage,
|
||||
new EhClient.OnGetPageListListener() {
|
||||
mClient.getPreviewList(mUrl, mCurPage,
|
||||
new EhClient.OnGetPreviewListListener() {
|
||||
@Override
|
||||
public void onSuccess(Object checkFlag, PreviewList pageList) {
|
||||
public void onSuccess(Object checkFlag, NormalPreviewList pageList) {
|
||||
if (mActivity.isFinishing())
|
||||
return;
|
||||
|
||||
mWaitPreviewList.setVisibility(View.GONE);
|
||||
mPreviewRefreshButton.setVisibility(View.GONE);
|
||||
|
||||
int page = (Integer)checkFlag;
|
||||
mGalleryDetail.previewLists[page] = pageList;
|
||||
addPageItem(page);
|
||||
mGalleryDetail.previewLists[page].setData(DetailSectionFragment.this, mActivity, mGalleryDetail, page);
|
||||
mGalleryDetail.previewLists[page].addPreview(mPreviewListLayout);
|
||||
|
||||
|
||||
mShowPreview = true;
|
||||
}
|
||||
|
||||
@@ -412,7 +336,11 @@ public class DetailSectionFragment extends Fragment
|
||||
}
|
||||
});
|
||||
} else {
|
||||
addPageItem(mCurPage);
|
||||
// Update layout
|
||||
mWaitPreviewList.setVisibility(View.GONE);
|
||||
mPreviewRefreshButton.setVisibility(View.GONE);
|
||||
|
||||
mGalleryDetail.previewLists[mCurPage].addPreview(mPreviewListLayout);
|
||||
mShowPreview = true;
|
||||
}
|
||||
}
|
||||
@@ -642,7 +570,8 @@ public class DetailSectionFragment extends Fragment
|
||||
}
|
||||
|
||||
mCurPage = 0;
|
||||
addPageItem(0);
|
||||
mGalleryDetail.previewLists[0].setData(this, mActivity, mGalleryDetail, 0);
|
||||
mGalleryDetail.previewLists[0].addPreview(mPreviewListLayout);
|
||||
} else
|
||||
new SuperToast(mActivity)
|
||||
.setMessage(R.string.detail_preview_error)
|
||||
@@ -797,20 +726,10 @@ public class DetailSectionFragment extends Fragment
|
||||
return mGalleryDetail.comments;
|
||||
}
|
||||
|
||||
private class TextViewWithUrl extends TextView {
|
||||
private String url;
|
||||
|
||||
public TextViewWithUrl(Context context, String url) {
|
||||
super(context);
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
private class GDetailGetListener
|
||||
implements EhClient.OnGetGDetailListener {
|
||||
@Override
|
||||
public void onSuccess(GalleryDetail md) {
|
||||
Cache.mdCache.put(String.valueOf(mGalleryDetail.gid), md);
|
||||
layout(md);
|
||||
}
|
||||
|
||||
@@ -823,62 +742,6 @@ public class DetailSectionFragment extends Fragment
|
||||
new SuperToast(mActivity).setIcon(R.drawable.ic_warning).setMessage(eMsg).show();
|
||||
}
|
||||
}
|
||||
private class PageImageGetListener implements EhClient.OnGetImageListener {
|
||||
@Override
|
||||
public void onSuccess(Object checkFlag, Object res) {
|
||||
if (res instanceof Bitmap && checkFlag instanceof int[]) {
|
||||
int[] indexs = (int[])checkFlag;
|
||||
addPageImage(indexs[0], indexs[1], (Bitmap) res);
|
||||
} else {
|
||||
Log.e(TAG, "WTF? PageImageGetListener is error !");
|
||||
mWaitPreviewList.setVisibility(View.GONE);
|
||||
mPreviewRefreshButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onFailure(int errorMessageId) {
|
||||
mWaitPreviewList.setVisibility(View.GONE);
|
||||
mPreviewRefreshButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void addPageImage(int page, int rowIndex, Bitmap bitmap) {
|
||||
if (mActivity.isFinishing())
|
||||
return;
|
||||
|
||||
if (page != mCurPage)
|
||||
return;
|
||||
PreviewList pageList = mGalleryDetail.previewLists[page];
|
||||
if (pageList == null) {
|
||||
Log.d(TAG, "WTF, I may check mangaDetail.pageLists[page] is not null");
|
||||
return;
|
||||
}
|
||||
|
||||
int maxWidth = bitmap.getWidth();
|
||||
int maxHeight = bitmap.getHeight();
|
||||
|
||||
PreviewList.Row row = pageList.rowArray.get(rowIndex);
|
||||
ArrayList<PreviewList.Row.Item> items = row.itemArray;
|
||||
|
||||
int startIndex = row.startIndex;
|
||||
int endIndex = startIndex + items.size();
|
||||
for(int i = startIndex ; i < endIndex; i++) {
|
||||
PreviewList.Row.Item item = items.get(i - startIndex);
|
||||
|
||||
if (item.xOffset + item.width > maxWidth)
|
||||
item.width = maxWidth - item.xOffset;
|
||||
if (item.yOffset + item.height > maxHeight)
|
||||
item.height = maxHeight - item.yOffset;
|
||||
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(
|
||||
getResources(), Bitmap.createBitmap(bitmap,
|
||||
item.xOffset, item.yOffset, item.width, item.height));
|
||||
bitmapDrawable.setBounds(0, 0, item.width, item.height);
|
||||
|
||||
TextViewWithUrl tvu = (TextViewWithUrl)mPreviewListLayout.getChildAt(i);
|
||||
tvu.setCompoundDrawables(null, bitmapDrawable, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleVote implements View.OnClickListener {
|
||||
|
||||
|
||||
@@ -21,11 +21,10 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.ImageGeterManager;
|
||||
import com.hippo.ehviewer.ImageLoader;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.service.DownloadService;
|
||||
import com.hippo.ehviewer.service.DownloadServiceConnection;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Download;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
@@ -63,7 +62,7 @@ public class DownloadActivity extends AbstractActivity {
|
||||
private DownloadServiceConnection mServiceConn = new DownloadServiceConnection();
|
||||
|
||||
private AppContext mAppContext;
|
||||
private ImageGeterManager mImageGeterManager;
|
||||
private ImageLoader mImageLoader;
|
||||
|
||||
private List<DownloadInfo> mDownloadInfos;
|
||||
private DlAdapter mDlAdapter;
|
||||
@@ -155,20 +154,10 @@ public class DownloadActivity extends AbstractActivity {
|
||||
|
||||
LoadImageView thumb = (LoadImageView)view
|
||||
.findViewById(R.id.thumb);
|
||||
Bitmap bmp = null;
|
||||
if (Cache.memoryCache != null &&
|
||||
(bmp = Cache.memoryCache.get(di.gid)) != null) {
|
||||
thumb.setLoadInfo(di.thumb, di.gid);
|
||||
thumb.setImageBitmap(bmp);
|
||||
thumb.setState(LoadImageView.LOADED);
|
||||
} else {
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(di.thumb, di.gid);
|
||||
thumb.setState(LoadImageView.NONE);
|
||||
mImageGeterManager.add(di.thumb, di.gid,
|
||||
ImageGeterManager.DISK_CACHE | ImageGeterManager.DOWNLOAD,
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
}
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(di.thumb, di.gid);
|
||||
mImageLoader.add(di.thumb, di.gid,
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
|
||||
TextView title = (TextView)view.findViewById(R.id.title);
|
||||
title.setText(di.title);
|
||||
@@ -221,20 +210,10 @@ public class DownloadActivity extends AbstractActivity {
|
||||
|
||||
LoadImageView thumb = (LoadImageView)view
|
||||
.findViewById(R.id.thumb);
|
||||
Bitmap bmp = null;
|
||||
if (Cache.memoryCache != null &&
|
||||
(bmp = Cache.memoryCache.get(di.gid)) != null) {
|
||||
thumb.setLoadInfo(di.thumb, di.gid);
|
||||
thumb.setImageBitmap(bmp);
|
||||
thumb.setState(LoadImageView.LOADED);
|
||||
} else {
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(di.thumb, di.gid);
|
||||
thumb.setState(LoadImageView.NONE);
|
||||
mImageGeterManager.add(di.thumb, di.gid,
|
||||
ImageGeterManager.DISK_CACHE | ImageGeterManager.DOWNLOAD,
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
}
|
||||
thumb.setImageDrawable(null);
|
||||
thumb.setLoadInfo(di.thumb, di.gid);
|
||||
mImageLoader.add(di.thumb, di.gid,
|
||||
new LoadImageView.SimpleImageGetListener(thumb));
|
||||
|
||||
TextView title = (TextView)view.findViewById(R.id.title);
|
||||
title.setText(di.title);
|
||||
@@ -375,7 +354,7 @@ public class DownloadActivity extends AbstractActivity {
|
||||
setContentView(R.layout.download);
|
||||
|
||||
mAppContext = (AppContext)getApplication();
|
||||
mImageGeterManager = mAppContext.getImageGeterManager();
|
||||
mImageLoader = ImageLoader.getInstance(this);
|
||||
|
||||
Ui.translucent(this);
|
||||
|
||||
|
||||
@@ -138,7 +138,6 @@ public class FavouriteActivity extends AbstractGalleryActivity
|
||||
mAppContext = (AppContext)getApplication();
|
||||
mData = mAppContext.getData();
|
||||
mResources =getResources();
|
||||
mImageGeterManager = mAppContext.getImageGeterManager();
|
||||
mClient = mAppContext.getEhClient();
|
||||
|
||||
final ActionBar actionBar = getActionBar();
|
||||
|
||||
@@ -24,10 +24,8 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.DiskCache;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.UpdateHelper;
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Favorite;
|
||||
import com.hippo.ehviewer.util.Theme;
|
||||
@@ -351,6 +349,7 @@ public class SettingsActivity extends AbstractPreferenceActivity {
|
||||
}
|
||||
|
||||
private void updateClearCacheSummary() {
|
||||
/*
|
||||
if (Cache.diskCache != null) {
|
||||
mClearCache.setSummary(String.format(
|
||||
getString(R.string.clear_cache_summary_on),
|
||||
@@ -362,12 +361,15 @@ public class SettingsActivity extends AbstractPreferenceActivity {
|
||||
.setSummary(getString(R.string.clear_cache_summary_off));
|
||||
mClearCache.setEnabled(false);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object objValue) {
|
||||
final String key = preference.getKey();
|
||||
if (KEY_CACHE_SIZE.equals(key)) {
|
||||
/*
|
||||
|
||||
long cacheSize = 0;
|
||||
try {
|
||||
cacheSize = Integer.parseInt((String) objValue) * 1024 * 1024;
|
||||
@@ -393,7 +395,7 @@ public class SettingsActivity extends AbstractPreferenceActivity {
|
||||
} else
|
||||
Cache.diskCache.setMaxSize(cacheSize);
|
||||
updateClearCacheSummary();
|
||||
|
||||
*/
|
||||
} else if (KEY_MEDIA_SCAN.equals(key)) {
|
||||
boolean value = (Boolean)objValue;
|
||||
File nomedia = new File(Config.getDownloadPath(), ".nomedia");
|
||||
@@ -413,9 +415,10 @@ public class SettingsActivity extends AbstractPreferenceActivity {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
final String key = preference.getKey();
|
||||
if (KEY_CLEAR_CACHE.equals(key)) {
|
||||
/*
|
||||
Cache.diskCache.clear();
|
||||
updateClearCacheSummary();
|
||||
|
||||
*/
|
||||
} else if (KEY_DOWNLOAD_PATH.equals(key)) {
|
||||
View view = LayoutInflater.from(mActivity)
|
||||
.inflate(R.layout.dir_selection, null);
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 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.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.util.LruCache;
|
||||
|
||||
import com.hippo.ehviewer.data.GalleryDetail;
|
||||
import com.hippo.ehviewer.DiskCache;
|
||||
|
||||
public class Cache {
|
||||
private static final String TAG = "Cache";
|
||||
|
||||
private static Context mContext;
|
||||
|
||||
public static final String cpCachePath = "/EhViewer/cache/cover";
|
||||
public static DiskCache diskCache = null;
|
||||
public static LruCache<String, Bitmap> memoryCache = null;
|
||||
public static LruCache<String, GalleryDetail> mdCache = null;
|
||||
|
||||
private static final int MD_CACHE_SIZE = 5;
|
||||
|
||||
private static boolean mInit = false;
|
||||
|
||||
/**
|
||||
* Init Cache
|
||||
*
|
||||
* @param context Application context
|
||||
*/
|
||||
public static void init(Context context) {
|
||||
if (mInit)
|
||||
return;
|
||||
mInit = true;
|
||||
|
||||
mContext = context;
|
||||
|
||||
if (hasSdCard()) {
|
||||
try {
|
||||
int cpCacheSize = Config.getCoverDiskCacheSize() * 1024 * 1024;
|
||||
if (cpCacheSize <= 0)
|
||||
diskCache = null;
|
||||
else
|
||||
diskCache = new DiskCache(mContext, cpCachePath, cpCacheSize);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
final int cacheSize = 8 * 1024 * 1024; // 8MB, can store 80 thumb
|
||||
memoryCache = new LruCache<String, Bitmap>(cacheSize) {
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
protected int sizeOf(String key, Bitmap bitmap) {
|
||||
if (Build.VERSION.SDK_INT < 19)
|
||||
return bitmap.getByteCount();
|
||||
else
|
||||
return bitmap.getAllocationByteCount();
|
||||
}
|
||||
};
|
||||
|
||||
mdCache = new LruCache<String, GalleryDetail>(MD_CACHE_SIZE) {
|
||||
@Override
|
||||
protected int sizeOf(String key, GalleryDetail md) {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Is init
|
||||
* @return True if init
|
||||
*/
|
||||
public static boolean isInit() {
|
||||
return mInit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Have sd card or not
|
||||
* @return True if has sd card
|
||||
*/
|
||||
public static boolean hasSdCard() {
|
||||
return Environment.getExternalStorageState().equals(
|
||||
Environment.MEDIA_MOUNTED);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
package com.hippo.ehviewer.widget;
|
||||
|
||||
import com.hippo.ehviewer.ImageGeterManager;
|
||||
import com.hippo.ehviewer.ImageLoader;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -33,7 +34,6 @@ import android.view.animation.AlphaAnimation;
|
||||
import android.widget.ImageView;
|
||||
|
||||
public class LoadImageView extends ImageView {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "LoadImageView";
|
||||
|
||||
public static final int NONE = 0x0;
|
||||
@@ -51,8 +51,6 @@ public class LoadImageView extends ImageView {
|
||||
private String mUrl;
|
||||
private String mKey;
|
||||
|
||||
private static ImageGeterManager mImageGeterManager;
|
||||
|
||||
public LoadImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -80,8 +78,7 @@ public class LoadImageView extends ImageView {
|
||||
public void onClick(View v) {
|
||||
setClickable(false);
|
||||
setImageDrawable(null);
|
||||
mImageGeterManager.add(mUrl, mKey,
|
||||
ImageGeterManager.DISK_CACHE | ImageGeterManager.DOWNLOAD,
|
||||
ImageLoader.getInstance(getContext()).add(mUrl, mKey,
|
||||
new SimpleImageGetListener(LoadImageView.this));
|
||||
}
|
||||
});
|
||||
@@ -129,17 +126,19 @@ public class LoadImageView extends ImageView {
|
||||
*/
|
||||
}
|
||||
|
||||
public static void setImageGeterManager(ImageGeterManager i) {
|
||||
mImageGeterManager = i;
|
||||
}
|
||||
|
||||
public static class SimpleImageGetListener
|
||||
implements ImageGeterManager.OnGetImageListener {
|
||||
|
||||
implements ImageLoader.OnGetImageListener {
|
||||
private boolean mIsTransitabled = true;
|
||||
private LoadImageView mLiv;
|
||||
|
||||
public SimpleImageGetListener(LoadImageView liv) {
|
||||
mLiv = liv;
|
||||
mLiv.setState(LoadImageView.LOADING);
|
||||
}
|
||||
|
||||
public SimpleImageGetListener setTransitabled(boolean isTransitabled) {
|
||||
mIsTransitabled = isTransitabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isVaild(String key) {
|
||||
@@ -148,31 +147,19 @@ public class LoadImageView extends ImageView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGetImageStart(String key) {
|
||||
boolean re = isVaild(key) && (mLiv.getState() == LoadImageView.NONE ||
|
||||
mLiv.getState() == LoadImageView.FAIL);
|
||||
if (re)
|
||||
mLiv.setState(LoadImageView.LOADING);
|
||||
return re;
|
||||
}
|
||||
@Override
|
||||
public void onGetImageFail(String key) {
|
||||
public void onGetImage(String key, Bitmap bmp) {
|
||||
if (isVaild(key) && mLiv.getState() == LoadImageView.LOADING) {
|
||||
mLiv.setTouchImage();
|
||||
mLiv.setOnClick();
|
||||
mLiv.setState(LoadImageView.FAIL);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onGetImageSuccess(String key, Bitmap bmp, int mode) {
|
||||
if (isVaild(key) && mLiv.getState() == LoadImageView.LOADING) {
|
||||
if (mode == ImageGeterManager.MEMORY_CACHE
|
||||
|| mode == ImageGeterManager.DISK_CACHE)
|
||||
mLiv.setImageBitmap(bmp);
|
||||
else if (mode == ImageGeterManager.DOWNLOAD) {
|
||||
mLiv.setContextImage(bmp);
|
||||
if (bmp != null) {
|
||||
if (mIsTransitabled)
|
||||
mLiv.setContextImage(bmp);
|
||||
else
|
||||
mLiv.setImageBitmap(bmp);
|
||||
mLiv.setState(LoadImageView.LOADED);
|
||||
} else {
|
||||
mLiv.setTouchImage();
|
||||
mLiv.setOnClick();
|
||||
mLiv.setState(LoadImageView.FAIL);
|
||||
}
|
||||
mLiv.setState(LoadImageView.LOADED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,968 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jakewharton.disklrucache;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
/**
|
||||
* A cache that uses a bounded amount of space on a filesystem. Each cache entry
|
||||
* has a string key and a fixed number of values. Each key must match the regex
|
||||
* <strong>[a-z0-9_-]{1,64}</strong>. Values are byte sequences, accessible as
|
||||
* streams or files. Each value must be between {@code 0} and
|
||||
* {@code Integer.MAX_VALUE} bytes in length.
|
||||
*
|
||||
* <p>
|
||||
* The cache stores its data in a directory on the filesystem. This directory
|
||||
* must be exclusive to the cache; the cache may delete or overwrite files from
|
||||
* its directory. It is an error for multiple processes to use the same cache
|
||||
* directory at the same time.
|
||||
*
|
||||
* <p>
|
||||
* This cache limits the number of bytes that it will store on the filesystem.
|
||||
* When the number of stored bytes exceeds the limit, the cache will remove
|
||||
* entries in the background until the limit is satisfied. The limit is not
|
||||
* strict: the cache may temporarily exceed it while waiting for files to be
|
||||
* deleted. The limit does not include filesystem overhead or the cache journal
|
||||
* so space-sensitive applications should set a conservative limit.
|
||||
*
|
||||
* <p>
|
||||
* Clients call {@link #edit} to create or update the values of an entry. An
|
||||
* entry may have only one editor at one time; if a value is not available to be
|
||||
* edited then {@link #edit} will return null.
|
||||
* <ul>
|
||||
* <li>When an entry is being <strong>created</strong> it is necessary to supply
|
||||
* a full set of values; the empty value should be used as a placeholder if
|
||||
* necessary.
|
||||
* <li>When an entry is being <strong>edited</strong>, it is not necessary to
|
||||
* supply data for every value; values default to their previous value.
|
||||
* </ul>
|
||||
* Every {@link #edit} call must be matched by a call to {@link Editor#commit}
|
||||
* or {@link Editor#abort}. Committing is atomic: a read observes the full set
|
||||
* of values as they were before or after the commit, but never a mix of values.
|
||||
*
|
||||
* <p>
|
||||
* Clients call {@link #get} to read a snapshot of an entry. The read will
|
||||
* observe the value at the time that {@link #get} was called. Updates and
|
||||
* removals after the call do not impact ongoing reads.
|
||||
*
|
||||
* <p>
|
||||
* This class is tolerant of some I/O errors. If files are missing from the
|
||||
* filesystem, the corresponding entries will be dropped from the cache. If an
|
||||
* error occurs while writing a cache value, the edit will fail silently.
|
||||
* Callers should handle other problems by catching {@code IOException} and
|
||||
* responding appropriately.
|
||||
*/
|
||||
public final class DiskLruCache implements Closeable {
|
||||
static final String TAG = "DiskLruCache";
|
||||
static final String JOURNAL_FILE = "journal";
|
||||
static final String JOURNAL_FILE_TEMP = "journal.tmp";
|
||||
static final String JOURNAL_FILE_BACKUP = "journal.bkp";
|
||||
static final String MAGIC = "libcore.io.DiskLruCache";
|
||||
static final String VERSION_1 = "1";
|
||||
static final long ANY_SEQUENCE_NUMBER = -1;
|
||||
static final Pattern LEGAL_KEY_PATTERN = Pattern
|
||||
.compile("[a-z0-9_-]{1,64}");
|
||||
private static final String CLEAN = "CLEAN";
|
||||
private static final String DIRTY = "DIRTY";
|
||||
private static final String REMOVE = "REMOVE";
|
||||
private static final String READ = "READ";
|
||||
|
||||
/*
|
||||
* This cache uses a journal file named "journal". A typical journal file
|
||||
* looks like this: libcore.io.DiskLruCache 1 100 2
|
||||
*
|
||||
* CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 DIRTY
|
||||
* 335c4c6028171cfddfbaae1a9c313c52 CLEAN 335c4c6028171cfddfbaae1a9c313c52
|
||||
* 3934 2342 REMOVE 335c4c6028171cfddfbaae1a9c313c52 DIRTY
|
||||
* 1ab96a171faeeee38496d8b330771a7a CLEAN 1ab96a171faeeee38496d8b330771a7a
|
||||
* 1600 234 READ 335c4c6028171cfddfbaae1a9c313c52 READ
|
||||
* 3400330d1dfc7f3f7f4b8d4d803dfcf6
|
||||
*
|
||||
* The first five lines of the journal form its header. They are the
|
||||
* constant string "libcore.io.DiskLruCache", the disk cache's version, the
|
||||
* application's version, the value count, and a blank line.
|
||||
*
|
||||
* Each of the subsequent lines in the file is a record of the state of a
|
||||
* cache entry. Each line contains space-separated values: a state, a key,
|
||||
* and optional state-specific values. o DIRTY lines track that an entry is
|
||||
* actively being created or updated. Every successful DIRTY action should
|
||||
* be followed by a CLEAN or REMOVE action. DIRTY lines without a matching
|
||||
* CLEAN or REMOVE indicate that temporary files may need to be deleted. o
|
||||
* CLEAN lines track a cache entry that has been successfully published and
|
||||
* may be read. A publish line is followed by the lengths of each of its
|
||||
* values. o READ lines track accesses for LRU. o REMOVE lines track entries
|
||||
* that have been deleted.
|
||||
*
|
||||
* The journal file is appended to as cache operations occur. The journal
|
||||
* may occasionally be compacted by dropping redundant lines. A temporary
|
||||
* file named "journal.tmp" will be used during compaction; that file should
|
||||
* be deleted if it exists when the cache is opened.
|
||||
*/
|
||||
|
||||
private final File directory;
|
||||
private final File journalFile;
|
||||
private final File journalFileTmp;
|
||||
private final File journalFileBackup;
|
||||
private final int appVersion;
|
||||
private long maxSize;
|
||||
private final int valueCount;
|
||||
private long size = 0;
|
||||
private Writer journalWriter;
|
||||
private final LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<String, Entry>(
|
||||
0, 0.75f, true);
|
||||
private int redundantOpCount;
|
||||
|
||||
/**
|
||||
* To differentiate between old and current snapshots, each entry is given a
|
||||
* sequence number each time an edit is committed. A snapshot is stale if
|
||||
* its sequence number is not equal to its entry's sequence number.
|
||||
*/
|
||||
private long nextSequenceNumber = 0;
|
||||
|
||||
/** This cache uses a single background thread to evict entries. */
|
||||
final ThreadPoolExecutor executorService = new ThreadPoolExecutor(0, 1,
|
||||
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
|
||||
private final Callable<Void> cleanupCallable = new Callable<Void>() {
|
||||
public Void call() throws Exception {
|
||||
synchronized (DiskLruCache.this) {
|
||||
if (journalWriter == null) {
|
||||
return null; // Closed.
|
||||
}
|
||||
trimToSize();
|
||||
if (journalRebuildRequired()) {
|
||||
rebuildJournal();
|
||||
redundantOpCount = 0;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private DiskLruCache(File directory, int appVersion, int valueCount,
|
||||
long maxSize) {
|
||||
this.directory = directory;
|
||||
this.appVersion = appVersion;
|
||||
this.journalFile = new File(directory, JOURNAL_FILE);
|
||||
this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
|
||||
this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
|
||||
this.valueCount = valueCount;
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the cache in {@code directory}, creating a cache if none exists
|
||||
* there.
|
||||
*
|
||||
* @param directory
|
||||
* a writable directory
|
||||
* @param valueCount
|
||||
* the number of values per cache entry. Must be positive.
|
||||
* @param maxSize
|
||||
* the maximum number of bytes this cache should use to store
|
||||
* @throws IOException
|
||||
* if reading or writing the cache directory fails
|
||||
*/
|
||||
public static DiskLruCache open(File directory, int appVersion,
|
||||
int valueCount, long maxSize) throws IOException {
|
||||
if (maxSize <= 0) {
|
||||
throw new IllegalArgumentException("maxSize <= 0");
|
||||
}
|
||||
if (valueCount <= 0) {
|
||||
throw new IllegalArgumentException("valueCount <= 0");
|
||||
}
|
||||
|
||||
// If a bkp file exists, use it instead.
|
||||
File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
|
||||
if (backupFile.exists()) {
|
||||
File journalFile = new File(directory, JOURNAL_FILE);
|
||||
// If journal file also exists just delete backup file.
|
||||
if (journalFile.exists()) {
|
||||
backupFile.delete();
|
||||
} else {
|
||||
renameTo(backupFile, journalFile, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer to pick up where we left off.
|
||||
DiskLruCache cache = new DiskLruCache(directory, appVersion,
|
||||
valueCount, maxSize);
|
||||
if (cache.journalFile.exists()) {
|
||||
try {
|
||||
cache.readJournal();
|
||||
cache.processJournal();
|
||||
cache.journalWriter = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(
|
||||
cache.journalFile, true), Util.US_ASCII));
|
||||
return cache;
|
||||
} catch (IOException journalIsCorrupt) {
|
||||
System.out.println("DiskLruCache " + directory
|
||||
+ " is corrupt: " + journalIsCorrupt.getMessage()
|
||||
+ ", removing");
|
||||
cache.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new empty cache.
|
||||
directory.mkdirs();
|
||||
cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
|
||||
cache.rebuildJournal();
|
||||
return cache;
|
||||
}
|
||||
|
||||
private void readJournal() throws IOException {
|
||||
StrictLineReader reader = new StrictLineReader(new FileInputStream(
|
||||
journalFile), Util.US_ASCII);
|
||||
try {
|
||||
String magic = reader.readLine();
|
||||
String version = reader.readLine();
|
||||
String appVersionString = reader.readLine();
|
||||
String valueCountString = reader.readLine();
|
||||
String blank = reader.readLine();
|
||||
if (!MAGIC.equals(magic) || !VERSION_1.equals(version)
|
||||
|| !Integer.toString(appVersion).equals(appVersionString)
|
||||
|| !Integer.toString(valueCount).equals(valueCountString)
|
||||
|| !"".equals(blank)) {
|
||||
throw new IOException("unexpected journal header: [" + magic
|
||||
+ ", " + version + ", " + valueCountString + ", "
|
||||
+ blank + "]");
|
||||
}
|
||||
|
||||
int lineCount = 0;
|
||||
while (true) {
|
||||
try {
|
||||
readJournalLine(reader.readLine());
|
||||
lineCount++;
|
||||
} catch (EOFException endOfJournal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
redundantOpCount = lineCount - lruEntries.size();
|
||||
} finally {
|
||||
Util.closeQuietly(reader);
|
||||
}
|
||||
}
|
||||
|
||||
private void readJournalLine(String line) throws IOException {
|
||||
int firstSpace = line.indexOf(' ');
|
||||
if (firstSpace == -1) {
|
||||
throw new IOException("unexpected journal line: " + line);
|
||||
}
|
||||
|
||||
int keyBegin = firstSpace + 1;
|
||||
int secondSpace = line.indexOf(' ', keyBegin);
|
||||
final String key;
|
||||
if (secondSpace == -1) {
|
||||
key = line.substring(keyBegin);
|
||||
if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
|
||||
lruEntries.remove(key);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
key = line.substring(keyBegin, secondSpace);
|
||||
}
|
||||
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (entry == null) {
|
||||
entry = new Entry(key);
|
||||
lruEntries.put(key, entry);
|
||||
}
|
||||
|
||||
if (secondSpace != -1 && firstSpace == CLEAN.length()
|
||||
&& line.startsWith(CLEAN)) {
|
||||
String[] parts = line.substring(secondSpace + 1).split(" ");
|
||||
entry.readable = true;
|
||||
entry.currentEditor = null;
|
||||
entry.setLengths(parts);
|
||||
} else if (secondSpace == -1 && firstSpace == DIRTY.length()
|
||||
&& line.startsWith(DIRTY)) {
|
||||
entry.currentEditor = new Editor(entry);
|
||||
} else if (secondSpace == -1 && firstSpace == READ.length()
|
||||
&& line.startsWith(READ)) {
|
||||
// This work was already done by calling lruEntries.get().
|
||||
} else {
|
||||
throw new IOException("unexpected journal line: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the initial size and collects garbage as a part of opening the
|
||||
* cache. Dirty entries are assumed to be inconsistent and will be deleted.
|
||||
*/
|
||||
private void processJournal() throws IOException {
|
||||
deleteIfExists(journalFileTmp);
|
||||
for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext();) {
|
||||
Entry entry = i.next();
|
||||
if (entry.currentEditor == null) {
|
||||
for (int t = 0; t < valueCount; t++) {
|
||||
size += entry.lengths[t];
|
||||
}
|
||||
} else {
|
||||
entry.currentEditor = null;
|
||||
for (int t = 0; t < valueCount; t++) {
|
||||
deleteIfExists(entry.getCleanFile(t));
|
||||
deleteIfExists(entry.getDirtyFile(t));
|
||||
}
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new journal that omits redundant information. This replaces the
|
||||
* current journal if it exists.
|
||||
*/
|
||||
private synchronized void rebuildJournal() throws IOException {
|
||||
if (journalWriter != null) {
|
||||
journalWriter.close();
|
||||
}
|
||||
|
||||
Writer writer = new BufferedWriter(new OutputStreamWriter(
|
||||
new FileOutputStream(journalFileTmp), Util.US_ASCII));
|
||||
try {
|
||||
writer.write(MAGIC);
|
||||
writer.write("\n");
|
||||
writer.write(VERSION_1);
|
||||
writer.write("\n");
|
||||
writer.write(Integer.toString(appVersion));
|
||||
writer.write("\n");
|
||||
writer.write(Integer.toString(valueCount));
|
||||
writer.write("\n");
|
||||
writer.write("\n");
|
||||
|
||||
for (Entry entry : lruEntries.values()) {
|
||||
if (entry.currentEditor != null) {
|
||||
writer.write(DIRTY + ' ' + entry.key + '\n');
|
||||
} else {
|
||||
writer.write(CLEAN + ' ' + entry.key + entry.getLengths()
|
||||
+ '\n');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
|
||||
if (journalFile.exists()) {
|
||||
renameTo(journalFile, journalFileBackup, true);
|
||||
}
|
||||
renameTo(journalFileTmp, journalFile, false);
|
||||
journalFileBackup.delete();
|
||||
|
||||
journalWriter = new BufferedWriter(new OutputStreamWriter(
|
||||
new FileOutputStream(journalFile, true), Util.US_ASCII));
|
||||
}
|
||||
|
||||
private static void deleteIfExists(File file) throws IOException {
|
||||
if (file.exists() && !file.delete()) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renameTo(File from, File to, boolean deleteDestination)
|
||||
throws IOException {
|
||||
if (deleteDestination) {
|
||||
deleteIfExists(to);
|
||||
}
|
||||
if (!from.renameTo(to)) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a snapshot of the entry named {@code key}, or null if it doesn't
|
||||
* exist is not currently readable. If a value is returned, it is moved to
|
||||
* the head of the LRU queue.
|
||||
*/
|
||||
public synchronized Snapshot get(String key) throws IOException {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!entry.readable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Open all streams eagerly to guarantee that we see a single published
|
||||
// snapshot. If we opened streams lazily then the streams could come
|
||||
// from different edits.
|
||||
InputStream[] ins = new InputStream[valueCount];
|
||||
try {
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
ins[i] = new FileInputStream(entry.getCleanFile(i));
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// A file must have been deleted manually!
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
if (ins[i] != null) {
|
||||
Util.closeQuietly(ins[i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
redundantOpCount++;
|
||||
journalWriter.append(READ + ' ' + key + '\n');
|
||||
if (journalRebuildRequired()) {
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
|
||||
return new Snapshot(key, entry.sequenceNumber, ins, entry.lengths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an editor for the entry named {@code key}, or null if another
|
||||
* edit is in progress.
|
||||
*/
|
||||
public Editor edit(String key) throws IOException {
|
||||
return edit(key, ANY_SEQUENCE_NUMBER);
|
||||
}
|
||||
|
||||
private synchronized Editor edit(String key, long expectedSequenceNumber)
|
||||
throws IOException {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
|
||||
&& (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
|
||||
return null; // Snapshot is stale.
|
||||
}
|
||||
if (entry == null) {
|
||||
entry = new Entry(key);
|
||||
lruEntries.put(key, entry);
|
||||
} else if (entry.currentEditor != null) {
|
||||
return null; // Another edit is in progress.
|
||||
}
|
||||
|
||||
Editor editor = new Editor(entry);
|
||||
entry.currentEditor = editor;
|
||||
|
||||
// Flush the journal before creating files to prevent file leaks.
|
||||
journalWriter.write(DIRTY + ' ' + key + '\n');
|
||||
journalWriter.flush();
|
||||
return editor;
|
||||
}
|
||||
|
||||
/** Returns the directory where this cache stores its data. */
|
||||
public File getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of bytes that this cache should use to store
|
||||
* its data.
|
||||
*/
|
||||
public synchronized long getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the maximum number of bytes the cache can store and queues a job
|
||||
* to trim the existing store, if necessary.
|
||||
*/
|
||||
public synchronized void setMaxSize(long maxSize) {
|
||||
if (maxSize <= 0) {
|
||||
throw new IllegalArgumentException("maxSize <= 0");
|
||||
}
|
||||
this.maxSize = maxSize;
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes currently being used to store the values in
|
||||
* this cache. This may be greater than the max size if a background
|
||||
* deletion is pending.
|
||||
*/
|
||||
public synchronized long size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
private synchronized void completeEdit(Editor editor, boolean success)
|
||||
throws IOException {
|
||||
Entry entry = editor.entry;
|
||||
if (entry.currentEditor != editor) {
|
||||
// throw new IllegalStateException();
|
||||
// TODO I add, It may error when call delete()
|
||||
return;
|
||||
}
|
||||
|
||||
// If this edit is creating the entry for the first time, every index
|
||||
// must have a value.
|
||||
if (success && !entry.readable) {
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
if (!editor.written[i]) {
|
||||
editor.abort();
|
||||
throw new IllegalStateException(
|
||||
"Newly created entry didn't create value for index "
|
||||
+ i);
|
||||
}
|
||||
if (!entry.getDirtyFile(i).exists()) {
|
||||
editor.abort();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
File dirty = entry.getDirtyFile(i);
|
||||
if (success) {
|
||||
if (dirty.exists()) {
|
||||
File clean = entry.getCleanFile(i);
|
||||
dirty.renameTo(clean);
|
||||
long oldLength = entry.lengths[i];
|
||||
long newLength = clean.length();
|
||||
entry.lengths[i] = newLength;
|
||||
size = size - oldLength + newLength;
|
||||
}
|
||||
} else {
|
||||
deleteIfExists(dirty);
|
||||
}
|
||||
}
|
||||
|
||||
redundantOpCount++;
|
||||
entry.currentEditor = null;
|
||||
if (entry.readable | success) {
|
||||
entry.readable = true;
|
||||
journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths()
|
||||
+ '\n');
|
||||
if (success) {
|
||||
entry.sequenceNumber = nextSequenceNumber++;
|
||||
}
|
||||
} else {
|
||||
lruEntries.remove(entry.key);
|
||||
journalWriter.write(REMOVE + ' ' + entry.key + '\n');
|
||||
}
|
||||
journalWriter.flush();
|
||||
|
||||
if (size > maxSize || journalRebuildRequired()) {
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We only rebuild the journal when it will halve the size of the journal
|
||||
* and eliminate at least 2000 ops.
|
||||
*/
|
||||
private boolean journalRebuildRequired() {
|
||||
final int redundantOpCompactThreshold = 2000;
|
||||
return redundantOpCount >= redundantOpCompactThreshold //
|
||||
&& redundantOpCount >= lruEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the entry for {@code key} if it exists and can be removed. Entries
|
||||
* actively being edited cannot be removed.
|
||||
*
|
||||
* @return true if an entry was removed.
|
||||
*/
|
||||
public synchronized boolean remove(String key) throws IOException {
|
||||
checkNotClosed();
|
||||
validateKey(key);
|
||||
Entry entry = lruEntries.get(key);
|
||||
if (entry == null || entry.currentEditor != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueCount; i++) {
|
||||
File file = entry.getCleanFile(i);
|
||||
if (file.exists() && !file.delete()) {
|
||||
throw new IOException("failed to delete " + file);
|
||||
}
|
||||
size -= entry.lengths[i];
|
||||
entry.lengths[i] = 0;
|
||||
}
|
||||
|
||||
redundantOpCount++;
|
||||
journalWriter.append(REMOVE + ' ' + key + '\n');
|
||||
lruEntries.remove(key);
|
||||
|
||||
if (journalRebuildRequired()) {
|
||||
executorService.submit(cleanupCallable);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Returns true if this cache has been closed. */
|
||||
public synchronized boolean isClosed() {
|
||||
return journalWriter == null;
|
||||
}
|
||||
|
||||
private void checkNotClosed() {
|
||||
if (journalWriter == null) {
|
||||
throw new IllegalStateException("cache is closed");
|
||||
}
|
||||
}
|
||||
|
||||
/** Force buffered operations to the filesystem. */
|
||||
public synchronized void flush() throws IOException {
|
||||
checkNotClosed();
|
||||
trimToSize();
|
||||
journalWriter.flush();
|
||||
}
|
||||
|
||||
/** Closes this cache. Stored values will remain on the filesystem. */
|
||||
public synchronized void close() throws IOException {
|
||||
if (journalWriter == null) {
|
||||
return; // Already closed.
|
||||
}
|
||||
for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
|
||||
if (entry.currentEditor != null) {
|
||||
entry.currentEditor.abort();
|
||||
}
|
||||
}
|
||||
trimToSize();
|
||||
journalWriter.close();
|
||||
journalWriter = null;
|
||||
}
|
||||
|
||||
private void trimToSize() throws IOException {
|
||||
while (size > maxSize) {
|
||||
Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator()
|
||||
.next();
|
||||
remove(toEvict.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the cache and deletes all of its stored values. This will delete
|
||||
* all files in the cache directory including files that weren't created by
|
||||
* the cache.
|
||||
*/
|
||||
public synchronized void delete() throws IOException {
|
||||
close();
|
||||
Util.deleteContents(directory);
|
||||
// TODO I add
|
||||
size = 0;
|
||||
lruEntries.clear();
|
||||
rebuildJournal();
|
||||
}
|
||||
|
||||
private void validateKey(String key) {
|
||||
Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
"keys must match regex [a-z0-9_-]{1,64}: \"" + key + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
private static String inputStreamToString(InputStream in)
|
||||
throws IOException {
|
||||
return Util.readFully(new InputStreamReader(in, Util.UTF_8));
|
||||
}
|
||||
|
||||
/** A snapshot of the values for an entry. */
|
||||
public final class Snapshot implements Closeable {
|
||||
private final String key;
|
||||
private final long sequenceNumber;
|
||||
private final InputStream[] ins;
|
||||
private final long[] lengths;
|
||||
|
||||
private Snapshot(String key, long sequenceNumber, InputStream[] ins,
|
||||
long[] lengths) {
|
||||
this.key = key;
|
||||
this.sequenceNumber = sequenceNumber;
|
||||
this.ins = ins;
|
||||
this.lengths = lengths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an editor for this snapshot's entry, or null if either the
|
||||
* entry has changed since this snapshot was created or if another edit
|
||||
* is in progress.
|
||||
*/
|
||||
public Editor edit() throws IOException {
|
||||
return DiskLruCache.this.edit(key, sequenceNumber);
|
||||
}
|
||||
|
||||
/** Returns the unbuffered stream with the value for {@code index}. */
|
||||
public InputStream getInputStream(int index) {
|
||||
return ins[index];
|
||||
}
|
||||
|
||||
/** Returns the string value for {@code index}. */
|
||||
public String getString(int index) throws IOException {
|
||||
return inputStreamToString(getInputStream(index));
|
||||
}
|
||||
|
||||
/** Returns the byte length of the value for {@code index}. */
|
||||
public long getLength(int index) {
|
||||
return lengths[index];
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (InputStream in : ins) {
|
||||
Util.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
// Eat all writes silently. Nom nom.
|
||||
}
|
||||
};
|
||||
|
||||
/** Edits the values for an entry. */
|
||||
public final class Editor {
|
||||
private final Entry entry;
|
||||
private final boolean[] written;
|
||||
private boolean hasErrors;
|
||||
private boolean committed;
|
||||
|
||||
private Editor(Entry entry) {
|
||||
this.entry = entry;
|
||||
this.written = (entry.readable) ? null : new boolean[valueCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unbuffered input stream to read the last committed value,
|
||||
* or null if no value has been committed.
|
||||
*/
|
||||
public InputStream newInputStream(int index) throws IOException {
|
||||
synchronized (DiskLruCache.this) {
|
||||
if (entry.currentEditor != this) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!entry.readable) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new FileInputStream(entry.getCleanFile(index));
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last committed value as a string, or null if no value has
|
||||
* been committed.
|
||||
*/
|
||||
public String getString(int index) throws IOException {
|
||||
InputStream in = newInputStream(index);
|
||||
return in != null ? inputStreamToString(in) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new unbuffered output stream to write the value at
|
||||
* {@code index}. If the underlying output stream encounters errors when
|
||||
* writing to the filesystem, this edit will be aborted when
|
||||
* {@link #commit} is called. The returned output stream does not throw
|
||||
* IOExceptions.
|
||||
*/
|
||||
public OutputStream newOutputStream(int index) throws IOException {
|
||||
synchronized (DiskLruCache.this) {
|
||||
if (entry.currentEditor != this) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!entry.readable) {
|
||||
written[index] = true;
|
||||
}
|
||||
File dirtyFile = entry.getDirtyFile(index);
|
||||
FileOutputStream outputStream;
|
||||
try {
|
||||
outputStream = new FileOutputStream(dirtyFile);
|
||||
} catch (FileNotFoundException e) {
|
||||
// Attempt to recreate the cache directory.
|
||||
directory.mkdirs();
|
||||
try {
|
||||
outputStream = new FileOutputStream(dirtyFile);
|
||||
} catch (FileNotFoundException e2) {
|
||||
// We are unable to recover. Silently eat the writes.
|
||||
return NULL_OUTPUT_STREAM;
|
||||
}
|
||||
}
|
||||
return new FaultHidingOutputStream(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the value at {@code index} to {@code value}. */
|
||||
public void set(int index, String value) throws IOException {
|
||||
Writer writer = null;
|
||||
try {
|
||||
writer = new OutputStreamWriter(newOutputStream(index),
|
||||
Util.UTF_8);
|
||||
writer.write(value);
|
||||
} finally {
|
||||
Util.closeQuietly(writer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits this edit so it is visible to readers. This releases the edit
|
||||
* lock so another edit may be started on the same key.
|
||||
*/
|
||||
public void commit() throws IOException {
|
||||
if (hasErrors) {
|
||||
completeEdit(this, false);
|
||||
remove(entry.key); // The previous entry is stale.
|
||||
} else {
|
||||
completeEdit(this, true);
|
||||
}
|
||||
committed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts this edit. This releases the edit lock so another edit may be
|
||||
* started on the same key.
|
||||
*/
|
||||
public void abort() throws IOException {
|
||||
completeEdit(this, false);
|
||||
}
|
||||
|
||||
public void abortUnlessCommitted() {
|
||||
if (!committed) {
|
||||
try {
|
||||
abort();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FaultHidingOutputStream extends FilterOutputStream {
|
||||
private FaultHidingOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) {
|
||||
try {
|
||||
out.write(oneByte);
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) {
|
||||
try {
|
||||
out.write(buffer, offset, length);
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
out.flush();
|
||||
} catch (IOException e) {
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class Entry {
|
||||
private final String key;
|
||||
|
||||
/** Lengths of this entry's files. */
|
||||
private final long[] lengths;
|
||||
|
||||
/** True if this entry has ever been published. */
|
||||
private boolean readable;
|
||||
|
||||
/** The ongoing edit or null if this entry is not being edited. */
|
||||
private Editor currentEditor;
|
||||
|
||||
/**
|
||||
* The sequence number of the most recently committed edit to this
|
||||
* entry.
|
||||
*/
|
||||
private long sequenceNumber;
|
||||
|
||||
private Entry(String key) {
|
||||
this.key = key;
|
||||
this.lengths = new long[valueCount];
|
||||
}
|
||||
|
||||
public String getLengths() throws IOException {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (long size : lengths) {
|
||||
result.append(' ').append(size);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/** Set lengths using decimal numbers like "10123". */
|
||||
private void setLengths(String[] strings) throws IOException {
|
||||
if (strings.length != valueCount) {
|
||||
throw invalidLengths(strings);
|
||||
}
|
||||
|
||||
try {
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
lengths[i] = Long.parseLong(strings[i]);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw invalidLengths(strings);
|
||||
}
|
||||
}
|
||||
|
||||
private IOException invalidLengths(String[] strings) throws IOException {
|
||||
throw new IOException("unexpected journal line: "
|
||||
+ java.util.Arrays.toString(strings));
|
||||
}
|
||||
|
||||
public File getCleanFile(int i) {
|
||||
return new File(directory, key + "." + i);
|
||||
}
|
||||
|
||||
public File getDirtyFile(int i) {
|
||||
return new File(directory, key + "." + i + ".tmp");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jakewharton.disklrucache;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Buffers input from an {@link InputStream} for reading lines.
|
||||
*
|
||||
* <p>This class is used for buffered reading of lines. For purposes of this class, a line ends
|
||||
* with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
|
||||
* line at end of input is invalid and will be ignored, the caller may use {@code
|
||||
* hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
|
||||
*
|
||||
* <p>This class is intended for reading input that strictly consists of lines, such as line-based
|
||||
* cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
|
||||
* with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
|
||||
* end-of-input reporting and a more restrictive definition of a line.
|
||||
*
|
||||
* <p>This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
|
||||
* and 10, respectively, and the representation of no other character contains these values.
|
||||
* We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
|
||||
* The default charset is US_ASCII.
|
||||
*/
|
||||
class StrictLineReader implements Closeable {
|
||||
private static final byte CR = (byte) '\r';
|
||||
private static final byte LF = (byte) '\n';
|
||||
|
||||
private final InputStream in;
|
||||
private final Charset charset;
|
||||
|
||||
/*
|
||||
* Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
|
||||
* and the data in the range [pos, end) is buffered for reading. At end of input, if there is
|
||||
* an unterminated line, we set end == -1, otherwise end == pos. If the underlying
|
||||
* {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
|
||||
*/
|
||||
private byte[] buf;
|
||||
private int pos;
|
||||
private int end;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code LineReader} with the specified charset and the default capacity.
|
||||
*
|
||||
* @param in the {@code InputStream} to read data from.
|
||||
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
|
||||
* supported.
|
||||
* @throws NullPointerException if {@code in} or {@code charset} is null.
|
||||
* @throws IllegalArgumentException if the specified charset is not supported.
|
||||
*/
|
||||
public StrictLineReader(InputStream in, Charset charset) {
|
||||
this(in, 8192, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code LineReader} with the specified capacity and charset.
|
||||
*
|
||||
* @param in the {@code InputStream} to read data from.
|
||||
* @param capacity the capacity of the buffer.
|
||||
* @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
|
||||
* supported.
|
||||
* @throws NullPointerException if {@code in} or {@code charset} is null.
|
||||
* @throws IllegalArgumentException if {@code capacity} is negative or zero
|
||||
* or the specified charset is not supported.
|
||||
*/
|
||||
public StrictLineReader(InputStream in, int capacity, Charset charset) {
|
||||
if (in == null || charset == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (capacity < 0) {
|
||||
throw new IllegalArgumentException("capacity <= 0");
|
||||
}
|
||||
if (!(charset.equals(Util.US_ASCII))) {
|
||||
throw new IllegalArgumentException("Unsupported encoding");
|
||||
}
|
||||
|
||||
this.in = in;
|
||||
this.charset = charset;
|
||||
buf = new byte[capacity];
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the reader by closing the underlying {@code InputStream} and
|
||||
* marking this reader as closed.
|
||||
*
|
||||
* @throws IOException for errors when closing the underlying {@code InputStream}.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
synchronized (in) {
|
||||
if (buf != null) {
|
||||
buf = null;
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
|
||||
* this end of line marker is not included in the result.
|
||||
*
|
||||
* @return the next line from the input.
|
||||
* @throws IOException for underlying {@code InputStream} errors.
|
||||
* @throws EOFException for the end of source stream.
|
||||
*/
|
||||
public String readLine() throws IOException {
|
||||
synchronized (in) {
|
||||
if (buf == null) {
|
||||
throw new IOException("LineReader is closed");
|
||||
}
|
||||
|
||||
// Read more data if we are at the end of the buffered data.
|
||||
// Though it's an error to read after an exception, we will let {@code fillBuf()}
|
||||
// throw again if that happens; thus we need to handle end == -1 as well as end == pos.
|
||||
if (pos >= end) {
|
||||
fillBuf();
|
||||
}
|
||||
// Try to find LF in the buffered data and return the line if successful.
|
||||
for (int i = pos; i != end; ++i) {
|
||||
if (buf[i] == LF) {
|
||||
int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
|
||||
String res = new String(buf, pos, lineEnd - pos, charset.name());
|
||||
pos = i + 1;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Let's anticipate up to 80 characters on top of those already read.
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
|
||||
@Override
|
||||
public String toString() {
|
||||
int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
|
||||
try {
|
||||
return new String(buf, 0, length, charset.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError(e); // Since we control the charset this will never happen.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
out.write(buf, pos, end - pos);
|
||||
// Mark unterminated line in case fillBuf throws EOFException or IOException.
|
||||
end = -1;
|
||||
fillBuf();
|
||||
// Try to find LF in the buffered data and return the line if successful.
|
||||
for (int i = pos; i != end; ++i) {
|
||||
if (buf[i] == LF) {
|
||||
if (i != pos) {
|
||||
out.write(buf, pos, i - pos);
|
||||
}
|
||||
pos = i + 1;
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads new input data into the buffer. Call only with pos == end or end == -1,
|
||||
* depending on the desired outcome if the function throws.
|
||||
*/
|
||||
private void fillBuf() throws IOException {
|
||||
int result = in.read(buf, 0, buf.length);
|
||||
if (result == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
pos = 0;
|
||||
end = result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jakewharton.disklrucache;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/** Junk drawer of utility methods. */
|
||||
final class Util {
|
||||
static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||
static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
private Util() {
|
||||
}
|
||||
|
||||
static String readFully(Reader reader) throws IOException {
|
||||
try {
|
||||
StringWriter writer = new StringWriter();
|
||||
char[] buffer = new char[1024];
|
||||
int count;
|
||||
while ((count = reader.read(buffer)) != -1) {
|
||||
writer.write(buffer, 0, count);
|
||||
}
|
||||
return writer.toString();
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the contents of {@code dir}. Throws an IOException if any file
|
||||
* could not be deleted, or if {@code dir} is not a readable directory.
|
||||
*/
|
||||
static void deleteContents(File dir) throws IOException {
|
||||
File[] files = dir.listFiles();
|
||||
if (files == null) {
|
||||
throw new IOException("not a readable directory: " + dir);
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
deleteContents(file);
|
||||
}
|
||||
if (!file.delete()) {
|
||||
throw new IOException("failed to delete file: " + file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void closeQuietly(/*Auto*/Closeable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (RuntimeException rethrown) {
|
||||
throw rethrown;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user