Merge remote-tracking branch 'origin/master'

pull/1282/head
gedoor 3 years ago
commit 967462b891
  1. 2
      .github/workflows/release.yml
  2. 2
      .github/workflows/test.yml
  3. 1
      app/.gitignore
  4. 4
      app/build.gradle
  5. BIN
      app/cronetlib/src/cronet_api-src.jar
  6. BIN
      app/cronetlib/src/cronet_impl_common_java-src.jar
  7. BIN
      app/cronetlib/src/cronet_impl_native_java-src.jar
  8. BIN
      app/cronetlib/src/cronet_impl_platform_java-src.jar
  9. 110
      app/download.gradle
  10. 1
      app/src/main/assets/cronet.json
  11. 17
      app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt
  12. 3
      app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt
  13. 89
      app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt
  14. 42
      app/src/main/res/values-pt-rBR/strings.xml
  15. 1
      build.gradle
  16. 2
      gradle.properties

@ -22,6 +22,8 @@ jobs:
UPLOAD_ARTIFACT: 'true' UPLOAD_ARTIFACT: 'true'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: |

@ -22,6 +22,8 @@ jobs:
UPLOAD_ARTIFACT: 'true' UPLOAD_ARTIFACT: 'true'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/cache@v2 - uses: actions/cache@v2
with: with:
path: | path: |

1
app/.gitignore vendored

@ -1 +1,2 @@
/build /build
/so

@ -3,6 +3,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'de.timfreiheit.resourceplaceholders' apply plugin: 'de.timfreiheit.resourceplaceholders'
apply from:'download.gradle'
static def releaseTime() { static def releaseTime() {
return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+8")) return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+8"))
@ -55,14 +56,17 @@ android {
} }
buildTypes { buildTypes {
release { release {
buildConfigField "String", "Cronet_Version", "\"$CronetVersion\""
if (project.hasProperty("RELEASE_STORE_FILE")) { if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.myConfig signingConfig signingConfigs.myConfig
} }
applicationIdSuffix '.release' applicationIdSuffix '.release'
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug { debug {
buildConfigField "String", "Cronet_Version", "\"$CronetVersion\""
if (project.hasProperty("RELEASE_STORE_FILE")) { if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.myConfig signingConfig signingConfigs.myConfig
} }

@ -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()
}
}

@ -0,0 +1 @@
{"arm64-v8a":"1f4e088f6e00175e12ee153e4004d283","armeabi-v7a":"a6726219c9a6217b95763baa3d61eb18","x86":"73b6a220fe16e0cdeebf1094980825c2","x86_64":"12fd5bb0a12664294b64fffccd326347","version":"92.0.4515.159"}

@ -1,6 +1,7 @@
package io.legado.app.help.http.cronet package io.legado.app.help.http.cronet
import android.util.Log import android.util.Log
import com.google.android.gms.net.CronetProviderInstaller
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import okhttp3.Headers import okhttp3.Headers
import okhttp3.MediaType import okhttp3.MediaType
@ -18,22 +19,24 @@ import java.util.concurrent.Executors
val executor: Executor by lazy { Executors.newCachedThreadPool() } val executor: Executor by lazy { Executors.newCachedThreadPool() }
val cronetEngine: ExperimentalCronetEngine by lazy { val cronetEngine: ExperimentalCronetEngine by lazy {
CronetLoader.preDownload() if (AppConfig.isGooglePlay) {
CronetProviderInstaller.installProvider(appCtx)
} else {
CronetLoader.preDownload()
}
val builder = ExperimentalCronetEngine.Builder(appCtx).apply { val builder = ExperimentalCronetEngine.Builder(appCtx).apply {
if (!AppConfig.isGooglePlay) { if (!AppConfig.isGooglePlay&&CronetLoader.install()) {
setLibraryLoader(CronetLoader)//设置自定义so库加载 setLibraryLoader(CronetLoader)//设置自定义so库加载
} }
setStoragePath(appCtx.externalCacheDir?.absolutePath)//设置缓存路径 setStoragePath(appCtx.externalCacheDir?.absolutePath)//设置缓存路径
enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50))//设置缓存模式 enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50).toLong())//设置缓存模式
enableQuic(true)//设置支持http/3 enableQuic(true)//设置支持http/3
enableHttp2(true) //设置支持http/2 enableHttp2(true) //设置支持http/2
enablePublicKeyPinningBypassForLocalTrustAnchors(true) enablePublicKeyPinningBypassForLocalTrustAnchors(true)
//enableNetworkQualityEstimator(true)
//Brotli压缩 enableBrotli(true)//Brotli压缩
enableBrotli(true)
//setExperimentalOptions("{\"quic_version\": \"h3-29\"}")
} }
val engine = builder.build() val engine = builder.build()
Log.d("Cronet", "Cronet Version:" + engine.versionString) Log.d("Cronet", "Cronet Version:" + engine.versionString)

@ -15,6 +15,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
} else try { } else try {
//移除Keep-Alive,手动设置会导致400 BadRequest //移除Keep-Alive,手动设置会导致400 BadRequest
builder.removeHeader("Keep-Alive") builder.removeHeader("Keep-Alive")
builder.removeHeader("Accept-Encoding")
val cookieStr = getCookie(original.url) val cookieStr = getCookie(original.url)
//设置Cookie //设置Cookie
if (cookieStr.length > 3) { if (cookieStr.length > 3) {
@ -44,7 +45,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
val callback = CronetUrlRequestCallback(request, call) val callback = CronetUrlRequestCallback(request, call)
val urlRequest = buildRequest(request, callback) val urlRequest = buildRequest(request, callback)
urlRequest.start() urlRequest.start()
return callback.waitForDone(urlRequest) return callback.waitForDone(urlRequest)
} }
private fun getCookie(url: HttpUrl): String { private fun getCookie(url: HttpUrl): String {

@ -6,11 +6,12 @@ import android.content.pm.ApplicationInfo
import android.os.Build import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import android.util.Log 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.AppConfig
import io.legado.app.help.coroutine.Coroutine 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.chromium.net.CronetEngine
import org.json.JSONObject
import splitties.init.appCtx import splitties.init.appCtx
import java.io.* import java.io.*
import java.math.BigInteger import java.math.BigInteger
@ -19,28 +20,25 @@ import java.net.URL
import java.security.MessageDigest import java.security.MessageDigest
import java.util.* import java.util.*
object CronetLoader : CronetEngine.Builder.LibraryLoader() { 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://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.127.so.js //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 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 const val soName = "libcronet.$soVersion.so"
private val soUrl: String private val soUrl: String
private val md5Url: String
private val soFile: File private val soFile: File
private val downloadFile: File private val downloadFile: File
private var cpuAbi: String? = null private var cpuAbi: String? = null
private var md5: String? = appCtx.getPrefString("soMd5") private var md5: String
private val version: String? = appCtx.getPrefString("soVersion", soVersion)
var download = false var download = false
init { init {
soUrl = ("https://storage.googleapis.com/chromium-cronet/android/" soUrl = ("https://storage.googleapis.com/chromium-cronet/android/"
+ soVersion + "/Release/cronet/libs/" + soVersion + "/Release/cronet/libs/"
+ getCpuAbi(appCtx) + "/" + soName) + getCpuAbi(appCtx) + "/" + soName)
md5Url = ("https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@" + md5 = getMd5(appCtx)
soVersion + "/cronet/" + soVersion + "/"
+ getCpuAbi(appCtx) + "/" + soName + ".js")
val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE) val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE)
soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName) soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName)
downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName) downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName)
@ -54,13 +52,20 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
if (AppConfig.isGooglePlay) { if (AppConfig.isGooglePlay) {
return true return true
} }
if (md5.length != 32 || !soFile.exists() || md5 != getFileMD5(soFile)) {
return false
}
return soFile.exists() return soFile.exists()
} }
fun preDownload() { fun preDownload() {
if (AppConfig.isGooglePlay) return if (AppConfig.isGooglePlay) {
CronetProviderInstaller.installProvider(appCtx)
return
}
Coroutine.async { Coroutine.async {
md5 = getUrlMd5(md5Url) //md5 = getUrlMd5(md5Url)
if (soFile.exists() && md5 == getFileMD5(soFile)) { if (soFile.exists() && md5 == getFileMD5(soFile)) {
Log.e(TAG, "So 库已存在") Log.e(TAG, "So 库已存在")
} else { } 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") @SuppressLint("UnsafeDynamicallyLoadedCode")
override fun loadLibrary(libName: String) { override fun loadLibrary(libName: String) {
Log.e(TAG, "libName:$libName") Log.e(TAG, "libName:$libName")
@ -89,9 +115,9 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
//如果找不到,则从远程下载 //如果找不到,则从远程下载
//删除历史文件 //删除历史文件
deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile) deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile)
md5 = getUrlMd5(md5Url) //md5 = getUrlMd5(md5Url)
Log.i(TAG, "soMD5:$md5") Log.i(TAG, "soMD5:$md5")
if (md5 == null || md5!!.length != 32 || soUrl.isEmpty()) { if (md5.length != 32 || soUrl.isEmpty()) {
//如果md5或下载的url为空,则调用系统行为进行加载 //如果md5或下载的url为空,则调用系统行为进行加载
System.loadLibrary(libName) System.loadLibrary(libName)
return return
@ -144,39 +170,6 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
return cpuAbi 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
}
}
/** /**
* 删除历史文件 * 删除历史文件

@ -315,9 +315,9 @@
<string name="line_size">Espaço entre linhas</string> <string name="line_size">Espaço entre linhas</string>
<string name="paragraph_size">Espaço entre parágrafos</string> <string name="paragraph_size">Espaço entre parágrafos</string>
<string name="to_top">Na parte superior</string> <string name="to_top">Na parte superior</string>
<string name="selection_to_top">Seleção no topo</string> <string name="selection_to_top">Seleção até o topo</string>
<string name="to_bottom">Na parte inferior</string> <string name="to_bottom">Na parte inferior</string>
<string name="selection_to_bottom">Seleção no fundo</string> <string name="selection_to_bottom">Seleção até o final</string>
<string name="auto_expand_find">Expansão automática de Descoberta</string> <string name="auto_expand_find">Expansão automática de Descoberta</string>
<string name="default_expand_first">A expansão padrão da primeira Descoberta.</string> <string name="default_expand_first">A expansão padrão da primeira Descoberta.</string>
<string name="threads_num">Linhas atuais %s</string> <string name="threads_num">Linhas atuais %s</string>
@ -393,11 +393,11 @@
<string name="source_group">源分组(fonteGrupo)</string> <string name="source_group">源分组(fonteGrupo)</string>
<string name="diy_source_group">自定义源分组</string> <string name="diy_source_group">自定义源分组</string>
<string name="diy_edit_source_group">输入自定义源分组名称</string> <string name="diy_edit_source_group">输入自定义源分组名称</string>
<string name="source_concurrent_rate">并发率(concurrentRate)</string> <string name="source_concurrent_rate">并发率(taxaSimultânea)</string>
<string name="sort_url">分类Url(sortUrl)</string> <string name="sort_url">分类Url(ordenarUrl)</string>
<string name="login_url">登录URL(loginUrl)</string> <string name="login_url">登录URL(loginUrl)</string>
<string name="login_ui">UI(loginUi)</string> <string name="login_ui">登UI(loginIU)</string>
<string name="login_check_js">登录检查JS(loginCheckJs)</string> <string name="login_check_js">登录检查JS(loginVerifJs)</string>
<string name="comment">源注释(fonteComentário)</string> <string name="comment">源注释(fonteComentário)</string>
<string name="r_search_url">搜索地址(url)</string> <string name="r_search_url">搜索地址(url)</string>
<string name="r_find_url">发现地址规则(url)</string> <string name="r_find_url">发现地址规则(url)</string>
@ -849,20 +849,20 @@
<string name="reset">Resetar</string> <string name="reset">Resetar</string>
<string name="null_url">Nenhuma url</string> <string name="null_url">Nenhuma url</string>
<string name="dict">Dicionários</string> <string name="dict">Dicionários</string>
<string name="unknown_error">未知错误</string> <string name="unknown_error">Erro desconhecido</string>
<string name="end">end</string> <string name="end">fim</string>
<string name="custom_group_summary">关闭替换分组/开启添加分组</string> <string name="custom_group_summary">Desativar substituir agrupamento / Ativar adicionar agrupamento</string>
<string name="pref_media_button_per_next">媒体按钮•上一首|下一首</string> <string name="pref_media_button_per_next">Botões de mídia - Anterior|Próximo</string>
<string name="pref_media_button_per_next_summary">上一段|下一段/上一章|下一章</string> <string name="pref_media_button_per_next_summary">Anterior|Próximo Parágrafo/Anterior|Próximo Capítulo</string>
<string name="read_aloud_by_page_summary">及时翻页,翻页时会停顿一下</string> <string name="read_aloud_by_page_summary">Virar as páginas durante tempo, com uma pausa ao virar as páginas</string>
<string name="check_source_show_debug_message">Verificar a fonte do livro mostra uma mensagem de depuração</string> <string name="check_source_show_debug_message">Marcando a fonte do livro mostra uma mensagem de depuração</string>
<string name="check_source_show_debug_message_summary">Exibir etapas de solicitação de rede e tempo durante a verificação da fonte do livro</string> <string name="check_source_show_debug_message_summary">Mostrar o status da rede com a data e hora durante a verificação da fonte</string>
<string name="export_no_chapter_name">No export chapter names</string> <string name="export_no_chapter_name">Não há nomes de capítulos de exportação</string>
<string name="autobackup_fail">Autobackup failed</string> <string name="autobackup_fail">Auto-Backup falhou</string>
<string name="background_image_blurring">Background image blurring</string> <string name="background_image_blurring">Desfocagem da imagem de fundo</string>
<string name="background_image_blurring_radius">Blurring radius</string> <string name="background_image_blurring_radius">Raio da desfocagem</string>
<string name="background_image_hint">Disabled when 0, enable range from 1 to 25\nThe greater the radius, the stronger the effect of blurring</string> <string name="background_image_hint">Desativado quando 0, Ativado entre 1 e 25\n Quanto maior o raio, mais forte o efeito de desfocagem</string>
<string name="need_login">需登录</string> <string name="need_login">Login necessário</string>
<string name="pref_cronet_summary">使用Cronet网络组件</string> <string name="pref_cronet_summary">Usando componentes de rede Cronet</string>
</resources> </resources>

@ -13,6 +13,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.0.1' classpath 'com.android.tools.build:gradle:7.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4' classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4'
classpath 'de.undercouch:gradle-download-task:4.1.2'
} }
} }

@ -21,3 +21,5 @@ android.enableJetifier=true
kotlin.code.style=official kotlin.code.style=official
android.enableResourceOptimizations=true android.enableResourceOptimizations=true
CronetVersion=92.0.4515.159

Loading…
Cancel
Save