HapeLee 1 год назад
Родитель
Сommit
963cc90621

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

@@ -72,8 +72,6 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
     }
     private var groups = arrayListOf<String>()
     private var groupMenu: SubMenu? = null
-
-    private var sortMode = "desc"
     private var searchKey: String? = null
     private val qrCodeResult = registerForActivityResult(QrCodeResult()) {
         it ?: return@registerForActivityResult

+ 41 - 18
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<Any>
     ) {
         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 {

+ 63 - 35
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%")
+        }
 
-            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%")
+        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
             }
+
+            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<ReplaceRule>) {
         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<ReplaceRule>) {
         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())
         }

+ 6 - 4
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

+ 24 - 15
app/src/main/res/layout/activity_replace_edit.xml

@@ -39,26 +39,35 @@
                     tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
             </com.google.android.material.textfield.TextInputLayout>
 
-            <com.google.android.material.textfield.TextInputLayout
-                android:id="@+id/til_group"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
-                android:hint="@string/group">
+                android:orientation="horizontal">
 
-                <com.google.android.material.textfield.MaterialAutoCompleteTextView
-                    android:id="@+id/et_group"
-                    android:layout_width="match_parent"
+                <com.google.android.material.textfield.TextInputLayout
+                    android:id="@+id/til_group"
+                    android:layout_width="0dp"
                     android:layout_height="wrap_content"
-                    android:text="@string/text_default"
-                    tools:ignore="SpeakableTextPresentCheck,TouchTargetSizeCheck" />
-            </com.google.android.material.textfield.TextInputLayout>
+                    android:layout_weight="1"
+                    style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
+                    android:hint="@string/group">
+
+                    <com.google.android.material.textfield.MaterialAutoCompleteTextView
+                        android:id="@+id/et_group"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        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"/>
 
-            <com.google.android.material.button.MaterialButton
-                android:id="@+id/iv_manage_groups"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/group_manage"/>
+            </LinearLayout>
 
             <com.google.android.material.textfield.TextInputLayout
                 android:id="@+id/til_replace_rule"

+ 11 - 2
app/src/main/res/layout/item_replace_rule.xml

@@ -12,7 +12,7 @@
 
     <com.google.android.material.checkbox.MaterialCheckBox
         android:id="@+id/cb_name"
-        android:layout_width="0dp"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginStart="4dp"
         android:layout_marginEnd="8dp"
@@ -20,10 +20,19 @@
         android:maxLines="2"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toLeftOf="@+id/swt_enabled"
         app:layout_constraintTop_toTopOf="parent"
         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
         android:id="@+id/swt_enabled"
         android:name="@string/enable"