From c746d582109d11be3606cbc512538debdc4f80b6 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 25 Jan 2023 00:41:12 +0800 Subject: [PATCH] Refactor move download (#721) * Rename .java to .kt * Convert DownloadManager to kotlin * Refactor move download --- .../main/java/com/hippo/ehviewer/EhDB.java | 23 +- .../ehviewer/download/DownloadManager.java | 1257 ----------------- .../ehviewer/download/DownloadManager.kt | 1234 ++++++++++++++++ .../hippo/ehviewer/ui/scene/DownloadsScene.kt | 71 +- 4 files changed, 1270 insertions(+), 1315 deletions(-) delete mode 100644 app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java create mode 100644 app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt diff --git a/app/src/main/java/com/hippo/ehviewer/EhDB.java b/app/src/main/java/com/hippo/ehviewer/EhDB.java index 08525145a..04b82e66d 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhDB.java +++ b/app/src/main/java/com/hippo/ehviewer/EhDB.java @@ -111,28 +111,9 @@ public class EhDB { return list; } - public static synchronized void moveDownloadInfo(int fromPosition, int toPosition) { - if (fromPosition == toPosition) { - return; - } - - boolean reverse = fromPosition > toPosition; - int offset = reverse ? toPosition : fromPosition; - int limit = reverse ? fromPosition - toPosition + 1 : toPosition - fromPosition + 1; - + public static synchronized void updateDownloadInfo(List downloadInfos) { DownloadsDao dao = db.downloadsDao(); - List list = dao.list(offset, limit); - - int step = reverse ? 1 : -1; - int start = reverse ? limit - 1 : 0; - int end = reverse ? 0 : limit - 1; - long toTime = list.get(end).time; - for (int i = end; reverse ? i < start : i > start; i += step) { - list.get(i).time = list.get(i + step).time; - } - list.get(start).time = toTime; - - dao.update(list); + dao.update(downloadInfos); } // Insert or update diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java deleted file mode 100644 index 3649f3abf..000000000 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java +++ /dev/null @@ -1,1257 +0,0 @@ -/* - * 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.annotation.SuppressLint; -import android.os.AsyncTask; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.hippo.ehviewer.EhDB; -import com.hippo.ehviewer.client.data.BaseGalleryInfo; -import com.hippo.ehviewer.client.data.GalleryInfo; -import com.hippo.ehviewer.dao.DownloadInfo; -import com.hippo.ehviewer.dao.DownloadLabel; -import com.hippo.ehviewer.spider.SpiderDen; -import com.hippo.ehviewer.spider.SpiderInfo; -import com.hippo.ehviewer.spider.SpiderQueen; -import com.hippo.image.Image; -import com.hippo.unifile.UniFile; -import com.hippo.util.IoThreadPoolExecutor; -import com.hippo.yorozuya.ConcurrentPool; -import com.hippo.yorozuya.MathUtils; -import com.hippo.yorozuya.ObjectUtils; -import com.hippo.yorozuya.SimpleHandler; -import com.hippo.yorozuya.collect.LongList; -import com.hippo.yorozuya.collect.SparseIJArray; -import com.hippo.yorozuya.collect.SparseJLArray; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -public class DownloadManager implements SpiderQueen.OnSpiderListener { - - private static final String TAG = DownloadManager.class.getSimpleName(); - private static final Comparator DATE_DESC_COMPARATOR = (lhs, rhs) -> lhs.time == rhs.time ? 0 : lhs.time - rhs.time > 0 ? -1 : 1; - // All download info list - private final LinkedList mAllInfoList; - // All download info map - private final SparseJLArray mAllInfoMap; - // label and info list map, without default label info list - private final Map> mMap; - // All labels without default label - private final List mLabelList; - // Store download info with default label - private final LinkedList mDefaultInfoList; - // Store download info wait to start - private final LinkedList mWaitList; - private final SpeedReminder mSpeedReminder; - private final List mDownloadInfoListeners; - private final ConcurrentPool mNotifyTaskPool = new ConcurrentPool<>(5); - @Nullable - private DownloadListener mDownloadListener; - @Nullable - private DownloadInfo mCurrentTask; - @Nullable - private SpiderQueen mCurrentSpider; - - public DownloadManager() { - - // Get all labels - List labels = EhDB.getAllDownloadLabelList(); - mLabelList = labels; - - // Create list for each label - HashMap> map = new HashMap<>(); - mMap = map; - for (DownloadLabel label : labels) { - map.put(label.getLabel(), new LinkedList<>()); - } - - // Create default for non tag - mDefaultInfoList = new LinkedList<>(); - - // Get all info - List allInfoList = EhDB.getAllDownloadInfo(); - mAllInfoList = new LinkedList<>(allInfoList); - - // Create all info map - SparseJLArray allInfoMap = new SparseJLArray<>(allInfoList.size() + 10); - mAllInfoMap = allInfoMap; - - for (int i = 0, n = allInfoList.size(); i < n; i++) { - DownloadInfo info = allInfoList.get(i); - - // Add to all info map - allInfoMap.put(info.getGid(), info); - - // Add to each label list - LinkedList list = getInfoListForLabel(info.label); - if (list == null) { - // Can't find the label in label list - list = new LinkedList<>(); - map.put(info.label, list); - if (!containLabel(info.label)) { - // Add label to DB and list - labels.add(EhDB.addDownloadLabel(info.label)); - } - } - list.add(info); - } - - mWaitList = new LinkedList<>(); - mSpeedReminder = new SpeedReminder(); - mDownloadInfoListeners = new ArrayList<>(); - } - - @Nullable - private LinkedList getInfoListForLabel(String label) { - if (label == null) { - return mDefaultInfoList; - } else { - return mMap.get(label); - } - } - - public boolean containLabel(String label) { - if (label == null) { - return false; - } - - for (DownloadLabel raw : mLabelList) { - if (label.equals(raw.getLabel())) { - return true; - } - } - - return false; - } - - public boolean containDownloadInfo(long gid) { - return mAllInfoMap.indexOfKey(gid) >= 0; - } - - @NonNull - public List getLabelList() { - return mLabelList; - } - - @NonNull - public List getAllDownloadInfoList() { - return mAllInfoList; - } - - @NonNull - public List getDefaultDownloadInfoList() { - return mDefaultInfoList; - } - - @Nullable - public List getLabelDownloadInfoList(String label) { - return mMap.get(label); - } - - @Nullable - public DownloadInfo getDownloadInfo(long gid) { - return mAllInfoMap.get(gid); - } - - public int getDownloadState(long gid) { - DownloadInfo info = mAllInfoMap.get(gid); - if (null != info) { - return info.state; - } else { - return DownloadInfo.STATE_INVALID; - } - } - - public void addDownloadInfoListener(@Nullable DownloadInfoListener downloadInfoListener) { - mDownloadInfoListeners.add(downloadInfoListener); - } - - public void removeDownloadInfoListener(@Nullable DownloadInfoListener downloadInfoListener) { - mDownloadInfoListeners.remove(downloadInfoListener); - } - - public void setDownloadListener(@Nullable 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(info, SpiderQueen.MODE_DOWNLOAD); - mCurrentTask = info; - mCurrentSpider = spider; - spider.addOnSpiderListener(this); - info.state = DownloadInfo.STATE_DOWNLOAD; - info.speed = -1; - info.remaining = -1; - info.total = -1; - info.finished = 0; - info.downloaded = 0; - info.legacy = -1; - // Update in DB - EhDB.putDownloadInfo(info); - // Start speed count - mSpeedReminder.start(); - // Notify start downloading - if (mDownloadListener != null) { - mDownloadListener.onStart(info); - } - // Notify state update - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - } - } - - void startDownload(GalleryInfo galleryInfo, @Nullable String label) { - if (mCurrentTask != null && mCurrentTask.getGid() == galleryInfo.getGid()) { - // It is current task - return; - } - - // Check in download list - DownloadInfo info = mAllInfoMap.get(galleryInfo.getGid()); - 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.putDownloadInfo(info); - // Notify state update - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - // Make sure download is running - ensureDownload(); - } - } else { - // It is new download info - info = new DownloadInfo(galleryInfo); - info.label = label; - info.state = DownloadInfo.STATE_WAIT; - info.time = System.currentTimeMillis(); - - // Add to label download list - LinkedList list = getInfoListForLabel(info.label); - if (list == null) { - Log.e(TAG, "Can't find download info list with label: " + label); - return; - } - list.addFirst(info); - - // Add to all download list and map - mAllInfoList.addFirst(info); - mAllInfoMap.put(galleryInfo.getGid(), info); - - // Add to wait list - mWaitList.add(info); - - // Save to - EhDB.putDownloadInfo(info); - - // Notify - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onAdd(info, list, list.size() - 1); - } - // Make sure download is running - ensureDownload(); - - // Add it to history - EhDB.putHistoryInfo(info); - } - } - - void startRangeDownload(LongList gidList) { - boolean update = false; - - for (int i = 0, n = gidList.size(); i < n; i++) { - long gid = gidList.get(i); - DownloadInfo info = mAllInfoMap.get(gid); - if (null == info) { - Log.d(TAG, "Can't get download info with gid: " + gid); - continue; - } - - if (info.state == DownloadInfo.STATE_NONE || - info.state == DownloadInfo.STATE_FAILED || - info.state == DownloadInfo.STATE_FINISH) { - update = true; - // Set state DownloadInfo.STATE_WAIT - info.state = DownloadInfo.STATE_WAIT; - // Add to wait list - mWaitList.add(info); - // Update in DB - EhDB.putDownloadInfo(info); - } - } - - if (update) { - // Notify Listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdateAll(); - } - // Ensure download - ensureDownload(); - } - } - - void startAllDownload() { - boolean update = false; - // Start all STATE_NONE and STATE_FAILED item - for (DownloadInfo info : mAllInfoList) { - if (info.state == DownloadInfo.STATE_NONE || info.state == DownloadInfo.STATE_FAILED) { - update = true; - // Set state DownloadInfo.STATE_WAIT - info.state = DownloadInfo.STATE_WAIT; - // Add to wait list - mWaitList.add(info); - // Update in DB - EhDB.putDownloadInfo(info); - } - } - - if (update) { - // Notify Listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdateAll(); - } - // Ensure download - ensureDownload(); - } - } - - public void addDownload(List downloadInfoList) { - addDownload(downloadInfoList, true); - } - - public void addDownload(List downloadInfoList, boolean notify) { - for (DownloadInfo info : downloadInfoList) { - if (containDownloadInfo(info.getGid())) { - // Contain - return; - } - - // Ensure download state - if (DownloadInfo.STATE_WAIT == info.state || - DownloadInfo.STATE_DOWNLOAD == info.state) { - info.state = DownloadInfo.STATE_NONE; - } - - // Add to label download list - LinkedList list = getInfoListForLabel(info.label); - if (null == list) { - // Can't find the label in label list - list = new LinkedList<>(); - mMap.put(info.label, list); - if (!containLabel(info.label)) { - // Add label to DB and list - mLabelList.add(EhDB.addDownloadLabel(info.label)); - } - } - list.add(info); - // Sort - list.sort(DATE_DESC_COMPARATOR); - - // Add to all download list and map - mAllInfoList.add(info); - mAllInfoMap.put(info.getGid(), info); - - // Save to - EhDB.putDownloadInfo(info); - } - - // Sort all download list - mAllInfoList.sort(DATE_DESC_COMPARATOR); - - // Notify - if (notify) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onReload(); - } - } - } - - public void addDownloadLabel(List downloadLabelList) { - for (DownloadLabel label : downloadLabelList) { - String labelString = label.getLabel(); - if (!containLabel(labelString)) { - mMap.put(labelString, new LinkedList<>()); - mLabelList.add(EhDB.addDownloadLabel(label)); - } - } - } - - public void addDownload(GalleryInfo galleryInfo, @Nullable String label) { - if (containDownloadInfo(galleryInfo.getGid())) { - // Contain - return; - } - - // It is new download info - DownloadInfo info = new DownloadInfo(galleryInfo); - info.label = label; - info.state = DownloadInfo.STATE_NONE; - info.time = System.currentTimeMillis(); - - // Add to label download list - LinkedList list = getInfoListForLabel(info.label); - if (list == null) { - Log.e(TAG, "Can't find download info list with label: " + label); - return; - } - list.addFirst(info); - - // Add to all download list and map - mAllInfoList.addFirst(info); - mAllInfoMap.put(galleryInfo.getGid(), info); - - // Save to - EhDB.putDownloadInfo(info); - - // Notify - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onAdd(info, list, list.size() - 1); - } - } - - public void stopDownload(long gid) { - DownloadInfo info = stopDownloadInternal(gid); - if (info != null) { - // Update listener - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - // Ensure download - ensureDownload(); - } - } - - void stopCurrentDownload() { - DownloadInfo info = stopCurrentDownloadInternal(); - if (info != null) { - // Update listener - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - // Ensure download - ensureDownload(); - } - } - - public void stopRangeDownload(LongList gidList) { - stopRangeDownloadInternal(gidList); - - // Update listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdateAll(); - } - - // Ensure download - ensureDownload(); - } - - public void stopAllDownload() { - // Stop all in wait list - for (DownloadInfo info : mWaitList) { - info.state = DownloadInfo.STATE_NONE; - // Update in DB - EhDB.putDownloadInfo(info); - } - mWaitList.clear(); - - // Stop current - stopCurrentDownloadInternal(); - - // Notify mDownloadInfoListener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdateAll(); - } - } - - public void deleteDownload(long gid) { - stopDownloadInternal(gid); - DownloadInfo info = mAllInfoMap.get(gid); - if (info != null) { - // Remove from DB - EhDB.removeDownloadInfo(info); - - // Remove all list and map - mAllInfoList.remove(info); - mAllInfoMap.remove(info.getGid()); - - // Remove label list - LinkedList list = getInfoListForLabel(info.label); - if (list != null) { - int index = list.indexOf(info); - if (index >= 0) { - list.remove(info); - // Update listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onRemove(info, list, index); - } - } - } - - // Ensure download - ensureDownload(); - } - } - - public void deleteRangeDownload(LongList gidList) { - stopRangeDownloadInternal(gidList); - - for (int i = 0, n = gidList.size(); i < n; i++) { - long gid = gidList.get(i); - DownloadInfo info = mAllInfoMap.get(gid); - if (null == info) { - Log.d(TAG, "Can't get download info with gid: " + gid); - continue; - } - - // Remove from DB - EhDB.removeDownloadInfo(info); - - // Remove from all info map - mAllInfoList.remove(info); - mAllInfoMap.remove(info.getGid()); - - // Remove from label list - LinkedList list = getInfoListForLabel(info.label); - if (list != null) { - list.remove(info); - } - } - - // Update listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onReload(); - } - - // Ensure download - ensureDownload(); - } - - @SuppressLint("StaticFieldLeak") - public void resetAllReadingProgress() { - LinkedList list = new LinkedList<>(mAllInfoList); - - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - GalleryInfo galleryInfo = new BaseGalleryInfo(); - for (DownloadInfo downloadInfo : list) { - galleryInfo.setGid(downloadInfo.getGid()); - galleryInfo.setToken(downloadInfo.getToken()); - galleryInfo.setTitle(downloadInfo.getTitle()); - galleryInfo.setThumb(downloadInfo.getThumb()); - galleryInfo.setCategory(downloadInfo.getCategory()); - galleryInfo.setPosted(downloadInfo.getPosted()); - galleryInfo.setUploader(downloadInfo.getUploader()); - galleryInfo.setRating(downloadInfo.getRating()); - - UniFile downloadDir = SpiderDen.getGalleryDownloadDir(galleryInfo.getGid()); - if (downloadDir == null) { - continue; - } - UniFile file = downloadDir.findFile(SpiderQueen.SPIDER_INFO_FILENAME); - if (file == null) { - continue; - } - SpiderInfo spiderInfo = SpiderInfo.read(file); - if (spiderInfo == null) { - continue; - } - spiderInfo.startPage = 0; - - try { - spiderInfo.write(file.openOutputStream()); - } catch (IOException e) { - Log.e(TAG, "Can't write SpiderInfo", e); - } - } - return null; - } - }.executeOnExecutor(IoThreadPoolExecutor.getInstance()); - } - - // Update in DB - // Update listener - // No ensureDownload - private DownloadInfo stopDownloadInternal(long gid) { - // Check current task - if (mCurrentTask != null && mCurrentTask.getGid() == gid) { - // Stop current - return stopCurrentDownloadInternal(); - } - - for (Iterator iterator = mWaitList.iterator(); iterator.hasNext(); ) { - DownloadInfo info = iterator.next(); - if (info.getGid() == gid) { - // Remove from wait list - iterator.remove(); - // Update state - info.state = DownloadInfo.STATE_NONE; - // Update in DB - EhDB.putDownloadInfo(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.putDownloadInfo(info); - // Listener - if (mDownloadListener != null) { - mDownloadListener.onCancel(info); - } - return info; - } - - // Update in DB - // Update mDownloadListener - private void stopRangeDownloadInternal(LongList gidList) { - // Two way - if (gidList.size() < mWaitList.size()) { - for (int i = 0, n = gidList.size(); i < n; i++) { - stopDownloadInternal(gidList.get(i)); - } - } else { - // Check current task - if (mCurrentTask != null && gidList.contains(mCurrentTask.getGid())) { - // Stop current - stopCurrentDownloadInternal(); - } - - // Check all in wait list - for (Iterator iterator = mWaitList.iterator(); iterator.hasNext(); ) { - DownloadInfo info = iterator.next(); - if (gidList.contains(info.getGid())) { - // Remove from wait list - iterator.remove(); - // Update state - info.state = DownloadInfo.STATE_NONE; - // Update in DB - EhDB.putDownloadInfo(info); - } - } - } - } - - /** - * @param label Not allow new label - */ - public void changeLabel(List list, String label) { - if (null != label && !containLabel(label)) { - Log.e(TAG, "Not exits label: " + label); - return; - } - - List dstList = getInfoListForLabel(label); - if (dstList == null) { - Log.e(TAG, "Can't find label with label: " + label); - return; - } - - for (DownloadInfo info : list) { - if (ObjectUtils.equal(info.label, label)) { - continue; - } - - List srcList = getInfoListForLabel(info.label); - if (srcList == null) { - Log.e(TAG, "Can't find label with label: " + info.label); - continue; - } - - srcList.remove(info); - dstList.add(info); - info.label = label; - dstList.sort(DATE_DESC_COMPARATOR); - - // Save to DB - EhDB.putDownloadInfo(info); - } - - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onReload(); - } - } - - public void addLabel(String label) { - if (label == null || containLabel(label)) { - return; - } - - mLabelList.add(EhDB.addDownloadLabel(label)); - mMap.put(label, new LinkedList<>()); - - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdateLabels(); - } - } - - public void moveLabel(int fromPosition, int toPosition) { - final DownloadLabel item = mLabelList.remove(fromPosition); - mLabelList.add(toPosition, item); - EhDB.moveDownloadLabel(fromPosition, toPosition); - - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdateLabels(); - } - } - - public void renameLabel(@NonNull String from, @NonNull String to) { - // Find in label list - boolean found = false; - for (DownloadLabel raw : mLabelList) { - if (from.equals(raw.getLabel())) { - found = true; - raw.setLabel(to); - // Update in DB - EhDB.updateDownloadLabel(raw); - break; - } - } - if (!found) { - return; - } - - LinkedList list = mMap.remove(from); - if (list == null) { - return; - } - - // Update info label - for (DownloadInfo info : list) { - info.label = to; - // Update in DB - EhDB.putDownloadInfo(info); - } - // Put list back with new label - mMap.put(to, list); - - // Notify listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onRenameLabel(from, to); - } - } - - public void deleteLabel(@NonNull String label) { - // Find in label list and remove - boolean found = false; - for (Iterator iterator = mLabelList.iterator(); iterator.hasNext(); ) { - DownloadLabel raw = iterator.next(); - if (label.equals(raw.getLabel())) { - found = true; - iterator.remove(); - EhDB.removeDownloadLabel(raw); - break; - } - } - if (!found) { - return; - } - - LinkedList list = mMap.remove(label); - if (list == null) { - return; - } - - // Update info label - for (DownloadInfo info : list) { - info.label = null; - // Update in DB - EhDB.putDownloadInfo(info); - mDefaultInfoList.add(info); - } - - // Sort - mDefaultInfoList.sort(DATE_DESC_COMPARATOR); - - // Notify listener - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onChange(); - } - } - - 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 finished, int downloaded, int total) { - NotifyTask task = mNotifyTaskPool.pop(); - if (task == null) { - task = new NotifyTask(); - } - task.setOnPageSuccessData(index, finished, downloaded, total); - SimpleHandler.getInstance().post(task); - } - - @Override - public void onPageFailure(int index, String error, int finished, int downloaded, int total) { - NotifyTask task = mNotifyTaskPool.pop(); - if (task == null) { - task = new NotifyTask(); - } - task.setOnPageFailureDate(index, error, finished, downloaded, total); - SimpleHandler.getInstance().post(task); - } - - @Override - public void onFinish(int finished, int downloaded, int total) { - NotifyTask task = mNotifyTaskPool.pop(); - if (task == null) { - task = new NotifyTask(); - } - task.setOnFinishDate(finished, downloaded, total); - SimpleHandler.getInstance().post(task); - } - - @Override - public void onGetImageSuccess(int index, Image image) { - // Ignore - } - - @Override - public void onGetImageFailure(int index, String error) { - // Ignore - } - - - public interface DownloadInfoListener { - - /** - * Add the special info to the special position - */ - void onAdd(@NonNull DownloadInfo info, @NonNull List list, int position); - - /** - * The special info is changed - */ - void onUpdate(@NonNull DownloadInfo info, @NonNull List list); - - /** - * Maybe all data is changed, but size is the same - */ - void onUpdateAll(); - - /** - * Maybe all data is changed, maybe list is changed - */ - void onReload(); - - /** - * The list is gone, use default list please - */ - void onChange(); - - /** - * Rename label - */ - void onRenameLabel(String from, String to); - - /** - * Remove the special info from the special position - */ - void onRemove(@NonNull DownloadInfo info, @NonNull List list, int position); - - void onUpdateLabels(); - } - - 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); - } - - 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; - @SuppressWarnings("unused") - private String mError; - private int mFinished; - 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 finished, int downloaded, int total) { - mType = TYPE_ON_PAGE_SUCCESS; - mIndex = index; - mFinished = finished; - mDownloaded = downloaded; - mTotal = total; - } - - public void setOnPageFailureDate(int index, String error, int finished, int downloaded, int total) { - mType = TYPE_ON_PAGE_FAILURE; - mIndex = index; - mError = error; - mFinished = finished; - mDownloaded = downloaded; - mTotal = total; - } - - public void setOnFinishDate(int finished, int downloaded, int total) { - mType = TYPE_ON_FINISH; - mFinished = finished; - 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; - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - } - } - case TYPE_ON_GET_509 -> { - if (mDownloadListener != null) { - mDownloadListener.onGet509(); - } - } - case TYPE_ON_PAGE_DOWNLOAD -> - mSpeedReminder.onDownload(mIndex, mContentLength, mReceivedSize, mBytesRead); - case TYPE_ON_PAGE_SUCCESS -> { - mSpeedReminder.onDone(mIndex); - DownloadInfo info = mCurrentTask; - if (info == null) { - Log.e(TAG, "Current task is null, but it should not be"); - } else { - info.finished = mFinished; - info.downloaded = mDownloaded; - info.total = mTotal; - if (mDownloadListener != null) { - mDownloadListener.onGetPage(info); - } - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - } - } - case TYPE_ON_PAGE_FAILURE -> { - mSpeedReminder.onDone(mIndex); - DownloadInfo info = mCurrentTask; - if (info == null) { - Log.e(TAG, "Current task is null, but it should not be"); - } else { - info.finished = mFinished; - info.downloaded = mDownloaded; - info.total = mTotal; - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - } - } - case TYPE_ON_FINISH -> { - mSpeedReminder.onFinish(); - // 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.finished = mFinished; - info.downloaded = mDownloaded; - info.total = mTotal; - info.legacy = mTotal - mFinished; - if (info.legacy == 0) { - info.state = DownloadInfo.STATE_FINISH; - } else { - info.state = DownloadInfo.STATE_FAILED; - } - // Update in DB - EhDB.putDownloadInfo(info); - // Notify - if (mDownloadListener != null) { - mDownloadListener.onFinish(info); - } - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - // Start next download - ensureDownload(); - } - } - - mNotifyTaskPool.push(this); - } - } - - class SpeedReminder implements Runnable { - - private final SparseIJArray mContentLengthMap = new SparseIJArray(); - private final SparseIJArray mReceivedSizeMap = new SparseIJArray(); - private boolean mStop = true; - private long mBytesRead; - private long oldSpeed = -1; - - public void start() { - if (mStop) { - mStop = false; - SimpleHandler.getInstance().post(this); - } - } - - public void stop() { - if (!mStop) { - mStop = true; - mBytesRead = 0; - oldSpeed = -1; - mContentLengthMap.clear(); - mReceivedSizeMap.clear(); - SimpleHandler.getInstance().removeCallbacks(this); - } - } - - public void onDownload(int index, long contentLength, long receivedSize, int bytesRead) { - mContentLengthMap.put(index, contentLength); - mReceivedSizeMap.put(index, receivedSize); - mBytesRead += bytesRead; - } - - public void onDone(int index) { - mContentLengthMap.delete(index); - mReceivedSizeMap.delete(index); - } - - public void onFinish() { - mContentLengthMap.clear(); - mReceivedSizeMap.clear(); - } - - @Override - public void run() { - DownloadInfo info = mCurrentTask; - if (info != null) { - long newSpeed = mBytesRead / 2; - if (oldSpeed != -1) { - newSpeed = (long) MathUtils.lerp(oldSpeed, newSpeed, 0.75f); - } - oldSpeed = newSpeed; - info.speed = newSpeed; - - // Calculate remaining - if (info.total <= 0) { - info.remaining = -1; - } else if (newSpeed == 0) { - info.remaining = 300L * 24L * 60L * 60L * 1000L; // 300 days - } else { - int downloadingCount = 0; - long downloadingContentLengthSum = 0; - long totalSize = 0; - for (int i = 0, n = Math.max(mContentLengthMap.size(), mReceivedSizeMap.size()); i < n; i++) { - long contentLength = mContentLengthMap.valueAt(i); - long receivedSize = mReceivedSizeMap.valueAt(i); - downloadingCount++; - downloadingContentLengthSum += contentLength; - totalSize += contentLength - receivedSize; - } - if (downloadingCount != 0) { - totalSize += downloadingContentLengthSum * (info.total - info.downloaded - downloadingCount) / downloadingCount; - info.remaining = totalSize / newSpeed * 1000; - } - } - if (mDownloadListener != null) { - mDownloadListener.onDownload(info); - } - List list = getInfoListForLabel(info.label); - if (list != null) { - for (DownloadInfoListener l : mDownloadInfoListeners) { - l.onUpdate(info, list); - } - } - } - - mBytesRead = 0; - - if (!mStop) { - SimpleHandler.getInstance().postDelayed(this, 2000); - } - } - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt new file mode 100644 index 000000000..d31aed9b2 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt @@ -0,0 +1,1234 @@ +/* + * 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.annotation.SuppressLint +import android.os.AsyncTask +import android.util.Log +import com.hippo.ehviewer.EhDB +import com.hippo.ehviewer.client.data.BaseGalleryInfo +import com.hippo.ehviewer.client.data.GalleryInfo +import com.hippo.ehviewer.dao.DownloadInfo +import com.hippo.ehviewer.dao.DownloadLabel +import com.hippo.ehviewer.spider.SpiderDen +import com.hippo.ehviewer.spider.SpiderInfo +import com.hippo.ehviewer.spider.SpiderQueen +import com.hippo.ehviewer.spider.SpiderQueen.OnSpiderListener +import com.hippo.image.Image +import com.hippo.util.IoThreadPoolExecutor +import com.hippo.yorozuya.ConcurrentPool +import com.hippo.yorozuya.MathUtils +import com.hippo.yorozuya.ObjectUtils +import com.hippo.yorozuya.SimpleHandler +import com.hippo.yorozuya.collect.LongList +import com.hippo.yorozuya.collect.SparseIJArray +import com.hippo.yorozuya.collect.SparseJLArray +import java.io.IOException +import java.util.LinkedList + +class DownloadManager : OnSpiderListener { + // All download info list + private val mAllInfoList: LinkedList + + // All download info map + private val mAllInfoMap: SparseJLArray + + // label and info list map, without default label info list + private val mMap: MutableMap> + + // All labels without default label + private val mLabelList: MutableList + + // Store download info with default label + private val mDefaultInfoList: LinkedList + + // Store download info wait to start + private val mWaitList: LinkedList + private val mSpeedReminder: SpeedReminder + private val mDownloadInfoListeners: MutableList + private val mNotifyTaskPool = ConcurrentPool(5) + private var mDownloadListener: DownloadListener? = null + private var mCurrentTask: DownloadInfo? = null + private var mCurrentSpider: SpiderQueen? = null + + init { + + // Get all labels + val labels = EhDB.getAllDownloadLabelList() + mLabelList = labels + + // Create list for each label + val map = HashMap>() + mMap = map + for ((_, label1) in labels) { + map[label1] = LinkedList() + } + + // Create default for non tag + mDefaultInfoList = LinkedList() + + // Get all info + val allInfoList = EhDB.getAllDownloadInfo() + mAllInfoList = LinkedList(allInfoList) + + // Create all info map + val allInfoMap = SparseJLArray(allInfoList.size + 10) + mAllInfoMap = allInfoMap + for (info in allInfoList) { + + // Add to all info map + allInfoMap.put(info.gid, info) + + // Add to each label list + var list = getInfoListForLabel(info.label) + if (list == null) { + // Can't find the label in label list + list = LinkedList() + map[info.label] = list + if (!containLabel(info.label)) { + // Add label to DB and list + labels.add(EhDB.addDownloadLabel(info.label)) + } + } + list.add(info) + } + mWaitList = LinkedList() + mSpeedReminder = SpeedReminder() + mDownloadInfoListeners = ArrayList() + } + + private fun getInfoListForLabel(label: String?): LinkedList? { + return if (label == null) { + mDefaultInfoList + } else { + mMap[label] + } + } + + fun containLabel(label: String?): Boolean { + if (label == null) { + return false + } + for ((_, label1) in mLabelList) { + if (label == label1) { + return true + } + } + return false + } + + fun containDownloadInfo(gid: Long): Boolean { + return mAllInfoMap.indexOfKey(gid) >= 0 + } + + val labelList: List + get() = mLabelList + val allDownloadInfoList: List + get() = mAllInfoList + val defaultDownloadInfoList: List + get() = mDefaultInfoList + + fun getLabelDownloadInfoList(label: String?): List? { + return mMap[label] + } + + fun getDownloadInfo(gid: Long): DownloadInfo? { + return mAllInfoMap[gid] + } + + fun getDownloadState(gid: Long): Int { + val info = mAllInfoMap[gid] + return info?.state ?: DownloadInfo.STATE_INVALID + } + + fun addDownloadInfoListener(downloadInfoListener: DownloadInfoListener?) { + mDownloadInfoListeners.add(downloadInfoListener) + } + + fun removeDownloadInfoListener(downloadInfoListener: DownloadInfoListener?) { + mDownloadInfoListeners.remove(downloadInfoListener) + } + + fun setDownloadListener(listener: DownloadListener?) { + mDownloadListener = listener + } + + private fun ensureDownload() { + if (mCurrentTask != null) { + // Only one download + return + } + + // Get download from wait list + if (!mWaitList.isEmpty()) { + val info = mWaitList.removeFirst() + val spider = SpiderQueen.obtainSpiderQueen(info, SpiderQueen.MODE_DOWNLOAD) + mCurrentTask = info + mCurrentSpider = spider + spider.addOnSpiderListener(this) + info.state = DownloadInfo.STATE_DOWNLOAD + info.speed = -1 + info.remaining = -1 + info.total = -1 + info.finished = 0 + info.downloaded = 0 + info.legacy = -1 + // Update in DB + EhDB.putDownloadInfo(info) + // Start speed count + mSpeedReminder.start() + // Notify start downloading + if (mDownloadListener != null) { + mDownloadListener!!.onStart(info) + } + // Notify state update + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + } + } + + fun startDownload(galleryInfo: GalleryInfo, label: String?) { + if (mCurrentTask != null && mCurrentTask!!.gid == galleryInfo.gid) { + // It is current task + return + } + + // Check in download list + var info = mAllInfoMap[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.putDownloadInfo(info) + // Notify state update + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + // Make sure download is running + ensureDownload() + } + } else { + // It is new download info + info = DownloadInfo(galleryInfo) + info.label = label + info.state = DownloadInfo.STATE_WAIT + info.time = System.currentTimeMillis() + + // Add to label download list + val list = getInfoListForLabel(info.label) + if (list == null) { + Log.e(TAG, "Can't find download info list with label: $label") + return + } + list.addFirst(info) + + // Add to all download list and map + mAllInfoList.addFirst(info) + mAllInfoMap.put(galleryInfo.gid, info) + + // Add to wait list + mWaitList.add(info) + + // Save to + EhDB.putDownloadInfo(info) + + // Notify + for (l in mDownloadInfoListeners) { + l!!.onAdd(info, list, list.size - 1) + } + // Make sure download is running + ensureDownload() + + // Add it to history + EhDB.putHistoryInfo(info) + } + } + + fun startRangeDownload(gidList: LongList) { + var update = false + for (i in 0 until gidList.size()) { + val gid = gidList[i] + val info = mAllInfoMap[gid] + if (null == info) { + Log.d(TAG, "Can't get download info with gid: $gid") + continue + } + if (info.state == DownloadInfo.STATE_NONE || info.state == DownloadInfo.STATE_FAILED || info.state == DownloadInfo.STATE_FINISH) { + update = true + // Set state DownloadInfo.STATE_WAIT + info.state = DownloadInfo.STATE_WAIT + // Add to wait list + mWaitList.add(info) + // Update in DB + EhDB.putDownloadInfo(info) + } + } + if (update) { + // Notify Listener + for (l in mDownloadInfoListeners) { + l!!.onUpdateAll() + } + // Ensure download + ensureDownload() + } + } + + fun startAllDownload() { + var update = false + // Start all STATE_NONE and STATE_FAILED item + for (info in mAllInfoList) { + if (info.state == DownloadInfo.STATE_NONE || info.state == DownloadInfo.STATE_FAILED) { + update = true + // Set state DownloadInfo.STATE_WAIT + info.state = DownloadInfo.STATE_WAIT + // Add to wait list + mWaitList.add(info) + // Update in DB + EhDB.putDownloadInfo(info) + } + } + if (update) { + // Notify Listener + for (l in mDownloadInfoListeners) { + l!!.onUpdateAll() + } + // Ensure download + ensureDownload() + } + } + + @JvmOverloads + fun addDownload(downloadInfoList: List, notify: Boolean = true) { + for (info in downloadInfoList) { + if (containDownloadInfo(info.gid)) { + // Contain + return + } + + // Ensure download state + if (DownloadInfo.STATE_WAIT == info.state || + DownloadInfo.STATE_DOWNLOAD == info.state + ) { + info.state = DownloadInfo.STATE_NONE + } + + // Add to label download list + var list = getInfoListForLabel(info.label) + if (null == list) { + // Can't find the label in label list + list = LinkedList() + mMap[info.label] = list + if (!containLabel(info.label)) { + // Add label to DB and list + mLabelList.add(EhDB.addDownloadLabel(info.label)) + } + } + list.add(info) + // Sort + list.sortByDateDescending() + + // Add to all download list and map + mAllInfoList.add(info) + mAllInfoMap.put(info.gid, info) + + // Save to + EhDB.putDownloadInfo(info) + } + + // Sort all download list + mAllInfoList.sortByDateDescending() + + // Notify + if (notify) { + for (l in mDownloadInfoListeners) { + l!!.onReload() + } + } + } + + fun addDownloadLabel(downloadLabelList: List) { + for (label in downloadLabelList) { + val labelString = label.label + if (!containLabel(labelString)) { + mMap[labelString] = LinkedList() + mLabelList.add(EhDB.addDownloadLabel(label)) + } + } + } + + fun addDownload(galleryInfo: GalleryInfo, label: String?) { + if (containDownloadInfo(galleryInfo.gid)) { + // Contain + return + } + + // It is new download info + val info = DownloadInfo(galleryInfo) + info.label = label + info.state = DownloadInfo.STATE_NONE + info.time = System.currentTimeMillis() + + // Add to label download list + val list = getInfoListForLabel(info.label) + if (list == null) { + Log.e(TAG, "Can't find download info list with label: $label") + return + } + list.addFirst(info) + + // Add to all download list and map + mAllInfoList.addFirst(info) + mAllInfoMap.put(galleryInfo.gid, info) + + // Save to + EhDB.putDownloadInfo(info) + + // Notify + for (l in mDownloadInfoListeners) { + l!!.onAdd(info, list, list.size - 1) + } + } + + fun stopDownload(gid: Long) { + val info = stopDownloadInternal(gid) + if (info != null) { + // Update listener + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + // Ensure download + ensureDownload() + } + } + + fun stopCurrentDownload() { + val info = stopCurrentDownloadInternal() + if (info != null) { + // Update listener + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + // Ensure download + ensureDownload() + } + } + + fun stopRangeDownload(gidList: LongList) { + stopRangeDownloadInternal(gidList) + + // Update listener + for (l in mDownloadInfoListeners) { + l!!.onUpdateAll() + } + + // Ensure download + ensureDownload() + } + + fun stopAllDownload() { + // Stop all in wait list + for (info in mWaitList) { + info.state = DownloadInfo.STATE_NONE + // Update in DB + EhDB.putDownloadInfo(info) + } + mWaitList.clear() + + // Stop current + stopCurrentDownloadInternal() + + // Notify mDownloadInfoListener + for (l in mDownloadInfoListeners) { + l!!.onUpdateAll() + } + } + + fun deleteDownload(gid: Long) { + stopDownloadInternal(gid) + val info = mAllInfoMap[gid] + if (info != null) { + // Remove from DB + EhDB.removeDownloadInfo(info) + + // Remove all list and map + mAllInfoList.remove(info) + mAllInfoMap.remove(info.gid) + + // Remove label list + val list = getInfoListForLabel(info.label) + if (list != null) { + val index = list.indexOf(info) + if (index >= 0) { + list.remove(info) + // Update listener + for (l in mDownloadInfoListeners) { + l!!.onRemove(info, list, index) + } + } + } + + // Ensure download + ensureDownload() + } + } + + fun deleteRangeDownload(gidList: LongList) { + stopRangeDownloadInternal(gidList) + for (i in 0 until gidList.size()) { + val gid = gidList[i] + val info = mAllInfoMap[gid] + if (null == info) { + Log.d(TAG, "Can't get download info with gid: $gid") + continue + } + + // Remove from DB + EhDB.removeDownloadInfo(info) + + // Remove from all info map + mAllInfoList.remove(info) + mAllInfoMap.remove(info.gid) + + // Remove from label list + val list = getInfoListForLabel(info.label) + list?.remove(info) + } + + // Update listener + for (l in mDownloadInfoListeners) { + l!!.onReload() + } + + // Ensure download + ensureDownload() + } + + fun moveDownload(fromPosition: Int, toPosition: Int) { + if (fromPosition > toPosition) { + val time = mAllInfoList[toPosition].time + for (i in toPosition until fromPosition) { + mAllInfoList[i].time = mAllInfoList[i + 1].time + } + mAllInfoList[fromPosition].time = time + EhDB.updateDownloadInfo(mAllInfoList.slice(toPosition..fromPosition)) + } else { + val time = mAllInfoList[fromPosition].time + for (i in fromPosition until toPosition) { + mAllInfoList[i].time = mAllInfoList[i + 1].time + } + mAllInfoList[toPosition].time = time + EhDB.updateDownloadInfo(mAllInfoList.slice(fromPosition..toPosition)) + } + val label = mAllInfoList[fromPosition].label + mAllInfoList.sortByDateDescending() + val list = getInfoListForLabel(label)!! + list.sortByDateDescending() + } + + fun moveDownload(label: String?, fromPosition: Int, toPosition: Int) { + val list = getInfoListForLabel(label) + list?.let { + val absFromPosition = mAllInfoList.indexOf(it[fromPosition]) + val absToPosition = mAllInfoList.indexOf(it[toPosition]) + moveDownload(absFromPosition, absToPosition) + } + } + + @SuppressLint("StaticFieldLeak") + fun resetAllReadingProgress() { + val list = LinkedList(mAllInfoList) + object : AsyncTask() { + @Deprecated("Deprecated in Java") + override fun doInBackground(vararg voids: Void?): Void? { + val galleryInfo: GalleryInfo = BaseGalleryInfo() + for (downloadInfo in list) { + galleryInfo.gid = downloadInfo.gid + galleryInfo.token = downloadInfo.token + galleryInfo.title = downloadInfo.title + galleryInfo.thumb = downloadInfo.thumb + galleryInfo.category = downloadInfo.category + galleryInfo.posted = downloadInfo.posted + galleryInfo.uploader = downloadInfo.uploader + galleryInfo.rating = downloadInfo.rating + val downloadDir = SpiderDen.getGalleryDownloadDir(galleryInfo.gid) ?: continue + val file = downloadDir.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: continue + val spiderInfo = SpiderInfo.read(file) ?: continue + spiderInfo.startPage = 0 + try { + spiderInfo.write(file.openOutputStream()) + } catch (e: IOException) { + Log.e(TAG, "Can't write SpiderInfo", e) + } + } + return null + } + }.executeOnExecutor(IoThreadPoolExecutor.getInstance()) + } + + // Update in DB + // Update listener + // No ensureDownload + private fun stopDownloadInternal(gid: Long): DownloadInfo? { + // Check current task + if (mCurrentTask != null && mCurrentTask!!.gid == gid) { + // Stop current + return stopCurrentDownloadInternal() + } + val iterator = mWaitList.iterator() + while (iterator.hasNext()) { + val info = iterator.next() + if (info.gid == gid) { + // Remove from wait list + iterator.remove() + // Update state + info.state = DownloadInfo.STATE_NONE + // Update in DB + EhDB.putDownloadInfo(info) + return info + } + } + return null + } + + // Update in DB + // Update mDownloadListener + private fun stopCurrentDownloadInternal(): DownloadInfo? { + val info = mCurrentTask + val spider = mCurrentSpider + // Release spider + if (spider != null) { + spider.removeOnSpiderListener(this@DownloadManager) + 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.putDownloadInfo(info) + // Listener + if (mDownloadListener != null) { + mDownloadListener!!.onCancel(info) + } + return info + } + + // Update in DB + // Update mDownloadListener + private fun stopRangeDownloadInternal(gidList: LongList) { + // Two way + if (gidList.size() < mWaitList.size) { + for (i in 0 until gidList.size()) { + stopDownloadInternal(gidList[i]) + } + } else { + // Check current task + if (mCurrentTask != null && gidList.contains(mCurrentTask!!.gid)) { + // Stop current + stopCurrentDownloadInternal() + } + + // Check all in wait list + val iterator = mWaitList.iterator() + while (iterator.hasNext()) { + val info = iterator.next() + if (gidList.contains(info.gid)) { + // Remove from wait list + iterator.remove() + // Update state + info.state = DownloadInfo.STATE_NONE + // Update in DB + EhDB.putDownloadInfo(info) + } + } + } + } + + /** + * @param label Not allow new label + */ + fun changeLabel(list: List, label: String?) { + if (null != label && !containLabel(label)) { + Log.e(TAG, "Not exits label: $label") + return + } + val dstList: MutableList? = getInfoListForLabel(label) + if (dstList == null) { + Log.e(TAG, "Can't find label with label: $label") + return + } + for (info in list) { + if (ObjectUtils.equal(info.label, label)) { + continue + } + val srcList: MutableList? = getInfoListForLabel(info.label) + if (srcList == null) { + Log.e(TAG, "Can't find label with label: " + info.label) + continue + } + srcList.remove(info) + dstList.add(info) + info.label = label + dstList.sortByDateDescending() + + // Save to DB + EhDB.putDownloadInfo(info) + } + for (l in mDownloadInfoListeners) { + l!!.onReload() + } + } + + fun addLabel(label: String?) { + if (label == null || containLabel(label)) { + return + } + mLabelList.add(EhDB.addDownloadLabel(label)) + mMap[label] = LinkedList() + for (l in mDownloadInfoListeners) { + l!!.onUpdateLabels() + } + } + + fun moveLabel(fromPosition: Int, toPosition: Int) { + val item = mLabelList.removeAt(fromPosition) + mLabelList.add(toPosition, item) + EhDB.moveDownloadLabel(fromPosition, toPosition) + for (l in mDownloadInfoListeners) { + l!!.onUpdateLabels() + } + } + + fun renameLabel(from: String, to: String) { + // Find in label list + var found = false + for (raw in mLabelList) { + if (from == raw.label) { + found = true + raw.label = to + // Update in DB + EhDB.updateDownloadLabel(raw) + break + } + } + if (!found) { + return + } + val list = mMap.remove(from) ?: return + + // Update info label + for (info in list) { + info.label = to + // Update in DB + EhDB.putDownloadInfo(info) + } + // Put list back with new label + mMap[to] = list + + // Notify listener + for (l in mDownloadInfoListeners) { + l!!.onRenameLabel(from, to) + } + } + + fun deleteLabel(label: String) { + // Find in label list and remove + var found = false + val iterator = mLabelList.iterator() + while (iterator.hasNext()) { + val raw = iterator.next() + if (label == raw.label) { + found = true + iterator.remove() + EhDB.removeDownloadLabel(raw) + break + } + } + if (!found) { + return + } + val list = mMap.remove(label) ?: return + + // Update info label + for (info in list) { + info.label = null + // Update in DB + EhDB.putDownloadInfo(info) + mDefaultInfoList.add(info) + } + + // Sort + mDefaultInfoList.sortByDateDescending() + + // Notify listener + for (l in mDownloadInfoListeners) { + l!!.onChange() + } + } + + val isIdle: Boolean + get() = mCurrentTask == null && mWaitList.isEmpty() + + override fun onGetPages(pages: Int) { + var task = mNotifyTaskPool.pop() + if (task == null) { + task = NotifyTask() + } + task.setOnGetPagesData(pages) + SimpleHandler.getInstance().post(task) + } + + override fun onGet509(index: Int) { + var task = mNotifyTaskPool.pop() + if (task == null) { + task = NotifyTask() + } + task.setOnGet509Data(index) + SimpleHandler.getInstance().post(task) + } + + override fun onPageDownload( + index: Int, + contentLength: Long, + receivedSize: Long, + bytesRead: Int + ) { + var task = mNotifyTaskPool.pop() + if (task == null) { + task = NotifyTask() + } + task.setOnPageDownloadData(index, contentLength, receivedSize, bytesRead) + SimpleHandler.getInstance().post(task) + } + + override fun onPageSuccess(index: Int, finished: Int, downloaded: Int, total: Int) { + var task = mNotifyTaskPool.pop() + if (task == null) { + task = NotifyTask() + } + task.setOnPageSuccessData(index, finished, downloaded, total) + SimpleHandler.getInstance().post(task) + } + + override fun onPageFailure( + index: Int, + error: String, + finished: Int, + downloaded: Int, + total: Int + ) { + var task = mNotifyTaskPool.pop() + if (task == null) { + task = NotifyTask() + } + task.setOnPageFailureDate(index, error, finished, downloaded, total) + SimpleHandler.getInstance().post(task) + } + + override fun onFinish(finished: Int, downloaded: Int, total: Int) { + var task = mNotifyTaskPool.pop() + if (task == null) { + task = NotifyTask() + } + task.setOnFinishDate(finished, downloaded, total) + SimpleHandler.getInstance().post(task) + } + + override fun onGetImageSuccess(index: Int, image: Image) { + // Ignore + } + + override fun onGetImageFailure(index: Int, error: String) { + // Ignore + } + + interface DownloadInfoListener { + /** + * Add the special info to the special position + */ + fun onAdd(info: DownloadInfo, list: List, position: Int) + + /** + * The special info is changed + */ + fun onUpdate(info: DownloadInfo, list: List) + + /** + * Maybe all data is changed, but size is the same + */ + fun onUpdateAll() + + /** + * Maybe all data is changed, maybe list is changed + */ + fun onReload() + + /** + * The list is gone, use default list please + */ + fun onChange() + + /** + * Rename label + */ + fun onRenameLabel(from: String, to: String) + + /** + * Remove the special info from the special position + */ + fun onRemove(info: DownloadInfo, list: List, position: Int) + fun onUpdateLabels() + } + + interface DownloadListener { + /** + * Get 509 error + */ + fun onGet509() + + /** + * Start download + */ + fun onStart(info: DownloadInfo) + + /** + * Update download speed + */ + fun onDownload(info: DownloadInfo) + + /** + * Update page downloaded + */ + fun onGetPage(info: DownloadInfo) + + /** + * Download done + */ + fun onFinish(info: DownloadInfo) + + /** + * Download done + */ + fun onCancel(info: DownloadInfo) + } + + private inner class NotifyTask : Runnable { + private var mType = 0 + private var mPages = 0 + private var mIndex = 0 + private var mContentLength: Long = 0 + private var mReceivedSize: Long = 0 + private var mBytesRead = 0 + private var mError: String? = null + private var mFinished = 0 + private var mDownloaded = 0 + private var mTotal = 0 + fun setOnGetPagesData(pages: Int) { + mType = Companion.TYPE_ON_GET_PAGES + mPages = pages + } + + fun setOnGet509Data(index: Int) { + mType = Companion.TYPE_ON_GET_509 + mIndex = index + } + + fun setOnPageDownloadData( + index: Int, + contentLength: Long, + receivedSize: Long, + bytesRead: Int + ) { + mType = Companion.TYPE_ON_PAGE_DOWNLOAD + mIndex = index + mContentLength = contentLength + mReceivedSize = receivedSize + mBytesRead = bytesRead + } + + fun setOnPageSuccessData(index: Int, finished: Int, downloaded: Int, total: Int) { + mType = Companion.TYPE_ON_PAGE_SUCCESS + mIndex = index + mFinished = finished + mDownloaded = downloaded + mTotal = total + } + + fun setOnPageFailureDate( + index: Int, + error: String?, + finished: Int, + downloaded: Int, + total: Int + ) { + mType = Companion.TYPE_ON_PAGE_FAILURE + mIndex = index + mError = error + mFinished = finished + mDownloaded = downloaded + mTotal = total + } + + fun setOnFinishDate(finished: Int, downloaded: Int, total: Int) { + mType = Companion.TYPE_ON_FINISH + mFinished = finished + mDownloaded = downloaded + mTotal = total + } + + override fun run() { + when (mType) { + Companion.TYPE_ON_GET_PAGES -> { + val info = mCurrentTask + if (info == null) { + Log.e(TAG, "Current task is null, but it should not be") + } else { + info.total = mPages + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + } + } + + Companion.TYPE_ON_GET_509 -> { + if (mDownloadListener != null) { + mDownloadListener!!.onGet509() + } + } + + Companion.TYPE_ON_PAGE_DOWNLOAD -> mSpeedReminder.onDownload( + mIndex, + mContentLength, + mReceivedSize, + mBytesRead + ) + + Companion.TYPE_ON_PAGE_SUCCESS -> { + mSpeedReminder.onDone(mIndex) + val info = mCurrentTask + if (info == null) { + Log.e(TAG, "Current task is null, but it should not be") + } else { + info.finished = mFinished + info.downloaded = mDownloaded + info.total = mTotal + if (mDownloadListener != null) { + mDownloadListener!!.onGetPage(info) + } + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + } + } + + Companion.TYPE_ON_PAGE_FAILURE -> { + mSpeedReminder.onDone(mIndex) + val info = mCurrentTask + if (info == null) { + Log.e(TAG, "Current task is null, but it should not be") + } else { + info.finished = mFinished + info.downloaded = mDownloaded + info.total = mTotal + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + } + } + + Companion.TYPE_ON_FINISH -> { + mSpeedReminder.onFinish() + // Download done + val info = mCurrentTask + mCurrentTask = null + val spider = mCurrentSpider + mCurrentSpider = null + // Release spider + if (spider != null) { + spider.removeOnSpiderListener(this@DownloadManager) + 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") + } else { + // Stop speed count + mSpeedReminder.stop() + // Update state + info.finished = mFinished + info.downloaded = mDownloaded + info.total = mTotal + info.legacy = mTotal - mFinished + if (info.legacy == 0) { + info.state = DownloadInfo.STATE_FINISH + } else { + info.state = DownloadInfo.STATE_FAILED + } + // Update in DB + EhDB.putDownloadInfo(info) + // Notify + if (mDownloadListener != null) { + mDownloadListener!!.onFinish(info) + } + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + // Start next download + ensureDownload() + } + } + } + mNotifyTaskPool.push(this) + } + } + + internal inner class SpeedReminder : Runnable { + private val mContentLengthMap = SparseIJArray() + private val mReceivedSizeMap = SparseIJArray() + private var mStop = true + private var mBytesRead: Long = 0 + private var oldSpeed: Long = -1 + fun start() { + if (mStop) { + mStop = false + SimpleHandler.getInstance().post(this) + } + } + + fun stop() { + if (!mStop) { + mStop = true + mBytesRead = 0 + oldSpeed = -1 + mContentLengthMap.clear() + mReceivedSizeMap.clear() + SimpleHandler.getInstance().removeCallbacks(this) + } + } + + fun onDownload(index: Int, contentLength: Long, receivedSize: Long, bytesRead: Int) { + mContentLengthMap.put(index, contentLength) + mReceivedSizeMap.put(index, receivedSize) + mBytesRead += bytesRead.toLong() + } + + fun onDone(index: Int) { + mContentLengthMap.delete(index) + mReceivedSizeMap.delete(index) + } + + fun onFinish() { + mContentLengthMap.clear() + mReceivedSizeMap.clear() + } + + override fun run() { + val info = mCurrentTask + if (info != null) { + var newSpeed = mBytesRead / 2 + if (oldSpeed != -1L) { + newSpeed = + MathUtils.lerp(oldSpeed.toFloat(), newSpeed.toFloat(), 0.75f).toLong() + } + oldSpeed = newSpeed + info.speed = newSpeed + + // Calculate remaining + if (info.total <= 0) { + info.remaining = -1 + } else if (newSpeed == 0L) { + info.remaining = 300L * 24L * 60L * 60L * 1000L // 300 days + } else { + var downloadingCount = 0 + var downloadingContentLengthSum: Long = 0 + var totalSize: Long = 0 + for (i in 0 until maxOf(mContentLengthMap.size(), mReceivedSizeMap.size())) { + val contentLength = mContentLengthMap.valueAt(i) + val receivedSize = mReceivedSizeMap.valueAt(i) + downloadingCount++ + downloadingContentLengthSum += contentLength + totalSize += contentLength - receivedSize + } + if (downloadingCount != 0) { + totalSize += downloadingContentLengthSum * (info.total - info.downloaded - downloadingCount) / downloadingCount + info.remaining = totalSize / newSpeed * 1000 + } + } + if (mDownloadListener != null) { + mDownloadListener!!.onDownload(info) + } + val list: List? = getInfoListForLabel(info.label) + if (list != null) { + for (l in mDownloadInfoListeners) { + l!!.onUpdate(info, list) + } + } + } + mBytesRead = 0 + if (!mStop) { + SimpleHandler.getInstance().postDelayed(this, 2000) + } + } + } + + companion object { + private val TAG = DownloadManager::class.java.simpleName + private const val TYPE_ON_GET_PAGES = 0 + private const val TYPE_ON_GET_509 = 1 + private const val TYPE_ON_PAGE_DOWNLOAD = 2 + private const val TYPE_ON_PAGE_SUCCESS = 3 + private const val TYPE_ON_PAGE_FAILURE = 4 + private const val TYPE_ON_FINISH = 5 + + fun MutableList.sortByDateDescending() { + sortByDescending { it.time } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index f3e2c71ec..fb323e2fc 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -91,7 +91,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe ---------------*/ private var mDownloadManager: DownloadManager? = null private var mLabel: String? = null - private var mList: MutableList? = null + private var mList: List? = null /*--------------- View life cycle @@ -115,8 +115,8 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe // Add "All" and "Default" label names mLabels.add(getString(R.string.download_all)) mLabels.add(getString(R.string.default_download_label_name)) - for ((_, label) in listLabel) { - mLabels.add(label!!) + listLabel.forEach { + mLabels.add(it.label!!) } } @@ -179,11 +179,12 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe ?: Log.e(TAG, "Can't removeDownloadInfoListener") } + @SuppressLint("NotifyDataSetChanged") private fun updateForLabel() { if (null == mDownloadManager) { return } - var list: MutableList? + var list: List? if (mLabel == null) { list = mDownloadManager!!.allDownloadInfoList } else if (mLabel == getString(R.string.default_download_label_name)) { @@ -195,15 +196,10 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe list = mDownloadManager!!.allDownloadInfoList } } - if (mType != -1) { - mList = ArrayList() - for (info in list) { - if (info.state == mType) { - mList!!.add(info) - } - } + mList = if (mType != -1) { + list.filter { it.state == mType } } else { - mList = list + list } if (mAdapter != null) { mAdapter!!.notifyDataSetChanged() @@ -391,7 +387,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe R.id.action_start_all_reversed -> { val list = mList ?: return true val gidList = LongList() - for (i in list.size - 1 downTo -1 + 1) { + for (i in list.size - 1 downTo 0) { val info = list[i] if (info.state != DownloadInfo.STATE_FINISH) { gidList.add(info.gid) @@ -410,7 +406,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe fun updateView() { if (mViewTransition != null) { - if (mList == null || mList!!.size == 0) { + if (mList.isNullOrEmpty()) { mViewTransition!!.showView(1) } else { mViewTransition!!.showView(0) @@ -552,9 +548,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe downloadInfoList = LinkedList() } val stateArray = recyclerView.checkedItemPositions - var i = 0 - val n = stateArray.size() - while (i < n) { + for (i in 0 until stateArray.size()) { if (stateArray.valueAt(i)) { val info = list[stateArray.keyAt(i)] if (collectDownloadInfo) { @@ -564,7 +558,6 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe gidList!!.add(info.gid) } } - i++ } when (position) { 1 -> { @@ -580,7 +573,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe 2 -> { // Stop if (null != mDownloadManager) { - mDownloadManager!!.stopRangeDownload(gidList) + mDownloadManager!!.stopRangeDownload(gidList!!) } // Cancel check mode recyclerView.outOfCustomChoiceMode() @@ -595,7 +588,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe Settings.getRemoveImageFiles() ) val helper = DeleteRangeDialogHelper( - downloadInfoList, gidList, builder + downloadInfoList!!, gidList, builder ) builder.setTitle(R.string.download_remove_dialog_title) .setPositiveButton(android.R.string.ok, helper) @@ -607,11 +600,8 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe val labelRawList = downloadManager.labelList val labelList: MutableList = ArrayList(labelRawList.size + 1) labelList.add(getString(R.string.default_download_label_name)) - var i = 0 - val n = labelRawList.size - while (i < n) { - labelRawList[i].label?.let { labelList.add(it) } - i++ + labelRawList.forEach { + labelList.add(it.label!!) } val labels = labelList.toTypedArray() val helper = MoveDialogHelper(labels, downloadInfoList!!) @@ -644,12 +634,14 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe } } + @SuppressLint("NotifyDataSetChanged") override fun onUpdateAll() { if (mAdapter != null) { mAdapter!!.notifyDataSetChanged() } } + @SuppressLint("NotifyDataSetChanged") override fun onReload() { if (mAdapter != null) { mAdapter!!.notifyDataSetChanged() @@ -867,8 +859,8 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe } private inner class DeleteRangeDialogHelper( - private val mDownloadInfoList: List?, - private val mGidList: LongList?, private val mBuilder: CheckBoxDialogBuilder + private val mDownloadInfoList: List, + private val mGidList: LongList, private val mBuilder: CheckBoxDialogBuilder ) : DialogInterface.OnClickListener { override fun onClick(dialog: DialogInterface, which: Int) { if (which != DialogInterface.BUTTON_POSITIVE) { @@ -889,7 +881,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe val checked = mBuilder.isChecked Settings.putRemoveImageFiles(checked) if (checked) { - val files = arrayOfNulls(mDownloadInfoList!!.size) + val files = arrayOfNulls(mDownloadInfoList.size) for ((i, info) in mDownloadInfoList.withIndex()) { // Put file files[i] = SpiderDen.getGalleryDownloadDir(info.gid) @@ -908,6 +900,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe private val mLabels: Array, private val mDownloadInfoList: List ) : DialogInterface.OnClickListener { + @SuppressLint("NotifyDataSetChanged") override fun onClick(dialog: DialogInterface, which: Int) { // Cancel check mode context ?: return @@ -1095,6 +1088,7 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe button.setOnClickListener(this) } + @SuppressLint("NotifyDataSetChanged") override fun onClick(v: View) { context ?: return val text = mBuilder.text @@ -1225,16 +1219,19 @@ class DownloadsScene : BaseToolbarScene(), DownloadInfoListener, OnClickFabListe if (fromPosition == toPosition) { return false } - val allInfoList = mDownloadManager!!.allDownloadInfoList - val dbFromPosition = allInfoList.indexOf(mList!![fromPosition]) - val dbToPosition = allInfoList.indexOf(mList!![toPosition]) lifecycleScope.launchIO { - EhDB.moveDownloadInfo(dbFromPosition, dbToPosition) - val item = allInfoList.removeAt(dbFromPosition) - allInfoList.add(dbToPosition, item) - if (mLabel != null) { - val item1 = mList!!.removeAt(fromPosition) - mList!!.add(toPosition, item1) + when (mLabel) { + null -> { + mDownloadManager!!.moveDownload(fromPosition, toPosition) + } + + getString(R.string.default_download_label_name) -> { + mDownloadManager!!.moveDownload(null, fromPosition, toPosition) + } + + else -> { + mDownloadManager!!.moveDownload(mLabel, fromPosition, toPosition) + } } withUIContext { mAdapter!!.notifyItemMoved(fromPosition, toPosition)