diff --git a/app/build.gradle b/app/build.gradle
index 59cf0dee1..945c9ebb3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,13 +2,13 @@ apply plugin: 'com.android.model.application'
model {
android {
- compileSdkVersion = 22
- buildToolsVersion = "23.0.0 rc3"
+ compileSdkVersion = 23
+ buildToolsVersion = "23.0.0"
defaultConfig.with {
applicationId = "com.hippo.ehviewer.test"
minSdkVersion.apiLevel = 14
- targetSdkVersion.apiLevel = 22
+ targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
}
@@ -45,8 +45,8 @@ model {
}
dependencies {
- compile 'com.android.support:support-v4:22.2.1'
- compile 'com.android.support:appcompat-v7:22.2.1'
+ compile 'com.android.support:support-v4:23.0.0'
+ compile 'com.android.support:appcompat-v7:23.0.0'
compile 'de.greenrobot:greendao:1.3.7'
compile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
compile fileTree(dir: 'libs', include: ['*.jar'])
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e09a9e938..c3ad241bc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -51,6 +51,11 @@
+
+
diff --git a/app/src/main/java/com/hippo/ehviewer/Constants.java b/app/src/main/java/com/hippo/ehviewer/Constants.java
index a48b91cda..41a9ebc38 100644
--- a/app/src/main/java/com/hippo/ehviewer/Constants.java
+++ b/app/src/main/java/com/hippo/ehviewer/Constants.java
@@ -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();
}
}
diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java
index c31dc8ca0..d29cdfc0f 100644
--- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java
+++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java
@@ -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) {
diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java
index 16ca9ca73..49c7f6e63 100644
--- a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java
@@ -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();
+ }
}
}
}
diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java
index e4c3e512d..e526a7664 100644
--- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java
+++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java
@@ -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;
diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java b/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java
new file mode 100644
index 000000000..5e34666a9
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/client/data/DownloadInfo.java
@@ -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;
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/GallerySpider.java b/app/src/main/java/com/hippo/ehviewer/gallery/GallerySpider.java
index fe0047f27..788048311 100644
--- a/app/src/main/java/com/hippo/ehviewer/gallery/GallerySpider.java
+++ b/app/src/main/java/com/hippo/ehviewer/gallery/GallerySpider.java
@@ -58,7 +58,8 @@ public class GallerySpider implements GalleryProvider, SpiderQueen.SpiderListene
private AtomicLongArray mReceivedSizes;
private AtomicReferenceArray mPercents;
- private List mGalleryProviderListeners = new ArrayList<>();
+ private List mGalleryProviderListeners = new ArrayList<>(2);
+ private List 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() {
diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ImageHandler.java b/app/src/main/java/com/hippo/ehviewer/gallery/ImageHandler.java
index 064520166..844d4f242 100644
--- a/app/src/main/java/com/hippo/ehviewer/gallery/ImageHandler.java
+++ b/app/src/main/java/com/hippo/ehviewer/gallery/ImageHandler.java
@@ -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;
diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/SpiderInfo.java b/app/src/main/java/com/hippo/ehviewer/gallery/SpiderInfo.java
index 4e0b9b5d1..e708833c2 100644
--- a/app/src/main/java/com/hippo/ehviewer/gallery/SpiderInfo.java
+++ b/app/src/main/java/com/hippo/ehviewer/gallery/SpiderInfo.java
@@ -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 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 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();
- }
}
diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/SpiderQueen.java b/app/src/main/java/com/hippo/ehviewer/gallery/SpiderQueen.java
index b8596b679..9a3f47142 100644
--- a/app/src/main/java/com/hippo/ehviewer/gallery/SpiderQueen.java
+++ b/app/src/main/java/com/hippo/ehviewer/gallery/SpiderQueen.java
@@ -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 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);
}
}
diff --git a/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java b/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java
new file mode 100644
index 000000000..d1d2c8c91
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/service/DownloadManager.java
@@ -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 mDownloadInfos;
+ private Map> mDownloadInfoMap;
+ private List mDefaultDownloadInfos;
+
+ private List 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 tags = DBUtils.getAllDownloadTag();
+ List downloadInfos = DBUtils.getAllDownloadInfo();
+ mDownloadInfos = downloadInfos;
+ mDownloadInfoMap = new HashMap<>();
+
+ // Create list for each tag
+ for (String tag : tags) {
+ mDownloadInfoMap.put(tag, new ArrayList());
+ }
+ // 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 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 getDownloadList(String tag) {
+ return getDownloadListInternal(tag, false);
+ }
+
+ private List getDownloadListInternal(String tag, boolean create) {
+ if (TextUtils.isEmpty(tag)) {
+ return mDefaultDownloadInfos;
+ } else {
+ List 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 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 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;
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java b/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java
new file mode 100644
index 000000000..79a8b350b
--- /dev/null
+++ b/app/src/main/java/com/hippo/ehviewer/service/DownloadService.java
@@ -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();
+ }
+ }
+}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java
index dbd168438..6ee4aeca9 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/ContentActivity.java
@@ -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();
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/AbsDragSortScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/AbsDragSortScene.java
index 1dac34814..7657f1fbb 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/AbsDragSortScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/AbsDragSortScene.java
@@ -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));
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java
index c53f22431..d6db5891d 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadScene.java
@@ -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 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 {
+
+ 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;
+ }
+ }
}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java
index ac05e27af..76cb8e0bc 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java
@@ -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) {
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
index 155f997b4..c33472b17 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java
@@ -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;
}
}
diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java
index 4bbcf9e8c..a5866a72b 100644
--- a/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java
+++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/ModifyQuickSearchScene.java
@@ -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) {
diff --git a/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java b/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java
index ca6fdbedf..d8c46f6ba 100644
--- a/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java
+++ b/app/src/main/java/com/hippo/ehviewer/util/DBUtils.java
@@ -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 getAllDownloadInfo() {
+ DownloadInfoObjDao dao = sDaoSession.getDownloadInfoObjDao();
+ List list = dao.queryBuilder().orderAsc(DownloadInfoObjDao.Properties.Time).list();
+ List 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 getAllDownloadTag() {
+ DownloadTagObjDao dao = sDaoSession.getDownloadTagObjDao();
+ List list = dao.queryBuilder().list();
+ List 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);
+ }
}
diff --git a/app/src/main/java/com/hippo/network/DownloadClient.java b/app/src/main/java/com/hippo/network/DownloadClient.java
index 587479978..6db8718d3 100644
--- a/app/src/main/java/com/hippo/network/DownloadClient.java
+++ b/app/src/main/java/com/hippo/network/DownloadClient.java
@@ -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);
diff --git a/app/src/main/java/com/hippo/network/DownloadRequest.java b/app/src/main/java/com/hippo/network/DownloadRequest.java
index 2f818c6a3..c2281f8e5 100644
--- a/app/src/main/java/com/hippo/network/DownloadRequest.java
+++ b/app/src/main/java/com/hippo/network/DownloadRequest.java
@@ -61,7 +61,7 @@ public class DownloadRequest {
public void cancel() {
if (mHttpRequest != null) {
- mHttpRequest.disconnect();
+ mHttpRequest.cancel();
}
}
}
diff --git a/app/src/main/java/com/hippo/scene/Scene.java b/app/src/main/java/com/hippo/scene/Scene.java
index ec1e17da7..f6d2d0745 100644
--- a/app/src/main/java/com/hippo/scene/Scene.java
+++ b/app/src/main/java/com/hippo/scene/Scene.java
@@ -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;
}
diff --git a/app/src/main/java/com/hippo/widget/recyclerview/LinearDividerItemDecoration.java b/app/src/main/java/com/hippo/widget/recyclerview/LinearDividerItemDecoration.java
index d290bf032..8817fb62e 100644
--- a/app/src/main/java/com/hippo/widget/recyclerview/LinearDividerItemDecoration.java
+++ b/app/src/main/java/com/hippo/widget/recyclerview/LinearDividerItemDecoration.java
@@ -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}.
* 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);
diff --git a/app/src/main/res/drawable/ic_close_x24.xml b/app/src/main/res/drawable/ic_close_x24.xml
new file mode 100644
index 000000000..02e8b4398
--- /dev/null
+++ b/app/src/main/res/drawable/ic_close_x24.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_pause_x24.xml b/app/src/main/res/drawable/ic_pause_x24.xml
new file mode 100644
index 000000000..5c5ec333d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_pause_x24.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_play_x24.xml b/app/src/main/res/drawable/ic_play_x24.xml
new file mode 100644
index 000000000..49fe1d385
--- /dev/null
+++ b/app/src/main/res/drawable/ic_play_x24.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/list_divider.xml b/app/src/main/res/drawable/list_divider.xml
deleted file mode 100644
index a7e38fe80..000000000
--- a/app/src/main/res/drawable/list_divider.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_download.xml b/app/src/main/res/layout/item_download.xml
new file mode 100644
index 000000000..23ca52b6f
--- /dev/null
+++ b/app/src/main/res/layout/item_download.xml
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/scene_download.xml b/app/src/main/res/layout/scene_download.xml
new file mode 100644
index 000000000..8dbd40aab
--- /dev/null
+++ b/app/src/main/res/layout/scene_download.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml
index bb35d60de..5f90920d2 100644
--- a/app/src/main/res/values/pathdata.xml
+++ b/app/src/main/res/values/pathdata.xml
@@ -48,6 +48,9 @@
M11.6,7v7.1c0,1-0.3,1.8-0.9,2.4c-0.6,0.6-1.5,1-2.5,1.1l-0.4,0c-1.1,0-2.1-0.3-2.7-0.9c-0.7-0.6-1-1.5-1-2.6V7h1.4v7.1c0,0.8,0.2,1.3,0.6,1.8c0.4,0.4,1,0.6,1.8,0.6c0.8,0,1.4-0.2,1.8-0.6c0.4-0.4,0.6-1,0.6-1.8V7H11.6M17.3,10.9c-0.2,0-0.4-0.1-0.7-0.1c-0.9,0-1.5,0.4-1.8,1.1v5.5h-1.3V9.7h1.3l0,0.9c0.4-0.7,1.1-1,1.9-1c0.3,0,0.5,0,0.6,0.1V10.9M20,17.5h-1.3V6.4H20V17.5z
M11,19V9A2,2 0 0,0 9,7H5V17H9A2,2 0 0,1 11,19M13,9V19A2,2 0 0,1 15,17H19V7H15A2,2 0 0,0 13,9M21,19H15A2,2 0 0,0 13,21H11A2,2 0 0,0 9,19H3V5H9A2,2 0 0,1 11,7H13A2,2 0 0,1 15,5H21V19Z
M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z
+ M8,5.14V19.14L19,12.14L8,5.14Z
+ M14,19.14H18V5.14H14M6,19.14H10V5.14H6V19.14Z
+ M19,7.1L16.9 5 12 9.9 7.1 5 5 7.1 9.9 12 5 16.9 7.1 19 12 14.1 16.9 19 19 16.9 14.1 12 z
M13,0c7.2,0,13,5.9,13,13.2c0,3.7-2,7.6-7.3,12.9L13,32l-5.8-5.8C2,20.8,0,16.9,0,13.3C0,5.9,5.8,0,13,0z
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 60636f3ff..1fc5265bc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -216,6 +216,27 @@
\"What ?\"
\"I lose my panda …\"
+
+ Download server
+ Stop
+ Stop all
+ Successfully downloaded
+ Incompletely downloaded
+ Downloading complete
+
+ - One item successfully downloaded
+ - %d items successfully downloaded
+
+
+ - One item incompletely downloaded
+ - %d items incompletely downloaded
+
+ IDLE
+ Waiting
+ Done
+ Done
+ Incomplete
+
Keyword
Search
Retry
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 66b19a3b4..923d12ae7 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -97,6 +97,7 @@
+
+
diff --git a/beerbelly b/beerbelly
index f2b6766de..2b2da1551 160000
--- a/beerbelly
+++ b/beerbelly
@@ -1 +1 @@
-Subproject commit f2b6766de55bdbe46d09be40179f8fa602567783
+Subproject commit 2b2da15514cd0b1ec50625f16dfcd7a137b90848
diff --git a/cardsalon b/cardsalon
index 97d1d3082..18444fc75 160000
--- a/cardsalon
+++ b/cardsalon
@@ -1 +1 @@
-Subproject commit 97d1d3082284eac5e36543303ee6433e3e2bbe33
+Subproject commit 18444fc75ef70a335884f395d1ddf5080e216ed5
diff --git a/conaco b/conaco
index 6bd893739..5c1fea7a1 160000
--- a/conaco
+++ b/conaco
@@ -1 +1 @@
-Subproject commit 6bd89373911617204a925839e627a32578fa9cb2
+Subproject commit 5c1fea7a11296df6f16126dbeb0b52f906e629d8
diff --git a/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java b/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java
index 4f2a6d208..98647ca62 100644
--- a/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java
+++ b/daogenerator/src/main/java/com/hippo/ehviewer/EhDaoGenerator.java
@@ -37,6 +37,7 @@ public class EhDaoGenerator {
addGalleryBase(schema);
addQuickSearch(schema);
addDownloadInfo(schema);
+ addDownloadTag(schema);
addDirname(schema);
addReadInfo(schema);
new DaoGenerator().generateAll(schema, OUT_DIR);
@@ -71,12 +72,20 @@ public class EhDaoGenerator {
}
public static void addDownloadInfo(Schema schema) {
- Entity downloadInfo = schema.addEntity("DownloadInfoObj");
- downloadInfo.setTableName("DOWNLOAD_INFO");
- downloadInfo.addLongProperty("gid").primaryKey();
- downloadInfo.addIntProperty("state");
- downloadInfo.addIntProperty("legacy");
- downloadInfo.addLongProperty("time");
+ Entity entity = schema.addEntity("DownloadInfoObj");
+ entity.setTableName("DOWNLOAD_INFO");
+ entity.addLongProperty("gid").primaryKey();
+ entity.addStringProperty("tag");
+ entity.addIntProperty("state");
+ entity.addIntProperty("legacy");
+ entity.addLongProperty("time");
+ }
+
+ public static void addDownloadTag(Schema schema) {
+ Entity entity = schema.addEntity("DownloadTagObj");
+ entity.setTableName("DOWNLOAD_TAG");
+ entity.addIdProperty();
+ entity.addStringProperty("tag");
}
public static void addDirname(Schema schema) {
diff --git a/hotspot b/hotspot
index 04129925b..2783f8b9c 160000
--- a/hotspot
+++ b/hotspot
@@ -1 +1 @@
-Subproject commit 04129925be33a1c84b3df51c8a8cb2b22fb907a6
+Subproject commit 2783f8b9c0dc42cacdd887703be64fdd5a8bdcbf
diff --git a/httpclient b/httpclient
index c4288ea90..005aad851 160000
--- a/httpclient
+++ b/httpclient
@@ -1 +1 @@
-Subproject commit c4288ea904a7e386205dbc98b5897509dd39cb6d
+Subproject commit 005aad85170c0405f73bc4388b3f2e92149bc92d
diff --git a/rippleold b/rippleold
index f6aeb8cb1..53a806d99 160000
--- a/rippleold
+++ b/rippleold
@@ -1 +1 @@
-Subproject commit f6aeb8cb1c4aac5a7a0149263e6c7334d439ca43
+Subproject commit 53a806d99dbc351ffd01a13df9f3bea77fca8873
diff --git a/unifile b/unifile
index f068bc951..60e3145f8 160000
--- a/unifile
+++ b/unifile
@@ -1 +1 @@
-Subproject commit f068bc951b2fc9045d50a375b97530b95df49013
+Subproject commit 60e3145f858f7047549faa2c1be07f62a4d10272
diff --git a/vectorold b/vectorold
index 34d314b58..b07793e9c 160000
--- a/vectorold
+++ b/vectorold
@@ -1 +1 @@
-Subproject commit 34d314b584741a703ba7e17cf3f7f123bd3ade40
+Subproject commit b07793e9c539eb9f5ce5b7e306e5b067f93ad74b
diff --git a/yorozuya b/yorozuya
index cd4d1b981..805f0c5f7 160000
--- a/yorozuya
+++ b/yorozuya
@@ -1 +1 @@
-Subproject commit cd4d1b981cd1205efe3159425fb094dae8be64b0
+Subproject commit 805f0c5f7aa80d82ddde2360e9f9b6d27f797124