pull/2130/head
fisher 2 years ago
commit 6be0cb9884
  1. BIN
      app/cronetlib/cronet_api.jar
  2. BIN
      app/cronetlib/cronet_impl_common_java.jar
  3. BIN
      app/cronetlib/cronet_impl_native_java.jar
  4. 2
      app/src/main/assets/cronet.json
  5. 2
      app/src/main/assets/defaultData/txtTocRule.json
  6. 4
      app/src/main/assets/updateLog.md
  7. 8
      app/src/main/java/io/legado/app/constant/AppConst.kt
  8. 2
      app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
  9. 2
      app/src/main/java/io/legado/app/lib/webdav/WebDav.kt
  10. 2
      app/src/main/java/io/legado/app/ui/book/local/rule/TxtTocRuleActivity.kt
  11. 28
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  12. 38
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  13. 29
      app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt
  14. 1
      app/src/main/java/io/legado/app/ui/document/HandleFileActivity.kt
  15. 11
      app/src/main/java/io/legado/app/ui/document/HandleFileContract.kt
  16. 30
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  17. 2
      app/src/main/java/io/legado/app/utils/DocumentExtensions.kt
  18. 3
      app/src/main/res/layout/activity_source_debug.xml
  19. 2
      gradle.properties

Binary file not shown.

@ -1 +1 @@
{"armeabi-v7a":"2001ec065243c76166fe5cc3c7ee2657","x86":"6d1301e1dc1c63eedd6ad9d57716972f","arm64-v8a":"ff861c88eaa622eba7f8a7af0672352b","x86_64":"5eb49453ef59221deaee4913a0d693b6","version":"103.0.5060.129"} {"armeabi-v7a":"a36bb21eec579e2c4bed0009eb0f1587","x86":"afce9bdd790c215374bb906919179ca5","arm64-v8a":"3ea9fa21d9b18860cfa12957c39dded1","x86_64":"717c1ebe0d1d3562f5c91c2b2606d2c2","version":"104.0.5112.69"}

@ -159,4 +159,4 @@
"example": "标题后面数字没有括号124", "example": "标题后面数字没有括号124",
"serialNumber": 18 "serialNumber": 18
} }
] ]

@ -11,11 +11,13 @@
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
**2022/07/31** **2022/08/02**
* 更新cronet: 104.0.5112.69
* 修复一些bug by 821938089 * 修复一些bug by 821938089
* 备份自定义直链上传规则 * 备份自定义直链上传规则
* 尝试修复epub加载OOM的问题 * 尝试修复epub加载OOM的问题
* 图片添加保存按钮
* 一些优化 * 一些优化
**2022/07/21** **2022/07/21**

@ -50,15 +50,17 @@ object AppConst {
const val notificationIdDownload = -1122395 const val notificationIdDownload = -1122395
const val notificationIdCheckSource = -1122395 const val notificationIdCheckSource = -1122395
const val imagePathKey = "imagePath"
val menuViewNames = arrayOf( val menuViewNames = arrayOf(
"com.android.internal.view.menu.ListMenuItemView", "com.android.internal.view.menu.ListMenuItemView",
"androidx.appcompat.view.menu.ListMenuItemView" "androidx.appcompat.view.menu.ListMenuItemView"
) )
@SuppressLint("PrivateResource") @SuppressLint("PrivateResource")
val sysElevation = val sysElevation = appCtx.resources
appCtx.resources.getDimension(com.google.android.material.R.dimen.design_appbar_elevation) .getDimension(com.google.android.material.R.dimen.design_appbar_elevation)
.toInt() .toInt()
val androidId: String by lazy { val androidId: String by lazy {
Settings.System.getString(appCtx.contentResolver, Settings.Secure.ANDROID_ID) ?: "null" Settings.System.getString(appCtx.contentResolver, Settings.Secure.ANDROID_ID) ?: "null"

@ -148,7 +148,7 @@ class Coroutine<T>(
success?.let { dispatchCallback(this, value, it) } success?.let { dispatchCallback(this, value, it) }
} catch (e: Throwable) { } catch (e: Throwable) {
e.printOnDebug() e.printOnDebug()
if (e is CancellationException && !isActive && e !is ActivelyCancelException) { if (e is CancellationException && e !is ActivelyCancelException && isCancelled) {
this@Coroutine.cancel() this@Coroutine.cancel()
} }
val consume: Boolean = errorReturn?.value?.let { value -> val consume: Boolean = errorReturn?.value?.let { value ->

@ -71,7 +71,7 @@ open class WebDav(val path: String, val authorization: Authorization) {
private val webDavClient by lazy { private val webDavClient by lazy {
val authInterceptor = Interceptor { chain -> val authInterceptor = Interceptor { chain ->
var request = chain.request() var request = chain.request()
if (request.url.host == host) { if (request.url.host.equals(host, true)) {
request = request request = request
.newBuilder() .newBuilder()
.header(authorization.name, authorization.data) .header(authorization.name, authorization.data)

@ -159,7 +159,7 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
private fun showImportDialog() { private fun showImportDialog() {
val aCache = ACache.get(cacheDir = false) val aCache = ACache.get(cacheDir = false)
val defaultUrl = "https://raw.githubusercontent.com/52fisher/YueDuJson/master/myTxtChapterRule.json" val defaultUrl = "https://gitee.com/fisher52/YueDuJson/raw/master/myTxtChapterRule.json"
val cacheUrls: MutableList<String> = aCache val cacheUrls: MutableList<String> = aCache
.getAsString(importTocRuleKey) .getAsString(importTocRuleKey)
?.splitNotBlank(",") ?.splitNotBlank(",")

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -14,10 +15,7 @@ import androidx.core.view.size
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.BookType import io.legado.app.constant.*
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.constant.Status
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
@ -58,6 +56,7 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.toc.TocActivityResult import io.legado.app.ui.book.toc.TocActivityResult
import io.legado.app.ui.browser.WebViewActivity import io.legado.app.ui.browser.WebViewActivity
import io.legado.app.ui.dict.DictDialog import io.legado.app.ui.dict.DictDialog
import io.legado.app.ui.document.HandleFileContract
import io.legado.app.ui.login.SourceLoginActivity import io.legado.app.ui.login.SourceLoginActivity
import io.legado.app.ui.replace.ReplaceRuleActivity import io.legado.app.ui.replace.ReplaceRuleActivity
import io.legado.app.ui.replace.edit.ReplaceEditActivity import io.legado.app.ui.replace.edit.ReplaceEditActivity
@ -129,6 +128,12 @@ class ReadBookActivity : BaseReadBookActivity(),
} }
} }
} }
private val selectImageDir = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri ->
ACache.get().put(AppConst.imagePathKey, uri.toString())
viewModel.saveImage(it.value, uri)
}
}
private var menu: Menu? = null private var menu: Menu? = null
private var autoPageJob: Job? = null private var autoPageJob: Job? = null
private var backupJob: Job? = null private var backupJob: Job? = null
@ -1001,13 +1006,26 @@ class ReadBookActivity : BaseReadBookActivity(),
popupAction.setItems( popupAction.setItems(
listOf( listOf(
SelectItem(getString(R.string.show), "show"), SelectItem(getString(R.string.show), "show"),
SelectItem(getString(R.string.refresh), "refresh") SelectItem(getString(R.string.refresh), "refresh"),
SelectItem(getString(R.string.action_save), "save"),
SelectItem(getString(R.string.select_folder), "selectFolder")
) )
) )
popupAction.onActionClick = { popupAction.onActionClick = {
when (it) { when (it) {
"show" -> showDialogFragment(PhotoDialog(src)) "show" -> showDialogFragment(PhotoDialog(src))
"refresh" -> viewModel.refreshImage(src) "refresh" -> viewModel.refreshImage(src)
"save" -> {
val path = ACache.get().getAsString(AppConst.imagePathKey)
if (path.isNullOrEmpty()) {
selectImageDir.launch {
value = src
}
} else {
viewModel.saveImage(src, Uri.parse(path))
}
}
"selectFolder" -> selectImageDir.launch()
} }
popupAction.dismiss() popupAction.dismiss()
} }

@ -2,6 +2,8 @@ package io.legado.app.ui.book.read
import android.app.Application import android.app.Application
import android.content.Intent import android.content.Intent
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.legado.app.R import io.legado.app.R
@ -28,12 +30,12 @@ import io.legado.app.service.BaseReadAloudService
import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.ui.book.read.page.provider.ImageProvider import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.ui.book.searchContent.SearchResult import io.legado.app.ui.book.searchContent.SearchResult
import io.legado.app.utils.msg import io.legado.app.utils.*
import io.legado.app.utils.postEvent
import io.legado.app.utils.toStringArray
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
/** /**
* 阅读界面数据处理 * 阅读界面数据处理
@ -425,6 +427,34 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} }
} }
@Suppress("BlockingMethodInNonBlockingContext")
fun saveImage(src: String?, uri: Uri) {
src ?: return
val book = ReadBook.book ?: return
execute {
val image = BookHelp.getImage(book, src)
FileInputStream(image).use { input ->
if (uri.isContentScheme()) {
DocumentFile.fromTreeUri(context, uri)?.let { doc ->
val imageDoc = DocumentUtils.createFileIfNotExist(doc, image.name)!!
context.contentResolver.openOutputStream(imageDoc.uri)!!.use { output ->
input.copyTo(output)
}
}
} else {
val dir = File(uri.path ?: uri.toString())
val file = FileUtils.createFileIfNotExist(dir, image.name)
FileOutputStream(file).use { output ->
input.copyTo(output)
}
}
}
}.onError {
AppLog.put("保存图片出错\n${it.localizedMessage}", it)
context.toastOnUi("保存图片出错\n${it.localizedMessage}")
}
}
/** /**
* 替换规则变化 * 替换规则变化
*/ */

@ -109,6 +109,35 @@ class BookSourceDebugActivity : VMBaseActivity<ActivitySourceDebugBinding, BookS
searchView.setQuery(binding.textFx.text, true) searchView.setQuery(binding.textFx.text, true)
} }
} }
binding.textInfo.onClick {
if (!searchView.query.isNullOrBlank()) {
searchView.setQuery(searchView.query, true)
}
}
binding.textToc.onClick {
val query = searchView.query
if (query.isNullOrBlank() || query.length <= 2) {
searchView.setQuery("++", false)
} else {
if (!query.startsWith("++")) {
searchView.setQuery("++$query", true)
} else {
searchView.setQuery(query, true)
}
}
}
binding.textContent.onClick {
val query = searchView.query
if (query.isNullOrBlank() || query.length <= 2) {
searchView.setQuery("--", false)
} else {
if (!query.startsWith("--")) {
searchView.setQuery("--$query", true)
} else {
searchView.setQuery(query, true)
}
}
}
} }
/** /**

@ -223,6 +223,7 @@ class HandleFileActivity :
} }
} }
} else { } else {
data.putExtra("value", intent.getStringExtra("value"))
setResult(RESULT_OK, data) setResult(RESULT_OK, data)
finish() finish()
} }

@ -32,15 +32,16 @@ class HandleFileContract :
intent.putExtra("fileKey", IntentData.put(fileData.second)) intent.putExtra("fileKey", IntentData.put(fileData.second))
intent.putExtra("contentType", fileData.third) intent.putExtra("contentType", fileData.third)
} }
intent.putExtra("value", it.value)
} }
return intent return intent
} }
override fun parseResult(resultCode: Int, intent: Intent?): Result { override fun parseResult(resultCode: Int, intent: Intent?): Result {
if (resultCode == RESULT_OK) { if (resultCode == RESULT_OK) {
return Result(intent?.data, requestCode) return Result(intent?.data, requestCode, intent?.getStringExtra("value"))
} }
return Result(null, requestCode) return Result(null, requestCode, intent?.getStringExtra("value"))
} }
companion object { companion object {
@ -57,12 +58,14 @@ class HandleFileContract :
var allowExtensions: Array<String> = arrayOf(), var allowExtensions: Array<String> = arrayOf(),
var otherActions: ArrayList<SelectItem<Int>>? = null, var otherActions: ArrayList<SelectItem<Int>>? = null,
var fileData: Triple<String, Any, String>? = null, var fileData: Triple<String, Any, String>? = null,
var requestCode: Int = 0 var requestCode: Int = 0,
var value: String? = null
) )
data class Result( data class Result(
val uri: Uri?, val uri: Uri?,
val requestCode: Int val requestCode: Int,
val value: String?
) )
} }

@ -14,9 +14,11 @@ import androidx.webkit.WebViewFeature
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.imagePathKey
import io.legado.app.databinding.ActivityRssReadBinding import io.legado.app.databinding.ActivityRssReadBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.SelectItem import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.model.Download import io.legado.app.model.Download
@ -38,15 +40,13 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
override val binding by viewBinding(ActivityRssReadBinding::inflate) override val binding by viewBinding(ActivityRssReadBinding::inflate)
override val viewModel by viewModels<ReadRssViewModel>() override val viewModel by viewModels<ReadRssViewModel>()
private val imagePathKey = "imagePath"
private var starMenuItem: MenuItem? = null private var starMenuItem: MenuItem? = null
private var ttsMenuItem: MenuItem? = null private var ttsMenuItem: MenuItem? = null
private var customWebViewCallback: WebChromeClient.CustomViewCallback? = null private var customWebViewCallback: WebChromeClient.CustomViewCallback? = null
private var webPic: String? = null private val selectImageDir = registerForActivityResult(HandleFileContract()) {
private val saveImage = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
ACache.get().put(imagePathKey, uri.toString()) ACache.get().put(imagePathKey, uri.toString())
viewModel.saveImage(webPic, uri) viewModel.saveImage(it.value, uri)
} }
} }
@ -135,8 +135,18 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
if (hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || if (hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
) { ) {
hitTestResult.extra?.let { hitTestResult.extra?.let { webPic ->
saveImage(it) selector(
arrayListOf(
SelectItem(getString(R.string.action_save), "save"),
SelectItem(getString(R.string.select_folder), "selectFolder")
)
) { _, charSequence, i ->
when (charSequence.value) {
"save" -> saveImage(webPic)
"selectFolder" -> selectSaveFolder(null)
}
}
return@setOnLongClickListener true return@setOnLongClickListener true
} }
} }
@ -153,23 +163,23 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
} }
private fun saveImage(webPic: String) { private fun saveImage(webPic: String) {
this.webPic = webPic
val path = ACache.get().getAsString(imagePathKey) val path = ACache.get().getAsString(imagePathKey)
if (path.isNullOrEmpty()) { if (path.isNullOrEmpty()) {
selectSaveFolder() selectSaveFolder(webPic)
} else { } else {
viewModel.saveImage(webPic, Uri.parse(path)) viewModel.saveImage(webPic, Uri.parse(path))
} }
} }
private fun selectSaveFolder() { private fun selectSaveFolder(webPic: String?) {
val default = arrayListOf<SelectItem<Int>>() val default = arrayListOf<SelectItem<Int>>()
val path = ACache.get().getAsString(imagePathKey) val path = ACache.get().getAsString(imagePathKey)
if (!path.isNullOrEmpty()) { if (!path.isNullOrEmpty()) {
default.add(SelectItem(path, -1)) default.add(SelectItem(path, -1))
} }
saveImage.launch { selectImageDir.launch {
otherActions = default otherActions = default
value = webPic
} }
} }

@ -31,7 +31,7 @@ object DocumentUtils {
vararg subDirs: String vararg subDirs: String
): DocumentFile? { ): DocumentFile? {
val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs) val parent: DocumentFile? = createFolderIfNotExist(root, *subDirs)
return parent?.createFile(mimeType, fileName) return parent?.findFile(fileName) ?: parent?.createFile(mimeType, fileName)
} }
fun createFolderIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? { fun createFolderIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? {

@ -132,6 +132,7 @@
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/text_info"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="3dp" android:layout_margin="3dp"
@ -156,6 +157,7 @@
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/text_toc"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="3dp" android:layout_margin="3dp"
@ -180,6 +182,7 @@
tools:ignore="HardcodedText" /> tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/text_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="3dp" android:layout_margin="3dp"

@ -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=103.0.5060.129 CronetVersion=104.0.5112.69

Loading…
Cancel
Save