Merge remote-tracking branch 'origin/master'

pull/1994/head
kunfei 3 years ago
commit 6f4bb0f387
  1. 2
      app/build.gradle
  2. BIN
      app/cronetlib/cronet_api.jar
  3. BIN
      app/cronetlib/cronet_impl_common_java.jar
  4. BIN
      app/cronetlib/cronet_impl_native_java.jar
  5. 2
      app/src/main/assets/cronet.json
  6. 1
      app/src/main/assets/updateLog.md
  7. 41
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
  8. 3
      app/src/main/java/io/legado/app/model/ReadBook.kt
  9. 10
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBook.kt
  10. 31
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookActivity.kt
  11. 21
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookAdapter.kt
  12. 2
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookManager.kt
  13. 9
      app/src/main/java/io/legado/app/ui/book/remote/RemoteBookViewModel.kt
  14. 27
      app/src/main/java/io/legado/app/ui/book/remote/manager/RemoteBookWebDav.kt
  15. 2
      app/src/main/res/layout/activity_remote_book.xml
  16. 1
      app/src/main/res/values-es-rES/strings.xml
  17. 1
      app/src/main/res/values-ja-rJP/strings.xml
  18. 1
      app/src/main/res/values-pt-rBR/strings.xml
  19. 1
      app/src/main/res/values-zh-rHK/strings.xml
  20. 1
      app/src/main/res/values-zh-rTW/strings.xml
  21. 1
      app/src/main/res/values-zh/strings.xml
  22. 1
      app/src/main/res/values/strings.xml
  23. 2
      gradle.properties

@ -190,7 +190,7 @@ dependencies {
//media //media
implementation("androidx.media:media:1.6.0") implementation("androidx.media:media:1.6.0")
def exoplayer_version = '2.17.1' def exoplayer_version = '2.18.0'
implementation("com.google.android.exoplayer:exoplayer-core:$exoplayer_version") implementation("com.google.android.exoplayer:exoplayer-core:$exoplayer_version")
implementation("com.google.android.exoplayer:extension-okhttp:$exoplayer_version") implementation("com.google.android.exoplayer:extension-okhttp:$exoplayer_version")

Binary file not shown.

@ -1 +1 @@
{"x86_64":"136f62aca55e9479aa3994213be6ca1d","x86":"2a7c8d7107832839d2459334c1d8f966","armeabi-v7a":"d890a3023e76d3e942485b80c74f8e9b","arm64-v8a":"d4bb1dce5666d38fda7c46a53f2c5aaa","version":"102.0.5005.125"} {"x86_64":"cbb7148b4ac5771e6ebd9392bfb65010","armeabi-v7a":"f4006d0fef882d5c09f75e59e5256933","arm64-v8a":"ee4a9a6c4b5faf0588a050f3fbd0f0b5","x86":"a323610b6994b79bbf17223da1192945","version":"103.0.5060.53"}

@ -13,6 +13,7 @@
**2022/06/16** **2022/06/16**
* 更新cronet: 103.0.5060.53
* 导出使用单线程,导出占用大量内存,并行容易OOM * 导出使用单线程,导出占用大量内存,并行容易OOM
* 导出到WebDav文件夹改为books,方便导入 * 导出到WebDav文件夹改为books,方便导入
* WebDav导入基本能用了 * WebDav导入基本能用了

@ -6,6 +6,7 @@ import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.http.newCallResponse import io.legado.app.help.http.newCallResponse
import io.legado.app.help.http.okHttpClient import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text import io.legado.app.help.http.text
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.printOnDebug import io.legado.app.utils.printOnDebug
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody import okhttp3.RequestBody.Companion.asRequestBody
@ -61,24 +62,28 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
/** /**
* 填充文件信息实例化WebDAVFile对象时并没有将远程文件的信息填充到实例中需要手动填充 * 获取当前url文件信息
* @return 远程文件是否存在
*/ */
suspend fun indexFileInfo(): Boolean { suspend fun getWebDavFile(): WebDavFile? {
return !propFindResponse(ArrayList()).isNullOrEmpty() return kotlin.runCatching {
propFindResponse(depth = 0)?.let {
return parseBody(it).firstOrNull()
}
}.getOrNull()
} }
/** /**
* 列出当前路径下的文件 * 列出当前路径下的文件
*
* @return 文件列表 * @return 文件列表
*/ */
@Throws(WebDavException::class) @Throws(WebDavException::class)
suspend fun listFiles(): List<WebDavFile> { suspend fun listFiles(): List<WebDavFile> {
propFindResponse()?.let { body -> propFindResponse()?.let { body ->
return parseDir(body) return parseBody(body).filter {
it.path != path
} }
return ArrayList() }
return emptyList()
} }
/** /**
@ -112,12 +117,15 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
}.body?.text() }.body?.text()
} }
private fun parseDir(s: String): List<WebDavFile> { /**
* 解析webDav返回的xml
*/
private fun parseBody(s: String): List<WebDavFile> {
val list = ArrayList<WebDavFile>() val list = ArrayList<WebDavFile>()
val document = Jsoup.parse(s) val document = Jsoup.parse(s)
val elements = document.getElementsByTag("d:response") val elements = document.getElementsByTag("d:response")
httpUrl?.let { urlStr -> httpUrl?.let { urlStr ->
val baseUrl = if (urlStr.endsWith("/")) urlStr else "$urlStr/" val baseUrl = NetworkUtils.getBaseUrl(urlStr)
for (element in elements) { for (element in elements) {
val href = element.getElementsByTag("d:href")[0].text() val href = element.getElementsByTag("d:href")[0].text()
if (!href.endsWith("/")) { if (!href.endsWith("/")) {
@ -137,11 +145,12 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
val lastModify: Long = kotlin.runCatching { val lastModify: Long = kotlin.runCatching {
element.getElementsByTag("d:getlastmodified") element.getElementsByTag("d:getlastmodified")
.firstOrNull()?.text()?.let { .firstOrNull()?.text()?.let {
LocalDateTime.parse(it, dateTimeFormatter).toInstant(ZoneOffset.of("+8")).toEpochMilli() LocalDateTime.parse(it, dateTimeFormatter)
.toInstant(ZoneOffset.of("+8")).toEpochMilli()
} }
}.getOrNull() ?: 0 }.getOrNull() ?: 0
webDavFile = WebDavFile( webDavFile = WebDavFile(
baseUrl + fileName, "$baseUrl$href",
authorization, authorization,
displayName = fileName, displayName = fileName,
urlName = urlName, urlName = urlName,
@ -163,12 +172,7 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
* 文件是否存在 * 文件是否存在
*/ */
suspend fun exists(): Boolean { suspend fun exists(): Boolean {
return kotlin.runCatching { return getWebDavFile() != null
val response = propFindResponse(depth = 0) ?: return false
val document = Jsoup.parse(response)
val elements = document.getElementsByTag("d:response")
return elements.isNotEmpty()
}.getOrDefault(false)
} }
/** /**
@ -297,6 +301,9 @@ open class WebDav(urlStr: String, val authorization: Authorization) {
}.isSuccess }.isSuccess
} }
/**
* 检测返回结果是否正确
*/
private fun checkResult(response: Response) { private fun checkResult(response: Response) {
if (!response.isSuccessful) { if (!response.isSuccessful) {
throw WebDavException("${url}\n${response.code}:${response.message}") throw WebDavException("${url}\n${response.code}:${response.message}")

@ -21,6 +21,7 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import splitties.init.appCtx import splitties.init.appCtx
import kotlin.math.min
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@ -437,7 +438,7 @@ object ReadBook : CoroutineScope by MainScope() {
delay(1000) delay(1000)
download(i) download(i)
} }
val minChapterIndex = durChapterIndex - 5 val minChapterIndex = durChapterIndex - min(5, AppConfig.preDownloadNum)
for (i in durChapterIndex.minus(2) downTo minChapterIndex) { for (i in durChapterIndex.minus(2) downTo minChapterIndex) {
delay(1000) delay(1000)
download(i) download(i)

@ -2,9 +2,15 @@ package io.legado.app.ui.book.remote
data class RemoteBook( data class RemoteBook(
val filename: String, val filename: String,
val urlName: String, val path: String,
val size: Long, val size: Long,
val contentType: String, val contentType: String,
val lastModify: Long, val lastModify: Long,
val isOnBookShelf: Boolean val isOnBookShelf: Boolean
) ) {
val isDir by lazy {
contentType == "folder"
}
}

@ -12,6 +12,7 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.databinding.ActivityRemoteBookBinding import io.legado.app.databinding.ActivityRemoteBookBinding
import io.legado.app.ui.book.remote.manager.RemoteBookWebDav
import io.legado.app.ui.widget.dialog.WaitDialog import io.legado.app.ui.widget.dialog.WaitDialog
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
@ -50,7 +51,7 @@ class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding, RemoteBookV
adapter.setItems(remoteBooks) adapter.setItems(remoteBooks)
} }
} }
viewModel.loadRemoteBookList() viewModel.loadRemoteBookList(RemoteBookWebDav.rootBookUrl)
} }
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@ -61,12 +62,38 @@ class RemoteBookActivity : VMBaseActivity<ActivityRemoteBookBinding, RemoteBookV
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_refresh -> { R.id.menu_refresh -> {
viewModel.loadRemoteBookList() viewModel.loadRemoteBookList(
viewModel.dirList.lastOrNull()?.path ?: RemoteBookWebDav.rootBookUrl
)
} }
} }
return super.onCompatOptionsItemSelected(item) return super.onCompatOptionsItemSelected(item)
} }
override fun finish() {
if (viewModel.dirList.isEmpty()) {
super.finish()
} else {
viewModel.dirList.removeLastOrNull()
val remoteBook = viewModel.dirList.lastOrNull()
viewModel.dataCallback?.clear()
if (remoteBook != null) {
binding.titleBar.title = remoteBook.filename
viewModel.loadRemoteBookList(remoteBook.path)
} else {
binding.titleBar.setTitle(R.string.remote_book)
viewModel.loadRemoteBookList(RemoteBookWebDav.rootBookUrl)
}
}
}
override fun openDir(remoteBook: RemoteBook) {
viewModel.dirList.add(remoteBook)
binding.titleBar.title = remoteBook.filename
viewModel.dataCallback?.clear()
viewModel.loadRemoteBookList(remoteBook.path)
}
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
override fun addToBookshelf(remoteBook: RemoteBook) { override fun addToBookshelf(remoteBook: RemoteBook) {
waitDialog.show() waitDialog.show()

@ -2,6 +2,7 @@ package io.legado.app.ui.book.remote
import android.content.Context import android.content.Context
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isGone
import cn.hutool.core.date.LocalDateTimeUtil import cn.hutool.core.date.LocalDateTimeUtil
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
@ -13,8 +14,8 @@ import io.legado.app.utils.ConvertUtils
* 适配器 * 适配器
* @author qianfanguojin * @author qianfanguojin
*/ */
class RemoteBookAdapter (context: Context, val callBack: CallBack) : class RemoteBookAdapter(context: Context, val callBack: CallBack) :
RecyclerAdapter<RemoteBook, ItemRemoteBookBinding>(context){ RecyclerAdapter<RemoteBook, ItemRemoteBookBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemRemoteBookBinding { override fun getViewBinding(parent: ViewGroup): ItemRemoteBookBinding {
return ItemRemoteBookBinding.inflate(inflater, parent, false) return ItemRemoteBookBinding.inflate(inflater, parent, false)
@ -37,21 +38,31 @@ class RemoteBookAdapter (context: Context, val callBack: CallBack) :
tvName.text = item.filename.substringBeforeLast(".") tvName.text = item.filename.substringBeforeLast(".")
tvContentType.text = item.contentType tvContentType.text = item.contentType
tvSize.text = ConvertUtils.formatFileSize(item.size) tvSize.text = ConvertUtils.formatFileSize(item.size)
tvDate.text = LocalDateTimeUtil.format(LocalDateTimeUtil.of(item.lastModify), "yyyy-MM-dd") tvDate.text =
LocalDateTimeUtil.format(LocalDateTimeUtil.of(item.lastModify), "yyyy-MM-dd")
llInfo.isGone = item.isDir
tvContentType.isGone = item.isDir
btnDownload.isGone = item.isDir
} }
} }
override fun registerListener(holder: ItemViewHolder, binding: ItemRemoteBookBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemRemoteBookBinding) {
binding.root.setOnClickListener {
getItem(holder.layoutPosition)?.let {
if (it.isDir) {
callBack.openDir(it)
}
}
}
binding.btnDownload.setOnClickListener { binding.btnDownload.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
callBack.addToBookshelf(it) callBack.addToBookshelf(it)
} }
} }
} }
interface CallBack { interface CallBack {
fun openDir(remoteBook: RemoteBook)
fun addToBookshelf(remoteBook: RemoteBook) fun addToBookshelf(remoteBook: RemoteBook)
} }
} }

@ -5,7 +5,7 @@ import android.net.Uri
abstract class RemoteBookManager { abstract class RemoteBookManager {
protected val remoteBookFolder : String = "books" protected val remoteBookFolder : String = "books"
abstract suspend fun initRemoteContext() abstract suspend fun initRemoteContext()
abstract suspend fun getRemoteBookList(): MutableList<RemoteBook> abstract suspend fun getRemoteBookList(path: String): MutableList<RemoteBook>
abstract suspend fun upload(localBookUri: Uri): Boolean abstract suspend fun upload(localBookUri: Uri): Boolean
abstract suspend fun delete(remoteBookUrl: String): Boolean abstract suspend fun delete(remoteBookUrl: String): Boolean

@ -13,7 +13,10 @@ import kotlinx.coroutines.flow.flowOn
import java.util.* import java.util.*
class RemoteBookViewModel(application: Application): BaseViewModel(application){ class RemoteBookViewModel(application: Application): BaseViewModel(application){
private var dataCallback : DataCallback? = null
val dirList = arrayListOf<RemoteBook>()
var dataCallback: DataCallback? = null
val dataFlow = callbackFlow<List<RemoteBook>> { val dataFlow = callbackFlow<List<RemoteBook>> {
@ -49,10 +52,10 @@ class RemoteBookViewModel(application: Application): BaseViewModel(application){
} }
} }
fun loadRemoteBookList() { fun loadRemoteBookList(path: String) {
execute { execute {
dataCallback?.clear() dataCallback?.clear()
val bookList = RemoteBookWebDav.getRemoteBookList() val bookList = RemoteBookWebDav.getRemoteBookList(path)
dataCallback?.setItems(bookList) dataCallback?.setItems(bookList)
}.onError { }.onError {
context.toastOnUi("获取webDav书籍出错\n${it.localizedMessage}") context.toastOnUi("获取webDav书籍出错\n${it.localizedMessage}")

@ -19,7 +19,7 @@ import java.io.File
import java.net.URLDecoder import java.net.URLDecoder
object RemoteBookWebDav : RemoteBookManager() { object RemoteBookWebDav : RemoteBookManager() {
private val remoteBookUrl get() = "${AppWebDav.rootWebDavUrl}${remoteBookFolder}" val rootBookUrl get() = "${AppWebDav.rootWebDavUrl}${remoteBookFolder}"
init { init {
runBlocking { runBlocking {
@ -29,7 +29,7 @@ object RemoteBookWebDav : RemoteBookManager() {
override suspend fun initRemoteContext() { override suspend fun initRemoteContext() {
AppWebDav.authorization?.let { AppWebDav.authorization?.let {
WebDav(remoteBookUrl, it).makeAsDir() WebDav(rootBookUrl, it).makeAsDir()
} }
} }
@ -37,20 +37,24 @@ object RemoteBookWebDav : RemoteBookManager() {
* 获取远程书籍列表 * 获取远程书籍列表
*/ */
@Throws(Exception::class) @Throws(Exception::class)
override suspend fun getRemoteBookList(): MutableList<RemoteBook> { override suspend fun getRemoteBookList(path: String): MutableList<RemoteBook> {
val remoteBooks = mutableListOf<RemoteBook>() val remoteBooks = mutableListOf<RemoteBook>()
AppWebDav.authorization?.let { AppWebDav.authorization?.let {
//读取文件列表 //读取文件列表
val remoteWebDavFileList: List<WebDavFile> = WebDav(remoteBookUrl, it).listFiles() val remoteWebDavFileList: List<WebDavFile> = WebDav(path, it).listFiles()
//转化远程文件信息到本地对象 //转化远程文件信息到本地对象
remoteWebDavFileList.forEach { webDavFile -> remoteWebDavFileList.forEach { webDavFile ->
var webDavFileName = webDavFile.displayName var webDavFileName = webDavFile.displayName
var webDavUrlName = "${remoteBookUrl}${File.separator}${webDavFile.displayName}"
webDavFileName = URLDecoder.decode(webDavFileName, "utf-8") webDavFileName = URLDecoder.decode(webDavFileName, "utf-8")
webDavUrlName = URLDecoder.decode(webDavUrlName, "utf-8")
webDavFile.isDir
if (webDavFile.isDir) {
remoteBooks.add(
RemoteBook(
webDavFileName, webDavFile.path, webDavFile.size,
"folder", webDavFile.lastModify, false
)
)
} else {
//分割后缀 //分割后缀
val fileExtension = webDavFileName.substringAfterLast(".") val fileExtension = webDavFileName.substringAfterLast(".")
@ -59,12 +63,13 @@ object RemoteBookWebDav : RemoteBookManager() {
val isOnBookShelf = LocalBook.isOnBookShelf(webDavFileName) val isOnBookShelf = LocalBook.isOnBookShelf(webDavFileName)
remoteBooks.add( remoteBooks.add(
RemoteBook( RemoteBook(
webDavFileName, webDavUrlName, webDavFile.size, webDavFileName, webDavFile.path, webDavFile.size,
fileExtension, webDavFile.lastModify, isOnBookShelf fileExtension, webDavFile.lastModify, isOnBookShelf
) )
) )
} }
} }
}
} ?: throw NoStackTraceException("webDav没有配置") } ?: throw NoStackTraceException("webDav没有配置")
return remoteBooks return remoteBooks
} }
@ -74,7 +79,7 @@ object RemoteBookWebDav : RemoteBookManager() {
*/ */
override suspend fun getRemoteBook(remoteBook: RemoteBook): Uri? { override suspend fun getRemoteBook(remoteBook: RemoteBook): Uri? {
return AppWebDav.authorization?.let { return AppWebDav.authorization?.let {
val webdav = WebDav(remoteBook.urlName, it) val webdav = WebDav(remoteBook.path, it)
webdav.download().let { bytes -> webdav.download().let { bytes ->
LocalBook.saveBookFile(bytes, remoteBook.filename) LocalBook.saveBookFile(bytes, remoteBook.filename)
} }
@ -88,7 +93,7 @@ object RemoteBookWebDav : RemoteBookManager() {
if (!NetworkUtils.isAvailable()) return false if (!NetworkUtils.isAvailable()) return false
val localBookName = localBookUri.path?.substringAfterLast(File.separator) val localBookName = localBookUri.path?.substringAfterLast(File.separator)
val putUrl = "${remoteBookUrl}${File.separator}${localBookName}" val putUrl = "${rootBookUrl}${File.separator}${localBookName}"
AppWebDav.authorization?.let { AppWebDav.authorization?.let {
if (localBookUri.isContentScheme()) { if (localBookUri.isContentScheme()) {
WebDav(putUrl, it).upload( WebDav(putUrl, it).upload(

@ -10,7 +10,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:title="远程书籍" /> app:title="@string/remote_book" />
<io.legado.app.ui.widget.anima.RefreshProgressBar <io.legado.app.ui.widget.anima.RefreshProgressBar
android:id="@+id/refresh_progress_bar" android:id="@+id/refresh_progress_bar"

@ -996,4 +996,5 @@
<string name="error_decode_bitmap">Fail to decode bitmap</string> <string name="error_decode_bitmap">Fail to decode bitmap</string>
<string name="error_image_url_empty">Image url is empty, check replacement rules</string> <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -999,4 +999,5 @@
<string name="error_decode_bitmap">Fail to decode bitmap</string> <string name="error_decode_bitmap">Fail to decode bitmap</string>
<string name="error_image_url_empty">Image url is empty, check replacement rules</string> <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -999,4 +999,5 @@
<string name="error_decode_bitmap">Fail to decode bitmap</string> <string name="error_decode_bitmap">Fail to decode bitmap</string>
<string name="error_image_url_empty">Image url is empty, check replacement rules</string> <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -996,4 +996,5 @@
<string name="error_decode_bitmap">图片解码失败</string> <string name="error_decode_bitmap">图片解码失败</string>
<string name="error_image_url_empty">图片链接为空,检查替换净化规则</string> <string name="error_image_url_empty">图片链接为空,检查替换净化规则</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -998,4 +998,5 @@
<string name="error_decode_bitmap">图片解码失败</string> <string name="error_decode_bitmap">图片解码失败</string>
<string name="error_image_url_empty">图片链接为空,检查替换净化规则</string> <string name="error_image_url_empty">图片链接为空,检查替换净化规则</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -998,4 +998,5 @@
<string name="error_decode_bitmap">图片解码失败</string> <string name="error_decode_bitmap">图片解码失败</string>
<string name="error_image_url_empty">图片链接为空,检查替换净化规则</string> <string name="error_image_url_empty">图片链接为空,检查替换净化规则</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -999,4 +999,5 @@
<string name="error_decode_bitmap">Fail to decode bitmap</string> <string name="error_decode_bitmap">Fail to decode bitmap</string>
<string name="error_image_url_empty">Image url is empty, check replacement rules</string> <string name="error_image_url_empty">Image url is empty, check replacement rules</string>
<string name="variable_comment">变量说明(variableComment)</string> <string name="variable_comment">变量说明(variableComment)</string>
<string name="remote_book">远程书籍</string>
</resources> </resources>

@ -26,4 +26,4 @@ android.experimental.enableNewResourceShrinker.preciseShrinking=true
# and none from the library's dependencies, thereby reducing the size of the R class for that library. # and none from the library's dependencies, thereby reducing the size of the R class for that library.
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
#https://chromiumdash.appspot.com/releases?platform=Android #https://chromiumdash.appspot.com/releases?platform=Android
CronetVersion=102.0.5005.125 CronetVersion=103.0.5060.53

Loading…
Cancel
Save