Refactor subscription management to ViewModel

This commit is contained in:
2dust
2026-01-13 17:29:22 +08:00
parent 18f23d9d3b
commit c3a8ee59dc
3 changed files with 69 additions and 27 deletions
@@ -5,26 +5,27 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivitySubSettingBinding
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
import com.v2ray.ang.handler.AngConfigManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.helper.SimpleItemTouchHelperCallback
import com.v2ray.ang.viewmodel.SubscriptionsViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.getValue
class SubSettingActivity : BaseActivity() {
private val binding by lazy { ActivitySubSettingBinding.inflate(layoutInflater) }
var subscriptions: List<Pair<String, SubscriptionItem>> = listOf()
private val adapter by lazy { SubSettingRecyclerAdapter(this) }
private val viewModel: SubscriptionsViewModel by viewModels()
private lateinit var adapter: SubSettingRecyclerAdapter
private var mItemTouchHelper: ItemTouchHelper? = null
override fun onCreate(savedInstanceState: Bundle?) {
@@ -32,6 +33,8 @@ class SubSettingActivity : BaseActivity() {
//setContentView(binding.root)
setContentViewWithToolbar(binding.root, showHomeAsUp = true, title = getString(R.string.title_sub_setting))
adapter = SubSettingRecyclerAdapter(this, viewModel)
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider)
@@ -83,7 +86,7 @@ class SubSettingActivity : BaseActivity() {
@SuppressLint("NotifyDataSetChanged")
fun refreshData() {
subscriptions = MmkvManager.decodeSubscriptions()
viewModel.reload()
adapter.notifyDataSetChanged()
}
}
@@ -8,7 +8,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
@@ -16,16 +15,16 @@ import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
import com.v2ray.ang.extension.toast
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsChangeManager
import com.v2ray.ang.handler.SettingsManager
import com.v2ray.ang.helper.ItemTouchHelperAdapter
import com.v2ray.ang.helper.ItemTouchHelperViewHolder
import com.v2ray.ang.util.QRCodeDecoder
import com.v2ray.ang.util.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import com.v2ray.ang.viewmodel.SubscriptionsViewModel
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>(), ItemTouchHelperAdapter {
class SubSettingRecyclerAdapter(
val activity: SubSettingActivity,
private val viewModel: SubscriptionsViewModel
) : RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>(), ItemTouchHelperAdapter {
private var mActivity: SubSettingActivity = activity
@@ -33,11 +32,12 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
mActivity.resources.getStringArray(R.array.share_sub_method)
}
override fun getItemCount() = mActivity.subscriptions.size
override fun getItemCount() = viewModel.getAll().size
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
val subId = mActivity.subscriptions[position].first
val subItem = mActivity.subscriptions[position].second
val subscriptions = viewModel.getAll()
val subId = subscriptions[position].first
val subItem = subscriptions[position].second
holder.itemSubSettingBinding.tvName.text = subItem.remarks
holder.itemSubSettingBinding.tvUrl.text = subItem.url
holder.itemSubSettingBinding.chkEnable.isChecked = subItem.enabled
@@ -58,8 +58,7 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
holder.itemSubSettingBinding.chkEnable.setOnCheckedChangeListener { it, isChecked ->
if (!it.isPressed) return@setOnCheckedChangeListener
subItem.enabled = isChecked
MmkvManager.encodeSubscription(subId, subItem)
viewModel.update(subId, subItem)
}
if (TextUtils.isEmpty(subItem.url)) {
@@ -119,15 +118,10 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
}
private fun removeSubscriptionSub(subId: String, position: Int) {
mActivity.lifecycleScope.launch(Dispatchers.IO) {
MmkvManager.removeSubscription(subId)
launch(Dispatchers.Main) {
notifyItemRemoved(position)
notifyItemRangeChanged(position, mActivity.subscriptions.size)
mActivity.refreshData()
SettingsChangeManager.makeSetupGroupTab()
}
}
viewModel.remove(subId)
notifyItemRemoved(position)
notifyItemRangeChanged(position, viewModel.getAll().size)
mActivity.refreshData()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
@@ -154,14 +148,13 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
}
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
SettingsManager.swapSubscriptions(fromPosition, toPosition)
viewModel.swap(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
return true
}
override fun onItemMoveCompleted() {
mActivity.refreshData()
SettingsChangeManager.makeSetupGroupTab()
}
override fun onItemDismiss(position: Int) {
@@ -0,0 +1,46 @@
package com.v2ray.ang.viewmodel
import androidx.lifecycle.ViewModel
import com.v2ray.ang.dto.SubscriptionItem
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsChangeManager
import com.v2ray.ang.handler.SettingsManager
class SubscriptionsViewModel : ViewModel() {
private val subscriptions: MutableList<Pair<String, SubscriptionItem>> =
MmkvManager.decodeSubscriptions().toMutableList()
fun getAll(): List<Pair<String, SubscriptionItem>> = subscriptions.toList()
fun reload() {
subscriptions.clear()
subscriptions.addAll(MmkvManager.decodeSubscriptions())
}
fun remove(subId: String): Boolean {
val changed = subscriptions.removeAll { it.first == subId }
if (changed) {
MmkvManager.removeSubscription(subId)
SettingsChangeManager.makeSetupGroupTab()
}
return changed
}
fun update(subId: String, item: SubscriptionItem) {
val idx = subscriptions.indexOfFirst { it.first == subId }
if (idx >= 0) {
subscriptions[idx] = Pair(subId, item)
MmkvManager.encodeSubscription(subId, item)
}
}
fun swap(fromPosition: Int, toPosition: Int) {
if (fromPosition in subscriptions.indices && toPosition in subscriptions.indices) {
val item = subscriptions.removeAt(fromPosition)
subscriptions.add(toPosition, item)
SettingsManager.swapSubscriptions(fromPosition, toPosition)
SettingsChangeManager.makeSetupGroupTab()
}
}
}