替换规则排序
This commit is contained in:
@@ -72,8 +72,6 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
|
|||||||
}
|
}
|
||||||
private var groups = arrayListOf<String>()
|
private var groups = arrayListOf<String>()
|
||||||
private var groupMenu: SubMenu? = null
|
private var groupMenu: SubMenu? = null
|
||||||
|
|
||||||
private var sortMode = "desc"
|
|
||||||
private var searchKey: String? = null
|
private var searchKey: String? = null
|
||||||
private val qrCodeResult = registerForActivityResult(QrCodeResult()) {
|
private val qrCodeResult = registerForActivityResult(QrCodeResult()) {
|
||||||
it ?: return@registerForActivityResult
|
it ?: return@registerForActivityResult
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ import io.legado.app.databinding.ItemReplaceRuleBinding
|
|||||||
//import io.legado.app.lib.theme.backgroundColor
|
//import io.legado.app.lib.theme.backgroundColor
|
||||||
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
|
||||||
import io.legado.app.ui.widget.recycler.ItemTouchCallback
|
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) :
|
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 {
|
override fun areContentsTheSame(oldItem: ReplaceRule, newItem: ReplaceRule): Boolean {
|
||||||
if (oldItem.name != newItem.name) {
|
if (oldItem.name != newItem.name) return false
|
||||||
return false
|
if (oldItem.group != newItem.group) return false
|
||||||
}
|
if (oldItem.isEnabled != newItem.isEnabled) return false
|
||||||
if (oldItem.group != newItem.group) {
|
if (oldItem.order != newItem.order) return false // 添加 order 检查
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (oldItem.isEnabled != newItem.isEnabled) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getChangePayload(oldItem: ReplaceRule, newItem: ReplaceRule): Any? {
|
override fun getChangePayload(oldItem: ReplaceRule, newItem: ReplaceRule): Any? {
|
||||||
val payload = Bundle()
|
val payload = Bundle()
|
||||||
if (oldItem.name != newItem.name
|
if (oldItem.name != newItem.name || oldItem.group != newItem.group) {
|
||||||
|| oldItem.group != newItem.group
|
|
||||||
) {
|
|
||||||
payload.putBoolean("upName", true)
|
payload.putBoolean("upName", true)
|
||||||
}
|
}
|
||||||
if (oldItem.isEnabled != newItem.isEnabled) {
|
if (oldItem.isEnabled != newItem.isEnabled) {
|
||||||
payload.putBoolean("enabled", newItem.isEnabled)
|
payload.putBoolean("enabled", newItem.isEnabled)
|
||||||
}
|
}
|
||||||
|
if (oldItem.order != newItem.order) {
|
||||||
|
payload.putBoolean("orderChanged", true)
|
||||||
|
}
|
||||||
if (payload.isEmpty) {
|
if (payload.isEmpty) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -102,12 +102,9 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) :
|
|||||||
payloads: MutableList<Any>
|
payloads: MutableList<Any>
|
||||||
) {
|
) {
|
||||||
binding.run {
|
binding.run {
|
||||||
if (payloads.isEmpty()) {
|
var needUpdatePin = payloads.isEmpty()
|
||||||
//root.setBackgroundColor(ColorUtils.withAlpha(context.backgroundColor, 0.5f))
|
|
||||||
cbName.text = item.getDisplayNameGroup()
|
if (payloads.isNotEmpty()) {
|
||||||
swtEnabled.isChecked = item.isEnabled
|
|
||||||
cbName.isChecked = selected.contains(item)
|
|
||||||
} else {
|
|
||||||
for (i in payloads.indices) {
|
for (i in payloads.indices) {
|
||||||
val bundle = payloads[i] as Bundle
|
val bundle = payloads[i] as Bundle
|
||||||
bundle.keySet().forEach {
|
bundle.keySet().forEach {
|
||||||
@@ -115,10 +112,35 @@ class ReplaceRuleAdapter(context: Context, var callBack: CallBack) :
|
|||||||
"selected" -> cbName.isChecked = selected.contains(item)
|
"selected" -> cbName.isChecked = selected.contains(item)
|
||||||
"upName" -> cbName.text = item.getDisplayNameGroup()
|
"upName" -> cbName.text = item.getDisplayNameGroup()
|
||||||
"enabled" -> swtEnabled.isChecked = item.isEnabled
|
"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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CallBack {
|
interface CallBack {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.catch
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,39 +44,64 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
|||||||
val rulesFlow = combine(_searchKey, _sortMode) { search, sort ->
|
val rulesFlow = combine(_searchKey, _sortMode) { search, sort ->
|
||||||
Pair(search, sort)
|
Pair(search, sort)
|
||||||
}.flatMapLatest { (searchKey, sortMode) ->
|
}.flatMapLatest { (searchKey, sortMode) ->
|
||||||
when {
|
// 先获取基础数据
|
||||||
searchKey.isNullOrEmpty() -> when (sortMode) {
|
val baseFlow = when {
|
||||||
"asc" -> appDb.replaceRuleDao.flowAllAsc()
|
searchKey.isNullOrEmpty() -> appDb.replaceRuleDao.flowAll()
|
||||||
"name_asc" -> appDb.replaceRuleDao.flowAllNameAsc()
|
searchKey == appCtx.getString(R.string.no_group) -> appDb.replaceRuleDao.flowNoGroup()
|
||||||
"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()
|
|
||||||
}
|
|
||||||
|
|
||||||
searchKey.startsWith("group:") -> {
|
searchKey.startsWith("group:") -> {
|
||||||
val key = searchKey.substringAfter("group:")
|
val key = searchKey.substringAfter("group:")
|
||||||
when (sortMode) {
|
appDb.replaceRuleDao.flowGroupSearch("%$key%")
|
||||||
"asc" -> appDb.replaceRuleDao.flowGroupSearchAsc("%$key%")
|
}
|
||||||
"name_asc" -> appDb.replaceRuleDao.flowGroupSearchNameAsc("%$key%")
|
else -> appDb.replaceRuleDao.flowSearch("%$searchKey%")
|
||||||
"name_desc" -> appDb.replaceRuleDao.flowGroupSearchNameDesc("%$key%")
|
}
|
||||||
else -> appDb.replaceRuleDao.flowGroupSearchDesc("%$key%")
|
|
||||||
|
baseFlow.map { rules ->
|
||||||
|
val comparator = when (sortMode) {
|
||||||
|
"asc" -> Comparator<ReplaceRule> { 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<ReplaceRule> { 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<ReplaceRule> { 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<ReplaceRule> { 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) {
|
if (comparator != null) rules.sortedWith(comparator) else rules
|
||||||
"asc" -> appDb.replaceRuleDao.flowSearchAsc("%$searchKey%")
|
|
||||||
"name_asc" -> appDb.replaceRuleDao.flowSearchNameAsc("%$searchKey%")
|
|
||||||
"name_desc" -> appDb.replaceRuleDao.flowSearchNameDesc("%$searchKey%")
|
|
||||||
else -> appDb.replaceRuleDao.flowSearchDesc("%$searchKey%")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO)
|
}.flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
fun update(vararg rule: ReplaceRule) {
|
fun update(vararg rule: ReplaceRule) {
|
||||||
execute {
|
execute {
|
||||||
@@ -91,16 +117,15 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
|||||||
|
|
||||||
fun toTop(rule: ReplaceRule) {
|
fun toTop(rule: ReplaceRule) {
|
||||||
execute {
|
execute {
|
||||||
rule.order = appDb.replaceRuleDao.minOrder - 1
|
rule.order = -1
|
||||||
appDb.replaceRuleDao.update(rule)
|
appDb.replaceRuleDao.update(rule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun topSelect(rules: List<ReplaceRule>) {
|
fun topSelect(rules: List<ReplaceRule>) {
|
||||||
execute {
|
execute {
|
||||||
var minOrder = appDb.replaceRuleDao.minOrder - rules.size
|
|
||||||
rules.forEach {
|
rules.forEach {
|
||||||
it.order = ++minOrder
|
it.order = -1
|
||||||
}
|
}
|
||||||
appDb.replaceRuleDao.update(*rules.toTypedArray())
|
appDb.replaceRuleDao.update(*rules.toTypedArray())
|
||||||
}
|
}
|
||||||
@@ -108,16 +133,15 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
|||||||
|
|
||||||
fun toBottom(rule: ReplaceRule) {
|
fun toBottom(rule: ReplaceRule) {
|
||||||
execute {
|
execute {
|
||||||
rule.order = appDb.replaceRuleDao.maxOrder + 1
|
rule.order = -2
|
||||||
appDb.replaceRuleDao.update(rule)
|
appDb.replaceRuleDao.update(rule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bottomSelect(rules: List<ReplaceRule>) {
|
fun bottomSelect(rules: List<ReplaceRule>) {
|
||||||
execute {
|
execute {
|
||||||
var maxOrder = appDb.replaceRuleDao.maxOrder
|
|
||||||
rules.forEach {
|
rules.forEach {
|
||||||
it.order = maxOrder++
|
it.order = -2
|
||||||
}
|
}
|
||||||
appDb.replaceRuleDao.update(*rules.toTypedArray())
|
appDb.replaceRuleDao.update(*rules.toTypedArray())
|
||||||
}
|
}
|
||||||
@@ -125,9 +149,13 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
|||||||
|
|
||||||
fun upOrder() {
|
fun upOrder() {
|
||||||
execute {
|
execute {
|
||||||
|
// 重置所有非特殊排序的规则
|
||||||
val rules = appDb.replaceRuleDao.all
|
val rules = appDb.replaceRuleDao.all
|
||||||
for ((index, rule) in rules.withIndex()) {
|
var normalOrder = 1
|
||||||
rule.order = index + 1
|
rules.forEach { rule ->
|
||||||
|
if (rule.order >= 0) { // 只重置普通排序的规则
|
||||||
|
rule.order = normalOrder++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
appDb.replaceRuleDao.update(*rules.toTypedArray())
|
appDb.replaceRuleDao.update(*rules.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,8 +211,7 @@ class ReplaceEditActivity :
|
|||||||
|
|
||||||
private fun upReplaceView(rule: ReplaceRule) = binding.run {
|
private fun upReplaceView(rule: ReplaceRule) = binding.run {
|
||||||
etName.setText(rule.name)
|
etName.setText(rule.name)
|
||||||
val group = rule.group.takeUnless { it.isNullOrBlank() }
|
val group = rule.group.takeUnless { it.isNullOrBlank() } ?: "默认"
|
||||||
?: GroupManager.getLastSelectedGroup(this@ReplaceEditActivity)
|
|
||||||
etGroup.setText(group, false)
|
etGroup.setText(group, false)
|
||||||
etReplaceRule.setText(rule.pattern)
|
etReplaceRule.setText(rule.pattern)
|
||||||
cbUseRegex.isChecked = rule.isRegex
|
cbUseRegex.isChecked = rule.isRegex
|
||||||
@@ -229,8 +228,11 @@ class ReplaceEditActivity :
|
|||||||
val rule = viewModel.replaceRule ?: ReplaceRule()
|
val rule = viewModel.replaceRule ?: ReplaceRule()
|
||||||
|
|
||||||
rule.name = etName.text.toString()
|
rule.name = etName.text.toString()
|
||||||
rule.group = etGroup.text.toString().trim().ifBlank {
|
val groupText = etGroup.text.toString().trim()
|
||||||
GroupManager.getLastSelectedGroup(this@ReplaceEditActivity)
|
rule.group = if (groupText == "默认") {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
groupText.ifBlank { null }
|
||||||
}
|
}
|
||||||
rule.pattern = etReplaceRule.text.toString()
|
rule.pattern = etReplaceRule.text.toString()
|
||||||
rule.isRegex = cbUseRegex.isChecked
|
rule.isRegex = cbUseRegex.isChecked
|
||||||
|
|||||||
@@ -39,26 +39,35 @@
|
|||||||
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
|
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<LinearLayout
|
||||||
android:id="@+id/til_group"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
android:orientation="horizontal">
|
||||||
android:hint="@string/group">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/et_group"
|
android:id="@+id/til_group"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/text_default"
|
android:layout_weight="1"
|
||||||
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
android:hint="@string/group">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||||
android:id="@+id/iv_manage_groups"
|
android:id="@+id/et_group"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/group_manage"/>
|
android:text="@string/text_default"
|
||||||
|
tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/iv_manage_groups"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.Material3Expressive.Button.TextButton"
|
||||||
|
android:text="@string/group_manage"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/til_replace_rule"
|
android:id="@+id/til_replace_rule"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<com.google.android.material.checkbox.MaterialCheckBox
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
android:id="@+id/cb_name"
|
android:id="@+id/cb_name"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:layout_marginStart="4dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
@@ -20,10 +20,19 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toLeftOf="@+id/swt_enabled"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="RtlSymmetry" />
|
tools:ignore="RtlSymmetry" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_pin"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:src="@drawable/ic_praise"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/cb_name"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
android:id="@+id/swt_enabled"
|
android:id="@+id/swt_enabled"
|
||||||
android:name="@string/enable"
|
android:name="@string/enable"
|
||||||
|
|||||||
Reference in New Issue
Block a user