diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 3302fbfff..80066583d 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -3,6 +3,10 @@ * 旧版数据导入教程: * 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +**2020/01/27** +* 添加根据系统主题切换夜间模式 +* 合并Modificator提交的代码 + **2020/01/26** * 修复bug * 未加入书架可查看目录 diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index ee3e4f5f6..64d6abf9d 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -15,13 +15,13 @@ import io.legado.app.constant.AppConst.channelIdReadAloud import io.legado.app.constant.AppConst.channelIdWeb import io.legado.app.data.AppDatabase import io.legado.app.help.ActivityHelp +import io.legado.app.help.AppConfig import io.legado.app.help.CrashHandler import io.legado.app.help.ReadBookConfig import io.legado.app.lib.theme.ThemeStore import io.legado.app.ui.book.read.page.ChapterProvider import io.legado.app.utils.getCompatColor import io.legado.app.utils.getPrefInt -import io.legado.app.utils.isNightTheme @Suppress("DEPRECATION") class App : Application() { @@ -50,7 +50,7 @@ class App : Application() { } if (!ThemeStore.isConfigured(this, versionCode)) applyTheme() - initNightTheme() + initNightMode() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) createChannelId() @@ -67,7 +67,7 @@ class App : Application() { * 更新主题 */ fun applyTheme() { - if (isNightTheme) { + if (AppConfig.isNightTheme) { ThemeStore.editTheme(this) .primaryColor( getPrefInt("colorPrimaryNight", getCompatColor(R.color.shine_color)) @@ -98,15 +98,16 @@ class App : Application() { fun applyDayNight() { ReadBookConfig.upBg() applyTheme() - initNightTheme() + initNightMode() } - private fun initNightTheme() { - val targetMode = if (isNightTheme) { - AppCompatDelegate.MODE_NIGHT_YES - } else { - AppCompatDelegate.MODE_NIGHT_NO - } + private fun initNightMode() { + val targetMode = + if (AppConfig.isNightTheme) { + AppCompatDelegate.MODE_NIGHT_YES + } else { + AppCompatDelegate.MODE_NIGHT_NO + } AppCompatDelegate.setDefaultNightMode(targetMode) } diff --git a/app/src/main/java/io/legado/app/base/BaseActivity.kt b/app/src/main/java/io/legado/app/base/BaseActivity.kt index 2517cc616..048036892 100644 --- a/app/src/main/java/io/legado/app/base/BaseActivity.kt +++ b/app/src/main/java/io/legado/app/base/BaseActivity.kt @@ -52,8 +52,8 @@ abstract class BaseActivity( } ?: super.onCreateOptionsMenu(menu) } - override fun onMenuOpened(featureId: Int, menu: Menu?): Boolean { - menu?.applyOpenTint(this) + override fun onMenuOpened(featureId: Int, menu: Menu): Boolean { + menu.applyOpenTint(this) return super.onMenuOpened(featureId, menu) } diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 80efd5925..1cb5e2edc 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -21,4 +21,5 @@ object PreferKey { const val backupPath = "backupUri" const val threadCount = "threadCount" const val keepLight = "keep_light" + const val autoDarkMode = "autoDarkMode" } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/constant/Theme.kt b/app/src/main/java/io/legado/app/constant/Theme.kt index 40467a688..b8587997b 100644 --- a/app/src/main/java/io/legado/app/constant/Theme.kt +++ b/app/src/main/java/io/legado/app/constant/Theme.kt @@ -1,14 +1,13 @@ package io.legado.app.constant -import io.legado.app.App -import io.legado.app.utils.isNightTheme +import io.legado.app.help.AppConfig enum class Theme { Dark, Light, Auto; companion object { fun getTheme(): Theme { - return if (App.INSTANCE.isNightTheme) { + return if (AppConfig.isNightTheme) { Dark } else Light } diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt new file mode 100644 index 000000000..f51d7b479 --- /dev/null +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -0,0 +1,51 @@ +package io.legado.app.help + +import io.legado.app.App +import io.legado.app.constant.PreferKey +import io.legado.app.utils.* + +object AppConfig { + + var isNightTheme: Boolean + get() { + return when (App.INSTANCE.getPrefString("themeMode", "0")) { + "1" -> false + "2" -> true + else -> App.INSTANCE.sysIsDarkMode() + } + } + set(value) { + if (value) { + App.INSTANCE.putPrefString("themeMode", "2") + } else { + App.INSTANCE.putPrefString("themeMode", "1") + } + } + + var isTransparentStatusBar: Boolean + get() = App.INSTANCE.getPrefBoolean("transparentStatusBar") + set(value) { + App.INSTANCE.putPrefBoolean("transparentStatusBar", value) + } + + var isShowRSS: Boolean + get() = App.INSTANCE.getPrefBoolean(PreferKey.showRss) + set(value) { + App.INSTANCE.putPrefBoolean(PreferKey.showRss, value) + } + + var threadCount: Int + get() = App.INSTANCE.getPrefInt(PreferKey.threadCount) + set(value) { + App.INSTANCE.putPrefInt(PreferKey.threadCount, value) + } + + var autoDarkMode: Boolean + get() = App.INSTANCE.getPrefBoolean(PreferKey.autoDarkMode) + set(value) { + App.INSTANCE.putPrefBoolean(PreferKey.autoDarkMode, value) + } + + val isEInkMode: Boolean + get() = App.INSTANCE.getPrefBoolean("isEInkMode") +} diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index ff5ee0b7d..2c1330b12 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -1,19 +1,19 @@ package io.legado.app.help +import android.net.Uri +import androidx.documentfile.provider.DocumentFile import io.legado.app.App import io.legado.app.constant.PreferKey import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.ReplaceRule -import io.legado.app.utils.MD5Utils -import io.legado.app.utils.getPrefInt -import io.legado.app.utils.getPrefString +import io.legado.app.utils.* import org.apache.commons.text.similarity.JaccardSimilarity import java.io.File import kotlin.math.min object BookHelp { - + private const val cacheFolderName = "book_cache" private var downloadPath: String = App.INSTANCE.getPrefString(PreferKey.downloadPath) ?: App.INSTANCE.getExternalFilesDir(null)?.absolutePath @@ -26,76 +26,127 @@ object BookHelp { ?: App.INSTANCE.cacheDir.absolutePath } + private val downloadUri get() = Uri.parse(downloadPath) + + private fun bookFolderName(book: Book): String { + return formatFolderName(book.name) + MD5Utils.md5Encode16(book.bookUrl) + } + + private fun bookChapterName(bookChapter: BookChapter): String { + return String.format("%05d-%s", bookChapter.index, MD5Utils.md5Encode(bookChapter.title)) + } + private fun getBookCachePath(): String { - return "$downloadPath${File.separator}book_cache" + return "$downloadPath${File.separator}$cacheFolderName" } fun clearCache() { - FileHelp.deleteFile(getBookCachePath()) - FileHelp.getFolder(getBookCachePath()) + FileUtils.deleteFile(getBookCachePath()) + FileUtils.getFolder(getBookCachePath()) } @Synchronized fun saveContent(book: Book, bookChapter: BookChapter, content: String) { if (content.isEmpty()) return - FileHelp.getFolder(getBookFolder(book)).listFiles()?.forEach { - if (it.name.startsWith(String.format("%05d", bookChapter.index))) { - it.delete() - return@forEach + if (downloadUri.isDocumentUri(App.INSTANCE)) { + DocumentFile.fromTreeUri(App.INSTANCE, downloadUri)?.let { + DocumentUtils.createFileIfNotExist( + it, + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + )?.uri?.writeText(App.INSTANCE, content) } + } else { + FileUtils.createFileIfNotExist( + File(downloadPath), + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ).listFiles()?.forEach { + if (it.name.startsWith(String.format("%05d", bookChapter.index))) { + it.delete() + return@forEach + } + } + FileUtils.createFileIfNotExist( + File(downloadPath), + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ).writeText(content) } - val filePath = getChapterPath(book, bookChapter) - val file = FileHelp.getFile(filePath) - file.writeText(content) } fun getChapterCount(book: Book): Int { - return FileHelp.getFolder(getBookFolder(book)).list()?.size ?: 0 + if (downloadUri.isDocumentUri(App.INSTANCE)) { + DocumentFile.fromTreeUri(App.INSTANCE, downloadUri)?.let { + return DocumentUtils.createFileIfNotExist( + it, + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + )?.listFiles()?.size ?: 0 + } + } else { + return FileUtils.createFileIfNotExist( + File(downloadPath), + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ).list()?.size ?: 0 + } + return 0 } fun hasContent(book: Book, bookChapter: BookChapter): Boolean { - val filePath = getChapterPath(book, bookChapter) - runCatching { - val file = File(filePath) - if (file.exists()) { - return true + if (downloadUri.isDocumentUri(App.INSTANCE)) { + DocumentFile.fromTreeUri(App.INSTANCE, downloadUri)?.let { + return DocumentUtils.exists( + it, + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ) } + } else { + return FileUtils.exists( + File(downloadPath), + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ) } return false } fun getContent(book: Book, bookChapter: BookChapter): String? { - val filePath = getChapterPath(book, bookChapter) - runCatching { - val file = File(filePath) - if (file.exists()) { - return file.readText() + if (downloadUri.isDocumentUri(App.INSTANCE)) { + DocumentFile.fromTreeUri(App.INSTANCE, downloadUri)?.let { + return DocumentUtils.createFileIfNotExist( + it, + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + )?.uri?.readText(App.INSTANCE) } + } else { + return FileUtils.createFileIfNotExist( + File(downloadPath), + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ).readText() } return null } fun delContent(book: Book, bookChapter: BookChapter) { - val filePath = getChapterPath(book, bookChapter) - kotlin.runCatching { - val file = File(filePath) - if (file.exists()) { - file.delete() + if (downloadUri.isDocumentUri(App.INSTANCE)) { + DocumentFile.fromTreeUri(App.INSTANCE, downloadUri)?.let { + DocumentUtils.createFileIfNotExist( + it, + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + )?.delete() } + } else { + FileUtils.createFileIfNotExist( + File(downloadPath), + "${bookChapterName(bookChapter)}.nb", + subDirs = *arrayOf(cacheFolderName, bookFolderName(book)) + ).delete() } } - private fun getBookFolder(book: Book): String { - val bookFolder = formatFolderName(book.name + MD5Utils.md5Encode16(book.bookUrl)) - return "${getBookCachePath()}${File.separator}$bookFolder" - } - - private fun getChapterPath(book: Book, bookChapter: BookChapter): String { - val chapterFile = - String.format("%05d-%s", bookChapter.index, MD5Utils.md5Encode(bookChapter.title)) - return "${getBookFolder(book)}${File.separator}$chapterFile.nb" - } - private fun formatFolderName(folderName: String): String { return folderName.replace("[\\\\/:*?\"<>|.]".toRegex(), "") } @@ -124,16 +175,16 @@ object BookHelp { } var newIndex = 0 - val jaccardSimilarity = JaccardSimilarity() + val jSimilarity = JaccardSimilarity() var similarity = if (chapters.size > index) { - jaccardSimilarity.apply(title, chapters[index].title) + jSimilarity.apply(title, chapters[index].title) } else 0.0 if (similarity == 1.0) { return index } else { for (i in 1..50) { if (index - i in chapters.indices) { - jaccardSimilarity.apply(title, chapters[index - i].title).let { + jSimilarity.apply(title, chapters[index - i].title).let { if (it > similarity) { similarity = it newIndex = index - i @@ -144,7 +195,7 @@ object BookHelp { } } if (index + i in chapters.indices) { - jaccardSimilarity.apply(title, chapters[index + i].title).let { + jSimilarity.apply(title, chapters[index + i].title).let { if (it > similarity) { similarity = it newIndex = index + i diff --git a/app/src/main/java/io/legado/app/help/FileHelp.kt b/app/src/main/java/io/legado/app/help/FileHelp.kt deleted file mode 100644 index 5a7678563..000000000 --- a/app/src/main/java/io/legado/app/help/FileHelp.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.legado.app.help - -import io.legado.app.App -import java.io.File -import java.io.IOException - -object FileHelp { - - - //获取文件夹 - fun getFolder(filePath: String): File { - val file = File(filePath) - //如果文件夹不存在,就创建它 - if (!file.exists()) { - file.mkdirs() - } - return file - } - - //获取文件 - @Synchronized - fun getFile(filePath: String): File { - val file = File(filePath) - try { - if (!file.exists()) { - //创建父类文件夹 - getFolder(file.parent) - //创建文件 - file.createNewFile() - } - } catch (e: IOException) { - e.printStackTrace() - } - return file - } - - fun getCachePath(): String { - return App.INSTANCE.externalCacheDir?.absolutePath - ?: App.INSTANCE.cacheDir.absolutePath - } - - //递归删除文件夹下的数据 - @Synchronized - fun deleteFile(filePath: String) { - val file = File(filePath) - if (!file.exists()) return - - if (file.isDirectory) { - val files = file.listFiles() - files?.forEach { subFile -> - val path = subFile.path - deleteFile(path) - } - } - //删除文件 - file.delete() - } -} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/PrefExtensions.kt b/app/src/main/java/io/legado/app/help/PrefExtensions.kt deleted file mode 100644 index 9a41707a1..000000000 --- a/app/src/main/java/io/legado/app/help/PrefExtensions.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.legado.app.help - -import android.content.Context -import io.legado.app.constant.PreferKey -import io.legado.app.utils.getPrefBoolean -import io.legado.app.utils.getPrefInt - - -val Context.isShowRSS: Boolean - get() = getPrefBoolean(PreferKey.showRss, true) - -val Context.threadCount: Int - get() = getPrefInt(PreferKey.threadCount, 16) \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt index 328433c3c..311093c13 100644 --- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt @@ -111,7 +111,7 @@ object ReadBookConfig { var textSize: Int = 15 ) { fun setBg(bgType: Int, bg: String) { - if (App.INSTANCE.isNightTheme) { + if (AppConfig.isNightTheme) { bgTypeNight = bgType bgStrNight = bg } else { @@ -121,7 +121,7 @@ object ReadBookConfig { } fun setTextColor(color: Int) { - if (App.INSTANCE.isNightTheme) { + if (AppConfig.isNightTheme) { textColorNight = "#${color.hexString}" } else { textColor = "#${color.hexString}" @@ -129,7 +129,7 @@ object ReadBookConfig { } fun setStatusIconDark(isDark: Boolean) { - if (App.INSTANCE.isNightTheme) { + if (AppConfig.isNightTheme) { darkStatusIconNight = isDark } else { darkStatusIcon = isDark @@ -137,7 +137,7 @@ object ReadBookConfig { } fun statusIconDark(): Boolean { - return if (App.INSTANCE.isNightTheme) { + return if (AppConfig.isNightTheme) { darkStatusIconNight } else { darkStatusIcon @@ -145,17 +145,17 @@ object ReadBookConfig { } fun textColor(): Int { - return if (App.INSTANCE.isNightTheme) Color.parseColor(textColorNight) + return if (AppConfig.isNightTheme) Color.parseColor(textColorNight) else Color.parseColor(textColor) } fun bgStr(): String { - return if (App.INSTANCE.isNightTheme) bgStrNight + return if (AppConfig.isNightTheme) bgStrNight else bgStr } fun bgType(): Int { - return if (App.INSTANCE.isNightTheme) bgTypeNight + return if (AppConfig.isNightTheme) bgTypeNight else bgType } diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt index 90bf3b9e2..8a635bafc 100644 --- a/app/src/main/java/io/legado/app/help/storage/Backup.kt +++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt @@ -4,7 +4,6 @@ import android.content.Context import android.net.Uri import androidx.documentfile.provider.DocumentFile import io.legado.app.App -import io.legado.app.help.FileHelp import io.legado.app.help.ReadBookConfig import io.legado.app.utils.DocumentUtils import io.legado.app.utils.FileUtils @@ -48,37 +47,40 @@ object Backup { App.db.bookDao().allBooks.let { if (it.isNotEmpty()) { val json = GSON.toJson(it) - FileHelp.getFile(backupPath + File.separator + "bookshelf.json").writeText(json) + FileUtils.getFile(backupPath + File.separator + "bookshelf.json") + .writeText(json) } } App.db.bookGroupDao().all().let { if (it.isNotEmpty()) { val json = GSON.toJson(it) - FileHelp.getFile(backupPath + File.separator + "bookGroup.json").writeText(json) + FileUtils.getFile(backupPath + File.separator + "bookGroup.json") + .writeText(json) } } App.db.bookSourceDao().all.let { if (it.isNotEmpty()) { val json = GSON.toJson(it) - FileHelp.getFile(backupPath + File.separator + "bookSource.json") + FileUtils.getFile(backupPath + File.separator + "bookSource.json") .writeText(json) } } App.db.rssSourceDao().all.let { if (it.isNotEmpty()) { val json = GSON.toJson(it) - FileHelp.getFile(backupPath + File.separator + "rssSource.json").writeText(json) + FileUtils.getFile(backupPath + File.separator + "rssSource.json") + .writeText(json) } } App.db.replaceRuleDao().all.let { if (it.isNotEmpty()) { val json = GSON.toJson(it) - FileHelp.getFile(backupPath + File.separator + "replaceRule.json") + FileUtils.getFile(backupPath + File.separator + "replaceRule.json") .writeText(json) } } GSON.toJson(ReadBookConfig.configList)?.let { - FileHelp.getFile(backupPath + File.separator + ReadBookConfig.readConfigFileName) + FileUtils.getFile(backupPath + File.separator + ReadBookConfig.readConfigFileName) .writeText(it) } Preferences.getSharedPreferences(App.INSTANCE, backupPath, "config")?.let { sp -> @@ -111,7 +113,7 @@ object Backup { doc?.let { DocumentUtils.writeText( context, - FileHelp.getFile(backupPath + File.separator + fileName).readText(), + FileUtils.getFile(backupPath + File.separator + fileName).readText(), doc.uri ) } @@ -122,8 +124,8 @@ object Backup { private fun copyBackup() { try { for (fileName in backupFileNames) { - FileHelp.getFile(backupPath + File.separator + "bookshelf.json") - .copyTo(FileHelp.getFile(legadoPath + File.separator + "bookshelf.json"), true) + FileUtils.getFile(backupPath + File.separator + "bookshelf.json") + .copyTo(FileUtils.getFile(legadoPath + File.separator + "bookshelf.json"), true) } } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt index 15c14051c..6741e6830 100644 --- a/app/src/main/java/io/legado/app/help/storage/Restore.kt +++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt @@ -11,7 +11,6 @@ import com.jayway.jsonpath.ParseContext import io.legado.app.App import io.legado.app.constant.AppConst import io.legado.app.data.entities.* -import io.legado.app.help.FileHelp import io.legado.app.help.ReadBookConfig import io.legado.app.utils.* import kotlinx.coroutines.Dispatchers.IO @@ -38,7 +37,7 @@ object Restore { for (fileName in Backup.backupFileNames) { if (doc.name == fileName) { DocumentUtils.readText(context, doc.uri)?.let { - FileHelp.getFile(Backup.backupPath + File.separator + fileName) + FileUtils.getFile(Backup.backupPath + File.separator + fileName) .writeText(it) } } @@ -51,7 +50,7 @@ object Restore { suspend fun restore(path: String) { withContext(IO) { try { - val file = FileHelp.getFile(path + File.separator + "bookshelf.json") + val file = FileUtils.getFile(path + File.separator + "bookshelf.json") val json = file.readText() GSON.fromJsonArray(json)?.let { App.db.bookDao().insert(*it.toTypedArray()) @@ -60,7 +59,7 @@ object Restore { e.printStackTrace() } try { - val file = FileHelp.getFile(path + File.separator + "bookGroup.json") + val file = FileUtils.getFile(path + File.separator + "bookGroup.json") val json = file.readText() GSON.fromJsonArray(json)?.let { App.db.bookGroupDao().insert(*it.toTypedArray()) @@ -69,7 +68,7 @@ object Restore { e.printStackTrace() } try { - val file = FileHelp.getFile(path + File.separator + "bookSource.json") + val file = FileUtils.getFile(path + File.separator + "bookSource.json") val json = file.readText() GSON.fromJsonArray(json)?.let { App.db.bookSourceDao().insert(*it.toTypedArray()) @@ -78,7 +77,7 @@ object Restore { e.printStackTrace() } try { - val file = FileHelp.getFile(path + File.separator + "rssSource.json") + val file = FileUtils.getFile(path + File.separator + "rssSource.json") val json = file.readText() GSON.fromJsonArray(json)?.let { App.db.rssSourceDao().insert(*it.toTypedArray()) @@ -87,7 +86,7 @@ object Restore { e.printStackTrace() } try { - val file = FileHelp.getFile(path + File.separator + "replaceRule.json") + val file = FileUtils.getFile(path + File.separator + "replaceRule.json") val json = file.readText() GSON.fromJsonArray(json)?.let { App.db.replaceRuleDao().insert(*it.toTypedArray()) @@ -97,7 +96,7 @@ object Restore { } try { val file = - FileHelp.getFile(path + File.separator + ReadBookConfig.readConfigFileName) + FileUtils.getFile(path + File.separator + ReadBookConfig.readConfigFileName) val configFile = File(App.INSTANCE.filesDir.absolutePath + File.separator + ReadBookConfig.readConfigFileName) if (file.exists()) { @@ -126,7 +125,7 @@ object Restore { GlobalScope.launch(IO) { try {// 导入书架 val shelfFile = - FileHelp.getFile(Backup.defaultPath + File.separator + "myBookShelf.json") + FileUtils.getFile(Backup.defaultPath + File.separator + "myBookShelf.json") val json = shelfFile.readText() val importCount = importOldBookshelf(json) withContext(Main) { @@ -140,7 +139,7 @@ object Restore { try {// Book source val sourceFile = - FileHelp.getFile(Backup.defaultPath + File.separator + "myBookSource.json") + FileUtils.getFile(Backup.defaultPath + File.separator + "myBookSource.json") val json = sourceFile.readText() val importCount = importOldSource(json) withContext(Main) { @@ -154,7 +153,7 @@ object Restore { try {// Replace rules val ruleFile = - FileHelp.getFile(Backup.defaultPath + File.separator + "myBookReplaceRule.json") + FileUtils.getFile(Backup.defaultPath + File.separator + "myBookReplaceRule.json") val json = ruleFile.readText() val importCount = importOldReplaceRule(json) withContext(Main) { diff --git a/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt b/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt index 7d905299d..fb2e09a15 100644 --- a/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt +++ b/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt @@ -2,10 +2,10 @@ package io.legado.app.help.storage import android.content.Context import io.legado.app.App -import io.legado.app.help.FileHelp import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.webdav.WebDav import io.legado.app.lib.webdav.http.HttpAuth +import io.legado.app.utils.FileUtils import io.legado.app.utils.ZipUtils import io.legado.app.utils.getPrefString import kotlinx.coroutines.Dispatchers.IO @@ -17,9 +17,9 @@ import java.util.* import kotlin.math.min object WebDavHelp { - private val zipFilePath = FileHelp.getCachePath() + "/backup" + ".zip" + private val zipFilePath = FileUtils.getCachePath() + "/backup" + ".zip" private val unzipFilesPath by lazy { - FileHelp.getCachePath() + FileUtils.getCachePath() } private fun getWebDavUrl(): String? { @@ -86,7 +86,7 @@ object WebDavHelp { for (i in 0 until paths.size) { paths[i] = path + File.separator + paths[i] } - FileHelp.deleteFile(zipFilePath) + FileUtils.deleteFile(zipFilePath) if (ZipUtils.zipFiles(paths, zipFilePath)) { WebDav(getWebDavUrl() + "legado").makeAsDir() val putUrl = getWebDavUrl() + "legado/backup" + diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt index 46622b869..c625e1807 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -5,7 +5,6 @@ import android.app.Activity import android.app.ActivityManager import android.content.Context import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.os.Build import android.view.View import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR @@ -17,9 +16,8 @@ import androidx.recyclerview.widget.RecyclerView import androidx.viewpager.widget.ViewPager import com.google.android.material.bottomnavigation.BottomNavigationView import io.legado.app.R +import io.legado.app.help.AppConfig import io.legado.app.utils.getCompatColor -import io.legado.app.utils.isNightTheme -import io.legado.app.utils.isTransparentStatusBar import kotlinx.android.synthetic.main.activity_main.view.* import org.jetbrains.anko.backgroundColor @@ -38,7 +36,7 @@ object ATH { } fun setStatusBarColorAuto(activity: Activity, fullScreen: Boolean) { - val isTransparentStatusBar = activity.isTransparentStatusBar + val isTransparentStatusBar = AppConfig.isTransparentStatusBar setStatusBarColor( activity, ThemeStore.statusBarColor(activity, isTransparentStatusBar), @@ -132,14 +130,14 @@ object ATH { fun setTint( view: View, @ColorInt color: Int, - isDark: Boolean = view.context.isNightTheme + isDark: Boolean = AppConfig.isNightTheme ) { TintHelper.setTintAuto(view, color, false, isDark) } fun setBackgroundTint( view: View, @ColorInt color: Int, - isDark: Boolean = view.context.isNightTheme + isDark: Boolean = AppConfig.isNightTheme ) { TintHelper.setTintAuto(view, color, true, isDark) } @@ -207,11 +205,10 @@ object ATH { .setSelectedColor(ThemeStore.accentColor(bottom_navigation_view.context)).create() itemIconTintList = colorStateList itemTextColor = colorStateList - itemBackground = ColorDrawable(ThemeStore.primaryColor(bottomBar.context)) - /*itemBackgroundResource = when(context.isNightTheme) { + itemBackgroundResource = when (AppConfig.isNightTheme) { true -> R.drawable.item_bg_dark false -> R.drawable.item_bg_light - }*/ + } } } diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt index 6770f7db7..9c2e7a2f3 100644 --- a/app/src/main/java/io/legado/app/service/DownloadService.kt +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -9,10 +9,10 @@ import io.legado.app.base.BaseService import io.legado.app.constant.Action import io.legado.app.constant.AppConst import io.legado.app.constant.Bus +import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp import io.legado.app.help.IntentHelp import io.legado.app.help.coroutine.Coroutine -import io.legado.app.help.threadCount import io.legado.app.model.WebBook import io.legado.app.utils.postEvent import kotlinx.coroutines.Dispatchers.IO @@ -21,7 +21,7 @@ import java.util.concurrent.Executors class DownloadService : BaseService() { private var searchPool = - Executors.newFixedThreadPool(App.INSTANCE.threadCount).asCoroutineDispatcher() + Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() private var tasks: ArrayList> = arrayListOf() private val handler = Handler() private var runnable: Runnable = Runnable { upDownload() } diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt index c2467c1eb..b12fef048 100644 --- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt @@ -4,14 +4,10 @@ import android.app.PendingIntent import android.media.MediaPlayer import io.legado.app.constant.Bus import io.legado.app.data.api.IHttpPostApi -import io.legado.app.help.FileHelp import io.legado.app.help.IntentHelp import io.legado.app.help.http.HttpHelper import io.legado.app.service.help.ReadBook -import io.legado.app.utils.LogUtils -import io.legado.app.utils.getPrefInt -import io.legado.app.utils.getPrefString -import io.legado.app.utils.postEvent +import io.legado.app.utils.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Job import kotlinx.coroutines.isActive @@ -65,7 +61,7 @@ class HttpReadAloudService : BaseReadAloudService(), private fun downloadAudio() { job = launch(IO) { - FileHelp.deleteFile(ttsFolder) + FileUtils.deleteFile(ttsFolder) for (index in 0 until contentList.size) { if (isActive) { val bytes = HttpHelper.getByteRetrofit("http://tts.baidu.com") @@ -104,7 +100,7 @@ class HttpReadAloudService : BaseReadAloudService(), } private fun getSpeakFile(index: Int = nowSpeak): File { - return FileHelp.getFile("${ttsFolder}${File.separator}${index}.mp3") + return FileUtils.getFile("${ttsFolder}${File.separator}${index}.mp3") } private fun getAudioBody(content: String): Map { diff --git a/app/src/main/java/io/legado/app/ui/README.md b/app/src/main/java/io/legado/app/ui/README.md index 18c04f014..a1a7a2313 100644 --- a/app/src/main/java/io/legado/app/ui/README.md +++ b/app/src/main/java/io/legado/app/ui/README.md @@ -1 +1,24 @@ -## 放置与界面有关的类 \ No newline at end of file +## 放置与界面有关的类 + +* about 关于界面 +* audio 音频播放界面 +* book\info 书籍信息查看 +* book\read 书籍阅读界面 +* book\search 搜索书籍界面 +* book\source 搜索书源界面 +* changecover 封面换源界面 +* changesource 换源界面 +* chapterlist 目录界面 +* config 配置界面 +* download 下载界面 +* explore 发现界面 +* filechooser 文件选择界面 +* importbook 书籍导入界面 +* main 主界面 +* qrcode 二维码扫描界面 +* replacerule 替换净化界面 +* rss\article 订阅条目界面 +* rss\read 订阅阅读界面 +* rss\source 订阅源界面 +* welcome 欢迎界面 +* widget 自定义插件 \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index fb689519c..57839f1c9 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -98,8 +98,8 @@ class BookInfoActivity : return super.onCompatOptionsItemSelected(item) } - override fun onMenuOpened(featureId: Int, menu: Menu?): Boolean { - menu?.findItem(R.id.menu_can_update)?.isChecked = + override fun onMenuOpened(featureId: Int, menu: Menu): Boolean { + menu.findItem(R.id.menu_can_update)?.isChecked = viewModel.bookData.value?.canUpdate ?: true return super.onMenuOpened(featureId, menu) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/Help.kt b/app/src/main/java/io/legado/app/ui/book/read/Help.kt index 247349a60..939377cb1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/Help.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/Help.kt @@ -9,11 +9,11 @@ import android.view.Window import android.view.WindowManager import io.legado.app.App import io.legado.app.constant.PreferKey +import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ThemeStore import io.legado.app.utils.getPrefBoolean -import io.legado.app.utils.isTransparentStatusBar object Help { @@ -43,7 +43,7 @@ object Help { } else { ATH.setLightStatusBarAuto( activity, - ThemeStore.statusBarColor(activity, activity.isTransparentStatusBar) + ThemeStore.statusBarColor(activity, AppConfig.isTransparentStatusBar) ) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt index 19729f20b..12bbb6d8b 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt @@ -11,6 +11,7 @@ import androidx.core.view.isVisible import io.legado.app.App import io.legado.app.R import io.legado.app.constant.PreferKey +import io.legado.app.help.AppConfig import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.buttonDisabledColor import io.legado.app.service.help.ReadBook @@ -41,7 +42,7 @@ class ReadMenu : FrameLayout { init { callBack = activity as? CallBack inflate(context, R.layout.view_read_menu, this) - if (context.isNightTheme) { + if (AppConfig.isNightTheme) { fabNightTheme.setImageResource(R.drawable.ic_daytime) } else { fabNightTheme.setImageResource(R.drawable.ic_brightness) @@ -144,7 +145,7 @@ class ReadMenu : FrameLayout { //夜间模式 fabNightTheme.onClick { - context.putPrefBoolean("isNightTheme", !context.isNightTheme) + AppConfig.isNightTheme = !AppConfig.isNightTheme App.INSTANCE.applyDayNight() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt index 2b4a7dc4f..460e234c0 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt @@ -21,7 +21,6 @@ import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.constant.Bus -import io.legado.app.help.FileHelp import io.legado.app.help.ImageLoader import io.legado.app.help.ReadBookConfig import io.legado.app.help.permission.Permissions @@ -174,7 +173,7 @@ class BgTextConfigDialog : DialogFragment() { var file = requireContext().getExternalFilesDir(null) ?: requireContext().filesDir file = - FileHelp.getFile(file.absolutePath + File.separator + "bg" + File.separator + doc.name) + FileUtils.getFile(file.absolutePath + File.separator + "bg" + File.separator + doc.name) DocumentUtils.readBytes(requireContext(), uri)?.let { file.writeBytes(it) ReadBookConfig.getConfig().setBg(2, file.absolutePath) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index ae44ddaad..fb250fb46 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -76,7 +76,7 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se !getPrefBoolean(PreferKey.precisionSearch) ) precisionSearchMenuItem?.isChecked = getPrefBoolean(PreferKey.precisionSearch) - search_view.query.toString().trim()?.let { + search_view.query?.toString()?.trim()?.let { search_view.setQuery(it, true) } } @@ -88,7 +88,7 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se } else { putPrefString("searchGroup", item.title.toString()) } - search_view.query.toString().trim()?.let { + search_view.query?.toString()?.trim()?.let { search_view.setQuery(it, true) } } diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index 00148a7fd..125204062 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -6,8 +6,8 @@ import io.legado.app.base.BaseViewModel import io.legado.app.constant.PreferKey import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchKeyword +import io.legado.app.help.AppConfig import io.legado.app.help.coroutine.Coroutine -import io.legado.app.help.threadCount import io.legado.app.model.WebBook import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefString @@ -18,7 +18,7 @@ import java.util.concurrent.Executors class SearchViewModel(application: Application) : BaseViewModel(application) { private var searchPool = - Executors.newFixedThreadPool(context.threadCount).asCoroutineDispatcher() + Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() private var task: Coroutine<*>? = null var callBack: CallBack? = null var searchKey: String = "" diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt index 6ae3bd977..5c75187bd 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt @@ -7,7 +7,6 @@ import io.legado.app.App import io.legado.app.base.BaseViewModel import io.legado.app.data.api.IHttpGetApi import io.legado.app.data.entities.BookSource -import io.legado.app.help.FileHelp import io.legado.app.help.http.HttpHelper import io.legado.app.help.storage.Backup import io.legado.app.help.storage.OldRule @@ -92,7 +91,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) }.let { val json = GSON.toJson(it) val file = - FileHelp.getFile(Backup.exportPath + File.separator + "exportBookSource.json") + FileUtils.getFile(Backup.exportPath + File.separator + "exportBookSource.json") file.writeText(json) } }.onSuccess { diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt index 81dfcebe9..3b16a4359 100644 --- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt @@ -8,8 +8,8 @@ import io.legado.app.R import io.legado.app.base.BaseViewModel import io.legado.app.data.entities.Book import io.legado.app.data.entities.SearchBook +import io.legado.app.help.AppConfig import io.legado.app.help.coroutine.Coroutine -import io.legado.app.help.threadCount import io.legado.app.model.WebBook import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main @@ -20,7 +20,7 @@ import java.util.concurrent.Executors class ChangeSourceViewModel(application: Application) : BaseViewModel(application) { private var searchPool = - Executors.newFixedThreadPool(context.threadCount).asCoroutineDispatcher() + Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() var callBack: CallBack? = null val searchStateData = MutableLiveData() var name: String = "" diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index 07a5a641d..7a0d7fb0f 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -1,6 +1,8 @@ package io.legado.app.ui.config +import android.app.Activity.RESULT_OK import android.content.ComponentName +import android.content.Intent import android.content.SharedPreferences import android.content.pm.PackageManager import android.os.Bundle @@ -12,8 +14,10 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.constant.Bus import io.legado.app.constant.PreferKey +import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp -import io.legado.app.help.threadCount +import io.legado.app.help.permission.Permissions +import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.theme.ATH import io.legado.app.receiver.SharedReceiverActivity import io.legado.app.ui.filechooser.FileChooserDialog @@ -26,7 +30,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener, SharedPreferences.OnSharedPreferenceChangeListener { - private val downloadPath = 25324 + private val requestCodeDownloadPath = 25324 private val packageManager = App.INSTANCE.packageManager private val componentName = ComponentName( App.INSTANCE, @@ -61,18 +65,13 @@ class OtherConfigFragment : PreferenceFragmentCompat(), .setTitle(getString(R.string.threads_num_title)) .setMaxValue(999) .setMinValue(1) - .setValue(requireContext().threadCount) + .setValue(AppConfig.threadCount) .show { requireContext().putPrefInt(PreferKey.threadCount, it) findPreference(PreferKey.threadCount)?.summary = getString(R.string.threads_num, it.toString()) } - PreferKey.downloadPath -> FileChooserDialog.show( - childFragmentManager, - downloadPath, - mode = FileChooserDialog.DIRECTORY, - initPath = getPreferenceString(PreferKey.downloadPath).toString() - ) + PreferKey.downloadPath -> selectDownloadPathSys() PreferKey.cleanCache -> { BookHelp.clearCache() toast(R.string.clear_cache_success) @@ -125,7 +124,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(), PreferKey.downloadPath -> getPrefString(PreferKey.downloadPath) ?: App.INSTANCE.getExternalFilesDir(null)?.absolutePath ?: App.INSTANCE.cacheDir.absolutePath - PreferKey.threadCount -> requireContext().threadCount + PreferKey.threadCount -> AppConfig.threadCount else -> getPrefString(key) ?: "" } } @@ -148,7 +147,49 @@ class OtherConfigFragment : PreferenceFragmentCompat(), } } + private fun selectDownloadPathSys() { + try { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + startActivityForResult(intent, requestCodeDownloadPath) + } catch (e: Exception) { + selectDownloadPath() + } + } + + private fun selectDownloadPath() { + PermissionsCompat.Builder(this) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + FileChooserDialog.show( + childFragmentManager, + requestCodeDownloadPath, + mode = FileChooserDialog.DIRECTORY, + initPath = getPreferenceString(PreferKey.downloadPath).toString() + ) + } + .request() + } + override fun onFilePicked(requestCode: Int, currentPath: String) { - putPrefString(PreferKey.downloadPath, currentPath) + if (requestCode == requestCodeDownloadPath) { + putPrefString(PreferKey.downloadPath, currentPath) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + requestCodeDownloadPath -> if (resultCode == RESULT_OK) { + data?.data?.let { uri -> + requireContext().contentResolver.takePersistableUriPermission( + uri, + Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION + ) + putPrefString(PreferKey.downloadPath, uri.toString()) + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt index 4dff95998..2ee2f6621 100644 --- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt @@ -10,6 +10,7 @@ import androidx.preference.PreferenceFragmentCompat import io.legado.app.App import io.legado.app.R import io.legado.app.constant.Bus +import io.legado.app.help.AppConfig import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.yesButton @@ -20,7 +21,7 @@ import io.legado.app.utils.* class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { - val items = arrayOf("极简","曜夜","经典","黑白","A屏黑") + val items = arrayOf("极简", "曜夜", "经典", "黑白", "A屏黑") override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_config_theme) @@ -110,31 +111,37 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar putPrefInt("colorPrimary", getCompatColor(R.color.md_grey_100)) putPrefInt("colorAccent", getCompatColor(R.color.lightBlue_color)) putPrefInt("colorBackground", getCompatColor(R.color.md_grey_100)) - putPrefBoolean("isNightTheme", false) + AppConfig.isNightTheme = false } 1 -> { putPrefInt("colorPrimaryNight", getCompatColor(R.color.shine_color)) putPrefInt("colorAccentNight", getCompatColor(R.color.lightBlue_color)) putPrefInt("colorBackgroundNight", getCompatColor(R.color.shine_color)) - putPrefBoolean("isNightTheme", true) + AppConfig.isNightTheme = true } 2 -> { putPrefInt("colorPrimary", getCompatColor(R.color.md_light_blue_500)) putPrefInt("colorAccent", getCompatColor(R.color.md_pink_800)) putPrefInt("colorBackground", getCompatColor(R.color.md_grey_100)) - putPrefBoolean("isNightTheme", false) + AppConfig.isNightTheme = false } 3 -> { putPrefInt("colorPrimary", getCompatColor(R.color.white)) putPrefInt("colorAccent", getCompatColor(R.color.black)) putPrefInt("colorBackground", getCompatColor(R.color.white)) - putPrefBoolean("isNightTheme", false) + AppConfig.isNightTheme = false } 4 -> { putPrefInt("colorPrimaryNight", getCompatColor(R.color.black)) - putPrefInt("colorAccentNight", getCompatColor(R.color.md_grey_600)) - putPrefInt("colorBackgroundNight", getCompatColor(R.color.black)) - putPrefBoolean("isNightTheme", true) + putPrefInt( + "colorAccentNight", + getCompatColor(R.color.md_grey_600) + ) + putPrefInt( + "colorBackgroundNight", + getCompatColor(R.color.black) + ) + AppConfig.isNightTheme = true } } App.INSTANCE.applyDayNight() @@ -166,7 +173,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar } private fun upTheme(isNightTheme: Boolean) { - if (this.isNightTheme == isNightTheme) { + if (AppConfig.isNightTheme == isNightTheme) { App.INSTANCE.applyTheme() recreateActivities() } diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 0b47b24f6..bdaf72031 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -16,8 +16,8 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.constant.Bus import io.legado.app.constant.PreferKey +import io.legado.app.help.AppConfig import io.legado.app.help.coroutine.Coroutine -import io.legado.app.help.isShowRSS import io.legado.app.help.storage.Backup import io.legado.app.lib.theme.ATH import io.legado.app.service.BaseReadAloudService @@ -47,7 +47,7 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), view_pager_main.adapter = TabFragmentPageAdapter(supportFragmentManager) view_pager_main.addOnPageChangeListener(this) bottom_navigation_view.setOnNavigationItemSelectedListener(this) - bottom_navigation_view.menu.findItem(R.id.menu_rss).isVisible = isShowRSS + bottom_navigation_view.menu.findItem(R.id.menu_rss).isVisible = AppConfig.isShowRSS upVersion() } @@ -68,10 +68,10 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), fragmentList.add(RssFragment()) fragmentList.add(MyFragment()) } - if (isShowRSS && fragmentList.size < 4) { + if (AppConfig.isShowRSS && fragmentList.size < 4) { fragmentList.add(2, RssFragment()) } - if (!isShowRSS && fragmentList.size == 4) { + if (!AppConfig.isShowRSS && fragmentList.size == 4) { fragmentList.removeAt(2) } } @@ -89,7 +89,7 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), pagePosition = position when (position) { 0, 1, 3 -> bottom_navigation_view.menu.getItem(position).isChecked = true - 2 -> if (isShowRSS) { + 2 -> if (AppConfig.isShowRSS) { bottom_navigation_view.menu.getItem(position).isChecked = true } else { bottom_navigation_view.menu.getItem(3).isChecked = true @@ -147,10 +147,10 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), recreate() } observeEvent(Bus.SHOW_RSS) { - bottom_navigation_view.menu.findItem(R.id.menu_rss).isVisible = isShowRSS + bottom_navigation_view.menu.findItem(R.id.menu_rss).isVisible = AppConfig.isShowRSS upFragmentList() view_pager_main.adapter?.notifyDataSetChanged() - if (isShowRSS) { + if (AppConfig.isShowRSS) { view_pager_main.setCurrentItem(3, false) } } @@ -168,7 +168,7 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), } override fun getCount(): Int { - return if (isShowRSS) 4 else 3 + return if (AppConfig.isShowRSS) 4 else 3 } } diff --git a/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt b/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt index 6d79f55d4..d3ee247c7 100644 --- a/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt @@ -200,7 +200,7 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { when (key) { - "isNightTheme" -> App.INSTANCE.applyDayNight() + "themeMode" -> App.INSTANCE.applyDayNight() "webService" -> { if (requireContext().getPrefBoolean("webService")) { WebService.start(requireContext()) diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt index ba6bc2a61..60be30fae 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt @@ -6,9 +6,9 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseViewModel import io.legado.app.data.entities.ReplaceRule -import io.legado.app.help.FileHelp import io.legado.app.help.storage.Backup import io.legado.app.help.storage.Restore +import io.legado.app.utils.FileUtils import io.legado.app.utils.GSON import io.legado.app.utils.splitNotBlank import org.jetbrains.anko.toast @@ -80,7 +80,7 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application }.let { val json = GSON.toJson(it) val file = - FileHelp.getFile(Backup.exportPath + File.separator + "exportReplaceRule.json") + FileUtils.getFile(Backup.exportPath + File.separator + "exportReplaceRule.json") file.writeText(json) } }.onSuccess { diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index a8ca32fdd..894bed7a9 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -149,7 +149,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r webView.settings.javaScriptEnabled = true webView.evaluateJavascript("document.documentElement.outerHTML") { val html = StringEscapeUtils.unescapeJson(it) - viewModel.readAloud(Jsoup.clean(html, Whitelist())) + viewModel.readAloud(Jsoup.clean(html, Whitelist.none())) } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt index 5af6841e9..36a487a6c 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt @@ -85,17 +85,12 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), } fun clHtml(content: String): String { - return if (content.contains("$content - """ - } + + $content""" } override fun onInit(status: Int) { diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt index c47bb8c9d..22c8f62f4 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt @@ -4,10 +4,10 @@ import android.app.Application import android.text.TextUtils import com.jayway.jsonpath.JsonPath import io.legado.app.App +import io.legado.app.R import io.legado.app.base.BaseViewModel import io.legado.app.data.api.IHttpGetApi import io.legado.app.data.entities.RssSource -import io.legado.app.help.FileHelp import io.legado.app.help.http.HttpHelper import io.legado.app.help.storage.Backup import io.legado.app.help.storage.Restore.jsonPath @@ -65,7 +65,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application) App.db.rssSourceDao().getRssSources(*ids.toTypedArray()).let { val json = GSON.toJson(it) val file = - FileHelp.getFile(Backup.exportPath + File.separator + "exportRssSource.json") + FileUtils.getFile(Backup.exportPath + File.separator + "exportRssSource.json") file.writeText(json) } }.onSuccess { @@ -123,6 +123,8 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application) App.db.rssSourceDao().insert(*it.toTypedArray()) } } + }.onSuccess { + finally.invoke(context.getString(R.string.success)) } } diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt index 420bc9233..5e57e79fc 100644 --- a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt @@ -5,7 +5,6 @@ import android.app.Activity.RESULT_OK import android.content.Intent import android.net.Uri import android.os.Bundle -import android.os.Environment import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.MenuItem @@ -18,13 +17,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import io.legado.app.App import io.legado.app.R import io.legado.app.constant.PreferKey -import io.legado.app.help.FileHelp import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat -import io.legado.app.utils.DocumentUtils -import io.legado.app.utils.getPrefString -import io.legado.app.utils.putPrefString -import io.legado.app.utils.toast +import io.legado.app.utils.* import kotlinx.android.synthetic.main.dialog_font_select.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO @@ -127,11 +122,11 @@ class FontSelectDialog : DialogFragment(), @SuppressLint("DefaultLocale") private fun getFontFiles(uri: Uri) { launch(IO) { - FileHelp.deleteFile(fontCacheFolder) + FileUtils.deleteFile(fontCacheFolder) DocumentFile.fromTreeUri(App.INSTANCE, uri)?.listFiles()?.forEach { file -> if (file.name?.toLowerCase()?.matches(".*\\.[ot]tf".toRegex()) == true) { DocumentUtils.readBytes(App.INSTANCE, file.uri)?.let { - FileHelp.getFile(fontCacheFolder + file.name).writeBytes(it) + FileUtils.getFile(fontCacheFolder + file.name).writeBytes(it) } } } @@ -154,7 +149,7 @@ class FontSelectDialog : DialogFragment(), private fun getFontFilesOld() { try { val file = - File(Environment.getExternalStorageDirectory().absolutePath + File.separator + "Fonts") + File(FileUtils.getSdCardPath() + File.separator + "Fonts") file.listFiles { pathName -> pathName.name.toLowerCase().matches(".*\\.[ot]tf".toRegex()) }?.let { @@ -167,7 +162,7 @@ class FontSelectDialog : DialogFragment(), override fun onClick(file: File) { launch(IO) { - file.copyTo(FileHelp.getFile(fontFolder + file.name), true).absolutePath.let { path -> + file.copyTo(FileUtils.getFile(fontFolder + file.name), true).absolutePath.let { path -> val cb = (parentFragment as? CallBack) ?: (activity as? CallBack) cb?.let { if (it.curFontPath != path) { diff --git a/app/src/main/java/io/legado/app/utils/ACache.kt b/app/src/main/java/io/legado/app/utils/ACache.kt index 44f96631c..03a330d6a 100644 --- a/app/src/main/java/io/legado/app/utils/ACache.kt +++ b/app/src/main/java/io/legado/app/utils/ACache.kt @@ -171,7 +171,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) * @return JSONObject数据 */ fun getAsJSONObject(key: String): JSONObject? { - val json = getAsString(key) + val json = getAsString(key) ?: return null return try { JSONObject(json) } catch (e: Exception) { @@ -311,17 +311,17 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) fun getAsObject(key: String): Any? { val data = getAsBinary(key) if (data != null) { - var bais: ByteArrayInputStream? = null + var bis: ByteArrayInputStream? = null var ois: ObjectInputStream? = null try { - bais = ByteArrayInputStream(data) - ois = ObjectInputStream(bais) + bis = ByteArrayInputStream(data) + ois = ObjectInputStream(bis) return ois.readObject() } catch (e: Exception) { e.printStackTrace() } finally { try { - bais?.close() + bis?.close() } catch (e: IOException) { e.printStackTrace() } @@ -597,6 +597,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) val w = drawable.intrinsicWidth val h = drawable.intrinsicHeight // 取 drawable 的颜色格式 + @Suppress("DEPRECATION") val config = if (drawable.opacity != PixelFormat.OPAQUE) Bitmap.Config.ARGB_8888 else diff --git a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt index e53b33e30..d5fc85fdf 100644 --- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.content.res.ColorStateList +import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.provider.Settings @@ -126,11 +127,7 @@ fun Context.shareWithQr(title: String, text: String) { } } -val Context.isNightTheme: Boolean - get() = getPrefBoolean("isNightTheme") - -val Context.isEInkMode: Boolean - get() = getPrefBoolean("isEInkMode") - -val Context.isTransparentStatusBar: Boolean - get() = getPrefBoolean("transparentStatusBar", true) +fun Context.sysIsDarkMode(): Boolean { + val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return mode == Configuration.UI_MODE_NIGHT_YES +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/utils/DocumentUtils.kt b/app/src/main/java/io/legado/app/utils/DocumentUtils.kt index 3929874ef..3d1f53a58 100644 --- a/app/src/main/java/io/legado/app/utils/DocumentUtils.kt +++ b/app/src/main/java/io/legado/app/utils/DocumentUtils.kt @@ -2,9 +2,44 @@ package io.legado.app.utils import android.content.Context import android.net.Uri +import androidx.documentfile.provider.DocumentFile object DocumentUtils { + fun exists(root: DocumentFile, fileName: String, vararg subDirs: String): Boolean { + val parent = getDirDocument(root, *subDirs) ?: return false + return parent.findFile(fileName)?.exists() ?: false + } + + fun createFileIfNotExist( + root: DocumentFile, + fileName: String, + mimeType: String = "", + vararg subDirs: String + ): DocumentFile? { + val parent: DocumentFile? = createFileIfNotExist(root, *subDirs) + return parent?.createFile(mimeType, fileName) + } + + fun createFileIfNotExist(root: DocumentFile, vararg subDirs: String): DocumentFile? { + var parent: DocumentFile? = root + for (subDirName in subDirs) { + val subDir = parent?.findFile(subDirName) + ?: parent?.createDirectory(subDirName) + parent = subDir + } + return parent + } + + fun getDirDocument(root: DocumentFile, vararg subDirs: String): DocumentFile? { + var parent = root + for (subDirName in subDirs) { + val subDir = parent.findFile(subDirName) + parent = subDir ?: return null + } + return parent + } + @JvmStatic @Throws(Exception::class) fun writeText(context: Context, data: String, fileUri: Uri): Boolean { diff --git a/app/src/main/java/io/legado/app/utils/FileUtils.kt b/app/src/main/java/io/legado/app/utils/FileUtils.kt index 39148fe76..3d9923448 100644 --- a/app/src/main/java/io/legado/app/utils/FileUtils.kt +++ b/app/src/main/java/io/legado/app/utils/FileUtils.kt @@ -4,86 +4,92 @@ import android.content.ContentUris import android.content.Context import android.net.Uri import android.os.Environment -import android.os.storage.StorageManager import android.provider.DocumentsContract import android.provider.MediaStore -import android.util.Log -import androidx.core.content.ContextCompat +import io.legado.app.App import java.io.File import java.io.IOException -import java.lang.reflect.Array -import java.util.* object FileUtils { - fun getFileByPath(filePath: String): File? { - return if (filePath.isBlank()) null else File(filePath) + fun exists(file: File, fileName: String, vararg subDirs: String): Boolean { + val filePath = + file.absolutePath + File.separator + subDirs.joinToString(File.separator) + File.separator + fileName + return File(filePath).exists() } - fun getSdCardPath(): String { - var sdCardDirectory = Environment.getExternalStorageDirectory().absolutePath - - try { - sdCardDirectory = File(sdCardDirectory).canonicalPath - } catch (ioe: IOException) { - ioe.printStackTrace() - } + fun createFileIfNotExist(file: File, fileName: String, vararg subDirs: String): File { + val filePath = + file.absolutePath + File.separator + subDirs.joinToString(File.separator) + File.separator + fileName + return getFile(filePath) + } - return sdCardDirectory + fun createFileIfNotExist(file: File, vararg subDirs: String): File { + val filePath = file.absolutePath + File.separator + subDirs.joinToString(File.separator) + return getFolder(filePath) } - fun getStorageData(pContext: Context): ArrayList? { + fun getCachePath(): String { + return App.INSTANCE.externalCacheDir?.absolutePath + ?: App.INSTANCE.cacheDir.absolutePath + } - val storageManager = pContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager + //获取文件夹 + fun getFolder(filePath: String): File { + val file = File(filePath) + //如果文件夹不存在,就创建它 + if (!file.exists()) { + file.mkdirs() + } + return file + } + //获取文件 + @Synchronized + fun getFile(filePath: String): File { + val file = File(filePath) try { - val getVolumeList = storageManager.javaClass.getMethod("getVolumeList") - - val storageVolumeClazz = Class.forName("android.os.storage.StorageVolume") - val getPath = storageVolumeClazz.getMethod("getPath") - - val invokeVolumeList = getVolumeList.invoke(storageManager) - val length = Array.getLength(invokeVolumeList) - - val list = ArrayList() - for (i in 0 until length) { - val storageVolume = Array.get(invokeVolumeList, i)//得到StorageVolume对象 - val path = getPath.invoke(storageVolume) as String - - list.add(path) + if (!file.exists()) { + //创建父类文件夹 + file.parent?.let { + getFolder(it) + } + //创建文件 + file.createNewFile() } - return list - } catch (e: Exception) { + } catch (e: IOException) { e.printStackTrace() } - - return null + return file } - - fun getExtSdCardPaths(con: Context): ArrayList { - val paths = ArrayList() - val files = ContextCompat.getExternalFilesDirs(con, "external") - val firstFile = files[0] - for (file in files) { - if (file != null && file != firstFile) { - val index = file.absolutePath.lastIndexOf("/Android/data") - if (index < 0) { - Log.w("", "Unexpected external file dir: " + file.absolutePath) - } else { - var path = file.absolutePath.substring(0, index) - try { - path = File(path).canonicalPath - } catch (e: IOException) { - // Keep non-canonical path. - } - - paths.add(path) - } + //递归删除文件夹下的数据 + @Synchronized + fun deleteFile(filePath: String) { + val file = File(filePath) + if (!file.exists()) return + + if (file.isDirectory) { + val files = file.listFiles() + files?.forEach { subFile -> + val path = subFile.path + deleteFile(path) } } - return paths + //删除文件 + file.delete() + } + + fun getSdCardPath(): String { + @Suppress("DEPRECATION") + var sdCardDirectory = Environment.getExternalStorageDirectory().absolutePath + try { + sdCardDirectory = File(sdCardDirectory).canonicalPath + } catch (ioe: IOException) { + ioe.printStackTrace() + } + return sdCardDirectory } fun getPath(context: Context, uri: Uri): String? { @@ -96,6 +102,7 @@ object FileUtils { val type = split[0] if ("primary".equals(type, ignoreCase = true)) { + @Suppress("DEPRECATION") return Environment.getExternalStorageDirectory().toString() + "/" + split[1] } @@ -145,7 +152,7 @@ object FileUtils { private fun getDataColumn( context: Context, uri: Uri, selection: String?, - selectionArgs: kotlin.Array? + selectionArgs: Array? ): String? { val column = "_data" diff --git a/app/src/main/java/io/legado/app/utils/FragmentExtensions.kt b/app/src/main/java/io/legado/app/utils/FragmentExtensions.kt index f233929fb..b2379e89a 100644 --- a/app/src/main/java/io/legado/app/utils/FragmentExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/FragmentExtensions.kt @@ -54,17 +54,9 @@ fun Fragment.getCompatDrawable(@DrawableRes id: Int): Drawable? = requireContext fun Fragment.getCompatColorStateList(@ColorRes id: Int): ColorStateList? = requireContext().getCompatColorStateList(id) -val Fragment.isNightTheme: Boolean - get() = getPrefBoolean("isNightTheme") - -val Fragment.isTransparentStatusBar: Boolean - get() = getPrefBoolean("transparentStatusBar") - - inline fun Fragment.startActivity(vararg params: Pair) = AnkoInternals.internalStartActivity(requireActivity(), T::class.java, params) - inline fun Fragment.startActivityForResult(requestCode: Int, vararg params: Pair) = startActivityForResult(AnkoInternals.createIntent(requireActivity(), T::class.java, params), requestCode) diff --git a/app/src/main/java/io/legado/app/utils/LogUtils.kt b/app/src/main/java/io/legado/app/utils/LogUtils.kt index 1662974a7..b6712ad4d 100644 --- a/app/src/main/java/io/legado/app/utils/LogUtils.kt +++ b/app/src/main/java/io/legado/app/utils/LogUtils.kt @@ -2,7 +2,6 @@ package io.legado.app.utils import android.annotation.SuppressLint import io.legado.app.App -import io.legado.app.help.FileHelp import java.io.File import java.text.SimpleDateFormat import java.util.* @@ -30,8 +29,8 @@ object LogUtils { } private val fileHandler by lazy { - val logFolder = FileHelp.getCachePath() + File.separator + "logs" - FileHelp.getFolder(logFolder) + val logFolder = FileUtils.getCachePath() + File.separator + "logs" + FileUtils.getFolder(logFolder) FileHandler( logFolder + File.separator + "app.log", 10240, diff --git a/app/src/main/java/io/legado/app/utils/StringExtensions.kt b/app/src/main/java/io/legado/app/utils/StringExtensions.kt index 987a0fce8..19319d3ba 100644 --- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt @@ -4,34 +4,41 @@ package io.legado.app.utils fun String?.safeTrim() = if (this.isNullOrBlank()) null else this.trim() -fun String?.isAbsUrl() = if (this.isNullOrBlank()) false else this.startsWith("http://", true) - || this.startsWith("https://", true) - -fun String?.isJson(): Boolean = this?.run { - val str = this.trim() - when { - str.startsWith("{") && str.endsWith("}") -> true - str.startsWith("[") && str.endsWith("]") -> true - else -> false - } -} ?: false - -fun String?.isJsonObject(): Boolean = this?.run { - val str = this.trim() - str.startsWith("{") && str.endsWith("}") -} ?: false - -fun String?.isJsonArray(): Boolean = this?.run { - val str = this.trim() - str.startsWith("[") && str.endsWith("]") -} ?: false - -fun String?.htmlFormat(): String = if (this.isNullOrBlank()) "" else - this.replace("(?i)<(br[\\s/]*|/*p\\b.*?|/*div\\b.*?)>".toRegex(), "\n")// 替换特定标签为换行符 - .replace("<[script>]*.*?>| ".toRegex(), "")// 删除script标签对和空格转义符 - .replace("\\s*\\n+\\s*".toRegex(), "\n  ")// 移除空行,并增加段前缩进2个汉字 - .replace("^[\\n\\s]+".toRegex(), "  ")//移除开头空行,并增加段前缩进2个汉字 - .replace("[\\n\\s]+$".toRegex(), "") //移除尾部空行 +fun String?.isAbsUrl() = + this?.let { + it.startsWith("http://", true) + || it.startsWith("https://", true) + } ?: false + +fun String?.isJson(): Boolean = + this?.run { + val str = this.trim() + when { + str.startsWith("{") && str.endsWith("}") -> true + str.startsWith("[") && str.endsWith("]") -> true + else -> false + } + } ?: false + +fun String?.isJsonObject(): Boolean = + this?.run { + val str = this.trim() + str.startsWith("{") && str.endsWith("}") + } ?: false + +fun String?.isJsonArray(): Boolean = + this?.run { + val str = this.trim() + str.startsWith("[") && str.endsWith("]") + } ?: false + +fun String?.htmlFormat(): String = + this?.replace("(?i)<(br[\\s/]*|/*p\\b.*?|/*div\\b.*?)>".toRegex(), "\n") + ?.replace("<[script>]*.*?>| ".toRegex(), "") + ?.replace("\\s*\\n+\\s*".toRegex(), "\n  ") + ?.replace("^[\\n\\s]+".toRegex(), "  ") + ?.replace("[\\n\\s]+$".toRegex(), "") + ?: "" fun String.splitNotBlank(vararg delimiter: String): Array = run { this.split(*delimiter).map { it.trim() }.filterNot { it.isBlank() }.toTypedArray() diff --git a/app/src/main/java/io/legado/app/utils/StringUtils.kt b/app/src/main/java/io/legado/app/utils/StringUtils.kt index c9f80866f..95096c087 100644 --- a/app/src/main/java/io/legado/app/utils/StringUtils.kt +++ b/app/src/main/java/io/legado/app/utils/StringUtils.kt @@ -41,16 +41,18 @@ object StringUtils { //将时间转换成日期 fun dateConvert(time: Long, pattern: String): String { val date = Date(time) - @SuppressLint("SimpleDateFormat") val format = SimpleDateFormat(pattern) + @SuppressLint("SimpleDateFormat") + val format = SimpleDateFormat(pattern) return format.format(date) } //将日期转换成昨天、今天、明天 fun dateConvert(source: String, pattern: String): String { - @SuppressLint("SimpleDateFormat") val format = SimpleDateFormat(pattern) + @SuppressLint("SimpleDateFormat") + val format = SimpleDateFormat(pattern) val calendar = Calendar.getInstance() try { - val date = format.parse(source) + val date = format.parse(source) ?: return "" val curTime = calendar.timeInMillis calendar.time = date //将MISC 转换成 sec @@ -62,13 +64,18 @@ object StringUtils { //如果没有时间 if (oldHour == 0) { //比日期:昨天今天和明天 - if (difDate == 0L) { - return "今天" - } else if (difDate < DAY_OF_YESTERDAY) { - return "昨天" - } else { - @SuppressLint("SimpleDateFormat") val convertFormat = SimpleDateFormat("yyyy-MM-dd") - return convertFormat.format(date) + return when { + difDate == 0L -> { + "今天" + } + difDate < DAY_OF_YESTERDAY -> { + "昨天" + } + else -> { + @SuppressLint("SimpleDateFormat") + val convertFormat = SimpleDateFormat("yyyy-MM-dd") + convertFormat.format(date) + } } } @@ -78,7 +85,8 @@ object StringUtils { difHour < HOUR_OF_DAY -> difHour.toString() + "小时前" difDate < DAY_OF_YESTERDAY -> "昨天" else -> { - @SuppressLint("SimpleDateFormat") val convertFormat = SimpleDateFormat("yyyy-MM-dd") + @SuppressLint("SimpleDateFormat") + val convertFormat = SimpleDateFormat("yyyy-MM-dd") convertFormat.format(date) } } @@ -242,7 +250,7 @@ object StringUtils { val m = p.matcher(data) val buf = StringBuffer(data.length) while (m.find()) { - val ch = Integer.parseInt(m.group(1), 16).toChar().toString() + val ch = Integer.parseInt(m.group(1)!!, 16).toChar().toString() m.appendReplacement(buf, Matcher.quoteReplacement(ch)) } m.appendTail(buf) @@ -254,9 +262,9 @@ object StringUtils { "(?i)<(br[\\s/]*|/*p.*?|/*div.*?)>".toRegex(), "\n" )// 替换特定标签为换行符 - .replace("<[script>]*.*?>| ".toRegex(), "")// 删除script标签对和空格转义符 - .replace("\\s*\\n+\\s*".toRegex(), "\n  ")// 移除空行,并增加段前缩进2个汉字 - .replace("^[\\n\\s]+".toRegex(), "  ")//移除开头空行,并增加段前缩进2个汉字 - .replace("[\\n\\s]+$".toRegex(), "") //移除尾部空行 + .replace("<[script>]*.*?>| ".toRegex(), "")// 删除script标签对和空格转义符 + .replace("\\s*\\n+\\s*".toRegex(), "\n  ")// 移除空行,并增加段前缩进2个汉字 + .replace("^[\\n\\s]+".toRegex(), "  ")//移除开头空行,并增加段前缩进2个汉字 + .replace("[\\n\\s]+$".toRegex(), "") //移除尾部空行 } } diff --git a/app/src/main/java/io/legado/app/utils/UriExtensions.kt b/app/src/main/java/io/legado/app/utils/UriExtensions.kt index 297a5a612..49b76b439 100644 --- a/app/src/main/java/io/legado/app/utils/UriExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/UriExtensions.kt @@ -5,6 +5,9 @@ import android.net.Uri import androidx.documentfile.provider.DocumentFile import java.io.File +fun Uri.isDocumentUri(context: Context): Boolean { + return DocumentFile.isDocumentUri(context, this) +} @Throws(Exception::class) fun Uri.readBytes(context: Context): ByteArray? { diff --git a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt index 81cdafe81..403f0d40c 100644 --- a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt @@ -24,7 +24,7 @@ private tailrec fun getCompatActivity(context: Context?): AppCompatActivity? { val View.activity: AppCompatActivity? get() = getCompatActivity(context) -inline fun View.hideSoftInput() = run { +fun View.hideSoftInput() = run { val imm = App.INSTANCE.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager imm?.let { imm.hideSoftInputFromWindow(this.windowToken, 0) diff --git a/app/src/main/java/io/legado/app/utils/ZipUtils.kt b/app/src/main/java/io/legado/app/utils/ZipUtils.kt index eab9951c7..44d547e5c 100644 --- a/app/src/main/java/io/legado/app/utils/ZipUtils.kt +++ b/app/src/main/java/io/legado/app/utils/ZipUtils.kt @@ -57,7 +57,7 @@ object ZipUtils { } return true } finally { - if (zos != null) { + zos?.let { zos.finish() zos.close() } @@ -89,7 +89,7 @@ object ZipUtils { } return true } finally { - if (zos != null) { + zos?.let { zos.finish() zos.close() } @@ -261,7 +261,7 @@ object ZipUtils { val files = ArrayList() val zip = ZipFile(zipFile) val entries = zip.entries() - try { + zip.use { if (isSpace(keyword)) { while (entries.hasMoreElements()) { val entry = entries.nextElement() as ZipEntry @@ -285,8 +285,6 @@ object ZipUtils { } } } - } finally { - zip.close() } return files } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 67bb70836..b8cea9e3b 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -63,6 +63,18 @@ @string/jf_convert_f + + 跟随系统 + 亮色主题 + 暗色主题 + + + + 0 + 1 + 2 + + 自动 黑色 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d481ccdff..43a0e6019 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -433,6 +433,7 @@ 扫描二维码 选中时点击可弹出菜单 主题 + 主题模式 默认主题 恢复主题为默认配色 加入QQ群 @@ -583,5 +584,7 @@ 文字太多,生成二维码失败 分享RSS源 分享书源 + 自动切换夜间模式 + 夜间模式跟随系统 diff --git a/app/src/main/res/xml/pref_config_theme.xml b/app/src/main/res/xml/pref_config_theme.xml index f9f3df963..4988e08bf 100644 --- a/app/src/main/res/xml/pref_config_theme.xml +++ b/app/src/main/res/xml/pref_config_theme.xml @@ -2,7 +2,6 @@ - + +