parent
0d814d6494
commit
f2ef5927ca
@ -1,91 +0,0 @@ |
||||
/* |
||||
* 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 androidx.lifecycle.DefaultLifecycleObserver |
||||
import androidx.lifecycle.Lifecycle |
||||
import androidx.lifecycle.LifecycleOwner |
||||
import com.arialyy.annotations.TaskEnum |
||||
import com.arialyy.aria.core.common.ProxyHelper |
||||
import com.arialyy.aria.core.download.DownloadGroupTaskListener |
||||
import com.arialyy.aria.core.download.DownloadTaskListener |
||||
import com.arialyy.aria.core.scheduler.M3U8PeerTaskListener |
||||
import com.arialyy.aria.core.scheduler.SubTaskListener |
||||
import com.arialyy.aria.core.scheduler.TaskInternalListenerInterface |
||||
import com.arialyy.aria.core.scheduler.TaskSchedulers |
||||
import com.arialyy.aria.util.ALog |
||||
import timber.log.Timber |
||||
import java.util.Timer |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 10:55 AM 2023/1/20 |
||||
**/ |
||||
object LifecycleManager { |
||||
|
||||
private fun register(obj: Any) { |
||||
if (obj is TaskInternalListenerInterface) { |
||||
ProxyHelper.getInstance().checkProxyType(obj.javaClass) |
||||
if (obj is DownloadTaskListener) { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD) |
||||
} |
||||
if (obj is DownloadGroupTaskListener) { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD_GROUP) |
||||
} |
||||
if (obj is M3U8PeerTaskListener) { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.M3U8_PEER) |
||||
} |
||||
if (obj is SubTaskListener<*, *>) { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD_GROUP_SUB) |
||||
} |
||||
return |
||||
} |
||||
val set: Set<Int> = ProxyHelper.getInstance().checkProxyType(obj.javaClass) |
||||
if (set.isNotEmpty()) { |
||||
for (type in set) { |
||||
when (type) { |
||||
ProxyHelper.PROXY_TYPE_DOWNLOAD -> { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD) |
||||
} |
||||
ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP -> { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD_GROUP) |
||||
} |
||||
ProxyHelper.PROXY_TYPE_M3U8_PEER -> { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.M3U8_PEER) |
||||
} |
||||
ProxyHelper.PROXY_TYPE_DOWNLOAD_GROUP_SUB -> { |
||||
TaskSchedulers.getInstance().register(obj, TaskEnum.DOWNLOAD_GROUP_SUB) |
||||
} |
||||
} |
||||
} |
||||
return |
||||
} |
||||
Timber.e("没有Aria的注解方法,详情见:https://aria.laoyuyu.me/aria_doc/other/annotaion_invalid.html") |
||||
} |
||||
|
||||
fun addObserver(lifecycle: Lifecycle) { |
||||
lifecycle.addObserver(DuaObserver(lifecycle)) |
||||
} |
||||
|
||||
private class DuaObserver(val obj: Lifecycle) : DefaultLifecycleObserver { |
||||
override fun onCreate(owner: LifecycleOwner) { |
||||
super.onCreate(owner) |
||||
register(obj) |
||||
} |
||||
} |
||||
} |
||||
|
@ -1,131 +0,0 @@ |
||||
/* |
||||
* 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.queue; |
||||
|
||||
import com.arialyy.aria.core.AriaConfig; |
||||
import com.arialyy.aria.core.download.DTaskWrapper; |
||||
import com.arialyy.aria.core.download.DownloadEntity; |
||||
import com.arialyy.aria.core.event.DMaxNumEvent; |
||||
import com.arialyy.aria.core.event.Event; |
||||
import com.arialyy.aria.core.event.EventMsgUtil; |
||||
import com.arialyy.aria.core.inf.TaskSchedulerType; |
||||
import com.arialyy.aria.core.scheduler.TaskSchedulers; |
||||
import com.arialyy.aria.core.task.DownloadTask; |
||||
import com.arialyy.aria.util.ALog; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
/** |
||||
* Created by lyy on 2016/8/17. |
||||
* 下载任务队列 |
||||
*/ |
||||
public class DTaskQueue extends AbsTaskQueue<DownloadTask, DTaskWrapper> { |
||||
private static final String TAG = "DownloadTaskQueue"; |
||||
private static volatile DTaskQueue INSTANCE = null; |
||||
|
||||
public static DTaskQueue getInstance() { |
||||
if (INSTANCE == null) { |
||||
synchronized (DTaskQueue.class) { |
||||
INSTANCE = new DTaskQueue(); |
||||
EventMsgUtil.getDefault().register(INSTANCE); |
||||
} |
||||
} |
||||
return INSTANCE; |
||||
} |
||||
|
||||
private DTaskQueue() { |
||||
} |
||||
|
||||
@Override int getQueueType() { |
||||
return TYPE_D_QUEUE; |
||||
} |
||||
|
||||
@Event |
||||
public void maxTaskNum(DMaxNumEvent event) { |
||||
setMaxTaskNum(event.maxNum); |
||||
} |
||||
|
||||
@Override public int getOldMaxNum() { |
||||
return AriaConfig.getInstance().getDConfig().oldMaxTaskNum; |
||||
} |
||||
|
||||
/** |
||||
* 设置任务为最高优先级任务 |
||||
*/ |
||||
public void setTaskHighestPriority(DownloadTask task) { |
||||
task.setHighestPriority(true); |
||||
//Map<String, DownloadTask> exeTasks = mExecutePool.getAllTask();
|
||||
List<DownloadTask> exeTasks = mExecutePool.getAllTask(); |
||||
if (exeTasks != null && !exeTasks.isEmpty()) { |
||||
for (DownloadTask temp : exeTasks) { |
||||
if (temp != null && temp.isRunning() && temp.isHighestPriorityTask() && !temp.getKey() |
||||
.equals(task.getKey())) { |
||||
ALog.e(TAG, "设置最高优先级任务失败,失败原因【任务中已经有最高优先级任务,请等待上一个最高优先级任务完成,或手动暂停该任务】"); |
||||
task.setHighestPriority(false); |
||||
return; |
||||
} |
||||
} |
||||
int maxSize = AriaConfig.getInstance().getDConfig().getMaxTaskNum(); |
||||
int currentSize = mExecutePool.size(); |
||||
if (currentSize == 0 || currentSize < maxSize) { |
||||
startTask(task); |
||||
} else { |
||||
Set<DownloadTask> tempTasks = new LinkedHashSet<>(); |
||||
for (int i = 0; i < maxSize; i++) { |
||||
DownloadTask oldTsk = mExecutePool.pollTask(); |
||||
if (oldTsk != null && oldTsk.isRunning()) { |
||||
if (i == maxSize - 1) { |
||||
oldTsk.stop(TaskSchedulerType.TYPE_STOP_AND_WAIT); |
||||
mCachePool.putTaskToFirst(oldTsk); |
||||
break; |
||||
} |
||||
tempTasks.add(oldTsk); |
||||
} |
||||
} |
||||
startTask(task); |
||||
|
||||
for (DownloadTask temp : tempTasks) { |
||||
mExecutePool.putTask(temp); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override public DownloadTask createTask(DTaskWrapper wrapper) { |
||||
super.createTask(wrapper); |
||||
DownloadTask task = null; |
||||
if (!mCachePool.taskExits(wrapper.getKey()) && !mExecutePool.taskExits(wrapper.getKey())) { |
||||
task = (DownloadTask) TaskFactory.getInstance() |
||||
.createTask(wrapper, TaskSchedulers.getInstance()); |
||||
addTask(task); |
||||
} else { |
||||
ALog.w(TAG, "任务已存在"); |
||||
} |
||||
|
||||
return task; |
||||
} |
||||
|
||||
@Override public void stopTask(DownloadTask task) { |
||||
task.setHighestPriority(false); |
||||
super.stopTask(task); |
||||
} |
||||
|
||||
@Override public int getMaxTaskNum() { |
||||
return AriaConfig.getInstance().getDConfig().getMaxTaskNum(); |
||||
} |
||||
} |
@ -1,155 +0,0 @@ |
||||
/* |
||||
* 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.queue; |
||||
|
||||
import com.arialyy.aria.core.download.DGTaskWrapper; |
||||
import com.arialyy.aria.core.download.DTaskWrapper; |
||||
import com.arialyy.aria.core.inf.TaskSchedulerType; |
||||
import com.arialyy.aria.core.task.DownloadGroupTask; |
||||
import com.arialyy.aria.core.task.DownloadTask; |
||||
import com.arialyy.aria.core.task.ITask; |
||||
import com.arialyy.aria.core.task.UploadTask; |
||||
import com.arialyy.aria.core.upload.UTaskWrapper; |
||||
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
||||
|
||||
/** |
||||
* Created by lyy on 2016/8/16. 任务功能接口 |
||||
*/ |
||||
public interface ITaskQueue<TASK extends ITask, TASK_WRAPPER extends AbsTaskWrapper> { |
||||
|
||||
/** |
||||
* 通过key跑断任务是在存在 |
||||
* |
||||
* @param key 下载链接,或上传文件的路径 |
||||
* @return {@code true} 任务存在 |
||||
*/ |
||||
boolean taskExists(String key); |
||||
|
||||
/** |
||||
* 通过key判断任务是否正在执行 |
||||
* |
||||
* @param key 下载链接,或上传文件的路径 |
||||
* @return {@code true} 任务正在运行 |
||||
*/ |
||||
boolean taskIsRunning(String key); |
||||
|
||||
/** |
||||
* 恢复任务 如果执行队列任务未满,则直接启动任务。 如果执行队列已经满了,则暂停执行队列队首任务,并恢复指定任务 |
||||
* |
||||
* @param task 需要恢复飞任务 |
||||
*/ |
||||
void resumeTask(TASK task); |
||||
|
||||
/** |
||||
* 停止所有任务 |
||||
*/ |
||||
void stopAllTask(); |
||||
|
||||
/** |
||||
* 开始任务 |
||||
* |
||||
* @param task {@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} |
||||
*/ |
||||
void startTask(TASK task); |
||||
|
||||
/** |
||||
* 开始任务 |
||||
* |
||||
* @param action {@link TaskSchedulerType} |
||||
*/ |
||||
void startTask(TASK task, int action); |
||||
|
||||
/** |
||||
* 停止任务 |
||||
* |
||||
* @param task {@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} |
||||
*/ |
||||
void stopTask(TASK task); |
||||
|
||||
/** |
||||
* 取消任务 |
||||
* |
||||
* @param task {@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} |
||||
*/ |
||||
void cancelTask(TASK task); |
||||
|
||||
/** |
||||
* 取消任务 |
||||
* |
||||
* @param action {@link TaskSchedulerType} |
||||
*/ |
||||
void cancelTask(TASK task, int action); |
||||
|
||||
/** |
||||
* 通过key从队列中删除任务 |
||||
* |
||||
* @param key 如果是下载,则为下载链接;如果是上传,为文件保存路径;如果是下载任务组,则为任务组名 |
||||
*/ |
||||
void removeTaskFormQueue(String key); |
||||
|
||||
/** |
||||
* 重试下载 |
||||
* |
||||
* @param task {@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} |
||||
*/ |
||||
void reTryStart(TASK task); |
||||
|
||||
/** |
||||
* 获取当前执行池中的任务数量 |
||||
*/ |
||||
int getCurrentExePoolNum(); |
||||
|
||||
/** |
||||
* 获取当前任务缓存池中的任务数量 |
||||
*/ |
||||
int getCurrentCachePoolNum(); |
||||
|
||||
/** |
||||
* 设置执行池可执行的最大任务数 |
||||
* |
||||
* @param newMaxNum 最大任务数 |
||||
*/ |
||||
void setMaxTaskNum(int newMaxNum); |
||||
|
||||
/** |
||||
* 获取执行池可执行的最大任务数 |
||||
*/ |
||||
int getMaxTaskNum(); |
||||
|
||||
/** |
||||
* 创建一个缓存任务,创建时只是将新任务存储到缓存池 |
||||
* |
||||
* @param wrapper 任务实体{@link DTaskWrapper}、{@link UTaskWrapper}、{@link DGTaskWrapper} |
||||
* @return {@link DownloadTask}、{@link UploadTask}、{@link DGTaskWrapper} |
||||
*/ |
||||
TASK createTask(TASK_WRAPPER wrapper); |
||||
|
||||
/** |
||||
* 通过工作实体缓存池或任务池搜索下载任务,如果缓存池或任务池都没有任务,则创建新任务 |
||||
* |
||||
* @param key 如果是下载,则为下载链接;如果是上传,为文件保存路径;如果是下载任务组,则为任务组名 |
||||
* @return {@link DownloadTask}、{@link UploadTask}、{@link DownloadGroupTask} |
||||
*/ |
||||
TASK getTask(String key); |
||||
|
||||
/** |
||||
* 获取缓存池的下一个任务 |
||||
* |
||||
* @return 下载任务 or null |
||||
*/ |
||||
TASK getNextTask(); |
||||
} |
@ -1,78 +0,0 @@ |
||||
/* |
||||
* 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.queue; |
||||
|
||||
import com.arialyy.aria.core.AriaConfig; |
||||
import com.arialyy.aria.core.AriaManager; |
||||
import com.arialyy.aria.core.event.Event; |
||||
import com.arialyy.aria.core.event.EventMsgUtil; |
||||
import com.arialyy.aria.core.event.UMaxNumEvent; |
||||
import com.arialyy.aria.core.scheduler.TaskSchedulers; |
||||
import com.arialyy.aria.core.upload.UTaskWrapper; |
||||
import com.arialyy.aria.core.task.UploadTask; |
||||
import com.arialyy.aria.util.ALog; |
||||
|
||||
/** |
||||
* Created by lyy on 2017/2/27. 上传任务队列 |
||||
*/ |
||||
public class UTaskQueue extends AbsTaskQueue<UploadTask, UTaskWrapper> { |
||||
private static final String TAG = "UploadTaskQueue"; |
||||
private static volatile UTaskQueue INSTANCE = null; |
||||
|
||||
public static UTaskQueue getInstance() { |
||||
if (INSTANCE == null) { |
||||
synchronized (UTaskQueue.class) { |
||||
INSTANCE = new UTaskQueue(); |
||||
EventMsgUtil.getDefault().register(INSTANCE); |
||||
} |
||||
} |
||||
return INSTANCE; |
||||
} |
||||
|
||||
private UTaskQueue() { |
||||
} |
||||
|
||||
@Event |
||||
public void maxTaskNum(UMaxNumEvent event){ |
||||
setMaxTaskNum(event.maxNum); |
||||
} |
||||
|
||||
@Override int getQueueType() { |
||||
return TYPE_U_QUEUE; |
||||
} |
||||
|
||||
@Override public int getOldMaxNum() { |
||||
return AriaConfig.getInstance().getUConfig().oldMaxTaskNum; |
||||
} |
||||
|
||||
@Override public int getMaxTaskNum() { |
||||
return AriaConfig.getInstance().getUConfig().getMaxTaskNum(); |
||||
} |
||||
|
||||
@Override public UploadTask createTask(UTaskWrapper wrapper) { |
||||
super.createTask(wrapper); |
||||
UploadTask task = null; |
||||
if (!mCachePool.taskExits(wrapper.getKey()) && !mExecutePool.taskExits(wrapper.getKey())) { |
||||
task = (UploadTask) TaskFactory.getInstance() |
||||
.createTask(wrapper, TaskSchedulers.getInstance()); |
||||
addTask(task); |
||||
} else { |
||||
ALog.w(TAG, "任务已存在"); |
||||
} |
||||
return task; |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
/* |
||||
* 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.http |
||||
|
||||
import com.arialyy.aria.http.download.HttpDownloader |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 12:40 PM 2023/1/22 |
||||
**/ |
||||
open class HttpBaseController(val target: Any) { |
||||
|
||||
} |
@ -0,0 +1,20 @@ |
||||
package com.arialyy.aria.http |
||||
|
||||
import android.content.Context |
||||
import com.arialyy.aria.core.DuaContext |
||||
import com.arialyy.aria.core.inf.IComponentInit |
||||
import com.arialyy.aria.queue.DTaskQueue |
||||
import com.arialyy.aria.queue.UTaskQueue |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 3:40 PM 2023/1/26 |
||||
**/ |
||||
class HttpComponent : IComponentInit { |
||||
override fun init(context: Context) { |
||||
DuaContext.getServiceManager().registerService(DuaContext.D_QUEUE, com.arialyy.aria.queue.DTaskQueue.getInstance()) |
||||
DuaContext.getServiceManager().registerService(DuaContext.U_QUEUE, com.arialyy.aria.queue.UTaskQueue.getInstance()) |
||||
|
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
/* |
||||
* 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.http |
||||
|
||||
import com.arialyy.aria.http.download.HttpDTaskOption |
||||
import com.arialyy.aria.util.CheckUtil |
||||
import timber.log.Timber |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 12:40 PM 2023/1/22 |
||||
**/ |
||||
internal object HttpUtil { |
||||
|
||||
fun checkHttpDParams(option: HttpDTaskOption?): Boolean { |
||||
if (option == null) { |
||||
Timber.e("option is null") |
||||
return false |
||||
} |
||||
if (option.sourUrl.isNullOrEmpty()) { |
||||
Timber.e("url is null") |
||||
return false |
||||
} |
||||
if (!CheckUtil.checkUrl(option.sourUrl!!)) { |
||||
Timber.e("invalid url, ${option.sourUrl}") |
||||
return false |
||||
} |
||||
if (option.savePathUri == null) { |
||||
Timber.e("save path is null") |
||||
return false |
||||
} |
||||
if (!CheckUtil.checkUri(option.savePathUri)) { |
||||
Timber.e("invalid uri, ${option.savePathUri}") |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
/* |
||||
* 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.http.download |
||||
|
||||
import android.net.Uri |
||||
import com.arialyy.aria.core.DuaContext |
||||
import com.arialyy.aria.core.event.EventMsgUtil |
||||
import com.arialyy.aria.core.inf.IStartController |
||||
import com.arialyy.aria.core.task.DownloadTask |
||||
import com.arialyy.aria.http.HttpBaseController |
||||
import com.arialyy.aria.http.HttpOption |
||||
import com.arialyy.aria.http.HttpUtil |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 12:38 PM 2023/1/22 |
||||
**/ |
||||
class HttpDStartController(target: Any, val url: String) : HttpBaseController(target), |
||||
IStartController { |
||||
|
||||
private var httpDTaskOption = HttpDTaskOption() |
||||
|
||||
init { |
||||
httpDTaskOption.sourUrl = url |
||||
} |
||||
|
||||
/** |
||||
* set http params, link Header |
||||
*/ |
||||
fun setHttpOption(httpOption: HttpOption): HttpBaseController { |
||||
httpDTaskOption.httpOption = httpOption |
||||
return this |
||||
} |
||||
|
||||
fun setListener(listener: HttpDownloadListener): HttpBaseController { |
||||
DuaContext.getLifeManager().addCustomListener(target, listener) |
||||
return this |
||||
} |
||||
|
||||
fun setSavePath(savePath: Uri): HttpDStartController { |
||||
httpDTaskOption.savePathUri = savePath |
||||
return this |
||||
} |
||||
|
||||
private fun createTask(): DownloadTask { |
||||
if (HttpUtil.checkHttpDParams(httpDTaskOption)) { |
||||
throw IllegalArgumentException("invalid params") |
||||
} |
||||
val task = DownloadTask() |
||||
} |
||||
|
||||
override fun add(): Long { |
||||
if (!HttpUtil.checkHttpDParams(httpDTaskOption)) { |
||||
return -1 |
||||
} |
||||
EventMsgUtil.getDefault().post() |
||||
TODO("Not yet implemented") |
||||
} |
||||
|
||||
override fun create(): Long { |
||||
if (!HttpUtil.checkHttpDParams(httpDTaskOption)) { |
||||
return -1 |
||||
} |
||||
|
||||
TODO("Not yet implemented") |
||||
} |
||||
|
||||
override fun resume(): Long { |
||||
if (!HttpUtil.checkHttpDParams(httpDTaskOption)) { |
||||
return -1 |
||||
} |
||||
TODO("Not yet implemented") |
||||
} |
||||
|
||||
override fun resume(newStart: Boolean): Long { |
||||
if (!HttpUtil.checkHttpDParams(httpDTaskOption)) { |
||||
return -1 |
||||
} |
||||
TODO("Not yet implemented") |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
/* |
||||
* 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.http.download |
||||
|
||||
import com.arialyy.aria.core.download.DTaskOption |
||||
import com.arialyy.aria.http.HttpOption |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 12:47 PM 2023/1/22 |
||||
**/ |
||||
class HttpDTaskOption : DTaskOption() { |
||||
|
||||
var httpOption: HttpOption? = null |
||||
} |
@ -0,0 +1,27 @@ |
||||
/* |
||||
* 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.http.download |
||||
|
||||
import com.arialyy.aria.core.listener.ITaskStatusListener |
||||
import com.arialyy.aria.core.task.DownloadTask |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 12:24 PM 2023/1/22 |
||||
**/ |
||||
class HttpDownloadListener : ITaskStatusListener<DownloadTask> { |
||||
} |
@ -0,0 +1 @@ |
||||
com.arialyy.aria.http.HttpComponent |
@ -0,0 +1,13 @@ |
||||
|
||||
package com.arialyy.aria.core.config |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 10:36 PM 2023/1/24 |
||||
**/ |
||||
class CommonConfig : BaseTaskConfig() { |
||||
override fun getType(): Int { |
||||
return TYPE_COMMON |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
/* |
||||
* 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.download |
||||
|
||||
import android.net.Uri |
||||
import com.arialyy.aria.core.inf.ITaskOption |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 4:16 PM 2023/1/25 |
||||
**/ |
||||
open class DTaskOption : ITaskOption() { |
||||
var sourUrl: String? = null |
||||
var savePathUri: Uri? = null |
||||
} |
@ -0,0 +1,27 @@ |
||||
/* |
||||
* 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.inf |
||||
|
||||
import android.content.Context |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 3:38 PM 2023/1/26 |
||||
**/ |
||||
interface IComponentInit { |
||||
fun init(context: Context) |
||||
} |
@ -0,0 +1,52 @@ |
||||
/* |
||||
* 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.inf |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 12:32 PM 2023/1/22 |
||||
**/ |
||||
interface IStartController { |
||||
/** |
||||
* 添加任务 |
||||
* |
||||
* @return 正常添加,返回任务id,否则返回-1 |
||||
*/ |
||||
fun add(): Long |
||||
|
||||
/** |
||||
* 创建并开始任务 |
||||
* |
||||
* @return 正常启动,返回任务id,否则返回-1 |
||||
*/ |
||||
fun create(): Long |
||||
|
||||
/** |
||||
* 恢复任务 |
||||
* @return 正常启动,返回任务id,否则返回-1 |
||||
*/ |
||||
fun resume(): Long |
||||
|
||||
/** |
||||
* 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。 |
||||
* 如果希望调用恢复接口,马上进入执行队列,需要使用该方法 |
||||
* |
||||
* @param newStart true 立即将任务恢复到执行队列中 |
||||
* @return 正常启动,返回任务id,否则返回-1 |
||||
*/ |
||||
fun resume(newStart: Boolean): Long |
||||
} |
@ -0,0 +1,88 @@ |
||||
/* |
||||
* 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.inf; |
||||
|
||||
import com.arialyy.aria.core.service.IService; |
||||
import com.arialyy.aria.core.task.ITask; |
||||
|
||||
/** |
||||
* Created by lyy on 2016/8/16. 任务功能接口 |
||||
*/ |
||||
public interface ITaskQueue<TASK extends ITask> extends IService { |
||||
|
||||
/** |
||||
* @return {@code true} task exists |
||||
*/ |
||||
boolean taskExists(int taskId); |
||||
|
||||
/** |
||||
* @return {@code true} task is running |
||||
*/ |
||||
boolean taskIsRunning(int taskId); |
||||
|
||||
/** |
||||
* stop task |
||||
*/ |
||||
void stopTask(TASK task); |
||||
|
||||
/** |
||||
* stop all task |
||||
*/ |
||||
void stopAllTask(); |
||||
|
||||
/** |
||||
* start a task |
||||
* |
||||
* @return TaskId is returned if the task is created successfully, and -1 is returned for failure. |
||||
*/ |
||||
int startTask(TASK task); |
||||
|
||||
/** |
||||
* remove task |
||||
*/ |
||||
void removeTask(TASK task); |
||||
|
||||
/** |
||||
* 重试 |
||||
*/ |
||||
void reTry(TASK task); |
||||
|
||||
/** |
||||
* get cache queue size |
||||
*/ |
||||
int getCacheSize(); |
||||
|
||||
/** |
||||
* get process queue size |
||||
*/ |
||||
int getQueueSize(); |
||||
|
||||
/** |
||||
* modify process queue size |
||||
*/ |
||||
void setQueueSize(int size); |
||||
|
||||
/** |
||||
* get task by id |
||||
*/ |
||||
TASK getTask(int taskId); |
||||
|
||||
/** |
||||
* get next task |
||||
*/ |
||||
boolean startNextTask(); |
||||
} |
@ -0,0 +1,76 @@ |
||||
/* |
||||
* 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.listener |
||||
|
||||
import com.arialyy.aria.core.task.ITask |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 11:43 AM 2023/1/22 |
||||
**/ |
||||
interface ITaskStatusListener<TASK : ITask> { |
||||
/** |
||||
* 队列已经满了,继续创建任务,将会回调该方法 |
||||
*/ |
||||
fun onWait(task: TASK) {} |
||||
|
||||
/** |
||||
* 预处理,有时有些地址链接比较慢,这时可以先在这个地方出来一些界面上的UI,如按钮的状态。 |
||||
* 在这个回调中,任务是获取不到文件大小,下载速度等参数 |
||||
*/ |
||||
fun onPre(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务预加载完成 |
||||
*/ |
||||
fun onTaskPre(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务恢复下载 |
||||
*/ |
||||
fun onTaskResume(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务开始 |
||||
*/ |
||||
fun onTaskStart(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务停止 |
||||
*/ |
||||
fun onTaskStop(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务取消 |
||||
*/ |
||||
fun onTaskCancel(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务失败 |
||||
*/ |
||||
fun onTaskFail(task: TASK, e: Exception?) {} |
||||
|
||||
/** |
||||
* 任务完成 |
||||
*/ |
||||
fun onTaskComplete(task: TASK) {} |
||||
|
||||
/** |
||||
* 任务执行中 |
||||
*/ |
||||
fun onTaskRunning(task: TASK) {} |
||||
} |
@ -0,0 +1,69 @@ |
||||
/* |
||||
* 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.service |
||||
|
||||
import com.arialyy.aria.core.inf.IBaseLoader |
||||
import com.arialyy.aria.core.inf.IComponentLoader |
||||
import com.arialyy.aria.core.listener.ITaskStatusListener |
||||
import java.util.concurrent.ConcurrentHashMap |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 10:55 AM 2023/1/20 |
||||
**/ |
||||
object LifecycleManager { |
||||
|
||||
private val listenerMap = ConcurrentHashMap<Any, MutableSet<ITaskStatusListener<*>>>() |
||||
private val loaderMap = ConcurrentHashMap<IComponentLoader, Any>() |
||||
|
||||
fun getTargetByLoader(loader: IComponentLoader) = loaderMap[loader] |
||||
|
||||
/** |
||||
* Associate the Loader with the Target |
||||
*/ |
||||
fun loaderAssociationTarget(target: Any, loader: IComponentLoader) { |
||||
loaderMap[loader] = target |
||||
} |
||||
|
||||
fun removeLoader(target: Any) { |
||||
val tempKey = mutableListOf<IComponentLoader>() |
||||
loaderMap.forEach { |
||||
if (it.value == target) { |
||||
tempKey.add(it.key) |
||||
} |
||||
} |
||||
tempKey.forEach { |
||||
loaderMap.remove(it) |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Monitoring set by user, [IBaseLoader] |
||||
*/ |
||||
fun <T : ITaskStatusListener<*>> addCustomListener(target: Any, listener: T) { |
||||
var listeners = listenerMap[target] |
||||
if (listeners == null) { |
||||
listeners = hashSetOf() |
||||
} |
||||
listeners.add(listener) |
||||
} |
||||
|
||||
fun removeCustomListener(target: Any) { |
||||
listenerMap.remove(target) |
||||
} |
||||
} |
||||
|
@ -0,0 +1,90 @@ |
||||
/* |
||||
* 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.task |
||||
|
||||
import com.arialyy.aria.core.config.Configuration |
||||
import com.arialyy.aria.core.inf.IEntity |
||||
import com.arialyy.aria.util.CommonUtil |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 10:04 PM 2023/1/24 |
||||
**/ |
||||
class TaskState { |
||||
var state: Int = IEntity.STATE_WAIT |
||||
|
||||
/** |
||||
* current task progress, unit: byte |
||||
*/ |
||||
var curProgress: Long = 0 |
||||
|
||||
/** |
||||
* Bytes transferred in 1 second, if file size 0, return 0 |
||||
* curSpeed, unit: byte/s |
||||
*/ |
||||
var speed: Long = 0 |
||||
get() { |
||||
return if (fileSize == 0L) 0 else field |
||||
} |
||||
set(value) { |
||||
if (value <= 0L) { |
||||
timeLeft = Int.MAX_VALUE |
||||
field = 0L |
||||
return |
||||
} |
||||
if (fileSize == 0L) { |
||||
timeLeft = Int.MAX_VALUE |
||||
} |
||||
timeLeft = ((fileSize - curProgress) / value).toInt() |
||||
field = value |
||||
} |
||||
|
||||
var fileSize: Long = 0 |
||||
|
||||
/** |
||||
* task time left, unit: s |
||||
*/ |
||||
var timeLeft: Int = Int.Companion.MAX_VALUE |
||||
|
||||
fun getPercent() = ((curProgress * 100) / fileSize).toInt() |
||||
|
||||
fun isComplete() = state == IEntity.STATE_COMPLETE |
||||
|
||||
/** |
||||
* you need set params in config |
||||
* @return Returns the converted speed:1b/s、1kb/s、1mb/s、1gb/s、1tb/s |
||||
* xml: |
||||
* <pre> |
||||
* `<xml> |
||||
* <download> |
||||
* ... |
||||
* <convertSpeed value="true"/> |
||||
* </download> |
||||
* |
||||
* code: |
||||
* Dua.getCommonConfig().setConvertSpeed(true); |
||||
* </xml> |
||||
* |
||||
* </pre> |
||||
*/ |
||||
fun getConvertSpeed(): String { |
||||
if (Configuration.getInstance().cCommonCfg.isConvertSpeed) { |
||||
return CommonUtil.formatFileSize((if (speed < 0L) 0L else speed.toDouble()) as Double) + "/s" |
||||
} |
||||
return "0b/s" |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
/* |
||||
* 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.task |
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 10:03 PM 2023/1/24 |
||||
**/ |
||||
object TaskStatePool { |
||||
private val stateMap = hashMapOf<Int, TaskState>() |
||||
private val taskCounter = AtomicInteger(1) |
||||
|
||||
fun putTaskState(taskId: Int, state: TaskState) { |
||||
stateMap[taskId] = state |
||||
} |
||||
|
||||
fun getTaskState(taskId: Int) = stateMap[taskId] |
||||
|
||||
internal fun buildTaskId() = taskCounter.addAndGet(1) |
||||
} |
@ -0,0 +1,83 @@ |
||||
/* |
||||
* 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.util |
||||
|
||||
import android.database.Cursor |
||||
import android.net.Uri |
||||
import com.arialyy.aria.core.DuaContext |
||||
import timber.log.Timber |
||||
import java.io.InputStream |
||||
|
||||
object CheckUtil { |
||||
|
||||
val HTTP_REGEX = |
||||
Regex( |
||||
"(https?)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]", |
||||
RegexOption.IGNORE_CASE |
||||
) |
||||
|
||||
/** |
||||
* check if url is correct |
||||
*/ |
||||
fun checkUrl(url: String) = HTTP_REGEX.matches(url) |
||||
|
||||
/** |
||||
* 1. Check if Uri is correct |
||||
* 2. Whether the file corresponding to Uri exists (may be deleted, or the system db has Uri related records, but the file is invalid or damaged) |
||||
* |
||||
* https://stackoverflow.com/questions/7645951/how-to-check-if-resource-pointed-by-uri-is-available |
||||
*/ |
||||
fun checkUri(uri: Uri?): Boolean { |
||||
if (uri == null) return false |
||||
val resolver = DuaContext.context.contentResolver |
||||
|
||||
//1. Check Uri |
||||
var cursor: Cursor? = null |
||||
val isUriExist: Boolean = try { |
||||
cursor = resolver.query(uri, null, null, null, null) |
||||
//cursor null: content Uri was invalid or some other error occurred |
||||
//cursor.moveToFirst() false: Uri was ok but no entry found. |
||||
(cursor != null && cursor.moveToFirst()) |
||||
} catch (t: Throwable) { |
||||
Timber.e("1.Check Uri Error: ${t.message}") |
||||
false |
||||
} finally { |
||||
try { |
||||
cursor?.close() |
||||
} catch (t: Throwable) { |
||||
} |
||||
} |
||||
|
||||
//2. Check File Exist |
||||
//如果系统 db 存有 Uri 相关记录, 但是文件失效或者损坏 (If the system db has Uri related records, but the file is invalid or damaged) |
||||
var ins: InputStream? = null |
||||
val isFileExist: Boolean = try { |
||||
ins = resolver.openInputStream(uri) |
||||
// file exists |
||||
true |
||||
} catch (t: Throwable) { |
||||
// File was not found eg: open failed: ENOENT (No such file or directory) |
||||
Timber.e("2. Check File Exist Error: ${t.message}") |
||||
false |
||||
} finally { |
||||
try { |
||||
ins?.close() |
||||
} catch (t: Throwable) { |
||||
} |
||||
} |
||||
return isUriExist && isFileExist |
||||
} |
||||
} |
@ -0,0 +1 @@ |
||||
/build |
@ -0,0 +1,43 @@ |
||||
plugins { |
||||
id 'com.android.library' |
||||
id 'org.jetbrains.kotlin.android' |
||||
} |
||||
|
||||
android { |
||||
|
||||
compileSdkVersion libs.versions.compilesdk.get().toInteger() |
||||
buildToolsVersion libs.versions.buildToolsVersion.get() |
||||
|
||||
namespace 'com.arialyy.aria.queue' |
||||
|
||||
defaultConfig { |
||||
minSdkVersion libs.versions.minSdk.get().toInteger() |
||||
targetSdkVersion libs.versions.targetsdk.get().toInteger() |
||||
|
||||
consumerProguardFiles 'consumer-rules.pro' |
||||
} |
||||
|
||||
buildTypes { |
||||
release { |
||||
minifyEnabled false |
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||
} |
||||
} |
||||
lintOptions { |
||||
abortOnError false |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
implementation fileTree(dir: 'libs', include: ['*.jar']) |
||||
|
||||
implementation project(path: ':PublicComponent') |
||||
testImplementation(libs.junit) |
||||
androidTestImplementation(libs.bundles.android.test) |
||||
} |
||||
|
||||
//apply from: 'bintray-release.gradle' |
||||
ext { |
||||
PUBLISH_ARTIFACT_ID = 'queue' |
||||
} |
||||
apply from: '../gradle/mavenCentral-release.gradle' |
@ -0,0 +1,21 @@ |
||||
# Add project specific ProGuard rules here. |
||||
# You can control the set of applied configuration files using the |
||||
# proguardFiles setting in build.gradle. |
||||
# |
||||
# For more details, see |
||||
# http://developer.android.com/guide/developing/tools/proguard.html |
||||
|
||||
# If your project uses WebView with JS, uncomment the following |
||||
# and specify the fully qualified class name to the JavaScript interface |
||||
# class: |
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||
# public *; |
||||
#} |
||||
|
||||
# Uncomment this to preserve the line number information for |
||||
# debugging stack traces. |
||||
#-keepattributes SourceFile,LineNumberTable |
||||
|
||||
# If you keep the line number information, uncomment this to |
||||
# hide the original source file name. |
||||
#-renamesourcefileattribute SourceFile |
@ -0,0 +1,24 @@ |
||||
package com.arialyy.aria.queue |
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry |
||||
import androidx.test.ext.junit.runners.AndroidJUnit4 |
||||
|
||||
import org.junit.Test |
||||
import org.junit.runner.RunWith |
||||
|
||||
import org.junit.Assert.* |
||||
|
||||
/** |
||||
* Instrumented test, which will execute on an Android device. |
||||
* |
||||
* See [testing documentation](http://d.android.com/tools/testing). |
||||
*/ |
||||
@RunWith(AndroidJUnit4::class) |
||||
class ExampleInstrumentedTest { |
||||
@Test |
||||
fun useAppContext() { |
||||
// Context of the app under test. |
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext |
||||
assertEquals("com.arialyy.aria.queue.test", appContext.packageName) |
||||
} |
||||
} |
@ -0,0 +1,4 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
||||
</manifest> |
@ -0,0 +1,264 @@ |
||||
/* |
||||
* 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.queue; |
||||
|
||||
import com.arialyy.aria.core.inf.IEntity; |
||||
import com.arialyy.aria.core.inf.ITaskQueue; |
||||
import com.arialyy.aria.core.inf.TaskSchedulerType; |
||||
import com.arialyy.aria.core.manager.ThreadTaskManager; |
||||
import com.arialyy.aria.core.task.ITask; |
||||
import timber.log.Timber; |
||||
|
||||
/** |
||||
* Created by lyy on 2017/2/23. 任务队列 |
||||
*/ |
||||
public abstract class AbsTaskQueue<TASK extends ITask> implements ITaskQueue<TASK> { |
||||
|
||||
private final IPool<TASK> DEF_CACHE_POOL = new BaseCachePool<>(); |
||||
private final IPool<TASK> DEF_EXE_POOL = new BaseExecutePool<>(); |
||||
private int maxSize; |
||||
|
||||
protected AbsTaskQueue() { |
||||
maxSize = getMaxTaskSize(); |
||||
} |
||||
|
||||
protected IPool<TASK> getCachePool() { |
||||
return DEF_CACHE_POOL; |
||||
} |
||||
|
||||
protected IPool<TASK> getExePool() { |
||||
return DEF_EXE_POOL; |
||||
} |
||||
|
||||
protected abstract int getMaxTaskSize(); |
||||
|
||||
@Override public boolean taskExists(int taskId) { |
||||
return getCachePool().taskExist(taskId) || getExePool().taskExist(taskId); |
||||
} |
||||
|
||||
/** |
||||
* @return if cachePool or exePool has task, return true, otherwise false |
||||
*/ |
||||
@Override public boolean taskIsRunning(int taskId) { |
||||
if (getCachePool().getTask(taskId) != null) { |
||||
return true; |
||||
} |
||||
TASK task = getExePool().getTask(taskId); |
||||
if (task == null && ThreadTaskManager.getInstance().taskIsRunning(taskId)) { |
||||
ThreadTaskManager.getInstance().removeTaskThread(taskId); |
||||
} |
||||
return task != null && task.isRunning() && taskExists(taskId); |
||||
} |
||||
|
||||
@Override public int startTask(TASK task) { |
||||
if (task == null) { |
||||
Timber.e("task is null"); |
||||
return -1; |
||||
} |
||||
if (getExePool().taskExist(task.getTaskId())) { |
||||
Timber.w("task running, taskId: %s", task.getTaskId()); |
||||
return task.getTaskId(); |
||||
} |
||||
Timber.i("start a task, taskId: %s", task.getTaskId()); |
||||
if (getExePool().size() >= getMaxTaskSize()) { |
||||
Timber.i("exe queue is full, task into cache queue"); |
||||
task.setState(IEntity.STATE_WAIT); |
||||
boolean b = getCachePool().putTask(task); |
||||
return b ? task.getTaskId() : -1; |
||||
} |
||||
boolean b = getExePool().putTask(task); |
||||
task.start(TaskSchedulerType.TYPE_DEFAULT); |
||||
return b ? task.getTaskId() : -1; |
||||
} |
||||
|
||||
@Override public void removeTask(TASK task) { |
||||
if (task == null) { |
||||
Timber.e("task is null"); |
||||
return; |
||||
} |
||||
if (getCachePool().taskExist(task.getTaskId())) { |
||||
Timber.i("cache pool has task, which will be removed from the cache pool"); |
||||
getCachePool().removeTask(task.getTaskId()); |
||||
} |
||||
if (getExePool().taskExist(task.getTaskId())) { |
||||
stopTask(task); |
||||
getExePool().removeTask(task.getTaskId()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 停止所有任务 |
||||
*/ |
||||
@Override public void stopAllTask() { |
||||
for (TASK task : getExePool().getAllTask()) { |
||||
if (task != null) { |
||||
int state = task.getTaskState().getState(); |
||||
if (task.isRunning() |
||||
|| (state != IEntity.STATE_COMPLETE && state != IEntity.STATE_CANCEL)) { |
||||
task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT); |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (TASK task : getCachePool().getAllTask()) { |
||||
if (task != null) { |
||||
task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT); |
||||
} |
||||
} |
||||
ThreadTaskManager.getInstance().removeAllThreadTask(); |
||||
getCachePool().clear(); |
||||
} |
||||
|
||||
/** |
||||
* 获取配置文件旧的最大任务数 |
||||
*/ |
||||
public abstract int getOldMaxSize(); |
||||
|
||||
@Override public void stopTask(TASK task) { |
||||
if (task == null) { |
||||
Timber.e("stop fail, task is null"); |
||||
return; |
||||
} |
||||
int state = task.getTaskState().getState(); |
||||
boolean canStop = false; |
||||
switch (state) { |
||||
case IEntity.STATE_WAIT: |
||||
getCachePool().removeTask(task.getTaskId()); |
||||
canStop = true; |
||||
break; |
||||
case IEntity.STATE_POST_PRE: |
||||
case IEntity.STATE_PRE: |
||||
case IEntity.STATE_RUNNING: |
||||
getExePool().removeTask(task.getTaskId()); |
||||
canStop = true; |
||||
break; |
||||
case IEntity.STATE_STOP: |
||||
case IEntity.STATE_OTHER: |
||||
case IEntity.STATE_FAIL: |
||||
Timber.w("stop task fail,it already topped, taskId: %d", task.getTaskId()); |
||||
if (taskIsRunning(task.getTaskId())) { |
||||
getCachePool().removeTask(task.getTaskId()); |
||||
getExePool().removeTask(task.getTaskId()); |
||||
if (ThreadTaskManager.getInstance().taskIsRunning(task.getTaskId())) { |
||||
ThreadTaskManager.getInstance().removeTaskThread(task.getTaskId()); |
||||
} |
||||
} |
||||
break; |
||||
case IEntity.STATE_CANCEL: |
||||
Timber.w("stop task fail, it already removed, taskId: %d", task.getTaskId()); |
||||
break; |
||||
case IEntity.STATE_COMPLETE: |
||||
Timber.w("stop task fail, it already completed, taskId: %d", task.getTaskId()); |
||||
break; |
||||
} |
||||
|
||||
if (canStop) { |
||||
task.stop(TaskSchedulerType.TYPE_DEFAULT); |
||||
} |
||||
} |
||||
|
||||
@Override public void reTry(TASK task) { |
||||
if (task == null) { |
||||
Timber.e("task is null"); |
||||
return; |
||||
} |
||||
|
||||
int state = task.getTaskState().getState(); |
||||
switch (state) { |
||||
case IEntity.STATE_POST_PRE: |
||||
case IEntity.STATE_PRE: |
||||
case IEntity.STATE_RUNNING: |
||||
Timber.w("task is running, will restart task, taskId: %d", task.getTaskId()); |
||||
task.stop(TaskSchedulerType.TYPE_STOP_NOT_NEXT); |
||||
task.start(TaskSchedulerType.TYPE_DEFAULT); |
||||
break; |
||||
case IEntity.STATE_WAIT: |
||||
case IEntity.STATE_STOP: |
||||
case IEntity.STATE_OTHER: |
||||
case IEntity.STATE_FAIL: |
||||
task.start(TaskSchedulerType.TYPE_DEFAULT); |
||||
break; |
||||
case IEntity.STATE_CANCEL: |
||||
Timber.e("retry task fail, it already removed, taskId: %d", task.getTaskId()); |
||||
break; |
||||
case IEntity.STATE_COMPLETE: |
||||
Timber.e("retry task fail, it already completed, taskId: %d", task.getTaskId()); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
@Override public int getCacheSize() { |
||||
return getCachePool().size(); |
||||
} |
||||
|
||||
@Override public int getQueueSize() { |
||||
return getExePool().size(); |
||||
} |
||||
|
||||
@Override public void setQueueSize(int size) { |
||||
int oldMaxSize = getOldMaxSize(); |
||||
int diff = maxSize - oldMaxSize; |
||||
if (oldMaxSize == maxSize) { |
||||
Timber.w("There is no change in size"); |
||||
return; |
||||
} |
||||
maxSize = size; |
||||
//设置的任务数小于配置任务数
|
||||
if (diff <= -1 && getExePool().size() >= oldMaxSize) { |
||||
for (int i = 0, len = Math.abs(diff); i < len; i++) { |
||||
TASK eTask = getExePool().pollTask(); |
||||
if (eTask != null) { |
||||
stopTask(eTask); |
||||
} |
||||
} |
||||
} |
||||
getExePool().setPoolSize(maxSize); |
||||
if (diff >= 1) { |
||||
for (int i = 0; i < diff; i++) { |
||||
startNextTask(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override public TASK getTask(int taskId) { |
||||
if (taskId < 1) { |
||||
Timber.e("invalid taskId: %d", taskId); |
||||
return null; |
||||
} |
||||
TASK ct = getCachePool().getTask(taskId); |
||||
if (ct != null) { |
||||
return ct; |
||||
} |
||||
TASK et = getExePool().getTask(taskId); |
||||
if (et != null) { |
||||
return et; |
||||
} |
||||
Timber.w("not found task, taskId: %d", taskId); |
||||
return null; |
||||
} |
||||
|
||||
@Override public boolean startNextTask() { |
||||
TASK nextTask = getCachePool().pollTask(); |
||||
if (nextTask != null && nextTask.getTaskState().getState() == IEntity.STATE_WAIT) { |
||||
return startTask(nextTask) != -1; |
||||
} |
||||
Timber.w("start next fail"); |
||||
return false; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,53 @@ |
||||
/* |
||||
* 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.queue; |
||||
|
||||
import android.content.Context; |
||||
import com.arialyy.aria.core.AriaConfig; |
||||
import com.arialyy.aria.core.task.DownloadTask; |
||||
|
||||
/** |
||||
* Created by lyy on 2016/8/17. |
||||
* 下载任务队列 |
||||
*/ |
||||
public class DTaskQueue extends AbsTaskQueue<DownloadTask> { |
||||
private static volatile DTaskQueue INSTANCE = null; |
||||
|
||||
public static DTaskQueue getInstance() { |
||||
if (INSTANCE == null) { |
||||
synchronized (DTaskQueue.class) { |
||||
INSTANCE = new DTaskQueue(); |
||||
} |
||||
} |
||||
return INSTANCE; |
||||
} |
||||
|
||||
private DTaskQueue() { |
||||
} |
||||
|
||||
@Override protected int getMaxTaskSize() { |
||||
return AriaConfig.getInstance().getDConfig().getMaxTaskNum(); |
||||
} |
||||
|
||||
@Override public int getOldMaxSize() { |
||||
return AriaConfig.getInstance().getDConfig().oldMaxTaskNum; |
||||
} |
||||
|
||||
@Override public void init(@NonNull Context context) { |
||||
|
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
package com.arialyy.aria.queue |
||||
|
||||
import android.content.Context |
||||
import com.arialyy.aria.core.inf.IComponentInit |
||||
|
||||
/** |
||||
* @Author laoyuyu |
||||
* @Description |
||||
* @Date 4:08 PM 2023/1/26 |
||||
**/ |
||||
class QueueComponent : IComponentInit { |
||||
override fun init(context: Context) { |
||||
|
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
/* |
||||
* 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.queue; |
||||
|
||||
import android.content.Context; |
||||
import com.arialyy.aria.core.AriaConfig; |
||||
import com.arialyy.aria.core.task.UploadTask; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
/** |
||||
* Created by lyy on 2017/2/27. 上传任务队列 |
||||
*/ |
||||
public class UTaskQueue extends AbsTaskQueue<UploadTask> { |
||||
private static volatile UTaskQueue INSTANCE = null; |
||||
|
||||
public static UTaskQueue getInstance() { |
||||
if (INSTANCE == null) { |
||||
synchronized (UTaskQueue.class) { |
||||
INSTANCE = new UTaskQueue(); |
||||
} |
||||
} |
||||
return INSTANCE; |
||||
} |
||||
|
||||
private UTaskQueue() { |
||||
} |
||||
|
||||
@Override protected int getMaxTaskSize() { |
||||
return AriaConfig.getInstance().getUConfig().getMaxTaskNum(); |
||||
} |
||||
|
||||
@Override public int getOldMaxSize() { |
||||
return AriaConfig.getInstance().getUConfig().oldMaxTaskNum; |
||||
} |
||||
|
||||
@Override public void init(@NotNull Context context) { |
||||
|
||||
} |
||||
} |
@ -0,0 +1,2 @@ |
||||
Manifest-Version: 1.0 |
||||
|
@ -0,0 +1 @@ |
||||
com.arialyy.aria.queue.QueueComponent |
@ -0,0 +1,17 @@ |
||||
package com.arialyy.aria.queue |
||||
|
||||
import org.junit.Test |
||||
|
||||
import org.junit.Assert.* |
||||
|
||||
/** |
||||
* Example local unit test, which will execute on the development machine (host). |
||||
* |
||||
* See [testing documentation](http://d.android.com/tools/testing). |
||||
*/ |
||||
class ExampleUnitTest { |
||||
@Test |
||||
fun addition_isCorrect() { |
||||
assertEquals(4, 2 + 2) |
||||
} |
||||
} |
@ -0,0 +1 @@ |
||||
/build |
@ -0,0 +1,43 @@ |
||||
plugins { |
||||
id 'com.android.library' |
||||
id 'org.jetbrains.kotlin.android' |
||||
} |
||||
|
||||
android { |
||||
|
||||
compileSdkVersion libs.versions.compilesdk.get().toInteger() |
||||
buildToolsVersion libs.versions.buildToolsVersion.get() |
||||
|
||||
namespace 'com.arialyy.aria.schedulers' |
||||
|
||||
defaultConfig { |
||||
minSdkVersion libs.versions.minSdk.get().toInteger() |
||||
targetSdkVersion libs.versions.targetsdk.get().toInteger() |
||||
|
||||
consumerProguardFiles 'consumer-rules.pro' |
||||
} |
||||
|
||||
buildTypes { |
||||
release { |
||||
minifyEnabled false |
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||
} |
||||
} |
||||
lintOptions { |
||||
abortOnError false |
||||
} |
||||
} |
||||
|
||||
dependencies { |
||||
implementation fileTree(dir: 'libs', include: ['*.jar']) |
||||
|
||||
implementation project(path: ':PublicComponent') |
||||
testImplementation(libs.junit) |
||||
androidTestImplementation(libs.bundles.android.test) |
||||
} |
||||
|
||||
//apply from: 'bintray-release.gradle' |
||||
ext { |
||||
PUBLISH_ARTIFACT_ID = 'schedulers' |
||||
} |
||||
apply from: '../gradle/mavenCentral-release.gradle' |
@ -0,0 +1,21 @@ |
||||
# Add project specific ProGuard rules here. |
||||
# You can control the set of applied configuration files using the |
||||
# proguardFiles setting in build.gradle. |
||||
# |
||||
# For more details, see |
||||
# http://developer.android.com/guide/developing/tools/proguard.html |
||||
|
||||
# If your project uses WebView with JS, uncomment the following |
||||
# and specify the fully qualified class name to the JavaScript interface |
||||
# class: |
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||
# public *; |
||||
#} |
||||
|
||||
# Uncomment this to preserve the line number information for |
||||
# debugging stack traces. |
||||
#-keepattributes SourceFile,LineNumberTable |
||||
|
||||
# If you keep the line number information, uncomment this to |
||||
# hide the original source file name. |
||||
#-renamesourcefileattribute SourceFile |
@ -0,0 +1,24 @@ |
||||
package com.arialyy.aria.schedulers |
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry |
||||
import androidx.test.ext.junit.runners.AndroidJUnit4 |
||||
|
||||
import org.junit.Test |
||||
import org.junit.runner.RunWith |
||||
|
||||
import org.junit.Assert.* |
||||
|
||||
/** |
||||
* Instrumented test, which will execute on an Android device. |
||||
* |
||||
* See [testing documentation](http://d.android.com/tools/testing). |
||||
*/ |
||||
@RunWith(AndroidJUnit4::class) |
||||
class ExampleInstrumentedTest { |
||||
@Test |
||||
fun useAppContext() { |
||||
// Context of the app under test. |
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext |
||||
assertEquals("com.arialyy.aria.schedulers.test", appContext.packageName) |
||||
} |
||||
} |
@ -0,0 +1,4 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
|
||||
</manifest> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue