From 61ba26e58a593c4125c08ed86efc6cf425470679 Mon Sep 17 00:00:00 2001 From: laoyuyu <511455842@qq.com> Date: Mon, 13 Feb 2023 22:43:18 +0800 Subject: [PATCH] modify BlockManager.kt --- .../aria/http/download/HttpDEventListener.kt | 33 ++ .../http/download/HttpDStartController.kt | 6 +- .../aria/http/download/HttpDTaskUtil.kt | 18 +- .../http/download/HttpDThreadTaskAdapter.java | 249 ----------- .../aria/http/download/TimerInterceptor.kt | 9 +- .../com/arialyy/aria/m3u8/M3U8Listener.java | 4 +- .../arialyy/aria/core/inf/ITaskOption.java | 2 +- ...aseListener.java => AbsEventListener.java} | 48 +-- .../aria/core/listener/BaseDListener.java | 2 +- .../aria/core/listener/BaseUListener.java | 2 +- .../core/listener/DownloadGroupListener.java | 2 +- .../aria/core/listener/IEventListener.java | 3 - .../aria/core/manager/ThreadTaskManager.java | 265 ------------ .../com/arialyy/aria/core/task/AbsTask.java | 2 +- .../arialyy/aria/core/task/DBlockManager.kt | 5 +- .../arialyy/aria/core/task/DownloadTask.java | 1 - .../com/arialyy/aria/core/task/ThreadTask2.kt | 4 +- .../aria/core/task/ThreadTaskManager2.kt | 82 ++++ .../com/arialyy/aria/orm/dao/DEntityDao.kt | 6 +- .../java/com/arialyy/aria/util/FileUri.kt | 388 ++++++++++++++++++ .../java/com/arialyy/aria/util/FileUtils.kt | 18 + .../com/arialyy/aria/queue/AbsTaskQueue.java | 12 +- 22 files changed, 579 insertions(+), 582 deletions(-) create mode 100644 Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt delete mode 100644 Http/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java rename PublicComponent/src/main/java/com/arialyy/aria/core/listener/{BaseListener.java => AbsEventListener.java} (82%) delete mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTaskManager2.kt create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.kt diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt new file mode 100644 index 00000000..48a92123 --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDEventListener.kt @@ -0,0 +1,33 @@ +/* + * 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.AbsEventListener +import com.arialyy.aria.core.task.DownloadTask +import com.arialyy.aria.util.FileUtils + +class HttpDEventListener(task: DownloadTask) : AbsEventListener(task) { + + override fun handleCancel() { + + } + + override fun handleComplete() { + + } + + +} \ No newline at end of file diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt index 5c44a493..d098d256 100644 --- a/Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt @@ -123,8 +123,8 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta */ private suspend fun findDEntityBySavePath(option: HttpDTaskOption): DEntity { val savePath = option.savePathUri - val dao = DuaContext.getServiceManager().getDbService().getDuaDb()?.getDEntityDao() - val de = dao?.getDEntityBySavePath(savePath.toString()) + val dao = DuaContext.getServiceManager().getDbService().getDuaDb().getDEntityDao() + val de = dao.getDEntityBySavePath(savePath.toString()) if (de != null) { return de } @@ -132,7 +132,7 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta sourceUrl = option.sourUrl!!, savePath = savePath!!, ) - dao?.insert(newDe) + dao.insert(newDe) return newDe } diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt index 1ce94bdf..40ad7d49 100644 --- a/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt @@ -20,7 +20,9 @@ import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.inf.IBlockManager import com.arialyy.aria.core.task.AbsTaskUtil import com.arialyy.aria.core.task.BlockManager +import com.arialyy.aria.core.task.DownloadTask import com.arialyy.aria.core.task.TaskResp +import com.arialyy.aria.core.task.ThreadTaskManager2 import com.arialyy.aria.exception.AriaException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -33,6 +35,12 @@ import kotlinx.coroutines.launch internal class HttpDTaskUtil : AbsTaskUtil() { private var blockManager: BlockManager? = null + + init { + getTask().getTaskOption(HttpDTaskOption::class.java).eventListener = + HttpDEventListener(getTask() as DownloadTask) + } + override fun getBlockManager(): IBlockManager { if (blockManager == null) { blockManager = BlockManager(getTask()) @@ -45,11 +53,15 @@ internal class HttpDTaskUtil : AbsTaskUtil() { } override fun cancel() { - TODO("Not yet implemented") + DuaContext.duaScope.launch(Dispatchers.IO) { + ThreadTaskManager2.stopThreadTask(getTask().taskId, true) + } } override fun stop() { - blockManager?.stop() + DuaContext.duaScope.launch(Dispatchers.IO) { + ThreadTaskManager2.stopThreadTask(getTask().taskId) + } } override fun start() { @@ -67,7 +79,7 @@ internal class HttpDTaskUtil : AbsTaskUtil() { addCoreInterceptor(HttpBlockThreadInterceptor()) val resp = interceptor() if (resp == null || resp.code != TaskResp.CODE_SUCCESS) { - getTask().getTaskOption(HttpDTaskOption::class.java).taskListener.onFail( + getTask().getTaskOption(HttpDTaskOption::class.java).eventListener.onFail( false, AriaException("start task fail, task interrupt, code: ${resp?.code ?: TaskResp.CODE_INTERRUPT}") ) diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java b/Http/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java deleted file mode 100644 index 51bf30dc..00000000 --- a/Http/src/main/java/com/arialyy/aria/http/download/HttpDThreadTaskAdapter.java +++ /dev/null @@ -1,249 +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.http.download; - -import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.core.task.AbsThreadTaskAdapter; -import com.arialyy.aria.core.task.ThreadConfig; -import com.arialyy.aria.exception.AriaHTTPException; -import com.arialyy.aria.http.ConnectionHelp; -import com.arialyy.aria.http.HttpOption; -import com.arialyy.aria.http.request.IRequest; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.BufferedRandomAccessFile; -import java.io.BufferedInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URLEncoder; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; -import java.util.Map; -import java.util.Set; -import timber.log.Timber; - -/** - * Created by lyy on 2017/1/18. 下载线程 - */ -final class HttpDThreadTaskAdapter extends AbsThreadTaskAdapter { - - HttpDThreadTaskAdapter(ThreadConfig threadConfig) { - super(threadConfig); - } - - private HttpDTaskOption getTaskOption() { - return (HttpDTaskOption) getThreadConfig().getOption(); - } - - @Override protected void handlerThreadTask() { - HttpURLConnection conn = null; - BufferedInputStream is = null; - BufferedRandomAccessFile file = null; - try { - HttpDTaskOption taskOption = getTaskOption(); - HttpOption option = taskOption.getHttpOption(); - conn = IRequest.Companion.getRequest(option).getDConnection(taskOption.getSourUrl(), option); - if (!taskOption.isSupportResume()) { - Timber.w("this task not support resume, url: %s", taskOption.getSourUrl()); - }else { - conn.setRequestProperty("Range", - String.format("bytes=%s-%s", getThreadConfig().getBlockRecord().getStartLocation(), - (getThreadConfig().getBlockRecord().getEndLocation() - 1))); - } - - ConnectionHelp.setConnectParam(mTaskOption, conn); - conn.setConnectTimeout(getTaskConfig().getConnectTimeOut()); - conn.setReadTimeout(getTaskConfig().getIOTimeOut()); //设置读取流的等待时间,必须设置该参数 - if (mTaskOption.isChunked()) { - conn.setDoInput(true); - conn.setChunkedStreamingMode(0); - } - conn.connect(); - - is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); - if (mTaskOption.isChunked()) { - readChunked(is); - } else if (getThreadConfig().isBlock) { - readDynamicFile(is); - } else { - //创建可设置位置的文件 - file = - new BufferedRandomAccessFile(getThreadConfig().tempFile, "rwd", - getTaskConfig().getBuffSize()); - //设置每条线程写入文件的位置 - if (getThreadRecord().startLocation > 0) { - file.seek(getThreadRecord().startLocation); - } - readNormal(is, file); - handleComplete(); - } - } catch (MalformedURLException e) { - fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getFilePath(), getEntity().getUrl()), e), false); - } catch (IOException e) { - fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getFilePath(), getEntity().getUrl()), e), true); - } catch (ArrayIndexOutOfBoundsException e) { - fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getFilePath(), getEntity().getUrl()), e), false); - } catch (Exception e) { - fail(new AriaHTTPException(String.format("任务【%s】下载失败,filePath: %s, url: %s", getFileName(), - getEntity().getFilePath(), getEntity().getUrl()), e), false); - } finally { - try { - if (file != null) { - file.close(); - } - if (is != null) { - is.close(); - } - if (conn != null) { - conn.getInputStream().close(); - conn.disconnect(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /** - * 读取chunked数据 - */ - private void readChunked(InputStream is) { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(getThreadConfig().tempFile, true); - byte[] buffer = new byte[getTaskConfig().getBuffSize()]; - int len; - while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { - if (getThreadTask().isBreak()) { - break; - } - if (mSpeedBandUtil != null) { - mSpeedBandUtil.limitNextBytes(len); - } - fos.write(buffer, 0, len); - progress(len); - } - handleComplete(); - } catch (IOException e) { - fail(new AriaHTTPException( - String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), - getThreadConfig().url), e), true); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - /** - * 动态长度文件读取方式 - */ - private void readDynamicFile(InputStream is) { - FileOutputStream fos = null; - FileChannel foc = null; - ReadableByteChannel fic = null; - try { - int len; - fos = new FileOutputStream(getThreadConfig().tempFile, true); - foc = fos.getChannel(); - fic = Channels.newChannel(is); - - ByteBuffer bf = ByteBuffer.allocate(getTaskConfig().getBuffSize()); - //如果要通过 Future 的 cancel 方法取消正在运行的任务,那么该任务必定是可以 对线程中断做出响应 的任务。 - - while (getThreadTask().isLive() && (len = fic.read(bf)) != -1) { - if (getThreadTask().isBreak()) { - break; - } - if (mSpeedBandUtil != null) { - mSpeedBandUtil.limitNextBytes(len); - } - if (getRangeProgress() + len >= getThreadRecord().endLocation) { - len = (int) (getThreadRecord().endLocation - getRangeProgress()); - bf.flip(); - fos.write(bf.array(), 0, len); - bf.compact(); - progress(len); - break; - } else { - bf.flip(); - foc.write(bf); - bf.compact(); - progress(len); - } - } - handleComplete(); - } catch (IOException e) { - fail(new AriaHTTPException( - String.format("文件下载失败,savePath: %s, url: %s", getEntity().getFilePath(), - getThreadConfig().url), e), true); - } finally { - try { - if (fos != null) { - fos.flush(); - fos.close(); - } - if (foc != null) { - foc.close(); - } - if (fic != null) { - fic.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - /** - * 读取普通的文件流 - */ - private void readNormal(InputStream is, BufferedRandomAccessFile file) - throws IOException { - byte[] buffer = new byte[getTaskConfig().getBuffSize()]; - int len; - while (getThreadTask().isLive() && (len = is.read(buffer)) != -1) { - if (getThreadTask().isBreak()) { - break; - } - if (mSpeedBandUtil != null) { - mSpeedBandUtil.limitNextBytes(len); - } - file.write(buffer, 0, len); - progress(len); - } - } - - @Override public void cancel() { - - } - - @Override public void stop() { - - } -} diff --git a/Http/src/main/java/com/arialyy/aria/http/download/TimerInterceptor.kt b/Http/src/main/java/com/arialyy/aria/http/download/TimerInterceptor.kt index eb6acde9..6e050c90 100644 --- a/Http/src/main/java/com/arialyy/aria/http/download/TimerInterceptor.kt +++ b/Http/src/main/java/com/arialyy/aria/http/download/TimerInterceptor.kt @@ -15,9 +15,8 @@ */ package com.arialyy.aria.http.download -import com.arialyy.aria.core.inf.IBlockManager import com.arialyy.aria.core.inf.ITaskOption -import com.arialyy.aria.core.manager.ThreadTaskManager +import com.arialyy.aria.core.task.ThreadTaskManager2 import com.arialyy.aria.core.task.ITask import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.TaskChain @@ -61,12 +60,12 @@ open class TimerInterceptor : ITaskInterceptor { || blockManager.hasFailedBlock() || !isRunning(chain.getTask()) ) { - ThreadTaskManager.getInstance().removeTaskThread(chain.getTask().taskId) + ThreadTaskManager2.stopThreadTask(chain.getTask().taskId) closeTimer() return } if (chain.getTask().taskState.curProgress >= 0) { - chain.getTask().getTaskOption(ITaskOption::class.java).taskListener.onProgress( + chain.getTask().getTaskOption(ITaskOption::class.java).eventListener.onProgress( blockManager.currentProgress ) return @@ -86,6 +85,6 @@ open class TimerInterceptor : ITaskInterceptor { } @Synchronized fun isRunning(task: ITask): Boolean { - return ThreadTaskManager.getInstance().taskIsRunning(task.taskId) + return ThreadTaskManager2.taskIsRunning(task.taskId) } } \ No newline at end of file diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java index 9f2ce537..76ebbd42 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8Listener.java @@ -19,7 +19,7 @@ import android.os.Bundle; import android.os.Message; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.TaskSchedulerType; -import com.arialyy.aria.core.listener.BaseListener; +import com.arialyy.aria.core.listener.AbsEventListener; import com.arialyy.aria.core.listener.IDLoadListener; import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.task.DownloadTask; @@ -29,7 +29,7 @@ import com.arialyy.aria.util.DeleteM3u8Record; /** * 下载监听类 */ -public final class M3U8Listener extends BaseListener implements IDLoadListener { +public final class M3U8Listener extends AbsEventListener implements IDLoadListener { @Override public void onPostPre(long fileSize) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java index cf87ffd1..bc4863e4 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskOption.java @@ -23,6 +23,6 @@ import com.arialyy.aria.core.listener.IEventListener; */ public abstract class ITaskOption { - public IEventListener taskListener; + public IEventListener eventListener; public int threadNum; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java similarity index 82% rename from PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java index 0d285246..69acfd10 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/AbsEventListener.java @@ -20,37 +20,41 @@ import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.DuaContext; import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.TaskSchedulerType; -import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.task.ITask; +import com.arialyy.aria.core.task.TaskCachePool; import com.arialyy.aria.core.task.TaskState; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.AriaException; -import com.arialyy.aria.core.task.TaskCachePool; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ErrorHelp; import java.lang.ref.WeakReference; import timber.log.Timber; -public abstract class BaseListener implements IEventListener { +public abstract class AbsEventListener implements IEventListener { static final int RUN_SAVE_INTERVAL = 5 * 1000; //5s保存一次下载中的进度 protected Handler outHandler; private long mLastLen; //上一次发送长度 private boolean isFirst = true; - private ITask mTask; + private final ITask mTask; long mLastSaveTime; - private long mUpdateInterval; + private final long mUpdateInterval; + + /** + * 处理任务取消 + */ + protected abstract void handleCancel(); + + protected abstract void handleComplete(); - @Override public IEventListener setParams(ITask task) { + protected AbsEventListener(ITask task) { this.outHandler = DuaContext.INSTANCE.getServiceManager().getSchedulerHandler(); mTask = new WeakReference<>(task).get(); mUpdateInterval = AriaConfig.getInstance().getCConfig().getUpdateInterval(); mLastLen = task.getTaskState().getCurProgress(); mLastSaveTime = System.currentTimeMillis(); - return this; } - protected TASK getTask(Class clazz) { - return (TASK) mTask; + protected ITask getTask() { + return mTask; } @Override public void onPre() { @@ -93,7 +97,7 @@ public abstract class BaseListener implements IEventListener { } @Override public void onComplete() { - saveData(IEntity.STATE_COMPLETE, mEntity.getFileSize()); + saveData(IEntity.STATE_COMPLETE, mTask.getTaskState().getFileSize()); handleSpeed(0); sendInState2Target(ISchedulers.COMPLETE); } @@ -128,30 +132,8 @@ public abstract class BaseListener implements IEventListener { speed = speed * 1000 / mUpdateInterval; } mTask.getTaskState().setSpeed(speed); - - int taskType = mTaskWrapper.getRequestType(); - if (taskType != ITaskWrapper.M3U8_VOD && taskType != ITaskWrapper.M3U8_LIVE) { - mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0 - : mEntity.getCurrentProgress() * 100 / mEntity.getFileSize())); - } } - /** - * 处理任务完成后的情况 - */ - private void handleComplete() { - mEntity.setComplete(true); - mEntity.setCompleteTime(System.currentTimeMillis()); - mEntity.setCurrentProgress(mEntity.getFileSize()); - mEntity.setPercent(100); - handleSpeed(0); - } - - /** - * 处理任务取消 - */ - protected abstract void handleCancel(); - /** * 将任务状态发送给下载器 * diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java index 862247b3..e7e28a3b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseDListener.java @@ -24,7 +24,7 @@ import com.arialyy.aria.util.DeleteDRecord; /** * 下载监听类 */ -public class BaseDListener extends BaseListener implements IDLoadListener { +public class BaseDListener extends AbsEventListener implements IDLoadListener { @Override public void onPostPre(long fileSize) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java index 0b16ecfa..d1f23c90 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseUListener.java @@ -23,7 +23,7 @@ import com.arialyy.aria.util.DeleteURecord; /** * 下载监听类 */ -public class BaseUListener extends BaseListener implements IUploadListener { +public class BaseUListener extends AbsEventListener implements IUploadListener { @Override protected void handleCancel() { int sType = getTask(UploadTask.class).getSchedulerType(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java index 12beb071..3d07df62 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/DownloadGroupListener.java @@ -34,7 +34,7 @@ import static com.arialyy.aria.core.task.AbsTask.ERROR_INFO_KEY; /** * Created by Aria.Lao on 2017/7/20. 任务组下载事件 */ -public class DownloadGroupListener extends BaseListener implements IDGroupListener { +public class DownloadGroupListener extends AbsEventListener implements IDGroupListener { private GroupSendParams mSeedEntity; @Override public IEventListener setParams(AbsTask task, Handler outHandler) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java index 18f8847f..2b84a233 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/IEventListener.java @@ -15,7 +15,6 @@ */ package com.arialyy.aria.core.listener; -import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.exception.AriaException; /** @@ -24,8 +23,6 @@ import com.arialyy.aria.exception.AriaException; */ public interface IEventListener { - IEventListener setParams(ITask task); - /** * 预处理,有时有些地址链接比较慢,这时可以先在这个地方出来一些界面上的UI,如按钮的状态 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java deleted file mode 100644 index 84e19802..00000000 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/manager/ThreadTaskManager.java +++ /dev/null @@ -1,265 +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 android.text.TextUtils; -import com.arialyy.aria.core.task.ITask; -import com.arialyy.aria.core.task.IThreadTask; -import com.arialyy.aria.util.ALog; -import com.arialyy.aria.util.CommonUtil; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Future; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - -/** - * 线程任务管理器 - */ -@Deprecated -public class ThreadTaskManager1 { - private final String TAG = CommonUtil.getClassName(this); - private static volatile ThreadTaskManager INSTANCE = null; - private static final int CORE_POOL_NUM = 20; - private static final ReentrantLock LOCK = new ReentrantLock(); - private final ThreadPoolExecutor mExePool; - private final Map> mThreadTasks = new ConcurrentHashMap<>(); - - public static synchronized ThreadTaskManager getInstance() { - if (INSTANCE == null) { - INSTANCE = new ThreadTaskManager(); - } - return INSTANCE; - } - - private ThreadTaskManager() { - mExePool = new ThreadPoolExecutor(CORE_POOL_NUM, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue()); - mExePool.allowsCoreThreadTimeOut(); - } - - /** - * 删除所有线程任务 - */ - public void removeAllThreadTask() { - if (mThreadTasks.isEmpty()) { - return; - } - try { - LOCK.tryLock(2, TimeUnit.SECONDS); - for (Set threads : mThreadTasks.values()) { - for (FutureContainer container : threads) { - if (container.future.isDone() || container.future.isCancelled()) { - continue; - } - container.threadTask.destroy(); - } - threads.clear(); - } - mThreadTasks.clear(); - } catch (InterruptedException e) { - e.printStackTrace(); - } finally { - LOCK.unlock(); - } - } - - /** - * 启动线程任务 - * - * @param taskId {@link ITask#getTaskId()} - * @param threadTask 线程任务{@link IThreadTask} - */ - public void startThread(Integer taskId, IThreadTask threadTask) { - try { - LOCK.tryLock(2, TimeUnit.SECONDS); - if (mExePool.isShutdown()) { - ALog.e(TAG, "线程池已经关闭"); - return; - } - Set temp = mThreadTasks.get(taskId); - if (temp == null) { - temp = new HashSet<>(); - mThreadTasks.put(taskId, temp); - } - FutureContainer container = new FutureContainer(); - container.threadTask = threadTask; - container.future = mExePool.submit(threadTask); - temp.add(container); - } catch (Exception e) { - e.printStackTrace(); - } finally { - LOCK.unlock(); - } - } - - /** - * 任务是否在执行 - * - * @return {@code true} 任务正在运行 - */ - public boolean taskIsRunning(Integer taskId) { - return mThreadTasks.get(taskId) != null; - } - - /** - * 停止任务的所有线程 - */ - public void removeTaskThread(Integer taskId) { - try { - LOCK.tryLock(2, TimeUnit.SECONDS); - if (mExePool.isShutdown()) { - ALog.e(TAG, "线程池已经关闭"); - return; - } - Set temp = mThreadTasks.get(taskId); - if (temp != null && temp.size() > 0) { - for (FutureContainer container : temp) { - if (container.future.isDone() || container.future.isCancelled()) { - continue; - } - container.threadTask.destroy(); - } - temp.clear(); - mThreadTasks.remove(taskId); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - LOCK.unlock(); - } - } - - /** - * 根据线程名删除任务的中的线程 - * - * @param threadName 线程名 - * @return true 删除线程成功;false 删除线程失败 - */ - public boolean removeSingleTaskThread(Integer taskId, String threadName) { - try { - LOCK.tryLock(2, TimeUnit.SECONDS); - if (mExePool.isShutdown()) { - ALog.e(TAG, "线程池已经关闭"); - return false; - } - if (TextUtils.isEmpty(threadName)) { - ALog.e(TAG, "线程名为空"); - return false; - } - - Set temp = mThreadTasks.get(taskId); - if (temp != null && temp.size() > 0) { - FutureContainer tempC = null; - for (FutureContainer container : temp) { - if (container.threadTask.getThreadName().equals(threadName)) { - tempC = container; - break; - } - } - if (tempC != null) { - tempC.threadTask.destroy(); - temp.remove(tempC); - return true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - LOCK.unlock(); - } - return false; - } - - /** - * 删除单个线程任务 - * - * @param task 线程任务 - */ - public boolean removeSingleTaskThread(Integer taskId, IThreadTask task) { - try { - LOCK.tryLock(2, TimeUnit.SECONDS); - if (mExePool.isShutdown()) { - ALog.e(TAG, "线程池已经关闭"); - return false; - } - if (task == null) { - ALog.e(TAG, "线程任务为空"); - return false; - } - Set temp = mThreadTasks.get(taskId); - if (temp != null && temp.size() > 0) { - FutureContainer tempC = null; - for (FutureContainer container : temp) { - if (container.threadTask == task) { - tempC = container; - break; - } - } - if (tempC != null) { - task.destroy(); - temp.remove(tempC); - return true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - LOCK.unlock(); - } - return false; - } - - /** - * 重试线程任务 - * - * @param task 线程任务 - */ - public void retryThread(IThreadTask task) { - try { - LOCK.tryLock(2, TimeUnit.SECONDS); - if (mExePool.isShutdown()) { - ALog.e(TAG, "线程池已经关闭"); - return; - } - try { - if (task == null || task.isDestroy()) { - ALog.e(TAG, "线程为空或线程已经中断"); - return; - } - } catch (Exception e) { - ALog.e(TAG, "", e); - return; - } - mExePool.submit(task); - } catch (Exception e) { - e.printStackTrace(); - } finally { - LOCK.unlock(); - } - } - - private static class FutureContainer { - Future future; - IThreadTask threadTask; - } -} diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java index 5d966651..9e7c62c8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTask.java @@ -45,7 +45,7 @@ public abstract class AbsTask implements ITask { mUtil = util; taskId = TaskStatePool.INSTANCE.buildTaskId$PublicComponent_debug(); TaskStatePool.INSTANCE.putTaskState(getTaskId(), mTaskState); - util.init(this, taskOption.taskListener); + util.init(this, taskOption.eventListener); } @Override public void setState(int state) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/DBlockManager.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DBlockManager.kt index 47f462ec..8cd756c1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/DBlockManager.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DBlockManager.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import timber.log.Timber import java.util.concurrent.LinkedBlockingQueue -import java.util.concurrent.ThreadFactory import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit.MILLISECONDS import java.util.concurrent.atomic.AtomicInteger @@ -55,19 +54,21 @@ class BlockManager(task: ITask) : IBlockManager { private lateinit var handler: Handler private var blockNum: Int = 1 private var eventListener: IEventListener = - task.getTaskOption(ITaskOption::class.java).taskListener + task.getTaskOption(ITaskOption::class.java).eventListener private val callback = Callback { msg -> when (msg.what) { IBlockManager.STATE_STOP -> { stoppedNum.getAndIncrement() if (isStopped) { + eventListener.onStop(currentProgress) quitLooper() } } IBlockManager.STATE_CANCEL -> { canceledNum.getAndIncrement() if (isCanceled) { + eventListener.onCancel() quitLooper() } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java index bd1acfea..e6c23723 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadTask.java @@ -29,7 +29,6 @@ public class DownloadTask extends AbsTask { public DownloadTask(DTaskOption taskOption, ITaskUtil util) { super(taskOption, util); - taskOption.taskListener.setParams(this); } public Uri getSavePath() { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask2.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask2.kt index c1040fb1..5fac7ae0 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask2.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask2.kt @@ -51,13 +51,13 @@ class ThreadTask2( } override fun cancel() { - adapter.cancel() + adapter.breakTask() isCanceled = true handler.obtainMessage(IBlockManager.STATE_CANCEL) } override fun stop() { - adapter.stop() + adapter.breakTask() isStopped = true handler.obtainMessage(IBlockManager.STATE_STOP) } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTaskManager2.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTaskManager2.kt new file mode 100644 index 00000000..297f6c83 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTaskManager2.kt @@ -0,0 +1,82 @@ +/* + * 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.ConcurrentHashMap +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.locks.ReentrantLock + +object ThreadTaskManager2 { + private val mThreadTasks: ConcurrentHashMap> = ConcurrentHashMap() + private val LOCK = ReentrantLock() + + /** + * 任务是否在执行 + * + * @return `true` 任务正在运行 + */ + fun taskIsRunning(taskId: Int): Boolean { + return mThreadTasks[taskId] != null + } + + /** + * stop thread task + * @param isRemoveTask if true, remove task and block + */ + fun stopThreadTask(taskId: Int, isRemoveTask: Boolean = false) { + try { + LOCK.tryLock(2, SECONDS) + val threadTaskList: MutableSet? = mThreadTasks[taskId] + threadTaskList?.forEach { + if (isRemoveTask) { + it.cancel() + return@forEach + } + it.stop() + } + mThreadTasks.remove(taskId) + + } catch (e: Exception) { + e.printStackTrace() + } finally { + LOCK.unlock() + } + } + + /** + * 删除所有线程任务 + */ + fun removeAllThreadTask() { + if (mThreadTasks.isEmpty()) { + return + } + try { + LOCK.tryLock(2, SECONDS) + for (threads in mThreadTasks.values) { + for (tt in threads) { + tt.stop() + } + threads.clear() + } + mThreadTasks.clear() + } catch (e: InterruptedException) { + e.printStackTrace() + } finally { + LOCK.unlock() + } + } + +} \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DEntityDao.kt b/PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DEntityDao.kt index 73caf5db..29404438 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DEntityDao.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DEntityDao.kt @@ -36,13 +36,13 @@ interface DEntityDao { suspend fun getDEntityList(): List @Query("SELECT * FROM DEntity WHERE :savePath=savePath") - suspend fun getDEntityBySavePath(savePath: String): DEntity + suspend fun getDEntityBySavePath(savePath: String): DEntity? @Query("SELECT * FROM DEntity WHERE :dId=dId") - suspend fun getDEntityById(did: String): DEntity + suspend fun getDEntityById(did: String): DEntity? @Query("SELECT * FROM DEntity WHERE :sourceUrl=sourceUrl") - suspend fun getDEntityBySource(sourceUrl: String): DEntity + suspend fun getDEntityBySource(sourceUrl: String): DEntity? @Insert suspend fun insert(dEntity: DEntity) diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.kt b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.kt new file mode 100644 index 00000000..c0731f5d --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUri.kt @@ -0,0 +1,388 @@ +package com.arialyy.aria.util + +import android.annotation.SuppressLint +import android.content.* +import android.database.Cursor +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.* +import androidx.core.content.FileProvider +import com.arialyy.aria.core.DuaContext +import timber.log.Timber +import java.io.* + +/** + * # FileUri + * + * - Uri & Path Tool + * + * @author javakam + * @date 2020/8/24 11:24 + */ +object FileUri { + + //Android R + //---------------------------------------------------------------- + + /** + * `MANAGE_EXTERNAL_STORAGE` 权限检查 + * + * @return `true` Have permission + */ + fun isExternalStorageManager(): Boolean = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) Environment.isExternalStorageManager() else false + + /** + * 跳转到 `MANAGE_EXTERNAL_STORAGE` 权限设置页面 + * + * @return `true` Has been set + */ + fun jumpManageAppAllFilesPermissionSetting( + context: Context, + isNewTask: Boolean = false, + ): Boolean { + if (isExternalStorageManager()) return true + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + try { + val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + intent.data = Uri.parse("package:${context.packageName}") + if (isNewTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } catch (e: Exception) { + Timber.e("jumpManageAppAllFilesPermissionSetting: $e") + } + } + return false + } + + //从 FilePath 中获取 Uri (Get Uri from FilePath) + //---------------------------------------------------------------- + + fun getUriByPath(path: String?): Uri? = + if (path.isNullOrBlank()) null else getUriByFile(File(path)) + + /** + * Return a content URI for a given file. + * + * @param file The file. + * @param isOriginal true content:// or file:// ; false file://xxx + * @return a content URI for a given file + */ + fun getUriByFile(file: File?, isOriginal: Boolean = false): Uri? { + return if (isOriginal) Uri.fromFile(file) + else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val authority = DuaContext.context.packageName + AUTHORITY + FileProvider.getUriForFile(DuaContext.context, authority, file ?: return null) + } else { + Uri.fromFile(file) + } + } + } + + fun getShareUri(path: String?): Uri? = + if (path.isNullOrBlank()) null else getUriByFile(File(path), isOriginal = false) + + /** + * @return content:// or file:// + */ + fun getShareUri(file: File?): Uri? = getUriByFile(file, isOriginal = false) + + fun getOriginalUri(path: String?): Uri? = + if (path.isNullOrBlank()) null else getUriByFile(File(path), isOriginal = true) + + /** + * @return file://xxx + */ + fun getOriginalUri(file: File?): Uri? = getUriByFile(file, isOriginal = true) + + //获取Uri对应的文件路径, Compatible with API 26 + //---------------------------------------------------------------- + + /** + * ### Get the file path through Uri + * + * - Need permission: RequiresPermission(permission.READ_EXTERNAL_STORAGE) + * + * - Modified from: https://github.com/coltoscosmin/FileUtils/blob/master/FileUtils.java + * + * @return file path + */ + fun getPathByUri(uri: Uri?): String? { + return uri?.use { + Timber.i( + "FileUri getPathByUri -> " + + "Uri: " + uri + + ", Authority: " + uri.authority + + ", Fragment: " + uri.fragment + + ", Port: " + uri.port + + ", Query: " + uri.query + + ", Scheme: " + uri.scheme + + ", Host: " + uri.host + + ", Segments: " + uri.pathSegments.toString() + ) + + // 以 file:// 开头的使用第三方应用打开 (open with third-party applications starting with file://) + if (ContentResolver.SCHEME_FILE.equals(uri.scheme, ignoreCase = true)) return getDataColumn( + uri + ) + + @SuppressLint("ObsoleteSdkInt") + val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT + + // Before 4.4 , API 19 content:// 开头, 比如 content://media/external/images/media/123 + if (!isKitKat && ContentResolver.SCHEME_CONTENT.equals(uri.scheme, true)) { + if (isGooglePhotosUri(uri)) return uri.lastPathSegment + return getDataColumn(uri) + } + + val context = DuaContext.context + // After 4.4 , API 19 + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // LocalStorageProvider + if (isLocalStorageDocument(uri)) { + // The path is the id + return DocumentsContract.getDocumentId(uri); + } + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + val docId = DocumentsContract.getDocumentId(uri) + val split = docId.split(":").toTypedArray() + val type = split[0] + if ("primary".equals(type, ignoreCase = true)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) + .toString() + File.separator + split[1] + } else { + @Suppress("DEPRECATION") + return Environment.getExternalStorageDirectory() + .toString() + File.separator + split[1] + } + } else if ("home".equals(type, ignoreCase = true)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) + .toString() + File.separator + "documents" + File.separator + split[1] + } else { + @Suppress("DEPRECATION") + return Environment.getExternalStorageDirectory() + .toString() + File.separator + "documents" + File.separator + split[1] + } + } else { + @Suppress("DEPRECATION") + val sdcardPath = + Environment.getExternalStorageDirectory() + .toString() + File.separator + "documents" + File.separator + split[1] + return if (sdcardPath.startsWith("file://")) { + sdcardPath.replace("file://", "") + } else { + sdcardPath + } + } + } + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + val id = DocumentsContract.getDocumentId(uri) + if (id != null && id.startsWith("raw:")) { + return id.substring(4) + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + val contentUriPrefixesToTry = arrayOf( + "content://downloads/public_downloads", + "content://downloads/my_downloads", + "content://downloads/all_downloads" + ) + for (contentUriPrefix in contentUriPrefixesToTry) { + val contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), id.toLong()) + try { + val path = getDataColumn(contentUri) + if (!path.isNullOrBlank()) return path + } catch (e: Exception) { + Timber.e(e.toString()) + } + } + } else { + //testPath(uri) + return getDataColumn(uri) + } + } + // MediaProvider + else if (isMediaDocument(uri)) { + val docId = DocumentsContract.getDocumentId(uri) + val split = docId.split(":").toTypedArray() + val contentUri: Uri? = when (split[0]) { + "image" -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "video" -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI + "audio" -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "download" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + MediaStore.Downloads.EXTERNAL_CONTENT_URI + } else null + else -> null + } + val selectionArgs = arrayOf(split[1]) + return getDataColumn(contentUri, "_id=?", selectionArgs) + } + + //GoogleDriveProvider + else if (isGoogleDriveUri(uri)) { + return getGoogleDriveFilePath(uri, context) + } + } + // MediaStore (and general) + else if ("content".equals(uri.scheme, ignoreCase = true)) { + // Return the remote address + if (isGooglePhotosUri(uri)) { + return uri.lastPathSegment + } + // Google drive legacy provider + else if (isGoogleDriveUri(uri)) { + return getGoogleDriveFilePath(uri, context) + } + // Huawei + else if (isHuaWeiUri(uri)) { + val uriPath = getDataColumn(uri) ?: uri.toString() + //content://com.huawei.hidisk.fileprovider/root/storage/emulated/0/Android/data/com.xxx.xxx/ + if (uriPath.startsWith("/root")) { + return uriPath.replace("/root".toRegex(), "") + } + } + return getDataColumn(uri) + } + return getDataColumn(uri) + } + } + + /** + * BUG : 部分机型进入"文件管理器" 执行到 cursor.getColumnIndexOrThrow(column);出现 + * Caused by: java.lang.IllegalArgumentException: column '_data' does not exist. Available columns: [] + * + * Fixed : + * https://stackoverflow.com/questions/42508383/illegalargumentexception-column-data-does-not-exist + * + */ + private fun getDataColumn( + uri: Uri?, + selection: String? = null, + selectionArgs: Array? = null + ): String? { + @Suppress("DEPRECATION") + val column = MediaStore.Files.FileColumns.DATA + val projection = arrayOf(column) + try { + DuaContext.context.contentResolver.query( + uri ?: return null, + projection, + selection, + selectionArgs, + null + )?.use { c: Cursor -> + if (c.moveToFirst()) { + val columnIndex = c.getColumnIndex(column) + return c.getString(columnIndex) + } + } + } catch (e: Throwable) { + Timber.e("getDataColumn -> ${e.message}") + } + return null + } + + //The Uri to check + //---------------------------------------------------------------- + + private fun getGoogleDriveFilePath(uri: Uri, context: Context): String? { + context.contentResolver.query(uri, null, null, null, null)?.use { c: Cursor -> + /* + Get the column indexes of the data in the Cursor, + move to the first row in the Cursor, get the data, and display it. + */ + val nameIndex: Int = c.getColumnIndex(OpenableColumns.DISPLAY_NAME) + //val sizeIndex: Int = c.getColumnIndex(OpenableColumns.SIZE) + if (!c.moveToFirst()) { + return uri.toString() + } + val name: String = c.getString(nameIndex) + //val size = c.getLong(sizeIndex).toString() + val file = File(context.cacheDir, name) + + var inputStream: InputStream? = null + var outputStream: FileOutputStream? = null + try { + inputStream = context.contentResolver.openInputStream(uri) + outputStream = FileOutputStream(file) + var read = 0 + val maxBufferSize = 1 * 1024 * 1024 + val bytesAvailable: Int = inputStream?.available() ?: 0 + val bufferSize = bytesAvailable.coerceAtMost(maxBufferSize) + val buffers = ByteArray(bufferSize) + while (inputStream?.read(buffers)?.also { read = it } != -1) { + outputStream.write(buffers, 0, read) + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + inputStream?.close() + outputStream?.close() + } + return file.path + } + return uri.toString() + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is Google Photos. + */ + fun isGooglePhotosUri(uri: Uri?): Boolean { + return "com.google.android.apps.photos.content".equals(uri?.authority, true) + } + + fun isGoogleDriveUri(uri: Uri?): Boolean { + return "com.google.android.apps.docs.storage.legacy" == uri?.authority || "com.google.android.apps.docs.storage" == uri?.authority + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is local. + */ + fun isLocalStorageDocument(uri: Uri?): Boolean { + return AUTHORITY.equals(uri?.authority, true) + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + private fun isExternalStorageDocument(uri: Uri?): Boolean { + return "com.android.externalstorage.documents".equals(uri?.authority, true) + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + private fun isDownloadsDocument(uri: Uri?): Boolean { + return "com.android.providers.downloads.documents".equals(uri?.authority, true) + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + private fun isMediaDocument(uri: Uri?): Boolean { + return "com.android.providers.media.documents".equals(uri?.authority, true) + } + + /** + * content://com.huawei.hidisk.fileprovider/root/storage/emulated/0/Android/data/com.xxx.xxx/ + * + * @param uri + * @return + */ + private fun isHuaWeiUri(uri: Uri?): Boolean { + return "com.huawei.hidisk.fileprovider".equals(uri?.authority, true) + } + +} \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt index 97cd2cc4..d8e8b27f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt @@ -20,7 +20,9 @@ import android.database.Cursor import android.net.Uri import android.provider.OpenableColumns import com.arialyy.aria.core.DuaContext +import com.arialyy.aria.orm.entity.BlockRecord import timber.log.Timber +import java.io.File import java.io.InputStream import java.util.Locale @@ -32,6 +34,22 @@ object FileUtils { RegexOption.IGNORE_CASE ) + fun mergeBlock(blockRecordList: List, targetPath: Uri): Boolean { + if (blockRecordList.isEmpty()) { + Timber.e("block record list empty") + return false + } + if (!uriEffective(targetPath)) { + Timber.e("invalid uri: $targetPath") + return false + } + val fileList = arrayListOf() + blockRecordList.forEach { + fileList.add(File(it.blockPath)) + } + return FileUtil.mergeFile() + } + /** * check if url is correct */ diff --git a/Queue/src/main/java/com/arialyy/aria/queue/AbsTaskQueue.java b/Queue/src/main/java/com/arialyy/aria/queue/AbsTaskQueue.java index db22c311..92f43a9d 100644 --- a/Queue/src/main/java/com/arialyy/aria/queue/AbsTaskQueue.java +++ b/Queue/src/main/java/com/arialyy/aria/queue/AbsTaskQueue.java @@ -20,7 +20,7 @@ import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IPool; 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.ThreadTaskManager2; import com.arialyy.aria.core.task.ITask; import timber.log.Timber; @@ -68,8 +68,8 @@ public abstract class AbsTaskQueue implements ITaskQueue implements ITaskQueue implements ITaskQueue