完善阅读下载策略
This commit is contained in:
1 parent
374ed3a515
commit
a2bbe855e8
38 files changed
+1297
-2142
No files matched your search
@@ -26,6 +26,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:name=".AppContext"
|
||||
|
||||
@@ -28,7 +28,7 @@ import android.app.Application;
|
||||
import com.hippo.ehviewer.cache.ImageCache;
|
||||
import com.hippo.ehviewer.data.Data;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.ehclient.ExDownloadManager;
|
||||
import com.hippo.ehviewer.ehclient.ExDownloaderManager;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Crash;
|
||||
import com.hippo.ehviewer.util.Download;
|
||||
@@ -71,7 +71,7 @@ public class AppContext extends Application implements UncaughtExceptionHandler
|
||||
Download.init(this);
|
||||
Favorite.init(this);
|
||||
Data.createInstance(this);
|
||||
ExDownloadManager.createInstance();
|
||||
ExDownloaderManager.createInstance();
|
||||
MaterialToast.setContext(this);
|
||||
|
||||
// Do catch error prepare
|
||||
|
||||
@@ -1,43 +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 com.hippo.ehviewer.data.GalleryInfo;
|
||||
|
||||
public class GalleryDownloadInfo {
|
||||
|
||||
public static final int NONE = 0x0;
|
||||
public static final int DOWNLOADING = 0x1;
|
||||
public static final int FINISHED = 0x2;
|
||||
|
||||
public GalleryInfo galleryInfo;
|
||||
|
||||
// For ui set progressbar
|
||||
public int picNum;
|
||||
public int downloadNum;
|
||||
|
||||
public int state;
|
||||
|
||||
public GalleryDownloadInfo(GalleryInfo galleryInfo) {
|
||||
this(galleryInfo, NONE);
|
||||
}
|
||||
|
||||
public GalleryDownloadInfo(GalleryInfo galleryInfo, int state) {
|
||||
this.galleryInfo = galleryInfo;
|
||||
this.state = state == DOWNLOADING ? NONE : FINISHED;
|
||||
}
|
||||
}
|
||||
@@ -1,311 +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.File;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.hippo.ehviewer.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.data.PreviewList;
|
||||
import com.hippo.ehviewer.ehclient.DetailParser;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
/**
|
||||
* Download a gallery
|
||||
*
|
||||
* @author Hippo
|
||||
*
|
||||
*/
|
||||
|
||||
// TODO For gallerys whose Resized is Originals,
|
||||
// we can get download info from hathdl
|
||||
|
||||
public class GallerysDownloader implements Runnable {
|
||||
private static final String TAG = GallerysDownloader.class.getSimpleName();
|
||||
|
||||
private static final int MAX_RETRY = 3;
|
||||
|
||||
private static final int OK = 0x0;
|
||||
private static final int FAIL = 0x1;
|
||||
private static final int STOP = 0x2;
|
||||
|
||||
private static final int MAX_DOWNLOAD_THREAD_NUM = 3;
|
||||
|
||||
private final Context mContext;
|
||||
private final GalleryInfo mGalleryInfo;
|
||||
private int mPageIndex;
|
||||
private int mPageSum;
|
||||
private int mPagePerDp;
|
||||
private PreviewList[] mPages;
|
||||
private final Thread mMainThread;
|
||||
private final Queue<Integer> mRequstDetailPage;
|
||||
private final Queue<Integer> mRequstPage;
|
||||
private int downloadThreadNum = 0;
|
||||
private final File mDownloadDir;
|
||||
|
||||
public GallerysDownloader(Context context, GalleryInfo galleryInfo) {
|
||||
this(context, galleryInfo, 0);
|
||||
}
|
||||
|
||||
public GallerysDownloader(Context context, GalleryInfo galleryInfo, int startIndex) {
|
||||
mContext = context;
|
||||
mGalleryInfo = galleryInfo;
|
||||
mPageIndex = startIndex;
|
||||
|
||||
|
||||
|
||||
mDownloadDir = new File(Config.getDownloadPath(),
|
||||
Utils.standardizeFilename(mGalleryInfo.gid + '-' + mGalleryInfo.title));
|
||||
mDownloadDir.mkdirs();
|
||||
// TODO What if these is a file with the same name
|
||||
|
||||
mMainThread = new Thread(this);
|
||||
mRequstDetailPage = new LinkedList<Integer>();
|
||||
mRequstPage = new LinkedList<Integer>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void start() {
|
||||
mMainThread.start();
|
||||
}
|
||||
|
||||
private int getDetailPageForPageIndex(int pageIndex) {
|
||||
return pageIndex / mPagePerDp;
|
||||
}
|
||||
|
||||
private boolean parserDetailPage(HttpHelper hh, DetailParser dp, int page, int mode) {
|
||||
int times = 0;
|
||||
while(times++ < MAX_RETRY) {
|
||||
hh.reset();
|
||||
dp.reset();
|
||||
// For get more page url one time
|
||||
hh.setPreviewMode(Config.PREVIEW_MODE_NORMAL);
|
||||
String body = hh.get(EhClient.getDetailUrl(mGalleryInfo.gid, mGalleryInfo.token, page, Config.getMode()));
|
||||
if (body == null)
|
||||
continue;
|
||||
|
||||
if (dp.parser(body, mode) != mode)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main thread body
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
int detailPageMax = 0;
|
||||
HttpHelper hh = new HttpHelper(mContext);
|
||||
DetailParser dp = new DetailParser();
|
||||
|
||||
// Get detail info, pages, previewsum, previewPerPage
|
||||
if (parserDetailPage(hh, dp, 0, DetailParser.DETAIL &
|
||||
DetailParser.PREVIEW_INFO & DetailParser.PREVIEW)) {
|
||||
// Get Ok
|
||||
mPageSum = dp.pages;
|
||||
mPagePerDp = dp.previewPerPage;
|
||||
mPages = new PreviewList[dp.previewSum];
|
||||
mPages[0] = dp.previewList;
|
||||
detailPageMax = dp.pages / dp.previewPerPage;
|
||||
} else {
|
||||
// TODO Get error, do something
|
||||
}
|
||||
|
||||
int startPageIndex = mPageIndex;
|
||||
|
||||
// Start get
|
||||
for (downloadThreadNum = 0; downloadThreadNum < MAX_DOWNLOAD_THREAD_NUM; downloadThreadNum++) {
|
||||
new DownloadThread().start();
|
||||
}
|
||||
|
||||
// Start get other page from startindex
|
||||
int detailPage = getDetailPageForPageIndex(startPageIndex);
|
||||
// Already get page 0
|
||||
if (detailPage == 0)
|
||||
detailPage++;
|
||||
for (; detailPage <= detailPageMax; detailPage++) {
|
||||
if (parserDetailPage(hh, dp, detailPage, DetailParser.PREVIEW)) {
|
||||
mPages[detailPage] = dp.previewList;
|
||||
} else {
|
||||
// TODO Get error, do something
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class DownloadThread extends Thread {
|
||||
|
||||
private final HttpHelper mHttpHelper;
|
||||
|
||||
public DownloadThread() {
|
||||
mHttpHelper = new HttpHelper(mContext);
|
||||
}
|
||||
|
||||
private int getTargetPageIndex() {
|
||||
synchronized(mRequstPage) {
|
||||
if (mRequstPage.isEmpty()) {
|
||||
return mPageIndex++;
|
||||
} else {
|
||||
return mRequstPage.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
private String getImageUrlForPage() {
|
||||
int times = 0;
|
||||
while(times++ < MAX_RETRY) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public int downloadImage(String pageUrl, int index) {
|
||||
String body = mHttpHelper.get(pageUrl);
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
downloadThreadNum--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
private AppContext mAppContext;
|
||||
|
||||
private Data mData;
|
||||
private List<DownloadInfo> mDownloadList;
|
||||
private String[] mPageTokes;
|
||||
private DownloadInfo curDownloadInfo;
|
||||
private OnGalleryDownloadListener mListener;
|
||||
|
||||
private static DownloadInfo emptyDownloadInfo = new DownloadInfo(null);
|
||||
|
||||
public GallerysDownloader(AppContext appContext) {
|
||||
mAppContext = appContext;
|
||||
mPageTokes = null;
|
||||
mData = mAppContext.getData();
|
||||
mDownloadList = mData.getAllDownloads();
|
||||
}
|
||||
|
||||
public void setOnGalleryDownloadListener(OnGalleryDownloadListener l) {
|
||||
mListener = l;
|
||||
}
|
||||
|
||||
public synchronized void add(GalleryInfo galleryInfo) {
|
||||
DownloadInfo downloadInfo = new DownloadInfo(galleryInfo);
|
||||
mData.addDownload(downloadInfo);
|
||||
downloadInfo.setDownloadState(DownloadInfo.WAITING);
|
||||
|
||||
checkDownloadThread();
|
||||
}
|
||||
|
||||
public synchronized boolean start(int index) {
|
||||
DownloadInfo downloadInfo = mDownloadList.get(index);
|
||||
if (downloadInfo.getDownloadState() == DownloadInfo.NONE
|
||||
&& downloadInfo.getState() != DownloadInfo.COMPLETED) {
|
||||
downloadInfo.setDownloadState(DownloadInfo.WAITING);
|
||||
|
||||
checkDownloadThread();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void checkDownloadThread() {
|
||||
if (curDownloadInfo == null) {
|
||||
curDownloadInfo = emptyDownloadInfo;
|
||||
new Thread(new DownloadGallery()).start();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized DownloadInfo getFirstWaiting() {
|
||||
for (DownloadInfo downloadInfo : mDownloadList) {
|
||||
if (downloadInfo.getDownloadState() == DownloadInfo.WAITING)
|
||||
return downloadInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getUrlContext(HttpHelper hp, String url) {
|
||||
int times = 3;
|
||||
String pageContent;
|
||||
for (int i = 0; i < times; i++) {
|
||||
pageContent = hp.get(url);
|
||||
if (pageContent != null)
|
||||
return pageContent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class DownloadGallery implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if ((curDownloadInfo = getFirstWaiting()) != null) {
|
||||
curDownloadInfo.setDownloadState(DownloadInfo.DOWNLOADING);
|
||||
if (mListener != null)
|
||||
mListener.onGalleryDownloadStart(curDownloadInfo);
|
||||
|
||||
// get
|
||||
GalleryInfo galleryInfo = curDownloadInfo.getGalleryInfo();
|
||||
|
||||
HttpHelper hp = new HttpHelper(mAppContext);
|
||||
String url = EhClient.getDetailUrl(galleryInfo.gid, galleryInfo.token,
|
||||
curDownloadInfo.getMode());
|
||||
String pageContent = getUrlContext(hp, url);
|
||||
if (pageContent == null) {
|
||||
if (mListener != null)
|
||||
mListener.onGalleryDownloadFailed(curDownloadInfo, hp.getEMsg());
|
||||
// New turn
|
||||
new Thread(new DownloadGallery()).start();
|
||||
} else {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnGalleryDownloadListener {
|
||||
void onGalleryDownloadStart(DownloadInfo downloadInfo);
|
||||
void onGalleryDownloadStop(DownloadInfo downloadInfo);
|
||||
void onGalleryDownloadFailed(DownloadInfo downloadInfo, String eMsg);
|
||||
void onGalleryDownloadCompleted(DownloadInfo downloadInfo);
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -138,6 +138,7 @@ public class ImageLoader {
|
||||
loadTask = mDownloadTasks.pop();
|
||||
}
|
||||
|
||||
// TODO use proxy to get image
|
||||
loadTask.bitmap = httpHelper.getImage(loadTask.url);
|
||||
mImageCache.addBitmapToCache(loadTask.key, loadTask.bitmap);
|
||||
|
||||
|
||||
@@ -76,19 +76,17 @@ public class LargePreviewList extends PreviewList {
|
||||
|
||||
View view = inflater.inflate(R.layout.preview_item, null);
|
||||
((TextView)view.findViewById(R.id.text)).setText(String.valueOf(index + 1));
|
||||
final int finalIndex = index;
|
||||
final String url = item.mPageUrl;
|
||||
final int _index = index;
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Add to read in Data
|
||||
Intent intent = new Intent(mActivity,
|
||||
GalleryActivity.class);
|
||||
intent.putExtra("url", url);
|
||||
intent.putExtra("gid", mPi.getGid());
|
||||
intent.putExtra("title", mPi.getTitle());
|
||||
intent.putExtra("firstPage", finalIndex);
|
||||
//intent.putExtra("pageSum", mPi.pages);
|
||||
intent.putExtra(GalleryActivity.KEY_GID, mPi.getGid());
|
||||
intent.putExtra(GalleryActivity.KEY_TOKEN, mPi.getToken());
|
||||
intent.putExtra(GalleryActivity.KEY_TITLE, mPi.getTitle());
|
||||
intent.putExtra(GalleryActivity.KEY_START_INDEX, _index);
|
||||
mActivity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,19 +118,17 @@ public class NormalPreviewList extends PreviewList{
|
||||
|
||||
((TextView)view.findViewById(R.id.text)).setText(String.valueOf(index + 1));
|
||||
|
||||
final int finalIndex = index;
|
||||
final String url = item.url;
|
||||
final int _index = index;
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Add to read in Data
|
||||
Intent intent = new Intent(mActivity,
|
||||
GalleryActivity.class);
|
||||
intent.putExtra("url", url);
|
||||
intent.putExtra("gid", mPi.getGid());
|
||||
intent.putExtra("title", mPi.getTitle());
|
||||
intent.putExtra("firstPage", finalIndex);
|
||||
//intent.putExtra("pageSum", mGi.pages);
|
||||
intent.putExtra(GalleryActivity.KEY_GID, mPi.getGid());
|
||||
intent.putExtra(GalleryActivity.KEY_TOKEN, mPi.getToken());
|
||||
intent.putExtra(GalleryActivity.KEY_TITLE, mPi.getTitle());
|
||||
intent.putExtra(GalleryActivity.KEY_START_INDEX, _index);
|
||||
mActivity.startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.hippo.ehviewer.ehclient;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -45,16 +44,10 @@ import com.hippo.ehviewer.data.GalleryPopular;
|
||||
import com.hippo.ehviewer.data.ListUrls;
|
||||
import com.hippo.ehviewer.data.LofiGalleryDetail;
|
||||
import com.hippo.ehviewer.data.PreviewList;
|
||||
import com.hippo.ehviewer.gallery.data.ImageSet;
|
||||
import com.hippo.ehviewer.network.Downloader;
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
import com.hippo.ehviewer.service.DownloadService;
|
||||
import com.hippo.ehviewer.ui.DownloadInfo;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.Download;
|
||||
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.ThreadPool.Job;
|
||||
import com.hippo.ehviewer.util.ThreadPool.JobContext;
|
||||
@@ -982,341 +975,6 @@ public class EhClient {
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnDownloadMangaListener {
|
||||
public void onDownloadMangaStart(String id);
|
||||
public void onDownloadMangaStart(String id, int pageSum, int startIndex);
|
||||
public void onDownloadMangaStop(String id);
|
||||
public void onDownloadMangaOver(String id, boolean ok);
|
||||
|
||||
public void onDownloadPage(String id, int pageSum, int index);
|
||||
public void onDownloadPageProgress(String id, int pageSum,
|
||||
int index, float totalSize, float downloadSize);
|
||||
|
||||
public void onDownloadMangaAllStart();
|
||||
public void onDownloadMangaAllOver();
|
||||
}
|
||||
|
||||
// TODO 下载前检查文件是否已存在
|
||||
|
||||
/****** DownloadMangaManager ******/
|
||||
public class DownloadMangaManager {
|
||||
private DownloadInfo curDownloadInfo = null;
|
||||
private Downloader.Controlor curControlor = null;
|
||||
|
||||
private final ArrayList<DownloadInfo> mDownloadQueue = new ArrayList<DownloadInfo>();
|
||||
private final Object taskLock = new Object();
|
||||
private OnDownloadMangaListener listener;
|
||||
private DownloadService mService;
|
||||
|
||||
private final boolean mStop = false;
|
||||
|
||||
public void init() {
|
||||
|
||||
}
|
||||
|
||||
public void setOnDownloadMangaListener(OnDownloadMangaListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setDownloadService(DownloadService service) {
|
||||
this.mService = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a download task to task queue
|
||||
*
|
||||
* @param detailUrlStr
|
||||
* @param foldName
|
||||
* @return
|
||||
*/
|
||||
public void add(DownloadInfo di) {
|
||||
synchronized (taskLock) {
|
||||
mDownloadQueue.add(di);
|
||||
if (curDownloadInfo == null && mDownloadQueue.size() != 0)
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel(String id) {
|
||||
synchronized (taskLock) {
|
||||
DownloadInfo di = Download.get(id);
|
||||
if (di != null) {
|
||||
di.status = DownloadInfo.STOP;
|
||||
mDownloadQueue.remove(di);
|
||||
if (!id.equals(getCurDownloadId())) {
|
||||
if (curControlor != null)
|
||||
curControlor.stop();
|
||||
mService.notifyUpdate();
|
||||
Download.notify(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getCurDownloadId() {
|
||||
synchronized (taskLock) {
|
||||
if (curDownloadInfo == null)
|
||||
return null;
|
||||
else
|
||||
return curDownloadInfo.gid;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWaiting(String str) {
|
||||
synchronized (taskLock) {
|
||||
for (DownloadInfo di : mDownloadQueue) {
|
||||
if (di.gid.equals(str))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void start(){
|
||||
if(curDownloadInfo != null){
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDownloadMangaAllStart();
|
||||
|
||||
Parser parser = new Parser();
|
||||
Downloader imageDownloader = new Downloader(mAppContext);
|
||||
imageDownloader.setOnDownloadListener(new Downloader.OnDownloadListener() {
|
||||
@Override
|
||||
public void onDownloadStartConnect() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDownloadStartDownload(int totalSize) {
|
||||
if (curDownloadInfo != null) {
|
||||
curDownloadInfo.downloadSize = 0;
|
||||
curDownloadInfo.totalSize = totalSize/1024.0f;
|
||||
mService.notifyUpdate(curDownloadInfo.gid, curDownloadInfo.lastStartIndex, ImageSet.STATE_LOADING);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStatusUpdate(int downloadSize,
|
||||
int totalSize) {
|
||||
curDownloadInfo.downloadSize = downloadSize/1024.0f;
|
||||
curDownloadInfo.totalSize = totalSize/1024.0f;
|
||||
mService.notifyUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadOver(int status, String eMsg) {
|
||||
if (status == Downloader.COMPLETED)
|
||||
mService.notifyUpdate(curDownloadInfo.gid, curDownloadInfo.lastStartIndex, ImageSet.STATE_LOADED);
|
||||
else
|
||||
mService.notifyUpdate(curDownloadInfo.gid, curDownloadInfo.lastStartIndex, ImageSet.STATE_FAIL);
|
||||
}
|
||||
});
|
||||
while(mDownloadQueue.size() > 0){
|
||||
synchronized (taskLock) {
|
||||
curDownloadInfo = mDownloadQueue.get(0);
|
||||
curDownloadInfo.status = DownloadInfo.DOWNLOADING;
|
||||
mDownloadQueue.remove(0);
|
||||
}
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
|
||||
if (curDownloadInfo.type == DownloadInfo.DETAIL_URL) {
|
||||
listener.onDownloadMangaStart(curDownloadInfo.gid);
|
||||
mService.notifyUpdate();
|
||||
if (parser.getFirstPagePageSumForDetail(curDownloadInfo.detailUrlStr)) { // Get page info
|
||||
curDownloadInfo.type = DownloadInfo.PAGE_URL;
|
||||
curDownloadInfo.lastStartIndex = 0;
|
||||
curDownloadInfo.pageSum = parser.getPageSum();
|
||||
curDownloadInfo.pageUrlStr = parser.getFirstPage();
|
||||
Download.notify(curDownloadInfo.gid);
|
||||
|
||||
} else { // If get info error
|
||||
listener.onDownloadMangaOver(curDownloadInfo.gid, false);
|
||||
curDownloadInfo.status = DownloadInfo.FAILED;
|
||||
mService.notifyUpdate();
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Create folder
|
||||
File folder = new File(Config.getDownloadPath() + File.separatorChar + Utils.standardizeFilename(curDownloadInfo.title)); // TODO For title contain invailed char
|
||||
if (!folder.mkdirs() && !folder.isDirectory()) {
|
||||
listener.onDownloadMangaOver(curDownloadInfo.gid, false);
|
||||
curDownloadInfo.status = DownloadInfo.FAILED;
|
||||
mService.notifyUpdate();
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
continue;
|
||||
}
|
||||
|
||||
String nextPage = curDownloadInfo.pageUrlStr;
|
||||
curDownloadInfo.pageUrlStr = null;
|
||||
String imageUrlStr = null;
|
||||
boolean mComplete = true;
|
||||
boolean mStop = false;
|
||||
// Get page
|
||||
while (!nextPage.equals(curDownloadInfo.pageUrlStr)) {
|
||||
curDownloadInfo.pageUrlStr = nextPage;
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
listener.onDownloadPage(curDownloadInfo.gid, curDownloadInfo.pageSum, curDownloadInfo.lastStartIndex);
|
||||
mService.notifyUpdate();
|
||||
|
||||
if (parser.getPageInfoSumForPage(curDownloadInfo.pageUrlStr)) {
|
||||
nextPage = parser.getNextPage();
|
||||
imageUrlStr = parser.getImageUrlStr();
|
||||
|
||||
// Check stop
|
||||
if (curDownloadInfo.status == DownloadInfo.STOP) {
|
||||
listener.onDownloadMangaStop(curDownloadInfo.gid);
|
||||
mService.notifyUpdate();
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
mComplete = false;
|
||||
mStop = true;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO
|
||||
|
||||
Log.d(TAG, folder.getPath());
|
||||
|
||||
curControlor = imageDownloader.resetData(folder.getPath(),
|
||||
String.format("%05d", curDownloadInfo.lastStartIndex + 1) + "." + Utils.getExtension(imageUrlStr),
|
||||
imageUrlStr);
|
||||
imageDownloader.run();
|
||||
|
||||
curDownloadInfo.downloadSize = 0;
|
||||
curDownloadInfo.totalSize = 0;
|
||||
|
||||
int downloadStatus = imageDownloader.getStatus();
|
||||
if (downloadStatus == Downloader.STOPED) { // If stop by user
|
||||
listener.onDownloadMangaStop(curDownloadInfo.gid);
|
||||
mService.notifyUpdate();
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
mComplete = false;
|
||||
mStop = true;
|
||||
break;
|
||||
} else if (downloadStatus != Downloader.COMPLETED) { // If get image error
|
||||
Log.e(TAG, "Download image error, downloadStatus is " + downloadStatus);
|
||||
mComplete = false;
|
||||
break;
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
mComplete = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
mComplete = false;
|
||||
break;
|
||||
}
|
||||
curDownloadInfo.lastStartIndex++;
|
||||
}
|
||||
if (mComplete) {
|
||||
listener.onDownloadMangaOver(curDownloadInfo.gid, true);
|
||||
curDownloadInfo.status = DownloadInfo.COMPLETED;
|
||||
mService.notifyUpdate();
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
} else if (!mStop) {
|
||||
listener.onDownloadMangaOver(curDownloadInfo.gid, false);
|
||||
curDownloadInfo.status = DownloadInfo.FAILED;
|
||||
mService.notifyUpdate();
|
||||
Download.notify(String.valueOf(curDownloadInfo.gid));
|
||||
}
|
||||
}
|
||||
synchronized (taskLock) {
|
||||
curDownloadInfo = null;
|
||||
listener.onDownloadMangaAllOver();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
synchronized (taskLock) {
|
||||
return curDownloadInfo == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Parser {
|
||||
|
||||
private int errorMegId;
|
||||
|
||||
private int pageSum;
|
||||
private String firstPage;
|
||||
|
||||
private String prePage;
|
||||
private String nextPage;
|
||||
private String imageUrlStr;
|
||||
|
||||
|
||||
public int getPageSum() {
|
||||
return pageSum;
|
||||
}
|
||||
|
||||
public String getFirstPage() {
|
||||
return firstPage;
|
||||
}
|
||||
|
||||
public String getPrePage() {
|
||||
return prePage;
|
||||
}
|
||||
|
||||
public String getNextPage() {
|
||||
return nextPage;
|
||||
}
|
||||
|
||||
public String getImageUrlStr() {
|
||||
return imageUrlStr;
|
||||
}
|
||||
|
||||
public boolean getFirstPagePageSumForDetail(String detailUrlStr) {
|
||||
HttpHelper hp = new HttpHelper(mAppContext);
|
||||
String pageContent = hp.get(detailUrlStr);
|
||||
if (pageContent == null)
|
||||
return false;
|
||||
|
||||
Pattern p = Pattern.compile("<p class=\"ip\">Showing [\\d|,]+ - [\\d|,]+ of ([\\d|,]+) images</p>.+<div id=\"gdt\"><div[^<>]*>(?:<div[^<>]*>)?<a[^<>]*href=\"([^<>\"]+)\"[^<>]*>");
|
||||
Matcher m = p.matcher(pageContent);
|
||||
if (m.find()) {
|
||||
pageSum = Integer.parseInt(m.group(1).replace(",", ""));
|
||||
firstPage = m.group(2);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page info, previous page, next page, image url
|
||||
*
|
||||
* @param pageUrlStr
|
||||
* @return True if get
|
||||
*/
|
||||
public boolean getPageInfoSumForPage(String pageUrlStr) {
|
||||
HttpHelper hp = new HttpHelper(mAppContext);
|
||||
String pageContent = hp.get(pageUrlStr);
|
||||
if (pageContent == null)
|
||||
return false;
|
||||
|
||||
Pattern p = Pattern.compile("<a[^<>]*id=\"prev\"[^<>]*href=\"([^<>\"]+)\"><img[^<>]*/></a>.+<a[^<>]id=\"next\"[^<>]*href=\"([^<>\"]+)\"><img[^<>]*/></a>.+<img[^<>]*src=\"([^<>\"]+?)\"[^<>]*style=\"[^<>\"]*\"[^<>]*/>");
|
||||
Matcher m = p.matcher(pageContent);
|
||||
if (m.find()) {
|
||||
prePage = m.group(1);
|
||||
nextPage = m.group(2);
|
||||
imageUrlStr = Utils.unescapeXml(m.group(3));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Post comment
|
||||
public interface OnCommentListener {
|
||||
void onSuccess(List<Comment> comments);
|
||||
|
||||
@@ -23,8 +23,10 @@ import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -33,7 +35,7 @@ import android.os.Process;
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.network.HttpHelper;
|
||||
import com.hippo.ehviewer.util.AutoExpandArray;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.EhUtils;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
@@ -44,55 +46,74 @@ public class ExDownloader implements Runnable {
|
||||
private static final int WORKER_NUM = 3;
|
||||
|
||||
private final Context mContext;
|
||||
private final ExDownloadManager mManager;
|
||||
private final ExDownloaderManager mManager;
|
||||
|
||||
private final int mGid;
|
||||
private final String mToken;
|
||||
private final String mTitle;
|
||||
private final int mMode;
|
||||
private int mPreviewPageNum = -1;
|
||||
private int mPreviewPerPage = -1;
|
||||
private int mImageNum = -1;
|
||||
private volatile int mPreviewPageNum = -1;
|
||||
private volatile int mPreviewPerPage = -1;
|
||||
private volatile int mImageNum = -1;
|
||||
private final AutoExpandArray<String> mPageTokeArray = new AutoExpandArray<String>();
|
||||
private final AutoExpandArray<String> mImageFilenameArray = new AutoExpandArray<String>();
|
||||
|
||||
private volatile int mOwnerNum = 0;
|
||||
private Thread mMainThread = null;
|
||||
/** Only work for EhClient.MODE_LOFI **/
|
||||
/** Only work for EhClient.MODE_LOFI when do not know image number **/
|
||||
private int mCurMaxPreviewPage = -1;
|
||||
|
||||
private volatile int mStartIndex = 0;
|
||||
private final Queue<Integer> mRequestIndexArray = new ConcurrentLinkedQueue<Integer>();
|
||||
private final Queue<Integer> mNoTokenIndexArray = new ConcurrentLinkedQueue<Integer>();
|
||||
private final Queue<Integer> mRequestPageIndexArray = new ConcurrentLinkedQueue<Integer>();
|
||||
|
||||
/** The set contains the requsting index **/
|
||||
private final Set<Integer> mRequstingIndexSet = new HashSet<Integer>();
|
||||
|
||||
private volatile int mCurRequestPageIndex = -1;
|
||||
|
||||
private final Worker[] mWorkerArray = new Worker[WORKER_NUM];
|
||||
private final HttpHelper.DownloadControlor[] mControlorArray = new HttpHelper.DownloadControlor[WORKER_NUM];
|
||||
|
||||
private final Object mPageTokenLock = new Object();
|
||||
private final Object mWorkerLock = new Object();
|
||||
private final Object mWorkerGetIndexLock = new Object();
|
||||
|
||||
private volatile boolean mPauseWork = false;
|
||||
private volatile boolean mStopWork = false;
|
||||
|
||||
private final File mDir;
|
||||
private ListenerForImageSet mLfis;
|
||||
|
||||
private static final int IO_BUFFER_SIZE = 8 * 1024;
|
||||
|
||||
public interface ListenerForImageSet {
|
||||
public void onDownloadStart(int index);
|
||||
public void onDownloading(int index, float percent);
|
||||
public void onDownloadComplete(int index);
|
||||
public void onDownloadFail(int index);
|
||||
}
|
||||
|
||||
public interface ListenerForDownload {
|
||||
public void onDownloading(int index);
|
||||
}
|
||||
|
||||
ExDownloader(int gid, String token, String title, int mode) {
|
||||
mContext = AppContext.getInstance();
|
||||
mManager = ExDownloadManager.getInstance();
|
||||
mManager = ExDownloaderManager.getInstance();
|
||||
mGid = gid;
|
||||
mToken = token;
|
||||
mTitle = title;
|
||||
mMode = mode;
|
||||
|
||||
// Make sure dir
|
||||
mDir = new File(Config.getDownloadPath(),
|
||||
Utils.standardizeFilename(mGid + "-" + mTitle));
|
||||
if (!mDir.exists()) {
|
||||
mDir.mkdirs();
|
||||
} else if (mDir.isFile()) {
|
||||
mDir.delete();
|
||||
mDir.mkdirs();
|
||||
}
|
||||
mDir = EhUtils.getGalleryDir(mGid, mTitle);
|
||||
Utils.ensureDir(mDir, true);
|
||||
}
|
||||
|
||||
public void setListenerForImageSet(ListenerForImageSet l) {
|
||||
mLfis = l;
|
||||
}
|
||||
|
||||
void occupy() {
|
||||
@@ -107,6 +128,39 @@ public class ExDownloader implements Runnable {
|
||||
return mOwnerNum == 0;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
// TODO stop all thread, release all resources
|
||||
mStopWork = true;
|
||||
// Stop download
|
||||
for (HttpHelper.DownloadControlor c : mControlorArray) {
|
||||
if (c != null)
|
||||
c.stop();
|
||||
}
|
||||
// Wake all thread
|
||||
synchronized(mPageTokenLock) {mPageTokenLock.notifyAll();}
|
||||
synchronized(mWorkerGetIndexLock) {mWorkerGetIndexLock.notifyAll();}
|
||||
synchronized(mWorkerLock) {mWorkerLock.notifyAll();}
|
||||
}
|
||||
|
||||
void pause(boolean pause) {
|
||||
mPauseWork = pause;
|
||||
if (mPauseWork) {
|
||||
// Stop download
|
||||
for (HttpHelper.DownloadControlor c : mControlorArray)
|
||||
if (c != null) c.stop();
|
||||
// Wake all thread
|
||||
synchronized(mPageTokenLock) {mPageTokenLock.notifyAll();}
|
||||
synchronized(mWorkerGetIndexLock) {mWorkerGetIndexLock.notifyAll();}
|
||||
synchronized(mWorkerLock) {mWorkerLock.notifyAll();}
|
||||
} else {
|
||||
// Reset download
|
||||
for (HttpHelper.DownloadControlor c : mControlorArray)
|
||||
if (c != null) c.reset();
|
||||
ensureStart();
|
||||
ensureWorkers();
|
||||
}
|
||||
}
|
||||
|
||||
public int getGid() {
|
||||
return mGid;
|
||||
}
|
||||
@@ -116,7 +170,7 @@ public class ExDownloader implements Runnable {
|
||||
}
|
||||
|
||||
public void setStartIndex(int startIndex) {
|
||||
if (mImageNum != -1 && mStartIndex >= mImageNum)
|
||||
if (mImageNum != -1 && startIndex >= mImageNum)
|
||||
mStartIndex = 0;
|
||||
else
|
||||
mStartIndex = startIndex;
|
||||
@@ -125,14 +179,49 @@ public class ExDownloader implements Runnable {
|
||||
ensureWorkers();
|
||||
}
|
||||
|
||||
public void setTargetIndex(int startIndex) {
|
||||
if (mImageNum != -1 && mStartIndex >= mImageNum) {
|
||||
mRequestIndexArray.offer(startIndex);
|
||||
ensureStart();
|
||||
ensureWorkers();
|
||||
public void setTargetIndex(int index) {
|
||||
if (mImageNum != -1 && index >= mImageNum)
|
||||
return;
|
||||
|
||||
mRequestIndexArray.offer(index);
|
||||
ensureStart();
|
||||
ensureWorkers();
|
||||
}
|
||||
|
||||
public int getMaxEnsureIndex() {
|
||||
if (mImageNum != -1) {
|
||||
return mImageNum;
|
||||
} else {
|
||||
return Math.max(Math.max(mStartIndex - 1,
|
||||
Math.max(mCurMaxPreviewPage, mPreviewPageNum - 1) * mPreviewPerPage),
|
||||
mPageTokeArray.maxValidIndex());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If do not know image num, return -1.
|
||||
* You should call getMaxEnsureIndex().
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getImageNum() {
|
||||
return mImageNum;
|
||||
}
|
||||
|
||||
public AutoExpandArray<String> getImageFilenameArray() {
|
||||
return mImageFilenameArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* If targer index image is downloading, return true
|
||||
*
|
||||
* @param index
|
||||
* @return
|
||||
*/
|
||||
public boolean isDownloading(int index) {
|
||||
return mRequstingIndexSet.contains(index);
|
||||
}
|
||||
|
||||
private synchronized void ensureStart() {
|
||||
if (mMainThread != null)
|
||||
return;
|
||||
@@ -196,10 +285,14 @@ public class ExDownloader implements Runnable {
|
||||
}
|
||||
|
||||
mImageNum = Integer.parseInt(Utils.readAsciiLine(is));
|
||||
if (mImageNum != -1)
|
||||
if (mImageNum != -1) {
|
||||
mPageTokeArray.setCapacity(mImageNum);
|
||||
else if (mPreviewPageNum != -1 && mPreviewPerPage != -1)
|
||||
mImageFilenameArray.setCapacity(mImageNum);
|
||||
}
|
||||
else if (mPreviewPageNum != -1 && mPreviewPerPage != -1) {
|
||||
mPageTokeArray.setCapacity(mPreviewPageNum * mPreviewPerPage);
|
||||
mImageFilenameArray.setCapacity(mPreviewPageNum * mPreviewPerPage);
|
||||
}
|
||||
|
||||
// read page token info
|
||||
try {
|
||||
@@ -288,6 +381,7 @@ public class ExDownloader implements Runnable {
|
||||
mPreviewPageNum = edp.previewPageNum;
|
||||
mImageNum = edp.imageNum;
|
||||
mPageTokeArray.setCapacity(mPreviewPageNum * mPreviewPerPage);
|
||||
mImageFilenameArray.setCapacity(mPreviewPageNum * mPreviewPerPage);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < pageTokenArray.size(); i++)
|
||||
@@ -301,27 +395,21 @@ public class ExDownloader implements Runnable {
|
||||
}
|
||||
|
||||
private void ensureWorkers() {
|
||||
if (mPreviewPerPage == -1)
|
||||
return;
|
||||
|
||||
synchronized(mWorkerArray) {
|
||||
for (int i = 0; i < mWorkerArray.length; i++) {
|
||||
if (mWorkerArray[i] == null) {
|
||||
Worker worker = new Worker();
|
||||
Worker worker = new Worker(i);
|
||||
worker.start();
|
||||
mWorkerArray[i] = worker;
|
||||
mControlorArray[i] = new HttpHelper.DownloadControlor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxEnsureIndex() {
|
||||
if (mImageNum != -1) {
|
||||
return mImageNum;
|
||||
} else {
|
||||
return Math.max(Math.max(mStartIndex - 1,
|
||||
Math.max(mCurMaxPreviewPage, mPreviewPageNum - 1) * mPreviewPerPage),
|
||||
mPageTokeArray.maxValidIndex());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO for download , when to claim download completed
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -337,7 +425,10 @@ public class ExDownloader implements Runnable {
|
||||
|
||||
// A loop to get page token
|
||||
while (true) {
|
||||
Log.d(TAG, "A new loop to get page token");
|
||||
|
||||
// Check stop
|
||||
if (mStopWork)
|
||||
break;
|
||||
|
||||
int noTokenIndex = -1;
|
||||
int pageIndex = -1;
|
||||
@@ -357,6 +448,9 @@ public class ExDownloader implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "noTokenIndex = " + noTokenIndex);
|
||||
Log.d(TAG, "pageIndex = " + pageIndex);
|
||||
|
||||
if (noTokenIndex != -1) {
|
||||
if (mPageTokeArray.get(noTokenIndex) == null) {
|
||||
getDetailInfo(noTokenIndex / mPreviewPerPage, ediFile, false);
|
||||
@@ -373,18 +467,22 @@ public class ExDownloader implements Runnable {
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// TODO handle error here
|
||||
|
||||
} finally {
|
||||
synchronized (this) {
|
||||
mMainThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "ExDownloader over");
|
||||
}
|
||||
|
||||
private class Worker extends Thread {
|
||||
|
||||
public Worker() {
|
||||
private final int mIndex;
|
||||
|
||||
public Worker(int index) {
|
||||
super();
|
||||
mIndex = index;
|
||||
setPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
}
|
||||
|
||||
@@ -408,12 +506,20 @@ public class ExDownloader implements Runnable {
|
||||
private int getTargetIndex() {
|
||||
synchronized(mWorkerGetIndexLock) {
|
||||
while (true) {
|
||||
|
||||
// Check stop
|
||||
if (mStopWork || mPauseWork)
|
||||
return -1;
|
||||
|
||||
if (!mRequestIndexArray.isEmpty()) {
|
||||
return mRequestIndexArray.poll();
|
||||
} else if (mImageNum != -1 && mStartIndex == mImageNum) {
|
||||
return -1;
|
||||
} else if (mImageNum == -1 && mStartIndex > getMaxEnsureIndex()) {
|
||||
// If do not sure the index is valid
|
||||
|
||||
Log.d(TAG, "mPreviewPerPage = " + mPreviewPerPage);
|
||||
|
||||
addRequstPageIndex(getMaxEnsureIndex() / mPreviewPerPage);
|
||||
try {
|
||||
mWorkerGetIndexLock.wait();
|
||||
@@ -425,47 +531,57 @@ public class ExDownloader implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private String getExtension(String name, String defautl) {
|
||||
int index = name.lastIndexOf('.');
|
||||
if (index == -1 || index == name.length() - 1)
|
||||
return defautl;
|
||||
else
|
||||
return name.substring(index + 1);
|
||||
}
|
||||
|
||||
private String getImageName(int index, String url) {
|
||||
return String.format("%08d.%s", index + 1, getExtension(url, "jpg"));
|
||||
}
|
||||
|
||||
private String[] getPossibleFilenames(int index) {
|
||||
String prefix = String.format("%08d.", index + 1);
|
||||
return new String[]{prefix + "jpg", prefix + "jpeg", prefix + "png", prefix + "gif"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
HttpDownloadListener listener = new HttpDownloadListener();
|
||||
HttpHelper hh = new HttpHelper(mContext);
|
||||
ImagePageParser ipp = new ImagePageParser();
|
||||
|
||||
while (true) {
|
||||
// Check stop
|
||||
if (mStopWork || mPauseWork)
|
||||
break;
|
||||
|
||||
int targetIndex = getTargetIndex();
|
||||
if (targetIndex == -1)
|
||||
break;
|
||||
|
||||
// Check is the index is requsted
|
||||
if (mRequstingIndexSet.contains(targetIndex))
|
||||
continue;
|
||||
|
||||
// Check is this already downloaded
|
||||
boolean isAlreadyDownloaded = false;
|
||||
for (String possibleFilename : getPossibleFilenames(targetIndex)) {
|
||||
File file = new File(mDir, possibleFilename);
|
||||
if (file.exists()) {
|
||||
// First check mImageFilenameArray
|
||||
String imageFilename = mImageFilenameArray.get(targetIndex);
|
||||
if (imageFilename != null) {
|
||||
File file = new File(mDir, imageFilename);
|
||||
if (file.exists())
|
||||
isAlreadyDownloaded = true;
|
||||
break;
|
||||
} else {
|
||||
// Can not find image file name, just guess
|
||||
for (String possibleFilename : EhUtils.getPossibleImageFilenames(targetIndex)) {
|
||||
File file = new File(mDir, possibleFilename);
|
||||
if (file.exists()) {
|
||||
isAlreadyDownloaded = true;
|
||||
mImageFilenameArray.set(targetIndex, possibleFilename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isAlreadyDownloaded)
|
||||
continue;
|
||||
|
||||
String pageToken;
|
||||
mRequstingIndexSet.add(targetIndex);
|
||||
|
||||
listener.index = targetIndex;
|
||||
|
||||
String pageToken = null;
|
||||
while (true) {
|
||||
// Check stop
|
||||
if (mStopWork || mPauseWork)
|
||||
break;
|
||||
|
||||
pageToken = mPageTokeArray.get(targetIndex);
|
||||
if (pageToken == null) {
|
||||
addNoTokenIndex(targetIndex);
|
||||
@@ -474,75 +590,106 @@ public class ExDownloader implements Runnable {
|
||||
catch (InterruptedException e) {}
|
||||
}
|
||||
} else {
|
||||
// Jump out of loop
|
||||
// Get valid page token, jump out of loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// i == 1, add "?nl=48"
|
||||
hh.reset();
|
||||
ipp.reset();
|
||||
String body = hh.get(EhClient.getPageUrl(mGid, pageToken, targetIndex + 1, mMode)
|
||||
+ (i == 1 ? "?nl=48" : ""));
|
||||
if (ipp.parser(body)) {
|
||||
String imageUrl = ipp.imageUrl;
|
||||
String filename = getImageName(targetIndex, imageUrl);
|
||||
for (int j = 0; j < 2; j++) {
|
||||
// j == 0, use proxy
|
||||
|
||||
}
|
||||
} else {
|
||||
// TODO parser error, Do somthing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Parser image page
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int i = 0; i < 2 && !mStopWork && !mPauseWork; i++) {
|
||||
hh.reset();
|
||||
ipp.reset();
|
||||
String body = hh.get(EhClient.getPageUrl(mGid, pageToken, targetIndex + 1, mMode)
|
||||
+ (i == 1 ? "?nl=48" : ""));
|
||||
if (ipp.parser(body)) {
|
||||
if (ipp.parser(body, mMode)) {
|
||||
// Download image
|
||||
HttpHelper.DownloadControlor c = mControlorArray[mIndex];
|
||||
String imageUrl = ipp.imageUrl;
|
||||
String filename = getImageName(targetIndex, imageUrl);
|
||||
String filename = EhUtils.getImageFilename(targetIndex, Utils.getExtension(imageUrl, "jpg"));
|
||||
// Just put filename to mImageFilenameArray
|
||||
mImageFilenameArray.set(targetIndex, filename);
|
||||
hh.reset();
|
||||
if (hh.download(imageUrl, mDir, filename, false, null, null) == null) {
|
||||
if (hh.download(imageUrl, mDir, filename, false, c, listener) == null) {
|
||||
hh.reset();
|
||||
if (hh.download(imageUrl, mDir, filename, true, null, null) == null) {
|
||||
if (hh.download(imageUrl, mDir, filename, true, c, listener) == null) {
|
||||
if (i == 1) {
|
||||
// TODO download error
|
||||
Log.d(TAG, "download error");
|
||||
}
|
||||
} else {
|
||||
// download ok
|
||||
Log.d(TAG, "download ok proxy");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// download ok
|
||||
Log.d(TAG, "download ok");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO parser error, Do somthing
|
||||
Log.d(TAG, "parser error");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from requsting set
|
||||
mRequstingIndexSet.remove(targetIndex);
|
||||
}
|
||||
|
||||
// Worker over
|
||||
synchronized(mWorkerArray) {
|
||||
// Remove from mWorkerArray
|
||||
for (int i = 0; i < mWorkerArray.length; i++) {
|
||||
if (mWorkerArray[i] == this) {
|
||||
mWorkerArray[i] = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mWorkerArray[mIndex] = null;
|
||||
mControlorArray[mIndex] = null;
|
||||
}
|
||||
Log.d(TAG, "Worker over");
|
||||
Log.d(TAG, "ExDownloader Worker over");
|
||||
}
|
||||
}
|
||||
|
||||
public class HttpDownloadListener implements HttpHelper.OnDownloadListener {
|
||||
|
||||
public int index;
|
||||
private float lastPercent;
|
||||
|
||||
@Override
|
||||
public void onDownloadStartConnect() {
|
||||
lastPercent = 0.0f;
|
||||
|
||||
if (mLfis != null)
|
||||
mLfis.onDownloadStart(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStartDownload(int totalSize) {
|
||||
lastPercent = 0.0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStatusUpdate(int downloadSize, int totalSize) {
|
||||
if (totalSize == -1)
|
||||
return;
|
||||
|
||||
float percent = (float)downloadSize / totalSize;
|
||||
if (percent - lastPercent >= 0.05f) {
|
||||
lastPercent = percent;
|
||||
|
||||
if (mLfis != null)
|
||||
mLfis.onDownloading(index, percent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadOver(int status, String eMsg) {
|
||||
if (status == HttpHelper.DOWNLOAD_OK_CODE)
|
||||
mRequstingIndexSet.remove(index);
|
||||
|
||||
if (mLfis != null && status == HttpHelper.DOWNLOAD_OK_CODE)
|
||||
mLfis.onDownloadComplete(index);
|
||||
else if (mLfis != null && status == HttpHelper.DOWNLOAD_FAIL_CODE)
|
||||
mLfis.onDownloadFail(index);
|
||||
|
||||
lastPercent = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-7
@@ -23,25 +23,25 @@ import android.util.SparseArray;
|
||||
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
|
||||
public class ExDownloadManager {
|
||||
public class ExDownloaderManager {
|
||||
|
||||
private static final String TAG = ExDownloadManager.class.getSimpleName();
|
||||
private static final String TAG = ExDownloaderManager.class.getSimpleName();
|
||||
|
||||
private final Context mContext;
|
||||
private final SparseArray<ExDownloader> mExDownloadList;
|
||||
private final File mExDownloadInfoDir;
|
||||
|
||||
private static ExDownloadManager sInstance;
|
||||
private static ExDownloaderManager sInstance;
|
||||
|
||||
public static void createInstance() {
|
||||
sInstance = new ExDownloadManager();
|
||||
sInstance = new ExDownloaderManager();
|
||||
}
|
||||
|
||||
public static ExDownloadManager getInstance() {
|
||||
public static ExDownloaderManager getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private ExDownloadManager() {
|
||||
private ExDownloaderManager() {
|
||||
mContext = AppContext.getInstance();
|
||||
mExDownloadList = new SparseArray<ExDownloader>();
|
||||
// Init download info dir
|
||||
@@ -70,7 +70,9 @@ public class ExDownloadManager {
|
||||
|
||||
public synchronized void freeExDownloader(ExDownloader exDownloader) {
|
||||
exDownloader.free();
|
||||
if (exDownloader.isOrphans())
|
||||
if (exDownloader.isOrphans()) {
|
||||
exDownloader.stop();
|
||||
mExDownloadList.remove(exDownloader.getGid());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,19 +33,42 @@ public class ImagePageParser {
|
||||
token = null;
|
||||
}
|
||||
|
||||
public boolean parser(String body) {
|
||||
Pattern p = Pattern.compile("<div id=\"i3\">.+?<img id=\"img\" src=\"(.+?)\""
|
||||
+ ".+?"
|
||||
+ "<div id=\"i5\"><div class=\"sb\"><a href=\"(?:http|https)://.+?/g/(\\d+)/(\\w+)");
|
||||
Matcher m = p.matcher(body);
|
||||
public boolean parser(String body, int mode) {
|
||||
Pattern p;
|
||||
Matcher m;
|
||||
|
||||
if (mode ==EhClient.MODE_LOFI) {
|
||||
p = Pattern.compile("<img id=\"sm\" src=\"([^\"]+)\"[^>]+>");
|
||||
m = p.matcher(body);
|
||||
if (m.find())
|
||||
imageUrl = Utils.unescapeXml(m.group(1));
|
||||
else
|
||||
return false;
|
||||
|
||||
p = Pattern.compile("<a href=\"(?:http|https)://.+?/g/(\\d+)/(\\w+)/?\">Back</a>");
|
||||
m = p.matcher(body);
|
||||
if (m.find()) {
|
||||
gid = Integer.parseInt(m.group(1));
|
||||
token = m.group(2);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m.find()) {
|
||||
imageUrl = Utils.unescapeXml(m.group(1));
|
||||
gid = Integer.parseInt(m.group(2));
|
||||
token = m.group(3);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
p = Pattern.compile("<div id=\"i3\">.+?<img id=\"img\" src=\"(.+?)\""
|
||||
+ ".+?"
|
||||
+ "<div id=\"i5\"><div class=\"sb\"><a href=\"(?:http|https)://.+?/g/(\\d+)/(\\w+)");
|
||||
m = p.matcher(body);
|
||||
|
||||
if (m.find()) {
|
||||
imageUrl = Utils.unescapeXml(m.group(1));
|
||||
gid = Integer.parseInt(m.group(2));
|
||||
token = m.group(3);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
import com.hippo.ehviewer.util.MathUtils;
|
||||
|
||||
public class AlphaAnimation extends CanvasAnimation {
|
||||
private final float mStartAlpha;
|
||||
@@ -42,7 +42,7 @@ public class AlphaAnimation extends CanvasAnimation {
|
||||
|
||||
@Override
|
||||
protected void onCalculate(float progress) {
|
||||
mCurrentAlpha = Utils.clamp(mStartAlpha
|
||||
mCurrentAlpha = MathUtils.clamp(mStartAlpha
|
||||
+ (mEndAlpha - mStartAlpha) * progress, 0f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.util.MathUtils;
|
||||
|
||||
// Animation calculates a value according to the current input time.
|
||||
//
|
||||
@@ -81,7 +81,7 @@ abstract public class Animation {
|
||||
if (mStartTime == NO_ANIMATION) return false;
|
||||
if (mStartTime == ANIMATION_START) mStartTime = currentTimeMillis;
|
||||
int elapse = (int) (currentTimeMillis - mStartTime);
|
||||
float x = Utils.clamp((float) elapse / mDuration, 0f, 1f);
|
||||
float x = MathUtils.clamp((float) elapse / mDuration, 0f, 1f);
|
||||
Interpolator i = mInterpolator;
|
||||
onCalculate(i != null ? i.getInterpolation(x) : x);
|
||||
if (elapse >= mDuration) mStartTime = NO_ANIMATION;
|
||||
|
||||
@@ -1,340 +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.hippo.ehviewer.gallery.common;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.text.TextUtils;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
public class Utils {
|
||||
private static final String TAG = "Utils";
|
||||
private static final String DEBUG_TAG = "GalleryDebug";
|
||||
|
||||
private static final long POLY64REV = 0x95AC9329AC4BC9B5L;
|
||||
private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;
|
||||
|
||||
private static long[] sCrcTable = new long[256];
|
||||
|
||||
private static final boolean IS_DEBUG_BUILD =
|
||||
Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug");
|
||||
|
||||
private static final String MASK_STRING = "********************************";
|
||||
|
||||
// Throws AssertionError if the input is false.
|
||||
public static void assertTrue(boolean cond) {
|
||||
if (!cond) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
// Throws AssertionError with the message. We had a method having the form
|
||||
// assertTrue(boolean cond, String message, Object ... args);
|
||||
// However a call to that method will cause memory allocation even if the
|
||||
// condition is false (due to autoboxing generated by "Object ... args"),
|
||||
// so we don't use that anymore.
|
||||
public static void fail(String message, Object ... args) {
|
||||
throw new AssertionError(
|
||||
args.length == 0 ? message : String.format(message, args));
|
||||
}
|
||||
|
||||
// Throws NullPointerException if the input is null.
|
||||
public static <T> T checkNotNull(T object) {
|
||||
if (object == null) throw new NullPointerException();
|
||||
return object;
|
||||
}
|
||||
|
||||
// Returns true if two input Object are both null or equal
|
||||
// to each other.
|
||||
public static boolean equals(Object a, Object b) {
|
||||
return (a == b) || (a == null ? false : a.equals(b));
|
||||
}
|
||||
|
||||
// Returns the next power of two.
|
||||
// Returns the input if it is already power of 2.
|
||||
// Throws IllegalArgumentException if the input is <= 0 or
|
||||
// the answer overflows.
|
||||
public static int nextPowerOf2(int n) {
|
||||
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
|
||||
n -= 1;
|
||||
n |= n >> 16;
|
||||
n |= n >> 8;
|
||||
n |= n >> 4;
|
||||
n |= n >> 2;
|
||||
n |= n >> 1;
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
// Returns the previous power of two.
|
||||
// Returns the input if it is already power of 2.
|
||||
// Throws IllegalArgumentException if the input is <= 0
|
||||
public static int prevPowerOf2(int n) {
|
||||
if (n <= 0) throw new IllegalArgumentException();
|
||||
return Integer.highestOneBit(n);
|
||||
}
|
||||
|
||||
// Returns the input value x clamped to the range [min, max].
|
||||
public static int clamp(int x, int min, int max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
// Returns the input value x clamped to the range [min, max].
|
||||
public static float clamp(float x, float min, float max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
// Returns the input value x clamped to the range [min, max].
|
||||
public static long clamp(long x, long min, long max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
public static boolean isOpaque(int color) {
|
||||
return color >>> 24 == 0xFF;
|
||||
}
|
||||
|
||||
public static void swap(int[] array, int i, int j) {
|
||||
int temp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function thats returns a 64-bit crc for string
|
||||
*
|
||||
* @param in input string
|
||||
* @return a 64-bit crc value
|
||||
*/
|
||||
public static final long crc64Long(String in) {
|
||||
if (in == null || in.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return crc64Long(getBytes(in));
|
||||
}
|
||||
|
||||
static {
|
||||
// http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c
|
||||
long part;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
part = i;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
long x = ((int) part & 1) != 0 ? POLY64REV : 0;
|
||||
part = (part >> 1) ^ x;
|
||||
}
|
||||
sCrcTable[i] = part;
|
||||
}
|
||||
}
|
||||
|
||||
public static final long crc64Long(byte[] buffer) {
|
||||
long crc = INITIALCRC;
|
||||
for (int k = 0, n = buffer.length; k < n; ++k) {
|
||||
crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
public static byte[] getBytes(String in) {
|
||||
byte[] result = new byte[in.length() * 2];
|
||||
int output = 0;
|
||||
for (char ch : in.toCharArray()) {
|
||||
result[output++] = (byte) (ch & 0xFF);
|
||||
result[output++] = (byte) (ch >> 8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void closeSilently(Closeable c) {
|
||||
if (c == null) return;
|
||||
try {
|
||||
c.close();
|
||||
} catch (IOException t) {
|
||||
Log.w(TAG, "close fail ", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static int compare(long a, long b) {
|
||||
return a < b ? -1 : a == b ? 0 : 1;
|
||||
}
|
||||
|
||||
public static int ceilLog2(float value) {
|
||||
int i;
|
||||
for (i = 0; i < 31; i++) {
|
||||
if ((1 << i) >= value) break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static int floorLog2(float value) {
|
||||
int i;
|
||||
for (i = 0; i < 31; i++) {
|
||||
if ((1 << i) > value) break;
|
||||
}
|
||||
return i - 1;
|
||||
}
|
||||
|
||||
public static void closeSilently(ParcelFileDescriptor fd) {
|
||||
try {
|
||||
if (fd != null) fd.close();
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "fail to close", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void closeSilently(Cursor cursor) {
|
||||
try {
|
||||
if (cursor != null) cursor.close();
|
||||
} catch (Throwable t) {
|
||||
Log.w(TAG, "fail to close", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static float interpolateAngle(
|
||||
float source, float target, float progress) {
|
||||
// interpolate the angle from source to target
|
||||
// We make the difference in the range of [-179, 180], this is the
|
||||
// shortest path to change source to target.
|
||||
float diff = target - source;
|
||||
if (diff < 0) diff += 360f;
|
||||
if (diff > 180) diff -= 360f;
|
||||
|
||||
float result = source + diff * progress;
|
||||
return result < 0 ? result + 360f : result;
|
||||
}
|
||||
|
||||
public static float interpolateScale(
|
||||
float source, float target, float progress) {
|
||||
return source + progress * (target - source);
|
||||
}
|
||||
|
||||
public static String ensureNotNull(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
public static float parseFloatSafely(String content, float defaultValue) {
|
||||
if (content == null) return defaultValue;
|
||||
try {
|
||||
return Float.parseFloat(content);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static int parseIntSafely(String content, int defaultValue) {
|
||||
if (content == null) return defaultValue;
|
||||
try {
|
||||
return Integer.parseInt(content);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNullOrEmpty(String exifMake) {
|
||||
return TextUtils.isEmpty(exifMake);
|
||||
}
|
||||
|
||||
public static void waitWithoutInterrupt(Object object) {
|
||||
try {
|
||||
object.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Log.w(TAG, "unexpected interrupt: " + object);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean handleInterrruptedException(Throwable e) {
|
||||
// A helper to deal with the interrupt exception
|
||||
// If an interrupt detected, we will setup the bit again.
|
||||
if (e instanceof InterruptedIOException
|
||||
|| e instanceof InterruptedException) {
|
||||
Thread.currentThread().interrupt();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String with special XML characters escaped.
|
||||
*/
|
||||
public static String escapeXml(String s) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0, len = s.length(); i < len; ++i) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '<': sb.append("<"); break;
|
||||
case '>': sb.append(">"); break;
|
||||
case '\"': sb.append("""); break;
|
||||
case '\'': sb.append("'"); break;
|
||||
case '&': sb.append("&"); break;
|
||||
default: sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getUserAgent(Context context) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (NameNotFoundException e) {
|
||||
throw new IllegalStateException("getPackageInfo failed");
|
||||
}
|
||||
return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s",
|
||||
packageInfo.packageName,
|
||||
packageInfo.versionName,
|
||||
Build.BRAND,
|
||||
Build.DEVICE,
|
||||
Build.MODEL,
|
||||
Build.ID,
|
||||
Build.VERSION.SDK_INT,
|
||||
Build.VERSION.RELEASE,
|
||||
Build.VERSION.INCREMENTAL);
|
||||
}
|
||||
|
||||
public static String[] copyOf(String[] source, int newSize) {
|
||||
String[] result = new String[newSize];
|
||||
newSize = Math.min(source.length, newSize);
|
||||
System.arraycopy(source, 0, result, 0, newSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Mask information for debugging only. It returns <code>info.toString()</code> directly
|
||||
// for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****")
|
||||
// in release build to protect the information (e.g. for privacy issue).
|
||||
public static String maskDebugInfo(Object info) {
|
||||
if (info == null) return null;
|
||||
String s = info.toString();
|
||||
int length = Math.min(s.length(), MASK_STRING.length());
|
||||
return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length);
|
||||
}
|
||||
|
||||
// This method should be ONLY used for debugging.
|
||||
public static void debug(String message, Object ... args) {
|
||||
Log.v(DEBUG_TAG, String.format(message, args));
|
||||
}
|
||||
}
|
||||
@@ -1,60 +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.gallery.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import com.hippo.ehviewer.service.DownloadService;
|
||||
|
||||
public class DownloadImageSet extends ImageSet {
|
||||
|
||||
public DownloadImageSet(Context context, int gid, File folder, int size,
|
||||
int startIndex, int endIndex, Set<Integer> failIndexSet) {
|
||||
super(context, gid, folder, size, startIndex, endIndex, failIndexSet);
|
||||
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(DownloadService.ACTION_UPDATE);
|
||||
mContext.registerReceiver(mReceiver, filter);
|
||||
}
|
||||
|
||||
public void unregisterReceiver() {
|
||||
mContext.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
private BroadcastReceiver mReceiver = new BroadcastReceiver(){
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String gid;
|
||||
if(intent.getAction().equals(DownloadService.ACTION_UPDATE)
|
||||
&& (gid = intent.getStringExtra(DownloadService.KEY_GID)) != null
|
||||
&& gid.equals(mGid)) {
|
||||
int index = intent.getIntExtra(DownloadService.KEY_INDEX, -1);
|
||||
int state = intent.getIntExtra(DownloadService.KEY_STATE, -1);
|
||||
if (index != -1 && state != -1) {
|
||||
changeState(index, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -19,244 +19,252 @@ package com.hippo.ehviewer.gallery.data;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Set;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Movie;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.hippo.ehviewer.util.Future;
|
||||
import com.hippo.ehviewer.util.FutureListener;
|
||||
import com.hippo.ehviewer.util.ThreadPool;
|
||||
import com.hippo.ehviewer.util.ThreadPool.Job;
|
||||
import com.hippo.ehviewer.util.ThreadPool.JobContext;
|
||||
import com.hippo.ehviewer.AppHandler;
|
||||
import com.hippo.ehviewer.ehclient.ExDownloader;
|
||||
import com.hippo.ehviewer.ehclient.ExDownloaderManager;
|
||||
import com.hippo.ehviewer.util.AutoExpandArray;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
import com.hippo.ehviewer.util.EhUtils;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
// TODO 初期建立一个线程检索文件夹中所有文件以寻找到所有图片文件
|
||||
|
||||
/**
|
||||
* @author Hippo
|
||||
*
|
||||
* ImageSet 用于记录漫画的下载信息
|
||||
* 对于已完成下载的则无特殊之处。
|
||||
* 对于未完成下载的要记录,开始下载之处,目前下载截止之处,
|
||||
* 以及下载失败之处。
|
||||
* 同时拥有检查该目录识别图片文件。
|
||||
*/
|
||||
public class ImageSet {
|
||||
public class ImageSet implements ExDownloader.ListenerForImageSet {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "ImageSet";
|
||||
private static final String TAG = ImageSet.class.getSimpleName();
|
||||
|
||||
private static final String GIF_EXTENSION = "gif";
|
||||
/**
|
||||
* These are code reture by getImage()
|
||||
*/
|
||||
public static final int RESULT_NONE = 0x0;
|
||||
public static final int RESULT_DOWNLOADING = 0x1;
|
||||
public static final int RESULT_DECODE = 0x2;
|
||||
|
||||
public static final int TYPE_NONE = 0x0;
|
||||
public static final int TYPE_BITMAP = 0x1;
|
||||
public static final int TYPE_MOVIE = 0x2;
|
||||
private final ExDownloaderManager mEdManager;
|
||||
private final ExDownloader mExDownloader;
|
||||
|
||||
public static final int INVALID_ID = -1;
|
||||
private final File mDir;
|
||||
private final AutoExpandArray<String> mImageFilenameArray;
|
||||
private ImageListener mListener;
|
||||
private final Queue<DecodeInfo> mDecodeQueue = new ConcurrentLinkedQueue<DecodeInfo>();
|
||||
private final SparseArray<Float> mPercentMap = new SparseArray<Float>(5);
|
||||
private final DecodeWorker mDecodeWorker;
|
||||
/** If true, then wake Worker, worker will stop **/
|
||||
private volatile boolean mStopWork = false;
|
||||
|
||||
public static final int STATE_NONE = 0;
|
||||
public static final int STATE_LOADING = 1;
|
||||
public static final int STATE_LOADED = 2;
|
||||
public static final int STATE_FAIL = 3;
|
||||
public class DecodeInfo {
|
||||
public int index;
|
||||
public String filename;
|
||||
|
||||
protected Context mContext;
|
||||
protected int mGid;
|
||||
protected File mFolder;
|
||||
protected int mSize;
|
||||
|
||||
private final SparseArray<ImageData> mImagesDate;
|
||||
|
||||
private final ThreadPool mThreadPool;
|
||||
private OnStateChangeListener mOnStateChangeListener;
|
||||
|
||||
public interface OnStateChangeListener {
|
||||
void onStateChange(int index, int state);
|
||||
public DecodeInfo(int index, String filename) {
|
||||
this.index = index;
|
||||
this.filename = filename;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnDecodeOverListener {
|
||||
public interface ImageListener {
|
||||
/**
|
||||
* May return Bitmap or Movie
|
||||
* It will be invoke when ExDownloader download successful
|
||||
*
|
||||
* @param res
|
||||
* @param index
|
||||
*/
|
||||
void onDecodeOver(Object res, int index);
|
||||
void onGetImage(int index);
|
||||
|
||||
void onDownloading(int index, float percent);
|
||||
|
||||
/**
|
||||
* It will be invoke when decode image over
|
||||
*
|
||||
* @param index
|
||||
* @param res
|
||||
*/
|
||||
void onDecodeOver(int index, Object res);
|
||||
}
|
||||
|
||||
private class ImageData {
|
||||
int state;
|
||||
String fileName;
|
||||
int type;
|
||||
public ImageSet(int gid, String token, String title, int startIndex) {
|
||||
|
||||
public ImageData() {
|
||||
state = STATE_NONE;
|
||||
fileName = null;
|
||||
type = TYPE_NONE;
|
||||
}
|
||||
mDir = EhUtils.getGalleryDir(gid, title);
|
||||
Utils.ensureDir(mDir, true);
|
||||
|
||||
// Init ExDownloader
|
||||
mEdManager = ExDownloaderManager.getInstance();
|
||||
mExDownloader = mEdManager.getExDownloader(gid, token, title, Config.getMode());
|
||||
mExDownloader.setStartIndex(startIndex);
|
||||
mExDownloader.setListenerForImageSet(this);
|
||||
|
||||
mImageFilenameArray = mExDownloader.getImageFilenameArray();
|
||||
|
||||
// Start decode worker
|
||||
mDecodeWorker = new DecodeWorker();
|
||||
mDecodeWorker.start();
|
||||
}
|
||||
|
||||
// [startIndex, endIndex)
|
||||
public ImageSet(Context context, int gid, File folder, int size, int startIndex, int endIndex,
|
||||
Set<Integer> failIndexSet) {
|
||||
|
||||
if (folder == null || !folder.isDirectory())
|
||||
size = 0;
|
||||
|
||||
|
||||
// TODO
|
||||
/*
|
||||
if (folder == null)
|
||||
throw new IllegalArgumentException("Folder is null");
|
||||
if (!folder.isDirectory())
|
||||
throw new IllegalArgumentException("Folder is not directory, path is " + folder.getPath());
|
||||
*/
|
||||
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
if (endIndex > size)
|
||||
endIndex = size;
|
||||
if (startIndex < 0)
|
||||
startIndex = 0;
|
||||
if (startIndex > endIndex)
|
||||
startIndex = endIndex;
|
||||
|
||||
// TODO
|
||||
/*
|
||||
if (size < 0 || startIndex < 0
|
||||
|| startIndex > endIndex || endIndex > size)
|
||||
throw new IllegalArgumentException("size or index value error, size = "
|
||||
+ size + ", startIndex = " + startIndex
|
||||
+ ", endIndex = " + endIndex + ", path is " + folder.getPath());
|
||||
*/
|
||||
|
||||
mContext = context;
|
||||
mGid = gid;
|
||||
mFolder = folder;
|
||||
mSize = size;
|
||||
|
||||
// TODO sometimes size is too large
|
||||
mImagesDate = new SparseArray<ImageData>(mSize);
|
||||
int i = 0;
|
||||
for (; i < startIndex; i++) {
|
||||
mImagesDate.append(i, new ImageData());
|
||||
}
|
||||
for (; i < endIndex; i++) {
|
||||
if (failIndexSet != null && failIndexSet.contains(i)) {
|
||||
ImageData imageData = new ImageData();
|
||||
imageData.state = STATE_FAIL;
|
||||
mImagesDate.append(i, imageData);
|
||||
}
|
||||
else {
|
||||
// Get file name only when you need it
|
||||
ImageData imageData = new ImageData();
|
||||
imageData.state = STATE_LOADED;
|
||||
mImagesDate.append(i, imageData);
|
||||
}
|
||||
}
|
||||
for (; i < size; i++) {
|
||||
mImagesDate.append(i, new ImageData());
|
||||
}
|
||||
|
||||
mThreadPool = new ThreadPool(1, 1);
|
||||
public boolean isStop() {
|
||||
return mStopWork;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return mSize;
|
||||
return mExDownloader.getImageNum();
|
||||
}
|
||||
|
||||
public void setOnStateChangeListener(OnStateChangeListener l) {
|
||||
mOnStateChangeListener = l;
|
||||
public int getEnsureSize() {
|
||||
return mExDownloader.getMaxEnsureIndex() + 1;
|
||||
}
|
||||
|
||||
public int getImage(final int index, final OnDecodeOverListener listener) {
|
||||
final ImageData imageData = mImagesDate.get(index);
|
||||
int state;
|
||||
if (imageData == null)
|
||||
state = INVALID_ID;
|
||||
else
|
||||
state = imageData.state;
|
||||
if (state == STATE_LOADED && listener != null) {
|
||||
mThreadPool.submit(new Job<Object>() {
|
||||
@Override
|
||||
public Object run(JobContext jc) {
|
||||
Object res = null;
|
||||
if (imageData.fileName == null
|
||||
&& !getFileForName(getFileNameForIndex(index), imageData))
|
||||
return null;
|
||||
public void setImageListener(ImageListener l) {
|
||||
mListener = l;
|
||||
}
|
||||
|
||||
File file = new File(mFolder, imageData.fileName);
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(file);
|
||||
if (imageData.type == TYPE_BITMAP) {
|
||||
BitmapFactory.Options opt = new BitmapFactory.Options();
|
||||
// TODO why only ARGB_8888 always work well, other may slit image
|
||||
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
res = BitmapFactory.decodeStream(fis, null, opt);
|
||||
} else if (imageData.type == TYPE_MOVIE) {
|
||||
res = Movie.decodeStream(fis);
|
||||
} else {
|
||||
// TYPE_NONE or something else, get error
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (fis != null)
|
||||
Utils.closeQuietly(fis);
|
||||
}
|
||||
return res;
|
||||
public void setTargetIndex(int index) {
|
||||
mExDownloader.setTargetIndex(index);
|
||||
}
|
||||
|
||||
public Object getImage(int index) {
|
||||
// Try to get file name
|
||||
String filename = mImageFilenameArray.get(index);
|
||||
if (filename == null) {
|
||||
// Just guess filename
|
||||
for (String possibleFilename : EhUtils.getPossibleImageFilenames(index)) {
|
||||
File file = new File(mDir, possibleFilename);
|
||||
if (file.exists()) {
|
||||
// Get filename
|
||||
filename = possibleFilename;
|
||||
mImageFilenameArray.set(index, filename);
|
||||
break;
|
||||
}
|
||||
}, new FutureListener<Object>() {
|
||||
@Override
|
||||
public void onFutureDone(Future<Object> future) {
|
||||
listener.onDecodeOver(future.get(), index);
|
||||
}
|
||||
});
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public String getFileNameForIndex(int index) {
|
||||
return String.format("%05d", index + 1);
|
||||
}
|
||||
|
||||
public boolean getFileForName(String name, ImageData imageData) {
|
||||
String[] list = mFolder.list();
|
||||
for (String item : list) {
|
||||
if(name.equals(Utils.getName(item))) {
|
||||
imageData.fileName = item;
|
||||
if (Utils.getExtension(item).toLowerCase().equals(GIF_EXTENSION))
|
||||
imageData.type = TYPE_MOVIE;
|
||||
else
|
||||
imageData.type = TYPE_BITMAP;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void changeState(int index, int state) {
|
||||
final ImageData imageData = mImagesDate.get(index);
|
||||
if(imageData != null) {
|
||||
imageData.state = state;
|
||||
if (mOnStateChangeListener != null) {
|
||||
mOnStateChangeListener.onStateChange(index, state);
|
||||
}
|
||||
Float percent;
|
||||
if ((percent = mPercentMap.get(index)) != null) {
|
||||
return percent;
|
||||
} else if (mExDownloader.isDownloading(index)) {
|
||||
// downloading
|
||||
return RESULT_DOWNLOADING;
|
||||
} else if (filename == null || !new File(mDir, filename).exists()) {
|
||||
// Target index has not being downloading
|
||||
return RESULT_NONE;
|
||||
} else {
|
||||
mDecodeQueue.offer(new DecodeInfo(index, filename));
|
||||
// wake decode worker
|
||||
synchronized(mDecodeQueue) {mDecodeQueue.notify();}
|
||||
return RESULT_DECODE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* mSize is faithful
|
||||
*
|
||||
* You must call it when you do not need it any more
|
||||
*/
|
||||
public void scan() {
|
||||
String[] fileList = mFolder.list();
|
||||
public void free() {
|
||||
mStopWork = true;
|
||||
// wake decode worker to wash wash sleep
|
||||
synchronized(mDecodeQueue) {mDecodeQueue.notify();}
|
||||
|
||||
// Remove listener for ImageSet
|
||||
mExDownloader.setListenerForImageSet(null);
|
||||
// Free ExDownloader
|
||||
mEdManager.freeExDownloader(mExDownloader);
|
||||
}
|
||||
|
||||
private class DecodeWorker extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
DecodeInfo decodeInfo;
|
||||
while (true) {
|
||||
// Check stop work or not
|
||||
if (mStopWork)
|
||||
break;
|
||||
|
||||
// Get decode task
|
||||
synchronized (mDecodeQueue) {
|
||||
if (mDecodeQueue.isEmpty()) {
|
||||
try {
|
||||
mDecodeQueue.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
continue;
|
||||
}
|
||||
decodeInfo = mDecodeQueue.poll();
|
||||
}
|
||||
|
||||
// do decode
|
||||
final int index = decodeInfo.index;
|
||||
String filename = decodeInfo.filename;
|
||||
boolean isGif = Utils.getExtension(filename, "jpg").equals("gif");
|
||||
FileInputStream fis;
|
||||
Object res = null;
|
||||
try {
|
||||
fis = new FileInputStream(new File(mDir, filename));
|
||||
if (isGif) {
|
||||
res = Movie.decodeStream(fis);
|
||||
} else {
|
||||
BitmapFactory.Options opt = new BitmapFactory.Options();
|
||||
// TODO why only ARGB_8888 always work well, other may slit image
|
||||
opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
res = BitmapFactory.decodeStream(fis, null, opt);
|
||||
}
|
||||
} catch (FileNotFoundException e) {}
|
||||
|
||||
final Object _res = res;
|
||||
// Post to UI thread
|
||||
AppHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mListener != null)
|
||||
mListener.onDecodeOver(index, _res);
|
||||
}
|
||||
});
|
||||
}
|
||||
Log.d(TAG, "DecodeWorker stop working");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadStart(final int index) {
|
||||
AppHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mListener != null)
|
||||
mListener.onDownloading(index, 0.0f);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloading(final int index, final float percent) {
|
||||
mPercentMap.append(index, percent);
|
||||
|
||||
AppHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mListener != null)
|
||||
mListener.onDownloading(index, percent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadComplete(final int index) {
|
||||
mPercentMap.remove(index);
|
||||
|
||||
AppHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mListener != null)
|
||||
mListener.onGetImage(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadFail(int index) {
|
||||
mPercentMap.remove(index);
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,13 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.RectF;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.MathUtils;
|
||||
|
||||
// BasicTexture is a Texture corresponds to a real GL texture.
|
||||
// The state of a BasicTexture indicates whether its data is loaded to GL memory.
|
||||
// If a BasicTexture is loaded into GL memory, it has a GL texture id.
|
||||
@@ -79,8 +79,8 @@ public abstract class BasicTexture implements Texture {
|
||||
public void setSize(int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
|
||||
mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
|
||||
mTextureWidth = width > 0 ? MathUtils.nextPowerOf2(width) : 0;
|
||||
mTextureHeight = height > 0 ? MathUtils.nextPowerOf2(height) : 0;
|
||||
if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
|
||||
Log.w(TAG, String.format("texture is too large: %d x %d",
|
||||
mTextureWidth, mTextureHeight), new Exception());
|
||||
@@ -138,16 +138,16 @@ public abstract class BasicTexture implements Texture {
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
canvas.drawTexture(this, x, y, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
canvas.drawTexture(this, x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
public void draw(GLCanvas canvas, RectF source, RectF target) {
|
||||
canvas.drawTexture(this, source, target);
|
||||
}
|
||||
|
||||
|
||||
// onBind is called before GLCanvas binds this texture.
|
||||
// It should make sure the data is uploaded to GL memory.
|
||||
abstract protected boolean onBind(GLCanvas canvas);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
// ColorTexture is a texture which fills the rectangle with the specified color.
|
||||
public class ColorTexture implements Texture {
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.ui.AnimationTime;
|
||||
import com.hippo.ehviewer.util.MathUtils;
|
||||
|
||||
// FadeTexture is a texture which fades the given texture along the time.
|
||||
public abstract class FadeTexture implements Texture {
|
||||
@@ -72,7 +72,7 @@ public abstract class FadeTexture implements Texture {
|
||||
|
||||
protected float getRatio() {
|
||||
float r = (float)(now() - mStartTime) / DURATION;
|
||||
return Utils.clamp(1.0f - r, 0.0f, 1.0f);
|
||||
return MathUtils.clamp(1.0f - r, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private long now() {
|
||||
|
||||
@@ -16,19 +16,6 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLU;
|
||||
import android.opengl.GLUtils;
|
||||
import android.opengl.Matrix;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.util.IntArray;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -40,6 +27,18 @@ import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11Ext;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLU;
|
||||
import android.opengl.GLUtils;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
import com.hippo.ehviewer.util.IntArray;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
public class GLES11Canvas implements GLCanvas {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "GLCanvasImp";
|
||||
@@ -54,7 +53,7 @@ public class GLES11Canvas implements GLCanvas {
|
||||
0, 0, 1, 1, // used for drawing a line
|
||||
0, 0, 0, 1, 1, 1, 1, 0}; // used for drawing the outline of a rectangle
|
||||
|
||||
private GL11 mGL;
|
||||
private final GL11 mGL;
|
||||
|
||||
private final float mMatrixValues[] = new float[16];
|
||||
private final float mTextureMatrixValues[] = new float[16];
|
||||
@@ -65,9 +64,9 @@ public class GLES11Canvas implements GLCanvas {
|
||||
|
||||
private final float mTextureColor[] = new float[4];
|
||||
|
||||
private int mBoxCoords;
|
||||
private final int mBoxCoords;
|
||||
|
||||
private GLState mGLState;
|
||||
private final GLState mGLState;
|
||||
private final ArrayList<RawTexture> mTargetStack = new ArrayList<RawTexture>();
|
||||
|
||||
private float mAlpha;
|
||||
@@ -81,8 +80,8 @@ public class GLES11Canvas implements GLCanvas {
|
||||
private final IntArray mDeleteBuffers = new IntArray();
|
||||
private int mScreenWidth;
|
||||
private int mScreenHeight;
|
||||
private boolean mBlendEnabled = true;
|
||||
private int mFrameBuffer[] = new int[1];
|
||||
private final boolean mBlendEnabled = true;
|
||||
private final int mFrameBuffer[] = new int[1];
|
||||
private static float[] sCropRect = new float[4];
|
||||
|
||||
private RawTexture mTargetTexture;
|
||||
@@ -592,7 +591,7 @@ public class GLES11Canvas implements GLCanvas {
|
||||
private int mTextureTarget = GL11.GL_TEXTURE_2D;
|
||||
private boolean mBlendEnabled = true;
|
||||
private float mLineWidth = 1.0f;
|
||||
private boolean mLineSmooth = false;
|
||||
private final boolean mLineSmooth = false;
|
||||
|
||||
public GLState(GL11 gl) {
|
||||
mGL = gl;
|
||||
@@ -959,7 +958,7 @@ public class GLES11Canvas implements GLCanvas {
|
||||
mGL.glBindTexture(target, texture.getId());
|
||||
GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap) {
|
||||
int target = texture.getTarget();
|
||||
|
||||
@@ -15,16 +15,6 @@
|
||||
*/
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import android.opengl.Matrix;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.util.IntArray;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
@@ -32,6 +22,16 @@ import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
import com.hippo.ehviewer.util.IntArray;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
public class GLES20Canvas implements GLCanvas {
|
||||
// ************** Constants **********************
|
||||
private static final String TAG = GLES20Canvas.class.getSimpleName();
|
||||
@@ -138,7 +138,7 @@ public class GLES20Canvas implements GLCanvas {
|
||||
// Keep track of restore state
|
||||
private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
|
||||
private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
|
||||
private IntArray mSaveFlags = new IntArray();
|
||||
private final IntArray mSaveFlags = new IntArray();
|
||||
|
||||
private int mCurrentAlphaIndex = 0;
|
||||
private int mCurrentMatrixIndex = 0;
|
||||
@@ -148,20 +148,20 @@ public class GLES20Canvas implements GLCanvas {
|
||||
private int mHeight;
|
||||
|
||||
// Projection matrix
|
||||
private float[] mProjectionMatrix = new float[MATRIX_SIZE];
|
||||
private final float[] mProjectionMatrix = new float[MATRIX_SIZE];
|
||||
|
||||
// Screen size for when we aren't bound to a texture
|
||||
private int mScreenWidth;
|
||||
private int mScreenHeight;
|
||||
|
||||
// GL programs
|
||||
private int mDrawProgram;
|
||||
private int mTextureProgram;
|
||||
private int mOesTextureProgram;
|
||||
private int mMeshProgram;
|
||||
private final int mDrawProgram;
|
||||
private final int mTextureProgram;
|
||||
private final int mOesTextureProgram;
|
||||
private final int mMeshProgram;
|
||||
|
||||
// GL buffer containing BOX_COORDINATES
|
||||
private int mBoxCoordinates;
|
||||
private final int mBoxCoordinates;
|
||||
|
||||
// Handle indices -- common
|
||||
private static final int INDEX_POSITION = 0;
|
||||
@@ -251,10 +251,10 @@ public class GLES20Canvas implements GLCanvas {
|
||||
|
||||
// Buffer for framebuffer IDs -- we keep track so we can switch the attached
|
||||
// texture.
|
||||
private int[] mFrameBuffer = new int[1];
|
||||
private final int[] mFrameBuffer = new int[1];
|
||||
|
||||
// Bound textures.
|
||||
private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
|
||||
private final ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
|
||||
|
||||
// Temporary variables used within calculations
|
||||
private final float[] mTempMatrix = new float[32];
|
||||
@@ -935,9 +935,11 @@ public class GLES20Canvas implements GLCanvas {
|
||||
int target = texture.getTarget();
|
||||
GLES20.glBindTexture(target, texture.getId());
|
||||
checkError();
|
||||
GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
|
||||
// TODO Maybe multi-thread error
|
||||
if (!bitmap.isRecycled())
|
||||
GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap) {
|
||||
int target = texture.getTarget();
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import com.hippo.ehviewer.util.MathUtils;
|
||||
|
||||
// NinePatchTexture is a texture backed by a NinePatch resource.
|
||||
//
|
||||
@@ -36,7 +36,7 @@ public class NinePatchTexture extends ResourceTexture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "NinePatchTexture";
|
||||
private NinePatchChunk mChunk;
|
||||
private SmallCache<NinePatchInstance> mInstanceCache
|
||||
private final SmallCache<NinePatchInstance> mInstanceCache
|
||||
= new SmallCache<NinePatchInstance>();
|
||||
|
||||
public NinePatchTexture(Context context, int resId) {
|
||||
@@ -80,8 +80,8 @@ public class NinePatchTexture extends ResourceTexture {
|
||||
private static class SmallCache<V> {
|
||||
private static final int CACHE_SIZE = 16;
|
||||
private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2;
|
||||
private int[] mKey = new int[CACHE_SIZE];
|
||||
private V[] mValue = (V[]) new Object[CACHE_SIZE];
|
||||
private final int[] mKey = new int[CACHE_SIZE];
|
||||
private final V[] mValue = (V[]) new Object[CACHE_SIZE];
|
||||
private int mCount; // number of items in this cache
|
||||
|
||||
// Puts a value into the cache. If the cache is full, also returns
|
||||
@@ -262,7 +262,7 @@ class NinePatchInstance {
|
||||
*/
|
||||
private static int stretch(
|
||||
float x[], float u[], int div[], int source, int target) {
|
||||
int textureSize = Utils.nextPowerOf2(source);
|
||||
int textureSize = MathUtils.nextPowerOf2(source);
|
||||
float textureBound = (float) source / textureSize;
|
||||
|
||||
float stretch = 0;
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.opengl.GLUtils;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import junit.framework.Assert;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.opengl.GLUtils;
|
||||
|
||||
// UploadedTextures use a Bitmap for the content of the texture.
|
||||
//
|
||||
// Subclasses should implement onGetBitmap() to provide the Bitmap and
|
||||
@@ -211,12 +210,10 @@ public abstract class UploadedTexture extends BasicTexture {
|
||||
private void uploadToCanvas(GLCanvas canvas) {
|
||||
|
||||
Bitmap bitmap = getBitmap();
|
||||
if (bitmap != null) {
|
||||
if (bitmap != null && !bitmap.isRecycled()) {
|
||||
try {
|
||||
int bWidth = bitmap.getWidth();
|
||||
int bHeight = bitmap.getHeight();
|
||||
int width = bWidth + mBorder * 2;
|
||||
int height = bHeight + mBorder * 2;
|
||||
int texWidth = getTextureWidth();
|
||||
int texHeight = getTextureHeight();
|
||||
|
||||
@@ -232,7 +229,7 @@ public abstract class UploadedTexture extends BasicTexture {
|
||||
int format = GLUtils.getInternalFormat(bitmap);
|
||||
int type = GLUtils.getType(bitmap);
|
||||
Config config = bitmap.getConfig();
|
||||
|
||||
|
||||
canvas.initializeTextureSize(this, format, type);
|
||||
canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
|
||||
|
||||
|
||||
@@ -16,6 +16,15 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
@@ -26,15 +35,11 @@ import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.util.AttributeSet;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.gallery.anim.CanvasAnimation;
|
||||
import com.hippo.ehviewer.gallery.common.ApiHelper;
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.BasicTexture;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLES11Canvas;
|
||||
@@ -42,15 +47,8 @@ import com.hippo.ehviewer.gallery.glrenderer.GLES20Canvas;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.UploadedTexture;
|
||||
import com.hippo.ehviewer.gallery.util.GalleryUtils;
|
||||
import com.hippo.ehviewer.gallery.util.MotionEventHelper;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
// The root component of all <code>GLView</code>s. The rendering is done in GL
|
||||
// thread while the event handling is done in the main thread. To synchronize
|
||||
@@ -89,7 +87,7 @@ public class GLRootView extends GLSurfaceView
|
||||
private int mCompensation;
|
||||
// mCompensationMatrix maps the coordinates of touch events. It is kept sync
|
||||
// with mCompensation.
|
||||
private Matrix mCompensationMatrix = new Matrix();
|
||||
private final Matrix mCompensationMatrix = new Matrix();
|
||||
private int mDisplayRotation;
|
||||
|
||||
private int mFlags = FLAG_NEED_LAYOUT;
|
||||
@@ -198,7 +196,7 @@ public class GLRootView extends GLSurfaceView
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable mRequestRenderOnAnimationFrame = new Runnable() {
|
||||
private final Runnable mRequestRenderOnAnimationFrame = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
superRequestRender();
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.hippo.ehviewer.gallery.anim.CanvasAnimation;
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import com.hippo.ehviewer.util.Utils;
|
||||
|
||||
// GLView is a UI component. It can render to a GLCanvas and accept touch
|
||||
// events. A GLView may have zero or more child GLView and they form a tree
|
||||
|
||||
@@ -16,46 +16,12 @@
|
||||
|
||||
package com.hippo.ehviewer.gallery.util;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.hippo.ehviewer.util.ThreadPool.CancelListener;
|
||||
import com.hippo.ehviewer.util.ThreadPool.JobContext;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
public class GalleryUtils {
|
||||
private static final String TAG = "GalleryUtils";
|
||||
private static final String MAPS_PACKAGE_NAME = "com.google.android.apps.maps";
|
||||
private static final String MAPS_CLASS_NAME = "com.google.android.maps.MapsActivity";
|
||||
|
||||
public static final String MIME_TYPE_IMAGE = "image/*";
|
||||
public static final String MIME_TYPE_VIDEO = "video/*";
|
||||
public static final String MIME_TYPE_PANORAMA360 = "application/vnd.google.panorama360+jpg";
|
||||
public static final String MIME_TYPE_ALL = "*/*";
|
||||
|
||||
private static float sPixelDensity = -1f;
|
||||
|
||||
public static void initialize(Context context) {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
WindowManager wm = (WindowManager)
|
||||
context.getSystemService(Context.WINDOW_SERVICE);
|
||||
wm.getDefaultDisplay().getMetrics(metrics);
|
||||
sPixelDensity = metrics.density;
|
||||
}
|
||||
private static final String TAG = GalleryUtils.class.getSimpleName();
|
||||
|
||||
public static float[] intColorToFloatARGBArray(int from) {
|
||||
return new float[] {
|
||||
@@ -66,29 +32,6 @@ public class GalleryUtils {
|
||||
};
|
||||
}
|
||||
|
||||
public static float dpToPixel(float dp) {
|
||||
return sPixelDensity * dp;
|
||||
}
|
||||
|
||||
public static int dpToPixel(int dp) {
|
||||
return Math.round(dpToPixel((float) dp));
|
||||
}
|
||||
|
||||
public static int meterToPixel(float meter) {
|
||||
// 1 meter = 39.37 inches, 1 inch = 160 dp.
|
||||
return Math.round(dpToPixel(meter * 39.37f * 160));
|
||||
}
|
||||
|
||||
public static byte[] getBytes(String in) {
|
||||
byte[] result = new byte[in.length() * 2];
|
||||
int output = 0;
|
||||
for (char ch : in.toCharArray()) {
|
||||
result[output++] = (byte) (ch & 0xFF);
|
||||
result[output++] = (byte) (ch >> 8);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Below are used the detect using database in the render thread. It only
|
||||
// works most of the time, but that's ok because it's for debugging only.
|
||||
|
||||
@@ -107,147 +50,4 @@ public class GalleryUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final double RAD_PER_DEG = Math.PI / 180.0;
|
||||
private static final double EARTH_RADIUS_METERS = 6367000.0;
|
||||
|
||||
public static double fastDistanceMeters(double latRad1, double lngRad1,
|
||||
double latRad2, double lngRad2) {
|
||||
if ((Math.abs(latRad1 - latRad2) > RAD_PER_DEG)
|
||||
|| (Math.abs(lngRad1 - lngRad2) > RAD_PER_DEG)) {
|
||||
return accurateDistanceMeters(latRad1, lngRad1, latRad2, lngRad2);
|
||||
}
|
||||
// Approximate sin(x) = x.
|
||||
double sineLat = (latRad1 - latRad2);
|
||||
|
||||
// Approximate sin(x) = x.
|
||||
double sineLng = (lngRad1 - lngRad2);
|
||||
|
||||
// Approximate cos(lat1) * cos(lat2) using
|
||||
// cos((lat1 + lat2)/2) ^ 2
|
||||
double cosTerms = Math.cos((latRad1 + latRad2) / 2.0);
|
||||
cosTerms = cosTerms * cosTerms;
|
||||
double trigTerm = sineLat * sineLat + cosTerms * sineLng * sineLng;
|
||||
trigTerm = Math.sqrt(trigTerm);
|
||||
|
||||
// Approximate arcsin(x) = x
|
||||
return EARTH_RADIUS_METERS * trigTerm;
|
||||
}
|
||||
|
||||
public static double accurateDistanceMeters(double lat1, double lng1,
|
||||
double lat2, double lng2) {
|
||||
double dlat = Math.sin(0.5 * (lat2 - lat1));
|
||||
double dlng = Math.sin(0.5 * (lng2 - lng1));
|
||||
double x = dlat * dlat + dlng * dlng * Math.cos(lat1) * Math.cos(lat2);
|
||||
return (2 * Math.atan2(Math.sqrt(x), Math.sqrt(Math.max(0.0,
|
||||
1.0 - x)))) * EARTH_RADIUS_METERS;
|
||||
}
|
||||
|
||||
|
||||
public static final double toMile(double meter) {
|
||||
return meter / 1609;
|
||||
}
|
||||
|
||||
// For debugging, it will block the caller for timeout millis.
|
||||
public static void fakeBusy(JobContext jc, int timeout) {
|
||||
final ConditionVariable cv = new ConditionVariable();
|
||||
jc.setCancelListener(new CancelListener() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
cv.open();
|
||||
}
|
||||
});
|
||||
cv.block(timeout);
|
||||
jc.setCancelListener(null);
|
||||
}
|
||||
|
||||
public static void startCameraActivity(Context context) {
|
||||
Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
| Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static String formatLatitudeLongitude(String format, double latitude,
|
||||
double longitude) {
|
||||
// We need to specify the locale otherwise it may go wrong in some language
|
||||
// (e.g. Locale.FRENCH)
|
||||
return String.format(Locale.ENGLISH, format, latitude, longitude);
|
||||
}
|
||||
|
||||
public static void showOnMap(Context context, double latitude, double longitude) {
|
||||
try {
|
||||
// We don't use "geo:latitude,longitude" because it only centers
|
||||
// the MapView to the specified location, but we need a marker
|
||||
// for further operations (routing to/from).
|
||||
// The q=(lat, lng) syntax is suggested by geo-team.
|
||||
String uri = formatLatitudeLongitude("http://maps.google.com/maps?f=q&q=(%f,%f)",
|
||||
latitude, longitude);
|
||||
ComponentName compName = new ComponentName(MAPS_PACKAGE_NAME,
|
||||
MAPS_CLASS_NAME);
|
||||
Intent mapsIntent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse(uri)).setComponent(compName);
|
||||
context.startActivity(mapsIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
// Use the "geo intent" if no GMM is installed
|
||||
Log.e(TAG, "GMM activity not found!", e);
|
||||
String url = formatLatitudeLongitude("geo:%f,%f", latitude, longitude);
|
||||
Intent mapsIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
context.startActivity(mapsIntent);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setViewPointMatrix(
|
||||
float matrix[], float x, float y, float z) {
|
||||
// The matrix is
|
||||
// -z, 0, x, 0
|
||||
// 0, -z, y, 0
|
||||
// 0, 0, 1, 0
|
||||
// 0, 0, 1, -z
|
||||
Arrays.fill(matrix, 0, 16, 0);
|
||||
matrix[0] = matrix[5] = matrix[15] = -z;
|
||||
matrix[8] = x;
|
||||
matrix[9] = y;
|
||||
matrix[10] = matrix[11] = 1;
|
||||
}
|
||||
|
||||
public static int getBucketId(String path) {
|
||||
return path.toLowerCase().hashCode();
|
||||
}
|
||||
|
||||
// Return the local path that matches the given bucketId. If no match is
|
||||
// found, return null
|
||||
public static String searchDirForPath(File dir, int bucketId) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
String path = file.getAbsolutePath();
|
||||
if (GalleryUtils.getBucketId(path) == bucketId) {
|
||||
return path;
|
||||
} else {
|
||||
path = searchDirForPath(file, bucketId);
|
||||
if (path != null) return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean hasSpaceForSize(long size) {
|
||||
String state = Environment.getExternalStorageState();
|
||||
if (!Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String path = Environment.getExternalStorageDirectory().getPath();
|
||||
try {
|
||||
StatFs stat = new StatFs(path);
|
||||
return stat.getAvailableBlocks() * (long) stat.getBlockSize() > size;
|
||||
} catch (Exception e) {
|
||||
Log.i(TAG, "Fail to access external storage", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -271,6 +271,9 @@ public class HttpHelper {
|
||||
} catch (Exception e) {
|
||||
mException = e;
|
||||
e.printStackTrace();
|
||||
// For download, if need stop, just stop
|
||||
if (mException instanceof StopRequestException)
|
||||
break;
|
||||
} finally {
|
||||
if (conn != null)
|
||||
conn.disconnect();
|
||||
@@ -464,7 +467,6 @@ public class HttpHelper {
|
||||
sb.append(URLEncoder.encode(arg[1], "UTF-8"));
|
||||
i++;
|
||||
}
|
||||
Log.d(TAG, sb.toString());
|
||||
out.writeBytes(sb.toString());
|
||||
out.flush();
|
||||
out.close();
|
||||
@@ -646,7 +648,8 @@ public class HttpHelper {
|
||||
// If just
|
||||
// Bitmap bmp = BitmapFactory.decodeStream(conn.getInputStream(), null, Ui.getBitmapOpt());
|
||||
// bitmap might be incomplete.
|
||||
FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream(24 * 1024); // 24K
|
||||
int size = conn.getContentLength();
|
||||
FastByteArrayOutputStream fbaos = new FastByteArrayOutputStream(size == -1 ? 24 * 1024 : (size + 100));
|
||||
Utils.copy(conn.getInputStream(), fbaos);
|
||||
Bitmap bmp = BitmapFactory.decodeByteArray(fbaos.getBuffer(), 0, fbaos.size(), Ui.getBitmapOpt());
|
||||
|
||||
@@ -659,13 +662,17 @@ public class HttpHelper {
|
||||
public void onGetException(Exception e) {}
|
||||
}
|
||||
|
||||
public interface OnDownloadListener {
|
||||
public static interface OnDownloadListener {
|
||||
public void onDownloadStartConnect();
|
||||
/**
|
||||
* If totalSize -1 for can't get length info
|
||||
* @param totalSize
|
||||
*/
|
||||
public void onDownloadStartDownload(int totalSize);
|
||||
/**
|
||||
* If totalSize -1 for can't get length info
|
||||
* @param totalSize
|
||||
*/
|
||||
public void onDownloadStatusUpdate(int downloadSize, int totalSize);
|
||||
public void onDownloadOver(int status, String eMsg);
|
||||
}
|
||||
@@ -714,6 +721,9 @@ public class HttpHelper {
|
||||
|
||||
@Override
|
||||
public void onBeforeConnect(HttpURLConnection conn) throws Exception {
|
||||
if (mControlor != null && mControlor.isStop())
|
||||
throw new StopRequestException();
|
||||
|
||||
if (mIsProxy) {
|
||||
conn.setDoOutput(true);
|
||||
conn.setDoInput(true);
|
||||
@@ -761,6 +771,10 @@ public class HttpHelper {
|
||||
|
||||
@Override
|
||||
public Object onAfterConnect(HttpURLConnection conn) throws Exception {
|
||||
// Check stop
|
||||
if (mControlor != null && mControlor.isStop())
|
||||
throw new StopRequestException();
|
||||
|
||||
mContentLength = getContentLength(conn);
|
||||
if (mListener != null)
|
||||
mListener.onDownloadStartDownload(mContentLength);
|
||||
@@ -786,7 +800,7 @@ public class HttpHelper {
|
||||
if (e instanceof StopRequestException)
|
||||
mListener.onDownloadOver(DOWNLOAD_STOP_CODE, getEMsg());
|
||||
else
|
||||
mListener.onDownloadOver(DOWNLOAD_STOP_CODE, getEMsg());
|
||||
mListener.onDownloadOver(DOWNLOAD_FAIL_CODE, getEMsg());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -796,6 +810,7 @@ public class HttpHelper {
|
||||
mReceivedSize = 0;
|
||||
|
||||
while (true) {
|
||||
// Check stop first
|
||||
if (mControlor != null && mControlor.isStop())
|
||||
throw new StopRequestException();
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@ import android.os.IBinder;
|
||||
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.ehclient.EhClient.DownloadMangaManager;
|
||||
import com.hippo.ehviewer.ui.DownloadInfo;
|
||||
import com.hippo.ehviewer.util.Download;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
@@ -46,7 +44,7 @@ public class DownloadService extends Service {
|
||||
|
||||
private NotificationManager mNotifyManager;
|
||||
|
||||
private DownloadMangaManager downloadMangaManager;
|
||||
//private DownloadMangaManager downloadMangaManager;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -58,6 +56,7 @@ public class DownloadService extends Service {
|
||||
mNotifyManager = (NotificationManager)
|
||||
getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
/*
|
||||
downloadMangaManager = EhClient.getInstance().new DownloadMangaManager();
|
||||
|
||||
downloadMangaManager.setDownloadService(this);
|
||||
@@ -159,6 +158,7 @@ public class DownloadService extends Service {
|
||||
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
private void setNotification(Notification.Builder builder) {
|
||||
@@ -237,11 +237,11 @@ public class DownloadService extends Service {
|
||||
|| di.status == DownloadInfo.WAITING)
|
||||
return;
|
||||
di.status = DownloadInfo.WAITING;
|
||||
downloadMangaManager.add(di);
|
||||
//downloadMangaManager.add(di);
|
||||
}
|
||||
|
||||
public void cancel(String id) {
|
||||
downloadMangaManager.cancel(id);
|
||||
//downloadMangaManager.cancel(id);
|
||||
}
|
||||
|
||||
public void notifyUpdate(){
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.hippo.ehviewer.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
@@ -24,12 +25,23 @@ import android.view.WindowManager;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.gallery.GalleryView;
|
||||
import com.hippo.ehviewer.gallery.data.ImageSet;
|
||||
import com.hippo.ehviewer.gallery.ui.GLRootView;
|
||||
import com.hippo.ehviewer.widget.AlertButton;
|
||||
import com.hippo.ehviewer.widget.DialogBuilder;
|
||||
|
||||
public class GalleryActivity extends AbstractActivity {
|
||||
@SuppressWarnings("unused")
|
||||
private final String TAG = GalleryActivity.class.getSimpleName();
|
||||
private final static String TAG = GalleryActivity.class.getSimpleName();
|
||||
|
||||
public final static String KEY_GID = "gid";
|
||||
public final static String KEY_TOKEN = "token";
|
||||
public final static String KEY_TITLE = "title";
|
||||
public final static String KEY_START_INDEX = "start_index";
|
||||
|
||||
private RelativeLayout mainView;
|
||||
private ImageSet mImageSet;
|
||||
|
||||
@Override
|
||||
public void onOrientationChanged(int paddingTop, int paddingBottom) {
|
||||
@@ -70,5 +82,35 @@ public class GalleryActivity extends AbstractActivity {
|
||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
|
||||
Intent intent = getIntent();
|
||||
int gid = intent.getIntExtra(KEY_GID, -1);
|
||||
String token = intent.getStringExtra(KEY_TOKEN);
|
||||
String title = intent.getStringExtra(KEY_TITLE);
|
||||
int startIndex = intent.getIntExtra(KEY_START_INDEX, 0);
|
||||
|
||||
if (gid == -1 || token == null || title == null) {
|
||||
new DialogBuilder(this).setTitle(R.string.error)
|
||||
.setMessage("数据错误!").setPositiveButton("关闭",
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
((AlertButton)v).dialog.dismiss();
|
||||
finish();
|
||||
}
|
||||
}).create().show();
|
||||
} else {
|
||||
mImageSet = new ImageSet(gid, token, title, startIndex);
|
||||
GalleryView isv = new GalleryView(getApplicationContext(), mImageSet, startIndex);
|
||||
GLRootView glrv= (GLRootView)findViewById(R.id.gl_root_view);
|
||||
glrv.setContentPane(isv);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
mImageSet.free();
|
||||
}
|
||||
}
|
||||
@@ -821,19 +821,13 @@ public class GalleryDetailActivity extends AbstractActivity
|
||||
MaterialToast.showToast(R.string.toast_add_download);
|
||||
} else if (v == mReadButton) {
|
||||
//mData.addRead(mGalleryInfo);
|
||||
if (mGalleryInfo instanceof GalleryDetail) {
|
||||
GalleryDetail galleryDetail = (GalleryDetail)mGalleryInfo;
|
||||
Intent intent = new Intent(this,
|
||||
GalleryActivity.class);
|
||||
intent.putExtra("url", galleryDetail.firstPage);
|
||||
intent.putExtra("gid", mGalleryInfo.gid);
|
||||
intent.putExtra("title", mGalleryInfo.title);
|
||||
intent.putExtra("firstPage", 0);
|
||||
intent.putExtra("pageSum", galleryDetail.pages);
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
Intent intent = new Intent(this,
|
||||
GalleryActivity.class);
|
||||
intent.putExtra(GalleryActivity.KEY_GID, mGalleryInfo.gid);
|
||||
intent.putExtra(GalleryActivity.KEY_TOKEN, mGalleryInfo.token);
|
||||
intent.putExtra(GalleryActivity.KEY_TITLE, mGalleryInfo.title);
|
||||
intent.putExtra(GalleryActivity.KEY_START_INDEX, 0);
|
||||
startActivity(intent);
|
||||
} else if (v == mCategory) {
|
||||
finish();
|
||||
Intent intent = new Intent(this, GalleryListActivity.class);
|
||||
@@ -1175,7 +1169,7 @@ public class GalleryDetailActivity extends AbstractActivity
|
||||
PreviewImpl previewImpl = (PreviewImpl)mGalleryInfo;
|
||||
previewImpl.setPreview(page, pageList);
|
||||
pageList.setData(GalleryDetailActivity.this,
|
||||
GalleryDetailActivity.this, previewImpl, page); // TODO how about LofiGalleryDetail
|
||||
GalleryDetailActivity.this, previewImpl, page);
|
||||
pageList.addPreview(mPreview);
|
||||
|
||||
if (isLastPage && mGalleryInfo instanceof LofiDetailImpl) {
|
||||
|
||||
@@ -1059,9 +1059,9 @@ public class GalleryListActivity extends AbstractGalleryActivity
|
||||
|
||||
case 5: // Download
|
||||
// TODO Download Activity
|
||||
//intent = new Intent(GalleryListActivity.this,
|
||||
//DownloadActivity.class);
|
||||
//startActivity(intent);
|
||||
intent = new Intent(GalleryListActivity.this,
|
||||
GalleryActivity.class);
|
||||
startActivity(intent);
|
||||
break;
|
||||
|
||||
case 6: // Settings
|
||||
|
||||
@@ -53,10 +53,7 @@ import android.widget.TextView;
|
||||
import com.hippo.ehviewer.AppContext;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.UpdateHelper;
|
||||
import com.hippo.ehviewer.ehclient.EhClient;
|
||||
import com.hippo.ehviewer.ehclient.EhInfo;
|
||||
import com.hippo.ehviewer.ehclient.ExDownloadManager;
|
||||
import com.hippo.ehviewer.ehclient.ExDownloader;
|
||||
import com.hippo.ehviewer.network.Downloader;
|
||||
import com.hippo.ehviewer.preference.AutoListPreference;
|
||||
import com.hippo.ehviewer.util.Config;
|
||||
@@ -114,13 +111,6 @@ public class SettingsActivity extends AbstractPreferenceActivity {
|
||||
|
||||
mListView = getListView();
|
||||
mListView.setClipToPadding(false);
|
||||
|
||||
|
||||
// Test here
|
||||
|
||||
ExDownloadManager edm = ExDownloadManager.getInstance();
|
||||
ExDownloader ed = edm.getExDownloader(728773, "1f90a6e00c", "[BaseSon] Shiki to Hitsuji to Urau Tsuki", EhClient.MODE_EX);
|
||||
ed.setStartIndex(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,7 +67,7 @@ public class AutoExpandArray<E> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized E get(int index) {
|
||||
if (index >= array.length) {
|
||||
if (index >= array.length || index < 0) {
|
||||
return null;
|
||||
}
|
||||
return (E)array[index];
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 java.io.File;
|
||||
|
||||
public final class EhUtils {
|
||||
|
||||
/**
|
||||
* Get gallery for read and download
|
||||
* @param gid
|
||||
* @param title
|
||||
* @return
|
||||
*/
|
||||
public static File getGalleryDir(int gid, String title) {
|
||||
return new File(Config.getDownloadPath(),
|
||||
Utils.standardizeFilename(gid + "-" + title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Index start from 0
|
||||
*
|
||||
* @param index
|
||||
* @param extension
|
||||
* @return
|
||||
*/
|
||||
public static String getImageFilename(int index, String extension) {
|
||||
return String.format("%08d.%s", index + 1, extension);
|
||||
}
|
||||
|
||||
|
||||
public static String[] getPossibleImageFilenames(int index) {
|
||||
String prefix = String.format("%08d.", index + 1);
|
||||
return new String[]{prefix + "jpg", prefix + "jpeg", prefix + "png", prefix + "gif"};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -153,6 +153,67 @@ public final class MathUtils {
|
||||
* ((value - minStart) / (minStop - minStart));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*
|
||||
* @param x
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static int clamp(int x, int min, int max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*
|
||||
* @param x
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static float clamp(float x, float min, float max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
* @param x
|
||||
* @param min
|
||||
* @param max
|
||||
* @return
|
||||
*/
|
||||
public static long clamp(long x, long min, long max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next power of two.
|
||||
* Returns the input if it is already power of 2.
|
||||
* Throws IllegalArgumentException if the input is <= 0 or
|
||||
* the answer overflows.
|
||||
*
|
||||
* @param n
|
||||
* @return
|
||||
*/
|
||||
public static int nextPowerOf2(int n) {
|
||||
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
|
||||
n -= 1;
|
||||
n |= n >> 16;
|
||||
n |= n >> 8;
|
||||
n |= n >> 4;
|
||||
n |= n >> 2;
|
||||
n |= n >> 1;
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
public static int random(int howbig) {
|
||||
return (int) (sRandom.nextFloat() * howbig);
|
||||
}
|
||||
|
||||
@@ -21,26 +21,26 @@ import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public abstract class TimeRunner {
|
||||
|
||||
|
||||
private static final int INTERVAL = 5;
|
||||
|
||||
|
||||
private static final int RUNNING = 0;
|
||||
private static final int START = 1;
|
||||
private static final int END = 2;
|
||||
|
||||
private boolean mRunInThread = true;
|
||||
private boolean mListenerInThread = true;
|
||||
|
||||
|
||||
private boolean mRunInThread = false;
|
||||
private boolean mListenerInThread = false;
|
||||
|
||||
private OnTimeListener mListener;
|
||||
|
||||
|
||||
public interface OnTimeListener {
|
||||
public void onStart();
|
||||
public void onEnd();
|
||||
}
|
||||
|
||||
|
||||
// TODO
|
||||
@SuppressLint("HandlerLeak")
|
||||
private Handler mHandler = new Handler() {
|
||||
private final Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
@@ -56,30 +56,30 @@ public abstract class TimeRunner {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private int mDuration = 0;
|
||||
private int mDelay = 0;
|
||||
|
||||
|
||||
public void setOnTimerListener(OnTimeListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
|
||||
public void setDuration(int duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
|
||||
public void setRunInThread(boolean runInThread) {
|
||||
mRunInThread = runInThread;
|
||||
}
|
||||
|
||||
|
||||
public void setListenerInThread(boolean listenerInThread) {
|
||||
mListenerInThread = listenerInThread;
|
||||
}
|
||||
|
||||
|
||||
public void start() {
|
||||
start(0);
|
||||
}
|
||||
|
||||
|
||||
public void start(int delay) {
|
||||
mDelay = delay;
|
||||
new Thread(new Runnable() {
|
||||
@@ -99,7 +99,7 @@ public abstract class TimeRunner {
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int runningTime = 0; runningTime < mDuration; runningTime = (int)(System.currentTimeMillis() - startTime)) {
|
||||
if (mRunInThread) {
|
||||
@@ -117,7 +117,7 @@ public abstract class TimeRunner {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mListener != null) {
|
||||
if (mListenerInThread) {
|
||||
mListener.onEnd();
|
||||
@@ -130,6 +130,6 @@ public abstract class TimeRunner {
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
protected abstract void run(float interpolatedTime, int runningTime);
|
||||
}
|
||||
@@ -30,9 +30,11 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Vibrator;
|
||||
import android.view.View;
|
||||
|
||||
public final class Utils {
|
||||
@@ -581,4 +583,74 @@ public final class Utils {
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to make dir exists
|
||||
*
|
||||
* @param dir
|
||||
* @param force
|
||||
*/
|
||||
public static void ensureDir(File dir, boolean force) {
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
} else if (force && dir.isFile()) {
|
||||
dir.delete();
|
||||
dir.mkdirs();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension from target string, int lower case
|
||||
*
|
||||
* @param name
|
||||
* @param defautl
|
||||
* @return
|
||||
*/
|
||||
public static String getExtension(String name, String defautl) {
|
||||
int index = name.lastIndexOf('.');
|
||||
if (index == -1 || index == name.length() - 1)
|
||||
return defautl;
|
||||
else
|
||||
return name.substring(index + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if point is in the rect
|
||||
*
|
||||
* @param area
|
||||
* @param x
|
||||
* @param y
|
||||
* @return
|
||||
*/
|
||||
public static boolean isInArea(int[] area, int x, int y) {
|
||||
if (area.length != 4)
|
||||
throw new IllegalArgumentException(
|
||||
"area's length should be 4, but it's length is " + area.length);
|
||||
if (x >= area[0] && x < area[2] && y >= area[1] && y < area[3])
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isOpaque(int color) {
|
||||
return color >>> 24 == 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws AssertionError if the input is false.
|
||||
* @param cond
|
||||
*/
|
||||
public static void assertTrue(boolean cond) {
|
||||
if (!cond) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vibrator
|
||||
*/
|
||||
public static void vibrator(Context context, long milliseconds) {
|
||||
Vibrator v = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
v.vibrate(milliseconds);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user