Add download

This commit is contained in:
seven332 committed 2015-08-19 22:20:03 +08:00
1 parent 3aaf14ef3f
commit 4a80ea7dcd
43 files changed
+2167 -270

No files matched your search

@@ -30,6 +30,8 @@ public final class Constants {
public static final int MESSENGER_ID_ARCHIVE_DOWNLOAD_LOCATION;
public static final int MESSENGER_ID_UPDATE_QUICK_SEARCH;
public static final int MESSENGER_ID_UPDATE_QUICK_SEARCH_NAME;
public static final int MESSENGER_ID_UPDATE_DOWNLOAD;
public static final int MESSENGER_ID_UPDATE_DOWNLOAD_TAG;
static {
Messenger messenger = Messenger.getInstance();
@@ -43,5 +45,7 @@ public final class Constants {
MESSENGER_ID_ARCHIVE_DOWNLOAD_LOCATION = messenger.newId();
MESSENGER_ID_UPDATE_QUICK_SEARCH = messenger.newId();
MESSENGER_ID_UPDATE_QUICK_SEARCH_NAME = messenger.newId();
MESSENGER_ID_UPDATE_DOWNLOAD = messenger.newId();
MESSENGER_ID_UPDATE_DOWNLOAD_TAG = messenger.newId();
}
}
@@ -26,6 +26,7 @@ import com.hippo.ehviewer.gallery.GallerySpider;
import com.hippo.ehviewer.gallery.ImageHandler;
import com.hippo.ehviewer.gallery.ui.TouchHelper;
import com.hippo.ehviewer.network.EhHttpClient;
import com.hippo.ehviewer.service.DownloadManager;
import com.hippo.ehviewer.util.DBUtils;
import com.hippo.ehviewer.util.Settings;
import com.hippo.scene.SceneApplication;
@@ -51,6 +52,7 @@ public class EhApplication extends SceneApplication {
Settings.initialize(this);
DBUtils.initialize(this);
TouchHelper.initialize(this);
DownloadManager.initialize();
File sayFile = AppConfig.getFileInAppDir("EhViewer.log");
if (sayFile != null) {
@@ -289,8 +289,10 @@ public final class EhClient {
// Clear
mEhHttpRequest = null;
mListener = null;
} else if (status == Status.FINISHED) {
mEhHttpRequest.disconnect();
} else if (status == Status.RUNNING) {
if (mEhHttpRequest != null) {
mEhHttpRequest.cancel();
}
}
}
}
@@ -53,7 +53,7 @@ class EhEngine {
String body = response.getString();
return SignInParser.parse(body);
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -71,7 +71,7 @@ class EhEngine {
String body = response.getString();
return GalleryListParser.parse(body, source);
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -93,7 +93,7 @@ class EhEngine {
String body = response.getString();
return PopularParser.parse(body);
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -116,7 +116,7 @@ class EhEngine {
GalleryDetailParser.REQUEST_PREVIEW |
GalleryDetailParser.REQUEST_COMMENT);
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -134,7 +134,7 @@ class EhEngine {
String body = response.getString();
return GalleryDetailParser.parsePreview(body, source);
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -174,7 +174,7 @@ class EhEngine {
return list;
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -203,7 +203,7 @@ class EhEngine {
return RateGalleryParser.parse(body);
} catch (Exception e) {
if (httpRequest.getState() == HttpRequest.STATE_DISCONNECT) {
if (httpRequest.isCancelled()) {
throw new CanceledException();
} else {
throw e;
@@ -0,0 +1,54 @@
/*
* Copyright 2015 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.client.data;
import com.hippo.ehviewer.dao.DownloadInfoObj;
import com.hippo.ehviewer.util.DBUtils;
public class DownloadInfo {
public static final int STATE_NONE = 0x0;
public static final int STATE_WAIT = 0x1;
public static final int STATE_DOWNLOAD = 0x2;
public static final int STATE_FINISH = 0x3;
public String tag;
public GalleryBase galleryBase;
public int state = STATE_NONE;
// byte/second, -1 for can't get speed now
public long speed;
// -1 for unknown
public int download = -1;
// -1 for unknown
public int total = -1;
// Integer.MAX_VALUE for unknown
public int legacy = Integer.MAX_VALUE;
public static DownloadInfo fromDownloadInfoObj(DownloadInfoObj obj) {
GalleryBase galleryBase = DBUtils.getGalleryBase((int) (long) obj.getGid());
if (galleryBase != null) {
DownloadInfo downloadInfo = new DownloadInfo();
downloadInfo.galleryBase = galleryBase;
downloadInfo.tag = obj.getTag();
downloadInfo.state = obj.getState();
downloadInfo.legacy = obj.getLegacy();
return downloadInfo;
} else {
return null;
}
}
}
@@ -58,7 +58,8 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
private AtomicLongArray mReceivedSizes;
private AtomicReferenceArray<Float> mPercents;
private List<GalleryProviderListener> mGalleryProviderListeners = new ArrayList<>();
private List<GalleryProviderListener> mGalleryProviderListeners = new ArrayList<>(2);
private List<SpiderQueen.SpiderListener> mSpiderListeners = new ArrayList<>(2);
private int mReference = 0;
@@ -160,6 +161,14 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
mGalleryProviderListeners.remove(listener);
}
public void addSpiderSpiderListener(SpiderQueen.SpiderListener listener) {
mSpiderListeners.add(listener);
}
public void removeSpiderSpiderListener(SpiderQueen.SpiderListener listener) {
mSpiderListeners.remove(listener);
}
@Override
public Object request(int index) {
if (mSpiderQueen != null) {
@@ -178,7 +187,11 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
@Override
public Object forceRequest(int index) {
return null;// TODO
if (mSpiderQueen != null) {
return mSpiderQueen.forceRequest(index);
} else {
return GalleryProvider.RESULT_OUT_OF_RANGE;
}
}
@Override
@@ -226,6 +239,10 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onTotallyFailed(e);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onTotallyFailed(e);
}
}
});
}
@@ -238,13 +255,24 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onPartlyFailed(e);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onPartlyFailed(e);
}
}
});
}
@Override
public void onDone() {
// TODO
public void onDone(final int legacy) {
SimpleHandler.getInstance().post(new Runnable() {
@Override
public void run() {
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onDone(legacy);
}
}
});
}
@Override
@@ -259,6 +287,10 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onGetSize(pages);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onGetPages(pages);
}
}
});
}
@@ -277,17 +309,21 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onPagePercent(index, 0.0f);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onSpiderStart(index, totalSize);
}
}
});
}
@Override
public void onSpiderPage(final int index, long receivedSize) {
public void onSpiderPage(final int index, final long receivedSize, final long singleReceivedSize) {
if (mReceivedSizes != null && index >= 0 && index < mReceivedSizes.length()) {
mReceivedSizes.lazySet(index, receivedSize);
}
long totalSize= mTotalSizes.get(index);
final long totalSize= mTotalSizes.get(index);
final float percent = totalSize == 0 ? 0.0f :
(float) receivedSize / (float) totalSize;
@@ -299,6 +335,10 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onPagePercent(index, percent);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onSpiderPage(index, receivedSize, singleReceivedSize);
}
}
});
}
@@ -311,6 +351,10 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onPageSucceed(index);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onSpiderSucceed(index);
}
}
});
}
@@ -323,10 +367,21 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
for (GalleryProviderListener l : mGalleryProviderListeners) {
l.onPageFailed(index, e);
}
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onSpiderFailed(index, e);
}
}
});
}
@Override
public void onSpiderCancelled(final int index) {
for (SpiderQueen.SpiderListener l : mSpiderListeners) {
l.onSpiderCancelled(index);
}
}
@Override
public void onGetImage(final int index, final Object obj) {
SimpleHandler.getInstance().post(new Runnable() {
@@ -250,6 +250,31 @@ public class ImageHandler {
}
}
public void removeFromRead(int index) {
sDiskCache.remove(EhCacheKeyFactory.getImageKey(mGalleryBase.gid, index));
}
public void removeFromDownload(int index) {
if (mDownloadDir == null) {
return;
}
for (String extension : POSSIBLE_IMAGE_EXTENSIONS) {
String filename = getImageFilename(index, extension);
UniFile file = mDownloadDir.findFile(filename);
if (file != null) {
file.delete();
}
}
}
public void remove(int index) {
removeFromRead(index);
if (mMode == Mode.DOWNLOAD) {
removeFromDownload(index);
}
}
public void save(HttpClient httpClient, String url, int index, SaveHelper helper) throws Exception {
OutputStreamPipe osPipe = null;
@@ -328,8 +353,8 @@ public class ImageHandler {
}
@Override
public void onDonwlad(long receivedSize) {
mHelper.onSave(receivedSize);
public void onDonwlad(long receivedSize, long singleReceivedSize) {
mHelper.onSave(receivedSize, singleReceivedSize);
}
@Override
@@ -442,7 +467,7 @@ public class ImageHandler {
public abstract void onStartSaving(long totalSize);
public abstract void onSave(long receivedSize);
public abstract void onSave(long receivedSize, long singleReceivedSize);
public boolean isCancelled() {
return mCancel;
@@ -20,12 +20,13 @@ import android.util.Log;
import com.hippo.ehviewer.client.EhConfig;
import com.hippo.ehviewer.client.data.GalleryBase;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.AutoExpandArray;
import com.hippo.yorozuya.IOUtils;
import java.io.BufferedInputStream;
import java.io.EOFException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -72,119 +73,86 @@ public class SpiderInfo {
}
GalleryBase gb = galleryBase;
OutputStreamWriter osWrite = new OutputStreamWriter(os, "UTF-8");
osWrite.append(Integer.toString(VERSION)).append('\n')
.append(Integer.toString(gb.gid)).append('\n')
.append(gb.token).append('\n')
.append(stringToHexString(gb.title)).append('\n')
.append(stringToHexString(gb.titleJpn)).append('\n')
.append(gb.thumb).append('\n')
.append(Integer.toString(gb.category)).append('\n')
.append(gb.posted).append('\n')
.append(gb.uploader).append('\n')
.append(Float.toString(gb.rating)).append('\n')
.append(Integer.toString(pages)).append('\n')
.append(previewSize).append('\n')
.append(Integer.toString(previewPages)).append('\n')
.append(Integer.toString(previewCountPerPage)).append('\n');
JSONObject jo;
try {
jo = new JSONObject();
jo.put("version", VERSION);
jo.put("gid", gb.gid);
jo.put("token", gb.token);
jo.put("title", gb.title);
jo.put("titleJpn", gb.titleJpn);
jo.put("thumb", gb.thumb);
jo.put("category", gb.category);
jo.put("posted", gb.posted);
jo.put("uploader", gb.uploader);
jo.put("rating", gb.rating);
jo.put("pages", pages);
jo.put("previewSize", previewSize);
jo.put("previewPages", previewPages);
jo.put("previewCountPerPage", previewCountPerPage);
if (tokens != null) {
JSONArray tokenJa = new JSONArray();
int length = Math.min(tokens.maxValidIndex() + 1, pages);
for (int i = 0; i < length; i++) {
String token = tokens.get(i);
if (token != null) {
osWrite.append(Integer.toString(i)).append(':')
.append(token).append('\n');
}
tokenJa.put(tokens.get(i));
}
jo.put("tokens", tokenJa);
} catch (JSONException e) {
Log.e(TAG, "Can't create SpiderInfo JSON to write", e);
jo = null;
}
osWrite.flush();
osWrite.close();
if (jo != null) {
OutputStreamWriter osWrite = new OutputStreamWriter(os, "UTF-8");
osWrite.append(jo.toString());
osWrite.flush();
osWrite.close();
}
}
public static SpiderInfo read(InputStream is) throws Exception {
BufferedInputStream bis = new BufferedInputStream(is);
SpiderInfo spiderInfo = new SpiderInfo();
String str = IOUtils.readString(is, "UTF-8");
int version = Integer.parseInt(IOUtils.readAsciiLine(bis));
JSONObject jo = new JSONObject(str);
int version = jo.optInt("version", 0);
if (VERSION != version){
throw new IllegalStateException("Version should be " + VERSION +
", but it is " + version);
}
SpiderInfo spiderInfo = new SpiderInfo();
GalleryBase gb = new GalleryBase();
gb.gid = Integer.parseInt(IOUtils.readAsciiLine(bis));
gb.token = IOUtils.readAsciiLine(bis);
gb.title = hexStringToString(IOUtils.readAsciiLine(bis));
gb.titleJpn = hexStringToString(IOUtils.readAsciiLine(bis));
gb.thumb = IOUtils.readAsciiLine(bis);
gb.category = Integer.parseInt(IOUtils.readAsciiLine(bis));
gb.posted = IOUtils.readAsciiLine(bis);
gb.uploader = IOUtils.readAsciiLine(bis);
gb.rating = Float.parseFloat(IOUtils.readAsciiLine(bis));
gb.gid = jo.getInt("gid");
gb.token = jo.getString("token");
gb.title = jo.getString("title");
gb.titleJpn = jo.optString("titleJpn", null);
gb.thumb = jo.getString("thumb");
gb.category = jo.getInt("category");
gb.posted = jo.getString("posted");
gb.uploader = jo.getString("uploader");
gb.rating = (float) jo.getDouble("rating");
spiderInfo.galleryBase = gb;
spiderInfo.pages = Integer.parseInt(IOUtils.readAsciiLine(bis));
spiderInfo.previewSize = IOUtils.readAsciiLine(bis);
spiderInfo.pages = jo.getInt("pages");
spiderInfo.previewSize = jo.getString("previewSize");
if (!spiderInfo.previewSize.equals(EhConfig.PREVIEW_SIZE_NORMAL) &&
!spiderInfo.previewSize.equals(EhConfig.PREVIEW_SIZE_LARGE)) {
throw new IllegalStateException("Unknown preview size " + spiderInfo.previewSize);
}
spiderInfo.previewPages = Integer.parseInt(IOUtils.readAsciiLine(bis));
spiderInfo.previewCountPerPage = Integer.parseInt(IOUtils.readAsciiLine(bis));
spiderInfo.previewPages = jo.getInt("previewPages");
spiderInfo.previewCountPerPage = jo.getInt("previewCountPerPage");
AutoExpandArray<String> tokens = new AutoExpandArray<>(spiderInfo.pages);
try {
//noinspection InfiniteLoopStatement
for (;;) {
String line = IOUtils.readAsciiLine(is);
int pos = line.indexOf(":");
if (pos < 0) {
Log.e(TAG, "Can't parse line " + line);
continue;
}
try {
int index = Integer.parseInt(line.substring(0, pos));
String token = line.substring(pos + 1);
tokens.set(index, token);
} catch (NumberFormatException e) {
// Empty
Log.e(TAG, "Can't parse line " + line);
}
}
} catch (EOFException e) {
// Empty
}
JSONArray ja = jo.getJSONArray("tokens");
AutoExpandArray<String> tokens = new AutoExpandArray<>(ja.length());
spiderInfo.tokens = tokens;
for (int i = 0, n = ja.length(); i < n; i++) {
tokens.set(i, ja.getString(i));
}
return spiderInfo;
}
public static String stringToHexString(String str) {
StringBuilder sb = new StringBuilder(320);
int length = str.length();
for (int i = 0; i < length; i++) {
char ch = str.charAt(i);
String s = Integer.toString((int) ch, 16);
int length2 = 4 - s.length();
for (int j = 0; j < length2; j++) {
sb.append('0');
}
sb.append(s);
}
return sb.toString();
}
public static String hexStringToString(String str) {
AssertUtils.assertEquals("Hex String's length must be a multiple of 4.", str.length() % 4, 0);
StringBuilder sb = new StringBuilder(80);
int length = str.length();
for (int i = 0; i < length; i += 4) {
sb.append((char) Integer.parseInt(str.substring(i, i + 4), 16));
}
return sb.toString();
}
}
@@ -39,7 +39,6 @@ import com.hippo.yorozuya.Say;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
@@ -47,8 +46,6 @@ import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.locks.ReentrantLock;
public class SpiderQueen implements Runnable {
@@ -79,9 +76,11 @@ public class SpiderQueen implements Runnable {
private SpiderListener mSpiderListener;
public SpiderInfo mSpiderInfo;
public boolean mHasCheckIndexAgain = true;
public volatile int mNextIndex;
public AtomicIntegerArray mPageStates;
public int[] mPageStates;
public LinkedList<Integer> mRequestIndex = new LinkedList<>();
private volatile HttpRequest mCurrentHttpRequest;
@@ -100,8 +99,8 @@ public class SpiderQueen implements Runnable {
// The lock to make worker sleep
private final Object mWorkerLock = new Object();
// The lock for next index
private final Object mNextIndexLock = new Object();
private final ReentrantLock mWorkerArrayLock = new ReentrantLock();
private final Object mIndexLock = new Object();
private final Object mWorkerArrayLock = new Object();
// Lock for mRequestTokens and mSpiderInfo.tokens
private final Object mTokenLock = new Object();
// The lock to make sure onGetSize call first
@@ -142,7 +141,7 @@ public class SpiderQueen implements Runnable {
return GalleryProvider.RESULT_OUT_OF_RANGE;
}
int state = mPageStates.get(index);
int state = getPageState(index);
switch (state) {
case PAGE_STATE_NONE:
// TODO set next index
@@ -169,11 +168,8 @@ public class SpiderQueen implements Runnable {
if (index < 0 || index >= mSpiderInfo.pages) {
return GalleryProvider.RESULT_OUT_OF_RANGE;
}
// TODO
throw new IllegalStateException();
addRequestIndex(index);
return GalleryProvider.RESULT_WAIT;
} else {
return GalleryProvider.RESULT_OUT_OF_RANGE;
}
@@ -188,7 +184,7 @@ public class SpiderQueen implements Runnable {
public void stop() {
mStop = true;
if (mCurrentHttpRequest != null) {
mCurrentHttpRequest.disconnect();
mCurrentHttpRequest.cancel();
mCurrentHttpRequest = null;
}
synchronized (mQueenLock) {
@@ -211,7 +207,7 @@ public class SpiderQueen implements Runnable {
}
public int pages() {
if (mSpiderInfo != null) {
if (mSpiderInfo != null && mGetSizeFinished) {
return mSpiderInfo.pages;
} else {
return -1;
@@ -567,17 +563,19 @@ public class SpiderQueen implements Runnable {
return;
}
setGetSizeFinished(false);
// 5. Perpare
// Update EhConfig preview size
ehConfig.previewSize = spiderInfo.previewSize;
ehConfig.setDirty();
mSpiderInfo = spiderInfo;
mNextIndex = 0;
// Init AtomicIntegerArray stuff
// Init int array stuff
int length = spiderInfo.pages;
mPageStates = new AtomicIntegerArray(length);
mPageStates = new int[length];
for (int i = 0; i < length; i++) {
mPageStates.lazySet(i, PAGE_STATE_NONE);
mPageStates[i] = PAGE_STATE_NONE;
}
mSpiderWorkers = new SpiderWorker[3];
mImageHandler = new ImageHandler(gb, spiderInfo.pages, mMode, mDownloadDirparent, mDownloadDirname);
@@ -587,7 +585,6 @@ public class SpiderQueen implements Runnable {
}
// 6. Make workers and render to work
setGetSizeFinished(false);
ensureWorker();
mRender = new Render();
Thread thread = new PriorityThread(mRender, "Render", Process.THREAD_PRIORITY_BACKGROUND);
@@ -601,42 +598,30 @@ public class SpiderQueen implements Runnable {
// 8. Get tokens
handleToken(spiderInfo, ehConfig);
// 5. The queen dies, workers die, render dies
mWorkerArrayLock.lock();
for (SpiderWorker worker : mSpiderWorkers) {
if (worker != null) {
worker.stop();
// 9. The queen dies, workers die, render dies
synchronized (mWorkerArrayLock) {
for (SpiderWorker worker : mSpiderWorkers) {
if (worker != null) {
worker.stop();
}
}
synchronized (mWorkerLock) {
mWorkerLock.notifyAll();
}
}
synchronized (mWorkerLock) {
mWorkerLock.notifyAll();
}
mWorkerArrayLock.unlock();
mRender.stop();
}
private void ensureWorker() {
mWorkerArrayLock.lock();
int length = mSpiderWorkers.length;
for (int i = 0; i < length; i++) {
if (mSpiderWorkers[i] == null) {
SpiderWorker worker = new SpiderWorker(i);
mSpiderWorkers[i] = worker;
mThreadFactory.newThread(worker).start();
}
}
mWorkerArrayLock.unlock();
}
private void setNextIndex(int index) {
synchronized (mNextIndexLock) {
if (mSpiderInfo != null && index < mSpiderInfo.pages) {
mNextIndex = index;
synchronized (mWorkerArrayLock) {
int length = mSpiderWorkers.length;
for (int i = 0; i < length; i++) {
if (mSpiderWorkers[i] == null) {
SpiderWorker worker = new SpiderWorker(i);
mSpiderWorkers[i] = worker;
mThreadFactory.newThread(worker).start();
}
}
}
}
@@ -656,29 +641,79 @@ public class SpiderQueen implements Runnable {
}
}
// TODO request index may
private void setNextIndex(int index) {
synchronized (mIndexLock) {
if (mSpiderInfo != null && index < mSpiderInfo.pages) {
mNextIndex = index;
}
}
}
private int getPageState(int index) {
synchronized (mIndexLock) {
return mPageStates[index];
}
}
private void setPageState(int index, int state) {
synchronized (mIndexLock) {
mPageStates[index] = state;
}
}
// it to request queue
private void addRequestIndex(int index) {
synchronized (mTokenLock) {
if (mPageStates[index] != PAGE_STATE_SPIDER) {
mPageStates[index] = PAGE_STATE_NONE;
mRequestIndex.add(index);
mImageHandler.remove(index);
}
}
}
// Get first PAGE_STATE_NONE index and set state PAGE_STATE_SPIDER
private int getRequestIndex() {
synchronized (mNextIndexLock) {
synchronized (mIndexLock) {
int length = mSpiderInfo.pages;
for (;;) {
if (mNextIndex >= length) {
if (!mHasCheckIndexAgain) {
mHasCheckIndexAgain = true;
mNextIndex = 0;
} else {
return -1;
int index;
if (mRequestIndex.isEmpty()) {
if (mNextIndex >= length) {
if (!mHasCheckIndexAgain) {
mHasCheckIndexAgain = true;
mNextIndex = 0;
} else {
return -1;
}
}
index = mNextIndex++;
} else {
index = mRequestIndex.remove(0);
}
int index = mNextIndex;
if (mPageStates.get(mNextIndex++) == PAGE_STATE_NONE) {
if (mPageStates[index] == PAGE_STATE_NONE) {
mPageStates[index] = PAGE_STATE_SPIDER;
return index;
}
}
}
}
private int getLegacy() {
synchronized (mIndexLock) {
int legacy = 0;
for (int i = 0, n = mPageStates.length; i < n; i++) {
int state = mPageStates[i];
if (state != PAGE_STATE_SUCCEED) {
legacy++;
}
}
return legacy;
}
}
class SpiderWorker implements Runnable {
private String TAG = SpiderWorker.class.getSimpleName();
@@ -698,7 +733,7 @@ public class SpiderQueen implements Runnable {
public void stop() {
mStop = true;
if (mCurrentHttpRequest != null) {
mCurrentHttpRequest.disconnect();
mCurrentHttpRequest.cancel();
mCurrentHttpRequest = null;
}
if (mCurrentSaveHelper != null) {
@@ -738,6 +773,13 @@ public class SpiderQueen implements Runnable {
Exception exception = null;
for (int i = 0; i < 2; i++) {
// Check stop
if (mStop) {
setPageState(index, PAGE_STATE_NONE);
mSpiderListener.onSpiderCancelled(index);
return;
}
// Get image url
GalleryPageParser.Result result;
try {
@@ -749,6 +791,13 @@ public class SpiderQueen implements Runnable {
imageUrl = result.imageUrl;
skipHathKey = result.skipHathKey;
// Check stop
if (mStop) {
setPageState(index, PAGE_STATE_NONE);
mSpiderListener.onSpiderCancelled(index);
return;
}
// Download image
try {
mCurrentSaveHelper = new ImageHandler.SaveHelper() {
@@ -758,18 +807,18 @@ public class SpiderQueen implements Runnable {
}
@Override
public void onSave(long receivedSize) {
mSpiderListener.onSpiderPage(index, receivedSize);
public void onSave(long receivedSize, long singleReceivedSize) {
mSpiderListener.onSpiderPage(index, receivedSize, singleReceivedSize);
}
};
mImageHandler.save(mHttpClient, imageUrl, index, mCurrentSaveHelper);
mPageStates.set(index, PAGE_STATE_SUCCEED);
setPageState(index, PAGE_STATE_SUCCEED);
mSpiderListener.onSpiderSucceed(index);
return;
} catch (Exception e) {
if (mCurrentSaveHelper != null && mCurrentSaveHelper.isCancelled()) {
mPageStates.lazySet(index, PAGE_STATE_FAILED);
mSpiderListener.onSpiderFailed(index, new IOException("Cancelled"));
setPageState(index, PAGE_STATE_NONE);
mSpiderListener.onSpiderCancelled(index);
return;
} else {
exception = e;
@@ -779,12 +828,10 @@ public class SpiderQueen implements Runnable {
}
}
mPageStates.lazySet(index, PAGE_STATE_FAILED);
setPageState(index, PAGE_STATE_FAILED);
mSpiderListener.onSpiderFailed(index, exception);
}
@Override
public void run() {
TAG = Thread.currentThread().getName();
@@ -803,14 +850,12 @@ public class SpiderQueen implements Runnable {
if (index == -1) {
// There is no more
break;
} else {
mPageStates.lazySet(index, PAGE_STATE_SPIDER);
}
// Check contain
if (handler.contain(index)) {
// Find it, no need to download
mPageStates.lazySet(index, PAGE_STATE_SUCCEED);
setPageState(index, PAGE_STATE_SUCCEED);
// Listener
mSpiderListener.onSpiderSucceed(index);
continue;
@@ -835,19 +880,26 @@ public class SpiderQueen implements Runnable {
break;
}
}
// For stop
if (mStop) {
mPageStates.lazySet(index, PAGE_STATE_NONE);
break;
}
downloadPage(gb.gid, index, token);
}
// TODO this lock is not right, we need to lock when before check it is over
mWorkerArrayLock.lock();
mSpiderWorkers[mIndex] = null;
mWorkerArrayLock.unlock();
synchronized (mWorkerArrayLock) {
mSpiderWorkers[mIndex] = null;
boolean allDie = true;
for (SpiderWorker worker : mSpiderWorkers) {
if (worker != null) {
allDie = false;
break;
}
}
if (allDie) {
// Get legacy
mSpiderListener.onDone(getLegacy());
}
}
Say.d(TAG, Thread.currentThread().getName() + " dies");
}
@@ -943,22 +995,20 @@ public class SpiderQueen implements Runnable {
void onPartlyFailed(Exception e);
/**
* The whole gallery has been spidered
* TODO
*/
void onDone();
void onDone(int legacy);
void onGetPages(int pages);
void onSpiderStart(int index, long totalSize);
void onSpiderPage(int index, long receivedSize);
void onSpiderPage(int index, long receivedSize, long singleReceivedSize);
void onSpiderSucceed(int index);
void onSpiderFailed(int index, Exception e);
void onSpiderCancelled(int index);
void onGetImage(int index, Object obj);
}
}
@@ -0,0 +1,595 @@
/*
* Copyright 2015 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.service;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.hippo.ehviewer.Constants;
import com.hippo.ehviewer.client.data.DownloadInfo;
import com.hippo.ehviewer.client.data.GalleryBase;
import com.hippo.ehviewer.gallery.GallerySpider;
import com.hippo.ehviewer.gallery.ImageHandler;
import com.hippo.ehviewer.gallery.SpiderQueen;
import com.hippo.ehviewer.util.DBUtils;
import com.hippo.ehviewer.util.EhUtils;
import com.hippo.yorozuya.Messenger;
import com.hippo.yorozuya.SimpleHandler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class DownloadManager {
private static final String TAG = DownloadManager.class.getSimpleName();
public static final int OPS_SHIFT = 30;
public static final int OPS_MASK = 0x3 << OPS_SHIFT;
public static final int OPS_ADD = 0 << OPS_SHIFT;
public static final int OPS_REMOVE = 1 << OPS_SHIFT;
public static final int OPS_UPDATE = 2 << OPS_SHIFT;
//
public static final int OPS_ALL_CHANGE = 3 << OPS_SHIFT;
public static int getOps(int mix) {
return (mix & OPS_MASK);
}
public static int getGid(int mix) {
return (mix & ~OPS_MASK);
}
public static int makeMix(int gid, int ops) {
return (gid & ~OPS_MASK) | (ops & OPS_MASK);
}
private List<DownloadInfo> mDownloadInfos;
private Map<String, List<DownloadInfo>> mDownloadInfoMap;
private List<DownloadInfo> mDefaultDownloadInfos;
private List<DownloadInfo> mWaitList;
private @Nullable DownloadInfo mCurrentTask;
private GallerySpider mCurrentSpider;
private SpeedReminder mSpeedReminder;
private DownloadSpiderListener mDownloadSpiderListener;
private @Nullable DownloadUpdateListener mDownloadUpdateListener;
private DownloadUpdateInfo mUpdateInfo;
private static DownloadManager sInstance;
public static void initialize() {
sInstance = new DownloadManager();
}
public static DownloadManager getInstance() {
return sInstance;
}
public DownloadManager() {
List<String> tags = DBUtils.getAllDownloadTag();
List<DownloadInfo> downloadInfos = DBUtils.getAllDownloadInfo();
mDownloadInfos = downloadInfos;
mDownloadInfoMap = new HashMap<>();
// Create list for each tag
for (String tag : tags) {
mDownloadInfoMap.put(tag, new ArrayList<DownloadInfo>());
}
// Create default for non tag
mDefaultDownloadInfos = new ArrayList<>();
// Fill each list with DownloadInfo
for (DownloadInfo info : downloadInfos) {
// Fix download state
if (info.state == DownloadInfo.STATE_DOWNLOAD || info.state == DownloadInfo.STATE_WAIT) {
info.state = DownloadInfo.STATE_NONE;
}
List<DownloadInfo> list;
if (TextUtils.isEmpty(info.tag)) {
list = mDefaultDownloadInfos;
} else {
list = mDownloadInfoMap.get(info.tag);
}
if (list == null) {
list = mDefaultDownloadInfos;
}
list.add(info);
}
mWaitList = new LinkedList<>();
mSpeedReminder = new SpeedReminder();
mDownloadSpiderListener = new DownloadSpiderListener();
mUpdateInfo = new DownloadUpdateInfo();
}
public void setDownloadUpdateListener(@Nullable DownloadUpdateListener listener) {
mDownloadUpdateListener = listener;
}
public List<DownloadInfo> getDownloadList(String tag) {
return getDownloadListInternal(tag, false);
}
private List<DownloadInfo> getDownloadListInternal(String tag, boolean create) {
if (TextUtils.isEmpty(tag)) {
return mDefaultDownloadInfos;
} else {
List<DownloadInfo> list = mDownloadInfoMap.get(tag);
if (list == null && create) {
list = new ArrayList<>();
mDownloadInfoMap.put(tag, list);
// notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD_TAG, null);
// Save tag to DB
DBUtils.addDownloadTag(tag);
}
return list;
}
}
// Make sure is downloading
private void ensureDownload() {
if (mCurrentTask != null) {
// Only one download
return;
}
Log.v(TAG, "ensureDownload");
// get download from wait list
if (!mWaitList.isEmpty()) {
DownloadInfo info = mWaitList.remove(0);
GallerySpider spider = null;
try {
spider = GallerySpider.obtain(info.galleryBase, ImageHandler.Mode.DOWNLOAD);
} catch (IOException e) {
e.printStackTrace();
}
if (spider != null) {
int gid = info.galleryBase.gid;
mCurrentTask = info;
mCurrentSpider = spider;
info.state = DownloadInfo.STATE_DOWNLOAD;
info.speed = -1;
// Fill Download update info
DownloadUpdateInfo updateInfo = mUpdateInfo;
updateInfo.gid = gid;
updateInfo.title = EhUtils.getSuitableTitle(info.galleryBase);
updateInfo.pages = spider.size();
updateInfo.downloadedPages = 0;
updateInfo.speed = -1;
updateInfo.legacy = Integer.MAX_VALUE;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onStart(updateInfo);
}
// Update DownloadInfo to DB
DBUtils.updateDownloadInfo(info);
info.total = spider.size();
mDownloadSpiderListener.start();
spider.addSpiderSpiderListener(mDownloadSpiderListener);
mSpeedReminder.start();
} else {
// Get error when getting spider
info.state = DownloadInfo.STATE_FINISH;
info.legacy = Integer.MAX_VALUE;
// Fill Download update info
DownloadUpdateInfo updateInfo = mUpdateInfo;
updateInfo.gid = info.galleryBase.gid;
updateInfo.title = EhUtils.getSuitableTitle(info.galleryBase);
updateInfo.pages = -1;
updateInfo.downloadedPages = 0;
updateInfo.speed = -1;
updateInfo.legacy = Integer.MAX_VALUE;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(info.galleryBase.gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onFinish(updateInfo);
}
// Update DownloadInfo to DB
DBUtils.updateDownloadInfo(info);
// Continue
ensureDownload();
}
}
// No gallery to download
}
/**
* @param tag null for default
*/
public void startDownload(GalleryBase galleryBase, @Nullable String tag) {
try {
if (mCurrentTask != null && mCurrentTask.galleryBase.gid == galleryBase.gid) {
Log.v(TAG, "It is current task");
return;
}
// Check in download list
for (DownloadInfo info: mDownloadInfos) {
if (info.galleryBase.gid == galleryBase.gid) {
Log.v(TAG, "It is in download list");
if (info.state == DownloadInfo.STATE_FINISH || info.state == DownloadInfo.STATE_NONE) {
info.state = DownloadInfo.STATE_WAIT;
// Add to wait list
mWaitList.add(info);
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(galleryBase.gid, OPS_UPDATE));
// Update DownloadInfo to DB
DBUtils.updateDownloadInfo(info);
} else {
// No need to update
}
return;
}
}
Log.v(TAG, "It is not in download list");
// It is new download info
DownloadInfo downloadInfo = new DownloadInfo();
downloadInfo.galleryBase = galleryBase;
downloadInfo.tag = tag;
downloadInfo.state = DownloadInfo.STATE_WAIT;
List<DownloadInfo> list = getDownloadListInternal(tag, true);
list.add(downloadInfo);
mDownloadInfos.add(downloadInfo);
// Add to wait list
mWaitList.add(downloadInfo);
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(galleryBase.gid, OPS_ADD));
// Save DownloadInfo to DB
DBUtils.addDownloadInfo(downloadInfo);
} finally {
ensureDownload();
}
}
public void startAllDownload() {
for (DownloadInfo info : mDownloadInfos) {
if (info.state == DownloadInfo.STATE_NONE ||
(info.state == DownloadInfo.STATE_FINISH && info.legacy != 0)) {
info.state = DownloadInfo.STATE_WAIT;
// Add to wait list
mWaitList.add(info);
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(info.galleryBase.gid, OPS_UPDATE));
// Update DownloadInfo to DB
DBUtils.updateDownloadInfo(info);
}
}
ensureDownload();
}
public void stopDownload(int gid) {
// Check current task
if (mCurrentTask != null && mCurrentTask.galleryBase.gid == gid) {
// Stop current
stopCurrentDownloadInternal();
// Continue
ensureDownload();
return;
}
// Check in wait list
if (!mWaitList.isEmpty()) {
for (Iterator<DownloadInfo> iter = mWaitList.iterator(); iter.hasNext();) {
DownloadInfo info = iter.next();
if (info.galleryBase.gid == gid) {
// Remove from wait list
iter.remove();
info.state = DownloadInfo.STATE_NONE;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(gid, OPS_UPDATE));
// Update in DB
DBUtils.updateDownloadInfo(info);
return;
}
}
}
}
public void stopCurrentDownload() {
stopCurrentDownloadInternal();
ensureDownload();
}
public void stopAllDownload() {
// Stop all in wait list
for (DownloadInfo info : mWaitList) {
info.state = DownloadInfo.STATE_NONE;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(info.galleryBase.gid, OPS_UPDATE));
// Update in DB
DBUtils.updateDownloadInfo(info);
}
mWaitList.clear();
// Stop current
stopCurrentDownloadInternal();
}
private void stopCurrentDownloadInternal() {
if (mCurrentTask == null) {
return;
}
mCurrentTask.state = DownloadInfo.STATE_NONE;
int gid = mCurrentTask.galleryBase.gid;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onCancel(mUpdateInfo);
}
// Update in DB
DBUtils.updateDownloadInfo(mCurrentTask);
// Release
releaseCurrent();
}
public boolean hasWaitingDownloadInfo() {
return !mWaitList.isEmpty();
}
public boolean isDownloading() {
return mCurrentTask != null || hasWaitingDownloadInfo();
}
private void releaseCurrent() {
if (mCurrentTask != null) {
mCurrentTask = null;
mCurrentSpider.removeSpiderSpiderListener(mDownloadSpiderListener);
GallerySpider.release(mCurrentSpider);
mCurrentSpider = null;
mDownloadSpiderListener.reset();
mSpeedReminder.stop();
}
}
private class DownloadSpiderListener implements SpiderQueen.SpiderListener {
private long mReceived;
private long mTime;
public void start() {
// reset
mReceived = 0;
mTime = SystemClock.uptimeMillis();
}
public void reset() {
// reset
mReceived = 0;
}
// -1 for can't get speed
public long getSpeed() {
if (mCurrentTask == null || mCurrentTask.total == -1) {
return -1;
} else {
long time = SystemClock.uptimeMillis();
long speed = mReceived * 1000 / (time - mTime);
mTime = time;
mReceived = 0;
return speed;
}
}
@Override
public void onTotallyFailed(Exception e) {
onDone(Integer.MAX_VALUE); // TODO
}
@Override
public void onPartlyFailed(Exception e) {
onDone(Integer.MAX_VALUE); // TODO
}
@Override
public void onDone(int legacy) {
if (mCurrentTask == null) {
return;
}
mCurrentTask.state = DownloadInfo.STATE_FINISH;
mCurrentTask.legacy = legacy;
int gid = mCurrentTask.galleryBase.gid;
// Fill download update info
mUpdateInfo.legacy = legacy;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onFinish(mUpdateInfo);
}
// Update to DB
DBUtils.updateDownloadInfo(mCurrentTask);
// Release
releaseCurrent();
}
@Override
public void onGetPages(int pages) {
if (mCurrentTask == null) {
return;
}
mCurrentTask.total = pages;
int gid = mCurrentTask.galleryBase.gid;
// Fill download update info
mUpdateInfo.pages = pages;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onGetPages(mUpdateInfo);
}
}
@Override
public void onSpiderStart(int index, long totalSize) {
}
@Override
public void onSpiderPage(int index, long receivedSize, long singleReceivedSize) {
mReceived += singleReceivedSize;
}
@Override
public void onSpiderSucceed(int index) {
// TODO force request may onSpiderSucceed more than once
if (mCurrentTask == null) {
return;
}
mCurrentTask.download++;
int gid = mCurrentTask.galleryBase.gid;
// Fill download update info
mUpdateInfo.downloadedPages++;
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onDownloadPage(mUpdateInfo);
}
}
@Override
public void onSpiderFailed(int index, Exception e) {
}
@Override
public void onSpiderCancelled(int index) {
}
@Override
public void onGetImage(int index, Object obj) {
}
}
class SpeedReminder implements Runnable {
private boolean mStop = true;
public void start() {
if (mStop) {
mStop = false;
SimpleHandler.getInstance().post(this);
}
}
public void stop() {
if (!mStop) {
mStop = true;
SimpleHandler.getInstance().removeCallbacks(this);
}
}
@Override
public void run() {
if (mCurrentTask != null && mCurrentTask.total >= 0) {
long speed = mDownloadSpiderListener.getSpeed();
// Fill download update info
mUpdateInfo.speed = speed;
mCurrentTask.speed = speed;
if (speed != -1) {
// Notify
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_DOWNLOAD,
makeMix(mCurrentTask.galleryBase.gid, OPS_UPDATE));
// Listener
if (mDownloadUpdateListener != null) {
mDownloadUpdateListener.onDownload(mUpdateInfo);
}
}
}
if (!mStop) {
SimpleHandler.getInstance().postDelayed(this, 2000);
}
}
}
public interface DownloadUpdateListener {
void onStart(DownloadUpdateInfo info);
void onGetPages(DownloadUpdateInfo info);
// Update download pages
void onDownloadPage(DownloadUpdateInfo info);
// Update speed
void onDownload(DownloadUpdateInfo info);
void onFinish(DownloadUpdateInfo info);
void onCancel(DownloadUpdateInfo info);
}
public class DownloadUpdateInfo {
public int gid;
public String title;
public int pages;
public int downloadedPages;
public long speed;
public int legacy;
}
}
@@ -0,0 +1,273 @@
/*
* Copyright 2015 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.service;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.data.GalleryBase;
import com.hippo.yorozuya.FileUtils;
public class DownloadService extends Service implements DownloadManager.DownloadUpdateListener {
private static final String TAG = DownloadService.class.getSimpleName();
public static final String KEY_GALLERY_BASE = "gallery_base";
public static final String KEY_TAG = "tag";
public static final String KEY_GID = "gid";
public static final String ACTION_START = "com.hippo.ehviewer.service.DownloadService.START";
public static final String ACTION_START_ALL = "com.hippo.ehviewer.service.DownloadService.START_ALL";
public static final String ACTION_STOP = "com.hippo.ehviewer.service.DownloadService.STOP";
public static final String ACTION_STOP_CURRENT = "com.hippo.ehviewer.service.DownloadService.STOP_CURRENT";
public static final String ACTION_STOP_ALL = "com.hippo.ehviewer.service.DownloadService.STOP_ALL";
public static final String ACTION_DELETE = "com.hippo.ehviewer.service.DownloadService.DELETE";
public static final String ACTION_CLEAR = "com.hippo.ehviewer.service.DownloadService.CLEAR";
private static final int NOTIFY_ID_DOWNLOADING = -1;
private static final int NOTIFY_ID_DOWNLOAD = -2;
private NotificationManager mNotifyManager;
private NotificationCompat.Builder mDownloadingBuilder;
private NotificationCompat.Builder mDownloadedBuilder;
private static int sSucceeCount = 0;
private static int sFailedCount = 0;
private DownloadManager mDownloadManager;
@Override
public void onCreate() {
super.onCreate();
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mDownloadManager = DownloadManager.getInstance();
mDownloadManager.setDownloadUpdateListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
mDownloadManager.setDownloadUpdateListener(null);
mDownloadManager = null;
mNotifyManager = null;
mDownloadingBuilder = null;
mDownloadedBuilder = null;
}
@Override
public @Nullable IBinder onBind(Intent intent) {
throw new IllegalStateException("No bindService");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
return START_STICKY;
}
private void handleIntent(Intent intent) {
String action = null;
if (intent != null) {
action = intent.getAction();
}
boolean checkStop = false;
if (ACTION_START.equals(action)) {
GalleryBase gb = intent.getParcelableExtra(KEY_GALLERY_BASE);
String tag = intent.getStringExtra(KEY_TAG);
if (gb != null) {
mDownloadManager.startDownload(gb, tag);
}
} else if (ACTION_START_ALL.equals(action)) {
mDownloadManager.startAllDownload();
} else if (ACTION_STOP.equals(action)) {
int gid = intent.getIntExtra(KEY_GID, 0);
if (gid != 0) {
mDownloadManager.stopDownload(gid);
}
checkStop = true;
} else if (ACTION_STOP_CURRENT.equals(action)) {
mDownloadManager.stopCurrentDownload();
checkStop = true;
} else if (ACTION_STOP_ALL.equals(action)) {
mDownloadManager.stopAllDownload();
checkStop = true;
} else if (ACTION_DELETE.equals(action)) {
// TODO
} else if (ACTION_CLEAR.equals(action)) {
sSucceeCount = 0;
sFailedCount = 0;
} else {
checkStop = true;
}
if (checkStop) {
if (!mDownloadManager.isDownloading()) {
stopForeground(true);
stopSelf();
}
}
}
private void ensureDownloadingNotification() {
if (mDownloadingBuilder != null) {
return;
}
mDownloadingBuilder = new NotificationCompat.Builder(getApplicationContext());
mDownloadingBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
mDownloadingBuilder.setOngoing(true).setAutoCancel(false);
// Add action
Intent stopIntent = new Intent(this, DownloadService.class);
stopIntent.setAction(ACTION_STOP_CURRENT);
PendingIntent piStop = PendingIntent.getService(this, 0, stopIntent, 0);
mDownloadingBuilder.addAction(R.drawable.ic_pause_x24, getString(R.string.stop), piStop);
Intent stopAllIntent = new Intent(this, DownloadService.class);
stopAllIntent.setAction(ACTION_STOP_ALL);
PendingIntent piStopAll = PendingIntent.getService(this, 0, stopAllIntent, 0);
mDownloadingBuilder.addAction(R.drawable.ic_close_x24, getString(R.string.stop_all), piStopAll);
}
private void ensureDownloadedNotification() {
if (mDownloadedBuilder != null) {
return;
}
Intent clearIntent = new Intent(this, DownloadService.class);
clearIntent.setAction(ACTION_CLEAR);
PendingIntent piClear = PendingIntent.getService(this, 0, clearIntent, 0);
mDownloadedBuilder = new NotificationCompat.Builder(getApplicationContext());
mDownloadedBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setDeleteIntent(piClear).setOngoing(false).setAutoCancel(true);
}
private void setPendingIntent() {
// TODO
}
@Override
public void onStart(DownloadManager.DownloadUpdateInfo info) {
ensureDownloadingNotification();
setPendingIntent();
mDownloadingBuilder.setContentTitle(info.title)
.setContentText(null)
.setProgress(0, 0, true);
startForeground(NOTIFY_ID_DOWNLOADING, mDownloadingBuilder.build());
}
@Override
public void onGetPages(DownloadManager.DownloadUpdateInfo info) {
ensureDownloadingNotification();
setPendingIntent();
mDownloadingBuilder.setContentTitle(info.title)
.setContentText(null)
.setProgress(info.pages, info.downloadedPages, false);
startForeground(NOTIFY_ID_DOWNLOADING, mDownloadingBuilder.build());
}
@Override
public void onDownloadPage(DownloadManager.DownloadUpdateInfo info) {
ensureDownloadingNotification();
setPendingIntent();
String text = FileUtils.humanReadableByteCount(info.speed, false) + "/S";
mDownloadingBuilder.setContentTitle(info.title)
.setContentText(text)
.setProgress(info.pages, info.downloadedPages, false);
startForeground(NOTIFY_ID_DOWNLOADING, mDownloadingBuilder.build());
}
@Override
public void onDownload(DownloadManager.DownloadUpdateInfo info) {
ensureDownloadingNotification();
setPendingIntent();
String text = FileUtils.humanReadableByteCount(info.speed, false) + "/S";
mDownloadingBuilder.setContentTitle(info.title)
.setContentText(text)
.setProgress(info.pages, info.downloadedPages, false);
startForeground(NOTIFY_ID_DOWNLOADING, mDownloadingBuilder.build());
}
@Override
public void onFinish(DownloadManager.DownloadUpdateInfo info) {
mNotifyManager.cancel(NOTIFY_ID_DOWNLOADING);
ensureDownloadedNotification();
if (info.legacy == 0) {
sSucceeCount++;
} else {
sFailedCount++;
}
if (sSucceeCount + sFailedCount == 1) {
mDownloadedBuilder.setContentTitle(info.title);
if (sSucceeCount == 1) {
mDownloadedBuilder.setContentText(getResources().getString(R.string.successfully_downloaded));
} else {
mDownloadedBuilder.setContentText(getResources().getString(R.string.incompletely_downloaded));
}
} else {
mDownloadedBuilder.setContentTitle(getResources().getString(R.string.downloading_complete));
StringBuilder sb = new StringBuilder();
if (sSucceeCount != 0) {
sb.append(getResources().getQuantityString(R.plurals.item_successfully_downloaded, sSucceeCount, sSucceeCount));
}
if (sFailedCount != 0) {
if (sSucceeCount != 0) {
sb.append("\n");
}
sb.append(getResources().getQuantityString(R.plurals.item_incompletely_downloaded, sFailedCount, sFailedCount));
}
mDownloadedBuilder.setContentText(sb.toString());
}
mNotifyManager.notify(NOTIFY_ID_DOWNLOAD, mDownloadedBuilder.build());
checkStopSelf();
}
@Override
public void onCancel(DownloadManager.DownloadUpdateInfo info) {
mNotifyManager.cancel(NOTIFY_ID_DOWNLOADING);
checkStopSelf();
}
private void checkStopSelf() {
if (!mDownloadManager.hasWaitingDownloadInfo()) {
stopForeground(true);
stopSelf();
}
}
}
@@ -28,6 +28,7 @@ import android.widget.AdapterView;
import com.hippo.ehviewer.Constants;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.ui.scene.DownloadScene;
import com.hippo.ehviewer.ui.scene.DrawerProvider;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.ehviewer.ui.scene.MainSettingsScene;
@@ -42,6 +43,7 @@ import com.hippo.scene.Scene;
import com.hippo.scene.SceneManager;
import com.hippo.scene.StageActivity;
import com.hippo.scene.StageLayout;
import com.hippo.unifile.UniFile;
import com.hippo.vectorold.content.VectorContext;
import com.hippo.widget.DrawerLayout;
import com.hippo.widget.DrawerListView;
@@ -88,9 +90,9 @@ public final class ContentActivity extends StageActivity
case DRAWER_LIST_FAVORITE:
break;
case DRAWER_LIST_DOWNLOAD:
startScene(DownloadScene.class, null, new OffsetCurtain(OffsetCurtain.DIRECTION_BOTTOM));
break;
case DRAWER_LIST_SETTINGS:
mDrawerLayout.closeDrawers();
startScene(MainSettingsScene.class, null, new OffsetCurtain(OffsetCurtain.DIRECTION_BOTTOM));
break;
}
@@ -171,6 +173,14 @@ public final class ContentActivity extends StageActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content);
// TODO check here ?
UniFile uniFile = Settings.getImageDownloadLocation();
if (uniFile != null) {
uniFile.createFile(".nomedia");
} else {
// TODO
}
addSceneStateListener(mSceneStateListener);
mResources = getResources();
@@ -26,7 +26,6 @@ import android.widget.TextView;
import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator;
import com.h6ah4i.android.widget.advrecyclerview.animator.SwipeDismissItemAnimator;
import com.h6ah4i.android.widget.advrecyclerview.decoration.SimpleListDividerDecorator;
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter;
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange;
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager;
@@ -39,6 +38,8 @@ import com.hippo.ehviewer.R;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.AppbarScene;
import com.hippo.widget.recyclerview.EasyRecyclerView;
import com.hippo.widget.recyclerview.LinearDividerItemDecoration;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.ViewUtils;
import java.util.List;
@@ -99,7 +100,12 @@ public abstract class AbsDragSortScene extends AppbarScene {
mRecyclerView.setOnItemClickListener((EasyRecyclerView.OnItemClickListener) mAdapter);
// additional decorations
mRecyclerView.addItemDecoration(new SimpleListDividerDecorator(resources.getDrawable(R.drawable.list_divider), true));
LinearDividerItemDecoration decoration = new LinearDividerItemDecoration(
LinearDividerItemDecoration.VERTICAL,
resources.getColor(R.color.divider_light),
LayoutUtils.dp2pix(getStageActivity(), 1));
decoration.setOverlap(true);
mRecyclerView.addItemDecoration(decoration);
// additional selector
mRecyclerView.setSelector(RippleSalon.generateRippleDrawable(false));
@@ -16,11 +16,434 @@
package com.hippo.ehviewer.ui.scene;
import android.content.Intent;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.hippo.conaco.Conaco;
import com.hippo.ehviewer.Constants;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.EhCacheKeyFactory;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.data.DownloadInfo;
import com.hippo.ehviewer.client.data.GalleryBase;
import com.hippo.ehviewer.service.DownloadManager;
import com.hippo.ehviewer.service.DownloadService;
import com.hippo.ehviewer.ui.ContentActivity;
import com.hippo.ehviewer.util.EhUtils;
import com.hippo.ehviewer.widget.LoadImageView;
import com.hippo.ehviewer.widget.SimpleRatingView;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Scene;
import com.hippo.widget.recyclerview.EasyRecyclerView;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.Messenger;
public class DownloadScene extends Scene {
import java.util.List;
public class DownloadScene extends Scene implements DrawerProvider,
EasyRecyclerView.OnItemClickListener, Messenger.Receiver {
private EasyRecyclerView mRecyclerView;
private DownloadAdapter mAdapter;
private List<DownloadInfo> mDownloadInfos;
private GidPositionMap mGidPositionMap;
private int mLastUpdateSize = -1;
@Override
protected void onInit() {
super.onInit();
mDownloadInfos = DownloadManager.getInstance().getDownloadList(null);
mGidPositionMap = new GidPositionMap();
Messenger.getInstance().register(Constants.MESSENGER_ID_UPDATE_DOWNLOAD, this);
}
@Override
protected void onDie() {
super.onDie();
Messenger.getInstance().unregister(Constants.MESSENGER_ID_UPDATE_DOWNLOAD, this);
}
@Override
protected void onCreate(boolean rebirth) {
super.onCreate(rebirth);
setContentView(R.layout.scene_download);
((ContentActivity) getStageActivity()).setDrawerListActivatedPosition(ContentActivity.DRAWER_LIST_DOWNLOAD);
mRecyclerView = (EasyRecyclerView) findViewById(R.id.recycler_view);
mAdapter = new DownloadAdapter();
mAdapter.setHasStableIds(true);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getStageActivity()));
mRecyclerView.setOnItemClickListener(this);
mRecyclerView.setHasFixedSize(true);
}
@Override
protected void onResume() {
super.onResume();
((ContentActivity) getStageActivity()).setDrawerListActivatedPosition(ContentActivity.DRAWER_LIST_DOWNLOAD);
}
@Override
public boolean showLeftDrawer() {
return true;
}
@Override
public void bindRightDrawer(ContentActivity activity) {
// TODO
activity.clearRightDrawerView();
}
@Override
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
return false;
}
private View.OnClickListener mStartListener = new View.OnClickListener() {
private int getAdapterPosition(View startView) {
EasyRecyclerView recyclerView = mRecyclerView;
for (int i = 0, n = recyclerView.getChildCount(); i < n; i++) {
View child = recyclerView.getChildAt(i);
DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(child);
if (startView == holder.start) {
return holder.getAdapterPosition();
}
}
return -1;
}
@Override
public void onClick(View v) {
int position = getAdapterPosition(v);
if (position >= 0) {
// Add to cache
DownloadInfo info = mDownloadInfos.get(position);
mGidPositionMap.add(info.galleryBase.gid, position);
Intent intent = new Intent(getStageActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_START);
intent.putExtra(DownloadService.KEY_GALLERY_BASE, info.galleryBase);
getStageActivity().startService(intent);
}
}
};
private View.OnClickListener mStopListener = new View.OnClickListener() {
private int getAdapterPosition(View stopView) {
EasyRecyclerView recyclerView = mRecyclerView;
for (int i = 0, n = recyclerView.getChildCount(); i < n; i++) {
View child = recyclerView.getChildAt(i);
DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(child);
if (stopView == holder.stop) {
return holder.getAdapterPosition();
}
}
return -1;
}
@Override
public void onClick(View v) {
int position = getAdapterPosition(v);
if (position >= 0) {
// Add to cache
DownloadInfo info = mDownloadInfos.get(position);
mGidPositionMap.add(info.galleryBase.gid, position);
Intent intent = new Intent(getStageActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_STOP);
intent.putExtra(DownloadService.KEY_GID, info.galleryBase.gid);
getStageActivity().startService(intent);
}
}
};
private View.OnClickListener mDeleteListener = new View.OnClickListener() {
private int getAdapterPosition(View deleteView) {
EasyRecyclerView recyclerView = mRecyclerView;
for (int i = 0, n = recyclerView.getChildCount(); i < n; i++) {
View child = recyclerView.getChildAt(i);
DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(child);
if (deleteView == holder.delete) {
return holder.getAdapterPosition();
}
}
return -1;
}
@Override
public void onClick(View v) {
int position = getAdapterPosition(v);
if (position >= 0) {
// Add to cache
DownloadInfo info = mDownloadInfos.get(position);
mGidPositionMap.add(info.galleryBase.gid, position);
Intent intent = new Intent(getStageActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_DELETE);
intent.putExtra(DownloadService.KEY_GID, info.galleryBase.gid);
getStageActivity().startService(intent);
}
}
};
private int getPositionForGid(int gid) {
if (mDownloadInfos == null) {
return -1;
}
int position = mGidPositionMap.getPosition(gid);
if (position == -1) {
for (int i = 0, n = mDownloadInfos.size(); i < n; i++) {
DownloadInfo info = mDownloadInfos.get(i);
if (info.galleryBase.gid == gid) {
position = i;
break;
}
}
}
return position;
}
@Override
public void onReceive(int id, Object obj) {
if (id == Constants.MESSENGER_ID_UPDATE_DOWNLOAD) {
if (!(obj instanceof Integer)) {
return;
}
if (mAdapter == null || mDownloadInfos == null) {
return;
}
int mix = (int) obj;
int ops = DownloadManager.getOps(mix);
int gid = DownloadManager.getGid(mix);
if (ops == DownloadManager.OPS_ALL_CHANGE) {
mAdapter.notifyDataSetChanged();
return;
}
int position = getPositionForGid(gid);
if (position == -1 && mLastUpdateSize != mAdapter.getItemCount()) {
// Can't get position, notifyDataSetChanged for safe
mAdapter.notifyDataSetChanged();
return;
}
if (ops == DownloadManager.OPS_ADD) {
mAdapter.notifyItemInserted(position);
} else if (ops == DownloadManager.OPS_REMOVE) {
mAdapter.notifyItemRemoved(position);
} else if (ops == DownloadManager.OPS_UPDATE) {
RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(position);
if (holder != null) {
bindHolder((DownloadHolder) holder, mDownloadInfos.get(position));
}
}
}
}
private class DownloadHolder extends RecyclerView.ViewHolder {
public LoadImageView thumb;
public TextView title;
public TextView uploader;
public SimpleRatingView rating;
public TextView category;
public View start;
public View stop;
public View delete;
public TextView state;
public ProgressBar progressBar;
public TextView percent;
public TextView speed;
public DownloadHolder(View itemView) {
super(itemView);
thumb = (LoadImageView) itemView.findViewById(R.id.thumb);
title = (TextView) itemView.findViewById(R.id.title);
uploader = (TextView) itemView.findViewById(R.id.uploader);
rating = (SimpleRatingView) itemView.findViewById(R.id.rating);
category = (TextView) itemView.findViewById(R.id.category);
start = itemView.findViewById(R.id.start);
stop = itemView.findViewById(R.id.stop);
delete = itemView.findViewById(R.id.delete);
state = (TextView) itemView.findViewById(R.id.state);
progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar);
percent = (TextView) itemView.findViewById(R.id.percent);
speed = (TextView) itemView.findViewById(R.id.speed);
RippleSalon.addRipple(start, false);
RippleSalon.addRipple(stop, false);
RippleSalon.addRipple(delete, false);
start.setOnClickListener(mStartListener);
stop.setOnClickListener(mStopListener);
delete.setOnClickListener(mDeleteListener);
}
}
private void bindState(DownloadHolder holder, DownloadInfo info, String state) {
GalleryBase galleryBase = info.galleryBase;
EhApplication.getConaco(getStageActivity()).load(holder.thumb,
EhCacheKeyFactory.getThumbKey(galleryBase.gid), galleryBase.thumb);
holder.title.setText(EhUtils.getSuitableTitle(galleryBase));
holder.uploader.setVisibility(View.VISIBLE);
holder.rating.setVisibility(View.VISIBLE);
holder.category.setVisibility(View.VISIBLE);
holder.state.setVisibility(View.VISIBLE);
holder.uploader.setText(galleryBase.uploader);
holder.rating.setRating(galleryBase.rating);
TextView category = holder.category;
String newCategoryText = EhUtils.getCategory(galleryBase.category);
if (!newCategoryText.equals(category.getText())) {
category.setText(newCategoryText);
category.setBackgroundColor(EhUtils.getCategoryColor(galleryBase.category));
}
holder.state.setText(state);
holder.progressBar.setVisibility(View.INVISIBLE);
holder.percent.setVisibility(View.INVISIBLE);
holder.speed.setVisibility(View.INVISIBLE);
}
private void bindProgress(DownloadHolder holder, DownloadInfo info) {
GalleryBase galleryBase = info.galleryBase;
EhApplication.getConaco(getStageActivity()).load(holder.thumb,
EhCacheKeyFactory.getThumbKey(info.galleryBase.gid), galleryBase.thumb);
holder.title.setText(EhUtils.getSuitableTitle(galleryBase));
holder.uploader.setVisibility(View.INVISIBLE);
holder.rating.setVisibility(View.INVISIBLE);
holder.category.setVisibility(View.INVISIBLE);
holder.state.setVisibility(View.INVISIBLE);
holder.progressBar.setVisibility(View.VISIBLE);
if (info.total <= 0 || info.speed < 0) {
holder.progressBar.setIndeterminate(true);
holder.percent.setVisibility(View.INVISIBLE);
holder.speed.setVisibility(View.INVISIBLE);
} else {
holder.progressBar.setIndeterminate(false);
holder.progressBar.setMax(info.total);
holder.progressBar.setProgress(info.download);
holder.percent.setVisibility(View.VISIBLE);
holder.speed.setVisibility(View.VISIBLE);
holder.percent.setText(info.download + "/" + info.total);
holder.speed.setText(FileUtils.humanReadableByteCount(info.speed, false) + "/S");
}
}
private void bindHolder(DownloadHolder holder, DownloadInfo info) {
switch (info.state) {
case DownloadInfo.STATE_NONE:
default:
bindState(holder, info, getContext().getResources().getString(R.string.download_state_none));
break;
case DownloadInfo.STATE_WAIT:
bindState(holder, info, getContext().getResources().getString(R.string.download_state_wait));
break;
case DownloadInfo.STATE_DOWNLOAD:
bindProgress(holder, info);
break;
case DownloadInfo.STATE_FINISH:
if (info.legacy == 0) {
bindState(holder, info, getContext().getResources().getString(R.string.download_state_done));
} else if (info.legacy > 0 && info.legacy != Integer.MAX_VALUE) {
bindState(holder, info, getContext().getResources().getString(R.string.download_state_incomplete, info.legacy));
} else {
bindState(holder, info, getContext().getResources().getString(R.string.download_state_incomplete));
}
break;
}
}
private class DownloadAdapter extends RecyclerView.Adapter<DownloadHolder> {
Conaco mConaco;
public DownloadAdapter() {
mConaco = EhApplication.getConaco(getStageActivity());
}
@Override
public DownloadHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new DownloadHolder(getStageActivity().getLayoutInflater().inflate(R.layout.item_download, parent, false));
}
@Override
public void onBindViewHolder(DownloadHolder holder, int position) {
DownloadInfo info = mDownloadInfos.get(position);
bindHolder(holder, info);
}
@Override
public long getItemId(int position) {
return mDownloadInfos.get(position).galleryBase.gid;
}
@Override
public int getItemCount() {
mLastUpdateSize = mDownloadInfos != null ? mDownloadInfos.size() : 0;
return mLastUpdateSize;
}
}
private static class GidPositionMap {
private static final int SIZE = 12;
private int[] mGids = new int[SIZE];
private int[] mPosition = new int[SIZE];
private int mStartIndex = 0;
private int mSize = 0;
public void add(int gid, int position) {
int index = (mStartIndex + mSize) % SIZE;
mGids[index] = gid;
mPosition[index] = position;
// Update start index size and
mSize++;
if (mSize > SIZE) {
mStartIndex = (mStartIndex + mSize) % SIZE;
mSize = Math.min(mSize, SIZE);
}
}
public void clear() {
mSize = 0;
}
public int getPosition(int gid) {
for (int i = 0; i < mSize; i++) {
int index = (mStartIndex + i) % SIZE;
if (gid == mGids[index]) {
return mPosition[index];
}
}
return -1;
}
}
}
@@ -46,6 +46,7 @@ import com.hippo.ehviewer.client.data.GalleryBase;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.data.PreviewSet;
import com.hippo.ehviewer.client.data.TagGroup;
import com.hippo.ehviewer.service.DownloadService;
import com.hippo.ehviewer.ui.ContentActivity;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.util.EhUtils;
@@ -558,7 +559,10 @@ public class GalleryDetailScene extends Scene implements View.OnClickListener,
requestGalleryDetail(mGid, mToken);
}
} else if (mDownload == v) {
// TODO
Intent intent = new Intent(getStageActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_START);
intent.putExtra(DownloadService.KEY_GALLERY_BASE, mGalleryDetail);
getStageActivity().startService(intent);
} else if (mRead == v) {
GalleryBase galleryBase = mGalleryDetail;
if (galleryBase == null) {
@@ -75,7 +75,9 @@ import com.hippo.util.AnimationUtils;
import com.hippo.widget.FabLayout;
import com.hippo.widget.FloatingActionButton;
import com.hippo.widget.recyclerview.EasyRecyclerView;
import com.hippo.widget.recyclerview.LinearDividerItemDecoration;
import com.hippo.yorozuya.AssertUtils;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.Messenger;
import com.hippo.yorozuya.SimpleHandler;
@@ -303,27 +305,6 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe
mSearchBarOriginalTop = lp.topMargin;
mSearchBarOriginalBottom = lp.topMargin + mSearchBar.getMeasuredHeight();
/*
mSearchBar.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(mSearchBar.getViewTreeObserver(), this);
mSearchBarOriginalTop = mSearchBar.getTop();
mSearchBarOriginalBottom = mSearchBar.getBottom();
int fitPaddingTop = mSearchBar.getHeight() + mResources.getDimensionPixelOffset(R.dimen.search_bar_padding_vertical);
mContentLayout.setFitPaddingTop(fitPaddingTop);
mSearchLayout.setFitPaddingTop(fitPaddingTop);
Log.d("TAG", "fitPaddingTop = " + fitPaddingTop);
Log.d("TAG", "mSearchBarOriginalTop = " + mSearchBarOriginalTop);
Log.d("TAG", "mSearchBarOriginalBottom = " + mSearchBarOriginalBottom);
}
});
*/
// Content Layout
// onRebirth may create new GalleryListHelper
if (mGalleryListHelper == null) {
@@ -365,6 +346,12 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe
mQuickSearchView.setOnItemClickListener(new QuickSearchClickListener());
mQuickSearchView.hasFixedSize();
mQuickSearchView.setClipToPadding(false);
LinearDividerItemDecoration decoration = new LinearDividerItemDecoration(
LinearDividerItemDecoration.VERTICAL,
mResources.getColor(R.color.divider_light),
LayoutUtils.dp2pix(getStageActivity(), 1));
decoration.setOverlap(true);
mQuickSearchView.addItemDecoration(decoration);
// When scene start
if (!rebirth) {
@@ -1279,6 +1266,9 @@ public final class GalleryListScene extends Scene implements ListSearchBar.Helpe
showSearchBar(true);
mGalleryListHelper.refresh();
((ContentActivity) getStageActivity()).closeDrawers();
mDrawerListActivatedPosition = ContentActivity.DRAWER_LIST_NONE;
mActivity.setDrawerListActivatedPosition(ContentActivity.DRAWER_LIST_NONE);
return true;
}
}
@@ -196,7 +196,7 @@ public class ModifyQuickSearchScene extends AppbarScene implements SearchLayout.
// New name
mQuickSearch.name = mNewName;
}
DBUtils.modifyQuickSearch(mQuickSearch);
DBUtils.updateQuickSearch(mQuickSearch);
Messenger.getInstance().notify(Constants.MESSENGER_ID_UPDATE_QUICK_SEARCH, null);
if (newName) {
@@ -20,6 +20,7 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import com.hippo.ehviewer.client.data.DownloadInfo;
import com.hippo.ehviewer.client.data.GalleryBase;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.data.QuickSearch;
@@ -27,6 +28,10 @@ import com.hippo.ehviewer.dao.DaoMaster;
import com.hippo.ehviewer.dao.DaoSession;
import com.hippo.ehviewer.dao.DirnameObj;
import com.hippo.ehviewer.dao.DirnameObjDao;
import com.hippo.ehviewer.dao.DownloadInfoObj;
import com.hippo.ehviewer.dao.DownloadInfoObjDao;
import com.hippo.ehviewer.dao.DownloadTagObj;
import com.hippo.ehviewer.dao.DownloadTagObjDao;
import com.hippo.ehviewer.dao.GalleryBaseObj;
import com.hippo.ehviewer.dao.GalleryBaseObjDao;
import com.hippo.ehviewer.dao.QuickSearchObj;
@@ -84,6 +89,26 @@ public class DBUtils {
}
}
public static GalleryBase getGalleryBase(int gid) {
GalleryBaseObjDao dao = sDaoSession.getGalleryBaseObjDao();
GalleryBaseObj obj = dao.load((long) gid);
if (obj != null) {
GalleryBase galleryBase = new GalleryBase();
galleryBase.gid = (int) (long) obj.getGid();
galleryBase.token = obj.getToken();
galleryBase.title = obj.getTitle();
galleryBase.titleJpn = obj.getTitleJpn();
galleryBase.thumb = obj.getThumb();
galleryBase.category = obj.getCategory();
galleryBase.posted = obj.getPosted();
galleryBase.uploader = obj.getUploader();
galleryBase.rating = obj.getRating();
return galleryBase;
} else {
return null;
}
}
public static String getDirname(int gid) {
DirnameObjDao dirnameObjDao = sDaoSession.getDirnameObjDao();
DirnameObj dirnameObj = dirnameObjDao.load((long) gid);
@@ -147,7 +172,7 @@ public class DBUtils {
dao.update(to);
}
public static void modifyQuickSearch(QuickSearch quickSearch) {
public static void updateQuickSearch(QuickSearch quickSearch) {
QuickSearchObjDao dao = sDaoSession.getQuickSearchObjDao();
QuickSearchObj obj = dao.load(quickSearch.id);
obj.setName(quickSearch.name);
@@ -172,4 +197,51 @@ public class DBUtils {
}
return result;
}
public static List<DownloadInfo> getAllDownloadInfo() {
DownloadInfoObjDao dao = sDaoSession.getDownloadInfoObjDao();
List<DownloadInfoObj> list = dao.queryBuilder().orderAsc(DownloadInfoObjDao.Properties.Time).list();
List<DownloadInfo> result = new ArrayList<>(list.size());
for (DownloadInfoObj obj : list) {
result.add(DownloadInfo.fromDownloadInfoObj(obj));
}
return result;
}
public static void addDownloadInfo(DownloadInfo downloadInfo) {
DownloadInfoObj obj = new DownloadInfoObj();
obj.setGid((long) downloadInfo.galleryBase.gid);
obj.setTag(downloadInfo.tag);
obj.setState(downloadInfo.state);
obj.setLegacy(downloadInfo.legacy);
obj.setTime(System.currentTimeMillis());
sDaoSession.getDownloadInfoObjDao().insert(obj);
addGalleryBase(downloadInfo.galleryBase);
}
public static void updateDownloadInfo(DownloadInfo downloadInfo) {
DownloadInfoObjDao dao = sDaoSession.getDownloadInfoObjDao();
DownloadInfoObj obj = dao.load((long) downloadInfo.galleryBase.gid);
obj.setState(downloadInfo.state);
obj.setLegacy(downloadInfo.legacy);
dao.update(obj);
}
public static List<String> getAllDownloadTag() {
DownloadTagObjDao dao = sDaoSession.getDownloadTagObjDao();
List<DownloadTagObj> list = dao.queryBuilder().list();
List<String> result = new ArrayList<>(list.size());
for (DownloadTagObj obj : list) {
result.add(obj.getTag());
}
return result;
}
public static void addDownloadTag(String tag) {
DownloadTagObj obj = new DownloadTagObj();
obj.setTag(tag);
sDaoSession.getDownloadTagObjDao().insert(obj);
}
}
@@ -51,7 +51,7 @@ public class DownloadClient {
out.write(data, 0, bytesRead);
receivedSize += bytesRead;
if (listener != null) {
listener.onDonwlad(receivedSize);
listener.onDonwlad(receivedSize, bytesRead);
}
}
@@ -207,7 +207,7 @@ public class DownloadClient {
}
@Override
public void onDonwlad(long receivedSize) {
public void onDonwlad(long receivedSize, long singleReceivedSize) {
}
@Override
@@ -227,7 +227,7 @@ public class DownloadClient {
void onConnect(long totalSize);
void onDonwlad(long receivedSize);
void onDonwlad(long receivedSize, long singleReceivedSize);
void onFailed(Exception e);
@@ -61,7 +61,7 @@ public class DownloadRequest {
public void cancel() {
if (mHttpRequest != null) {
mHttpRequest.disconnect();
mHttpRequest.cancel();
}
}
}
@@ -22,6 +22,7 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
@@ -29,6 +30,7 @@ import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
import com.hippo.ehviewer.client.data.DownloadInfo;
import com.hippo.yorozuya.AssertUtils;
/**
@@ -100,6 +102,13 @@ public abstract class Scene {
return stageActivity;
}
/**
* Aliases of {@link #getStageActivity()}
*/
public Context getContext() {
return getStageActivity();
}
public int getLaunchMode() {
return LAUNCH_MODE_STANDARD;
}
@@ -24,6 +24,7 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
// Add overlap
/**
* Only work for {@link android.support.v7.widget.LinearLayoutManager}.<br>
* Show divider between item, just like
@@ -44,6 +45,8 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
private int mPaddingStart = 0;
private int mPaddingEnd = 0;
private boolean mOverlap = false;
private ShowDividerHelper mShowDividerHelper;
public LinearDividerItemDecoration(int orientation, int color, int thickness) {
@@ -95,9 +98,23 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
mPaddingEnd = paddingEnd;
}
public void setOverlap(boolean overlap) {
mOverlap = overlap;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
if (parent.getAdapter() == null) {
outRect.set(0, 0, 0, 0);
return;
}
if (mOverlap) {
outRect.set(0, 0, 0, 0);
return;
}
final int position = parent.getChildLayoutPosition(view);
final int itemCount = parent.getAdapter().getItemCount();
@@ -147,6 +164,7 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
}
int itemCount = adapter.getItemCount();
boolean overlap = mOverlap;
if (mOrientation == VERTICAL) {
final boolean isRtl = ViewCompat.getLayoutDirection(parent) == ViewCompat.LAYOUT_DIRECTION_RTL;
@@ -176,7 +194,10 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
show = position != itemCount - 1 || mShowLastDivider;
}
if (show) {
final int top = child.getBottom() + lp.bottomMargin;
int top = child.getBottom() + lp.bottomMargin;
if (overlap) {
top -= mThickness;
}
final int bottom = top + mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
@@ -189,7 +210,10 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
show = mShowFirstDivider;
}
if (show) {
final int bottom = child.getTop() + lp.topMargin;
int bottom = child.getTop() + lp.topMargin;
if (overlap) {
bottom += mThickness;
}
final int top = bottom - mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
@@ -213,7 +237,10 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
show = position != itemCount - 1 || mShowLastDivider;
}
if (show) {
final int left = child.getRight() + lp.rightMargin;
int left = child.getRight() + lp.rightMargin;
if (overlap) {
left -= mThickness;
}
final int right = left + mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);
@@ -226,7 +253,10 @@ public class LinearDividerItemDecoration extends RecyclerView.ItemDecoration {
show = mShowFirstDivider;
}
if (show) {
final int right = child.getLeft() + lp.leftMargin;
int right = child.getLeft() + lp.leftMargin;
if (overlap) {
right += mThickness;
}
final int left = right - mThickness;
mRect.set(left, top, right, bottom);
c.drawRect(mRect, mPaint);