From 963cc9062169f038f1de72cc99150fac54f2f5fa Mon Sep 17 00:00:00 2001 From: HapeLee <1321903405@qq.com> Date: Mon, 3 Nov 2025 20:49:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E8=A7=84=E5=88=99=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/replace/ReplaceRuleActivity.kt | 2 - .../app/ui/replace/ReplaceRuleAdapter.kt | 59 +++++++---- .../app/ui/replace/ReplaceRuleViewModel.kt | 98 ++++++++++++------- .../ui/replace/edit/ReplaceEditActivity.kt | 10 +- .../main/res/layout/activity_replace_edit.xml | 39 +++++--- app/src/main/res/layout/item_replace_rule.xml | 13 ++- 6 files changed, 145 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt index 0f1175041..7bb16a08a 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt @@ -72,8 +72,6 @@ class ReplaceRuleActivity : VMBaseActivity() private var groupMenu: SubMenu? = null - - private var sortMode = "desc" private var searchKey: String? = null private val qrCodeResult = registerForActivityResult(QrCodeResult()) { it ?: return@registerForActivityResult diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt index c4a689d4b..460951298 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt @@ -16,6 +16,10 @@ import io.legado.app.databinding.ItemReplaceRuleBinding //import io.legado.app.lib.theme.backgroundColor import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.ItemTouchCallback +import io.legado.app.utils.gone +import io.legado.app.utils.themeColor +import io.legado.app.utils.visible +import splitties.views.backgroundColor class ReplaceRuleAdapter(context: Context, var callBack: CallBack) : @@ -38,28 +42,24 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) : } override fun areContentsTheSame(oldItem: ReplaceRule, newItem: ReplaceRule): Boolean { - if (oldItem.name != newItem.name) { - return false - } - if (oldItem.group != newItem.group) { - return false - } - if (oldItem.isEnabled != newItem.isEnabled) { - return false - } + if (oldItem.name != newItem.name) return false + if (oldItem.group != newItem.group) return false + if (oldItem.isEnabled != newItem.isEnabled) return false + if (oldItem.order != newItem.order) return false // 添加 order 检查 return true } override fun getChangePayload(oldItem: ReplaceRule, newItem: ReplaceRule): Any? { val payload = Bundle() - if (oldItem.name != newItem.name - || oldItem.group != newItem.group - ) { + if (oldItem.name != newItem.name || oldItem.group != newItem.group) { payload.putBoolean("upName", true) } if (oldItem.isEnabled != newItem.isEnabled) { payload.putBoolean("enabled", newItem.isEnabled) } + if (oldItem.order != newItem.order) { + payload.putBoolean("orderChanged", true) + } if (payload.isEmpty) { return null } @@ -102,12 +102,9 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) : payloads: MutableList ) { binding.run { - if (payloads.isEmpty()) { - //root.setBackgroundColor(ColorUtils.withAlpha(context.backgroundColor, 0.5f)) - cbName.text = item.getDisplayNameGroup() - swtEnabled.isChecked = item.isEnabled - cbName.isChecked = selected.contains(item) - } else { + var needUpdatePin = payloads.isEmpty() + + if (payloads.isNotEmpty()) { for (i in payloads.indices) { val bundle = payloads[i] as Bundle bundle.keySet().forEach { @@ -115,10 +112,35 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) : "selected" -> cbName.isChecked = selected.contains(item) "upName" -> cbName.text = item.getDisplayNameGroup() "enabled" -> swtEnabled.isChecked = item.isEnabled + "orderChanged" -> needUpdatePin = true } } } } + + if (payloads.isEmpty() || needUpdatePin) { + when (item.order) { + -1 -> { + ivPin.visible() + ivPin.setImageResource(R.drawable.ic_praise_filled) + ivPin.rotation = 0f + ivPin.setColorFilter(context.themeColor(com.google.android.material.R.attr.colorSecondary)) + } + -2 -> { + ivPin.visible() + ivPin.setImageResource(R.drawable.ic_praise_filled) + ivPin.rotation = 180f + ivPin.setColorFilter(context.themeColor(com.google.android.material.R.attr.colorSecondary)) + } + else -> ivPin.gone() + } + } + + if (payloads.isEmpty()) { + cbName.text = item.getDisplayNameGroup() + swtEnabled.isChecked = item.isEnabled + cbName.isChecked = selected.contains(item) + } } } @@ -221,6 +243,7 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) : } return false } + } interface CallBack { diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt index 45ee9b118..b28b7143e 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import splitties.init.appCtx /** @@ -43,39 +44,64 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application val rulesFlow = combine(_searchKey, _sortMode) { search, sort -> Pair(search, sort) }.flatMapLatest { (searchKey, sortMode) -> - when { - searchKey.isNullOrEmpty() -> when (sortMode) { - "asc" -> appDb.replaceRuleDao.flowAllAsc() - "name_asc" -> appDb.replaceRuleDao.flowAllNameAsc() - "name_desc" -> appDb.replaceRuleDao.flowAllNameDesc() - else -> appDb.replaceRuleDao.flowAllDesc() - } - - searchKey == appCtx.getString(R.string.no_group) -> when (sortMode) { - "asc" -> appDb.replaceRuleDao.flowNoGroupAsc() - "name_asc" -> appDb.replaceRuleDao.flowNoGroupNameAsc() - "name_desc" -> appDb.replaceRuleDao.flowNoGroupNameDesc() - else -> appDb.replaceRuleDao.flowNoGroupDesc() - } - + // 先获取基础数据 + val baseFlow = when { + searchKey.isNullOrEmpty() -> appDb.replaceRuleDao.flowAll() + searchKey == appCtx.getString(R.string.no_group) -> appDb.replaceRuleDao.flowNoGroup() searchKey.startsWith("group:") -> { val key = searchKey.substringAfter("group:") - when (sortMode) { - "asc" -> appDb.replaceRuleDao.flowGroupSearchAsc("%$key%") - "name_asc" -> appDb.replaceRuleDao.flowGroupSearchNameAsc("%$key%") - "name_desc" -> appDb.replaceRuleDao.flowGroupSearchNameDesc("%$key%") - else -> appDb.replaceRuleDao.flowGroupSearchDesc("%$key%") + appDb.replaceRuleDao.flowGroupSearch("%$key%") + } + else -> appDb.replaceRuleDao.flowSearch("%$searchKey%") + } + + baseFlow.map { rules -> + val comparator = when (sortMode) { + "asc" -> Comparator { a, b -> + when { + // 置顶优先 + a.order == -1 && b.order != -1 -> -1 + a.order != -1 && b.order == -1 -> 1 + // 置底最后 + a.order == -2 && b.order != -2 -> 1 + a.order != -2 && b.order == -2 -> -1 + // 普通规则按order排序 + else -> a.order.compareTo(b.order) + } } + "desc" -> Comparator { a, b -> + when { + a.order == -1 && b.order != -1 -> -1 + a.order != -1 && b.order == -1 -> 1 + a.order == -2 && b.order != -2 -> 1 + a.order != -2 && b.order == -2 -> -1 + else -> b.order.compareTo(a.order) + } + } + "name_asc" -> Comparator { a, b -> + when { + a.order == -1 && b.order != -1 -> -1 + a.order != -1 && b.order == -1 -> 1 + a.order == -2 && b.order != -2 -> 1 + a.order != -2 && b.order == -2 -> -1 + else -> a.name.lowercase().compareTo(b.name.lowercase()) + } + } + "name_desc" -> Comparator { a, b -> + when { + a.order == -1 && b.order != -1 -> -1 + a.order != -1 && b.order == -1 -> 1 + a.order == -2 && b.order != -2 -> 1 + a.order != -2 && b.order == -2 -> -1 + else -> b.name.lowercase().compareTo(a.name.lowercase()) + } + } + else -> null } - else -> when (sortMode) { - "asc" -> appDb.replaceRuleDao.flowSearchAsc("%$searchKey%") - "name_asc" -> appDb.replaceRuleDao.flowSearchNameAsc("%$searchKey%") - "name_desc" -> appDb.replaceRuleDao.flowSearchNameDesc("%$searchKey%") - else -> appDb.replaceRuleDao.flowSearchDesc("%$searchKey%") - } + if (comparator != null) rules.sortedWith(comparator) else rules } - }.flowOn(Dispatchers.IO) + }.flowOn(Dispatchers.Default) fun update(vararg rule: ReplaceRule) { execute { @@ -91,16 +117,15 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application fun toTop(rule: ReplaceRule) { execute { - rule.order = appDb.replaceRuleDao.minOrder - 1 + rule.order = -1 appDb.replaceRuleDao.update(rule) } } fun topSelect(rules: List) { execute { - var minOrder = appDb.replaceRuleDao.minOrder - rules.size rules.forEach { - it.order = ++minOrder + it.order = -1 } appDb.replaceRuleDao.update(*rules.toTypedArray()) } @@ -108,16 +133,15 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application fun toBottom(rule: ReplaceRule) { execute { - rule.order = appDb.replaceRuleDao.maxOrder + 1 + rule.order = -2 appDb.replaceRuleDao.update(rule) } } fun bottomSelect(rules: List) { execute { - var maxOrder = appDb.replaceRuleDao.maxOrder rules.forEach { - it.order = maxOrder++ + it.order = -2 } appDb.replaceRuleDao.update(*rules.toTypedArray()) } @@ -125,9 +149,13 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application fun upOrder() { execute { + // 重置所有非特殊排序的规则 val rules = appDb.replaceRuleDao.all - for ((index, rule) in rules.withIndex()) { - rule.order = index + 1 + var normalOrder = 1 + rules.forEach { rule -> + if (rule.order >= 0) { // 只重置普通排序的规则 + rule.order = normalOrder++ + } } appDb.replaceRuleDao.update(*rules.toTypedArray()) } diff --git a/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditActivity.kt b/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditActivity.kt index 397c73807..af6118b7b 100644 --- a/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditActivity.kt @@ -211,8 +211,7 @@ class ReplaceEditActivity : private fun upReplaceView(rule: ReplaceRule) = binding.run { etName.setText(rule.name) - val group = rule.group.takeUnless { it.isNullOrBlank() } - ?: GroupManager.getLastSelectedGroup(this@ReplaceEditActivity) + val group = rule.group.takeUnless { it.isNullOrBlank() } ?: "默认" etGroup.setText(group, false) etReplaceRule.setText(rule.pattern) cbUseRegex.isChecked = rule.isRegex @@ -229,8 +228,11 @@ class ReplaceEditActivity : val rule = viewModel.replaceRule ?: ReplaceRule() rule.name = etName.text.toString() - rule.group = etGroup.text.toString().trim().ifBlank { - GroupManager.getLastSelectedGroup(this@ReplaceEditActivity) + val groupText = etGroup.text.toString().trim() + rule.group = if (groupText == "默认") { + null + } else { + groupText.ifBlank { null } } rule.pattern = etReplaceRule.text.toString() rule.isRegex = cbUseRegex.isChecked diff --git a/app/src/main/res/layout/activity_replace_edit.xml b/app/src/main/res/layout/activity_replace_edit.xml index ea8662250..166fad9f8 100644 --- a/app/src/main/res/layout/activity_replace_edit.xml +++ b/app/src/main/res/layout/activity_replace_edit.xml @@ -39,26 +39,35 @@ tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" /> - + android:orientation="horizontal"> - - + android:layout_weight="1" + style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu" + android:hint="@string/group"> - + + + + + + + +