This commit is contained in:
HapeLee committed 2025-06-04 13:33:51 +08:00
1 parent b86d90021c
commit 6c0757c91b
972 files changed
+6885 -7321

No files matched your search

@@ -0,0 +1,668 @@
package io.legato.kazusa.help.config
import android.content.SharedPreferences
import android.os.Build
import io.legato.kazusa.BuildConfig
import io.legato.kazusa.constant.PreferKey
import io.legato.kazusa.data.appDb
import io.legato.kazusa.utils.canvasrecorder.CanvasRecorderFactory
import io.legato.kazusa.utils.getPrefBoolean
import io.legato.kazusa.utils.getPrefInt
import io.legato.kazusa.utils.getPrefLong
import io.legato.kazusa.utils.getPrefString
import io.legato.kazusa.utils.isNightMode
import io.legato.kazusa.utils.putPrefBoolean
import io.legato.kazusa.utils.putPrefInt
import io.legato.kazusa.utils.putPrefLong
import io.legato.kazusa.utils.putPrefString
import io.legato.kazusa.utils.removePref
import io.legato.kazusa.utils.sysConfiguration
import io.legato.kazusa.utils.toastOnUi
import splitties.init.appCtx
@Suppress("MemberVisibilityCanBePrivate", "ConstPropertyName")
object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val isCronet = appCtx.getPrefBoolean(PreferKey.cronet)
var useAntiAlias = appCtx.getPrefBoolean(PreferKey.antiAlias)
var userAgent: String = getPrefUserAgent()
var isEInkMode = appCtx.getPrefString(PreferKey.themeMode) == "3"
var clickActionTL = appCtx.getPrefInt(PreferKey.clickActionTL, 2)
var clickActionTC = appCtx.getPrefInt(PreferKey.clickActionTC, 2)
var clickActionTR = appCtx.getPrefInt(PreferKey.clickActionTR, 1)
var clickActionML = appCtx.getPrefInt(PreferKey.clickActionML, 2)
var clickActionMC = appCtx.getPrefInt(PreferKey.clickActionMC, 0)
var clickActionMR = appCtx.getPrefInt(PreferKey.clickActionMR, 1)
var clickActionBL = appCtx.getPrefInt(PreferKey.clickActionBL, 2)
var clickActionBC = appCtx.getPrefInt(PreferKey.clickActionBC, 1)
var clickActionBR = appCtx.getPrefInt(PreferKey.clickActionBR, 1)
var themeMode = appCtx.getPrefString(PreferKey.themeMode, "0")
var useDefaultCover = appCtx.getPrefBoolean(PreferKey.useDefaultCover, false)
var optimizeRender = CanvasRecorderFactory.isSupport
&& appCtx.getPrefBoolean(PreferKey.optimizeRender, false)
var recordLog = appCtx.getPrefBoolean(PreferKey.recordLog)
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
PreferKey.themeMode -> {
themeMode = appCtx.getPrefString(PreferKey.themeMode, "0")
isEInkMode = themeMode == "3"
}
PreferKey.clickActionTL -> clickActionTL =
appCtx.getPrefInt(PreferKey.clickActionTL, 2)
PreferKey.clickActionTC -> clickActionTC =
appCtx.getPrefInt(PreferKey.clickActionTC, 2)
PreferKey.clickActionTR -> clickActionTR =
appCtx.getPrefInt(PreferKey.clickActionTR, 1)
PreferKey.clickActionML -> clickActionML =
appCtx.getPrefInt(PreferKey.clickActionML, 2)
PreferKey.clickActionMC -> clickActionMC =
appCtx.getPrefInt(PreferKey.clickActionMC, 0)
PreferKey.clickActionMR -> clickActionMR =
appCtx.getPrefInt(PreferKey.clickActionMR, 1)
PreferKey.clickActionBL -> clickActionBL =
appCtx.getPrefInt(PreferKey.clickActionBL, 2)
PreferKey.clickActionBC -> clickActionBC =
appCtx.getPrefInt(PreferKey.clickActionBC, 1)
PreferKey.clickActionBR -> clickActionBR =
appCtx.getPrefInt(PreferKey.clickActionBR, 1)
PreferKey.readBodyToLh -> ReadBookConfig.readBodyToLh =
appCtx.getPrefBoolean(PreferKey.readBodyToLh, true)
PreferKey.useZhLayout -> ReadBookConfig.useZhLayout =
appCtx.getPrefBoolean(PreferKey.useZhLayout)
PreferKey.userAgent -> userAgent = getPrefUserAgent()
PreferKey.antiAlias -> useAntiAlias = appCtx.getPrefBoolean(PreferKey.antiAlias)
PreferKey.useDefaultCover -> useDefaultCover =
appCtx.getPrefBoolean(PreferKey.useDefaultCover, false)
PreferKey.optimizeRender -> optimizeRender = CanvasRecorderFactory.isSupport
&& appCtx.getPrefBoolean(PreferKey.optimizeRender, false)
PreferKey.recordLog -> recordLog = appCtx.getPrefBoolean(PreferKey.recordLog)
}
}
var isNightTheme: Boolean
get() = when (themeMode) {
"1" -> false
"2" -> true
"3" -> false
else -> sysConfiguration.isNightMode
}
set(value) {
if (isNightTheme != value) {
if (value) {
appCtx.putPrefString(PreferKey.themeMode, "2")
} else {
appCtx.putPrefString(PreferKey.themeMode, "1")
}
}
}
var showUnread: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showUnread, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.showUnread, value)
}
var showLastUpdateTime: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showLastUpdateTime, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.showLastUpdateTime, value)
}
var showWaitUpCount: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showWaitUpCount, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.showWaitUpCount, value)
}
var readBrightness: Int
get() = if (isNightTheme) {
appCtx.getPrefInt(PreferKey.nightBrightness, 100)
} else {
appCtx.getPrefInt(PreferKey.brightness, 100)
}
set(value) {
if (isNightTheme) {
appCtx.putPrefInt(PreferKey.nightBrightness, value)
} else {
appCtx.putPrefInt(PreferKey.brightness, value)
}
}
val textSelectAble: Boolean
get() = appCtx.getPrefBoolean(PreferKey.textSelectAble, true)
// val isTransparentStatusBar: Boolean
// get() = appCtx.getPrefBoolean(PreferKey.transparentStatusBar, true)
//
// val immNavigationBar: Boolean
// get() = appCtx.getPrefBoolean(PreferKey.immNavigationBar, true)
val screenOrientation: String?
get() = appCtx.getPrefString(PreferKey.screenOrientation)
var bookGroupStyle: Int
get() = appCtx.getPrefInt(PreferKey.bookGroupStyle, 0)
set(value) {
appCtx.putPrefInt(PreferKey.bookGroupStyle, value)
}
var bookshelfLayout: Int
get() = appCtx.getPrefInt(PreferKey.bookshelfLayout, 0)
set(value) {
appCtx.putPrefInt(PreferKey.bookshelfLayout, value)
}
var saveTabPosition: Int
get() = appCtx.getPrefInt(PreferKey.saveTabPosition, 0)
set(value) {
appCtx.putPrefInt(PreferKey.saveTabPosition, value)
}
var bookExportFileName: String?
get() = appCtx.getPrefString(PreferKey.bookExportFileName)
set(value) {
appCtx.putPrefString(PreferKey.bookExportFileName, value)
}
// 保存 自定义导出章节模式 文件名js表达式
var episodeExportFileName: String?
get() = appCtx.getPrefString(PreferKey.episodeExportFileName, "")
set(value) {
appCtx.putPrefString(PreferKey.episodeExportFileName, value)
}
var bookImportFileName: String?
get() = appCtx.getPrefString(PreferKey.bookImportFileName)
set(value) {
appCtx.putPrefString(PreferKey.bookImportFileName, value)
}
var backupPath: String?
get() = appCtx.getPrefString(PreferKey.backupPath)
set(value) {
if (value.isNullOrEmpty()) {
appCtx.removePref(PreferKey.backupPath)
} else {
appCtx.putPrefString(PreferKey.backupPath, value)
}
}
// 书籍保存位置
var defaultBookTreeUri: String?
get() = appCtx.getPrefString(PreferKey.defaultBookTreeUri)
set(value) {
if (value.isNullOrEmpty()) {
appCtx.removePref(PreferKey.defaultBookTreeUri)
} else {
appCtx.putPrefString(PreferKey.defaultBookTreeUri, value)
}
}
val showDiscovery: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showDiscovery, true)
val showRSS: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showRss, true)
val autoRefreshBook: Boolean
get() = appCtx.getPrefBoolean(PreferKey.autoRefresh)
var enableReview: Boolean
get() = BuildConfig.DEBUG && appCtx.getPrefBoolean(PreferKey.enableReview, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.enableReview, value)
}
var threadCount: Int
get() = appCtx.getPrefInt(PreferKey.threadCount, 16)
set(value) {
appCtx.putPrefInt(PreferKey.threadCount, value)
}
var remoteServerId: Long
get() = appCtx.getPrefLong(PreferKey.remoteServerId)
set(value) {
appCtx.putPrefLong(PreferKey.remoteServerId, value)
}
// 添加本地选择的目录
var importBookPath: String?
get() = appCtx.getPrefString("importBookPath")
set(value) {
if (value == null) {
appCtx.removePref("importBookPath")
} else {
appCtx.putPrefString("importBookPath", value)
}
}
var ttsFlowSys: Boolean
get() = appCtx.getPrefBoolean(PreferKey.ttsFollowSys, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.ttsFollowSys, value)
}
val noAnimScrollPage: Boolean
get() = appCtx.getPrefBoolean(PreferKey.noAnimScrollPage, false)
const val defaultSpeechRate = 5
var ttsSpeechRate: Int
get() = appCtx.getPrefInt(PreferKey.ttsSpeechRate, defaultSpeechRate)
set(value) {
appCtx.putPrefInt(PreferKey.ttsSpeechRate, value)
}
var ttsTimer: Int
get() = appCtx.getPrefInt(PreferKey.ttsTimer, 0)
set(value) {
appCtx.putPrefInt(PreferKey.ttsTimer, value)
}
val speechRatePlay: Int get() = if (ttsFlowSys) defaultSpeechRate else ttsSpeechRate
var chineseConverterType: Int
get() = appCtx.getPrefInt(PreferKey.chineseConverterType)
set(value) {
appCtx.putPrefInt(PreferKey.chineseConverterType, value)
}
var systemTypefaces: Int
get() = appCtx.getPrefInt(PreferKey.systemTypefaces)
set(value) {
appCtx.putPrefInt(PreferKey.systemTypefaces, value)
}
// var elevation: Int
// get() = if (isEInkMode) 0 else appCtx.getPrefInt(
// PreferKey.barElevation,
// AppConst.sysElevation
// )
// set(value) {
// appCtx.putPrefInt(PreferKey.barElevation, value)
// }
var readUrlInBrowser: Boolean
get() = appCtx.getPrefBoolean(PreferKey.readUrlOpenInBrowser)
set(value) {
appCtx.putPrefBoolean(PreferKey.readUrlOpenInBrowser, value)
}
var exportCharset: String
get() {
val c = appCtx.getPrefString(PreferKey.exportCharset)
if (c.isNullOrBlank()) {
return "UTF-8"
}
return c
}
set(value) {
appCtx.putPrefString(PreferKey.exportCharset, value)
}
var exportUseReplace: Boolean
get() = appCtx.getPrefBoolean(PreferKey.exportUseReplace, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.exportUseReplace, value)
}
var exportToWebDav: Boolean
get() = appCtx.getPrefBoolean(PreferKey.exportToWebDav)
set(value) {
appCtx.putPrefBoolean(PreferKey.exportToWebDav, value)
}
var exportNoChapterName: Boolean
get() = appCtx.getPrefBoolean(PreferKey.exportNoChapterName)
set(value) {
appCtx.putPrefBoolean(PreferKey.exportNoChapterName, value)
}
// 是否启用自定义导出 default->false
var enableCustomExport: Boolean
get() = appCtx.getPrefBoolean(PreferKey.enableCustomExport, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.enableCustomExport, value)
}
var exportType: Int
get() = appCtx.getPrefInt(PreferKey.exportType)
set(value) {
appCtx.putPrefInt(PreferKey.exportType, value)
}
var exportPictureFile: Boolean
get() = appCtx.getPrefBoolean(PreferKey.exportPictureFile, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.exportPictureFile, value)
}
var parallelExportBook: Boolean
get() = appCtx.getPrefBoolean(PreferKey.parallelExportBook, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.parallelExportBook, value)
}
var changeSourceCheckAuthor: Boolean
get() = appCtx.getPrefBoolean(PreferKey.changeSourceCheckAuthor)
set(value) {
appCtx.putPrefBoolean(PreferKey.changeSourceCheckAuthor, value)
}
var ttsEngine: String?
get() = appCtx.getPrefString(PreferKey.ttsEngine)
set(value) {
appCtx.putPrefString(PreferKey.ttsEngine, value)
}
var webPort: Int
get() = appCtx.getPrefInt(PreferKey.webPort, 1122)
set(value) {
appCtx.putPrefInt(PreferKey.webPort, value)
}
var tocUiUseReplace: Boolean
get() = appCtx.getPrefBoolean(PreferKey.tocUiUseReplace)
set(value) {
appCtx.putPrefBoolean(PreferKey.tocUiUseReplace, value)
}
var tocCountWords: Boolean
get() = appCtx.getPrefBoolean(PreferKey.tocCountWords, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.tocCountWords, value)
}
var enableReadRecord: Boolean
get() = appCtx.getPrefBoolean(PreferKey.enableReadRecord, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.enableReadRecord, value)
}
val autoChangeSource: Boolean
get() = appCtx.getPrefBoolean(PreferKey.autoChangeSource, true)
var changeSourceLoadInfo: Boolean
get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadInfo)
set(value) {
appCtx.putPrefBoolean(PreferKey.changeSourceLoadInfo, value)
}
var changeSourceLoadToc: Boolean
get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadToc)
set(value) {
appCtx.putPrefBoolean(PreferKey.changeSourceLoadToc, value)
}
var changeSourceLoadWordCount: Boolean
get() = appCtx.getPrefBoolean(PreferKey.changeSourceLoadWordCount)
set(value) {
appCtx.putPrefBoolean(PreferKey.changeSourceLoadWordCount, value)
}
var openBookInfoByClickTitle: Boolean
get() = appCtx.getPrefBoolean(PreferKey.openBookInfoByClickTitle, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.openBookInfoByClickTitle, value)
}
var showBookshelfFastScroller: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showBookshelfFastScroller, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.showBookshelfFastScroller, value)
}
var contentSelectSpeakMod: Int
get() = appCtx.getPrefInt(PreferKey.contentSelectSpeakMod)
set(value) {
appCtx.putPrefInt(PreferKey.contentSelectSpeakMod, value)
}
var batchChangeSourceDelay: Int
get() = appCtx.getPrefInt(PreferKey.batchChangeSourceDelay)
set(value) {
appCtx.putPrefInt(PreferKey.batchChangeSourceDelay, value)
}
val importKeepName get() = appCtx.getPrefBoolean(PreferKey.importKeepName)
val importKeepGroup get() = appCtx.getPrefBoolean(PreferKey.importKeepGroup)
var importKeepEnable: Boolean
get() = appCtx.getPrefBoolean(PreferKey.importKeepEnable, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.importKeepEnable, value)
}
var previewImageByClick: Boolean
get() = appCtx.getPrefBoolean(PreferKey.previewImageByClick, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.previewImageByClick, value)
}
var preDownloadNum
get() = appCtx.getPrefInt(PreferKey.preDownloadNum, 10)
set(value) {
appCtx.putPrefInt(PreferKey.preDownloadNum, value)
}
val syncBookProgress get() = appCtx.getPrefBoolean(PreferKey.syncBookProgress, true)
val syncBookProgressPlus get() = appCtx.getPrefBoolean(PreferKey.syncBookProgressPlus, false)
val mediaButtonOnExit get() = appCtx.getPrefBoolean("mediaButtonOnExit", true)
val readAloudByMediaButton
get() = appCtx.getPrefBoolean(PreferKey.readAloudByMediaButton, false)
val replaceEnableDefault get() = appCtx.getPrefBoolean(PreferKey.replaceEnableDefault, true)
val webDavDir get() = appCtx.getPrefString(PreferKey.webDavDir, "legado")
val webDavDeviceName get() = appCtx.getPrefString(PreferKey.webDavDeviceName, Build.MODEL)
val recordHeapDump get() = appCtx.getPrefBoolean(PreferKey.recordHeapDump, false)
val loadCoverOnlyWifi get() = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false)
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)
val ignoreAudioFocus get() = appCtx.getPrefBoolean(PreferKey.ignoreAudioFocus, false)
var pauseReadAloudWhilePhoneCalls
get() = appCtx.getPrefBoolean(PreferKey.pauseReadAloudWhilePhoneCalls, false)
set(value) = appCtx.putPrefBoolean(PreferKey.pauseReadAloudWhilePhoneCalls, value)
val onlyLatestBackup get() = appCtx.getPrefBoolean(PreferKey.onlyLatestBackup, true)
val defaultHomePage get() = appCtx.getPrefString(PreferKey.defaultHomePage, "bookshelf")
val updateToVariant get() = appCtx.getPrefString(PreferKey.updateToVariant, "default_version")
val streamReadAloudAudio get() = appCtx.getPrefBoolean(PreferKey.streamReadAloudAudio, false)
val doublePageHorizontal: String?
get() = appCtx.getPrefString(PreferKey.doublePageHorizontal)
val progressBarBehavior: String?
get() = appCtx.getPrefString(PreferKey.progressBarBehavior, "page")
val keyPageOnLongPress
get() = appCtx.getPrefBoolean(PreferKey.keyPageOnLongPress, false)
val volumeKeyPage
get() = appCtx.getPrefBoolean(PreferKey.volumeKeyPage, true)
val volumeKeyPageOnPlay
get() = appCtx.getPrefBoolean(PreferKey.volumeKeyPageOnPlay, true)
val mouseWheelPage
get() = appCtx.getPrefBoolean(PreferKey.mouseWheelPage, true)
var searchScope: String
get() = appCtx.getPrefString("searchScope") ?: ""
set(value) {
appCtx.putPrefString("searchScope", value)
}
var searchGroup: String
get() = appCtx.getPrefString("searchGroup") ?: ""
set(value) {
appCtx.putPrefString("searchGroup", value)
}
var pageTouchSlop: Int
get() = appCtx.getPrefInt(PreferKey.pageTouchSlop, 0)
set(value) {
appCtx.putPrefInt(PreferKey.pageTouchSlop, value)
}
var bookshelfSort: Int
get() = appCtx.getPrefInt(PreferKey.bookshelfSort, 0)
set(value) {
appCtx.putPrefInt(PreferKey.bookshelfSort, value)
}
fun getBookSortByGroupId(groupId: Long): Int {
return appDb.bookGroupDao.getByID(groupId)?.getRealBookSort()
?: bookshelfSort
}
private fun getPrefUserAgent(): String {
val ua = appCtx.getPrefString(PreferKey.userAgent)
if (ua.isNullOrBlank()) {
return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + BuildConfig.Cronet_Main_Version + " Safari/537.36"
}
return ua
}
var bitmapCacheSize: Int
get() = appCtx.getPrefInt(PreferKey.bitmapCacheSize, 50)
set(value) {
appCtx.putPrefInt(PreferKey.bitmapCacheSize, value)
}
var imageRetainNum: Int
get() = appCtx.getPrefInt(PreferKey.imageRetainNum, 0)
set(value) {
appCtx.putPrefInt(PreferKey.imageRetainNum, value)
}
var showReadTitleBarAddition: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showReadTitleAddition, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.showReadTitleAddition, value)
}
var readBarStyleFollowPage: Boolean
get() = appCtx.getPrefBoolean(PreferKey.readBarStyleFollowPage, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.readBarStyleFollowPage, value)
}
var sourceEditMaxLine: Int
get() {
val maxLine = appCtx.getPrefInt(PreferKey.sourceEditMaxLine, Int.MAX_VALUE)
if (maxLine < 10) {
return Int.MAX_VALUE
}
return maxLine
}
set(value) {
appCtx.putPrefInt(PreferKey.sourceEditMaxLine, value)
}
var audioPlayUseWakeLock: Boolean
get() = appCtx.getPrefBoolean(PreferKey.audioPlayWakeLock)
set(value) {
appCtx.putPrefBoolean(PreferKey.audioPlayWakeLock, value)
}
var brightnessVwPos: Boolean
get() = appCtx.getPrefBoolean(PreferKey.brightnessVwPos)
set(value) {
appCtx.putPrefBoolean(PreferKey.brightnessVwPos, value)
}
fun detectClickArea() {
if (clickActionTL * clickActionTC * clickActionTR
* clickActionML * clickActionMC * clickActionMR
* clickActionBL * clickActionBC * clickActionBR != 0
) {
appCtx.putPrefInt(PreferKey.clickActionMC, 0)
appCtx.toastOnUi("当前没有配置菜单区域,自动恢复中间区域为菜单.")
}
}
//跳转到漫画界面不使用富文本模式
val showMangaUi: Boolean
get() = appCtx.getPrefBoolean(PreferKey.showMangaUi, true)
//禁用漫画缩放
var disableMangaScale: Boolean
get() = appCtx.getPrefBoolean(PreferKey.disableMangaScale, true)
set(value) {
appCtx.putPrefBoolean(PreferKey.disableMangaScale, value)
}
//漫画预加载数量
var mangaPreDownloadNum
get() = appCtx.getPrefInt(PreferKey.mangaPreDownloadNum, 10)
set(value) {
appCtx.putPrefInt(PreferKey.mangaPreDownloadNum, value)
}
//点击翻页
var disableClickScroll
get() = appCtx.getPrefBoolean(PreferKey.disableClickScroll, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.disableClickScroll, value)
}
//漫画滚动速度
var mangaAutoPageSpeed
get() = appCtx.getPrefInt(PreferKey.mangaAutoPageSpeed, 3)
set(value) {
appCtx.putPrefInt(PreferKey.mangaAutoPageSpeed, value)
}
//漫画页脚配置
var mangaFooterConfig
get() = appCtx.getPrefString(PreferKey.mangaFooterConfig, "")
set(value) {
appCtx.putPrefString(PreferKey.mangaFooterConfig, value)
}
//漫画水平滚动
var enableMangaHorizontalScroll
get() = appCtx.getPrefBoolean(PreferKey.enableMangaHorizontalScroll, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.enableMangaHorizontalScroll, value)
}
var mangaColorFilter
get() = appCtx.getPrefString(PreferKey.mangaColorFilter, "")
set(value) {
appCtx.putPrefString(PreferKey.mangaColorFilter, value)
}
//禁用漫画内标题
var hideMangaTitle
get() = appCtx.getPrefBoolean(PreferKey.hideMangaTitle, false)
set(value) {
appCtx.putPrefBoolean(PreferKey.hideMangaTitle, value)
}
}
@@ -0,0 +1,126 @@
package io.legato.kazusa.help.config
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import io.legato.kazusa.utils.getBoolean
import io.legato.kazusa.utils.putBoolean
import io.legato.kazusa.utils.putLong
import io.legato.kazusa.utils.putString
import io.legato.kazusa.utils.remove
import splitties.init.appCtx
@Suppress("ConstPropertyName")
object LocalConfig : SharedPreferences
by appCtx.getSharedPreferences("local", Context.MODE_PRIVATE) {
private const val versionCodeKey = "appVersionCode"
/**
* 本地密码,用来对需要备份的敏感信息加密,如 webdav 配置等
*/
var password: String?
get() = getString("password", null)
set(value) {
if (value != null) {
putString("password", value)
} else {
remove("password")
}
}
var lastBackup: Long
get() = getLong("lastBackup", 0)
set(value) {
putLong("lastBackup", value)
}
var privacyPolicyOk: Boolean
get() = getBoolean("privacyPolicyOk")
set(value) {
putBoolean("privacyPolicyOk", value)
}
val readHelpVersionIsLast: Boolean
get() = isLastVersion(1, "readHelpVersion", "firstRead")
val backupHelpVersionIsLast: Boolean
get() = isLastVersion(1, "backupHelpVersion", "firstBackup")
val readMenuHelpVersionIsLast: Boolean
get() = isLastVersion(1, "readMenuHelpVersion", "firstReadMenu")
val bookSourcesHelpVersionIsLast: Boolean
get() = isLastVersion(1, "bookSourceHelpVersion", "firstOpenBookSources")
val webDavBookHelpVersionIsLast: Boolean
get() = isLastVersion(1, "webDavBookHelpVersion", "firstOpenWebDavBook")
val ruleHelpVersionIsLast: Boolean
get() = isLastVersion(1, "ruleHelpVersion")
val needUpHttpTTS: Boolean
get() = !isLastVersion(6, "httpTtsVersion")
val needUpTxtTocRule: Boolean
get() = !isLastVersion(3, "txtTocRuleVersion")
val needUpRssSources: Boolean
get() = !isLastVersion(6, "rssSourceVersion")
val needUpDictRule: Boolean
get() = !isLastVersion(2, "needUpDictRule")
var versionCode
get() = getLong(versionCodeKey, 0)
set(value) {
edit { putLong(versionCodeKey, value) }
}
val isFirstOpenApp: Boolean
get() {
val value = getBoolean("firstOpen", true)
if (value) {
edit { putBoolean("firstOpen", false) }
}
return value
}
@Suppress("SameParameterValue")
private fun isLastVersion(
lastVersion: Int,
versionKey: String,
firstOpenKey: String? = null
): Boolean {
var version = getInt(versionKey, 0)
if (version == 0 && firstOpenKey != null) {
if (!getBoolean(firstOpenKey, true)) {
version = 1
}
}
if (version < lastVersion) {
edit { putInt(versionKey, lastVersion) }
return false
}
return true
}
var bookInfoDeleteAlert: Boolean
get() = getBoolean("bookInfoDeleteAlert", true)
set(value) {
putBoolean("bookInfoDeleteAlert", value)
}
var deleteBookOriginal: Boolean
get() = getBoolean("deleteBookOriginal")
set(value) {
putBoolean("deleteBookOriginal", value)
}
var appCrash: Boolean
get() = getBoolean("appCrash")
set(value) {
putBoolean("appCrash", value)
}
}
@@ -0,0 +1,710 @@
package io.legato.kazusa.help.config
import android.graphics.Color
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import androidx.annotation.Keep
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
import io.legato.kazusa.help.DefaultData
import io.legato.kazusa.help.coroutine.Coroutine
import io.legato.kazusa.utils.BitmapUtils
import io.legato.kazusa.utils.FileUtils
import io.legato.kazusa.utils.GSON
import io.legato.kazusa.utils.compress.ZipUtils
import io.legato.kazusa.utils.createFolderReplace
import io.legato.kazusa.utils.externalCache
import io.legato.kazusa.utils.externalFiles
import io.legato.kazusa.utils.fromJsonArray
import io.legato.kazusa.utils.fromJsonObject
import io.legato.kazusa.utils.getFile
import io.legato.kazusa.utils.getMeanColor
import io.legato.kazusa.utils.getPrefBoolean
import io.legato.kazusa.utils.getPrefInt
import io.legato.kazusa.utils.hexString
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
/**
* 阅读界面配置
*/
@Suppress("ConstPropertyName")
@Keep
object ReadBookConfig {
const val configFileName = "readConfig.json"
const val shareConfigFileName = "shareReadConfig.json"
val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
val shareConfigFilePath = FileUtils.getPath(appCtx.filesDir, shareConfigFileName)
val configList: ArrayList<Config> = arrayListOf()
lateinit var shareConfig: Config
var durConfig
get() = getConfig(styleSelect)
set(value) {
configList[styleSelect] = value
if (shareLayout) {
shareConfig = value
}
}
var isComic: Boolean = false
var bg: Drawable? = null
var bgMeanColor: Int = 0
val textColor: Int get() = durConfig.curTextColor()
init {
initConfigs()
initShareConfig()
}
@Synchronized
fun getConfig(index: Int): Config {
if (configList.size < 5) {
resetAll()
}
return configList.getOrNull(index) ?: configList[0]
}
fun initConfigs() {
val configFile = File(configFilePath)
var configs: List<Config>? = null
if (configFile.exists()) {
try {
val json = configFile.readText()
configs = GSON.fromJsonArray<Config>(json).getOrThrow()
} catch (e: Exception) {
AppLog.put("读取排版配置文件出错", e)
}
}
(configs ?: DefaultData.readConfigs).let {
configList.clear()
configList.addAll(it)
}
}
fun initShareConfig() {
val configFile = File(shareConfigFilePath)
var c: Config? = null
if (configFile.exists()) {
try {
val json = configFile.readText()
c = GSON.fromJsonObject<Config>(json).getOrThrow()
} catch (e: Exception) {
e.printOnDebug()
}
}
shareConfig = c ?: configList.getOrNull(5) ?: Config()
}
fun upBg(width: Int, height: Int) {
val drawable = durConfig.curBgDrawable(width, height)
if (drawable is BitmapDrawable && drawable.bitmap != null) {
bgMeanColor = drawable.bitmap.getMeanColor()
} else if (drawable is ColorDrawable) {
bgMeanColor = drawable.color
}
val tmp = bg
bg = drawable
(tmp as? BitmapDrawable)?.bitmap?.recycle()
}
fun save() {
Coroutine.async {
synchronized(this) {
GSON.toJson(configList).let {
FileUtils.delete(configFilePath)
FileUtils.createFileIfNotExist(configFilePath).writeText(it)
}
GSON.toJson(shareConfig).let {
FileUtils.delete(shareConfigFilePath)
FileUtils.createFileIfNotExist(shareConfigFilePath).writeText(it)
}
}
}
}
fun getAllPicBgStr(): ArrayList<String> {
val list = arrayListOf<String>()
configList.forEach {
if (it.bgType == 2) {
list.add(it.bgStr)
}
if (it.bgTypeNight == 2) {
list.add(it.bgStrNight)
}
if (it.bgTypeEInk == 2) {
list.add(it.bgStrEInk)
}
}
return list
}
fun deleteDur(): Boolean {
if (configList.size > 5) {
val removeIndex = styleSelect
configList.removeAt(removeIndex)
if (removeIndex <= readStyleSelect) {
readStyleSelect -= 1
}
if (removeIndex <= comicStyleSelect) {
comicStyleSelect -= 1
}
return true
}
return false
}
private fun resetAll() {
DefaultData.readConfigs.let {
configList.clear()
configList.addAll(it)
save()
}
}
//配置写入读取
var readBodyToLh = appCtx.getPrefBoolean(PreferKey.readBodyToLh, true)
var autoReadSpeed = appCtx.getPrefInt(PreferKey.autoReadSpeed, 10)
set(value) {
field = value
appCtx.putPrefInt(PreferKey.autoReadSpeed, value)
}
var styleSelect: Int
get() = if (isComic) comicStyleSelect else readStyleSelect
set(value) {
if (isComic) {
comicStyleSelect = value
} else {
readStyleSelect = value
}
}
var readStyleSelect = appCtx.getPrefInt(PreferKey.readStyleSelect)
set(value) {
field = value
if (appCtx.getPrefInt(PreferKey.readStyleSelect) != value) {
appCtx.putPrefInt(PreferKey.readStyleSelect, value)
}
}
var comicStyleSelect = appCtx.getPrefInt(PreferKey.comicStyleSelect, readStyleSelect)
set(value) {
field = value
if (appCtx.getPrefInt(PreferKey.comicStyleSelect) != value) {
appCtx.putPrefInt(PreferKey.comicStyleSelect, value)
}
}
var shareLayout = appCtx.getPrefBoolean(PreferKey.shareLayout)
set(value) {
field = value
if (appCtx.getPrefBoolean(PreferKey.shareLayout) != value) {
appCtx.putPrefBoolean(PreferKey.shareLayout, value)
}
}
/**
* 两端对齐
*/
val textFullJustify get() = appCtx.getPrefBoolean(PreferKey.textFullJustify, true)
/**
* 底部对齐
*/
val textBottomJustify get() = appCtx.getPrefBoolean(PreferKey.textBottomJustify, true)
var hideStatusBar = appCtx.getPrefBoolean(PreferKey.hideStatusBar)
var hideNavigationBar = appCtx.getPrefBoolean(PreferKey.hideNavigationBar)
var useZhLayout = appCtx.getPrefBoolean(PreferKey.useZhLayout)
val config get() = if (shareLayout) shareConfig else durConfig
var bgAlpha: Int
get() = config.bgAlpha
set(value) {
config.bgAlpha = value
}
var pageAnim: Int
get() = config.curPageAnim()
set(@PageAnim.Anim value) {
config.setCurPageAnim(value)
}
var textFont: String
get() = config.textFont
set(value) {
config.textFont = value
}
var textBold: Int
get() = config.textBold
set(value) {
config.textBold = value
}
var textSize: Int
get() = config.textSize
set(value) {
config.textSize = value
}
var letterSpacing: Float
get() = config.letterSpacing
set(value) {
config.letterSpacing = value
}
var lineSpacingExtra: Int
get() = config.lineSpacingExtra
set(value) {
config.lineSpacingExtra = value
}
var paragraphSpacing: Int
get() = config.paragraphSpacing
set(value) {
config.paragraphSpacing = value
}
/**
* 标题位置 0:居左 1:居中 2:隐藏
*/
var titleMode: Int
get() = config.titleMode
set(value) {
config.titleMode = value
}
var titleSize: Int
get() = config.titleSize
set(value) {
config.titleSize = value
}
/**
* 是否标题居中
*/
val isMiddleTitle get() = titleMode == 1
var titleTopSpacing: Int
get() = config.titleTopSpacing
set(value) {
config.titleTopSpacing = value
}
var titleBottomSpacing: Int
get() = config.titleBottomSpacing
set(value) {
config.titleBottomSpacing = value
}
var paragraphIndent: String
get() = config.paragraphIndent
set(value) {
config.paragraphIndent = value
}
var underline: Boolean
get() = config.underline
set(value) {
config.underline = value
}
var paddingBottom: Int
get() = config.paddingBottom
set(value) {
config.paddingBottom = value
}
var paddingLeft: Int
get() = config.paddingLeft
set(value) {
config.paddingLeft = value
}
var paddingRight: Int
get() = config.paddingRight
set(value) {
config.paddingRight = value
}
var paddingTop: Int
get() = config.paddingTop
set(value) {
config.paddingTop = value
}
var headerPaddingBottom: Int
get() = config.headerPaddingBottom
set(value) {
config.headerPaddingBottom = value
}
var headerPaddingLeft: Int
get() = config.headerPaddingLeft
set(value) {
config.headerPaddingLeft = value
}
var headerPaddingRight: Int
get() = config.headerPaddingRight
set(value) {
config.headerPaddingRight = value
}
var headerPaddingTop: Int
get() = config.headerPaddingTop
set(value) {
config.headerPaddingTop = value
}
var footerPaddingBottom: Int
get() = config.footerPaddingBottom
set(value) {
config.footerPaddingBottom = value
}
var footerPaddingLeft: Int
get() = config.footerPaddingLeft
set(value) {
config.footerPaddingLeft = value
}
var footerPaddingRight: Int
get() = config.footerPaddingRight
set(value) {
config.footerPaddingRight = value
}
var footerPaddingTop: Int
get() = config.footerPaddingTop
set(value) {
config.footerPaddingTop = value
}
var showHeaderLine: Boolean
get() = config.showHeaderLine
set(value) {
config.showHeaderLine = value
}
var showFooterLine: Boolean
get() = config.showFooterLine
set(value) {
config.showFooterLine = value
}
fun getExportConfig(): Config {
val exportConfig = durConfig.copy()
if (shareLayout) {
exportConfig.textFont = shareConfig.textFont
exportConfig.textBold = shareConfig.textBold
exportConfig.textSize = shareConfig.textSize
exportConfig.letterSpacing = shareConfig.letterSpacing
exportConfig.lineSpacingExtra = shareConfig.lineSpacingExtra
exportConfig.paragraphSpacing = shareConfig.paragraphSpacing
exportConfig.titleMode = shareConfig.titleMode
exportConfig.titleSize = shareConfig.titleSize
exportConfig.titleTopSpacing = shareConfig.titleTopSpacing
exportConfig.titleBottomSpacing = shareConfig.titleBottomSpacing
exportConfig.paddingBottom = shareConfig.paddingBottom
exportConfig.paddingLeft = shareConfig.paddingLeft
exportConfig.paddingRight = shareConfig.paddingRight
exportConfig.paddingTop = shareConfig.paddingTop
exportConfig.headerPaddingBottom = shareConfig.headerPaddingBottom
exportConfig.headerPaddingLeft = shareConfig.headerPaddingLeft
exportConfig.headerPaddingRight = shareConfig.headerPaddingRight
exportConfig.headerPaddingTop = shareConfig.headerPaddingTop
exportConfig.footerPaddingBottom = shareConfig.footerPaddingBottom
exportConfig.footerPaddingLeft = shareConfig.footerPaddingLeft
exportConfig.footerPaddingRight = shareConfig.footerPaddingRight
exportConfig.footerPaddingTop = shareConfig.footerPaddingTop
exportConfig.showHeaderLine = shareConfig.showHeaderLine
exportConfig.showFooterLine = shareConfig.showFooterLine
exportConfig.tipHeaderLeft = shareConfig.tipHeaderLeft
exportConfig.tipHeaderMiddle = shareConfig.tipHeaderMiddle
exportConfig.tipHeaderRight = shareConfig.tipHeaderRight
exportConfig.tipFooterLeft = shareConfig.tipFooterLeft
exportConfig.tipFooterMiddle = shareConfig.tipFooterMiddle
exportConfig.tipFooterRight = shareConfig.tipFooterRight
exportConfig.tipColor = shareConfig.tipColor
exportConfig.headerMode = shareConfig.headerMode
exportConfig.footerMode = shareConfig.footerMode
}
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
}
}
}
@Keep
data class Config(
var name: String = "",
var bgStr: String = "#EEEEEE",//白天背景
var bgStrNight: String = "#000000",//夜间背景
var bgStrEInk: String = "#FFFFFF",//EInk背景
var bgAlpha: Int = 100,//背景透明度
var bgType: Int = 0,//白天背景类型 0:颜色, 1:assets图片, 2其它图片
var bgTypeNight: Int = 0,//夜间背景类型
var bgTypeEInk: Int = 0,//EInk背景类型
private var darkStatusIcon: Boolean = true,//白天是否暗色状态栏
private var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
private var darkStatusIconEInk: Boolean = true,
private var textColor: String = "#3E3D3B",//白天文字颜色
private var textColorNight: String = "#ADADAD",//夜间文字颜色
private var textColorEInk: String = "#000000",
private var pageAnim: Int = 0,//翻页动画
private var pageAnimEInk: Int = 4,
var textFont: String = "",//字体
var textBold: Int = 0,//是否粗体字 0:正常, 1:粗体, 2:细体
var textSize: Int = 20,//文字大小
var letterSpacing: Float = 0.1f,//字间距
var lineSpacingExtra: Int = 12,//行间距
var paragraphSpacing: Int = 2,//段距
var titleMode: Int = 0,//标题位置 0:居左 1:居中 2:隐藏
var titleSize: Int = 0,
var titleTopSpacing: Int = 0,
var titleBottomSpacing: Int = 0,
var paragraphIndent: String = "  ",//段落缩进
var underline: Boolean = false, //下划线
var paddingBottom: Int = 6,
var paddingLeft: Int = 16,
var paddingRight: Int = 16,
var paddingTop: Int = 6,
var headerPaddingBottom: Int = 0,
var headerPaddingLeft: Int = 16,
var headerPaddingRight: Int = 16,
var headerPaddingTop: Int = 0,
var footerPaddingBottom: Int = 6,
var footerPaddingLeft: Int = 16,
var footerPaddingRight: Int = 16,
var footerPaddingTop: Int = 6,
var showHeaderLine: Boolean = false,
var showFooterLine: Boolean = true,
var tipHeaderLeft: Int = ReadTipConfig.time,
var tipHeaderMiddle: Int = ReadTipConfig.none,
var tipHeaderRight: Int = ReadTipConfig.battery,
var tipFooterLeft: Int = ReadTipConfig.chapterTitle,
var tipFooterMiddle: Int = ReadTipConfig.none,
var tipFooterRight: Int = ReadTipConfig.pageAndTotal,
var tipColor: Int = 0,
var tipDividerColor: Int = -1,
var headerMode: Int = 0,
var footerMode: Int = 0
) {
@Transient
private var textColorIntEInk = -1
@Transient
private var textColorIntNight = -1
@Transient
private var textColorInt = -1
@Transient
private var initColorInt = false
private fun initColorInt() {
textColorIntEInk = Color.parseColor(textColorEInk)
textColorIntNight = Color.parseColor(textColorNight)
textColorInt = Color.parseColor(textColor)
initColorInt = true
}
fun setCurTextColor(color: Int) {
when {
AppConfig.isEInkMode -> {
textColorEInk = "#${color.hexString}"
textColorIntEInk = color
}
AppConfig.isNightTheme -> {
textColorNight = "#${color.hexString}"
textColorIntNight = color
}
else -> {
textColor = "#${color.hexString}"
textColorInt = color
}
}
}
fun curTextColor(): Int {
if (!initColorInt) {
initColorInt()
}
return when {
AppConfig.isEInkMode -> textColorIntEInk
AppConfig.isNightTheme -> textColorIntNight
else -> textColorInt
}
}
fun setCurStatusIconDark(isDark: Boolean) {
when {
AppConfig.isEInkMode -> darkStatusIconEInk = isDark
AppConfig.isNightTheme -> darkStatusIconNight = isDark
else -> darkStatusIcon = isDark
}
}
fun curStatusIconDark(): Boolean {
return when {
AppConfig.isEInkMode -> darkStatusIconEInk
AppConfig.isNightTheme -> darkStatusIconNight
else -> darkStatusIcon
}
}
fun setCurPageAnim(@PageAnim.Anim anim: Int) {
when {
AppConfig.isEInkMode -> pageAnimEInk = anim
else -> pageAnim = anim
}
}
fun curPageAnim(): Int {
return when {
AppConfig.isEInkMode -> pageAnimEInk
else -> pageAnim
}
}
fun setCurBg(bgType: Int, bg: String) {
when {
AppConfig.isEInkMode -> {
bgTypeEInk = bgType
bgStrEInk = bg
}
AppConfig.isNightTheme -> {
bgTypeNight = bgType
bgStrNight = bg
}
else -> {
this.bgType = bgType
bgStr = bg
}
}
}
fun curBgStr(): String {
return when {
AppConfig.isEInkMode -> bgStrEInk
AppConfig.isNightTheme -> bgStrNight
else -> bgStr
}
}
fun curBgType(): Int {
return when {
AppConfig.isEInkMode -> bgTypeEInk
AppConfig.isNightTheme -> bgTypeNight
else -> bgType
}
}
fun curBgDrawable(width: Int, height: Int): Drawable {
if (width == 0 || height == 0) {
val backgroundColor = MaterialColors.getColor(appCtx, com.google.android.material.R.attr.colorSurface, Color.WHITE)
return ColorDrawable(backgroundColor)
}
var bgDrawable: Drawable? = null
val resources = appCtx.resources
try {
bgDrawable = when (curBgType()) {
0 -> ColorDrawable(Color.parseColor(curBgStr()))
1 -> {
val path = "bg" + File.separator + curBgStr()
val bitmap = BitmapUtils.decodeAssetsBitmap(appCtx, path, width, height)
BitmapDrawable(resources, bitmap?.resizeAndRecycle(width, height))
}
else -> {
val path = curBgStr().let {
if (it.contains(File.separator)) it
else FileUtils.getPath(appCtx.externalFiles, "bg", curBgStr())
}
val bitmap = BitmapUtils.decodeBitmap(path, width, height)
BitmapDrawable(resources, bitmap?.resizeAndRecycle(width, height))
}
}
} catch (e: OutOfMemoryError) {
e.printOnDebug()
} catch (e: Exception) {
e.printOnDebug()
}
// fallback 使用 MD3 的 colorSurface 作为背景色
val fallbackColor = MaterialColors.getColor(appCtx, com.google.android.material.R.attr.colorSurface, Color.WHITE)
return bgDrawable ?: ColorDrawable(fallbackColor)
}
}
}
@@ -0,0 +1,107 @@
package io.legato.kazusa.help.config
import android.content.Context
import io.legato.kazusa.R
import splitties.init.appCtx
@Suppress("ConstPropertyName")
object ReadTipConfig {
const val none = 0
const val chapterTitle = 1
const val time = 2
const val battery = 3
const val batteryPercentage = 10
const val page = 4
const val totalProgress = 5
const val pageAndTotal = 6
const val bookName = 7
const val timeBattery = 8
const val timeBatteryPercentage = 9
const val totalProgress1 = 11
val tipValues = arrayOf(
none, bookName, chapterTitle, time, battery, batteryPercentage, page,
totalProgress, totalProgress1, pageAndTotal, timeBattery, timeBatteryPercentage
)
val tipNames get() = appCtx.resources.getStringArray(R.array.read_tip).toList()
val tipColorNames get() = appCtx.resources.getStringArray(R.array.tip_color).toList()
val tipDividerColorNames
get() = appCtx.resources.getStringArray(R.array.tip_divider_color).toList()
var tipHeaderLeft: Int
get() = ReadBookConfig.config.tipHeaderLeft
set(value) {
ReadBookConfig.config.tipHeaderLeft = value
}
var tipHeaderMiddle: Int
get() = ReadBookConfig.config.tipHeaderMiddle
set(value) {
ReadBookConfig.config.tipHeaderMiddle = value
}
var tipHeaderRight: Int
get() = ReadBookConfig.config.tipHeaderRight
set(value) {
ReadBookConfig.config.tipHeaderRight = value
}
var tipFooterLeft: Int
get() = ReadBookConfig.config.tipFooterLeft
set(value) {
ReadBookConfig.config.tipFooterLeft = value
}
var tipFooterMiddle: Int
get() = ReadBookConfig.config.tipFooterMiddle
set(value) {
ReadBookConfig.config.tipFooterMiddle = value
}
var tipFooterRight: Int
get() = ReadBookConfig.config.tipFooterRight
set(value) {
ReadBookConfig.config.tipFooterRight = value
}
var headerMode: Int
get() = ReadBookConfig.config.headerMode
set(value) {
ReadBookConfig.config.headerMode = value
}
var footerMode: Int
get() = ReadBookConfig.config.footerMode
set(value) {
ReadBookConfig.config.footerMode = value
}
var tipColor: Int
get() = ReadBookConfig.config.tipColor
set(value) {
ReadBookConfig.config.tipColor = value
}
var tipDividerColor: Int
get() = ReadBookConfig.config.tipDividerColor
set(value) {
ReadBookConfig.config.tipDividerColor = value
}
fun getHeaderModes(context: Context): LinkedHashMap<Int, String> {
return linkedMapOf(
Pair(0, context.getString(R.string.hide_when_status_bar_show)),
Pair(1, context.getString(R.string.show)),
Pair(2, context.getString(R.string.hide))
)
}
fun getFooterModes(context: Context): LinkedHashMap<Int, String> {
return linkedMapOf(
Pair(0, context.getString(R.string.show)),
Pair(1, context.getString(R.string.hide))
)
}
}
@@ -0,0 +1,45 @@
package io.legato.kazusa.help.config
import android.content.Context.MODE_PRIVATE
import androidx.core.content.edit
import splitties.init.appCtx
object SourceConfig {
private val sp = appCtx.getSharedPreferences("SourceConfig", MODE_PRIVATE)
fun setBookScore(origin: String, name: String, author: String, score: Int) {
sp.edit {
val preScore = getBookScore(origin, name, author)
var newScore = score
if (preScore != 0) {
newScore = score - preScore
}
putInt(origin, getSourceScore(origin) + newScore)
putInt("${origin}_${name}_${author}", score)
}
}
fun getBookScore(origin: String, name: String, author: String): Int {
return sp.getInt("${origin}_${name}_${author}", 0)
}
fun getSourceScore(origin: String): Int {
return sp.getInt(origin, 0)
}
fun removeSource(origin: String) {
sp.all.keys.filter {
it.startsWith(origin)
}.let {
sp.edit {
it.forEach {
remove(it)
}
}
}
}
}
@@ -0,0 +1,302 @@
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 io.legato.kazusa.R
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
import io.legato.kazusa.utils.fromJsonObject
import io.legato.kazusa.utils.getCompatColor
import io.legato.kazusa.utils.getPrefInt
import io.legato.kazusa.utils.getPrefString
import io.legato.kazusa.utils.hexString
import io.legato.kazusa.utils.postEvent
import io.legato.kazusa.utils.printOnDebug
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 {
const val configFileName = "themeConfig.json"
val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
val configList: ArrayList<Config> by lazy {
val cList = getConfigs() ?: DefaultData.themeConfigs
ArrayList(cList)
}
fun getTheme() = when {
AppConfig.isEInkMode -> Theme.EInk
AppConfig.isNightTheme -> Theme.Dark
else -> Theme.Light
}
fun isDarkTheme(): Boolean {
return getTheme() == Theme.Dark
}
fun applyDayNight(context: Context) {
applyTheme(context)
initNightMode()
BookCover.upDefaultCover()
postEvent(EventBus.RECREATE, "")
}
fun applyDayNightInit(context: Context) {
applyTheme(context)
initNightMode()
}
private fun initNightMode() {
val targetMode =
if (AppConfig.isNightTheme) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}
AppCompatDelegate.setDefaultNightMode(targetMode)
}
fun getBgImage(context: Context, metrics: DisplayMetrics): Bitmap? {
val bgCfg = when (getTheme()) {
Theme.Light -> Pair(
context.getPrefString(PreferKey.bgImage),
context.getPrefInt(PreferKey.bgImageBlurring, 0)
)
Theme.Dark -> Pair(
context.getPrefString(PreferKey.bgImageN),
context.getPrefInt(PreferKey.bgImageNBlurring, 0)
)
else -> null
} ?: return null
if (bgCfg.first.isNullOrBlank()) return null
val bgImage = BitmapUtils
.decodeBitmap(bgCfg.first!!, metrics.widthPixels, metrics.heightPixels)
if (bgCfg.second == 0) {
return bgImage
}
return bgImage?.stackBlur(bgCfg.second)
}
fun upConfig() {
getConfigs()?.forEach { config ->
addConfig(config)
}
}
fun save() {
val json = GSON.toJson(configList)
FileUtils.delete(configFilePath)
FileUtils.createFileIfNotExist(configFilePath).writeText(json)
}
fun delConfig(index: Int) {
configList.removeAt(index)
save()
}
fun addConfig(json: String): Boolean {
GSON.fromJsonObject<Config>(json.trim { it < ' ' }).getOrNull()
?.let {
addConfig(it)
return true
}
return false
}
fun addConfig(newConfig: Config) {
configList.forEachIndexed { index, config ->
if (newConfig.themeName == config.themeName) {
configList[index] = newConfig
return
}
}
configList.add(newConfig)
save()
}
private fun getConfigs(): List<Config>? {
val configFile = File(configFilePath)
if (configFile.exists()) {
kotlin.runCatching {
val json = configFile.readText()
return GSON.fromJsonArray<Config>(json).getOrThrow()
}.onFailure {
it.printOnDebug()
}
}
return null
}
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)
}
AppConfig.isNightTheme = config.isNightTheme
applyDayNight(context)
}
fun saveDayTheme(context: Context, name: String) {
val primary =
context.getPrefInt(PreferKey.cPrimary, context.getCompatColor(R.color.md_brown_500))
val accent =
context.getPrefInt(PreferKey.cAccent, context.getCompatColor(R.color.md_red_600))
val background =
context.getPrefInt(PreferKey.cBackground, context.getCompatColor(R.color.md_grey_100))
val bBackground =
context.getPrefInt(PreferKey.cBBackground, context.getCompatColor(R.color.md_grey_200))
val config = Config(
themeName = name,
isNightTheme = false,
primaryColor = "#${primary.hexString}",
accentColor = "#${accent.hexString}",
backgroundColor = "#${background.hexString}",
bottomBackground = "#${bBackground.hexString}"
)
addConfig(config)
}
fun saveNightTheme(context: Context, name: String) {
val primary =
context.getPrefInt(
PreferKey.cNPrimary,
context.getCompatColor(R.color.md_blue_grey_600)
)
val accent =
context.getPrefInt(
PreferKey.cNAccent,
context.getCompatColor(R.color.md_deep_orange_800)
)
val background =
context.getPrefInt(PreferKey.cNBackground, context.getCompatColor(R.color.md_grey_900))
val bBackground =
context.getPrefInt(PreferKey.cNBBackground, context.getCompatColor(R.color.md_grey_850))
val config = Config(
themeName = name,
isNightTheme = true,
primaryColor = "#${primary.hexString}",
accentColor = "#${accent.hexString}",
backgroundColor = "#${background.hexString}",
bottomBackground = "#${bBackground.hexString}"
)
addConfig(config)
}
/**
* 更新主题
*/
fun applyTheme(context: Context) = with(context) {
when {
AppConfig.isEInkMode -> {
ThemeStore.editTheme(this)
.primaryColor(Color.WHITE)
.accentColor(Color.BLACK)
.backgroundColor(Color.WHITE)
.bottomBackground(Color.WHITE)
.apply()
}
AppConfig.isNightTheme -> {
val primary =
getPrefInt(PreferKey.cNPrimary, getCompatColor(R.color.md_blue_grey_600))
val accent =
getPrefInt(PreferKey.cNAccent, getCompatColor(R.color.md_deep_orange_800))
var background =
getPrefInt(PreferKey.cNBackground, getCompatColor(R.color.md_grey_900))
if (ColorUtils.isColorLight(background)) {
background = getCompatColor(R.color.md_grey_900)
putPrefInt(PreferKey.cNBackground, background)
}
val bBackground =
getPrefInt(PreferKey.cNBBackground, getCompatColor(R.color.md_grey_850))
ThemeStore.editTheme(this)
.primaryColor(ColorUtils.withAlpha(primary, 1f))
.accentColor(ColorUtils.withAlpha(accent, 1f))
.backgroundColor(ColorUtils.withAlpha(background, 1f))
.bottomBackground(ColorUtils.withAlpha(bBackground, 1f))
.apply()
}
else -> {
val primary =
getPrefInt(PreferKey.cPrimary, getCompatColor(R.color.md_brown_500))
val accent =
getPrefInt(PreferKey.cAccent, getCompatColor(R.color.md_red_600))
var background =
getPrefInt(PreferKey.cBackground, getCompatColor(R.color.md_grey_100))
if (!ColorUtils.isColorLight(background)) {
background = getCompatColor(R.color.md_grey_100)
putPrefInt(PreferKey.cBackground, background)
}
val bBackground =
getPrefInt(PreferKey.cBBackground, getCompatColor(R.color.md_grey_200))
ThemeStore.editTheme(this)
.primaryColor(ColorUtils.withAlpha(primary, 1f))
.accentColor(ColorUtils.withAlpha(accent, 1f))
.backgroundColor(ColorUtils.withAlpha(background, 1f))
.bottomBackground(ColorUtils.withAlpha(bBackground, 1f))
.apply()
}
}
}
@Keep
data class Config(
var themeName: String,
var isNightTheme: Boolean,
var primaryColor: String,
var accentColor: String,
var backgroundColor: String,
var bottomBackground: String
) {
override fun hashCode(): Int {
return GSON.toJson(this).hashCode()
}
override fun equals(other: Any?): Boolean {
other ?: return false
if (other is Config) {
return other.themeName == themeName
&& other.isNightTheme == isNightTheme
&& other.primaryColor == primaryColor
&& other.accentColor == accentColor
&& other.backgroundColor == backgroundColor
&& other.bottomBackground == bottomBackground
}
return false
}
}
}