From 70539c65394789c01a2cee0de358c0530dbfe01e Mon Sep 17 00:00:00 2001 From: AriaLyy <511455842@qq.com> Date: Wed, 1 Nov 2017 21:42:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A9=BA=E6=A0=BC=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E4=B8=8D=E8=83=BD=E4=B8=8B=E8=BD=BD=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20https://github.com/AriaLyy/Aria/issues/131?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Aria/build.gradle | 6 +- .../com/arialyy/aria/core/AriaManager.java | 71 ++++++++++---- .../aria/core/command/normal/StartCmd.java | 1 + .../aria/core/download/DownloadReceiver.java | 14 ++- .../aria/core/download/DownloadTarget.java | 12 ++- .../aria/core/download/DownloadTask.java | 2 +- .../core/download/DownloadTaskEntity.java | 10 +- .../aria/core/download/FtpDownloadTarget.java | 30 ------ .../downloader/HttpFileInfoThread.java | 1 + .../arialyy/aria/core/inf/AbsReceiver.java | 6 +- .../aria/core/manager/DGTEntityFactory.java | 56 +++++++++++ .../aria/core/manager/DTEntityFactory.java | 57 ++++++++++++ .../aria/core/manager/IGTEntityFactory.java | 34 +++++++ .../aria/core/manager/ITEntityFactory.java | 34 +++++++ .../core/{ => manager}/SubTaskManager.java | 0 .../arialyy/aria/core/manager/TEManager.java | 93 +++++++++++++++++++ .../aria/core/{ => manager}/TaskManager.java | 0 .../aria/core/manager/UTEntityFactory.java | 57 ++++++++++++ .../aria/core/scheduler/AbsSchedulers.java | 8 +- .../aria/core/upload/UploadReceiver.java | 20 ++-- .../java/com/arialyy/aria/orm/DbEntity.java | 10 ++ .../java/com/arialyy/aria/orm/DbUtil.java | 11 +++ .../java/com/arialyy/aria/orm/SqlHelper.java | 29 +++++- .../com/arialyy/aria/util/CommonUtil.java | 10 +- .../java/com/arialyy/aria/util/Regular.java | 4 + app/build.gradle | 2 +- .../simple/download/SingleTaskActivity.java | 26 +++--- .../multi_download/MultiDownloadActivity.java | 5 + build.gradle | 2 +- 29 files changed, 517 insertions(+), 94 deletions(-) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/manager/IGTEntityFactory.java create mode 100644 Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java rename Aria/src/main/java/com/arialyy/aria/core/{ => manager}/SubTaskManager.java (100%) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java rename Aria/src/main/java/com/arialyy/aria/core/{ => manager}/TaskManager.java (100%) create mode 100644 Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java diff --git a/Aria/build.gradle b/Aria/build.gradle index ad5e8af1..4d7ee364 100644 --- a/Aria/build.gradle +++ b/Aria/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' android { compileSdkVersion 23 - buildToolsVersion '25.0.3' + buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 9 @@ -23,8 +23,8 @@ dependencies { testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' compile project(':AriaAnnotations') -// compile 'com.arialyy.aria:aria-ftp-plug:1.0.3' + compile 'com.arialyy.aria:aria-ftp-plug:1.0.3' - compile project(':AriaFtpPlug') +// compile project(':AriaFtpPlug') } apply from: 'bintray-release.gradle' diff --git a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java index d26a313a..a1c169e7 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java +++ b/Aria/src/main/java/com/arialyy/aria/core/AriaManager.java @@ -35,6 +35,7 @@ import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupTaskEntity; import com.arialyy.aria.core.download.DownloadReceiver; import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.inf.AbsReceiver; import com.arialyy.aria.core.inf.IReceiver; import com.arialyy.aria.core.upload.UploadEntity; import com.arialyy.aria.core.upload.UploadReceiver; @@ -78,7 +79,7 @@ import org.xml.sax.SAXException; public static final int LOG_DEFAULT = LOG_LEVEL_DEBUG; @SuppressLint("StaticFieldLeak") private static volatile AriaManager INSTANCE = null; - private Map mReceivers = new ConcurrentHashMap<>(); + private Map mReceivers = new ConcurrentHashMap<>(); public static Context APP; private List mCommands = new ArrayList<>(); private Configuration.DownloadConfig mDConfig; @@ -100,7 +101,7 @@ import org.xml.sax.SAXException; return INSTANCE; } - public Map getReceiver() { + public Map getReceiver() { return mReceivers; } @@ -264,25 +265,54 @@ import org.xml.sax.SAXException; } if (receiver == null) { + AbsReceiver absReceiver; if (isDownload) { - DownloadReceiver dReceiver = new DownloadReceiver(); - dReceiver.targetName = obj.getClass().getName(); - dReceiver.obj = obj; - dReceiver.needRmReceiver = needRmReceiver; - mReceivers.put(key, dReceiver); - receiver = dReceiver; + absReceiver = new DownloadReceiver(); } else { - UploadReceiver uReceiver = new UploadReceiver(); - uReceiver.targetName = obj.getClass().getName(); - uReceiver.obj = obj; - uReceiver.needRmReceiver = needRmReceiver; - mReceivers.put(key, uReceiver); - receiver = uReceiver; + absReceiver = new UploadReceiver(); } + receiver = checkTarget(key, absReceiver, obj, needRmReceiver); } return receiver; } + /** + * 不允许在"onDestroy"、"finish"、"onStop"这三个方法中添加注册器 + */ + private AbsReceiver checkTarget(String key, AbsReceiver receiver, Object obj, boolean needRmReceiver) { + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + int i = 0; + for (StackTraceElement e : stack) { + String name = e.getClassName(); + if (!name.equals(AriaManager.class.getName())) { + i++; + } else { + break; + } + } + i += 4; + String methodName = stack[i].getMethodName(); + boolean isDestroyed = + methodName.equals("onDestroy") || methodName.equals("finish") || methodName.equals( + "onStop"); + + if (isDestroyed) { + ALog.w(TAG, + "请不要在Activity或Fragment的onDestroy、finish、onStop等方法中调用Aria,Aria的unRegister会在Activity页面销毁时自动执行"); + } + + if (obj instanceof Activity && isDestroyed) { + return receiver; + } else if (obj instanceof Fragment && isDestroyed) { + return receiver; + } + receiver.targetName = obj.getClass().getName(); + receiver.obj = obj; + receiver.needRmListener = needRmReceiver; + mReceivers.put(key, receiver); + return receiver; + } + /** * 根据功能类型和控件类型获取对应的key */ @@ -394,10 +424,11 @@ import org.xml.sax.SAXException; * 移除指定对象的receiver */ public void removeReceiver(Object obj) { + if (obj == null) return; String clsName = obj.getClass().getName(); - for (Iterator> iter = mReceivers.entrySet().iterator(); + for (Iterator> iter = mReceivers.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = iter.next(); + Map.Entry entry = iter.next(); String key = entry.getKey(); if (key.contains(clsName)) { iter.remove(); @@ -410,14 +441,14 @@ import org.xml.sax.SAXException; */ void destroySchedulerListener(Object obj) { String clsName = obj.getClass().getName(); - for (Iterator> iter = mReceivers.entrySet().iterator(); + for (Iterator> iter = mReceivers.entrySet().iterator(); iter.hasNext(); ) { - Map.Entry entry = iter.next(); + Map.Entry entry = iter.next(); String key = entry.getKey(); if (key.contains(clsName)) { - IReceiver receiver = mReceivers.get(key); + AbsReceiver receiver = mReceivers.get(key); if (receiver != null) { - receiver.unRegister(); + receiver.unRegisterListener(); receiver.destroy(); } iter.remove(); diff --git a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java index 0a6eb7e7..8a47f681 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java +++ b/Aria/src/main/java/com/arialyy/aria/core/command/normal/StartCmd.java @@ -139,6 +139,7 @@ class StartCmd extends AbsNormalCmd { private void handleTask(List waitList) { for (AbsTaskEntity te : waitList) { + if (te.getEntity() == null) continue; if (te instanceof DownloadTaskEntity) { mQueue = DownloadTaskQueue.getInstance(); } else if (te instanceof UploadTaskEntity) { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java index 24ecb16d..20c4792e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadReceiver.java @@ -67,6 +67,7 @@ public class DownloadReceiver extends AbsReceiver { * @param refreshInfo 是否刷新下载信息 */ public DownloadTarget load(DownloadEntity entity, boolean refreshInfo) { + CheckUtil.checkDownloadUrl(entity.getUrl()); return new DownloadTarget(entity, targetName, refreshInfo); } @@ -152,9 +153,17 @@ public class DownloadReceiver extends AbsReceiver { } /** - * 取消注册 + * 取消注册,如果是Activity或fragment,Aria会界面销毁时自动调用该方法。 + * 如果是Dialog或popupwindow,需要你在撤销界面时调用该方法 */ @Override public void unRegister() { + if (needRmListener) { + unRegisterListener(); + } + AriaManager.getInstance(AriaManager.APP).removeReceiver(obj); + } + + @Override public void unRegisterListener() { String className = obj.getClass().getName(); Set dCounter = ProxyHelper.getInstance().downloadCounter; Set dgCounter = ProxyHelper.getInstance().downloadGroupCounter; @@ -166,9 +175,6 @@ public class DownloadReceiver extends AbsReceiver { && dgsCounter.contains(className))) { DownloadGroupSchedulers.getInstance().unRegister(obj); } - if (needRmReceiver) { - AriaManager.getInstance(AriaManager.APP).removeReceiver(obj); - } } @Override public void destroy() { diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java index c10d26cd..b08c803a 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTarget.java @@ -50,8 +50,7 @@ public class DownloadTarget DownloadTarget(String url, String targetName, boolean refreshInfo) { this.url = url; mTargetName = targetName; - DownloadEntity entity = getEntity(url); - initTask(entity); + initTask(getEntity(url)); mTaskEntity.refreshInfo = refreshInfo; } @@ -83,7 +82,6 @@ public class DownloadTarget entity = new DownloadEntity(); entity.setUrl(downloadUrl); entity.setGroupChild(false); - entity.save(); } File file = new File(entity.getDownloadPath()); if (!file.exists()) { @@ -107,6 +105,8 @@ public class DownloadTarget /** * 下载任务是否存在 + * + * @return {@code true}任务存在 */ @Override public boolean taskExists() { return DownloadTaskQueue.getInstance().getTask(mEntity.getUrl()) != null; @@ -125,16 +125,18 @@ public class DownloadTarget } File file = new File(downloadPath); if (file.isDirectory()) { - throw new IllegalArgumentException("文件不能为文件夹"); + throw new IllegalArgumentException("保存路径不能为文件夹,路径需要是完整的文件路径,如:/mnt/sdcard/game.zip"); } if (!downloadPath.equals(mEntity.getDownloadPath())) { + if (!mTaskEntity.refreshInfo && DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", downloadPath)) { + throw new IllegalArgumentException("保存路径【" + downloadPath + "】已经被其它任务占用,请设置其它保存路径"); + } File oldFile = new File(mEntity.getDownloadPath()); File newFile = new File(downloadPath); if (TextUtils.isEmpty(mEntity.getDownloadPath()) || oldFile.renameTo(newFile)) { mEntity.setDownloadPath(downloadPath); mEntity.setFileName(newFile.getName()); mTaskEntity.key = downloadPath; - mEntity.update(); mTaskEntity.update(); CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java index aa2b7394..b1f838a0 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTask.java @@ -166,7 +166,7 @@ public class DownloadTask extends AbsNormalTask { try { outHandler = new Handler(schedulers); } catch (Exception e) { - e.printStackTrace(); + ALog.w(TAG, ALog.getExceptionString(e)); outHandler = new Handler(Looper.getMainLooper(), schedulers); } return this; diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java index 5af62a2c..f7d84ad5 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/DownloadTaskEntity.java @@ -16,8 +16,6 @@ package com.arialyy.aria.core.download; import com.arialyy.aria.core.inf.AbsNormalTaskEntity; -import com.arialyy.aria.core.inf.AbsTaskEntity; -import com.arialyy.aria.orm.Ignore; import com.arialyy.aria.orm.NoNull; import com.arialyy.aria.orm.OneToOne; @@ -52,10 +50,12 @@ public class DownloadTaskEntity extends AbsNormalTaskEntity { } public void save(DownloadEntity entity) { - url = entity.getUrl(); - key = entity.getDownloadPath(); this.entity = entity; - entity.save(); + if (entity != null) { + url = entity.getUrl(); + key = entity.getDownloadPath(); + entity.save(); + } save(); } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java index fa7d8aa2..e886a170 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/FtpDownloadTarget.java @@ -42,36 +42,6 @@ public class FtpDownloadTarget extends DownloadTarget { mTaskEntity.requestType = AbsTaskEntity.FTP; } - /** - * 设置文件保存文件夹路径 - * 关于文件名: - * 1、如果保存路径是该文件的保存路径,如:/mnt/sdcard/file.zip,则使用路径中的文件名file.zip - * 2、如果保存路径是文件夹路径,如:/mnt/sdcard/,则使用FTP服务器该文件的文件名 - * - * @param downloadPath 路径必须为文件路径,不能为文件夹路径 - */ - @Override public FtpDownloadTarget setDownloadPath(@NonNull String downloadPath) { - if (TextUtils.isEmpty(downloadPath)) { - throw new IllegalArgumentException("文件保持路径不能为null"); - } - File file = new File(downloadPath); - if (file.isDirectory()) { - downloadPath += mEntity.getFileName(); - } - if (!downloadPath.equals(mEntity.getDownloadPath())) { - File oldFile = new File(mEntity.getDownloadPath()); - File newFile = new File(downloadPath); - if (TextUtils.isEmpty(mEntity.getDownloadPath()) || oldFile.renameTo(newFile)) { - mEntity.setDownloadPath(downloadPath); - mEntity.setFileName(newFile.getName()); - mTaskEntity.key = downloadPath; - mTaskEntity.update(); - CommonUtil.renameDownloadConfig(oldFile.getName(), newFile.getName()); - } - } - return this; - } - /** * 设置字符编码 */ diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java index 105b0ebf..652696d1 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/downloader/HttpFileInfoThread.java @@ -16,6 +16,7 @@ package com.arialyy.aria.core.download.downloader; import android.text.TextUtils; +import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.common.OnFileInfoCallback; import com.arialyy.aria.core.download.DownloadEntity; diff --git a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java index 360222ed..b1c6fa4f 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/inf/AbsReceiver.java @@ -26,5 +26,9 @@ public abstract class AbsReceiver implements IReceiver /** * 当dialog、dialogFragment、popupwindow已经被设置了关闭监听时,需要手动移除receiver */ - public boolean needRmReceiver = false; + public boolean needRmListener = false; + + public void unRegisterListener(){ + + } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java new file mode 100644 index 00000000..870b549c --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DGTEntityFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * 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.arialyy.aria.core.manager; + +import android.text.TextUtils; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.orm.DbEntity; + +/** + * Created by Aria.Lao on 2017/11/1. + * 任务实体工厂 + */ +class DGTaskEntityFactory + implements ITaskEntityFactory { + private static final String TAG = "DTaskEntityFactory"; + private static volatile DGTaskEntityFactory INSTANCE = null; + + private DGTaskEntityFactory() { + } + + public static DGTaskEntityFactory getInstance() { + if (INSTANCE == null) { + synchronized (DGTaskEntityFactory.class) { + INSTANCE = new DGTaskEntityFactory(); + } + } + return INSTANCE; + } + + @Override public DownloadGroupTaskEntity create(DownloadGroupEntity entity) { + DownloadGroupTaskEntity dgTaskEntity = + DbEntity.findFirst(DownloadGroupTaskEntity.class, "key=?", entity.getGroupName()); + if (dgTaskEntity == null) { + dgTaskEntity = new DownloadGroupTaskEntity(); + dgTaskEntity.save(entity); + } + if (dgTaskEntity.entity == null || TextUtils.isEmpty(dgTaskEntity.entity.getKey())) { + dgTaskEntity.save(entity); + } + return dgTaskEntity; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java new file mode 100644 index 00000000..01fd48a4 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/DTEntityFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * 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.arialyy.aria.core.manager; + +import android.text.TextUtils; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.orm.DbEntity; + +/** + * Created by Aria.Lao on 2017/11/1. + * 任务实体工厂 + */ +class DTaskEntityFactory implements ITaskEntityFactory { + private static final String TAG = "DTaskEntityFactory"; + private static volatile DTaskEntityFactory INSTANCE = null; + + private DTaskEntityFactory() { + } + + public static DTaskEntityFactory getInstance() { + if (INSTANCE == null) { + synchronized (DTaskEntityFactory.class) { + INSTANCE = new DTaskEntityFactory(); + } + } + return INSTANCE; + } + + @Override public DownloadTaskEntity create(DownloadEntity entity) { + DownloadTaskEntity taskEntity = + DbEntity.findFirst(DownloadTaskEntity.class, "key=? and isGroupTask='false' and url=?", + entity.getDownloadPath(), entity.getUrl()); + if (taskEntity == null) { + taskEntity = new DownloadTaskEntity(); + taskEntity.save(entity); + } else if (taskEntity.entity == null || TextUtils.isEmpty(taskEntity.entity.getUrl())) { + taskEntity.save(entity); + } else if (!taskEntity.entity.getUrl().equals(entity.getUrl())) { //处理地址切换而保存路径不变 + taskEntity.save(entity); + } + return taskEntity; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/IGTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/IGTEntityFactory.java new file mode 100644 index 00000000..e0532651 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/IGTEntityFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * 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.arialyy.aria.core.manager; + +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsTaskEntity; + +/** + * Created by Aria.Lao on 2017/11/1. + */ +interface IEntityFactory> { + /** + * 通过信息实体创建任务实体 + */ + TASK_ENTITY create(ENTITY entity); + + /** + * 通过key创建任务,只适应于单任务 + */ + TASK_ENTITY create(String key); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java new file mode 100644 index 00000000..e0532651 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/ITEntityFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * 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.arialyy.aria.core.manager; + +import com.arialyy.aria.core.inf.AbsEntity; +import com.arialyy.aria.core.inf.AbsTaskEntity; + +/** + * Created by Aria.Lao on 2017/11/1. + */ +interface IEntityFactory> { + /** + * 通过信息实体创建任务实体 + */ + TASK_ENTITY create(ENTITY entity); + + /** + * 通过key创建任务,只适应于单任务 + */ + TASK_ENTITY create(String key); +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/SubTaskManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/SubTaskManager.java rename to Aria/src/main/java/com/arialyy/aria/core/manager/SubTaskManager.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java new file mode 100644 index 00000000..69e500c0 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/TEManager.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * 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.arialyy.aria.core.manager; + +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadGroupEntity; +import com.arialyy.aria.core.download.DownloadGroupTaskEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.core.inf.AbsTaskEntity; +import com.arialyy.aria.core.upload.UploadEntity; +import com.arialyy.aria.core.upload.UploadTaskEntity; +import com.arialyy.aria.util.CommonUtil; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Created by Aria.Lao on 2017/11/1. + * 任务实体管理器,负责 + */ +public class TaskEntityManager { + private static final String TAG = "TaskManager"; + private static volatile TaskEntityManager INSTANCE = null; + private Map map = new ConcurrentHashMap<>(); + + public static TaskEntityManager getInstance() { + if (INSTANCE == null) { + synchronized (TaskEntityManager.class) { + INSTANCE = new TaskEntityManager(); + } + } + return INSTANCE; + } + + /** + * 通过下载实体获取下载任务实体,如果查找不到任务实体,则重新创建任务实体 + */ + public DownloadTaskEntity getDTEntity(DownloadEntity entity) { + AbsTaskEntity tEntity = map.get(convertKey(entity.getKey())); + if (tEntity == null || !(tEntity instanceof DownloadTaskEntity)) { + tEntity = DTEntityFactory.getInstance().create(entity); + map.put(convertKey(entity.getKey()), tEntity); + } + return (DownloadTaskEntity) tEntity; + } + + /** + * 通过下载实体获取下载任务组实体,如果查找不到任务组实体,则重新创建任务组实体 + */ + public DownloadGroupTaskEntity getDGTEntity(DownloadGroupEntity entity) { + AbsTaskEntity tEntity = map.get(convertKey(entity.getKey())); + if (tEntity == null || !(tEntity instanceof DownloadGroupTaskEntity)) { + tEntity = DGTEntityFactory.getInstance().create(entity); + map.put(convertKey(entity.getKey()), tEntity); + } + return (DownloadGroupTaskEntity) tEntity; + } + + /** + * 通过下载实体获取下载任务组实体,如果查找不到任务组实体,则重新创建任务组实体 + */ + public UploadTaskEntity getUTEntity(UploadEntity entity) { + AbsTaskEntity tEntity = map.get(convertKey(entity.getKey())); + if (tEntity == null || !(tEntity instanceof UploadTaskEntity)) { + tEntity = UTEntityFactory.getInstance().create(entity); + map.put(convertKey(entity.getKey()), tEntity); + } + return (UploadTaskEntity) tEntity; + } + + /** + * 通过key删除任务实体 + */ + public AbsTaskEntity removeTEntity(String key) { + return map.remove(convertKey(key)); + } + + private String convertKey(String key) { + return CommonUtil.encryptBASE64(key); + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/TaskManager.java b/Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java similarity index 100% rename from Aria/src/main/java/com/arialyy/aria/core/TaskManager.java rename to Aria/src/main/java/com/arialyy/aria/core/manager/TaskManager.java diff --git a/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java b/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java new file mode 100644 index 00000000..01fd48a4 --- /dev/null +++ b/Aria/src/main/java/com/arialyy/aria/core/manager/UTEntityFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) + * + * 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.arialyy.aria.core.manager; + +import android.text.TextUtils; +import com.arialyy.aria.core.download.DownloadEntity; +import com.arialyy.aria.core.download.DownloadTaskEntity; +import com.arialyy.aria.orm.DbEntity; + +/** + * Created by Aria.Lao on 2017/11/1. + * 任务实体工厂 + */ +class DTaskEntityFactory implements ITaskEntityFactory { + private static final String TAG = "DTaskEntityFactory"; + private static volatile DTaskEntityFactory INSTANCE = null; + + private DTaskEntityFactory() { + } + + public static DTaskEntityFactory getInstance() { + if (INSTANCE == null) { + synchronized (DTaskEntityFactory.class) { + INSTANCE = new DTaskEntityFactory(); + } + } + return INSTANCE; + } + + @Override public DownloadTaskEntity create(DownloadEntity entity) { + DownloadTaskEntity taskEntity = + DbEntity.findFirst(DownloadTaskEntity.class, "key=? and isGroupTask='false' and url=?", + entity.getDownloadPath(), entity.getUrl()); + if (taskEntity == null) { + taskEntity = new DownloadTaskEntity(); + taskEntity.save(entity); + } else if (taskEntity.entity == null || TextUtils.isEmpty(taskEntity.entity.getUrl())) { + taskEntity.save(entity); + } else if (!taskEntity.entity.getUrl().equals(entity.getUrl())) { //处理地址切换而保存路径不变 + taskEntity.save(entity); + } + return taskEntity; + } +} diff --git a/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java b/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java index 1c6a47bd..2a08ba9e 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java +++ b/Aria/src/main/java/com/arialyy/aria/core/scheduler/AbsSchedulers.java @@ -17,6 +17,7 @@ package com.arialyy.aria.core.scheduler; import android.os.CountDownTimer; import android.os.Message; +import android.util.Log; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.download.DownloadTask; import com.arialyy.aria.core.inf.AbsEntity; @@ -66,10 +67,15 @@ abstract class AbsSchedulers>> iter = mObservers.entrySet().iterator(); iter.hasNext(); ) { Map.Entry> entry = iter.next(); - if (entry.getKey().equals(obj.getClass().getName())) iter.remove(); + if (entry.getKey().equals(obj.getClass().getName())) { + iter.remove(); + } } } diff --git a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java index ae1b0ce7..069285f9 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java +++ b/Aria/src/main/java/com/arialyy/aria/core/upload/UploadReceiver.java @@ -18,11 +18,10 @@ package com.arialyy.aria.core.upload; import android.support.annotation.NonNull; import com.arialyy.aria.core.AriaManager; import com.arialyy.aria.core.command.ICmd; -import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.command.normal.NormalCmdFactory; +import com.arialyy.aria.core.common.ProxyHelper; import com.arialyy.aria.core.download.DownloadTaskEntity; import com.arialyy.aria.core.inf.AbsReceiver; -import com.arialyy.aria.core.inf.IReceiver; import com.arialyy.aria.core.scheduler.ISchedulerListener; import com.arialyy.aria.core.scheduler.UploadSchedulers; import com.arialyy.aria.orm.DbEntity; @@ -80,7 +79,8 @@ public class UploadReceiver extends AbsReceiver { @Override public void stopAllTask() { AriaManager.getInstance(AriaManager.APP) .setCmd(NormalCmdFactory.getInstance() - .createCmd(targetName, new UploadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL, ICmd.TASK_TYPE_UPLOAD)) + .createCmd(targetName, new UploadTaskEntity(), NormalCmdFactory.TASK_STOP_ALL, + ICmd.TASK_TYPE_UPLOAD)) .exe(); } @@ -119,14 +119,22 @@ public class UploadReceiver extends AbsReceiver { return this; } + /** + * 取消注册,如果是Activity或fragment,Aria会界面销毁时自动调用该方法。 + * 如果是Dialog或popupwindow,需要你在撤销界面时调用该方法 + */ @Override public void unRegister() { + if (needRmListener) { + unRegisterListener(); + } + AriaManager.getInstance(AriaManager.APP).removeReceiver(obj); + } + + @Override public void unRegisterListener() { String className = obj.getClass().getName(); Set dCounter = ProxyHelper.getInstance().uploadCounter; if (dCounter != null && dCounter.contains(className)) { UploadSchedulers.getInstance().unRegister(obj); } - if (needRmReceiver) { - AriaManager.getInstance(AriaManager.APP).removeReceiver(obj); - } } } \ No newline at end of file diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java index 041b3063..95a8f809 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbEntity.java @@ -36,6 +36,16 @@ public class DbEntity { } + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"downloadPath=?" + * @return {@code true}该字段的对应的value已存在 + */ + public static boolean checkDataExist(Class clazz, String... expression) { + return DbUtil.getInstance().checkDataExist(clazz, expression); + } + /** * 清空表数据 */ diff --git a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java index 172ea821..53940df8 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/DbUtil.java @@ -64,6 +64,17 @@ public class DbUtil { return INSTANCE; } + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + synchronized boolean checkDataExist(Class clazz, String... expression) { + checkDb(); + return SqlHelper.checkDataExist(mDb, clazz, expression); + } + /** * 清空表数据 */ diff --git a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java index d6b5e7db..91bf52e2 100644 --- a/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java +++ b/Aria/src/main/java/com/arialyy/aria/orm/SqlHelper.java @@ -162,6 +162,32 @@ final class SqlHelper extends SQLiteOpenHelper { return count; } + /** + * 检查某个字段的值是否存在 + * + * @param expression 字段和值"url=xxx" + * @return {@code true}该字段的对应的value已存在 + */ + static synchronized boolean checkDataExist(SQLiteDatabase db, Class clazz, + String... expression) { + db = checkDb(db); + CheckUtil.checkSqlExpression(expression); + String sql = + "SELECT rowid, * FROM " + CommonUtil.getClassName(clazz) + " WHERE " + expression[0] + " "; + sql = sql.replace("?", "%s"); + Object[] params = new String[expression.length - 1]; + for (int i = 0, len = params.length; i < len; i++) { + params[i] = "'" + expression[i + 1] + "'"; + } + sql = String.format(sql, params); + print(FIND_DATA, sql); + Cursor cursor = db.rawQuery(sql, null); + final boolean isExist = cursor.getCount() > 0; + closeCursor(cursor); + close(db); + return isExist; + } + /** * 条件查寻数据 */ @@ -456,7 +482,8 @@ final class SqlHelper extends SQLiteOpenHelper { continue; } if (SqlUtil.isPrimary(field)) { - sb.append(" PRIMARY KEY"); + //sb.append(" PRIMARY KEY"); + } if (SqlUtil.isForeign(field)) { foreignArray.add(field); diff --git a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java index 5510d3e6..e4497dc1 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/util/CommonUtil.java @@ -134,8 +134,10 @@ public class CommonUtil { */ public static String convertUrl(String url) { if (hasDoubleCharacter(url)) { + //预先处理空格,URLEncoder只会把空格转换为+ + url = url.replaceAll(" ", "%20"); //匹配双字节字符(包括汉字在内) - String regex = "[^\\x00-\\xff]"; + String regex = Regular.REG_DOUBLE_CHAR_AND_SPACE; Pattern p = Pattern.compile(regex); Matcher m = p.matcher(url); Set strs = new HashSet<>(); @@ -146,6 +148,7 @@ public class CommonUtil { for (String str : strs) { url = url.replaceAll(str, URLEncoder.encode(str, "UTF-8")); } + } catch (UnsupportedEncodingException e) { e.printStackTrace(); } @@ -154,7 +157,7 @@ public class CommonUtil { } /** - * 判断是否有双字节字符(包括汉字在内) + * 判断是否有双字节字符(包括汉字在内) 和空格、制表符、回车 * * @param chineseStr 需要进行判断的字符串 * @return {@code true}有双字节字符,{@code false} 无双字节字符 @@ -162,7 +165,8 @@ public class CommonUtil { public static boolean hasDoubleCharacter(String chineseStr) { char[] charArray = chineseStr.toCharArray(); for (char aCharArray : charArray) { - if ((aCharArray >= 0x0391) && (aCharArray <= 0xFFE5)) { + if (((aCharArray >= 0x0391) && (aCharArray <= 0xFFE5)) || (aCharArray == 0x0d) || (aCharArray + == 0x0a) || (aCharArray == 0x20)) { return true; } } diff --git a/Aria/src/main/java/com/arialyy/aria/util/Regular.java b/Aria/src/main/java/com/arialyy/aria/util/Regular.java index 1ee57d83..ba26b6df 100644 --- a/Aria/src/main/java/com/arialyy/aria/util/Regular.java +++ b/Aria/src/main/java/com/arialyy/aria/util/Regular.java @@ -25,4 +25,8 @@ public interface Regular { */ String REG_IP_V4 = "(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])"; + /** + * 匹配双字节字符、空格、制表符、换行符 + */ + String REG_DOUBLE_CHAR_AND_SPACE = "[^\\x00-\\xff]|[\\s\\p{Zs}]"; } diff --git a/app/build.gradle b/app/build.gradle index 3c077956..3776d524 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 23 - buildToolsVersion '25.0.3' + buildToolsVersion '26.0.2' defaultConfig { applicationId "com.arialyy.simple" diff --git a/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java index 1ffbad77..05cb13b2 100644 --- a/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/SingleTaskActivity.java @@ -47,7 +47,7 @@ public class SingleTaskActivity extends BaseActivity { private static final String DOWNLOAD_URL = //"http://kotlinlang.org/docs/kotlin-docs.pdf"; //"https://atom-installer.github.com/v1.13.0/AtomSetup.exe?s=1484074138&ext=.exe"; - "http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk"; + //"http://static.gaoshouyou.com/d/22/94/822260b849944492caadd2983f9bb624.apk"; //"http://sitcac.daxincf.cn/wp-content/uploads/swift_vido/01/element.mp4_1"; //"http://120.25.196.56:8000/filereq?id=15692406294&ipncid=105635&client=android&filename=20170819185541.avi"; //"http://down2.xiaoshuofuwuqi.com/d/file/filetxt/20170608/14/%BA%DA%CE%D7%CA%A6%E1%C8%C6%F0.txt"; @@ -59,6 +59,7 @@ public class SingleTaskActivity extends BaseActivity { //"http://ox.konsung.net:5555/ksdc-web/download/downloadFile/?fileName=ksdc_1.0.2.apk&rRange=0-"; //"http://172.18.104.50:8080/download/_302turn"; //"http://gdown.baidu.com/data/wisegame/0904344dee4a2d92/QQ_718.apk"; + "http://172.21.1.99:8080/download/test+ 中文123.zip"; @Bind(R.id.start) Button mStart; @Bind(R.id.stop) Button mStop; @Bind(R.id.cancel) Button mCancel; @@ -73,8 +74,8 @@ public class SingleTaskActivity extends BaseActivity { * 设置start 和 stop 按钮状态 */ private void setBtState(boolean state) { - //mStart.setEnabled(state); - //mStop.setEnabled(!state); + mStart.setEnabled(state); + mStop.setEnabled(!state); } @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -117,7 +118,7 @@ public class SingleTaskActivity extends BaseActivity { return true; } - @Download.onPre(DOWNLOAD_URL) protected void onPre(DownloadTask task) { + @Download.onPre protected void onPre(DownloadTask task) { setBtState(false); } @@ -125,7 +126,7 @@ public class SingleTaskActivity extends BaseActivity { getBinding().setFileSize(task.getConvertFileSize()); } - @Download.onTaskRunning(DOWNLOAD_URL) protected void running(DownloadTask task) { + @Download.onTaskRunning protected void running(DownloadTask task) { long len = task.getFileSize(); if (len == 0) { @@ -136,18 +137,18 @@ public class SingleTaskActivity extends BaseActivity { getBinding().setSpeed(task.getConvertSpeed()); } - @Download.onTaskResume(DOWNLOAD_URL) void taskResume(DownloadTask task) { + @Download.onTaskResume void taskResume(DownloadTask task) { mStart.setText("暂停"); setBtState(false); } - @Download.onTaskStop(DOWNLOAD_URL) void taskStop(DownloadTask task) { + @Download.onTaskStop void taskStop(DownloadTask task) { mStart.setText("恢复"); setBtState(true); getBinding().setSpeed(""); } - @Download.onTaskCancel(DOWNLOAD_URL) void taskCancel(DownloadTask task) { + @Download.onTaskCancel void taskCancel(DownloadTask task) { getBinding().setProgress(0); Toast.makeText(SingleTaskActivity.this, "取消下载", Toast.LENGTH_SHORT).show(); mStart.setText("开始"); @@ -155,12 +156,12 @@ public class SingleTaskActivity extends BaseActivity { getBinding().setSpeed(""); } - @Download.onTaskFail(DOWNLOAD_URL) void taskFail(DownloadTask task) { + @Download.onTaskFail void taskFail(DownloadTask task) { Toast.makeText(SingleTaskActivity.this, "下载失败", Toast.LENGTH_SHORT).show(); setBtState(true); } - @Download.onTaskComplete(DOWNLOAD_URL) void taskComplete(DownloadTask task) { + @Download.onTaskComplete void taskComplete(DownloadTask task) { getBinding().setProgress(100); Toast.makeText(SingleTaskActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); mStart.setText("重新开始?"); @@ -170,7 +171,7 @@ public class SingleTaskActivity extends BaseActivity { L.d(TAG, "md5Code ==> " + CommonUtil.getFileMD5(new File(task.getDownloadPath()))); } - @Download.onNoSupportBreakPoint(DOWNLOAD_URL) + @Download.onNoSupportBreakPoint public void onNoSupportBreakPoint(DownloadTask task) { T.showShort(SingleTaskActivity.this, "该下载链接不支持断点"); } @@ -220,8 +221,9 @@ public class SingleTaskActivity extends BaseActivity { } private void startD(){ + Aria.download(this).load("aaaa.apk"); Aria.download(SingleTaskActivity.this) - .load(DOWNLOAD_URL, true) + .load(DOWNLOAD_URL) .addHeader("groupName", "value") .setDownloadPath(Environment.getExternalStorageDirectory().getPath() + "/hhhhhhhh.apk") .start(); diff --git a/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java index ed697da2..0793d9a3 100644 --- a/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/download/multi_download/MultiDownloadActivity.java @@ -136,4 +136,9 @@ public class MultiDownloadActivity extends BaseActivity