Merge branch 'master' of https://github.com/52fisher/legado
This commit is contained in:
Binary file not shown.
Binary file not shown.
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"}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 替换规则变化
|
* 替换规则变化
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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? {
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user