Merge pull request #2 from gedoor/master

更新
pull/282/head
10bits 4 years ago committed by GitHub
commit b30b9197e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/build.gradle
  2. 5
      app/src/main/assets/updateLog.md
  3. 4
      app/src/main/java/io/legado/app/App.kt
  4. 13
      app/src/main/java/io/legado/app/base/BaseDialogFragment.kt
  5. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  6. 3
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  7. 5
      app/src/main/java/io/legado/app/help/http/HttpHelper.kt
  8. 84
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  9. 11
      app/src/main/java/io/legado/app/lib/dialogs/AlertBuilder.kt
  10. 15
      app/src/main/java/io/legado/app/lib/dialogs/AndroidAlertBuilder.kt
  11. 16
      app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt
  12. 3
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  13. 11
      app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
  14. 14
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
  15. 26
      app/src/main/java/io/legado/app/ui/widget/font/FontAdapter.kt
  16. 87
      app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
  17. 2
      app/src/main/java/io/legado/app/utils/DocumentUtils.kt
  18. 13
      app/src/main/java/io/legado/app/utils/FileUtils.kt
  19. 10
      app/src/main/java/io/legado/app/utils/StringExtensions.kt
  20. 12
      app/src/main/res/layout/view_read_menu.xml
  21. 3
      app/src/main/res/values-zh-rHK/strings.xml
  22. 3
      app/src/main/res/values-zh-rTW/strings.xml
  23. 3
      app/src/main/res/values-zh/strings.xml
  24. 6
      app/src/main/res/values/arrays.xml
  25. 41
      app/src/main/res/values/strings.xml
  26. 7
      app/src/main/res/xml/pref_config_backup.xml

@ -115,6 +115,7 @@ dependencies {
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation "com.android.support:multidex:1.0.3"
//kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

@ -3,6 +3,11 @@
* 请关注公众号[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
* 新公众号[开源阅读]()已启用,[开源阅读软件]()备用
**2020/07/21**
* 优化文字选择,不再缓存
* 添加忽略恢复列表,方便不同手机配置不同
* 其它一些bug修复
**2020/07/19**
* 添加自定义默认封面
* 修复封面选择本地图片时书架不显示的bug

@ -1,6 +1,5 @@
package io.legado.app
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
@ -9,6 +8,7 @@ import android.graphics.Color
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication
import com.jeremyliao.liveeventbus.LiveEventBus
import io.legado.app.constant.AppConst.channelIdDownload
import io.legado.app.constant.AppConst.channelIdReadAloud
@ -28,7 +28,7 @@ import io.legado.app.utils.postEvent
import io.legado.app.utils.putPrefInt
@Suppress("DEPRECATION")
class App : Application() {
class App : MultiDexApplication() {
companion object {
@JvmStatic

@ -3,6 +3,7 @@ package io.legado.app.base
import android.os.Bundle
import android.view.View
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.theme.ThemeStore
import kotlinx.coroutines.CoroutineScope
@ -10,6 +11,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
abstract class BaseDialogFragment : DialogFragment(), CoroutineScope {
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
@ -29,6 +31,17 @@ abstract class BaseDialogFragment : DialogFragment(), CoroutineScope {
abstract fun onFragmentCreated(view: View, savedInstanceState: Bundle?)
override fun show(manager: FragmentManager, tag: String?) {
try {
//在每个add事务前增加一个remove事务,防止连续的add
manager.beginTransaction().remove(this).commit()
super.show(manager, tag)
} catch (e: Exception) {
//同一实例使用不同的tag会异常,这里捕获一下
e.printStackTrace()
}
}
override fun onDestroy() {
super.onDestroy()
job.cancel()

@ -25,6 +25,7 @@ object PreferKey {
const val readBookFont = "readBookFont"
const val fontFolder = "fontFolder"
const val backupPath = "backupUri"
const val restoreIgnore = "restoreIgnore"
const val threadCount = "threadCount"
const val webPort = "webPort"
const val keepLight = "keep_light"

@ -19,8 +19,7 @@ import java.io.File
@Keep
object ReadBookConfig {
const val readConfigFileName = "readConfig.json"
private val configFilePath =
App.INSTANCE.filesDir.absolutePath + File.separator + readConfigFileName
private val configFilePath = FileUtils.getPath(App.INSTANCE.filesDir, readConfigFileName)
val configList: ArrayList<Config> = arrayListOf()
private val defaultConfigs by lazy {
val json = String(App.INSTANCE.assets.open(readConfigFileName).readBytes())

@ -1,7 +1,5 @@
package io.legado.app.help.http
import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import io.legado.app.constant.AppConst
import io.legado.app.help.http.api.HttpGetApi
import io.legado.app.utils.NetworkUtils
@ -19,8 +17,6 @@ object HttpHelper {
val client: OkHttpClient by lazy {
val cookieJar = PersistentCookieJar(SetCookieCache(), CookieStore)
val specs = arrayListOf(
ConnectionSpec.MODERN_TLS,
ConnectionSpec.COMPATIBLE_TLS,
@ -39,7 +35,6 @@ object HttpHelper {
.followSslRedirects(true)
.protocols(listOf(Protocol.HTTP_1_1))
.addInterceptor(getHeaderInterceptor())
.cookieJar(cookieJar)
builder.build()
}

@ -9,6 +9,7 @@ import com.jayway.jsonpath.Option
import com.jayway.jsonpath.ParseContext
import io.legado.app.App
import io.legado.app.BuildConfig
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.*
@ -24,6 +25,42 @@ import org.jetbrains.anko.defaultSharedPreferences
import java.io.File
object Restore {
private val ignoreConfigPath = FileUtils.getPath(App.INSTANCE.filesDir, "restoreIgnore.json")
val ignoreConfig: HashMap<String, Boolean> by lazy {
val file = FileUtils.createFileIfNotExist(ignoreConfigPath)
val json = file.readText()
GSON.fromJsonObject<HashMap<String, Boolean>>(json) ?: hashMapOf()
}
//忽略key
val ignoreKeys = arrayOf(
"readConfig",
PreferKey.themeMode,
PreferKey.bookshelfLayout
)
//忽略标题
val ignoreTitle = arrayOf(
App.INSTANCE.getString(R.string.read_config),
App.INSTANCE.getString(R.string.theme_mode),
App.INSTANCE.getString(R.string.bookshelf_layout)
)
//默认忽略keys
private val ignorePrefKeys = arrayOf(
PreferKey.versionCode,
PreferKey.defaultCover
)
private val readPrefKeys = arrayOf(
PreferKey.readStyleSelect,
PreferKey.shareLayout,
PreferKey.pageAnim,
PreferKey.hideStatusBar,
PreferKey.hideNavigationBar,
PreferKey.bodyIndent,
PreferKey.autoReadSpeed
)
val jsonPath: ParseContext by lazy {
JsonPath.using(
Configuration.builder()
@ -92,24 +129,25 @@ object Restore {
suspend fun restoreConfig(path: String = Backup.backupPath) {
withContext(IO) {
try {
val file =
FileUtils.createFileIfNotExist(path + File.separator + ReadBookConfig.readConfigFileName)
val configFile =
FileUtils.getFile(App.INSTANCE.filesDir, ReadBookConfig.readConfigFileName)
if (file.exists()) {
file.copyTo(configFile, true)
ReadBookConfig.upConfig()
if (!ignoreReadConfig) {
try {
val file =
FileUtils.createFileIfNotExist(path + File.separator + ReadBookConfig.readConfigFileName)
val configFile =
FileUtils.getFile(App.INSTANCE.filesDir, ReadBookConfig.readConfigFileName)
if (file.exists()) {
file.copyTo(configFile, true)
ReadBookConfig.upConfig()
}
} catch (e: Exception) {
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
}
Preferences.getSharedPreferences(App.INSTANCE, path, "config")?.all
?.let { map ->
val ignoreKeys = arrayOf(PreferKey.versionCode, PreferKey.defaultCover)
val edit = App.INSTANCE.defaultSharedPreferences.edit()
map.forEach {
if (!ignoreKeys.contains(it.key)) {
if (keyIsNotIgnore(it.key)) {
when (val value = it.value) {
is Int -> edit.putInt(it.key, value)
is Boolean -> edit.putBoolean(it.key, value)
@ -143,6 +181,28 @@ object Restore {
}
}
private fun keyIsNotIgnore(key: String): Boolean {
return when {
ignorePrefKeys.contains(key) -> false
readPrefKeys.contains(key) && ignoreReadConfig -> false
PreferKey.themeMode == key && ignoreThemeMode -> false
PreferKey.bookshelfLayout == key && ignoreBookshelfLayout -> false
else -> true
}
}
private val ignoreReadConfig: Boolean
get() = ignoreConfig["readConfig"] == true
private val ignoreThemeMode: Boolean
get() = ignoreConfig[PreferKey.themeMode] == true
private val ignoreBookshelfLayout: Boolean
get() = ignoreConfig[PreferKey.bookshelfLayout] == true
fun saveIgnoreConfig() {
val json = GSON.toJson(ignoreConfig)
FileUtils.createFileIfNotExist(ignoreConfigPath).writeText(json)
}
private inline fun <reified T> fileToListT(path: String, fileName: String): List<T>? {
try {
val file = FileUtils.createFileIfNotExist(path + File.separator + fileName)

@ -75,7 +75,16 @@ interface AlertBuilder<out D : DialogInterface> {
fun onKeyPressed(handler: (dialog: DialogInterface, keyCode: Int, e: KeyEvent) -> Boolean)
fun items(items: List<CharSequence>, onItemSelected: (dialog: DialogInterface, index: Int) -> Unit)
fun <T> items(items: List<T>, onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit)
fun <T> items(
items: List<T>,
onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit
)
fun multiChoiceItems(
items: Array<String>,
checkedItems: BooleanArray,
onClick: (dialog: DialogInterface, which: Int, isChecked: Boolean) -> Unit
)
fun build(): D
fun show(): D

@ -105,12 +105,25 @@ internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<Ale
}
}
override fun <T> items(items: List<T>, onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit) {
override fun <T> items(
items: List<T>,
onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit
) {
builder.setItems(Array(items.size) { i -> items[i].toString() }) { dialog, which ->
onItemSelected(dialog, items[which], which)
}
}
override fun multiChoiceItems(
items: Array<String>,
checkedItems: BooleanArray,
onClick: (dialog: DialogInterface, which: Int, isChecked: Boolean) -> Unit
) {
builder.setMultiChoiceItems(items, checkedItems) { dialog, which, isChecked ->
onClick(dialog, which, isChecked)
}
}
override fun build(): AlertDialog = builder.create()
override fun show(): AlertDialog = builder.show()

@ -11,6 +11,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.storage.Restore
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.filechooser.FileChooserDialog
@ -104,6 +106,7 @@ class BackupConfigFragment : PreferenceFragmentCompat(),
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
PreferKey.backupPath -> BackupRestoreUi.selectBackupFolder(this)
PreferKey.restoreIgnore -> restoreIgnore()
"web_dav_backup" -> BackupRestoreUi.backup(this)
"web_dav_restore" -> BackupRestoreUi.restore(this)
"import_old" -> BackupRestoreUi.importOldData(this)
@ -111,6 +114,19 @@ class BackupConfigFragment : PreferenceFragmentCompat(),
return super.onPreferenceTreeClick(preference)
}
private fun restoreIgnore() {
val checkedItems = BooleanArray(Restore.ignoreKeys.size) {
Restore.ignoreConfig[Restore.ignoreKeys[it]] ?: false
}
alert(R.string.restore_ignore) {
multiChoiceItems(Restore.ignoreTitle, checkedItems) { _, which, isChecked ->
Restore.ignoreConfig[Restore.ignoreKeys[which]] = isChecked
}
}.show().setOnDismissListener {
Restore.saveIgnoreConfig()
}
}
override fun onFilePicked(requestCode: Int, currentPath: String) {
BackupRestoreUi.onFilePicked(requestCode, currentPath)
}

@ -64,6 +64,9 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
viewModel.upChapterList()
}, 1000)
}
view_pager_main.postDelayed({
viewModel.postLoad()
}, 3000)
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {

@ -10,6 +10,7 @@ import io.legado.app.data.entities.RssSource
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.Restore
import io.legado.app.model.WebBook
import io.legado.app.utils.FileUtils
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.postEvent
@ -77,4 +78,14 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
}
}
}
fun postLoad() {
execute {
FileUtils.getDirFile(context.cacheDir, "Fonts").let {
if (it.exists()) {
it.delete()
}
}
}
}
}

@ -88,18 +88,18 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
R.id.menu_add_local -> startActivity<ImportBookActivity>()
R.id.menu_add_url -> addBookByUrl()
R.id.menu_arrange_bookshelf -> startActivity<ArrangeBookActivity>(
Pair("groupId", selectedGroup.groupId),
Pair("groupName", selectedGroup.groupName)
Pair("groupId", selectedGroup?.groupId ?: 0),
Pair("groupName", selectedGroup?.groupName ?: 0)
)
R.id.menu_download -> startActivity<DownloadActivity>(
Pair("groupId", selectedGroup.groupId),
Pair("groupName", selectedGroup.groupName)
Pair("groupId", selectedGroup?.groupId ?: 0),
Pair("groupName", selectedGroup?.groupName ?: 0)
)
}
}
private val selectedGroup: BookGroup
get() = bookGroups[view_pager_bookshelf.currentItem]
private val selectedGroup: BookGroup?
get() = bookGroups.getOrNull(view_pager_bookshelf?.currentItem ?: 0)
private fun initView() {
ATH.applyEdgeEffectColor(view_pager_bookshelf)
@ -267,7 +267,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
}
fun gotoTop() {
fragmentMap[selectedGroup.groupId]?.gotoTop()
fragmentMap[selectedGroup?.groupId]?.gotoTop()
}
private inner class TabFragmentPageAdapter internal constructor(fm: FragmentManager) :

@ -2,9 +2,12 @@ package io.legado.app.ui.widget.font
import android.content.Context
import android.graphics.Typeface
import android.os.Build
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.utils.DocItem
import io.legado.app.utils.RealPathUtil
import io.legado.app.utils.invisible
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.item_font.view.*
@ -13,15 +16,28 @@ import org.jetbrains.anko.toast
import java.io.File
class FontAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<File>(context, R.layout.item_font) {
SimpleRecyclerAdapter<DocItem>(context, R.layout.item_font) {
override fun convert(holder: ItemViewHolder, item: File, payloads: MutableList<Any>) {
override fun convert(holder: ItemViewHolder, item: DocItem, payloads: MutableList<Any>) {
with(holder.itemView) {
try {
val typeface = Typeface.createFromFile(item)
val typeface: Typeface? = if (item.isContentPath) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.contentResolver
.openFileDescriptor(item.uri, "r")
?.fileDescriptor?.let {
Typeface.Builder(it).build()
}
} else {
Typeface.createFromFile(RealPathUtil.getPath(context, item.uri))
}
} else {
Typeface.createFromFile(item.uri.toString())
}
tv_font.typeface = typeface
} catch (e: Exception) {
context.toast("读取${item.name}字体失败")
e.printStackTrace()
context.toast("Read ${item.name} Error: ${e.localizedMessage}")
}
tv_font.text = item.name
this.onClick { callBack.onClick(item) }
@ -42,7 +58,7 @@ class FontAdapter(context: Context, val callBack: CallBack) :
}
interface CallBack {
fun onClick(file: File)
fun onClick(docItem: DocItem)
val curFilePath: String
}
}

@ -26,11 +26,11 @@ import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_font_select.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.*
class FontSelectDialog : BaseDialogFragment(),
FileChooserDialog.CallBack,
@ -40,9 +40,6 @@ class FontSelectDialog : BaseDialogFragment(),
private val fontFolder by lazy {
FileUtils.createFolderIfNotExist(App.INSTANCE.filesDir, "Fonts")
}
private val fontCacheFolder by lazy {
FileUtils.createFolderIfNotExist(App.INSTANCE.cacheDir, "Fonts")
}
private var adapter: FontAdapter? = null
override fun onStart() {
@ -90,7 +87,9 @@ class FontSelectDialog : BaseDialogFragment(),
R.id.menu_default -> {
val requireContext = requireContext()
requireContext.alert(titleResource = R.string.system_typeface) {
items(requireContext.resources.getStringArray(R.array.system_typefaces).toList()) { _, i ->
items(
requireContext.resources.getStringArray(R.array.system_typefaces).toList()
) { _, i ->
AppConfig.systemTypefaces = i
onDefaultFontChange()
dismiss()
@ -117,40 +116,16 @@ class FontSelectDialog : BaseDialogFragment(),
@SuppressLint("DefaultLocale")
private fun getFontFiles(doc: DocumentFile) {
execute {
val fontItems = arrayListOf<DocItem>()
val docItems = DocumentUtils.listFiles(App.INSTANCE, doc.uri)
fontCacheFolder.listFiles()?.forEach { fontFile ->
var contain = false
for (item in docItems) {
if (fontFile.name == item.name) {
contain = true
break
}
}
if (!contain) {
fontFile.delete()
}
}
docItems.forEach { item ->
if (item.name.toLowerCase().matches(".*\\.[ot]tf".toRegex())) {
val fontFile = FileUtils.getFile(fontCacheFolder, item.name)
if (!fontFile.exists()) {
DocumentUtils.readBytes(App.INSTANCE, item.uri)?.let { byteArray ->
fontFile.writeBytes(byteArray)
}
}
}
}
try {
fontCacheFolder.listFiles { pathName ->
pathName.name.toLowerCase().matches(".*\\.[ot]tf".toRegex())
}?.let {
withContext(Main) {
adapter?.setItems(it.toList())
}
fontItems.add(item)
}
} catch (e: Exception) {
toast(e.localizedMessage ?: "")
}
fontItems
}.onSuccess {
adapter?.setItems(it)
}.onError {
toast("getFontFiles:${it.localizedMessage}")
}
@ -163,12 +138,22 @@ class FontSelectDialog : BaseDialogFragment(),
.rationale(R.string.tip_perm_request_storage)
.onGranted {
try {
val fontItems = arrayListOf<DocItem>()
val file = File(path)
file.listFiles { pathName ->
pathName.name.toLowerCase().matches(".*\\.[ot]tf".toRegex())
}?.let {
adapter?.setItems(it.toList())
}?.forEach {
fontItems.add(
DocItem(
it.name,
it.extension,
it.length(),
Date(it.lastModified()),
Uri.parse(it.absolutePath)
)
)
}
adapter?.setItems(fontItems)
} catch (e: Exception) {
toast(e.localizedMessage ?: "")
}
@ -176,16 +161,32 @@ class FontSelectDialog : BaseDialogFragment(),
.request()
}
override fun onClick(file: File) {
launch(IO) {
file.copyTo(FileUtils.createFileIfNotExist(fontFolder, file.name), true)
.absolutePath.let { path ->
if (curFilePath != path) {
withContext(Main) {
callBack?.selectFile(path)
override fun onClick(docItem: DocItem) {
execute {
fontFolder.listFiles()?.forEach {
it.delete()
}
if (docItem.isContentPath) {
val file = FileUtils.createFileIfNotExist(fontFolder, docItem.name)
@Suppress("BlockingMethodInNonBlockingContext")
requireActivity().contentResolver.openInputStream(docItem.uri)?.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
callBack?.selectFile(file.path)
} else {
val file = File(docItem.uri.toString())
file.copyTo(FileUtils.createFileIfNotExist(fontFolder, file.name), true)
.absolutePath.let { path ->
if (curFilePath != path) {
withContext(Main) {
callBack?.selectFile(path)
}
}
}
}
}.onSuccess {
dialog?.dismiss()
}
}

@ -138,6 +138,8 @@ data class DocItem(
val isDir: Boolean by lazy {
DocumentsContract.Document.MIME_TYPE_DIR == attr
}
val isContentPath get() = uri.toString().isContentPath()
}
@Throws(Exception::class)

@ -18,7 +18,7 @@ object FileUtils {
}
fun createFolderIfNotExist(root: File, vararg subDirs: String): File {
val filePath = root.absolutePath + File.separator + subDirs.joinToString(File.separator)
val filePath = getPath(root, subDirs = *subDirs)
return createFolderIfNotExist(filePath)
}
@ -60,11 +60,14 @@ object FileUtils {
}
fun getPath(root: File, fileName: String? = null, vararg subDirs: String): String {
return if (fileName.isNullOrEmpty()) {
root.absolutePath + File.separator + subDirs.joinToString(File.separator)
} else {
root.absolutePath + File.separator + subDirs.joinToString(File.separator) + File.separator + fileName
val path = StringBuilder(root.absolutePath)
subDirs.forEach {
path.append(File.separator).append(it)
}
if (!fileName.isNullOrEmpty()) {
path.append(File.separator).append(fileName)
}
return path.toString()
}
//递归删除文件夹下的数据

@ -1,7 +1,8 @@
package io.legado.app.utils
val removeHtmlRegex =
"</?(?:html|head|div|a|p|b|br|hr|h\\d|article|dd|dl|span|link|title)[^>]*>".toRegex()
val removeHtmlRegex = "</?(?:div|p|br|hr|h\\d|article|dd|dl)[^>]*>".toRegex()
val imgRegex = "<img[^>]*>".toRegex()
val notImgHtmlRegex = "</?(?!img)\\w+[^>]*>".toRegex()
fun String?.safeTrim() = if (this.isNullOrBlank()) null else this.trim()
@ -37,7 +38,10 @@ fun String?.isJsonArray(): Boolean =
fun String?.htmlFormat(): String {
this ?: return ""
return this.replace(removeHtmlRegex, "\n")
return this
.replace(imgRegex, "\n$0\n")
.replace(removeHtmlRegex, "\n")
.replace(notImgHtmlRegex, "")
.replace("\\s*\\n+\\s*".toRegex(), "\n  ")
.replace("^[\\n\\s]+".toRegex(), "  ")
.replace("[\\n\\s]+$".toRegex(), "")

@ -222,7 +222,7 @@
<!--目录按钮-->
<LinearLayout
android:id="@+id/ll_catalog"
android:layout_width="50dp"
android:layout_width="60dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
@ -245,6 +245,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:maxLines="1"
android:text="@string/chapter_list"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
@ -257,7 +258,7 @@
<!--调节按钮-->
<LinearLayout
android:id="@+id/ll_read_aloud"
android:layout_width="50dp"
android:layout_width="60dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
@ -280,6 +281,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:maxLines="1"
android:text="@string/read_aloud"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
@ -292,7 +294,7 @@
<!--界面按钮-->
<LinearLayout
android:id="@+id/ll_font"
android:layout_width="50dp"
android:layout_width="60dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
@ -315,6 +317,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:maxLines="1"
android:text="@string/interface_setting"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
@ -327,7 +330,7 @@
<!--设置按钮-->
<LinearLayout
android:id="@+id/ll_setting"
android:layout_width="50dp"
android:layout_width="60dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
@ -350,6 +353,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:maxLines="1"
android:text="@string/setting"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />

@ -721,4 +721,7 @@
<string name="contributors_summary">gedoor,Invinciblelee等,详情请在github中查看</string>
<string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
<string name="default_cover">默认封面</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
</resources>

@ -721,5 +721,8 @@
<string name="contributors_summary">gedoor,Invinciblelee等,详情请在github中查看</string>
<string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
<string name="default_cover">默认封面</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
</resources>

@ -721,4 +721,7 @@
<string name="contributors_summary">gedoor,Invinciblelee等,详情请在github中查看</string>
<string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
<string name="default_cover">默认封面</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
</resources>

@ -42,9 +42,9 @@
<string-array name="theme_mode">
<item>Follow system</item>
<item>Bright theme</item>
<item>Dark theme</item>
<item>E-Ink</item>
<item>Bright mode</item>
<item>Dark mode</item>
<item>E-Ink mode</item>
</string-array>
<string-array name="NavBarColors">

@ -218,7 +218,7 @@
<string name="import_select_book">Import selected book(s)</string>
<string name="threads_num_title">Number of concurrent tasks</string>
<string name="change_icon">Change icon</string>
<string name="remove_from_bookshelf">Remove books</string>
<string name="remove_from_bookshelf">Remove</string>
<string name="start_read">Start reading</string>
<string name="data_loading">Loading…</string>
<string name="load_error_retry">Load failed, tap to retry</string>
@ -332,7 +332,7 @@
<string name="tip_margin_change">Margin adjustment</string>
<string name="allow_update">Allow update</string>
<string name="disable_update">Disable update</string>
<string name="revert_selection">Invert selection</string>
<string name="revert_selection">Inverse</string>
<string name="search_book_key">Search bookName/author</string>
<string name="debug_hint">BookName,Author,URL</string>
<string name="faq">FAQ</string>
@ -586,8 +586,8 @@
<string name="bottom_line">I AM OVER!</string>
<string name="uri_to_path_fail">Uri To Path failed</string>
<string name="refresh_cover">Refresh cover</string>
<string name="change_cover_source">Change cover\'s origin</string>
<string name="select_local_image">Select a local image</string>
<string name="change_cover_source">Change origin</string>
<string name="select_local_image">Local image</string>
<string name="book_type">Type:</string>
<string name="book_type_text">Text</string>
<string name="book_type_audio">Audio</string>
@ -652,7 +652,7 @@
<string name="save_image">Save image</string>
<string name="no_default_path">No default path</string>
<string name="change_group">Group settings</string>
<string name="view_toc">View changes</string>
<string name="view_toc">View Chapters</string>
<string name="bar_elevation">Navigation bar shadow</string>
<string name="bar_elevation_s">Current shadow size(elevation): %s</string>
<string name="btn_default_s">Default</string>
@ -697,13 +697,13 @@
<string name="night_accent">Night,Accent</string>
<string name="night_background_color">Night,Background color</string>
<string name="night_navbar_color">Night,NavBar color</string>
<string name="auto_change_source">Changing source automatically</string>
<string name="auto_change_source">Change source automatically</string>
<string name="text_full_justify">Text justified</string>
<string name="text_bottom_justify">Text align bottom</string>
<string name="auto_page_speed">Auto scroll speed</string>
<string name="sort_by_url">Sort by URL</string>
<string name="backup_summary">The local backup and WebDav backup at the same time</string>
<string name="restore_summary">Restore from WebDAV first, and Restore form local backups if failed</string>
<string name="backup_summary">Backup the local and WebDav simultaneously</string>
<string name="restore_summary">Restore from WebDAV first, and Restore form the local backup if failed</string>
<string name="import_old_summary">Select a legacy backup folder</string>
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
@ -711,16 +711,19 @@
<string name="starting_download">Starting download</string>
<string name="already_in_download">This book is already in Download list</string>
<string name="click_to_open">click to open</string>
<string name="follow_public_account_summary">关注[开源阅读]点击广告支持我</string>
<string name="weChat_appreciation_code">微信赞赏码</string>
<string name="alipay">支付宝</string>
<string name="alipay_red_envelope_search_code">支付宝红包搜索码</string>
<string name="alipay_red_envelope_copy">537954522 点击复制</string>
<string name="alipay_red_envelope_qr_code">支付宝红包二维码</string>
<string name="alipay_payment_qr_code">支付宝收款二维码</string>
<string name="qq_collection_qr_code">QQ收款二维码</string>
<string name="contributors_summary">gedoor,Invinciblelee等,详情请在github中查看</string>
<string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
<string name="default_cover">默认封面</string>
<string name="follow_public_account_summary">Follow [开源阅读] to support me by clicking on ads</string>
<string name="weChat_appreciation_code">WeChat Tipping Code</string>
<string name="alipay">AliPay</string>
<string name="alipay_red_envelope_search_code">AliPay red envelope search code</string>
<string name="alipay_red_envelope_copy">537954522 Click to copy</string>
<string name="alipay_red_envelope_qr_code">AliPay red envelope QR code</string>
<string name="alipay_payment_qr_code">AliPay QR code</string>
<string name="qq_collection_qr_code">QQ Collection QR code</string>
<string name="contributors_summary">gedoor,Invinciblelee etc. Checking in github for details</string>
<string name="clear_cache_summary">Clear the cache of the downloaded books and fonts</string>
<string name="default_cover">Default cover</string>
<string name="restore_ignore">恢复忽略列表</string>
<string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
<string name="read_config">阅读界面设置</string>
</resources>

@ -39,6 +39,7 @@
</io.legado.app.ui.widget.prefs.PreferenceCategory>
<io.legado.app.ui.widget.prefs.PreferenceCategory
android:title="@string/backup_restore"
app:allowDividerBelow="false"
app:iconSpaceReserved="false"
app:layout="@layout/view_preference_category">
@ -61,6 +62,12 @@
android:summary="@string/restore_summary"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="restoreIgnore"
android:title="@string/restore_ignore"
android:summary="@string/restore_ignore_summary"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="import_old"
android:title="@string/menu_import_old_version"

Loading…
Cancel
Save