合并上游更改

感谢:@821938089、@zhongerxll、@youke2580c、@Leroy-X、@Dark-Tide、@lhjgege、@3harmony、@mgz0227
This commit is contained in:
HapeLee committed 2025-09-01 00:57:17 +08:00
1 parent dccb72adab
commit 8eb4881767
87 files changed
+800 -365

No files matched your search

@@ -101,7 +101,7 @@ object DirectLinkUpload {
@Keep
data class Rule(
var uploadUrl: String, //上传url
var uploadUrl: String, //创建分享链接
var downloadUrlRule: String, //下载链接规则
var summary: String, //注释
var compress: Boolean = false, //是否压缩
@@ -988,6 +988,7 @@ interface JsExtensions : JsEncodeUtils {
// 新增 mimeType 参数,默认为 null(保持兼容性)
fun openUrl(url: String, mimeType: String? = null) {
require(url.length < 64 * 1024) { "openUrl parameter url too long" }
rhinoContext.ensureActive()
val source = getSource() ?: throw NoStackTraceException("openUrl source cannot be null")
appCtx.startActivity<OpenUrlConfirmActivity> {
@@ -3,6 +3,7 @@ package io.legato.kazusa.help.book
import android.graphics.BitmapFactory
import android.os.ParcelFileDescriptor
import androidx.documentfile.provider.DocumentFile
import com.script.rhino.runScriptWithContext
import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.constant.AppPattern
import io.legato.kazusa.constant.EventBus
@@ -28,7 +29,6 @@ import io.legato.kazusa.utils.getFile
import io.legato.kazusa.utils.isContentScheme
import io.legato.kazusa.utils.onEachParallel
import io.legato.kazusa.utils.postEvent
import com.script.rhino.runScriptWithContext
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.ensureActive
@@ -157,7 +157,7 @@ object BookHelp {
}
}
suspend fun saveContent(
fun saveContent(
bookSource: BookSource,
book: Book,
bookChapter: BookChapter,
@@ -404,7 +404,11 @@ object BookHelp {
bookChapter.getFileName()
)
if (file.exists()) {
return file.readText()
val string = file.readText()
if (string.isEmpty()) {
return null
}
return string
}
if (book.isLocal) {
val string = LocalBook.getContent(book, bookChapter)
@@ -570,6 +570,9 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val mouseWheelPage
get() = appCtx.getPrefBoolean(PreferKey.mouseWheelPage, true)
val paddingDisplayCutouts
get() = appCtx.getPrefBoolean(PreferKey.paddingDisplayCutouts, false)
var searchScope: String
get() = appCtx.getPrefString("searchScope") ?: ""
set(value) {
@@ -5,8 +5,8 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import androidx.annotation.Keep
import androidx.core.graphics.toColorInt
import com.google.android.material.color.MaterialColors
import io.legato.kazusa.R
import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.constant.PageAnim
import io.legato.kazusa.constant.PreferKey
@@ -30,8 +30,6 @@ import io.legato.kazusa.utils.printOnDebug
import io.legato.kazusa.utils.putPrefBoolean
import io.legato.kazusa.utils.putPrefInt
import io.legato.kazusa.utils.resizeAndRecycle
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import splitties.init.appCtx
import java.io.File
@@ -439,66 +437,55 @@ object ReadBookConfig {
return exportConfig
}
suspend fun import(byteArray: ByteArray): Result<Config> {
return kotlin.runCatching {
withContext(IO) {
val configZipPath = FileUtils.getPath(appCtx.externalCache, "readConfig.zip")
FileUtils.delete(configZipPath)
val zipFile = FileUtils.createFileIfNotExist(configZipPath)
zipFile.writeBytes(byteArray)
val configDir = appCtx.externalCache.getFile("readConfig")
configDir.createFolderReplace()
ZipUtils.unZipToPath(zipFile, configDir)
val configFile = configDir.getFile(configFileName)
val config: Config = GSON.fromJsonObject<Config>(configFile.readText()).getOrThrow()
if (config.textFont.isNotEmpty()) {
val fontName = FileUtils.getName(config.textFont)
val fontPath =
FileUtils.getPath(appCtx.externalFiles, "font", fontName)
if (!FileUtils.exist(fontPath)) {
configDir.getFile(fontName).copyTo(File(fontPath))
}
config.textFont = fontPath
}
if (config.bgType == 2) {
val bgName = FileUtils.getName(config.bgStr)
config.bgStr = bgName
val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName)
if (!FileUtils.exist(bgPath)) {
val bgFile = configDir.getFile(bgName)
if (bgFile.exists()) {
bgFile.copyTo(File(bgPath))
}
}
config.bgStr = bgPath
}
if (config.bgTypeNight == 2) {
val bgName = FileUtils.getName(config.bgStrNight)
config.bgStrNight = bgName
val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName)
if (!FileUtils.exist(bgPath)) {
val bgFile = configDir.getFile(bgName)
if (bgFile.exists()) {
bgFile.copyTo(File(bgPath))
}
}
config.bgStrNight = bgPath
}
if (config.bgTypeEInk == 2) {
val bgName = FileUtils.getName(config.bgStrEInk)
config.bgStrEInk = bgName
val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName)
if (!FileUtils.exist(bgPath)) {
val bgFile = configDir.getFile(bgName)
if (bgFile.exists()) {
bgFile.copyTo(File(bgPath))
}
}
config.bgStrEInk = bgPath
}
return@withContext config
fun import(byteArray: ByteArray): Config {
val configZipPath = FileUtils.getPath(appCtx.externalCache, "readConfig.zip")
FileUtils.delete(configZipPath)
val zipFile = FileUtils.createFileIfNotExist(configZipPath)
zipFile.writeBytes(byteArray)
val configDir = appCtx.externalCache.getFile("readConfig")
configDir.createFolderReplace()
ZipUtils.unZipToPath(zipFile, configDir)
val configFile = configDir.getFile(configFileName)
val config: Config = GSON.fromJsonObject<Config>(configFile.readText()).getOrThrow()
if (config.textFont.isNotEmpty()) {
val fontName = FileUtils.getName(config.textFont)
val fontPath =
FileUtils.getPath(appCtx.externalFiles, "font", fontName)
if (!FileUtils.exist(fontPath)) {
configDir.getFile(fontName).copyTo(File(fontPath))
}
config.textFont = fontPath
}
if (config.bgType == 2) {
val bgName = FileUtils.getName(config.bgStr)
config.bgStr = bgName
val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName)
if (!FileUtils.exist(bgPath)) {
val bgFile = configDir.getFile(bgName)
if (bgFile.exists()) {
bgFile.copyTo(File(bgPath))
}
}
config.bgStrNight = bgPath
} else if (config.bgTypeNight == 0) {
config.bgStrNight.toColorInt()
}
if (config.bgTypeEInk == 2) {
val bgName = FileUtils.getName(config.bgStrEInk)
config.bgStrEInk = bgName
val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName)
if (!FileUtils.exist(bgPath)) {
val bgFile = configDir.getFile(bgName)
if (bgFile.exists()) {
bgFile.copyTo(File(bgPath))
}
}
config.bgStrEInk = bgPath
} else if (config.bgTypeEInk == 0) {
config.bgStrEInk.toColorInt()
}
config.curTextColor()
return config
}
@Keep
@@ -2,19 +2,18 @@ package io.legato.kazusa.help.config
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Color
import android.util.DisplayMetrics
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.graphics.toColorInt
import io.legato.kazusa.R
import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.constant.EventBus
import io.legato.kazusa.constant.PreferKey
import io.legato.kazusa.constant.Theme
import io.legato.kazusa.help.DefaultData
import io.legato.kazusa.lib.theme.ThemeStore
import io.legato.kazusa.model.BookCover
import io.legato.kazusa.utils.BitmapUtils
import io.legato.kazusa.utils.ColorUtils
import io.legato.kazusa.utils.FileUtils
import io.legato.kazusa.utils.GSON
import io.legato.kazusa.utils.fromJsonArray
@@ -29,7 +28,6 @@ import io.legato.kazusa.utils.putPrefInt
import io.legato.kazusa.utils.stackBlur
import splitties.init.appCtx
import java.io.File
import androidx.core.graphics.toColorInt
@Keep
object ThemeConfig {
@@ -117,13 +115,18 @@ object ThemeConfig {
fun addConfig(json: String): Boolean {
GSON.fromJsonObject<Config>(json.trim { it < ' ' }).getOrNull()
?.let {
addConfig(it)
return true
if (validateConfig(it)) {
addConfig(it)
return true
}
}
return false
}
fun addConfig(newConfig: Config) {
if (!validateConfig(newConfig)) {
return
}
configList.forEachIndexed { index, config ->
if (newConfig.themeName == config.themeName) {
configList[index] = newConfig
@@ -134,6 +137,18 @@ object ThemeConfig {
save()
}
private fun validateConfig(config: Config): Boolean {
try {
config.primaryColor.toColorInt()
config.accentColor.toColorInt()
config.backgroundColor.toColorInt()
config.bottomBackground.toColorInt()
return true
} catch (_: Exception) {
return false
}
}
private fun getConfigs(): List<Config>? {
val configFile = File(configFilePath)
if (configFile.exists()) {
@@ -148,23 +163,27 @@ object ThemeConfig {
}
fun applyConfig(context: Context, config: Config) {
val primary = config.primaryColor.toColorInt()
val accent = config.accentColor.toColorInt()
val background = config.backgroundColor.toColorInt()
val bBackground = config.bottomBackground.toColorInt()
if (config.isNightTheme) {
context.putPrefInt(PreferKey.cNPrimary, primary)
context.putPrefInt(PreferKey.cNAccent, accent)
context.putPrefInt(PreferKey.cNBackground, background)
context.putPrefInt(PreferKey.cNBBackground, bBackground)
} else {
context.putPrefInt(PreferKey.cPrimary, primary)
context.putPrefInt(PreferKey.cAccent, accent)
context.putPrefInt(PreferKey.cBackground, background)
context.putPrefInt(PreferKey.cBBackground, bBackground)
try {
val primary = config.primaryColor.toColorInt()
val accent = config.accentColor.toColorInt()
val background = config.backgroundColor.toColorInt()
val bBackground = config.bottomBackground.toColorInt()
if (config.isNightTheme) {
context.putPrefInt(PreferKey.cNPrimary, primary)
context.putPrefInt(PreferKey.cNAccent, accent)
context.putPrefInt(PreferKey.cNBackground, background)
context.putPrefInt(PreferKey.cNBBackground, bBackground)
} else {
context.putPrefInt(PreferKey.cPrimary, primary)
context.putPrefInt(PreferKey.cAccent, accent)
context.putPrefInt(PreferKey.cBackground, background)
context.putPrefInt(PreferKey.cBBackground, bBackground)
}
AppConfig.isNightTheme = config.isNightTheme
applyDayNight(context)
} catch (e: Exception) {
AppLog.put("设置主题出错\n$e", e, true)
}
AppConfig.isNightTheme = config.isNightTheme
applyDayNight(context)
}
fun saveDayTheme(context: Context, name: String) {
@@ -12,7 +12,7 @@ import java.security.MessageDigest
* @radius: 0..25
*/
class BlurTransformation(
@IntRange(from = 0, to = 25) private val radius: Int
@param:IntRange(from = 0, to = 25) private val radius: Int
) : BitmapTransformation() {
override fun transform(
@@ -2,6 +2,7 @@ package io.legato.kazusa.help.http
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.ResponseBody.Companion.asResponseBody
import okhttp3.internal.http.promisesBody
import okio.buffer
@@ -24,7 +25,7 @@ object DecompressInterceptor : Interceptor {
val response = chain.proceed(requestBuilder.build())
val body = response.body
if (!transparentDecompress || !response.promisesBody() || body == null) {
if (!transparentDecompress || !response.promisesBody() || body == ResponseBody.EMPTY) {
return response
}
@@ -127,7 +127,7 @@ val okHttpClientManga by lazy {
val response = chain.proceed(request)
val url = request.url.toString()
response.newBuilder()
.body(ProgressResponseBody(url, LISTENER, response.body!!))
.body(ProgressResponseBody(url, LISTENER, response.body))
.build()
}
interceptors.add(1) { chain ->
@@ -18,8 +18,6 @@ import okhttp3.Protocol
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.Response
import okhttp3.internal.notifyAll
import okhttp3.internal.wait
import okio.Buffer
import okio.BufferedSink
import okio.Pipe
@@ -186,7 +184,7 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac
return try {
val response = getResponse(true)
if (hasBody(response) && response.code >= HTTP_BAD_REQUEST) {
response.body!!.byteStream()
response.body.byteStream()
} else null
} catch (e: IOException) {
null
@@ -258,7 +256,7 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac
}
val response = getResponse(false)
if (response.code >= HTTP_BAD_REQUEST) throw FileNotFoundException(url.toString())
return response.body!!.byteStream()
return response.body.byteStream()
}
@Throws(IOException::class)
@@ -1174,6 +1172,14 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac
throw AssertionError()
}
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE")
private inline fun Any.wait() = (this as Object).wait()
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE")
private inline fun Any.notify() = (this as Object).notify()
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE")
private inline fun Any.notifyAll() = (this as Object).notifyAll()
@Throws(Exception::class)
@JvmStatic
fun main(args: Array<String>) {
@@ -29,6 +29,7 @@ object SourceVerificationHelp {
* 获取书源验证结果
* 图片验证码 防爬 滑动验证码 点击字符 等等
*/
@Synchronized
fun getVerificationResult(
source: BaseSource?,
url: String,
@@ -38,9 +39,8 @@ object SourceVerificationHelp {
): String {
source
?: throw NoStackTraceException("getVerificationResult parameter source cannot be null")
if (isMainThread) {
error("getVerificationResult must be called on a background thread")
}
require(url.length < 64 * 1024) { "getVerificationResult parameter url too long" }
check(!isMainThread) { "getVerificationResult must be called on a background thread" }
clearResult(source.getKey())
@@ -84,6 +84,7 @@ object SourceVerificationHelp {
refetchAfterSuccess: Boolean? = true
) {
source ?: throw NoStackTraceException("startBrowser parameter source cannot be null")
require(url.length < 64 * 1024) { "startBrowser parameter url too long" }
appCtx.startActivity<WebViewActivity> {
putExtra("title", title)
putExtra("url", url)
@@ -1,6 +1,7 @@
package io.legato.kazusa.help.storage
import android.content.Context
import android.database.sqlite.SQLiteConstraintException
import android.net.Uri
import androidx.core.content.edit
import androidx.documentfile.provider.DocumentFile
@@ -106,7 +107,6 @@ object Restore {
.forEach { book ->
book.coverUrl = LocalBook.getCoverPath(book)
}
val updateBooks = arrayListOf<Book>()
val newBooks = arrayListOf<Book>()
val ignoreLocalBook = BackupConfig.ignoreLocalBook
it.forEach { book ->
@@ -114,12 +114,15 @@ object Restore {
return@forEach
}
if (appDb.bookDao.has(book.bookUrl)) {
updateBooks.add(book)
try {
appDb.bookDao.update(book)
} catch (_: SQLiteConstraintException) {
appDb.bookDao.insert(book)
}
} else {
newBooks.add(book)
}
}
appDb.bookDao.update(*updateBooks.toTypedArray())
appDb.bookDao.insert(*newBooks.toTypedArray())
}
fileToListT<Bookmark>(path, "bookmark.json")?.let {