Horis 3 лет назад
Родитель
Сommit
806b53e092
33 измененных файлов с 281 добавлено и 109 удалено
  1. 1 1
      app/src/main/assets/help/txtTocRuleHelp.md
  2. 1 1
      app/src/main/java/io/legado/app/data/dao/DictRuleDao.kt
  3. 11 0
      app/src/main/java/io/legado/app/data/entities/DictRule.kt
  4. 1 7
      app/src/main/java/io/legado/app/data/entities/TxtTocRule.kt
  5. 1 1
      app/src/main/java/io/legado/app/ui/association/ImportDictRuleDialog.kt
  6. 0 1
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  7. 35 33
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceAdapter.kt
  8. 73 12
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleActivity.kt
  9. 46 1
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleAdapter.kt
  10. 5 5
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleDialog.kt
  11. 16 1
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleViewModel.kt
  12. 11 5
      app/src/main/java/io/legado/app/ui/dict/rule/DictRuleActivity.kt
  13. 1 0
      app/src/main/java/io/legado/app/ui/dict/rule/DictRuleAdapter.kt
  14. 4 8
      app/src/main/java/io/legado/app/ui/dict/rule/DictRuleViewModel.kt
  15. 1 1
      app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
  16. 1 1
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt
  17. 4 1
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt
  18. 4 1
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
  19. 1 4
      app/src/main/res/layout/activity_txt_toc_rule.xml
  20. 1 1
      app/src/main/res/layout/dialog_toc_regex_edit.xml
  21. 1 1
      app/src/main/res/menu/book_read.xml
  22. 1 1
      app/src/main/res/menu/book_toc.xml
  23. 1 1
      app/src/main/res/menu/dict_rule.xml
  24. 16 4
      app/src/main/res/menu/txt_toc_rule.xml
  25. 20 0
      app/src/main/res/menu/txt_toc_rule_sel.xml
  26. 3 2
      app/src/main/res/values-es-rES/strings.xml
  27. 3 2
      app/src/main/res/values-ja-rJP/strings.xml
  28. 3 2
      app/src/main/res/values-pt-rBR/strings.xml
  29. 3 2
      app/src/main/res/values-zh-rHK/strings.xml
  30. 3 2
      app/src/main/res/values-zh-rTW/strings.xml
  31. 3 2
      app/src/main/res/values-zh/strings.xml
  32. 3 2
      app/src/main/res/values/strings.xml
  33. 3 3
      app/src/main/res/xml/pref_main.xml

+ 1 - 1
app/src/main/assets/help/txtTocRegexHelp.md → app/src/main/assets/help/txtTocRuleHelp.md

@@ -4,7 +4,7 @@
 
 - 新增目录规则,当Legado自带的规则不能够满足需求时,用户可根据自己的情况自定义目录规则
 - 导入默认规则 在旧版本中,Legado自带的规则不会随着软件的更新而更新。用户需要使用最新规则或对自带规则修改后需要重置时,可点击 导入默认规则。**注意:导入默认规则不会重置用户自定义的规则,但如果您对自带的规则进行了修改,则修改的规则会被重置为默认规则。**
-- 导入在线规则 为了方便异步调试以及用户导入他人的目录规则,Legado增加目录规则的网络导入功能。点击 网络导入 的输入框,可以通过内置的链接导入在线规则。(在线规则优先比内置的规则更加激进,但适配了更多类型的标题格式,用户需根据自己的情况选择是否导入)
+- 网络导入 为了方便异步调试以及用户导入他人的目录规则,Legado增加目录规则的网络导入功能。点击 网络导入 的输入框,可以通过内置的链接导入在线规则。(在线规则优先比内置的规则更加激进,但适配了更多类型的标题格式,用户需根据自己的情况选择是否导入)
 - 拆分超长章节 根据目录正则分章时若单章超过三万字左右将自动分为多章
 
 ### 操作区

+ 1 - 1
app/src/main/java/io/legado/app/data/dao/DictRuleDao.kt

@@ -14,7 +14,7 @@ interface DictRuleDao {
     @get:Query("select * from dictRules where enabled = 1 order by sortNumber")
     val enabled: List<DictRule>
 
-    @Query("select * from dictRules where enabled = 1 order by sortNumber")
+    @Query("select * from dictRules order by sortNumber")
     fun flowAll(): Flow<List<DictRule>>
 
     @Query("select * from dictRules where name = :name")

+ 11 - 0
app/src/main/java/io/legado/app/data/entities/DictRule.kt

@@ -21,6 +21,17 @@ data class DictRule(
     var sortNumber: Int = 0
 ) {
 
+    override fun hashCode(): Int {
+        return name.hashCode()
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (other is DictRule) {
+            return name == other.name
+        }
+        return false
+    }
+
     /**
      * 搜索字典
      */

+ 1 - 7
app/src/main/java/io/legado/app/data/entities/TxtTocRule.kt

@@ -17,18 +17,12 @@ data class TxtTocRule(
 ) {
 
     override fun hashCode(): Int {
-        return GSON.toJson(this).hashCode()
+        return id.hashCode()
     }
 
     override fun equals(other: Any?): Boolean {
-        other ?: return false
         if (other is TxtTocRule) {
             return id == other.id
-                    && name == other.name
-                    && rule == other.rule
-                    && example == other.example
-                    && serialNumber == other.serialNumber
-                    && enable == other.enable
         }
         return false
     }

+ 1 - 1
app/src/main/java/io/legado/app/ui/association/ImportDictRuleDialog.kt

@@ -51,7 +51,7 @@ class ImportDictRuleDialog() : BaseDialogFragment(R.layout.dialog_recycler_view)
     @SuppressLint("NotifyDataSetChanged")
     override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
         binding.toolBar.setBackgroundColor(primaryColor)
-        binding.toolBar.setTitle(R.string.import_tts)
+        binding.toolBar.setTitle(R.string.import_dict_rule)
         binding.rotateLoading.visible()
         binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
         binding.recyclerView.adapter = adapter

+ 0 - 1
app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt

@@ -30,7 +30,6 @@ import io.legado.app.ui.book.search.SearchActivity
 import io.legado.app.ui.book.search.SearchScope
 import io.legado.app.ui.book.source.debug.BookSourceDebugActivity
 import io.legado.app.ui.book.source.edit.BookSourceEditActivity
-import io.legado.app.ui.book.toc.rule.TxtTocRuleActivity
 import io.legado.app.ui.config.CheckSourceConfig
 import io.legado.app.ui.document.HandleFileContract
 import io.legado.app.ui.qrcode.QrCodeResult

+ 35 - 33
app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceAdapter.kt

@@ -40,44 +40,43 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
             }
         }
 
-    val diffItemCallback: DiffUtil.ItemCallback<BookSource>
-        get() = object : DiffUtil.ItemCallback<BookSource>() {
+    val diffItemCallback = object : DiffUtil.ItemCallback<BookSource>() {
 
-            override fun areItemsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
-                return oldItem.bookSourceUrl == newItem.bookSourceUrl
-            }
+        override fun areItemsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
+            return oldItem.bookSourceUrl == newItem.bookSourceUrl
+        }
 
-            override fun areContentsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
-                return oldItem.bookSourceName == newItem.bookSourceName
-                        && oldItem.bookSourceGroup == newItem.bookSourceGroup
-                        && oldItem.enabled == newItem.enabled
-                        && oldItem.enabledExplore == newItem.enabledExplore
-                        && oldItem.exploreUrl == newItem.exploreUrl
-            }
+        override fun areContentsTheSame(oldItem: BookSource, newItem: BookSource): Boolean {
+            return oldItem.bookSourceName == newItem.bookSourceName
+                    && oldItem.bookSourceGroup == newItem.bookSourceGroup
+                    && oldItem.enabled == newItem.enabled
+                    && oldItem.enabledExplore == newItem.enabledExplore
+                    && oldItem.exploreUrl == newItem.exploreUrl
+        }
 
-            override fun getChangePayload(oldItem: BookSource, newItem: BookSource): Any? {
-                val payload = Bundle()
-                if (oldItem.bookSourceName != newItem.bookSourceName
-                    || oldItem.bookSourceGroup != newItem.bookSourceGroup
-                ) {
-                    payload.putBoolean("upName", true)
-                }
-                if (oldItem.enabled != newItem.enabled) {
-                    payload.putBoolean("enabled", newItem.enabled)
-                }
-                if (oldItem.enabledExplore != newItem.enabledExplore ||
-                    oldItem.exploreUrl != newItem.exploreUrl
-                ) {
-                    payload.putBoolean("upExplore", true)
-                }
-                if (payload.isEmpty) {
-                    return null
-                }
-                return payload
+        override fun getChangePayload(oldItem: BookSource, newItem: BookSource): Any? {
+            val payload = Bundle()
+            if (oldItem.bookSourceName != newItem.bookSourceName
+                || oldItem.bookSourceGroup != newItem.bookSourceGroup
+            ) {
+                payload.putBoolean("upName", true)
             }
-
+            if (oldItem.enabled != newItem.enabled) {
+                payload.putBoolean("enabled", newItem.enabled)
+            }
+            if (oldItem.enabledExplore != newItem.enabledExplore ||
+                oldItem.exploreUrl != newItem.exploreUrl
+            ) {
+                payload.putBoolean("upExplore", true)
+            }
+            if (payload.isEmpty) {
+                return null
+            }
+            return payload
         }
 
+    }
+
     override fun getViewBinding(parent: ViewGroup): ItemBookSourceBinding {
         return ItemBookSourceBinding.inflate(inflater, parent, false)
     }
@@ -190,7 +189,10 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
                 }
                 R.id.menu_search -> callBack.searchBook(source)
                 R.id.menu_debug_source -> callBack.debug(source)
-                R.id.menu_del -> callBack.del(source)
+                R.id.menu_del -> {
+                    callBack.del(source)
+                    selected.remove(source)
+                }
                 R.id.menu_enable_explore -> {
                     callBack.update(source.copy(enabledExplore = !source.enabledExplore))
                 }

+ 73 - 12
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleActivity.kt

@@ -5,6 +5,7 @@ import android.os.Bundle
 import android.view.Menu
 import android.view.MenuItem
 import androidx.activity.viewModels
+import androidx.appcompat.widget.PopupMenu
 import androidx.recyclerview.widget.ItemTouchHelper
 import io.legado.app.R
 import io.legado.app.base.VMBaseActivity
@@ -12,8 +13,13 @@ import io.legado.app.data.appDb
 import io.legado.app.data.entities.TxtTocRule
 import io.legado.app.databinding.ActivityTxtTocRuleBinding
 import io.legado.app.databinding.DialogEditTextBinding
+import io.legado.app.help.DirectLinkUpload
 import io.legado.app.lib.dialogs.alert
 import io.legado.app.lib.theme.primaryColor
+import io.legado.app.ui.association.ImportDictRuleDialog
+import io.legado.app.ui.association.ImportTxtTocRuleDialog
+import io.legado.app.ui.document.HandleFileContract
+import io.legado.app.ui.qrcode.QrCodeResult
 import io.legado.app.ui.widget.SelectActionBar
 import io.legado.app.ui.widget.dialog.TextDialog
 import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
@@ -27,7 +33,8 @@ import kotlinx.coroutines.launch
 class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleViewModel>(),
     TxtTocRuleAdapter.CallBack,
     SelectActionBar.CallBack,
-    TxtTocRuleEditDialog.Callback {
+    TxtTocRuleEditDialog.Callback,
+    PopupMenu.OnMenuItemClickListener {
 
     override val viewModel: TxtTocRuleViewModel by viewModels()
     override val binding: ActivityTxtTocRuleBinding by viewBinding(ActivityTxtTocRuleBinding::inflate)
@@ -35,6 +42,38 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
         TxtTocRuleAdapter(this, this)
     }
     private val importTocRuleKey = "tocRuleUrl"
+    private val qrCodeResult = registerForActivityResult(QrCodeResult()) {
+        it ?: return@registerForActivityResult
+        showDialogFragment(ImportTxtTocRuleDialog(it))
+    }
+    private val importDoc = registerForActivityResult(HandleFileContract()) {
+        kotlin.runCatching {
+            it.uri?.readText(this)?.let {
+                showDialogFragment(ImportTxtTocRuleDialog(it))
+            }
+        }.onFailure {
+            toastOnUi("readTextError:${it.localizedMessage}")
+        }
+    }
+    private val exportResult = registerForActivityResult(HandleFileContract()) {
+        it.uri?.let { uri ->
+            alert(R.string.export_success) {
+                if (uri.toString().isAbsUrl()) {
+                    DirectLinkUpload.getSummary()?.let { summary ->
+                        setMessage(summary)
+                    }
+                }
+                val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
+                    editView.hint = getString(R.string.path)
+                    editView.setText(uri.toString())
+                }
+                customView { alertBinding.root }
+                okButton {
+                    sendToClip(uri.toString())
+                }
+            }
+        }
+    }
 
     override fun onActivityCreated(savedInstanceState: Bundle?) {
         initView()
@@ -59,20 +98,22 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
 
     private fun initBottomActionBar() {
         binding.selectActionBar.setMainActionText(R.string.delete)
+        binding.selectActionBar.inflateMenu(R.menu.txt_toc_rule_sel)
+        binding.selectActionBar.setOnMenuItemClickListener(this)
         binding.selectActionBar.setCallBack(this)
     }
 
     private fun initData() {
         launch {
             appDb.txtTocRuleDao.observeAll().conflate().collect { tocRules ->
-                adapter.setItems(tocRules)
+                adapter.setItems(tocRules, adapter.diffItemCallBack)
                 upCountView()
             }
         }
     }
 
     override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
-        menuInflater.inflate(R.menu.txt_toc_regex, menu)
+        menuInflater.inflate(R.menu.txt_toc_rule, menu)
         return super.onCompatCreateOptionsMenu(menu)
     }
 
@@ -84,9 +125,15 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
     override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
             R.id.menu_add -> showDialogFragment(TxtTocRuleEditDialog())
-            R.id.menu_default -> viewModel.importDefault()
-            R.id.menu_import -> showImportDialog()
-            R.id.menu_help -> showTxtTocRegexHelp()
+            R.id.menu_import_local -> importDoc.launch {
+                mode = HandleFileContract.FILE
+                allowExtensions = arrayOf("txt", "json")
+            }
+            R.id.menu_import_onLine -> showImportDialog()
+            R.id.menu_import_qr -> qrCodeResult.launch()
+            R.id.menu_import_default -> viewModel.importDefault()
+            R.id.menu_help -> showTxtTocRuleHelp()
+
         }
         return super.onCompatOptionsItemSelected(item)
     }
@@ -182,18 +229,32 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
                         cacheUrls.add(0, it)
                         aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
                     }
-                    viewModel.importOnLine(it) { msg ->
-                        toastOnUi(msg)
-                    }
+                    showDialogFragment(ImportTxtTocRuleDialog(it))
                 }
             }
             cancelButton()
         }
     }
 
-    private fun showTxtTocRegexHelp() {
-        val text = String(assets.open("help/txtTocRegexHelp.md").readBytes())
+    private fun showTxtTocRuleHelp() {
+        val text = String(assets.open("help/txtTocRuleHelp.md").readBytes())
         showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
     }
 
-}
+    override fun onMenuItemClick(item: MenuItem): Boolean {
+        when (item.itemId) {
+            R.id.menu_enable_selection -> viewModel.enableSelection(*adapter.selection.toTypedArray())
+            R.id.menu_disable_selection -> viewModel.disableSelection(*adapter.selection.toTypedArray())
+            R.id.menu_export_selection -> exportResult.launch {
+                mode = HandleFileContract.EXPORT
+                fileData = HandleFileContract.FileData(
+                    "exportTxtTocRule.json",
+                    GSON.toJson(adapter.selection).toByteArray(),
+                    "application/json"
+                )
+            }
+        }
+        return true
+    }
+
+}

+ 46 - 1
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleAdapter.kt

@@ -6,10 +6,12 @@ import android.view.View
 import android.view.ViewGroup
 import android.widget.PopupMenu
 import androidx.core.os.bundleOf
+import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.RecyclerView
 import io.legado.app.R
 import io.legado.app.base.adapter.ItemViewHolder
 import io.legado.app.base.adapter.RecyclerAdapter
+import io.legado.app.data.entities.ReplaceRule
 import io.legado.app.data.entities.TxtTocRule
 import io.legado.app.databinding.ItemTxtTocRuleBinding
 import io.legado.app.lib.theme.backgroundColor
@@ -28,6 +30,43 @@ class TxtTocRuleAdapter(context: Context, private val callBack: CallBack) :
             selected.contains(it)
         }
 
+    val diffItemCallBack = object : DiffUtil.ItemCallback<TxtTocRule>() {
+
+        override fun areItemsTheSame(oldItem: TxtTocRule, newItem: TxtTocRule): Boolean {
+            return oldItem.id == newItem.id
+        }
+
+        override fun areContentsTheSame(oldItem: TxtTocRule, newItem: TxtTocRule): Boolean {
+            if (oldItem.name != newItem.name) {
+                return false
+            }
+            if (oldItem.enable != newItem.enable) {
+                return false
+            }
+            if (oldItem.example != newItem.example) {
+                return false
+            }
+            return true
+        }
+
+        override fun getChangePayload(oldItem: TxtTocRule, newItem: TxtTocRule): Any? {
+            val payload = Bundle()
+            if (oldItem.name != newItem.name) {
+                payload.putBoolean("upName", true)
+            }
+            if (oldItem.enable != newItem.enable) {
+                payload.putBoolean("enabled", newItem.enable)
+            }
+            if (oldItem.example != newItem.example) {
+                payload.putBoolean("upExample", true)
+            }
+            if (payload.isEmpty) {
+                return null
+            }
+            return payload
+        }
+    }
+
     override fun getViewBinding(parent: ViewGroup): ItemTxtTocRuleBinding {
         return ItemTxtTocRuleBinding.inflate(inflater, parent, false)
     }
@@ -50,6 +89,9 @@ class TxtTocRuleAdapter(context: Context, private val callBack: CallBack) :
                 bundle.keySet().map {
                     when (it) {
                         "selected" -> cbSource.isChecked = selected.contains(item)
+                        "upNmae" -> cbSource.text = item.name
+                        "upExample" -> titleExample.text = item.example
+                        "enabled" -> swtEnabled.isChecked = item.enable
                     }
                 }
             }
@@ -95,7 +137,10 @@ class TxtTocRuleAdapter(context: Context, private val callBack: CallBack) :
             when (menuItem.itemId) {
                 R.id.menu_top -> callBack.toTop(source)
                 R.id.menu_bottom -> callBack.toBottom(source)
-                R.id.menu_del -> callBack.del(source)
+                R.id.menu_del ->  {
+                    callBack.del(source)
+                    selected.remove(source)
+                }
             }
             true
         }

+ 5 - 5
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleDialog.kt

@@ -61,8 +61,8 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
     override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
         binding.toolBar.setBackgroundColor(primaryColor)
         durRegex = arguments?.getString("tocRegex")
-        binding.toolBar.setTitle(R.string.txt_toc_regex)
-        binding.toolBar.inflateMenu(R.menu.txt_toc_regex)
+        binding.toolBar.setTitle(R.string.txt_toc_rule)
+        binding.toolBar.inflateMenu(R.menu.txt_toc_rule)
         binding.toolBar.menu.applyTint(requireContext())
         binding.toolBar.menu.findItem(R.id.menu_split_long_chapter)
             ?.isChecked = ReadBook.book?.getSplitLongChapter() == true
@@ -125,7 +125,7 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
                 item.isChecked = !item.isChecked
                 if (!item.isChecked) context?.longToastOnUi(R.string.need_more_time_load_content)
             }
-            R.id.menu_help -> showTxtTocRegexHelp()
+            R.id.menu_help -> showTxtTocRuleHelp()
         }
         return false
     }
@@ -170,8 +170,8 @@ class TxtTocRuleDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
         }
     }
 
-    private fun showTxtTocRegexHelp() {
-        val text = String(requireContext().assets.open("help/txtTocRegexHelp.md").readBytes())
+    private fun showTxtTocRuleHelp() {
+        val text = String(requireContext().assets.open("help/txtTocRuleHelp.md").readBytes())
         showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
     }
 

+ 16 - 1
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtTocRuleViewModel.kt

@@ -3,6 +3,7 @@ package io.legado.app.ui.book.toc.rule
 import android.app.Application
 import io.legado.app.base.BaseViewModel
 import io.legado.app.data.appDb
+import io.legado.app.data.entities.DictRule
 import io.legado.app.data.entities.TxtTocRule
 import io.legado.app.help.DefaultData
 import io.legado.app.help.http.newCallResponseBody
@@ -83,4 +84,18 @@ class TxtTocRuleViewModel(app: Application) : BaseViewModel(app) {
         }
     }
 
-}
+    fun enableSelection(vararg txtTocRule: TxtTocRule) {
+        execute {
+            val array = txtTocRule.map { it.copy(enable = true) }.toTypedArray()
+            appDb.txtTocRuleDao.insert(*array)
+        }
+    }
+
+    fun disableSelection(vararg txtTocRule: TxtTocRule) {
+        execute {
+            val array = txtTocRule.map { it.copy(enable = false) }.toTypedArray()
+            appDb.txtTocRuleDao.insert(*array)
+        }
+    }
+
+}

+ 11 - 5
app/src/main/java/io/legado/app/ui/dict/rule/DictRuleActivity.kt

@@ -21,6 +21,7 @@ import io.legado.app.ui.association.ImportDictRuleDialog
 import io.legado.app.ui.document.HandleFileContract
 import io.legado.app.ui.qrcode.QrCodeResult
 import io.legado.app.ui.widget.SelectActionBar
+import io.legado.app.ui.widget.dialog.TextDialog
 import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
 import io.legado.app.ui.widget.recycler.ItemTouchCallback
 import io.legado.app.ui.widget.recycler.VerticalDivider
@@ -120,15 +121,15 @@ class DictRuleActivity : VMBaseActivity<ActivityDictRuleBinding, DictRuleViewMod
 
     override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
-            R.id.menu_create -> showDialogFragment<DictRuleEditDialog>()
-            R.id.menu_import_default -> viewModel.importDefault()
+            R.id.menu_add -> showDialogFragment<DictRuleEditDialog>()
             R.id.menu_import_local -> importDoc.launch {
                 mode = HandleFileContract.FILE
                 allowExtensions = arrayOf("txt", "json")
             }
             R.id.menu_import_onLine -> showImportDialog()
             R.id.menu_import_qr -> qrCodeResult.launch()
-            R.id.menu_help -> {}
+            R.id.menu_import_default -> viewModel.importDefault()
+            R.id.menu_help -> showDictRuleHelp()
         }
         return super.onCompatOptionsItemSelected(item)
     }
@@ -195,7 +196,7 @@ class DictRuleActivity : VMBaseActivity<ActivityDictRuleBinding, DictRuleViewMod
             .getAsString(importRecordKey)
             ?.splitNotBlank(",")
             ?.toMutableList() ?: mutableListOf()
-        alert(titleResource = R.string.import_replace_rule_on_line) {
+        alert(titleResource = R.string.import_on_line) {
             val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
                 editView.hint = "url"
                 editView.setFilterValues(cacheUrls)
@@ -220,4 +221,9 @@ class DictRuleActivity : VMBaseActivity<ActivityDictRuleBinding, DictRuleViewMod
             cancelButton()
         }
     }
-}
+
+    private fun showDictRuleHelp() {
+        val text = String(assets.open("help/dictRuleHelp.md").readBytes())
+        showDialogFragment(TextDialog(getString(R.string.help), text, TextDialog.Mode.MD))
+    }
+}

+ 1 - 0
app/src/main/java/io/legado/app/ui/dict/rule/DictRuleAdapter.kt

@@ -141,6 +141,7 @@ class DictRuleAdapter(context: Context, var callBack: CallBack) :
             ivDelete.setOnClickListener {
                 getItem(holder.layoutPosition)?.let {
                     callBack.delete(it)
+                    selected.remove(it)
                 }
             }
         }

+ 4 - 8
app/src/main/java/io/legado/app/ui/dict/rule/DictRuleViewModel.kt

@@ -41,19 +41,15 @@ class DictRuleViewModel(application: Application) : BaseViewModel(application) {
 
     fun enableSelection(vararg dictRule: DictRule) {
         execute {
-            dictRule.forEach {
-                it.enabled = true
-            }
-            appDb.dictRuleDao.insert(*dictRule)
+            val array = dictRule.map { it.copy(enabled = true) }.toTypedArray()
+            appDb.dictRuleDao.insert(*array)
         }
     }
 
     fun disableSelection(vararg dictRule: DictRule) {
         execute {
-            dictRule.forEach {
-                it.enabled = false
-            }
-            appDb.dictRuleDao.insert(*dictRule)
+            val array = dictRule.map { it.copy(enabled = false) }.toTypedArray()
+            appDb.dictRuleDao.insert(*array)
         }
     }
 

+ 1 - 1
app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt

@@ -139,7 +139,7 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config) {
                 "bookSourceManage" -> startActivity<BookSourceActivity>()
                 "replaceManage" -> startActivity<ReplaceRuleActivity>()
                 "dictRuleManage" -> startActivity<DictRuleActivity>()
-                "txtTocRegexManage" -> startActivity<TxtTocRuleActivity>()
+                "txtTocRuleManage" -> startActivity<TxtTocRuleActivity>()
                 "bookmark" -> startActivity<AllBookmarkActivity>()
                 "setting" -> startActivity<ConfigActivity> {
                     putExtra("configTag", ConfigTag.OTHER_CONFIG)

+ 1 - 1
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt

@@ -270,7 +270,7 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
             .getAsString(importRecordKey)
             ?.splitNotBlank(",")
             ?.toMutableList() ?: mutableListOf()
-        alert(titleResource = R.string.import_replace_rule_on_line) {
+        alert(titleResource = R.string.import_on_line) {
             val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
                 editView.hint = "url"
                 editView.setFilterValues(cacheUrls)

+ 4 - 1
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt

@@ -160,7 +160,10 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) :
             when (menuItem.itemId) {
                 R.id.menu_top -> callBack.toTop(item)
                 R.id.menu_bottom -> callBack.toBottom(item)
-                R.id.menu_del -> callBack.delete(item)
+                R.id.menu_del -> {
+                    callBack.delete(item)
+                    selected.remove(item)
+                }
             }
             true
         }

+ 4 - 1
app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt

@@ -179,7 +179,10 @@ class RssSourceAdapter(context: Context, val callBack: CallBack) :
             when (menuItem.itemId) {
                 R.id.menu_top -> callBack.toTop(source)
                 R.id.menu_bottom -> callBack.toBottom(source)
-                R.id.menu_del -> callBack.del(source)
+                R.id.menu_del -> {
+                    callBack.del(source)
+                    selected.remove(source)
+                }
             }
             true
         }

+ 1 - 4
app/src/main/res/layout/activity_txt_toc_rule.xml

@@ -9,10 +9,7 @@
         android:id="@+id/title_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        app:contentInsetStartWithNavigation="0dp"
-        app:contentLayout="@layout/view_search"
-        app:displayHomeAsUp="true"
-        app:title="@string/txt_toc_regex" />
+        app:title="@string/txt_toc_rule" />
 
     <FrameLayout
         android:layout_width="match_parent"

+ 1 - 1
app/src/main/res/layout/dialog_toc_regex_edit.xml

@@ -24,7 +24,7 @@
             app:displayHomeAsUp="false"
             app:fitStatusBar="false"
             app:popupTheme="@style/AppTheme.PopupOverlay"
-            app:title="@string/txt_toc_regex"
+            app:title="@string/txt_toc_rule"
             app:titleTextAppearance="@style/ToolbarTitle" />
 
         <androidx.core.widget.NestedScrollView

+ 1 - 1
app/src/main/res/menu/book_read.xml

@@ -29,7 +29,7 @@
         <item
             android:id="@+id/menu_toc_regex"
             android:icon="@drawable/ic_exchange"
-            android:title="@string/txt_toc_regex"
+            android:title="@string/txt_toc_rule"
             android:visible="false"
             app:showAsAction="always" />
     </group>

+ 1 - 1
app/src/main/res/menu/book_toc.xml

@@ -16,7 +16,7 @@
 
         <item
             android:id="@+id/menu_toc_regex"
-            android:title="@string/txt_toc_regex"
+            android:title="@string/txt_toc_rule"
             app:showAsAction="never" />
 
     </group>

+ 1 - 1
app/src/main/res/menu/dict_rule.xml

@@ -5,7 +5,7 @@
     tools:ignore="AlwaysShowAction">
 
     <item
-        android:id="@+id/menu_create"
+        android:id="@+id/menu_add"
         android:icon="@drawable/ic_add"
         android:title="@string/create"
         app:showAsAction="always" />

+ 16 - 4
app/src/main/res/menu/txt_toc_regex.xml → app/src/main/res/menu/txt_toc_rule.xml

@@ -11,14 +11,26 @@
         tools:ignore="AlwaysShowAction" />
 
     <item
-        android:id="@+id/menu_default"
-        android:title="@string/import_default_rule"
+        android:id="@+id/menu_import_local"
+        android:icon="@drawable/ic_import"
+        android:title="@string/import_local"
+        app:showAsAction="never" />
+
+    <item
+        android:id="@+id/menu_import_onLine"
+        android:title="@string/import_on_line"
         android:icon="@drawable/ic_import"
         app:showAsAction="never" />
 
     <item
-        android:id="@+id/menu_import"
-        android:title="@string/import_replace_rule_on_line"
+        android:id="@+id/menu_import_qr"
+        android:icon="@drawable/ic_import"
+        android:title="@string/import_by_qr_code"
+        app:showAsAction="never" />
+
+    <item
+        android:id="@+id/menu_import_default"
+        android:title="@string/import_default_rule"
         android:icon="@drawable/ic_import"
         app:showAsAction="never" />
 

+ 20 - 0
app/src/main/res/menu/txt_toc_rule_sel.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_enable_selection"
+        android:title="@string/enable_selection"
+        app:showAsAction="never" />
+
+    <item
+        android:id="@+id/menu_disable_selection"
+        android:title="@string/disable_selection"
+        app:showAsAction="never" />
+
+    <item
+        android:id="@+id/menu_export_selection"
+        android:title="@string/export_selection"
+        app:showAsAction="never" />
+
+</menu>

+ 3 - 2
app/src/main/res/values-es-rES/strings.xml

@@ -342,7 +342,7 @@
     <string name="pt_show_all_find">Mostrar todos los buscados</string>
     <string name="ps_show_all_find">Mostrar la fuente de búsqueda si Descubrir fue cerrado</string>
     <string name="update_toc">Actualizar capítulos</string>
-    <string name="txt_toc_regex">Capítulos de Txt Regex</string>
+    <string name="txt_toc_rule">Capítulos de Txt Rule</string>
     <string name="set_charset">Codificación de texto</string>
     <string name="swap_sort">Orden ascendente/descendente</string>
     <string name="sort">Ordenar</string>
@@ -1077,5 +1077,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 2
app/src/main/res/values-ja-rJP/strings.xml

@@ -346,7 +346,7 @@
     <string name="pt_show_all_find">Display all Discovery</string>
     <string name="ps_show_all_find">Display the selected origin\'s Discovery if closed</string>
     <string name="update_toc">Update chapters</string>
-    <string name="txt_toc_regex">Txt Chapters Regex</string>
+    <string name="txt_toc_rule">Txt Chapters Rule</string>
     <string name="set_charset">Text encoding</string>
     <string name="swap_sort">Ascending/Descending order</string>
     <string name="sort">Sort</string>
@@ -1080,5 +1080,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 2
app/src/main/res/values-pt-rBR/strings.xml

@@ -344,7 +344,7 @@
     <string name="pt_show_all_find">Mostrar todas as Descobertas</string>
     <string name="ps_show_all_find">Mostrar a fonte da Descoberta selecionada se encerrado</string>
     <string name="update_toc">Atualizar os capítulos</string>
-    <string name="txt_toc_regex">Capítulos de Txt Regex</string>
+    <string name="txt_toc_rule">Capítulos de Txt Rule</string>
     <string name="set_charset">Codificação de texto</string>
     <string name="swap_sort">Ordem ascendente/descendente</string>
     <string name="sort">Ordenar</string>
@@ -1080,5 +1080,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 2
app/src/main/res/values-zh-rHK/strings.xml

@@ -343,7 +343,7 @@
     <string name="pt_show_all_find">顯示所有發現</string>
     <string name="ps_show_all_find">關閉則只顯示勾選源的發現</string>
     <string name="update_toc">更新目錄</string>
-    <string name="txt_toc_regex">TXT目錄正則</string>
+    <string name="txt_toc_rule">TXT 目錄規則</string>
     <string name="set_charset">設置編碼</string>
     <string name="swap_sort">倒序-順序</string>
     <string name="sort">排序</string>
@@ -1077,5 +1077,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 2
app/src/main/res/values-zh-rTW/strings.xml

@@ -345,7 +345,7 @@
     <string name="pt_show_all_find">顯示所有發現</string>
     <string name="ps_show_all_find">關閉則只顯示勾選源的發現</string>
     <string name="update_toc">更新目錄</string>
-    <string name="txt_toc_regex">TXT目錄正則</string>
+    <string name="txt_toc_rule">TXT 目錄規則</string>
     <string name="set_charset">設定編碼</string>
     <string name="swap_sort">倒序-順序</string>
     <string name="sort">排序</string>
@@ -1079,5 +1079,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 2
app/src/main/res/values-zh/strings.xml

@@ -345,7 +345,7 @@
     <string name="pt_show_all_find">显示所有发现</string>
     <string name="ps_show_all_find">关闭则只显示勾选源的发现</string>
     <string name="update_toc">更新目录</string>
-    <string name="txt_toc_regex">TXT 目录正则</string>
+    <string name="txt_toc_rule">TXT 目录规则</string>
     <string name="set_charset">设置编码</string>
     <string name="swap_sort">倒序-顺序</string>
     <string name="sort">排序</string>
@@ -1079,5 +1079,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 2
app/src/main/res/values/strings.xml

@@ -346,7 +346,7 @@
     <string name="pt_show_all_find">Display all Discovery</string>
     <string name="ps_show_all_find">Display the selected origin\'s Discovery if closed</string>
     <string name="update_toc">Update chapters</string>
-    <string name="txt_toc_regex">Txt Chapters Regex</string>
+    <string name="txt_toc_rule">Txt Chapters Rule</string>
     <string name="set_charset">Text encoding</string>
     <string name="swap_sort">Ascending/Descending order</string>
     <string name="sort">Sort</string>
@@ -1080,5 +1080,6 @@
     <string name="create">新建</string>
     <string name="url_rule">url规则(urlRule)</string>
     <string name="show_rule">显示规则(showRule)</string>
-    <string name="config_txt_toc_regex">配置 TXT 目录正则</string>
+    <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
+    <string name="import_dict_rule">导入字典规则</string>
 </resources>

+ 3 - 3
app/src/main/res/xml/pref_main.xml

@@ -13,9 +13,9 @@
         app:iconSpaceReserved="false" />
 
     <io.legado.app.lib.prefs.Preference
-        android:key="txtTocRegexManage"
-        android:title="@string/txt_toc_regex"
-        android:summary="@string/config_txt_toc_regex"
+        android:key="txtTocRuleManage"
+        android:title="@string/txt_toc_rule"
+        android:summary="@string/config_txt_toc_rule"
         android:icon="@drawable/ic_cfg_source"
         app:iconSpaceReserved="false" />