This commit is contained in:
Horis
2023-02-21 17:21:10 +08:00
parent feea25ba06
commit 806b53e092
33 changed files with 284 additions and 112 deletions
@@ -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")
@@ -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
}
/**
* 搜索字典
*/
@@ -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
}
@@ -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
@@ -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
@@ -40,44 +40,43 @@ class BookSourceAdapter(context: Context, val callBack: CallBack) :
}
}
val diffItemCallback: DiffUtil.ItemCallback<BookSource>
get() = object : DiffUtil.ItemCallback<BookSource>() {
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 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
}
val diffItemCallback = object : DiffUtil.ItemCallback<BookSource>() {
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 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))
}
@@ -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
}
}
@@ -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
}
@@ -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))
}
@@ -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)
}
}
}
@@ -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))
}
}
@@ -141,6 +141,7 @@ class DictRuleAdapter(context: Context, var callBack: CallBack) :
ivDelete.setOnClickListener {
getItem(holder.layoutPosition)?.let {
callBack.delete(it)
selected.remove(it)
}
}
}
@@ -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)
}
}
@@ -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)
@@ -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)
@@ -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
}
@@ -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
}