From 3fd103d616ddc482c0cffeffbb8b2bae20a45ab0 Mon Sep 17 00:00:00 2001 From: laoyuyu Date: Sun, 29 Jan 2023 18:15:02 +0800 Subject: [PATCH] get file info reconstruct --- ...il => com.arialyy.aria.core.inf.ITaskUtil} | 0 .../com/arialyy/aria/http/ConnectionHelp.java | 28 ++- .../java/com/arialyy/aria/http/HttpOption.kt | 74 ++++++- .../java/com/arialyy/aria/http/HttpUtil.kt | 48 +++++ .../http/download/HttpDStartController.kt | 30 ++- .../aria/http/download/HttpDTaskOption.kt | 2 + .../aria/http/download/HttpDTaskUtil.kt | 31 +++ .../http/download/HttpHeaderInterceptor.kt | 203 ++++++++++++++++++ .../arialyy/aria/http/request/GetRequest.kt | 59 +++++ .../arialyy/aria/http/request/HeadRequest.kt | 59 +++++ .../com/arialyy/aria/http/request/IRequest.kt | 152 +++++++++++++ .../arialyy/aria/http/request/PostRequest.kt | 74 +++++++ ...il => com.arialyy.aria.core.inf.ITaskUtil} | 0 ...il => com.arialyy.aria.core.inf.ITaskUtil} | 0 .../arialyy/aria/core/common/RequestEnum.java | 3 +- .../arialyy/aria/core/download/DTaskOption.kt | 1 + .../aria/core/group/AbsGroupLoaderUtil.java | 7 +- .../aria/core/group/AbsSubDLoadUtil.java | 6 +- .../arialyy/aria/core/group/ISubQueue.java | 4 +- .../core/inf/{IUtil.java => ITaskUtil.java} | 28 +-- .../aria/core/listener/BaseListener.java | 4 +- .../aria/core/loader/AbsNormalLoaderUtil.java | 7 +- .../arialyy/aria/core/task/AbsGroupTask.java | 8 +- .../com/arialyy/aria/core/task/AbsTask.java | 32 ++- .../com/arialyy/aria/core/task/AbsTaskUtil.kt | 63 ++++++ .../arialyy/aria/core/task/DownloadTask.java | 5 +- .../aria/core/task/ITaskInterceptor.kt | 31 +++ .../task/TaskCachePool.kt} | 26 ++- .../com/arialyy/aria/core/task/TaskChain.kt | 38 ++++ .../com/arialyy/aria/core/task/TaskResp.kt | 17 ++ .../com/arialyy/aria/util/ComponentUtil.java | 4 +- ...il => com.arialyy.aria.core.inf.ITaskUtil} | 0 32 files changed, 958 insertions(+), 86 deletions(-) rename FtpComponent/src/main/resources/META-INF/services/{com.arialyy.aria.core.inf.IUtil => com.arialyy.aria.core.inf.ITaskUtil} (100%) create mode 100644 Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt create mode 100644 Http/src/main/java/com/arialyy/aria/http/download/HttpHeaderInterceptor.kt create mode 100644 Http/src/main/java/com/arialyy/aria/http/request/GetRequest.kt create mode 100644 Http/src/main/java/com/arialyy/aria/http/request/HeadRequest.kt create mode 100644 Http/src/main/java/com/arialyy/aria/http/request/IRequest.kt create mode 100644 Http/src/main/java/com/arialyy/aria/http/request/PostRequest.kt rename Http/src/main/resources/META-INF/services/{com.arialyy.aria.core.inf.IUtil => com.arialyy.aria.core.inf.ITaskUtil} (100%) rename M3U8Component/src/main/resources/META-INF/services/{com.arialyy.aria.core.inf.IUtil => com.arialyy.aria.core.inf.ITaskUtil} (100%) rename PublicComponent/src/main/java/com/arialyy/aria/core/inf/{IUtil.java => ITaskUtil.java} (62%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTaskUtil.kt create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/ITaskInterceptor.kt rename PublicComponent/src/main/java/com/arialyy/aria/{orm/EntityCachePool.kt => core/task/TaskCachePool.kt} (72%) create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskChain.kt create mode 100644 PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskResp.kt rename SFtpComponent/src/main/resources/META-INF/services/{com.arialyy.aria.core.inf.IUtil => com.arialyy.aria.core.inf.ITaskUtil} (100%) diff --git a/FtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil b/FtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil similarity index 100% rename from FtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil rename to FtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil diff --git a/Http/src/main/java/com/arialyy/aria/http/ConnectionHelp.java b/Http/src/main/java/com/arialyy/aria/http/ConnectionHelp.java index f092c915..dd3da4b4 100644 --- a/Http/src/main/java/com/arialyy/aria/http/ConnectionHelp.java +++ b/Http/src/main/java/com/arialyy/aria/http/ConnectionHelp.java @@ -19,7 +19,6 @@ import android.text.TextUtils; import com.arialyy.aria.core.AriaConfig; import com.arialyy.aria.core.ProtocolType; import com.arialyy.aria.core.common.RequestEnum; -import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.SSLContextUtil; import java.io.IOException; @@ -28,7 +27,6 @@ import java.net.CookieManager; import java.net.CookieStore; import java.net.HttpURLConnection; import java.net.MalformedURLException; -import java.net.ProtocolException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; @@ -49,25 +47,33 @@ public final class ConnectionHelp { /** * 处理url参数 */ - public static URL handleUrl(String url, HttpTaskOption taskDelegate) + public static URL handleUrl(String url, HttpOption option) throws MalformedURLException { - Map params = taskDelegate.getParams(); - if (params != null && taskDelegate.getRequestEnum() == RequestEnum.GET) { - if (url.contains("?")) { - ALog.e(TAG, String.format("设置参数失败,url中已经有?,url: %s", url)); - return new URL(CommonUtil.convertUrl(url)); + Map params = option.getParams(); + if (params.isEmpty()) { + return new URL(CommonUtil.convertUrl(url)); + } + + if (option.getRequestMethod() == RequestEnum.GET) { + if (!url.contains("?")) { + url = url.concat("?"); } StringBuilder sb = new StringBuilder(); sb.append(url).append("?"); Set keys = params.keySet(); for (String key : keys) { - sb.append(key).append("=").append(URLEncoder.encode(params.get(key))).append("&"); + sb.append(URLEncoder.encode(key)) + .append("=") + .append(URLEncoder.encode(params.get(key))) + .append("&"); } String temp = sb.toString(); temp = temp.substring(0, temp.length() - 1); return new URL(CommonUtil.convertUrl(temp)); - } else { - return new URL(CommonUtil.convertUrl(url)); + } + + if (option.getRequestMethod() == RequestEnum.POST) { + } } diff --git a/Http/src/main/java/com/arialyy/aria/http/HttpOption.kt b/Http/src/main/java/com/arialyy/aria/http/HttpOption.kt index 26418fbd..cffe788e 100644 --- a/Http/src/main/java/com/arialyy/aria/http/HttpOption.kt +++ b/Http/src/main/java/com/arialyy/aria/http/HttpOption.kt @@ -15,13 +15,85 @@ */ package com.arialyy.aria.http +import com.arialyy.aria.core.common.RequestEnum +import java.net.CookieManager +import java.net.Proxy + /** * @Author laoyuyu * @Description * @Date 14:27 AM 2023/1/20 **/ class HttpOption { + private val params = hashMapOf() + private val headers = hashMapOf() + private var requestEnum = RequestEnum.GET + private var proxy: Proxy? = null + private var cookieManager: CookieManager? = null + private var body: String? = null + private var bodyBinary: ByteArray? = null + + fun setProxy(proxy: Proxy): HttpOption { + this.proxy = proxy + return this + } + + fun getProxy() = proxy + + /** + * set http request key + */ + fun setParas(key: String, value: String): HttpOption { + params[key] = value + return this + } + + fun setParams(params: Map): HttpOption { + this.params.putAll(params) + return this + } + + fun getParams() = params + + fun getCookieManager(): CookieManager? { + return cookieManager + } + + fun setBody(body: String): HttpOption { + this.body = body + return this + } + + fun getBody() = body + + fun setBodyBinary(body: ByteArray): HttpOption { + this.bodyBinary = body + return this + } + + fun getBodyBinary() = bodyBinary + + fun setCookieManager(cookieManager: CookieManager): HttpOption { + this.cookieManager = cookieManager + return this + } + + fun setHeaders(headers: Map): HttpOption { + this.headers.putAll(headers) + return this + } + + fun getHeaders() = headers - // fun se + /** + * Attempts to get file information before downloading a file + * by default, the get method is used to get + * if the server supports head requests, please set true + */ + fun setRequestMethod(requestEnum: RequestEnum): HttpOption { + this.requestEnum = requestEnum + return this + } + fun getRequestMethod() = requestEnum } diff --git a/Http/src/main/java/com/arialyy/aria/http/HttpUtil.kt b/Http/src/main/java/com/arialyy/aria/http/HttpUtil.kt index 987afbd1..8202a84b 100644 --- a/Http/src/main/java/com/arialyy/aria/http/HttpUtil.kt +++ b/Http/src/main/java/com/arialyy/aria/http/HttpUtil.kt @@ -15,9 +15,17 @@ */ package com.arialyy.aria.http +import android.text.TextUtils import com.arialyy.aria.http.download.HttpDTaskOption import com.arialyy.aria.util.CheckUtil +import com.arialyy.aria.util.Regular import timber.log.Timber +import java.io.IOException +import java.io.InputStream +import java.net.HttpURLConnection +import java.util.regex.Pattern +import java.util.zip.GZIPInputStream +import java.util.zip.InflaterInputStream /** * @Author laoyuyu @@ -25,6 +33,46 @@ import timber.log.Timber * @Date 12:40 PM 2023/1/22 **/ internal object HttpUtil { + /** + * 拦截window.location.replace数据 + * + * @return 重定向url + */ + fun getWindowReplaceUrl(text: String?): String? { + if (text.isNullOrEmpty()) { + Timber.e("text is null") + return null + } + val reg = Regular.REG_WINLOD_REPLACE + val p = Pattern.compile(reg) + val m = p.matcher(text) + if (m.find()) { + var s = m.group() + s = s.substring(9, s.length - 2) + return s + } + return null + } + + /** + * 转换HttpUrlConnect的inputStream流 + * + * @return [GZIPInputStream]、[InflaterInputStream] + * @throws IOException + */ + @Throws(IOException::class) fun convertInputStream(connection: HttpURLConnection): InputStream? { + val encoding = connection.getHeaderField("Content-Encoding") + if (TextUtils.isEmpty(encoding)) { + return connection.inputStream + } + if (encoding.contains("gzip")) { + return GZIPInputStream(connection.inputStream) + } + if (encoding.contains("deflate")) { + return InflaterInputStream(connection.inputStream) + } + return connection.inputStream + } fun checkHttpDParams(option: HttpDTaskOption?): Boolean { if (option == null) { 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 e1b3f83a..9494e6ca 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 @@ -20,13 +20,15 @@ import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.command.AddCmd import com.arialyy.aria.core.command.StartCmd import com.arialyy.aria.core.inf.IStartController +import com.arialyy.aria.core.processor.IHttpFileLenAdapter import com.arialyy.aria.core.task.DownloadTask +import com.arialyy.aria.core.task.TaskCachePool import com.arialyy.aria.http.HttpBaseController import com.arialyy.aria.http.HttpOption import com.arialyy.aria.http.HttpUtil -import com.arialyy.aria.orm.EntityCachePool import com.arialyy.aria.orm.entity.DEntity import kotlinx.coroutines.launch +import java.net.HttpURLConnection /** * @Author laoyuyu @@ -45,16 +47,28 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta /** * set http params, link Header */ - fun setHttpOption(httpOption: HttpOption): HttpBaseController { + fun setHttpOption(httpOption: HttpOption): HttpDStartController { httpDTaskOption.httpOption = httpOption return this } - fun setListener(listener: HttpDownloadListener): HttpBaseController { + /** + * Maybe the server has special rules, you need set [IHttpFileLenAdapter] to get the file length from [HttpURLConnection.getHeaderFields] + */ + fun setHttpFileLenAdapter(adapter: IHttpFileLenAdapter): HttpDStartController { + + httpDTaskOption.fileSizeAdapter = adapter + return this + } + + fun setListener(listener: HttpDownloadListener): HttpDStartController { DuaContext.getLifeManager().addCustomListener(target, listener) return this } + /** + * set file save path, eg: /mnt/sdcard/Downloads/test.zip + */ fun setSavePath(savePath: Uri): HttpDStartController { httpDTaskOption.savePathUri = savePath return this @@ -64,10 +78,16 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseController(ta if (HttpUtil.checkHttpDParams(httpDTaskOption)) { throw IllegalArgumentException("invalid params") } - val task = DownloadTask(httpDTaskOption) + val savePath = httpDTaskOption.savePathUri!!.toString() + var util = TaskCachePool.getTaskUtil(savePath) + if (util == null) { + util = HttpDTaskUtil() + TaskCachePool.putTaskUtil(savePath, util) + } + val task = DownloadTask(httpDTaskOption, util) DuaContext.duaScope.launch { val dEntity = findDEntityBySavePath(httpDTaskOption) - EntityCachePool.putEntity(task.taskId, dEntity) + TaskCachePool.putEntity(task.taskId, dEntity) } return task } diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskOption.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskOption.kt index 9dc3b627..133fca15 100644 --- a/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskOption.kt +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskOption.kt @@ -16,6 +16,7 @@ package com.arialyy.aria.http.download import com.arialyy.aria.core.download.DTaskOption +import com.arialyy.aria.core.processor.IHttpFileLenAdapter import com.arialyy.aria.http.HttpOption /** @@ -26,4 +27,5 @@ import com.arialyy.aria.http.HttpOption class HttpDTaskOption : DTaskOption() { var httpOption: HttpOption? = null + var fileSizeAdapter: IHttpFileLenAdapter? = null } \ No newline at end of file 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 new file mode 100644 index 00000000..bc554f0f --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskUtil.kt @@ -0,0 +1,31 @@ +package com.arialyy.aria.http.download + +import com.arialyy.aria.core.task.AbsTaskUtil + +/** + * @Author laoyuyu + * @Description + * @Date 1:47 PM 2023/1/28 + **/ +internal class HttpDTaskUtil : AbsTaskUtil() { + + init { + + } + + override fun isRunning(): Boolean { + TODO("Not yet implemented") + } + + override fun cancel() { + TODO("Not yet implemented") + } + + override fun stop() { + TODO("Not yet implemented") + } + + override fun start() { + + } +} \ No newline at end of file diff --git a/Http/src/main/java/com/arialyy/aria/http/download/HttpHeaderInterceptor.kt b/Http/src/main/java/com/arialyy/aria/http/download/HttpHeaderInterceptor.kt new file mode 100644 index 00000000..fdbe2c04 --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/download/HttpHeaderInterceptor.kt @@ -0,0 +1,203 @@ +/* + * 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.TrafficStats +import android.net.Uri +import android.os.Looper +import android.os.Process +import android.text.TextUtils +import com.arialyy.aria.core.processor.IHttpFileLenAdapter +import com.arialyy.aria.core.task.ITask +import com.arialyy.aria.core.task.ITaskInterceptor +import com.arialyy.aria.core.task.TaskChain +import com.arialyy.aria.core.task.TaskResp +import com.arialyy.aria.http.HttpUtil +import com.arialyy.aria.http.request.IRequest +import com.arialyy.aria.util.CheckUtil +import timber.log.Timber +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.HttpURLConnection +import java.util.UUID + +/** + * @Author laoyuyu + * @Description + * @Date 2:27 PM 2023/1/28 + **/ +internal class HttpHeaderInterceptor : ITaskInterceptor { + private lateinit var task: ITask + private lateinit var taskOption: HttpDTaskOption + + companion object { + private val CODE_30X = listOf( + HttpURLConnection.HTTP_MOVED_TEMP, HttpURLConnection.HTTP_MOVED_PERM, + HttpURLConnection.HTTP_SEE_OTHER, HttpURLConnection.HTTP_CREATED, 307 + ) + } + + override fun interceptor(chain: TaskChain): TaskResp { + if (Looper.myLooper() == Looper.getMainLooper()) { + throw IllegalThreadStateException("Io operations cannot be in the main thread") + } + task = chain.getTask() + taskOption = task.getTaskOption(HttpDTaskOption::class.java) + return try { + val fileSize = getFileSize() + + chain.proceed(task) + } catch (e: IOException) { + Timber.e( + "download fail, url: ${ + chain.getTask().getTaskOption(HttpDTaskOption::class.java).sourUrl + }" + ) + TaskResp(TaskResp.CODE_GET_FILE_INFO_FAIL) + } + } + + @Throws(IOException::class) + private fun getFileSize(): Long { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND) + TrafficStats.setThreadStatsTag(UUID.randomUUID().toString().hashCode()) + val conn: HttpURLConnection = IRequest.getRequest(taskOption.httpOption!!) + .getDConnection(taskOption.sourUrl!!, taskOption.httpOption!!) + // https://httpwg.org/specs/rfc9110.html#byte.ranges + // conn.setRequestProperty("Range", "bytes=" + 0 + "-") + conn.setRequestProperty("Range", "bytes=0-1") // 尝试获取1个字节 + conn.connect() + return handleConnect(conn) + } + + @Throws(IOException::class) + private fun handleConnect(conn: HttpURLConnection): Long { + + val code = conn.responseCode + when { + code == HttpURLConnection.HTTP_PARTIAL -> return getFileSizeFromHeader( + conn.headerFields, + taskOption + ) + code == HttpURLConnection.HTTP_OK -> { + val len = getFileSizeFromHeader(conn.headerFields, taskOption) + if (len > 0) { + return len + } + val contentType = conn.getHeaderField("Content-Type") + if (contentType == "text/html") { + val reader = BufferedReader(InputStreamReader(HttpUtil.convertInputStream(conn))) + val sb = StringBuilder() + var line: String? + while (reader.readLine().also { line = it } != null) { + sb.append(line) + } + reader.close() + return handleUrlReTurn(conn, HttpUtil.getWindowReplaceUrl(sb.toString())) + } + // code is 200, but file size cannot be obtained. + return -1 + } + code in CODE_30X -> { + Timber.d("handle 30x turn, code: $code") + return handleUrlReTurn(conn, conn.getHeaderField("Location")) + } + code >= HttpURLConnection.HTTP_BAD_REQUEST -> { + Timber.e("download fail, code: $code") + return -1 + } + else -> { + return -1 + } + } + } + + @Throws(IOException::class) private fun handleUrlReTurn( + oldConn: HttpURLConnection, + newUrl: String? + ): Long { + Timber.i("handle 30x turn, new url: $newUrl") + if (newUrl.isNullOrEmpty() || newUrl.equals("null", ignoreCase = true)) { + return -1 + } + var tempUrl = newUrl + if (tempUrl.startsWith("/")) { + val uri = Uri.parse(taskOption.sourUrl!!) + tempUrl = uri.host + newUrl + } + if (!CheckUtil.checkUrl(tempUrl)) { + Timber.e("get redirect url fail, $tempUrl") + return -1 + } + + taskOption.redirectUrl = newUrl + val cookies = oldConn.getHeaderField("Set-Cookie") + oldConn.disconnect() + + val newConn: HttpURLConnection = IRequest.getRequest(taskOption.httpOption!!) + .getDConnection(taskOption.sourUrl!!, taskOption.httpOption!!) + newConn.setRequestProperty("Cookie", cookies) + newConn.setRequestProperty("Range", "bytes=" + 0 + "-") + + newConn.connect() + return handleConnect(newConn) + } + + /** + * get file size from header, if user not set [IHttpFileLenAdapter], use [FileLenAdapter] + */ + private fun getFileSizeFromHeader( + header: Map>, + taskOption: HttpDTaskOption + ): Long { + var lenAdapter = taskOption.fileSizeAdapter + if (lenAdapter == null) { + lenAdapter = FileLenAdapter() + } + return lenAdapter.handleFileLen(header) + } + + /** + * https://httpwg.org/specs/rfc9110.html#field.content-range + */ + private class FileLenAdapter : IHttpFileLenAdapter { + override fun handleFileLen(headers: Map>): Long { + if (headers.isEmpty()) { + Timber.e("header is empty, get file size fail") + return -1 + } + val sLength = headers["Content-Length"] + if (sLength == null || sLength.isEmpty()) { + return -1 + } + val temp = sLength[0] + var len = if (TextUtils.isEmpty(temp)) -1 else temp.toLong() + // 某些服务,如果设置了conn.setRequestProperty("Range", "bytes=" + 0 + "-"); + // 会返回 Content-Range: bytes 0-225427911/225427913 + if (len < 0) { + val sRange = headers["Content-Range"] + len = if (sRange == null || sRange.isEmpty()) { + -1 + } else { + val start = temp.indexOf("/") + temp.substring(start + 1).toLong() + } + } + return len + } + } +} diff --git a/Http/src/main/java/com/arialyy/aria/http/request/GetRequest.kt b/Http/src/main/java/com/arialyy/aria/http/request/GetRequest.kt new file mode 100644 index 00000000..983b6fc4 --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/request/GetRequest.kt @@ -0,0 +1,59 @@ +/* + * 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.request + +import com.arialyy.aria.core.common.RequestEnum.GET +import com.arialyy.aria.http.HttpOption +import com.arialyy.aria.util.CommonUtil +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder + +/** + * @Author laoyuyu + * @Description + * @Date 9:52 AM 2023/1/29 + **/ +object GetRequest : IRequest { + + override fun getDConnection(url: String, option: HttpOption): HttpURLConnection { + val params: Map = option.getParams() + + val realUrl = if (params.isNotEmpty()) { + val sb = StringBuilder() + sb.append(url) + if (!url.contains("?")) { + sb.append("?") + } + + val keys = params.keys + for (key in keys) { + sb.append(URLEncoder.encode(key, Charsets.UTF_8.toString())) + .append("=") + .append(URLEncoder.encode(params[key], Charsets.UTF_8.toString())) + .append("&") + } + var temp = sb.toString() + temp = temp.substring(0, temp.length - 1) + URL(CommonUtil.convertUrl(temp)) + } else { + URL(CommonUtil.convertUrl(url)) + } + val conn = createConnection(realUrl, option) + conn.requestMethod = GET.name + return conn + } +} \ No newline at end of file diff --git a/Http/src/main/java/com/arialyy/aria/http/request/HeadRequest.kt b/Http/src/main/java/com/arialyy/aria/http/request/HeadRequest.kt new file mode 100644 index 00000000..aa3b1d52 --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/request/HeadRequest.kt @@ -0,0 +1,59 @@ +/* + * 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.request + +import com.arialyy.aria.core.common.RequestEnum +import com.arialyy.aria.http.HttpOption +import com.arialyy.aria.util.CommonUtil +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder + +/** + * @Author laoyuyu + * @Description + * @Date 9:53 AM 2023/1/29 + **/ +internal object HeadRequest : IRequest { + + override fun getDConnection(url: String, option: HttpOption): HttpURLConnection { + val params: Map = option.getParams() + + val realUrl = if (params.isNotEmpty()) { + val sb = StringBuilder() + sb.append(url) + if (!url.contains("?")) { + sb.append("?") + } + + val keys = params.keys + for (key in keys) { + sb.append(URLEncoder.encode(key, Charsets.UTF_8.toString())) + .append("=") + .append(URLEncoder.encode(params[key], Charsets.UTF_8.toString())) + .append("&") + } + var temp = sb.toString() + temp = temp.substring(0, temp.length - 1) + URL(CommonUtil.convertUrl(temp)) + } else { + URL(CommonUtil.convertUrl(url)) + } + val conn = createConnection(realUrl, option) + conn.requestMethod = RequestEnum.HEAD.name + return conn + } +} \ No newline at end of file diff --git a/Http/src/main/java/com/arialyy/aria/http/request/IRequest.kt b/Http/src/main/java/com/arialyy/aria/http/request/IRequest.kt new file mode 100644 index 00000000..b4fc9f5c --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/request/IRequest.kt @@ -0,0 +1,152 @@ +/* + * 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.request + +import android.text.TextUtils +import com.arialyy.aria.core.AriaConfig +import com.arialyy.aria.core.ProtocolType +import com.arialyy.aria.core.common.RequestEnum.GET +import com.arialyy.aria.core.common.RequestEnum.HEAD +import com.arialyy.aria.core.common.RequestEnum.POST +import com.arialyy.aria.http.HttpOption +import com.arialyy.aria.util.SSLContextUtil +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLConnection +import javax.net.ssl.HttpsURLConnection + +/** + * @Author laoyuyu + * @Description + * @Date 19:48 AM 2023/1/29 + **/ +interface IRequest { + + companion object { + fun getRequest(option: HttpOption): IRequest { + return when (option.getRequestMethod()) { + GET -> GetRequest + POST -> PostRequest + HEAD -> HeadRequest + else -> throw UnsupportedOperationException("unsupported method ${option.getRequestMethod()}") + } + } + } + + fun getDConnection(url: String, option: HttpOption): HttpURLConnection + + @Throws(IOException::class) fun createConnection( + url: URL, + option: HttpOption + ): HttpURLConnection { + val conn: HttpURLConnection + val urlConn: URLConnection = if (option.getProxy() != null) { + url.openConnection(option.getProxy()) + } else { + url.openConnection() + } + if (urlConn is HttpsURLConnection) { + val config = AriaConfig.getInstance() + conn = urlConn + var sslContext = SSLContextUtil.getSSLContextFromAssets( + config.dConfig.caName, + config.dConfig.caPath, ProtocolType.Default + ) + if (sslContext == null) { + sslContext = SSLContextUtil.getDefaultSLLContext(ProtocolType.Default) + } + val ssf = sslContext!!.socketFactory + conn.sslSocketFactory = ssf + conn.hostnameVerifier = SSLContextUtil.HOSTNAME_VERIFIER + } else { + conn = urlConn as HttpURLConnection + } + setHeader(conn, option) + setConnectAttr(conn) + return conn + } + + private fun setConnectAttr(conn: HttpURLConnection) { + conn.connectTimeout = AriaConfig.getInstance().dConfig.connectTimeOut + } + + /** + * 设置头部参数 + */ + private fun setHeader(conn: HttpURLConnection, option: HttpOption) { + option.getHeaders().forEach { + conn.setRequestProperty(it.key, it.value) + } + + if (conn.getRequestProperty("Accept-Language") == null) { + conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7") + } + if (conn.getRequestProperty("Accept-Encoding") == null) { + conn.setRequestProperty("Accept-Encoding", "identity") + } + if (conn.getRequestProperty("Accept-Charset") == null) { + conn.setRequestProperty("Accept-Charset", "UTF-8") + } + if (conn.getRequestProperty("Connection") == null) { + conn.setRequestProperty("Connection", "Keep-Alive") + } + if (conn.getRequestProperty("Charset") == null) { + conn.setRequestProperty("Charset", "UTF-8") + } + if (conn.getRequestProperty("User-Agent") == null) { + conn.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36" + ) + } + if (conn.getRequestProperty("Accept") == null) { + val accept = StringBuilder() + accept.append("image/gif, ") + .append("image/jpeg, ") + .append("image/pjpeg, ") + .append("image/webp, ") + .append("image/apng, ") + .append("application/xml, ") + .append("application/xaml+xml, ") + .append("application/xhtml+xml, ") + .append("application/x-shockwave-flash, ") + .append("application/x-ms-xbap, ") + .append("application/x-ms-application, ") + .append("application/msword, ") + .append("application/vnd.ms-excel, ") + .append("application/vnd.ms-xpsdocument, ") + .append("application/vnd.ms-powerpoint, ") + .append("application/signed-exchange, ") + .append("text/plain, ") + .append("text/html, ") + .append("*/*") + conn.setRequestProperty("Accept", accept.toString()) + } + //302获取重定向地址 + conn.instanceFollowRedirects = false + val manager = option.getCookieManager() + if (manager != null) { + val store = manager.cookieStore + if (store != null && store.cookies.size > 0) { + conn.setRequestProperty( + "Cookie", + TextUtils.join(";", store.cookies) + ) + } + } + } +} \ No newline at end of file diff --git a/Http/src/main/java/com/arialyy/aria/http/request/PostRequest.kt b/Http/src/main/java/com/arialyy/aria/http/request/PostRequest.kt new file mode 100644 index 00000000..56422ef9 --- /dev/null +++ b/Http/src/main/java/com/arialyy/aria/http/request/PostRequest.kt @@ -0,0 +1,74 @@ +/* + * 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.request + +import com.arialyy.aria.core.common.RequestEnum +import com.arialyy.aria.http.HttpOption +import com.arialyy.aria.util.CommonUtil +import java.io.BufferedWriter +import java.io.OutputStreamWriter +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder + +/** + * @Author laoyuyu + * @Description + * @Date 19:52 AM 2023/1/29 + **/ +internal object PostRequest : IRequest { + override fun getDConnection(url: String, option: HttpOption): HttpURLConnection { + val conn = createConnection(URL(CommonUtil.convertUrl(url)), option) + conn.doInput = true + conn.doOutput = true + conn.useCaches = false + conn.requestMethod = RequestEnum.POST.name + + if (option.getParams().isNotEmpty() + || !option.getBody().isNullOrEmpty() + || option.getBodyBinary() != null + ) { + conn.outputStream.use { + val writer = BufferedWriter(OutputStreamWriter(it, "UTF-8")) + if (option.getParams().isNotEmpty()) { + writer.write(getQuery(option.getParams())) + } + if (!option.getBody().isNullOrEmpty()) { + writer.write(option.getBody()) + } + if (option.getBodyBinary() != null) { + it.write(option.getBodyBinary()) + } + writer.flush() + writer.close() + it.close() + } + } + return conn + } + + private fun getQuery(params: Map): String { + val result = StringBuilder() + var first = true + for (kv in params) { + if (first) first = false else result.append("&") + result.append(URLEncoder.encode(kv.key, Charsets.UTF_8.toString())) + result.append("=") + result.append(URLEncoder.encode(kv.value, Charsets.UTF_8.toString())) + } + return result.toString() + } +} \ No newline at end of file diff --git a/Http/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil b/Http/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil similarity index 100% rename from Http/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil rename to Http/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil diff --git a/M3U8Component/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil b/M3U8Component/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil similarity index 100% rename from M3U8Component/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil rename to M3U8Component/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java index 5822bb70..909431f3 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/common/RequestEnum.java @@ -20,12 +20,11 @@ package com.arialyy.aria.core.common; * url请求方式,目前支持GET、POST */ public enum RequestEnum { - GET("GET"), POST("POST"); + GET("GET"), POST("POST"), HEAD("HEAD"); public String name; RequestEnum(String name) { this.name = name; } - } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskOption.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskOption.kt index 9d24eb00..ce34fc56 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskOption.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/DTaskOption.kt @@ -26,4 +26,5 @@ import com.arialyy.aria.core.inf.ITaskOption open class DTaskOption : ITaskOption() { var sourUrl: String? = null var savePathUri: Uri? = null + var redirectUrl: String? = null } \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java index c333eaee..f5236eee 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupLoaderUtil.java @@ -15,11 +15,10 @@ */ package com.arialyy.aria.core.group; -import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.ITaskUtil; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.loader.LoaderStructure; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -27,7 +26,7 @@ import com.arialyy.aria.util.CommonUtil; * Created by AriaL on 2017/6/30. * 任务组核心逻辑 */ -public abstract class AbsGroupLoaderUtil implements IUtil { +public abstract class AbsGroupLoaderUtil implements ITaskUtil { protected String TAG = CommonUtil.getClassName(getClass()); private IEventListener mListener; @@ -36,7 +35,7 @@ public abstract class AbsGroupLoaderUtil implements IUtil { private boolean isStop = false, isCancel = false; - @Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { + @Override public ITaskUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { mTaskWrapper = taskWrapper; mListener = listener; mLoader = getLoader(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java index 8a31688c..3635ddd8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsSubDLoadUtil.java @@ -19,7 +19,7 @@ import android.os.Handler; import com.arialyy.aria.core.TaskRecord; import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.ITaskUtil; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.loader.LoaderStructure; @@ -31,7 +31,7 @@ import com.arialyy.aria.util.CommonUtil; /** * 子任务下载器工具,需要在线程池中执行 */ -public abstract class AbsSubDLoadUtil implements IUtil, Runnable { +public abstract class AbsSubDLoadUtil implements ITaskUtil, Runnable { protected final String TAG = CommonUtil.getClassName(getClass()); protected SubLoader mDLoader; @@ -51,7 +51,7 @@ public abstract class AbsSubDLoadUtil implements IUtil, Runnable { this.needGetInfo = needGetInfo; } - @Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { + @Override public ITaskUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { mWrapper = (DTaskWrapper) taskWrapper; mDLoader = getLoader(); return this; diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java index 70f80442..947a4b71 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/ISubQueue.java @@ -16,7 +16,7 @@ package com.arialyy.aria.core.group; import com.arialyy.aria.core.loader.AbsNormalLoader; -import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.ITaskUtil; import com.arialyy.aria.core.config.DGroupConfig; /** @@ -24,7 +24,7 @@ import com.arialyy.aria.core.config.DGroupConfig; * * @param {@link AbsNormalLoader}下载器 */ -interface ISubQueue { +interface ISubQueue { /** * 添加任务 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskUtil.java similarity index 62% rename from PublicComponent/src/main/java/com/arialyy/aria/core/inf/IUtil.java rename to PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskUtil.java index dd23b61b..bab111e6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/inf/IUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskUtil.java @@ -16,37 +16,17 @@ package com.arialyy.aria.core.inf; -import com.arialyy.aria.core.download.DownloadEntity; -import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.listener.IEventListener; -import com.arialyy.aria.core.upload.UploadEntity; -import com.arialyy.aria.core.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.task.ITask; +import org.jetbrains.annotations.NotNull; /** * Created by lyy on 2016/10/31. * 任务功能接口 */ -public interface IUtil { +public interface ITaskUtil { - IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener); - - /** - * 获取任务标志 - * - * @return {@link DownloadEntity#getKey()}、{@link DownloadGroupEntity#getKey()}、{@link - * UploadEntity#getKey()} - */ - String getKey(); - - /** - * 获取文件大小 - */ - long getFileSize(); - - /** - * 获取当前位置 - */ - long getCurrentLocation(); + void init(@NotNull ITask task, @NotNull IEventListener listener); /** * 任务是否正在执行 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java index d6c78ac3..0d285246 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/listener/BaseListener.java @@ -25,7 +25,7 @@ import com.arialyy.aria.core.task.ITask; import com.arialyy.aria.core.task.TaskState; import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.AriaException; -import com.arialyy.aria.orm.EntityCachePool; +import com.arialyy.aria.core.task.TaskCachePool; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ErrorHelp; import java.lang.ref.WeakReference; @@ -174,6 +174,6 @@ public abstract class BaseListener implements IEventListener { if (state == IEntity.STATE_COMPLETE) { handleComplete(); } - EntityCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location); + TaskCachePool.INSTANCE.updateState(mTask.getTaskId(), state, location); } } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java index 04002742..cc04d52d 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/AbsNormalLoaderUtil.java @@ -16,10 +16,9 @@ package com.arialyy.aria.core.loader; -import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.ITaskUtil; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; -import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.exception.AriaException; import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.CommonUtil; @@ -28,7 +27,7 @@ import com.arialyy.aria.util.CommonUtil; * Created by lyy on 2015/8/25. * HTTP\FTP单任务下载工具 */ -public abstract class AbsNormalLoaderUtil implements IUtil { +public abstract class AbsNormalLoaderUtil implements ITaskUtil { protected String TAG = CommonUtil.getClassName(getClass()); private IEventListener mListener; protected AbsNormalLoader mLoader; @@ -38,7 +37,7 @@ public abstract class AbsNormalLoaderUtil implements IUtil { protected AbsNormalLoaderUtil() { } - @Override public IUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { + @Override public ITaskUtil setParams(AbsTaskWrapper taskWrapper, IEventListener listener) { mTaskWrapper = taskWrapper; mListener = listener; mLoader = getLoader(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java index 215412a7..bc925169 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsGroupTask.java @@ -35,8 +35,8 @@ public abstract class AbsGroupTask * @param url 子任务下载地址 */ public void startSubTask(String url) { - if (getUtil() != null) { - ((AbsGroupLoaderUtil) getUtil()).startSubTask(url); + if (getTaskUtil() != null) { + ((AbsGroupLoaderUtil) getTaskUtil()).startSubTask(url); } } @@ -46,8 +46,8 @@ public abstract class AbsGroupTask * @param url 子任务下载地址 */ public void stopSubTask(String url) { - if (getUtil() != null) { - ((AbsGroupLoaderUtil) getUtil()).stopSubTask(url); + if (getTaskUtil() != null) { + ((AbsGroupLoaderUtil) getTaskUtil()).stopSubTask(url); } } } 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 c80c0c6a..5d966651 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 @@ -18,10 +18,9 @@ package com.arialyy.aria.core.task; import android.text.TextUtils; import com.arialyy.aria.core.common.AbsEntity; import com.arialyy.aria.core.inf.ITaskOption; -import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.ITaskUtil; import com.arialyy.aria.core.inf.TaskSchedulerType; import com.arialyy.aria.util.CommonUtil; -import com.arialyy.aria.util.ComponentUtil; import java.util.HashMap; import java.util.Map; import timber.log.Timber; @@ -32,7 +31,7 @@ import timber.log.Timber; public abstract class AbsTask implements ITask { protected ITaskOption mTaskOption; private boolean isCancel = false, isStop = false; - private IUtil mUtil; + private ITaskUtil mUtil; /** * 该任务的调度类型 */ @@ -41,20 +40,19 @@ public abstract class AbsTask implements ITask { private int taskId = -1; private final Map mExpand = new HashMap<>(); - protected AbsTask(ITaskOption taskOption) { + protected AbsTask(ITaskOption taskOption, ITaskUtil util) { mTaskOption = taskOption; + mUtil = util; taskId = TaskStatePool.INSTANCE.buildTaskId$PublicComponent_debug(); TaskStatePool.INSTANCE.putTaskState(getTaskId(), mTaskState); + util.init(this, taskOption.taskListener); } @Override public void setState(int state) { mTaskState.setState(state); } - synchronized IUtil getUtil() { - if (mUtil == null) { - mUtil = ComponentUtil.getInstance().buildUtil(mTaskWrapper, mListener); - } + ITaskUtil getTaskUtil() { return mUtil; } @@ -154,13 +152,13 @@ public abstract class AbsTask implements ITask { @Override public void start(int type) { mSchedulerType = type; - mUtil = getUtil(); + mUtil = getTaskUtil(); if (mUtil == null) { Timber.e("util is null"); return; } if (type == TaskSchedulerType.TYPE_START_AND_RESET_STATE) { - if (getUtil().isRunning()) { + if (getTaskUtil().isRunning()) { Timber.e("task restart fail"); return; } @@ -168,33 +166,33 @@ public abstract class AbsTask implements ITask { Timber.e("task restart success"); return; } - if (getUtil().isRunning()) { + if (getTaskUtil().isRunning()) { Timber.d("task is running"); return; } - getUtil().start(); + getTaskUtil().start(); } @Override public void stop(int type) { - mUtil = getUtil(); + mUtil = getTaskUtil(); if (mUtil == null) { Timber.e("util is null"); return; } isStop = true; mSchedulerType = type; - getUtil().stop(); + getTaskUtil().stop(); } @Override public void cancel(int type) { - mUtil = getUtil(); + mUtil = getTaskUtil(); if (mUtil == null) { Timber.e("util is null"); return; } isCancel = true; mSchedulerType = type; - getUtil().cancel(); + getTaskUtil().cancel(); } /** @@ -203,7 +201,7 @@ public abstract class AbsTask implements ITask { * @return {@code true} 正在下载 */ @Override public boolean isRunning() { - return getUtil().isRunning(); + return getTaskUtil().isRunning(); } /** diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTaskUtil.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTaskUtil.kt new file mode 100644 index 00000000..053c2d5e --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsTaskUtil.kt @@ -0,0 +1,63 @@ +/* + * 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.inf.ITaskUtil +import com.arialyy.aria.core.listener.IEventListener +import com.arialyy.aria.core.task.ITaskInterceptor.IChain + +/** + * @Author laoyuyu + * @Description + * @Date 1:12 PM 2023/1/28 + **/ +abstract class AbsTaskUtil : ITaskUtil { + protected lateinit var mTask: ITask + protected lateinit var mEventListener: IEventListener + + private val mUserInterceptor = mutableListOf() + private val mCoreInterceptor = mutableListOf() + + override fun init(task: ITask, listener: IEventListener) { + mTask = task + mEventListener = listener + } + + /** + * add user interceptor + */ + open fun setInterceptors(userInterceptors: List) { + mUserInterceptor.addAll(userInterceptors) + } + + protected open fun addCoreInterceptor(interceptor: ITaskInterceptor) { + mCoreInterceptor.add(interceptor) + } + + /** + * if interruption occurred, stop cmd + */ + protected open fun interceptor(): TaskResp? { + if (mUserInterceptor.isEmpty()) { + return null + } + val interceptors: MutableList = ArrayList() + interceptors.addAll(mUserInterceptor) + interceptors.addAll(mCoreInterceptor) + val chain: IChain = TaskChain(interceptors, 0, mTask) + return chain.proceed(mTask) + } +} \ No newline at end of file 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 46093477..9e714558 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 @@ -18,6 +18,7 @@ package com.arialyy.aria.core.task; import android.net.Uri; import com.arialyy.aria.core.download.DTaskOption; +import com.arialyy.aria.core.inf.ITaskUtil; /** * Created by lyy on 2016/8/11. @@ -25,8 +26,8 @@ import com.arialyy.aria.core.download.DTaskOption; */ public class DownloadTask extends AbsTask { - public DownloadTask(DTaskOption taskOption) { - super(taskOption); + public DownloadTask(DTaskOption taskOption, ITaskUtil util) { + super(taskOption, util); taskOption.taskListener.setParams(this); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITaskInterceptor.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITaskInterceptor.kt new file mode 100644 index 00000000..2d3bbc27 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ITaskInterceptor.kt @@ -0,0 +1,31 @@ +/* + * 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 + +/** + * @Author laoyuyu + * @Description + * @Date 1:19 PM 2023/1/28 + **/ +interface ITaskInterceptor { + + fun interceptor(chain: TaskChain): TaskResp + + interface IChain { + fun getTask(): ITask + fun proceed(task: ITask): TaskResp + } +} \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/orm/EntityCachePool.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskCachePool.kt similarity index 72% rename from PublicComponent/src/main/java/com/arialyy/aria/orm/EntityCachePool.kt rename to PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskCachePool.kt index 792d3722..903aea3c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/orm/EntityCachePool.kt +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskCachePool.kt @@ -13,10 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.arialyy.aria.orm +package com.arialyy.aria.core.task import com.arialyy.aria.core.inf.BaseEntity import com.arialyy.aria.core.inf.IEntity +import com.arialyy.aria.core.inf.ITaskUtil import timber.log.Timber /** @@ -24,15 +25,34 @@ import timber.log.Timber * @Description * @Date 21:43 AM 2023/1/22 **/ -object EntityCachePool { +object TaskCachePool { /** * key: taskId */ private val entityMap = hashMapOf() + private val taskUtilMap = hashMapOf() + + /** + * @param taskKey task unique identifier, like: savePath, sourceUrl + */ + fun putTaskUtil(taskKey: String, taskUtil: ITaskUtil) { + if (taskKey.isEmpty()) { + Timber.e("invalid taskKey: $taskKey") + return + } + taskUtilMap[taskKey] = taskUtil + } + + /** + * @param taskKey task unique identifier, like: savePath, sourceUrl + */ + fun getTaskUtil(taskKey: String): ITaskUtil? { + return taskUtilMap[taskKey] + } fun putEntity(taskId: Int, entity: BaseEntity) { if (taskId <= 0) { - Timber.e("invalid taskId: ${taskId}") + Timber.e("invalid taskId: $taskId") return } entityMap[taskId] = entity diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskChain.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskChain.kt new file mode 100644 index 00000000..353c15d6 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskChain.kt @@ -0,0 +1,38 @@ +/* + * 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 + +/** + * @Author laoyuyu + * @Description + * @Date 11:06 AM 2023/1/27 + **/ +class TaskChain( + private val interceptors: List, + private val index: Int = 0, + private val task: ITask, +) : ITaskInterceptor.IChain { + + override fun getTask(): ITask { + return task + } + + override fun proceed(task: ITask): TaskResp { + val next = TaskChain(interceptors, index, task) + val interceptor = interceptors[index] + return interceptor.interceptor(next) + } +} \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskResp.kt b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskResp.kt new file mode 100644 index 00000000..10113883 --- /dev/null +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskResp.kt @@ -0,0 +1,17 @@ +package com.arialyy.aria.core.task + +/** + * @Author laoyuyu + * @Description + * @Date 1:43 PM 2023/1/28 + **/ +class TaskResp(val code: Int = CODE_DEF) { + companion object { + const val CODE_COMPLETE = 1 + const val CODE_INTERRUPT = 999 + const val CODE_DEF = 0 + const val CODE_GET_FILE_INFO_FAIL = 2 + } + + var fileSize: Long = 0 +} \ No newline at end of file diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java index 88e51222..c409efb1 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/ComponentUtil.java @@ -19,7 +19,7 @@ import android.os.Handler; import com.arialyy.aria.core.TaskOptionParams; import com.arialyy.aria.core.inf.IEventHandler; import com.arialyy.aria.core.inf.ITaskOption; -import com.arialyy.aria.core.inf.IUtil; +import com.arialyy.aria.core.inf.ITaskUtil; import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.task.AbsTask; import com.arialyy.aria.core.wrapper.AbsTaskWrapper; @@ -104,7 +104,7 @@ public class ComponentUtil { * * @return 返回任务工具 */ - public synchronized T buildUtil(AbsTaskWrapper wrapper, + public synchronized T buildUtil(AbsTaskWrapper wrapper, IEventListener listener) { int requestType = wrapper.getRequestType(); String className = null; diff --git a/SFtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil b/SFtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil similarity index 100% rename from SFtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.IUtil rename to SFtpComponent/src/main/resources/META-INF/services/com.arialyy.aria.core.inf.ITaskUtil