Improve download

This commit is contained in:
seven332 committed 2016-02-26 14:09:01 +08:00
1 parent 41afc75cce
commit 61f09dc9f3
54 files changed
+3237 -214

No files matched your search

+4 -4
View File
@@ -10,8 +10,8 @@ android {
applicationId "com.hippo.ehviewer"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
versionCode 48
versionName "0.9.0"
}
buildTypes {
@@ -53,8 +53,8 @@ android {
}
clean {
//delete file('src/main/obj').absolutePath
//delete file('src/main/libs').absolutePath
delete file('src/main/obj').absolutePath
delete file('src/main/libs').absolutePath
}
dependencies {
+5
View File
@@ -67,6 +67,11 @@
android:configChanges="screenSize|uiMode"
android:theme="@style/AppTheme"/>
<service
android:name="com.hippo.ehviewer.download.DownloadService"
android:icon="@mipmap/ic_launcher"
android:label="@string/download"/>
</application>
</manifest>
@@ -22,6 +22,7 @@ import android.os.Debug;
import android.support.annotation.NonNull;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.hippo.beerbelly.LruCacheEx;
import com.hippo.conaco.Conaco;
@@ -30,6 +31,8 @@ import com.hippo.ehviewer.client.EhClient;
import com.hippo.ehviewer.client.EhCookieStore;
import com.hippo.ehviewer.client.data.GalleryDetail;
import com.hippo.ehviewer.client.data.LargePreviewSet;
import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.ehviewer.download.DownloadService;
import com.hippo.ehviewer.spider.SpiderDen;
import com.hippo.network.StatusCodeException;
import com.hippo.okhttp.CookieDB;
@@ -37,12 +40,12 @@ import com.hippo.scene.SceneApplication;
import com.hippo.text.Html;
import com.hippo.util.ReadableTime;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.IntIdGenerator;
import com.hippo.yorozuya.SimpleHandler;
import java.io.File;
import java.util.Comparator;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import okhttp3.OkHttpClient;
@@ -53,8 +56,8 @@ public class EhApplication extends SceneApplication {
private static final boolean DEBUG_CONACO = false;
private static final boolean DEBUG_NATIVE_MEMORY = false;
private IntIdGenerator mIdGenerator = new IntIdGenerator();
private SparseArray<Object> mGlobalStuffMap = new SparseArray<>();
private final AtomicInteger mIdGenerator = new AtomicInteger();
private final SparseArray<Object> mGlobalStuffMap = new SparseArray<>();
private EhCookieStore mEhCookieStore;
private EhClient mEhClient;
private OkHttpClient mOkHttpClient;
@@ -63,6 +66,13 @@ public class EhApplication extends SceneApplication {
private LruCacheEx<Integer, GalleryDetail> mGalleryDetailCache;
private LruCacheEx<String, LargePreviewSet> mLargePreviewSetCache;
private LruCacheEx<Integer, Integer> mPreviewPagesCache;
private DownloadManager mDownloadManager;
private SparseBooleanArray mItemStateArray;
private SparseArray<String> mItemTitleArray;
private int mFailedCount;
private int mFinishedCount;
private int mDownloadedCount;
@Override
public void onCreate() {
@@ -76,6 +86,11 @@ public class EhApplication extends SceneApplication {
Html.initialize(this);
AppConfig.initialize(this);
SpiderDen.initialize(this);
EhDB.initialize(this);
if (EhDB.needMerge()) {
EhDB.mergeOldDB(this);
}
if (DEBUG_NATIVE_MEMORY) {
debugNativeMemory();
@@ -94,7 +109,7 @@ public class EhApplication extends SceneApplication {
}
public int putGlobalStuff(@NonNull Object o) {
int id = mIdGenerator.nextId();
int id = mIdGenerator.getAndDecrement();
mGlobalStuffMap.put(id, o);
return id;
}
@@ -237,4 +252,43 @@ public class EhApplication extends SceneApplication {
}
return application.mPreviewPagesCache;
}
@NonNull
public static DownloadManager getDownloadManager(@NonNull Context context) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mDownloadManager == null) {
application.mDownloadManager = new DownloadManager(application);
}
return application.mDownloadManager;
}
public static void initDownloadService(Context context, DownloadService service) {
EhApplication application = ((EhApplication) context.getApplicationContext());
if (application.mItemStateArray == null) {
application.mItemStateArray = new SparseBooleanArray();
}
if (application.mItemTitleArray == null) {
application.mItemTitleArray = new SparseArray<>();
}
service.init(application.mItemStateArray, application.mItemTitleArray,
application.mFailedCount, application.mFinishedCount, application.mDownloadedCount);
}
public static void clearDownloadService(Context context, DownloadService service) {
EhApplication application = ((EhApplication) context.getApplicationContext());
application.mFailedCount = 0;
application.mFinishedCount = 0;
application.mDownloadedCount = 0;
service.clear();
}
public static void backupDownloadService(Context context,
int failedCount, int finishedCount, int downloadedCount) {
EhApplication application = ((EhApplication) context.getApplicationContext());
application.mFailedCount = failedCount;
application.mFinishedCount = finishedCount;
application.mDownloadedCount = downloadedCount;
}
}
@@ -0,0 +1,371 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.dao.DaoMaster;
import com.hippo.ehviewer.dao.DaoSession;
import com.hippo.ehviewer.dao.DownloadInfoDao;
import com.hippo.ehviewer.dao.DownloadInfoRaw;
import com.hippo.ehviewer.dao.DownloadLabelDao;
import com.hippo.ehviewer.dao.DownloadLabelRaw;
import com.hippo.ehviewer.dao.GalleryInfoDao;
import com.hippo.ehviewer.dao.GalleryInfoRaw;
import com.hippo.ehviewer.dao.HistoryInfoDao;
import com.hippo.ehviewer.dao.HistoryInfoRaw;
import com.hippo.ehviewer.dao.LocalFavoriteDao;
import com.hippo.ehviewer.dao.LocalFavoriteRaw;
import com.hippo.ehviewer.dao.QuickSearchDao;
import com.hippo.ehviewer.dao.QuickSearchRaw;
import com.hippo.ehviewer.download.DownloadInfo;
import java.util.ArrayList;
import java.util.List;
import de.greenrobot.dao.query.LazyList;
public class EhDB {
private static DaoSession sDaoSession;
private static boolean sHasOldDB;
private static boolean sNewDB;
private static class DBOpenHelper extends DaoMaster.OpenHelper {
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onCreate(SQLiteDatabase db) {
super.onCreate(db);
sNewDB = true;
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
private static class OldDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "data";
private static final int VERSION = 4;
private static final String TABLE_GALLERY = "gallery";
private static final String TABLE_LOCAL_FAVOURITE = "local_favourite";
private static final String TABLE_TAG = "tag";
private static final String TABLE_DOWNLOAD = "download";
private static final String TABLE_HISTORY = "history";
public OldDBHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
public static void initialize(Context context) {
sHasOldDB = context.getDatabasePath("data").exists();
DBOpenHelper helper = new DBOpenHelper(
context.getApplicationContext(), "eh.db", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
sDaoSession = daoMaster.newSession();
}
public static boolean needMerge() {
return sNewDB && sHasOldDB;
}
public static void mergeOldDB(Context context) {
sNewDB = false;
OldDBHelper oldDBHelper = new OldDBHelper(context);
SQLiteDatabase oldDB;
try {
oldDB = oldDBHelper.getReadableDatabase();
} catch (Exception e) {
return;
}
// Merge gallery info
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_GALLERY, null);
if (cursor != null) {
GalleryInfoDao dao = sDaoSession.getGalleryInfoDao();
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
GalleryInfoRaw raw = new GalleryInfoRaw();
raw.setGid((long) cursor.getInt(0));
raw.setToken(cursor.getString(1));
raw.setTitle(cursor.getString(2));
raw.setPosted(cursor.getString(3));
raw.setCategory(cursor.getInt(4));
raw.setThumb(cursor.getString(5));
raw.setUploader(cursor.getString(6));
raw.setRating(cursor.getFloat(7));
raw.setReference(cursor.getInt(8));
dao.insert(raw);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (Exception e) {
// Ignore
}
// Merge local favorite
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_LOCAL_FAVOURITE, null);
if (cursor != null) {
LocalFavoriteDao dao = sDaoSession.getLocalFavoriteDao();
if (cursor.moveToFirst()) {
long i = 0L;
while (!cursor.isAfterLast()) {
LocalFavoriteRaw raw = new LocalFavoriteRaw();
raw.setGid((long) cursor.getInt(0));
raw.setDate(i);
dao.insert(raw);
cursor.moveToNext();
i++;
}
}
cursor.close();
}
} catch (Exception e) {
// Ignore
}
// Merge quick search
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_TAG, null);
if (cursor != null) {
QuickSearchDao dao = sDaoSession.getQuickSearchDao();
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
QuickSearchRaw raw = new QuickSearchRaw();
int mode = cursor.getInt(2);
String search = cursor.getString(4);
String tag = cursor.getString(7);
if (mode == ListUrlBuilder.MODE_UPLOADER && search != null &&
search.startsWith("uploader:")) {
search = search.substring("uploader:".length());
}
raw.setDate((long) cursor.getInt(0));
raw.setName(cursor.getString(1));
raw.setMode(mode);
raw.setCategory(cursor.getInt(3));
raw.setKeyword(mode == ListUrlBuilder.MODE_TAG ? tag : search);
raw.setAdvanceSearch(cursor.getInt(5));
raw.setMinRating(cursor.getInt(6));
dao.insert(raw);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (Exception e) {
// Ignore
}
// Merge download info
try {
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_DOWNLOAD, null);
if (cursor != null) {
DownloadInfoDao dao = sDaoSession.getDownloadInfoDao();
if (cursor.moveToFirst()) {
long i = 0L;
while (!cursor.isAfterLast()) {
DownloadInfoRaw raw = new DownloadInfoRaw();
int state = cursor.getInt(2);
int legacy = cursor.getInt(3);
if (state == DownloadInfo.STATE_FINISH && legacy > 0) {
state = DownloadInfo.STATE_FAILED;
}
raw.setGid((long) cursor.getInt(0));
raw.setState(state);
raw.setLegacy(legacy);
if (cursor.getColumnCount() == 5) {
raw.setDate(cursor.getLong(4));
} else {
raw.setDate(i);
}
dao.insert(raw);
cursor.moveToNext();
i++;
}
}
cursor.close();
}
} catch (Exception e) {
// Ignore
}
try {
// Merge history info
Cursor cursor = oldDB.rawQuery("select * from " + OldDBHelper.TABLE_HISTORY, null);
if (cursor != null) {
HistoryInfoDao dao = sDaoSession.getHistoryInfoDao();
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
HistoryInfoRaw raw = new HistoryInfoRaw();
raw.setGid((long) cursor.getInt(0));
raw.setMode(cursor.getInt(1));
raw.setDate(cursor.getLong(2));
dao.insert(raw);
cursor.moveToNext();
}
}
cursor.close();
}
} catch (Exception e) {
// Ignore
}
try {
oldDBHelper.close();
} catch (Exception e) {
// Ignore
}
}
private static void addGalleryInfo(GalleryInfo galleryInfo) {
GalleryInfoDao dao = sDaoSession.getGalleryInfoDao();
GalleryInfoRaw raw = dao.load((long) galleryInfo.gid);
if (raw == null) {
// add new item, set reference 1
raw = new GalleryInfoRaw();
raw.setGid((long) galleryInfo.gid);
raw.setToken(galleryInfo.token);
raw.setTitle(galleryInfo.title);
raw.setThumb(galleryInfo.thumb);
raw.setCategory(galleryInfo.category);
raw.setPosted(galleryInfo.posted);
raw.setUploader(galleryInfo.uploader);
raw.setRating(galleryInfo.rating);
raw.setReference(1);
dao.insert(raw);
} else {
// already in db, add reference
raw.setReference(raw.getReference() + 1);
dao.update(raw);
}
}
private static void removeGalleryInfo(int gid) {
GalleryInfoDao dao = sDaoSession.getGalleryInfoDao();
GalleryInfoRaw raw = dao.load((long) gid);
int reference = raw.getReference();
if (reference <= 1) {
// No reference, delete it
dao.deleteByKey((long) gid);
} else {
// Still has reference, sub reference
raw.setReference(reference - 1);
dao.update(raw);
}
}
public static GalleryInfo getGalleryInfo(int gid) {
GalleryInfoDao dao = sDaoSession.getGalleryInfoDao();
GalleryInfoRaw raw = dao.load((long) gid);
if (raw != null) {
GalleryInfo gi = new GalleryInfo();
gi.gid = (int) (long) raw.getGid();
gi.token = raw.getToken();
gi.title = raw.getTitle();
gi.thumb = raw.getThumb();
gi.category = raw.getCategory();
gi.posted = raw.getPosted();
gi.uploader = raw.getUploader();
gi.rating = raw.getRating();
gi.generateSLang();
return gi;
} else {
return null;
}
}
public static List<String> getAllDownloadLabelList() {
DownloadLabelDao dao = sDaoSession.getDownloadLabelDao();
List<DownloadLabelRaw> list = dao.queryBuilder().orderAsc(DownloadLabelDao.Properties.Time).list();
List<String> result = new ArrayList<>(list.size());
for (int i = 0, n = list.size(); i < n; i++) {
result.add(list.get(i).getLabel());
}
return result;
}
public static LazyList<DownloadInfoRaw> getDownloadInfoLazyList() {
return sDaoSession.getDownloadInfoDao().queryBuilder()
.orderAsc(DownloadInfoDao.Properties.Date).listLazy();
}
public static void addDownloadInfo(DownloadInfo info) {
// Add gallery info first
addGalleryInfo(info.galleryInfo);
// Add download info
DownloadInfoDao dao = sDaoSession.getDownloadInfoDao();
DownloadInfoRaw raw = new DownloadInfoRaw();
raw.setGid((long) info.galleryInfo.gid);
raw.setState(info.state);
raw.setLegacy(info.legacy);
raw.setDate(info.date);
raw.setLabel(info.label);
dao.insert(raw);
}
public static void updateDownloadInfo(DownloadInfo info) {
DownloadInfoDao dao = sDaoSession.getDownloadInfoDao();
DownloadInfoRaw raw = dao.load((long) info.galleryInfo.gid);
if (raw == null) {
Log.e("TAG", "Can't find download info: " + info.galleryInfo.gid);
addDownloadInfo(info);
} else {
raw.setState(info.state);
raw.setLegacy(info.legacy);
raw.setDate(info.date);
raw.setLabel(info.label);
dao.update(raw);
}
}
}
@@ -117,6 +117,18 @@ public class Settings {
putBoolean(KEY_SHOW_WARNING, value);
}
public static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
public static String getRecentDownloadLabel() {
return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL);
}
public static void putRecentDownloadLabel(String value) {
putString(KEY_RECENT_DOWNLOAD_LABEL, value);
}
public static EhConfig getEhConfig() {
return sEhConfig;
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.download;
import com.hippo.ehviewer.client.data.GalleryInfo;
public class DownloadInfo {
public static final int STATE_NONE = 0;
public static final int STATE_WAIT = 1;
public static final int STATE_DOWNLOAD = 2;
public static final int STATE_FINISH = 3;
public static final int STATE_FAILED = 4;
public String label;
public GalleryInfo galleryInfo;
public int state = STATE_NONE;
// byte/second, -1 for can't get speed now
public long speed;
// -1 for unknown
public int download = 0;
// -1 for unknown
public int total = -1;
// -1 for unknown
public int legacy = -1;
// For order
public long date;
}
@@ -0,0 +1,686 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.download;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.util.SparseArray;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.dao.DownloadInfoRaw;
import com.hippo.ehviewer.spider.SpiderQueen;
import com.hippo.image.Image;
import com.hippo.yorozuya.ConcurrentPool;
import com.hippo.yorozuya.SimpleHandler;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import de.greenrobot.dao.query.LazyList;
public class DownloadManager implements SpiderQueen.OnSpiderListener {
private static final String TAG = DownloadManager.class.getSimpleName();
private final Context mContext;
private final Map<String, LinkedList<DownloadInfo>> mMap;
// All labels without default label
private final List<String> mLabelList;
// Store download info with default label
private final LinkedList<DownloadInfo> mDefaultInfoList;
// Store all download info
private final LinkedList<DownloadInfo> mAllInfoList;
// For quick search
private final SparseArray<DownloadInfo> mAllInfoMap;
// Store download info wait to start
private final LinkedList<DownloadInfo> mWaitList;
private final SpeedReminder mSpeedReminder;
private DownloadListener mDownloadListener;
private DownloadInfoListener mDownloadInfoListener;
@Nullable
private DownloadInfo mCurrentTask;
@Nullable
private SpiderQueen mCurrentSpider;
private final ConcurrentPool<NotifyTask> mNotifyTaskPool = new ConcurrentPool<>(5);
public DownloadManager(Context context) {
mContext = context;
List<String> labels = EhDB.getAllDownloadLabelList();
mLabelList = labels;
// Create list for each label
mMap = new HashMap<>();
for (String label : labels) {
mMap.put(label, new LinkedList<DownloadInfo>());
}
// Create default for non tag
mDefaultInfoList = new LinkedList<>();
// Create all download info list
mAllInfoList = new LinkedList<>();
// Create all download info map
mAllInfoMap = new SparseArray<>();
// Fill download info list
LazyList<DownloadInfoRaw> lazyList = EhDB.getDownloadInfoLazyList();
for (DownloadInfoRaw raw : lazyList) {
DownloadInfo info = createDownloadInfo(raw);
if (info == null) {
continue;
}
// Add to all info list
mAllInfoList.add(info);
// Add to all info map
mAllInfoMap.put(info.galleryInfo.gid, info);
// Add to list
List<DownloadInfo> list = null;
if (info.label != null) {
list = mMap.get(info.label);
}
if (list == null) {
list = mDefaultInfoList;
}
list.add(info);
}
lazyList.close();
// Create wait list
mWaitList = new LinkedList<>();
mSpeedReminder = new SpeedReminder();
}
@Nullable
private DownloadInfo createDownloadInfo(DownloadInfoRaw raw) {
GalleryInfo gi = EhDB.getGalleryInfo((int) (long) raw.getGid());
if (gi == null) {
Log.d(TAG, "Can't get GalleryInfo: " + raw.getGid());
return null;
}
DownloadInfo info = new DownloadInfo();
int state = raw.getState();
if (state == DownloadInfo.STATE_WAIT || state == DownloadInfo.STATE_DOWNLOAD) {
state = DownloadInfo.STATE_NONE;
}
info.galleryInfo = gi;
info.state = state;
info.legacy = raw.getLegacy();
info.date = raw.getDate();
info.label = raw.getLabel();
return info;
}
public boolean containLabel(String label) {
return mLabelList.contains(label);
}
@NonNull
public List<String> getLabelList() {
return mLabelList;
}
@NonNull
public List<DownloadInfo> getAllDownloadInfoList() {
return mAllInfoList;
}
@NonNull
public List<DownloadInfo> getDefaultDownloadInfoList() {
return mDefaultInfoList;
}
@Nullable
public List<DownloadInfo> getLabelDownloadInfoList(String label) {
return mMap.get(label);
}
public void setDownloadInfoListener(DownloadInfoListener downloadInfoListener) {
mDownloadInfoListener = downloadInfoListener;
}
public void setDownloadListener(DownloadListener listener) {
mDownloadListener = listener;
}
private void ensureDownload() {
if (mCurrentTask != null) {
// Only one download
return;
}
// Get download from wait list
if (!mWaitList.isEmpty()) {
DownloadInfo info = mWaitList.removeFirst();
SpiderQueen spider = SpiderQueen.obtainSpiderQueen(mContext, info.galleryInfo, SpiderQueen.MODE_DOWNLOAD);
mCurrentTask = info;
mCurrentSpider = spider;
spider.addOnSpiderListener(this);
info.state = DownloadInfo.STATE_DOWNLOAD;
info.speed = -1;
info.total = -1;
info.download = 0;
info.legacy = -1;
// Update in DB
EhDB.updateDownloadInfo(info);
// Start speed count
mSpeedReminder.start();
// Notify start downloading
if (mDownloadListener != null) {
mDownloadListener.onStart(info);
}
// Notify state update
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
}
}
void startDownload(GalleryInfo galleryInfo, @Nullable String label) {
if (mCurrentTask != null && mCurrentTask.galleryInfo.gid == galleryInfo.gid) {
// It is current task
return;
}
// Check in download list
DownloadInfo info = mAllInfoMap.get(galleryInfo.gid);
if (info != null) { // Get it in download list
if (info.state != DownloadInfo.STATE_WAIT) {
// Set state DownloadInfo.STATE_WAIT
info.state = DownloadInfo.STATE_WAIT;
// Add to wait list
mWaitList.add(info);
// Update in DB
EhDB.updateDownloadInfo(info);
// Notify state update
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
// Make sure download is running
ensureDownload();
}
} else {
// It is new download info
info = new DownloadInfo();
info.galleryInfo = galleryInfo;
info.label = label;
info.state = DownloadInfo.STATE_WAIT;
info.date = System.currentTimeMillis();
// Add to all download list and map
mAllInfoList.add(info);
mAllInfoMap.put(galleryInfo.gid, info);
// Add to label download list
LinkedList<DownloadInfo> list;
if (label == null) {
list = mDefaultInfoList;
} else {
list = mMap.get(label);
if (list == null) {
Log.e(TAG, "Can't find download info list with label: " + label);
list = new LinkedList<>();
mMap.put(label, list);
// Maybe label is missing in mLabelList
if (!mLabelList.contains(label)) {
mLabelList.add(label);
}
}
}
list.add(info);
// Add to wait list
mWaitList.add(info);
// Save to
EhDB.addDownloadInfo(info);
// Notify
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onAdd(info);
}
// Make sure download is running
ensureDownload();
}
}
void stopDownload(int gid) {
DownloadInfo info = stopDownloadInternal(gid);
if (info != null) {
// Update listener
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
// Ensure download
ensureDownload();
}
}
void stopCurrentDownload() {
DownloadInfo info = stopCurrentDownloadInternal();
if (info != null) {
// Update listener
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
// Ensure download
ensureDownload();
}
}
void stopAllDownload() {
// Stop all in wait list
for (DownloadInfo info : mWaitList) {
info.state = DownloadInfo.STATE_NONE;
// Update in DB
EhDB.updateDownloadInfo(info);
}
mWaitList.clear();
// Stop current
stopCurrentDownloadInternal();
// Notify mDownloadInfoListener
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdateAll();
}
}
// Update in DB
// Update listener
// No ensureDownload
private DownloadInfo stopDownloadInternal(int gid) {
// Check current task
if (mCurrentTask != null && mCurrentTask.galleryInfo.gid == gid) {
// Stop current
return stopCurrentDownloadInternal();
}
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext();) {
DownloadInfo info = iterator.next();
if (info.galleryInfo.gid == gid) {
// Remove from wait list
iterator.remove();
// Update state
info.state = DownloadInfo.STATE_NONE;
// Update in DB
EhDB.updateDownloadInfo(info);
return info;
}
}
return null;
}
// Update in DB
// Update mDownloadListener
private DownloadInfo stopCurrentDownloadInternal() {
DownloadInfo info = mCurrentTask;
SpiderQueen spider = mCurrentSpider;
// Release spider
if (spider != null) {
spider.removeOnSpiderListener(DownloadManager.this);
SpiderQueen.releaseSpiderQueen(spider, SpiderQueen.MODE_DOWNLOAD);
}
mCurrentTask = null;
mCurrentSpider = null;
// Stop speed reminder
mSpeedReminder.stop();
if (info == null) {
return null;
}
// Update state
info.state = DownloadInfo.STATE_NONE;
// Update in DB
EhDB.updateDownloadInfo(info);
// Listener
if (mDownloadListener != null) {
mDownloadListener.onCancel(info);
}
return info;
}
boolean isIdle() {
return mCurrentTask == null && mWaitList.isEmpty();
}
@Override
public void onGetPages(int pages) {
NotifyTask task = mNotifyTaskPool.pop();
if (task == null) {
task = new NotifyTask();
}
task.setOnGetPagesData(pages);
SimpleHandler.getInstance().post(task);
}
@Override
public void onGet509(int index) {
NotifyTask task = mNotifyTaskPool.pop();
if (task == null) {
task = new NotifyTask();
}
task.setOnGet509Data(index);
SimpleHandler.getInstance().post(task);
}
@Override
public void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead) {
NotifyTask task = mNotifyTaskPool.pop();
if (task == null) {
task = new NotifyTask();
}
task.setOnPageDownloadData(index, contentLength, receivedSize, bytesRead);
SimpleHandler.getInstance().post(task);
}
@Override
public void onPageSuccess(int index, int downloaded, int total) {
NotifyTask task = mNotifyTaskPool.pop();
if (task == null) {
task = new NotifyTask();
}
task.setOnPageSuccessData(index, downloaded, total);
SimpleHandler.getInstance().post(task);
}
@Override
public void onPageFailure(int index, String error, int downloaded, int total) {
NotifyTask task = mNotifyTaskPool.pop();
if (task == null) {
task = new NotifyTask();
}
task.setOnPageFailureDate(index, error, downloaded, total);
SimpleHandler.getInstance().post(task);
}
@Override
public void onFinish(int downloaded, int total) {
NotifyTask task = mNotifyTaskPool.pop();
if (task == null) {
task = new NotifyTask();
}
task.setOnFinishDate(downloaded, total);
SimpleHandler.getInstance().post(task);
}
@Override
public void onGetImageSuccess(int index, Image image) {
// Ignore
}
@Override
public void onGetImageFailure(int index, String error) {
// Ignore
}
private class NotifyTask implements Runnable {
public static final int TYPE_ON_GET_PAGES = 0;
public static final int TYPE_ON_GET_509 = 1;
public static final int TYPE_ON_PAGE_DOWNLOAD = 2;
public static final int TYPE_ON_PAGE_SUCCESS = 3;
public static final int TYPE_ON_PAGE_FAILURE = 4;
public static final int TYPE_ON_FINISH = 5;
private int mType;
private int mPages;
private int mIndex;
private long mContentLength;
private long mReceivedSize;
private int mBytesRead;
private String mError;
private int mDownloaded;
private int mTotal;
public void setOnGetPagesData(int pages) {
mType = TYPE_ON_GET_PAGES;
mPages = pages;
}
public void setOnGet509Data(int index) {
mType = TYPE_ON_GET_509;
mIndex = index;
}
public void setOnPageDownloadData(int index, long contentLength, long receivedSize, int bytesRead) {
mType = TYPE_ON_PAGE_DOWNLOAD;
mIndex = index;
mContentLength = contentLength;
mReceivedSize = receivedSize;
mBytesRead = bytesRead;
}
public void setOnPageSuccessData(int index, int downloaded, int total) {
mType = TYPE_ON_PAGE_SUCCESS;
mIndex = index;
mDownloaded = downloaded;
mTotal = total;
}
public void setOnPageFailureDate(int index, String error, int downloaded, int total) {
mType = TYPE_ON_PAGE_FAILURE;
mIndex = index;
mError = error;
mDownloaded = downloaded;
mTotal = total;
}
public void setOnFinishDate(int downloaded, int total) {
mType = TYPE_ON_FINISH;
mDownloaded = downloaded;
mTotal = total;
}
@Override
public void run() {
switch (mType) {
case TYPE_ON_GET_PAGES: {
DownloadInfo info = mCurrentTask;
if (info == null) {
Log.e(TAG, "Current task is null, but it should not be");
} else {
info.total = mPages;
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
}
break;
}
case TYPE_ON_GET_509: {
if (mDownloadListener != null) {
mDownloadListener.onGet509();
}
break;
}
case TYPE_ON_PAGE_DOWNLOAD: {
mSpeedReminder.up(mBytesRead);
break;
}
case TYPE_ON_PAGE_SUCCESS: {
DownloadInfo info = mCurrentTask;
if (info == null) {
Log.e(TAG, "Current task is null, but it should not be");
} else {
info.download = mDownloaded;
info.total = mTotal;
if (mDownloadListener != null) {
mDownloadListener.onGetPage(info);
}
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
}
break;
}
case TYPE_ON_PAGE_FAILURE: {
DownloadInfo info = mCurrentTask;
if (info == null) {
Log.e(TAG, "Current task is null, but it should not be");
} else {
info.download = mDownloaded;
info.total = mTotal;
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
}
break;
}
case TYPE_ON_FINISH: {
// Download done
DownloadInfo info = mCurrentTask;
mCurrentTask = null;
SpiderQueen spider = mCurrentSpider;
mCurrentSpider = null;
// Release spider
if (spider != null) {
spider.removeOnSpiderListener(DownloadManager.this);
SpiderQueen.releaseSpiderQueen(spider, SpiderQueen.MODE_DOWNLOAD);
}
// Check null
if (info == null || spider == null) {
Log.e(TAG, "Current stuff is null, but it should not be");
break;
}
// Stop speed count
mSpeedReminder.stop();
// Update state
info.download = mDownloaded;
info.total = mTotal;
info.legacy = mTotal - mDownloaded;
if (info.legacy == 0) {
info.state = DownloadInfo.STATE_FINISH;
} else {
info.state = DownloadInfo.STATE_FAILED;
}
// Update in DB
EhDB.updateDownloadInfo(info);
// Notify
if (mDownloadListener != null) {
mDownloadListener.onFinish(info);
}
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
// Start next download
ensureDownload();
break;
}
}
mNotifyTaskPool.push(this);
}
}
class SpeedReminder implements Runnable {
private boolean mStop = true;
private final AtomicLong mBytesRead = new AtomicLong();
public void start() {
if (mStop) {
mStop = false;
SimpleHandler.getInstance().post(this);
}
}
public void stop() {
if (!mStop) {
mStop = true;
mBytesRead.lazySet(0L);
SimpleHandler.getInstance().removeCallbacks(this);
}
}
public void up(int bytesRead) {
mBytesRead.addAndGet(bytesRead);
}
@Override
public void run() {
DownloadInfo info = mCurrentTask;
if (info != null) {
info.speed = mBytesRead.getAndSet(0) / 2;
if (mDownloadListener != null) {
mDownloadListener.onDownload(info);
}
if (mDownloadInfoListener != null) {
mDownloadInfoListener.onUpdate(info);
}
}
if (!mStop) {
SimpleHandler.getInstance().postDelayed(this, 2000);
}
}
}
public interface DownloadInfoListener {
void onAdd(DownloadInfo info);
void onUpdate(DownloadInfo info);
void onUpdateAll();
void onRemove(DownloadInfo info);
}
public interface DownloadListener {
/**
* Get 509 error
*/
void onGet509();
/**
* Start download
*/
void onStart(DownloadInfo info);
/**
* Update download speed
*/
void onDownload(DownloadInfo info);
/**
* Update page downloaded
*/
void onGetPage(DownloadInfo info);
/**
* Download done
*/
void onFinish(DownloadInfo info);
/**
* Download done
*/
void onCancel(DownloadInfo info);
}
}
@@ -0,0 +1,369 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.download;
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 android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.yorozuya.FileUtils;
public class DownloadService extends Service implements DownloadManager.DownloadListener {
public static final String ACTION_START = "start";
public static final String ACTION_START_ALL = "start_all";
public static final String ACTION_STOP = "stop";
public static final String ACTION_STOP_CURRENT = "stop_current";
public static final String ACTION_STOP_ALL = "stop_all";
public static final String ACTION_CLEAR = "clear";
public static final String KEY_GALLERY_INFO = "gallery_info";
public static final String KEY_LABEL = "label";
public static final String KEY_GID = "gid";
private static final int ID_DOWNLOADING = 1;
private static final int ID_DOWNLOADED = 2;
private static final int ID_509 = 3;
@Nullable
private NotificationManager mNotifyManager;
@Nullable
private DownloadManager mDownloadManager;
private NotificationCompat.Builder mDownloadingBuilder;
private NotificationCompat.Builder mDownloadedBuilder;
private NotificationCompat.Builder m509dBuilder;
@Nullable
private SparseBooleanArray mItemStateArray;
@Nullable
private SparseArray<String> mItemTitleArray;
private int mFailedCount;
private int mFinishedCount;
private int mDownloadedCount;
public void init(SparseBooleanArray stateArray, SparseArray<String> titleArray,
int failedCount, int finishedCount, int downloadedCount) {
mItemStateArray = stateArray;
mItemTitleArray = titleArray;
mFailedCount = failedCount;
mFinishedCount = finishedCount;
mDownloadedCount = downloadedCount;
}
public void clear() {
mFailedCount = 0;
mFinishedCount = 0;
mDownloadedCount = 0;
if (mItemStateArray != null) {
mItemStateArray.clear();
}
if (mItemTitleArray != null) {
mItemTitleArray.clear();
}
}
@Override
public void onCreate() {
super.onCreate();
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mDownloadManager = EhApplication.getDownloadManager(getApplicationContext());
mDownloadManager.setDownloadListener(this);
EhApplication.initDownloadService(getApplicationContext(), this);
}
@Override
public void onDestroy() {
super.onDestroy();
mNotifyManager = null;
if (mDownloadManager != null) {
mDownloadManager.setDownloadListener(null);
mDownloadManager = null;
}
mDownloadingBuilder = null;
mDownloadedBuilder = null;
m509dBuilder = null;
EhApplication.backupDownloadService(getApplicationContext(),
mFailedCount, mFinishedCount, mDownloadedCount);
mItemStateArray = null;
mItemTitleArray = null;
}
@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();
}
if (ACTION_START.equals(action)) {
GalleryInfo gi= intent.getParcelableExtra(KEY_GALLERY_INFO);
String label = intent.getStringExtra(KEY_LABEL);
if (gi != null && mDownloadManager != null) {
mDownloadManager.startDownload(gi, label);
}
} else if (ACTION_STOP.equals(action)) {
int gid = intent.getIntExtra(KEY_GID, -1);
if (gid != -1 && mDownloadManager != null) {
mDownloadManager.stopDownload(gid);
}
} else if (ACTION_STOP_CURRENT.equals(action)) {
if (mDownloadManager != null) {
mDownloadManager.stopCurrentDownload();
}
} else if (ACTION_STOP_ALL.equals(action)) {
if (mDownloadManager != null) {
mDownloadManager.stopAllDownload();
}
} else if (ACTION_CLEAR.equals(action)) {
EhApplication.clearDownloadService(getApplicationContext(), this);
}
checkStopSelf();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
throw new IllegalStateException("No bindService");
}
private void ensureDownloadingBuilder() {
if (mDownloadingBuilder != null) {
return;
}
mDownloadingBuilder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(android.R.drawable.stat_sys_download)
.setOngoing(true)
.setAutoCancel(false);
}
private void ensureDownloadedBuilder() {
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())
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setContentTitle(getString(R.string.stat_download_done_title))
.setDeleteIntent(piClear)
.setOngoing(false)
.setAutoCancel(true);
}
private void ensure509Builder() {
if (m509dBuilder != null) {
return;
}
m509dBuilder = new NotificationCompat.Builder(getApplicationContext())
.setSmallIcon(R.drawable.ic_stat_alert)
.setContentText(getString(R.string.stat_509_alert_title))
.setContentText(getString(R.string.stat_509_alert_text))
.setAutoCancel(true)
.setOngoing(false);
}
@Override
public void onGet509() {
if (mNotifyManager == null) {
return;
}
ensure509Builder();
mNotifyManager.notify(ID_509, m509dBuilder.build());
}
@Override
public void onStart(DownloadInfo info) {
if (mNotifyManager == null) {
return;
}
ensureDownloadingBuilder();
mDownloadingBuilder.setContentTitle(info.galleryInfo.title)
.setContentText(null)
.setProgress(0, 0, true);
startForeground(ID_DOWNLOADING, mDownloadingBuilder.build());
}
private void onUpdate(DownloadInfo info) {
if (mNotifyManager == null) {
return;
}
ensureDownloadingBuilder();
long speed = info.speed;
if (speed < 0) {
speed = 0;
}
String text = FileUtils.humanReadableByteCount(speed, false) + "/S";
mDownloadingBuilder.setContentTitle(info.galleryInfo.title)
.setContentText(text)
.setProgress(info.total, info.download, false);
startForeground(ID_DOWNLOADING, mDownloadingBuilder.build());
}
@Override
public void onDownload(DownloadInfo info) {
onUpdate(info);
}
@Override
public void onGetPage(DownloadInfo info) {
onUpdate(info);
}
@Override
public void onFinish(DownloadInfo info) {
if (mNotifyManager == null || mItemStateArray == null || mItemTitleArray == null) {
return;
}
mNotifyManager.cancel(ID_DOWNLOADING);
ensureDownloadedBuilder();
boolean finish = info.state == DownloadInfo.STATE_FINISH;
int gid = info.galleryInfo.gid;
int index = mItemStateArray.indexOfKey(gid);
if (index < 0) { // Not contain
mItemStateArray.put(gid, finish);
mItemTitleArray.put(gid, info.galleryInfo.title);
mDownloadedCount++;
if (finish) {
mFinishedCount++;
} else {
mFailedCount++;
}
} else { // Contain
boolean oldFinish = mItemStateArray.valueAt(index);
mItemStateArray.put(gid, finish);
mItemTitleArray.put(gid, info.galleryInfo.title);
if (oldFinish && !finish) {
mFinishedCount--;
mFailedCount++;
} else if (!oldFinish && finish) {
mFinishedCount++;
mFailedCount--;
}
}
String text;
boolean needStyle;
if (mFinishedCount != 0 && mFailedCount == 0) {
if (mFinishedCount == 1) {
if (mItemTitleArray.size() >= 1) {
text = getString(R.string.stat_download_done_line_succeeded, mItemTitleArray.valueAt(0));
} else {
Log.d("TAG", "WTF, mItemTitleArray is null");
text = getString(R.string.error_unknown);
}
needStyle = false;
} else {
text = getString(R.string.stat_download_done_text_succeeded, mFinishedCount);
needStyle = true;
}
} else if (mFinishedCount == 0 && mFailedCount != 0) {
if (mFailedCount == 1) {
if (mItemTitleArray.size() >= 1) {
text = getString(R.string.stat_download_done_line_failed, mItemTitleArray.valueAt(0));
} else {
Log.d("TAG", "WTF, mItemTitleArray is null");
text = getString(R.string.error_unknown);
}
needStyle = false;
} else {
text = getString(R.string.stat_download_done_text_failed, mFailedCount);
needStyle = true;
}
} else {
text = getString(R.string.stat_download_done_text_mix, mFinishedCount, mFailedCount);
needStyle = true;
}
NotificationCompat.InboxStyle style;
if (needStyle) {
style = new NotificationCompat.InboxStyle();
style.setBigContentTitle(getString(R.string.stat_download_done_title));
SparseBooleanArray stateArray = mItemStateArray;
SparseArray<String> titleArray = mItemTitleArray;
for (int i = 0, n = stateArray.size(); i < n; i++) {
int id = stateArray.keyAt(i);
boolean fin = stateArray.valueAt(i);
String title = titleArray.get(id);
if (title == null) {
continue;
}
style.addLine(getString(fin ? R.string.stat_download_done_line_succeeded :
R.string.stat_download_done_line_failed, title));
}
} else {
style = null;
}
mDownloadedBuilder.setContentText(text).setStyle(style);
mNotifyManager.notify(ID_DOWNLOADED, mDownloadedBuilder.build());
checkStopSelf();
}
@Override
public void onCancel(DownloadInfo info) {
if (mNotifyManager == null) {
return;
}
mNotifyManager.cancel(ID_DOWNLOADING);
checkStopSelf();
}
private void checkStopSelf() {
if (mDownloadManager == null || mDownloadManager.isIdle()) {
stopForeground(true);
stopSelf();
}
}
}
@@ -119,22 +119,26 @@ public class EhGalleryProvider extends GalleryProvider implements SpiderQueen.On
}
@Override
public void onDownload(int index, long contentLength, long receivedSize, int bytesRead) {
public void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead) {
if (contentLength > 0) {
notifyPagePercent(index, (float) receivedSize / contentLength);
}
}
@Override
public void onSuccess(int index) {
public void onPageSuccess(int index, int downloaded, int total) {
notifyDataChanged(index);
}
@Override
public void onFailure(int index, String error) {
public void onPageFailure(int index, String error, int downloaded, int total) {
notifyPageFailed(index, error);
}
@Override
public void onFinish(int downloaded, int total) {
}
@Override
public void onGetImageSuccess(int index, Image image) {
notifyPageSucceed(index, image);
@@ -22,6 +22,7 @@ import android.support.annotation.Nullable;
import android.webkit.MimeTypeMap;
import com.hippo.beerbelly.SimpleDiskCache;
import com.hippo.ehviewer.EhDB;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhCacheKeyFactory;
import com.hippo.ehviewer.client.data.GalleryInfo;
@@ -42,7 +43,8 @@ import java.util.Locale;
public final class SpiderDen {
private UniFile mDownloadDir;
@Nullable
private final UniFile mDownloadDir;
private volatile int mMode = SpiderQueen.MODE_READ;
private final int mGid;
@@ -57,14 +59,24 @@ public final class SpiderDen {
}
}
public SpiderDen(GalleryInfo galleryInfo) {
mGid = galleryInfo.gid;
public static UniFile getGalleryDownloadDir(GalleryInfo galleryInfo) {
UniFile dir = Settings.getDownloadLocation();
if (dir != null) {
mDownloadDir = dir.subFile(FileUtils.sanitizeFilename(galleryInfo.gid + "-" + galleryInfo.title));
GalleryInfo info = EhDB.getGalleryInfo(galleryInfo.gid);
if (info == null) {
info = galleryInfo;
}
return dir.subFile(FileUtils.sanitizeFilename(info.gid + "-" + info.title));
} else {
return null;
}
}
public SpiderDen(GalleryInfo galleryInfo) {
mGid = galleryInfo.gid;
mDownloadDir = getGalleryDownloadDir(galleryInfo);
}
public void setMode(@SpiderQueen.Mode int mode) {
mMode = mode;
@@ -71,7 +71,6 @@ import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@@ -82,6 +81,7 @@ public class SpiderQueen implements Runnable {
private static final String TAG = SpiderQueen.class.getSimpleName();
private static final AtomicInteger sIdGenerator = new AtomicInteger();
private static final boolean DEBUG_LOG = false;
@IntDef({MODE_READ, MODE_DOWNLOAD})
@Retention(RetentionPolicy.SOURCE)
@@ -132,7 +132,7 @@ public class SpiderQueen implements Runnable {
private final Stack<Integer> mDecodeRequestStack = new Stack<>();
private final AtomicInteger mDecodeIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
private volatile AtomicReferenceArray<Thread> mWorkers;
private volatile Thread[] mWorkers;
private final Object mWorkerLock = new Object();
private final Object mPTokenLock = new Object();
@@ -196,26 +196,49 @@ public class SpiderQueen implements Runnable {
}
}
private void notifyDownload(int index, long contentLength, long receivedSize, int bytesRead) {
private void notifyPageDownload(int index, long contentLength, long receivedSize, int bytesRead) {
synchronized (mSpiderListeners) {
for (OnSpiderListener listener : mSpiderListeners) {
listener.onDownload(index, contentLength, receivedSize, bytesRead);
listener.onPageDownload(index, contentLength, receivedSize, bytesRead);
}
}
}
private void notifySuccess(int index) {
private void notifyPageSuccess(int index) {
int size = -1;
int[] temp = mPageStateArray;
if (temp != null) {
size = temp.length;
}
synchronized (mSpiderListeners) {
for (OnSpiderListener listener : mSpiderListeners) {
listener.onSuccess(index);
listener.onPageSuccess(index, mFinishedPages.get(), size);
}
}
}
private void notifyFailure(int index, String error) {
private void notifyPageFailure(int index, String error) {
int size = -1;
int[] temp = mPageStateArray;
if (temp != null) {
size = temp.length;
}
synchronized (mSpiderListeners) {
for (OnSpiderListener listener : mSpiderListeners) {
listener.onFailure(index, error);
listener.onPageFailure(index, error, mFinishedPages.get(), size);
}
}
}
private void notifyFinish() {
int size = -1;
int[] temp = mPageStateArray;
if (temp != null) {
size = temp.length;
}
synchronized (mSpiderListeners) {
for (OnSpiderListener listener : mSpiderListeners) {
listener.onFinish(mFinishedPages.get(), size);
}
}
}
@@ -283,10 +306,19 @@ public class SpiderQueen implements Runnable {
}
// Update download page
if (mMode == MODE_DOWNLOAD) {
mDownloadPage = 0;
} else {
mDownloadPage = -1;
boolean startWorkers;
synchronized (mRequestPageQueue) {
if (mMode == MODE_DOWNLOAD) {
mDownloadPage = 0;
startWorkers = true;
} else {
mDownloadPage = -1;
startWorkers = false;
}
}
if (startWorkers && mPageStateArray != null) {
ensureWorkers();
}
}
@@ -439,14 +471,14 @@ public class SpiderQueen implements Runnable {
private void ensureWorkers() {
synchronized (mWorkerLock) {
if (mWorkers == null) {
mWorkers = new AtomicReferenceArray<>(NUMBER_SPIDER_WORKER);
mWorkers = new Thread[NUMBER_SPIDER_WORKER];
}
for (int i = 0, n = mWorkers.length(); i < n; i++) {
if (mWorkers.get(i) == null) {
for (int i = 0, n = mWorkers.length; i < n; i++) {
if (mWorkers[i] == null) {
SpiderWorker worker = new SpiderWorker(i);
Thread thread = mThreadFactory.newThread(worker);
mWorkers.lazySet(i, thread);
mWorkers[i] = thread;
thread.start();
}
}
@@ -521,7 +553,9 @@ public class SpiderQueen implements Runnable {
try {
String url = EhUrl.getGalleryDetailUrl(
mGalleryInfo.gid, mGalleryInfo.token, previewIndex, false);
Log.d(TAG, url);
if (DEBUG_LOG) {
Log.d(TAG, url);
}
Request request = new EhRequestBuilder(url, config).build();
Response response = mHttpClient.newCall(request).execute();
String body = response.body().string();
@@ -688,7 +722,9 @@ public class SpiderQueen implements Runnable {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + ": start");
if (DEBUG_LOG) {
Log.i(TAG, Thread.currentThread().getName() + ": start");
}
runInternal();
@@ -704,24 +740,28 @@ public class SpiderQueen implements Runnable {
// Interrupt all workers
synchronized (mWorkerLock) {
if (mWorkers != null) {
for (int i = 0, n = mWorkers.length(); i < n; i++) {
Thread thread = mWorkers.get(i);
for (int i = 0, n = mWorkers.length; i < n; i++) {
Thread thread = mWorkers[i];
if (thread != null) {
thread.interrupt();
mWorkers.set(i, null);
mWorkers[i] = null;
}
}
mWorkers = null;
}
}
Log.i(TAG, Thread.currentThread().getName() + ": end");
notifyFinish();
if (DEBUG_LOG) {
Log.i(TAG, Thread.currentThread().getName() + ": end");
}
}
private class SpiderWorker implements Runnable {
private int mWorkerIndex;
private int mGid;
private final int mWorkerIndex;
private final int mGid;
public SpiderWorker(int workerIndex) {
mWorkerIndex = workerIndex;
@@ -732,6 +772,10 @@ public class SpiderQueen implements Runnable {
updatePageState(index, state, null);
}
private boolean isStateDone(int state) {
return state == STATE_FINISHED || state == STATE_FAILED;
}
private void updatePageState(int index, @State int state, String error) {
int oldState;
synchronized (mPageStateLock) {
@@ -739,9 +783,9 @@ public class SpiderQueen implements Runnable {
mPageStateArray[index] = state;
}
if (oldState == STATE_NONE && state != STATE_NONE) {
if (!isStateDone(oldState) && isStateDone(state)) {
mDownloadedPages.incrementAndGet();
} else if (oldState != STATE_NONE && state == STATE_NONE) {
} else if (isStateDone(oldState) && !isStateDone(state)) {
mDownloadedPages.decrementAndGet();
}
if (oldState != STATE_FINISHED && state == STATE_FINISHED) {
@@ -763,9 +807,9 @@ public class SpiderQueen implements Runnable {
error = GetText.getString(R.string.error_unknown);
}
mPageErrorMap.put(index, error);
notifyFailure(index, error);
notifyPageFailure(index, error);
} else if (state == STATE_FINISHED) {
notifySuccess(index);
notifyPageSuccess(index);
}
}
@@ -775,7 +819,9 @@ public class SpiderQueen implements Runnable {
if (skipHathKey != null) {
url = url + "?nl=" + skipHathKey;
}
Log.d(TAG, url);
if (DEBUG_LOG) {
Log.d(TAG, url);
}
Response response = mHttpClient.newCall(new EhRequestBuilder(url).build()).execute();
String body = response.body().string();
GalleryPageParser.Result result = GalleryPageParser.parse(body);
@@ -828,7 +874,9 @@ public class SpiderQueen implements Runnable {
continue;
}
Log.d(TAG, imageUrl);
if (DEBUG_LOG) {
Log.d(TAG, imageUrl);
}
// Start download image
OutputStreamPipe pipe = mSpiderDen.openOutputStreamPipe(
@@ -842,7 +890,9 @@ public class SpiderQueen implements Runnable {
// Download image
InputStream is = null;
try {
Log.d(TAG, "Start download image " + index);
if (DEBUG_LOG) {
Log.d(TAG, "Start download image " + index);
}
Response response = mHttpClient.newCall(new EhRequestBuilder(imageUrl).build()).execute();
ResponseBody responseBody = response.body();
@@ -866,7 +916,7 @@ public class SpiderQueen implements Runnable {
mPagePercentMap.put(index, (float) receivedSize / contentLength);
}
// Notify listener
notifyDownload(index, contentLength, receivedSize, bytesRead);
notifyPageDownload(index, contentLength, receivedSize, bytesRead);
}
os.flush();
@@ -876,7 +926,9 @@ public class SpiderQueen implements Runnable {
break;
}
Log.d(TAG, "Download image succeed " + index);
if (DEBUG_LOG) {
Log.d(TAG, "Download image succeed " + index);
}
// Download finished
updatePageState(index, STATE_FINISHED);
@@ -888,7 +940,9 @@ public class SpiderQueen implements Runnable {
pipe.close();
pipe.release();
Log.d(TAG, "End download image " + index);
if (DEBUG_LOG) {
Log.d(TAG, "End download image " + index);
}
}
}
@@ -976,7 +1030,9 @@ public class SpiderQueen implements Runnable {
mWorkerLock.wait();
} catch (InterruptedException e) {
// Interrupted
Log.d(TAG, Thread.currentThread().getName() + " Interrupted");
if (DEBUG_LOG) {
Log.d(TAG, Thread.currentThread().getName() + " Interrupted");
}
break;
}
}
@@ -1005,19 +1061,40 @@ public class SpiderQueen implements Runnable {
@Override
@SuppressWarnings("StatementWithEmptyBody")
public void run() {
Log.i(TAG, Thread.currentThread().getName() + ": start");
if (DEBUG_LOG) {
Log.i(TAG, Thread.currentThread().getName() + ": start");
}
while (!Thread.currentThread().isInterrupted() && runInternal());
boolean finish = true;
// Clear in spider worker array
synchronized (mWorkerLock) {
if (mWorkers != null && mWorkerIndex < mWorkers.length() &&
mWorkers.get(mWorkerIndex) == Thread.currentThread()) {
mWorkers.lazySet(mWorkerIndex, null);
if (mWorkers != null && mWorkerIndex < mWorkers.length &&
mWorkers[mWorkerIndex] == Thread.currentThread()) {
mWorkers[mWorkerIndex] = null;
}
if (mWorkers != null) {
for (Thread mWorker : mWorkers) {
if (null != mWorker) {
finish = false;
break;
}
}
}
if (finish) {
mWorkers = null;
}
}
Log.i(TAG, Thread.currentThread().getName() + ": end");
if (finish) {
notifyFinish();
}
if (DEBUG_LOG) {
Log.i(TAG, Thread.currentThread().getName() + ": end");
}
}
}
@@ -1025,7 +1102,9 @@ public class SpiderQueen implements Runnable {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + ": start");
if (DEBUG_LOG) {
Log.i(TAG, Thread.currentThread().getName() + ": start");
}
while (!Thread.currentThread().isInterrupted()) {
int index;
@@ -1076,7 +1155,9 @@ public class SpiderQueen implements Runnable {
}
}
Log.i(TAG, Thread.currentThread().getName() + ": end");
if (DEBUG_LOG) {
Log.i(TAG, Thread.currentThread().getName() + ": end");
}
}
}
@@ -1089,11 +1170,16 @@ public class SpiderQueen implements Runnable {
/**
* @param contentLength -1 for unknown
*/
void onDownload(int index, long contentLength, long receivedSize, int bytesRead);
void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead);
void onSuccess(int index);
void onPageSuccess(int index, int downloaded, int total);
void onFailure(int index, String error);
void onPageFailure(int index, String error, int downloaded, int total);
/**
* All workers end
*/
void onFinish(int downloaded, int total);
void onGetImageSuccess(int index, Image image);
@@ -26,15 +26,18 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.NavigationView;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.Toast;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhUrlOpener;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.ui.scene.BaseScene;
import com.hippo.ehviewer.ui.scene.DownloadScene;
import com.hippo.ehviewer.ui.scene.GalleryListScene;
import com.hippo.ehviewer.ui.scene.LoginScene;
import com.hippo.ehviewer.ui.scene.WarningScene;
@@ -50,8 +53,12 @@ public final class MainActivity extends StageActivity
private static final String KEY_NAV_CHECKED_ITEM = "nav_checked_item";
@Nullable
private DrawerLayout mDrawerLayout;
@Nullable
private NavigationView mNavView;
@Nullable
private FrameLayout mRightDrawer;
private int mNavCheckedItem = 0;
@Override
@@ -66,8 +73,11 @@ public final class MainActivity extends StageActivity
mDrawerLayout = (DrawerLayout) findViewById(R.id.draw_view);
mNavView = (NavigationView) findViewById(R.id.nav_view);
mRightDrawer = (FrameLayout) findViewById(R.id.right_drawer);
mNavView.setNavigationItemSelectedListener(this);
if (mNavView != null) {
mNavView.setNavigationItemSelectedListener(this);
}
if (savedInstanceState == null) {
onInit();
@@ -76,6 +86,15 @@ public final class MainActivity extends StageActivity
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mDrawerLayout = null;
mNavView = null;
mRightDrawer = null;
}
private boolean handleIntent(Intent intent) {
if (intent == null) {
return false;
@@ -126,10 +145,6 @@ public final class MainActivity extends StageActivity
}
private void onRestore(Bundle savedInstanceState) {
Log.d("TAG", "onRestore");
Log.d("TAG", "action = " + getIntent().getAction());
mNavCheckedItem = savedInstanceState.getInt(KEY_NAV_CHECKED_ITEM);
}
@@ -158,34 +173,69 @@ public final class MainActivity extends StageActivity
}
}
@Override
public void onSceneViewCreated(SceneFragment scene, Bundle savedInstanceState) {
super.onSceneViewCreated(scene, savedInstanceState);
if (scene instanceof BaseScene && mRightDrawer != null && mDrawerLayout != null) {
BaseScene baseScene = (BaseScene) scene;
mRightDrawer.removeAllViews();
View drawerView = baseScene.onCreateDrawerView(
getLayoutInflater(), mRightDrawer, savedInstanceState);
if (drawerView != null) {
mRightDrawer.addView(drawerView);
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.RIGHT);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.RIGHT);
}
}
}
@Override
public void onSceneViewDestroyed(SceneFragment scene) {
super.onSceneViewDestroyed(scene);
if (scene instanceof BaseScene) {
BaseScene baseScene = (BaseScene) scene;
baseScene.onDestroyDrawerView();
}
}
public void setDrawerLayoutEnable(boolean enable) {
if (enable) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
if (mDrawerLayout != null) {
if (enable) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
}
}
}
public void toggleDrawer(int drawerGravity) {
if (mDrawerLayout.isDrawerOpen(drawerGravity)) {
mDrawerLayout.closeDrawer(drawerGravity);
} else {
mDrawerLayout.openDrawer(drawerGravity);
if (mDrawerLayout != null) {
if (mDrawerLayout.isDrawerOpen(drawerGravity)) {
mDrawerLayout.closeDrawer(drawerGravity);
} else {
mDrawerLayout.openDrawer(drawerGravity);
}
}
}
public void setNavCheckedItem(@IdRes int resId) {
mNavCheckedItem = resId;
if (resId == 0) {
mNavView.setCheckedItem(R.id.nav_stub);
} else {
mNavView.setCheckedItem(resId);
if (mNavView != null) {
if (resId == 0) {
mNavView.setCheckedItem(R.id.nav_stub);
} else {
mNavView.setCheckedItem(resId);
}
}
}
@Override
public void onBackPressed() {
if (mDrawerLayout.isDrawerOpen(Gravity.LEFT) || mDrawerLayout.isDrawerOpen(Gravity.RIGHT)) {
if (mDrawerLayout != null && (mDrawerLayout.isDrawerOpen(Gravity.LEFT) ||
mDrawerLayout.isDrawerOpen(Gravity.RIGHT))) {
mDrawerLayout.closeDrawers();
} else {
super.onBackPressed();
@@ -214,13 +264,13 @@ public final class MainActivity extends StageActivity
} else if (id == R.id.nav_history) {
} else if (id == R.id.nav_download) {
startScene(new Announcer(DownloadScene.class));
} else if (id == R.id.nav_settings) {
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
}
if (id != R.id.nav_stub) {
if (id != R.id.nav_stub && mDrawerLayout != null) {
mDrawerLayout.closeDrawers();
}
@@ -20,7 +20,9 @@ import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.scene.SceneFragment;
@@ -53,6 +55,14 @@ public class BaseScene extends SceneFragment {
}
}
public View onCreateDrawerView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return null;
}
public void onDestroyDrawerView() {
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
@@ -0,0 +1,503 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.scene;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.transition.TransitionInflater;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.hippo.easyrecyclerview.EasyRecyclerView;
import com.hippo.ehviewer.EhApplication;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.Settings;
import com.hippo.ehviewer.client.EhCacheKeyFactory;
import com.hippo.ehviewer.client.EhUtils;
import com.hippo.ehviewer.client.data.GalleryInfo;
import com.hippo.ehviewer.download.DownloadInfo;
import com.hippo.ehviewer.download.DownloadManager;
import com.hippo.ehviewer.download.DownloadService;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.ehviewer.widget.SimpleRatingView;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
import com.hippo.scene.TransitionHelper;
import com.hippo.util.ActivityHelper;
import com.hippo.util.ApiHelper;
import com.hippo.vector.VectorDrawable;
import com.hippo.view.ViewTransition;
import com.hippo.widget.LoadImageView;
import com.hippo.widget.SimpleImageView;
import com.hippo.yorozuya.FileUtils;
import com.hippo.yorozuya.ObjectUtils;
import java.util.List;
public class DownloadScene extends ToolbarScene
implements DownloadManager.DownloadInfoListener,
EasyRecyclerView.OnItemClickListener {
private static final String KEY_LABEL = "label";
@Nullable
private EasyRecyclerView mRecyclerView;
@Nullable
private ViewTransition mViewTransition;
@Nullable
private DownloadAdapter mAdapter;
@Nullable
private String mLabel;
@Nullable
private List<DownloadInfo> mList;
// TODO Only single instance
@Override
public int getLaunchMode() {
return LAUNCH_MODE_SINGLE_TOP;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DownloadManager manager = EhApplication.getDownloadManager(getContext());
manager.setDownloadInfoListener(this);
if (savedInstanceState == null) {
onInit();
} else {
onRestore(savedInstanceState);
}
}
private void onInit() {
mLabel = Settings.getRecentDownloadLabel();
DownloadManager manager = EhApplication.getDownloadManager(getContext());
if (mLabel == null) {
mList = manager.getDefaultDownloadInfoList();
} else {
mList = manager.getLabelDownloadInfoList(mLabel);
if (mList == null) {
mLabel = null;
mList = manager.getDefaultDownloadInfoList();
}
}
}
private void onRestore(@NonNull Bundle savedInstanceState) {
mLabel = savedInstanceState.getString(KEY_LABEL);
DownloadManager manager = EhApplication.getDownloadManager(getContext());
if (mLabel == null) {
mList = manager.getDefaultDownloadInfoList();
} else {
mList = manager.getLabelDownloadInfoList(mLabel);
if (mList == null) {
mLabel = null;
mList = manager.getDefaultDownloadInfoList();
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_LABEL, mLabel);
}
@Nullable
@Override
public View onCreateView2(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.scene_download, container, false);
mRecyclerView = (EasyRecyclerView) view.findViewById(R.id.recycler_view);
View tip = view.findViewById(R.id.tip);
SimpleImageView tipImage = (SimpleImageView) tip.findViewById(R.id.tip_image);
tipImage.setDrawable(VectorDrawable.create(getContext(), R.xml.sadpanda_head));
mViewTransition = new ViewTransition(mRecyclerView, tip);
if (mRecyclerView != null) {
mAdapter = new DownloadAdapter();
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerView.setSelector(RippleSalon.generateRippleDrawable(false));
mRecyclerView.setDrawSelectorOnTop(true);
mRecyclerView.hasFixedSize();
mRecyclerView.setClipToPadding(false);
mRecyclerView.setOnItemClickListener(this);
int paddingH = getResources().getDimensionPixelOffset(R.dimen.list_content_margin_h);
int paddingV = getResources().getDimensionPixelOffset(R.dimen.list_content_margin_v);
mRecyclerView.setPadding(paddingV, paddingH, paddingV, paddingH);
}
if (mList == null || mList.isEmpty()) {
mViewTransition.showView(1);
} else {
mViewTransition.showView(0);
}
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setTitle(R.string.download);
setNavigationIcon(VectorDrawable.create(getContext(), R.xml.ic_arrow_left_dark_x24));
// Clear nav checked item
setNavCheckedItem(R.id.nav_download);
// Hide IME
ActivityHelper.hideSoftInput(getActivity());
}
@Override
public void onNavigationClick() {
finish();
}
@Override
public void onDestroyView() {
super.onDestroyView();
DownloadManager manager = EhApplication.getDownloadManager(getContext());
manager.setDownloadInfoListener(null);
mRecyclerView = null;
mViewTransition = null;
mAdapter = null;
}
@Override
public void onDestroy() {
super.onDestroy();
mList = null;
}
@Override
public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) {
List<DownloadInfo> list = mList;
if (list == null) {
return false;
}
if (position < 0 && position >= list.size()) {
return false;
}
Intent intent = new Intent(getActivity(), GalleryActivity.class);
intent.setAction(GalleryActivity.ACTION_EH);
intent.putExtra(GalleryActivity.KEY_GALLERY_INFO, list.get(position).galleryInfo);
startActivity(intent);
return true;
}
@Override
public void onAdd(DownloadInfo info) {
// TODO
}
@Override
public void onUpdate(DownloadInfo info) {
if (!ObjectUtils.equal(info.label, mLabel)) {
return;
}
RecyclerView recyclerView = mRecyclerView;
if (recyclerView == null) {
return;
}
List<DownloadInfo> list = mList;
if (list == null) {
return;
}
int size = list.size();
for (int i = 0, n = recyclerView.getChildCount(); i < n; i++) {
View child = recyclerView.getChildAt(i);
int index = recyclerView.getChildAdapterPosition(child);
if (index >= 0 && index < size && list.get(index) == info) {
DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(child);
if (holder != null) {
bindForState(holder, info);
}
break;
}
}
}
@Override
public void onUpdateAll() {
RecyclerView recyclerView = mRecyclerView;
if (recyclerView == null) {
return;
}
List<DownloadInfo> list = mList;
if (list == null) {
return;
}
int size = list.size();
for (int i = 0, n = recyclerView.getChildCount(); i < n; i++) {
View child = recyclerView.getChildAt(i);
int index = recyclerView.getChildAdapterPosition(child);
if (index >= 0 && index < size) {
DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(child);
bindForState(holder, list.get(index));
}
}
}
@Override
public void onRemove(DownloadInfo info) {
}
private void bindForState(DownloadHolder holder, DownloadInfo info) {
switch (info.state) {
case DownloadInfo.STATE_NONE:
bindState(holder, info, getContext().getString(R.string.download_state_none));
break;
case DownloadInfo.STATE_WAIT:
bindState(holder, info, getContext().getString(R.string.download_state_wait));
break;
case DownloadInfo.STATE_DOWNLOAD:
bindProgress(holder, info);
break;
case DownloadInfo.STATE_FAILED:
String text;
if (info.legacy <= 0) {
text = getContext().getString(R.string.download_state_failed);
} else {
text = getContext().getString(R.string.download_state_failed_2, info.legacy);
}
bindState(holder, info, text);
break;
case DownloadInfo.STATE_FINISH:
bindState(holder, info, getContext().getString(R.string.download_state_finish));
break;
}
}
private void bindState(DownloadHolder holder, DownloadInfo info, String state) {
holder.uploader.setVisibility(View.VISIBLE);
holder.rating.setVisibility(View.VISIBLE);
holder.category.setVisibility(View.VISIBLE);
holder.state.setVisibility(View.VISIBLE);
holder.progressBar.setVisibility(View.INVISIBLE);
holder.percent.setVisibility(View.INVISIBLE);
holder.speed.setVisibility(View.INVISIBLE);
holder.state.setText(state);
}
@SuppressLint("SetTextI18n")
private void bindProgress(DownloadHolder holder, DownloadInfo info) {
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);
holder.percent.setVisibility(View.VISIBLE);
holder.speed.setVisibility(View.VISIBLE);
if (info.total <= 0 || info.download < 0) {
holder.percent.setText(null);
holder.progressBar.setIndeterminate(true);
} else {
holder.percent.setText(info.download + "/" + info.total);
holder.progressBar.setIndeterminate(false);
holder.progressBar.setMax(info.total);
holder.progressBar.setProgress(info.download);
}
long speed = info.speed;
if (speed < 0) {
speed = 0;
}
holder.speed.setText(FileUtils.humanReadableByteCount(speed, false) + "/S");
}
private class DownloadHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public final LoadImageView thumb;
public final TextView title;
public final TextView uploader;
public final SimpleRatingView rating;
public final TextView category;
public final View start;
public final View stop;
public final View delete;
public final TextView state;
public final ProgressBar progressBar;
public final TextView percent;
public final 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);
thumb.setOnClickListener(this);
start.setOnClickListener(this);
stop.setOnClickListener(this);
delete.setOnClickListener(this);
RippleSalon.addRipple(start, false);
RippleSalon.addRipple(stop, false);
RippleSalon.addRipple(delete, false);
}
@Override
public void onClick(View v) {
RecyclerView recyclerView = mRecyclerView;
if (recyclerView == null) {
return;
}
List<DownloadInfo> list = mList;
if (list == null) {
return;
}
int size = list.size();
int index = recyclerView.getChildAdapterPosition(itemView);
if (index < 0 && index >= size) {
return;
}
if (thumb == v) {
Bundle args = new Bundle();
args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GALLERY_INFO);
args.putParcelable(GalleryDetailScene.KEY_GALLERY_INFO, list.get(index).galleryInfo);
Announcer announcer = new Announcer(GalleryDetailScene.class).setArgs(args);
if (ApiHelper.SUPPORT_TRANSITION) {
DownloadHolder holder = (DownloadHolder) recyclerView.getChildViewHolder(itemView);
announcer.setTranHelper(new EnterGalleryDetailTransaction(holder));
}
startScene(announcer);
} else if (start == v) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_START);
intent.putExtra(DownloadService.KEY_GALLERY_INFO, list.get(index).galleryInfo);
getActivity().startService(intent);
} else if (stop == v) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_STOP);
intent.putExtra(DownloadService.KEY_GID, list.get(index).galleryInfo.gid);
getActivity().startService(intent);
} else if (delete == v) {
// TODO
}
}
}
private static class EnterGalleryDetailTransaction implements TransitionHelper {
private final DownloadHolder mHolder;
public EnterGalleryDetailTransaction(DownloadHolder holder) {
mHolder = holder;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTransition(Context context, FragmentTransaction transaction,
Fragment exit, Fragment enter) {
if (!(enter instanceof GalleryDetailScene)) {
return false;
}
exit.setSharedElementReturnTransition(
TransitionInflater.from(context).inflateTransition(R.transition.trans_move));
exit.setExitTransition(
TransitionInflater.from(context).inflateTransition(android.R.transition.fade));
enter.setSharedElementEnterTransition(
TransitionInflater.from(context).inflateTransition(R.transition.trans_move));
enter.setEnterTransition(
TransitionInflater.from(context).inflateTransition(android.R.transition.fade));
transaction.addSharedElement(mHolder.thumb, mHolder.thumb.getTransitionName());
transaction.addSharedElement(mHolder.title, mHolder.title.getTransitionName());
transaction.addSharedElement(mHolder.uploader, mHolder.uploader.getTransitionName());
transaction.addSharedElement(mHolder.category, mHolder.category.getTransitionName());
return true;
}
}
private class DownloadAdapter extends RecyclerView.Adapter<DownloadHolder> {
@Override
public DownloadHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new DownloadHolder(getActivity().getLayoutInflater().inflate(R.layout.item_download, parent, false));
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onBindViewHolder(DownloadHolder holder, int position) {
if (mList == null) {
return;
}
DownloadInfo info = mList.get(position);
GalleryInfo gi = info.galleryInfo;
holder.thumb.load(EhCacheKeyFactory.getThumbKey(gi.gid), gi.thumb, true);
holder.title.setText(gi.title);
holder.uploader.setText(gi.uploader);
holder.rating.setRating(gi.rating);
TextView category = holder.category;
String newCategoryText = EhUtils.getCategory(gi.category);
if (!newCategoryText.equals(category.getText())) {
category.setText(newCategoryText);
category.setBackgroundColor(EhUtils.getCategoryColor(gi.category));
}
bindForState(holder, info);
// Update transition name
if (ApiHelper.SUPPORT_TRANSITION) {
int gid = gi.gid;
holder.thumb.setTransitionName(TransitionNameFactory.getThumbTransitionName(gid));
holder.title.setTransitionName(TransitionNameFactory.getTitleTransitionName(gid));
holder.uploader.setTransitionName(TransitionNameFactory.getUploaderTransitionName(gid));
holder.category.setTransitionName(TransitionNameFactory.getCategoryTransitionName(gid));
}
}
@Override
public int getItemCount() {
return mList == null ? 0 : mList.size();
}
}
}
@@ -61,6 +61,7 @@ import com.hippo.ehviewer.client.data.GalleryTagGroup;
import com.hippo.ehviewer.client.data.LargePreviewSet;
import com.hippo.ehviewer.client.data.ListUrlBuilder;
import com.hippo.ehviewer.client.parser.RateGalleryParser;
import com.hippo.ehviewer.download.DownloadService;
import com.hippo.ehviewer.ui.GalleryActivity;
import com.hippo.rippleold.RippleSalon;
import com.hippo.scene.Announcer;
@@ -870,7 +871,19 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
lub.setCategory(category);
GalleryListScene.startScene(this, lub);
} else if (mDownload == v) {
// TODO
// Add download
GalleryInfo galleryInfo = null;
if (mGalleryInfo != null) {
galleryInfo = mGalleryInfo;
} else if (mGalleryDetail != null) {
galleryInfo = mGalleryDetail;
}
if (galleryInfo != null) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_START);
intent.putExtra(DownloadService.KEY_GALLERY_INFO, galleryInfo);
getActivity().startService(intent);
}
} else if (mRead == v) {
GalleryInfo galleryInfo = null;
if (mGalleryInfo != null) {
@@ -946,7 +959,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
// Only show transaction when thumb can be seen
if (location[1] + mThumb.getHeight() > 0) {
setTransitionName();
finish(new EnterGalleryListTransaction(mThumb, mTitle, mUploader, mCategory));
finish(new ExitTransaction(mThumb, mTitle, mUploader, mCategory));
return;
}
}
@@ -980,14 +993,14 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
}
}
private static class EnterGalleryListTransaction implements TransitionHelper {
private static class ExitTransaction implements TransitionHelper {
private View mThumb;
private View mTitle;
private View mUploader;
private View mCategory;
private final View mThumb;
private final View mTitle;
private final View mUploader;
private final View mCategory;
public EnterGalleryListTransaction(View thumb, View title, View uploader, View category) {
public ExitTransaction(View thumb, View title, View uploader, View category) {
mThumb = thumb;
mTitle = title;
mUploader = uploader;
@@ -998,7 +1011,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onTransition(Context context,
FragmentTransaction transaction, Fragment exit, Fragment enter) {
if (!(enter instanceof GalleryListScene)) {
if (!(enter instanceof GalleryListScene) && !(enter instanceof DownloadScene)) {
return false;
}
@@ -204,6 +204,14 @@ public final class GalleryListScene extends BaseScene
mState = savedInstanceState.getInt(KEY_STATE);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_HAS_FIRST_REFRESH, mHasFirstRefresh);
outState.putParcelable(KEY_LIST_URL_BUILDER, mUrlBuilder);
outState.putInt(KEY_STATE, mState);
}
private void setSearchBarHint(Context context, SearchBar searchBar) {
Resources resources = context.getResources();
Drawable searchImage = VectorDrawable.create(context, R.xml.ic_magnify_x24);
@@ -355,14 +363,6 @@ public final class GalleryListScene extends BaseScene
mFabAnimatorListener = null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_HAS_FIRST_REFRESH, mHasFirstRefresh);
outState.putParcelable(KEY_LIST_URL_BUILDER, mUrlBuilder);
outState.putInt(KEY_STATE, mState);
}
@Override
public void onClick(View v) {
if (mFab == v) {
@@ -417,7 +417,7 @@ public final class GalleryListScene extends BaseScene
private static class EnterGalleryDetailTransaction implements TransitionHelper {
private GalleryListHolder mHolder;
private final GalleryListHolder mHolder;
public EnterGalleryDetailTransaction(GalleryListHolder holder) {
mHolder = holder;
@@ -494,85 +494,69 @@ public final class GalleryListScene extends BaseScene
switch (oldState) {
case STATE_NORMAL:
switch (state) {
case STATE_SIMPLE_SEARCH:
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
showFab();
break;
case STATE_SEARCH:
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
showFab();
break;
case STATE_SEARCH_SHOW_LIST:
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
showFab();
break;
if (state == STATE_SIMPLE_SEARCH) {
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
showFab();
} else if (state == STATE_SEARCH) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
showFab();
} else if (state == STATE_SEARCH_SHOW_LIST) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
showFab();
}
break;
case STATE_SIMPLE_SEARCH:
switch (state) {
case STATE_NORMAL:
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
hideFab();
break;
case STATE_SEARCH:
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
break;
case STATE_SEARCH_SHOW_LIST:
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
break;
if (state == STATE_NORMAL) {
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
hideFab();
} else if (state == STATE_SEARCH) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
} else if (state == STATE_SEARCH_SHOW_LIST) {
mViewTransition.showView(1, animation);
mSearchLayout.scrollSearchContainerToTop();
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
}
break;
case STATE_SEARCH:
switch (state) {
case STATE_NORMAL:
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
hideFab();
break;
case STATE_SIMPLE_SEARCH:
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
break;
case STATE_SEARCH_SHOW_LIST:
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
break;
if (state == STATE_NORMAL) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
hideFab();
} else if (state == STATE_SIMPLE_SEARCH) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
} else if (state == STATE_SEARCH_SHOW_LIST) {
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
}
break;
case STATE_SEARCH_SHOW_LIST:
switch (state) {
case STATE_NORMAL:
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
hideFab();
break;
case STATE_SIMPLE_SEARCH:
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
break;
case STATE_SEARCH:
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
break;
if (state == STATE_NORMAL) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_NORMAL, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
hideFab();
} else if (state == STATE_SIMPLE_SEARCH) {
mViewTransition.showView(0, animation);
mSearchBar.setState(SearchBar.STATE_SEARCH_LIST, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
} else if (state == STATE_SEARCH) {
mSearchBar.setState(SearchBar.STATE_SEARCH, animation);
mSearchBarMoveHelper.returnSearchBarPosition();
}
break;
}
@@ -757,6 +741,7 @@ public final class GalleryListScene extends BaseScene
returnSearchBarPosition(true);
}
@SuppressWarnings("SimplifiableIfStatement")
private void returnSearchBarPosition(boolean animation) {
if (mSearchBar.getHeight() == 0) {
// Layout not called
@@ -919,13 +904,13 @@ public final class GalleryListScene extends BaseScene
private class GalleryListHolder extends RecyclerView.ViewHolder {
private LoadImageView thumb;
private TextView title;
private TextView uploader;
private SimpleRatingView rating;
private TextView category;
private TextView posted;
private TextView simpleLanguage;
private final LoadImageView thumb;
private final TextView title;
private final TextView uploader;
private final SimpleRatingView rating;
private final TextView category;
private final TextView posted;
private final TextView simpleLanguage;
public GalleryListHolder(View itemView) {
super(itemView);
@@ -942,7 +927,7 @@ public final class GalleryListScene extends BaseScene
private class GalleryListAdapter extends RecyclerView.Adapter<GalleryListHolder> {
private LayoutInflater mInflater;
private final LayoutInflater mInflater;
public GalleryListAdapter() {
mInflater = getActivity().getLayoutInflater();
@@ -1053,7 +1038,7 @@ public final class GalleryListScene extends BaseScene
private static class GetGalleryListListener extends EhCallback<GalleryListScene, GalleryListParser.Result> {
private int mTaskId;
private final int mTaskId;
public GetGalleryListListener(Context context, int stageId, String sceneTag, int taskId) {
super(context, stageId, sceneTag);
@@ -35,7 +35,7 @@ public class CookieDB {
private static DaoSession sDaoSession;
public static class DBOpenHelper extends DaoMaster.OpenHelper {
private static class DBOpenHelper extends DaoMaster.OpenHelper {
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
@@ -56,7 +56,7 @@ public class CookieDB {
sDaoSession = daoMaster.newSession();
}
public static Map<String, List<CookieWithID>> getAllCookies() {
static Map<String, List<CookieWithID>> getAllCookies() {
OkHttp3CookieDao dao = sDaoSession.getOkHttp3CookieDao();
List<OkHttp3CookieRaw> list = dao.queryBuilder().list();
@@ -107,11 +107,11 @@ public class CookieDB {
return result;
}
public static void removeCookie(long id) {
static void removeCookie(long id) {
sDaoSession.getOkHttp3CookieDao().deleteByKey(id);
}
public static long addCookie(Cookie cookie) {
static long addCookie(Cookie cookie) {
OkHttp3CookieDao dao = sDaoSession.getOkHttp3CookieDao();
OkHttp3CookieRaw raw = new OkHttp3CookieRaw();
@@ -25,6 +25,7 @@ import android.support.v4.app.FragmentActivity;
import android.view.View;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.ui.MainActivity;
import com.hippo.yorozuya.IntList;
import java.lang.annotation.Retention;
@@ -113,6 +114,17 @@ public class SceneFragment extends Fragment {
view.setTag(R.id.fragment_tag, getTag());
view.setBackgroundColor(getContext().getResources().getColor(R.color.background_light));
// Notify
((MainActivity) getActivity()).onSceneViewCreated(this, savedInstanceState);
}
@Override
public void onDestroyView() {
super.onDestroyView();
// Notify
((MainActivity) getActivity()).onSceneViewDestroyed(this);
}
void addRequest(String requestSceneTag, int requestCode) {
@@ -30,6 +30,7 @@ import com.hippo.ehviewer.R;
import com.hippo.yorozuya.IntIdGenerator;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class StageActivity extends AppCompatActivity {
@@ -44,7 +45,7 @@ public abstract class StageActivity extends AppCompatActivity {
// TODO ArrayList or LinkedList
private ArrayList<String> mSceneTagList = new ArrayList<>();
private IntIdGenerator mIdGenerator = new IntIdGenerator();
private final AtomicInteger mIdGenerator = new AtomicInteger();
private int mStageId = IntIdGenerator.INVALID_ID;
@@ -81,7 +82,7 @@ public abstract class StageActivity extends AppCompatActivity {
if (savedInstanceState != null) {
mStageId = savedInstanceState.getInt(KEY_STAGE_ID, IntIdGenerator.INVALID_ID);
mSceneTagList = savedInstanceState.getStringArrayList(KEY_SCENE_TAG_LIST);
mIdGenerator.setNextId(savedInstanceState.getInt(KEY_NEXT_ID));
mIdGenerator.lazySet(savedInstanceState.getInt(KEY_NEXT_ID));
}
if (mStageId == IntIdGenerator.INVALID_ID) {
@@ -97,6 +98,12 @@ public abstract class StageActivity extends AppCompatActivity {
((SceneApplication) getApplicationContext()).unregisterStageActivity(mStageId);
}
public void onSceneViewCreated(SceneFragment scene, Bundle savedInstanceState) {
}
public void onSceneViewDestroyed(SceneFragment scene) {
}
protected void onRegister(int id) {
mStageId = id;
}
@@ -165,7 +172,7 @@ public abstract class StageActivity extends AppCompatActivity {
newScene.setArguments(args);
// Create new scene tag
newTag = Integer.toString(mIdGenerator.nextId());
newTag = Integer.toString(mIdGenerator.getAndIncrement());
// Add new tag to list
mSceneTagList.add(newTag);
@@ -359,6 +366,6 @@ public abstract class StageActivity extends AppCompatActivity {
super.onSaveInstanceState(outState);
outState.putInt(KEY_STAGE_ID, mStageId);
outState.putStringArrayList(KEY_SCENE_TAG_LIST, mSceneTagList);
outState.putInt(KEY_NEXT_ID, mIdGenerator.nextId());
outState.putInt(KEY_NEXT_ID, mIdGenerator.getAndIncrement());
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.util;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import java.util.List;
public class SqlUtils {
public static void exeSQLSafely(SQLiteDatabase db, String sql) {
try {
db.execSQL(sql);
} catch (SQLException e) {
// Ignore
}
}
public static void dropTable(SQLiteDatabase db, String tableName) {
exeSQLSafely(db, "DROP TABLE IF EXISTS " + tableName);
}
public static void dropAllTable(SQLiteDatabase db) {
List<String> tables = new ArrayList<String>();
Cursor cursor = db.rawQuery("SELECT * FROM sqlite_master WHERE type='table';", null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String tableName = cursor.getString(1);
if (!tableName.equals("android_metadata") &&
!tableName.equals("sqlite_sequence"))
tables.add(tableName);
cursor.moveToNext();
}
cursor.close();
for(String tableName : tables) {
dropTable(db, tableName);
}
}
}
@@ -0,0 +1,351 @@
/*
* Copyright (C) 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.widget;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import com.hippo.anani.AnimationUtils;
import com.hippo.anani.SimpleAnimatorListener;
import com.hippo.ehviewer.R;
public class FabLayout extends ViewGroup implements View.OnClickListener {
private static final long ANIMATE_TIME = 300L;
private static final String STATE_KEY_SUPER = "super";
private static final String STATE_KEY_AUTO_CANCEL = "auto_cancel";
private static final String STATE_KEY_EXPANDED = "expanded";
private int mInterval;
private boolean mFirst = true;
private boolean mExpanded = false;
private boolean mAutoCancel = true;
private float mMainFabCenterY = -1f;
private OnExpandListener mOnExpandListener;
public FabLayout(Context context) {
super(context);
init(context);
}
public FabLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public FabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setSoundEffectsEnabled(false);
mInterval = context.getResources().getDimensionPixelOffset(R.dimen.keyline_margin);
}
@Override
public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) {
if (!(child instanceof FloatingActionButton)) {
throw new IllegalStateException("FloatingActionBarLayout should only " +
"contain FloatingActionButton, but try to add "+ child.getClass().getName());
}
super.addView(child, index, params);
}
public FloatingActionButton getPrimaryFab() {
View v = getChildAt(getChildCount() - 1);
if (v == null) {
return null;
} else {
return (FloatingActionButton) v;
}
}
public int getSecondaryFabCount() {
return Math.max(0, getChildCount() - 1);
}
public FloatingActionButton getSecondaryFabAt(int index) {
if (index < 0 || index >= getSecondaryFabCount()) {
return null;
}
return (FloatingActionButton) getChildAt(index);
}
private int getChildMeasureSpec(int parentMeasureSpec) {
int parentMode = MeasureSpec.getMode(parentMeasureSpec);
int parentSize = MeasureSpec.getSize(parentMeasureSpec);
int childMode;
int childSize;
switch (parentMode) {
default:
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
childMode = MeasureSpec.AT_MOST;
childSize = parentSize;
break;
case MeasureSpec.UNSPECIFIED:
childMode = MeasureSpec.UNSPECIFIED;
childSize = parentSize;
}
return MeasureSpec.makeMeasureSpec(childSize, childMode);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mFirst) {
mFirst = false;
boolean expanded = mExpanded;
int count = getChildCount() - 1;
for (int i = 0; i < count; i++) {
getChildAt(i).setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
}
}
int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec);
int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec);
measureChildren(childWidthMeasureSpec, childHeightMeasureSpec);
int maxWidth = 0;
int maxHeight = 0;
boolean firstChild = true;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = maxHeight + child.getMeasuredHeight();
if (firstChild) {
firstChild = false;
} else {
maxHeight += mInterval;
}
}
// Account for padding too
maxWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop() + getPaddingBottom();
// Check against minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int interval = mInterval;
int centerX = 0;
int bottom = getMeasuredHeight() - getPaddingBottom();
int count = getChildCount();
int i = count;
while(--i >= 0) {
View child = getChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
int right;
if (i == count - 1) {
right = getMeasuredWidth() - getPaddingRight();
} else {
right = centerX + (childWidth / 2);
}
child.layout(right - childWidth, bottom - childHeight, right, bottom);
if (i == count - 1) {
centerX = right - (childWidth / 2);
mMainFabCenterY = bottom - (childHeight / 2f);
}
bottom -= (childHeight + interval);
}
}
public void setOnExpandListener(OnExpandListener listener) {
mOnExpandListener = listener;
}
public void setAutoCancel(boolean autoCancel) {
if (mAutoCancel != autoCancel) {
mAutoCancel = autoCancel;
if (mExpanded) {
if (autoCancel) {
setOnClickListener(this);
} else {
setClickable(false);
}
}
}
}
public void toggle() {
setExpanded(!mExpanded);
}
public boolean isExpanded() {
return mExpanded;
}
public void setExpanded(boolean expanded) {
setExpanded(expanded, true);
}
public void setExpanded(boolean expanded, boolean animation) {
if (mExpanded != expanded) {
mExpanded = expanded;
if (mAutoCancel) {
if (expanded) {
setOnClickListener(this);
} else {
setClickable(false);
}
}
if (mMainFabCenterY == -1f) {
// It is before first onLayout
int count = getChildCount() - 1;
for (int i = 0; i < count; i++) {
getChildAt(i).setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
}
} else {
int count = getChildCount() - 1;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (animation) {
setChildAnimation(child, expanded);
} else {
child.setVisibility(expanded ? View.VISIBLE : View.INVISIBLE);
if (expanded) {
child.setAlpha(1f);
}
}
}
}
if (mOnExpandListener != null) {
mOnExpandListener.onExpand(expanded);
}
}
}
private void setChildAnimation(final View child, final boolean expanded) {
float startTranslationY;
float endTranslationY;
float startAlpha;
float endAlpha;
Interpolator interpolator;
if (expanded) {
startTranslationY = mMainFabCenterY -
(child.getTop() + (child.getHeight() / 2));
endTranslationY = 0f;
startAlpha = 0f;
endAlpha = 1f;
interpolator = AnimationUtils.FAST_SLOW_INTERPOLATOR;
} else {
startTranslationY = 0f;
endTranslationY = mMainFabCenterY -
(child.getTop() + (child.getHeight() / 2));
startAlpha = 1f;
endAlpha = 0f;
interpolator = AnimationUtils.SLOW_FAST_INTERPOLATOR;
}
PropertyValuesHolder translationYPvh = PropertyValuesHolder.ofFloat(
"translationY", startTranslationY, endTranslationY);
PropertyValuesHolder alphaPvh = PropertyValuesHolder.ofFloat(
"alpha", startAlpha, endAlpha);
ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(child, translationYPvh, alphaPvh);
oa.setDuration(ANIMATE_TIME);
oa.setInterpolator(interpolator);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
oa.setAutoCancel(true);
}
oa.addListener(new SimpleAnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
if (expanded) {
child.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (!expanded) {
child.setVisibility(View.INVISIBLE);
}
}
});
oa.start();
}
@Override
public void onClick(View v) {
setExpanded(false);
}
@Override
protected void dispatchSetPressed(boolean pressed) {
// Don't dispatch it to children
}
@Override
public Parcelable onSaveInstanceState() {
final Bundle state = new Bundle();
state.putParcelable(STATE_KEY_SUPER, super.onSaveInstanceState());
state.putBoolean(STATE_KEY_AUTO_CANCEL, mAutoCancel);
state.putBoolean(STATE_KEY_EXPANDED, mExpanded);
return state;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
final Bundle savedState = (Bundle) state;
super.onRestoreInstanceState(savedState.getParcelable(STATE_KEY_SUPER));
setAutoCancel(savedState.getBoolean(STATE_KEY_AUTO_CANCEL));
setExpanded(savedState.getBoolean(STATE_KEY_EXPANDED), false);
}
}
public interface OnExpandListener {
void onExpand(boolean expanded);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

+3 -5
View File
@@ -15,10 +15,8 @@
~ limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
<com.hippo.gl.view.GLRootView
android:id="@+id/gl_root_view"
@@ -80,4 +78,4 @@
android:gravity="center_horizontal"/>
</com.hippo.ehviewer.widget.SliderPanel>
</FrameLayout>
</merge>
@@ -38,4 +38,11 @@
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/nav_drawer_main"/>
<FrameLayout
android:id="@+id/right_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right"
android:minWidth="320dp"/>
</android.support.v4.widget.DrawerLayout>
@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+143
View File
@@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/card_white_2dp">
<com.hippo.ehviewer.widget.ThumbImageView
android:id="@+id/thumb"
android:layout_width="@dimen/list_thumb_width"
android:layout_height="@dimen/list_thumb_height"/>
<TextView
android:id="@+id/title"
style="@style/CardTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="4dp"
android:layout_toRightOf="@id/thumb"/>
<TextView
android:id="@+id/uploader"
style="@style/CardMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_below="@id/title"
android:layout_marginTop="2dp"/>
<com.hippo.ehviewer.widget.SimpleRatingView
android:id="@+id/rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_below="@id/uploader"
android:layout_marginTop="2dp"/>
<TextView
android:id="@+id/category"
style="@style/CardMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_alignBottom="@id/thumb"
android:layout_marginBottom="8dp"
android:paddingBottom="2dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="2dp"
android:textColor="@android:color/white"
android:textAllCaps="true"/>
<LinearLayout
android:id="@+id/actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/thumb"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:orientation="horizontal">
<com.hippo.widget.SimpleImageView
android:id="@+id/start"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="8dp"
android:src="@drawable/ic_play_x24"/>
<com.hippo.widget.SimpleImageView
android:id="@+id/stop"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:padding="8dp"
android:src="@drawable/ic_pause_x24"/>
<com.hippo.widget.SimpleImageView
android:id="@+id/delete"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="8dp"
android:src="@drawable/ic_close_x24"/>
</LinearLayout>
<TextView
android:id="@+id/state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/actions"
android:layout_marginBottom="4dp"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:textColor="?attr/colorAccent"/>
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/title"
android:layout_marginRight="8dp"
android:layout_above="@id/actions"
android:indeterminate="false"/>
<TextView
android:id="@+id/percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/progress_bar"
android:layout_alignLeft="@id/title"
android:textSize="@dimen/text_super_small"/>
<TextView
android:id="@+id/speed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/progress_bar"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:textSize="@dimen/text_super_small"/>
</RelativeLayout>
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2016 Hippo Seven
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.hippo.easyrecyclerview.EasyRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/corner_fab_margin"
android:clipToPadding="false"/>
<LinearLayout
android:id="@+id/tip"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<com.hippo.widget.SimpleImageView
android:id="@+id/tip_image"
android:layout_width="168dp"
android:layout_height="168dp"
android:layout_gravity="center_horizontal"/>
<TextView
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@string/no_download_info"/>
</LinearLayout>
</FrameLayout>
+1
View File
@@ -53,6 +53,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username"
android:imeOptions="actionNext"
android:inputType="text"
android:maxLines="1"
android:singleLine="true"/>
@@ -172,6 +172,22 @@
<string name="gallery_previews">画廊预览</string>
<string name="go_to">跳页</string>
<!-- Download -->
<string name="no_download_info">无下载项目</string>
<string name="download_state_none">未启动</string>
<string name="download_state_wait">等待中</string>
<string name="download_state_failed">失败</string>
<string name="download_state_failed_2">%d 未完成</string>
<string name="download_state_finish">完成</string>
<string name="stat_509_alert_title">509 警告</string>
<string name="stat_509_alert_text">图片配额已用尽。请停止下载,休息一下。</string>
<string name="stat_download_done_title">下载结束</string>
<string name="stat_download_done_text_succeeded">%d 成功</string>
<string name="stat_download_done_text_failed">%d 失败</string>
<string name="stat_download_done_text_mix">%1$d 成功,%2$d 失败</string>
<string name="stat_download_done_line_succeeded">成功:%s</string>
<string name="stat_download_done_line_failed">失败:%s</string>
<!-- Gallery Activity -->
<string name="page_menu_title">第 %d 页</string>
<string name="page_menu_refresh">@string/refresh</string>
@@ -172,6 +172,22 @@
<string name="gallery_previews">畫廊預覽</string>
<string name="go_to">跳頁</string>
<!-- Download -->
<string name="no_download_info">無下載項目</string>
<string name="download_state_none">未啟動</string>
<string name="download_state_wait">等待中</string>
<string name="download_state_failed">失敗</string>
<string name="download_state_failed_2">%d 未完成</string>
<string name="download_state_finish">完成</string>
<string name="stat_509_alert_title">509 警告</string>
<string name="stat_509_alert_text">圖片配額已用盡。請停止下載,休息一下。</string>
<string name="stat_download_done_title">下載結束</string>
<string name="stat_download_done_text_succeeded">%d 成功</string>
<string name="stat_download_done_text_failed">%d 失敗</string>
<string name="stat_download_done_text_mix">%1$d 成功,%2$d 失敗</string>
<string name="stat_download_done_line_succeeded">成功:%s</string>
<string name="stat_download_done_line_failed">失敗:%s</string>
<!-- Gallery Activity -->
<string name="page_menu_title">第 %d 頁</string>
<string name="page_menu_refresh">@string/refresh</string>
@@ -191,6 +207,21 @@
<string name="settings_read_reading_direction_left_to_right">從左至右</string>
<string name="settings_read_reading_direction_right_to_Left">從右至左</string>
<string name="settings_read_reading_direction_top_to_bottom">從上至下</string>
<string name="settings_read_page_scaling_title">頁面縮放</string>
<string name="settings_read_page_scaling_actual_size">原始尺寸</string>
<string name="settings_read_page_scaling_fit_to_width">匹配寬度</string>
<string name="settings_read_page_scaling_fit_to_height">匹配長度</string>
<string name="settings_read_page_scaling_fit_to_screen">匹配屏幕</string>
<string name="settings_read_page_scaling_fixed_scale">固定縮放</string>
<string name="settings_read_start_position_title">開頁位置</string>
<string name="settings_read_start_position_top_left">左上角</string>
<string name="settings_read_start_position_top_right">右上角</string>
<string name="settings_read_start_position_bottom_left">左下角</string>
<string name="settings_read_start_position_bottom_right">右下角</string>
<string name="settings_read_start_position_center">中心</string>
<string name="settings_read_keep_screen_on_title">屏幕常亮</string>
<string name="settings_read_show_clock_title">顯示時鐘</string>
<string name="settings_read_show_battery_title">顯示電量</string>
<string name="settings_read_volume_page_title">使用音量鍵翻頁</string>
<!-- Progress Scene -->
@@ -172,6 +172,22 @@
<string name="gallery_previews">畫廊預覽</string>
<string name="go_to">跳頁</string>
<!-- Download -->
<string name="no_download_info">無下載項目</string>
<string name="download_state_none">未啟動</string>
<string name="download_state_wait">等待中</string>
<string name="download_state_failed">失敗</string>
<string name="download_state_failed_2">%d 未完成</string>
<string name="download_state_finish">完成</string>
<string name="stat_509_alert_title">509 警告</string>
<string name="stat_509_alert_text">圖片配額已用盡。請停止下載,休息一下。</string>
<string name="stat_download_done_title">下載結束</string>
<string name="stat_download_done_text_succeeded">%d 成功</string>
<string name="stat_download_done_text_failed">%d 失敗</string>
<string name="stat_download_done_text_mix">%1$d 成功,%2$d 失敗</string>
<string name="stat_download_done_line_succeeded">成功:%s</string>
<string name="stat_download_done_line_failed">失敗:%s</string>
<!-- Gallery Activity -->
<string name="page_menu_title">第 %d 頁</string>
<string name="page_menu_refresh">@string/refresh</string>
@@ -191,6 +207,21 @@
<string name="settings_read_reading_direction_left_to_right">從左至右</string>
<string name="settings_read_reading_direction_right_to_Left">從右至左</string>
<string name="settings_read_reading_direction_top_to_bottom">從上至下</string>
<string name="settings_read_page_scaling_title">頁面縮放</string>
<string name="settings_read_page_scaling_actual_size">原始尺寸</string>
<string name="settings_read_page_scaling_fit_to_width">匹配寬度</string>
<string name="settings_read_page_scaling_fit_to_height">匹配長度</string>
<string name="settings_read_page_scaling_fit_to_screen">匹配螢幕</string>
<string name="settings_read_page_scaling_fixed_scale">固定縮放</string>
<string name="settings_read_start_position_title">開頁位置</string>
<string name="settings_read_start_position_top_left">左上角</string>
<string name="settings_read_start_position_top_right">右上角</string>
<string name="settings_read_start_position_bottom_left">左下角</string>
<string name="settings_read_start_position_bottom_right">右下角</string>
<string name="settings_read_start_position_center">中心</string>
<string name="settings_read_keep_screen_on_title">螢幕常亮</string>
<string name="settings_read_show_clock_title">顯示時鐘</string>
<string name="settings_read_show_battery_title">顯示電量</string>
<string name="settings_read_volume_page_title">使用音量鍵翻頁</string>
<!-- Progress Scene -->
+16
View File
@@ -212,6 +212,22 @@
<string name="gallery_previews">Gallery Previews</string>
<string name="go_to">Go to</string>
<!-- Download -->
<string name="no_download_info">No download items</string>
<string name="download_state_none">Idle</string>
<string name="download_state_wait">Waiting</string>
<string name="download_state_failed">Failed</string>
<string name="download_state_failed_2">%d incomplete</string>
<string name="download_state_finish">Done</string>
<string name="stat_509_alert_title">509 Alert</string>
<string name="stat_509_alert_text">Image limit is reached. Please stop download and have a relax.</string>
<string name="stat_download_done_title">Download Finished</string>
<string name="stat_download_done_text_succeeded">%d succeeded</string>
<string name="stat_download_done_text_failed">%d failed</string>
<string name="stat_download_done_text_mix">%1$d succeeded, %2$d failed</string>
<string name="stat_download_done_line_succeeded">Succeeded: %s</string>
<string name="stat_download_done_line_failed">Failed: %s</string>
<!-- Gallery Activity -->
<string name="page_menu_title">Page %d</string>
<string name="page_menu_refresh">@string/refresh</string>
+5 -1
View File
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- TextAppearance -->
<style name="TextAppearance" parent="TextAppearance.AppCompat">
@@ -129,4 +129,8 @@
<item name="indicatorColor">@color/divider_dark</item>
</style>
<style name="Widget.Design.FloatingActionButton.Mini">
<item name="fabSize">mini</item>
</style>
</resources>
@@ -0,0 +1,114 @@
/*
* Copyright 2016 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.daogenerator;
import java.io.File;
import de.greenrobot.daogenerator.DaoGenerator;
import de.greenrobot.daogenerator.Entity;
import de.greenrobot.daogenerator.Schema;
public class EhDaoGenerator {
private static final String PACKAGE = "com.hippo.ehviewer.dao";
private static final String OUT_DIR = "../app/src/main/java-gen";
private static final String DELETE_DIR = "../app/src/main/java-gen/com/hippo/ehviewer/dao";
private static final int VERSION = 1;
public static void generate() throws Exception {
Utilities.deleteContents(new File(DELETE_DIR));
File outDir = new File(OUT_DIR);
outDir.delete();
outDir.mkdirs();
Schema schema = new Schema(VERSION, PACKAGE);
addGalleryInfo(schema);
addDownloadInfo(schema);
addDownloadLabel(schema);
addHistoryInfo(schema);
addQuickSearch(schema);
addLocalFavorite(schema);
new DaoGenerator().generateAll(schema, OUT_DIR);
}
private static void addGalleryInfo(Schema schema) {
Entity entity = schema.addEntity("GalleryInfoRaw");
entity.setTableName("GALLERY_INFO");
entity.setClassNameDao("GalleryInfoDao");
entity.addLongProperty("gid").primaryKey();
entity.addStringProperty("token");
entity.addStringProperty("title");
entity.addStringProperty("posted");
entity.addIntProperty("category");
entity.addStringProperty("thumb");
entity.addStringProperty("uploader");
entity.addFloatProperty("rating");
entity.addIntProperty("reference");
}
private static void addDownloadInfo(Schema schema) {
Entity entity = schema.addEntity("DownloadInfoRaw");
entity.setTableName("DOWNLOAD_INFO");
entity.setClassNameDao("DownloadInfoDao");
entity.addLongProperty("gid").primaryKey();
entity.addIntProperty("state");
entity.addIntProperty("legacy");
entity.addLongProperty("date");
entity.addStringProperty("label");
}
private static void addDownloadLabel(Schema schema) {
Entity entity = schema.addEntity("DownloadLabelRaw");
entity.setTableName("DOWNLOAD_LABEL");
entity.setClassNameDao("DownloadLabelDao");
entity.addIdProperty();
entity.addStringProperty("label");
entity.addLongProperty("time");
}
private static void addHistoryInfo(Schema schema) {
Entity entity = schema.addEntity("HistoryInfoRaw");
entity.setTableName("HISTORY_INFO");
entity.setClassNameDao("HistoryInfoDao");
entity.addLongProperty("gid").primaryKey();
entity.addIntProperty("mode");
entity.addLongProperty("date");
}
private static void addQuickSearch(Schema schema) {
Entity entity = schema.addEntity("QuickSearchRaw");
entity.setTableName("QUICK_SEARCH");
entity.setClassNameDao("QuickSearchDao");
entity.addIdProperty();
entity.addStringProperty("name");
entity.addIntProperty("mode");
entity.addIntProperty("category");
entity.addStringProperty("keyword");
entity.addIntProperty("advanceSearch");
entity.addIntProperty("minRating");
entity.addLongProperty("date");
}
private static void addLocalFavorite(Schema schema) {
Entity entity = schema.addEntity("LocalFavoriteRaw");
entity.setTableName("LOCAL_FAVORITE");
entity.setClassNameDao("LocalFavoriteDao");
entity.addLongProperty("gid").primaryKey();
entity.addLongProperty("date");
}
}
@@ -20,5 +20,6 @@ public class Main {
public static void main(String args[]) throws Exception {
CookieDaoGenerator.generate();
EhDaoGenerator.generate();
}
}