diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e142d56bc..04be02dfb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,8 @@ jobs: UPLOAD_ARTIFACT: 'true' steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: actions/cache@v2 with: path: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c62d0be3..f7e57679a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,8 @@ jobs: UPLOAD_ARTIFACT: 'true' steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: actions/cache@v2 with: path: | diff --git a/app/.gitignore b/app/.gitignore index 796b96d1c..e431c26b5 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1,2 @@ /build +/so \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index f2cc4727e..3d38e0742 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-kapt' apply plugin: 'de.timfreiheit.resourceplaceholders' +apply from:'download.gradle' static def releaseTime() { return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+8")) @@ -55,14 +56,17 @@ android { } buildTypes { release { + buildConfigField "String", "Cronet_Version", "\"$CronetVersion\"" if (project.hasProperty("RELEASE_STORE_FILE")) { signingConfig signingConfigs.myConfig } applicationIdSuffix '.release' + minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } debug { + buildConfigField "String", "Cronet_Version", "\"$CronetVersion\"" if (project.hasProperty("RELEASE_STORE_FILE")) { signingConfig signingConfigs.myConfig } diff --git a/app/cronetlib/src/cronet_api-src.jar b/app/cronetlib/src/cronet_api-src.jar deleted file mode 100644 index c175fb4e0..000000000 Binary files a/app/cronetlib/src/cronet_api-src.jar and /dev/null differ diff --git a/app/cronetlib/src/cronet_impl_common_java-src.jar b/app/cronetlib/src/cronet_impl_common_java-src.jar deleted file mode 100644 index 6e9402d80..000000000 Binary files a/app/cronetlib/src/cronet_impl_common_java-src.jar and /dev/null differ diff --git a/app/cronetlib/src/cronet_impl_native_java-src.jar b/app/cronetlib/src/cronet_impl_native_java-src.jar deleted file mode 100644 index 83a357a91..000000000 Binary files a/app/cronetlib/src/cronet_impl_native_java-src.jar and /dev/null differ diff --git a/app/cronetlib/src/cronet_impl_platform_java-src.jar b/app/cronetlib/src/cronet_impl_platform_java-src.jar deleted file mode 100644 index 48c544ef7..000000000 Binary files a/app/cronetlib/src/cronet_impl_platform_java-src.jar and /dev/null differ diff --git a/app/download.gradle b/app/download.gradle new file mode 100644 index 000000000..ec479c2c2 --- /dev/null +++ b/app/download.gradle @@ -0,0 +1,110 @@ +import java.security.MessageDigest + +apply plugin: 'de.undercouch.download' + +def BASE_PATH = "https://storage.googleapis.com/chromium-cronet/android/" + CronetVersion + "/Release/cronet/" +def assetsDir = projectDir.toString() + "/src/main/assets" +def libPath = projectDir.toString() + "/cronetlib" +def soPath = projectDir.toString() + "/so" + +/** + * 从文件生成MD5 + * @param file + * @return + */ +static def generateMD5(final file) { + MessageDigest digest = MessageDigest.getInstance("MD5") + file.withInputStream() { is -> + byte[] buffer = new byte[1024] + int numRead = 0 + while ((numRead = is.read(buffer)) > 0) { + digest.update(buffer, 0, numRead) + } + } + return String.format("%032x", new BigInteger(1, digest.digest())).toLowerCase() +} + +/** + * 下载Cronet相关的jar + */ +task downloadJar(type: Download) { + src([ + BASE_PATH + "cronet_api.jar", + BASE_PATH + "cronet_impl_common_java.jar", + BASE_PATH + "cronet_impl_native_java.jar", + BASE_PATH + "cronet_impl_platform_java.jar", + ]) + dest libPath + overwrite true + onlyIfModified true +} +/** + * 下载Cronet的arm64-v8a so + */ +task downloadARM64(type: Download) { + src BASE_PATH + "libs/arm64-v8a/libcronet." + CronetVersion + ".so" + dest soPath + "/arm64-v8a.so" + overwrite true + onlyIfModified true +} +/** + * 下载Cronet的armeabi-v7a so + */ +task downloadARMv7(type: Download) { + src BASE_PATH + "libs/armeabi-v7a/libcronet." + CronetVersion + ".so" + dest soPath + "/armeabi-v7a.so" + overwrite true + onlyIfModified true +} +/** + * 下载Cronet的x86_64 so + */ +task downloadX86_64(type: Download) { + src BASE_PATH + "libs/x86_64/libcronet." + CronetVersion + ".so" + dest soPath + "/x86_64.so" + overwrite true + onlyIfModified true +} +/** + * 下载Cronet的x86 so + */ +task downloadX86(type: Download) { + src BASE_PATH + "libs/x86/libcronet." + CronetVersion + ".so" + dest soPath + "/x86.so" + overwrite true + onlyIfModified true +} + +/** + * 更新Cronet版本时执行这个task + * 先更改gradle.properties 里面的版本号,然后再执行 + * gradlew app:downloadCronet + */ +task downloadCronet() { + dependsOn downloadJar, downloadARM64, downloadARMv7, downloadX86_64, downloadX86 + + doLast { + StringBuilder sb = new StringBuilder("{") + def files = new File(soPath).listFiles() + for (File file : files) { + println file.name.replace(".so", "") + sb.append("\"").append(file.name.replace(".so", "")).append("\":\"").append(generateMD5(file)).append("\",") + } + sb.append("\"version\":\"").append(CronetVersion).append("\"}") + + println sb.toString() + + println assetsDir + def f1 = new File(assetsDir + "/cronet.json") + if (!f1.exists()) { + f1.parentFile.mkdirs() + f1.createNewFile() + } + f1.text = sb.toString() + + } + + +} + + diff --git a/app/src/main/assets/cronet.json b/app/src/main/assets/cronet.json new file mode 100644 index 000000000..1e118c39d --- /dev/null +++ b/app/src/main/assets/cronet.json @@ -0,0 +1 @@ +{"arm64-v8a":"1f4e088f6e00175e12ee153e4004d283","armeabi-v7a":"a6726219c9a6217b95763baa3d61eb18","x86":"73b6a220fe16e0cdeebf1094980825c2","x86_64":"12fd5bb0a12664294b64fffccd326347","version":"92.0.4515.159"} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt index ca5abca0d..ab4353049 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt @@ -1,6 +1,7 @@ package io.legado.app.help.http.cronet import android.util.Log +import com.google.android.gms.net.CronetProviderInstaller import io.legado.app.help.AppConfig import okhttp3.Headers import okhttp3.MediaType @@ -18,22 +19,24 @@ import java.util.concurrent.Executors val executor: Executor by lazy { Executors.newCachedThreadPool() } val cronetEngine: ExperimentalCronetEngine by lazy { - CronetLoader.preDownload() + if (AppConfig.isGooglePlay) { + CronetProviderInstaller.installProvider(appCtx) + } else { + CronetLoader.preDownload() + } + val builder = ExperimentalCronetEngine.Builder(appCtx).apply { - if (!AppConfig.isGooglePlay) { + if (!AppConfig.isGooglePlay&&CronetLoader.install()) { setLibraryLoader(CronetLoader)//设置自定义so库加载 } setStoragePath(appCtx.externalCacheDir?.absolutePath)//设置缓存路径 - enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50))//设置缓存模式 + enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50).toLong())//设置缓存模式 enableQuic(true)//设置支持http/3 enableHttp2(true) //设置支持http/2 enablePublicKeyPinningBypassForLocalTrustAnchors(true) - //enableNetworkQualityEstimator(true) - //Brotli压缩 - enableBrotli(true) - //setExperimentalOptions("{\"quic_version\": \"h3-29\"}") + enableBrotli(true)//Brotli压缩 } val engine = builder.build() Log.d("Cronet", "Cronet Version:" + engine.versionString) diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt index 2038e461a..ccd63d415 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt @@ -15,6 +15,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor { } else try { //移除Keep-Alive,手动设置会导致400 BadRequest builder.removeHeader("Keep-Alive") + builder.removeHeader("Accept-Encoding") val cookieStr = getCookie(original.url) //设置Cookie if (cookieStr.length > 3) { @@ -44,7 +45,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor { val callback = CronetUrlRequestCallback(request, call) val urlRequest = buildRequest(request, callback) urlRequest.start() - return callback.waitForDone(urlRequest) + return callback.waitForDone(urlRequest) } private fun getCookie(url: HttpUrl): String { diff --git a/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt b/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt index f2d8ec058..ad9258c5e 100644 --- a/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt +++ b/app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt @@ -6,11 +6,12 @@ import android.content.pm.ApplicationInfo import android.os.Build import android.text.TextUtils import android.util.Log +import com.google.android.gms.net.CronetProviderInstaller +import io.legado.app.BuildConfig import io.legado.app.help.AppConfig import io.legado.app.help.coroutine.Coroutine -import io.legado.app.utils.getPrefString -import io.legado.app.utils.putPrefString import org.chromium.net.CronetEngine +import org.json.JSONObject import splitties.init.appCtx import java.io.* import java.math.BigInteger @@ -19,28 +20,25 @@ import java.net.URL import java.security.MessageDigest import java.util.* + object CronetLoader : CronetEngine.Builder.LibraryLoader() { - //https://storage.googleapis.com/chromium-cronet/android/92.0.4515.127/Release/cronet/libs/arm64-v8a/libcronet.92.0.4515.127.so - //https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@92.0.4515.127/cronet/92.0.4515.127/arm64-v8a/libcronet.92.0.4515.127.so.js + //https://storage.googleapis.com/chromium-cronet/android/92.0.4515.127/Release/cronet/libs/arm64-v8a/libcronet.92.0.4515.159.so + //https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@92.0.4515.127/cronet/92.0.4515.127/arm64-v8a/libcronet.92.0.4515.159.so.js private const val TAG = "CronetLoader" - private const val soVersion = "92.0.4515.159" + private const val soVersion = BuildConfig.Cronet_Version private const val soName = "libcronet.$soVersion.so" private val soUrl: String - private val md5Url: String private val soFile: File private val downloadFile: File private var cpuAbi: String? = null - private var md5: String? = appCtx.getPrefString("soMd5") - private val version: String? = appCtx.getPrefString("soVersion", soVersion) + private var md5: String var download = false init { soUrl = ("https://storage.googleapis.com/chromium-cronet/android/" + soVersion + "/Release/cronet/libs/" + getCpuAbi(appCtx) + "/" + soName) - md5Url = ("https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@" + - soVersion + "/cronet/" + soVersion + "/" - + getCpuAbi(appCtx) + "/" + soName + ".js") + md5 = getMd5(appCtx) val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE) soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName) downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName) @@ -54,13 +52,20 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { if (AppConfig.isGooglePlay) { return true } + if (md5.length != 32 || !soFile.exists() || md5 != getFileMD5(soFile)) { + return false + } return soFile.exists() } + fun preDownload() { - if (AppConfig.isGooglePlay) return + if (AppConfig.isGooglePlay) { + CronetProviderInstaller.installProvider(appCtx) + return + } Coroutine.async { - md5 = getUrlMd5(md5Url) + //md5 = getUrlMd5(md5Url) if (soFile.exists() && md5 == getFileMD5(soFile)) { Log.e(TAG, "So 库已存在") } else { @@ -70,6 +75,27 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { } } + private fun getMd5(context: Context): String { + val stringBuilder = StringBuilder() + return try { + //获取assets资源管理器 + val assetManager = context.assets + //通过管理器打开文件并读取 + val bf = BufferedReader( + InputStreamReader( + assetManager.open("cronet.json") + ) + ) + var line: String? + while (bf.readLine().also { line = it } != null) { + stringBuilder.append(line) + } + JSONObject(stringBuilder.toString()).optString(getCpuAbi(context), "") + } catch (e: java.lang.Exception) { + return "" + } + } + @SuppressLint("UnsafeDynamicallyLoadedCode") override fun loadLibrary(libName: String) { Log.e(TAG, "libName:$libName") @@ -89,9 +115,9 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { //如果找不到,则从远程下载 //删除历史文件 deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile) - md5 = getUrlMd5(md5Url) + //md5 = getUrlMd5(md5Url) Log.i(TAG, "soMD5:$md5") - if (md5 == null || md5!!.length != 32 || soUrl.isEmpty()) { + if (md5.length != 32 || soUrl.isEmpty()) { //如果md5或下载的url为空,则调用系统行为进行加载 System.loadLibrary(libName) return @@ -144,39 +170,6 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() { return cpuAbi } - @Suppress("SameParameterValue") - private fun getUrlMd5(url: String): String? { - //这样在下载成功后,遇到无网条件下,只要版本未发生变化也能获取md5 - if (md5 != null && md5!!.length == 32 && version == soVersion) { - appCtx.putPrefString("soMd5", md5) - appCtx.putPrefString("soVersion", soVersion) - return md5 - } - val inputStream: InputStream - val outputStream: OutputStream - return try { - outputStream = ByteArrayOutputStream() - val connection = URL(url).openConnection() as HttpURLConnection - inputStream = connection.inputStream - val buffer = ByteArray(1024) - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - outputStream.write(buffer, 0, read) - outputStream.flush() - } - val tmd5 = outputStream.toString() - //成功获取到md5后保存md5和版本 - if (tmd5.length == 32) { - appCtx.putPrefString("soMd5", tmd5) - appCtx.putPrefString("soVersion", soVersion) - } - - return tmd5 - - } catch (e: IOException) { - null - } - } /** * 删除历史文件 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 4a79b1294..57936ae27 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -315,9 +315,9 @@ Espaço entre linhas Espaço entre parágrafos Na parte superior - Seleção no topo + Seleção até o topo Na parte inferior - Seleção no fundo + Seleção até o final Expansão automática de Descoberta A expansão padrão da primeira Descoberta. Linhas atuais %s @@ -393,11 +393,11 @@ 源分组(fonteGrupo) 自定义源分组 输入自定义源分组名称 - 并发率(concurrentRate) - 分类Url(sortUrl) + 并发率(taxaSimultânea) + 分类Url(ordenarUrl) 登录URL(loginUrl) - 登录UI(loginUi) - 登录检查JS(loginCheckJs) + 登UI(loginIU) + 登录检查JS(loginVerifJs) 源注释(fonteComentário) 搜索地址(url) 发现地址规则(url) @@ -849,20 +849,20 @@ Resetar Nenhuma url Dicionários - 未知错误 - end - 关闭替换分组/开启添加分组 - 媒体按钮•上一首|下一首 - 上一段|下一段/上一章|下一章 - 及时翻页,翻页时会停顿一下 - Verificar a fonte do livro mostra uma mensagem de depuração - Exibir etapas de solicitação de rede e tempo durante a verificação da fonte do livro - No export chapter names - Autobackup failed - Background image blurring - Blurring radius - Disabled when 0, enable range from 1 to 25\nThe greater the radius, the stronger the effect of blurring - 需登录 - 使用Cronet网络组件 + Erro desconhecido + fim + Desativar substituir agrupamento / Ativar adicionar agrupamento + Botões de mídia - Anterior|Próximo + Anterior|Próximo Parágrafo/Anterior|Próximo Capítulo + Virar as páginas durante tempo, com uma pausa ao virar as páginas + Marcando a fonte do livro mostra uma mensagem de depuração + Mostrar o status da rede com a data e hora durante a verificação da fonte + Não há nomes de capítulos de exportação + Auto-Backup falhou + Desfocagem da imagem de fundo + Raio da desfocagem + Desativado quando 0, Ativado entre 1 e 25\n Quanto maior o raio, mais forte o efeito de desfocagem + Login necessário + Usando componentes de rede Cronet diff --git a/build.gradle b/build.gradle index 967f30950..36559ec3b 100644 --- a/build.gradle +++ b/build.gradle @@ -13,6 +13,7 @@ buildscript { classpath 'com.android.tools.build:gradle:7.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4' + classpath 'de.undercouch:gradle-download-task:4.1.2' } } diff --git a/gradle.properties b/gradle.properties index 7934fd0e7..e8357fd01 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,3 +21,5 @@ android.enableJetifier=true kotlin.code.style=official android.enableResourceOptimizations=true + +CronetVersion=92.0.4515.159