Skip to content

Commit 846fa77

Browse files
committed
📝 (ui): 引入 HomeActivityVm 统一管理更新逻辑与状态
引入 HomeActivityVm 负责应用与规则的更新检查、状态卡数据更新及自动备份任务。 通过 LiveData 通知 UI 展示更新弹窗,解耦 Activity 与业务逻辑,提升可维护性。 状态卡组件现通过 VM 获取数据并触发检查,避免直接调用工具类。
1 parent 2053d39 commit 846fa77

6 files changed

Lines changed: 276 additions & 101 deletions

File tree

app/src/main/java/net/ankio/auto/ui/activity/HomeActivity.kt

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,53 +17,68 @@ package net.ankio.auto.ui.activity
1717

1818
import android.os.Bundle
1919
import android.view.MenuItem
20+
import androidx.activity.viewModels
21+
import androidx.core.net.toUri
22+
import androidx.lifecycle.lifecycleScope
2023
import androidx.navigation.fragment.NavHostFragment
2124
import androidx.navigation.ui.NavigationUI
2225
import androidx.navigation.ui.setupWithNavController
26+
import kotlinx.coroutines.launch
2327
import net.ankio.auto.App
2428
import net.ankio.auto.R
2529
import net.ankio.auto.databinding.ActivityMainBinding
2630
import net.ankio.auto.storage.backup.BackupManager
2731
import net.ankio.auto.ui.api.BaseActivity
28-
import net.ankio.auto.update.AppUpdateHelper
29-
import net.ankio.auto.update.RuleUpdateHelper
32+
import net.ankio.auto.ui.api.BaseSheetDialog
33+
import net.ankio.auto.ui.dialog.UpdateDialog
3034
import net.ankio.auto.ui.utils.slideDown
3135
import net.ankio.auto.ui.utils.slideUp
32-
import net.ankio.auto.utils.PrefManager
33-
import net.ankio.auto.utils.Throttle
36+
import net.ankio.auto.ui.vm.HomeActivityVm
37+
import net.ankio.auto.update.AppUpdateHelper
38+
import net.ankio.auto.update.RuleUpdateHelper
39+
import net.ankio.auto.utils.CustomTabsHelper
3440

3541
class HomeActivity : BaseActivity() {
42+
private val vm: HomeActivityVm by viewModels()
3643

37-
/** Pref 全量同步节流:5 分钟,持久化以支持进程重启后节流 */
38-
private val prefSyncThrottle = Throttle.asFunction(
39-
intervalMs = 5 * 60 * 1000L,
40-
persistKey = "pref_sync"
41-
) { App.launch { PrefManager.syncAllToBackend() } }
44+
private val binding: ActivityMainBinding by lazy {
45+
ActivityMainBinding.inflate(layoutInflater)
46+
}
4247

43-
/** 自动检查更新节流:30 分钟,持久化以支持进程重启后节流 */
44-
private val updateCheckThrottle = Throttle.asFunction(
45-
intervalMs = 30 * 60 * 1000L,
46-
persistKey = "auto_update_check"
47-
) {
48-
App.launch {
49-
runCatching {
50-
if (RuleUpdateHelper.isAutoCheckEnabled()) {
51-
RuleUpdateHelper.checkAndShow(this@HomeActivity, false)
52-
}
53-
if (AppUpdateHelper.isAutoCheckEnabled()) {
54-
AppUpdateHelper.checkAndShow(this@HomeActivity, false)
48+
/** 注册 VM 的 LiveData 观察,用于展示规则/应用更新弹窗 */
49+
private fun setupUpdateListeners() {
50+
vm.appUpdateModel.observe(this) { model ->
51+
if (model == null) return@observe
52+
vm.consumeAppUpdate()
53+
BaseSheetDialog.create<UpdateDialog>(this)
54+
.setUpdateModel(model)
55+
.setRuleTitle(getString(R.string.app))
56+
.setOnClickUpdate {
57+
CustomTabsHelper.launchUrl(AppUpdateHelper.buildApkUrl(model.version).toUri())
5558
}
56-
}
59+
.show()
5760
}
58-
}
5961

60-
private val binding: ActivityMainBinding by lazy {
61-
ActivityMainBinding.inflate(layoutInflater)
62+
vm.ruleUpdateModel.observe(this) { model ->
63+
if (model == null) return@observe
64+
vm.consumeRuleUpdate()
65+
BaseSheetDialog.create<UpdateDialog>(this)
66+
.setUpdateModel(model)
67+
.setRuleTitle(getString(R.string.rule))
68+
.setOnClickUpdate {
69+
lifecycleScope.launch {
70+
RuleUpdateHelper.updateRule(this@HomeActivity, model)
71+
vm.refreshStatus(this@HomeActivity)
72+
}
73+
}
74+
.show()
75+
}
6276
}
6377

6478
override fun onCreate(savedInstanceState: Bundle?) {
6579
super.onCreate(savedInstanceState)
6680
setContentView(binding.root)
81+
setupUpdateListeners()
6782
val navHostFragment = supportFragmentManager
6883
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
6984
?: throw IllegalStateException("NavHostFragment not found")
@@ -97,16 +112,12 @@ class HomeActivity : BaseActivity() {
97112

98113
override fun onResume() {
99114
super.onResume()
100-
prefSyncThrottle()
101-
updateCheckThrottle()
115+
vm.startSyncUpdateTask()
102116
}
103117

104118
override fun onStop() {
105119
super.onStop()
106-
// 使用全局协程作用域执行备份,避免阻塞UI线程
107-
App.launch {
108-
BackupManager.autoBackup()
109-
}
120+
vm.startAutoBackup()
110121
}
111122

112123
override fun onDestroy() {

app/src/main/java/net/ankio/auto/ui/fragment/HomeFragment.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package net.ankio.auto.ui.fragment
1818
import android.content.Intent
1919
import android.os.Bundle
2020
import android.view.View
21+
import androidx.fragment.app.activityViewModels
2122
import androidx.navigation.fragment.findNavController
2223
import com.google.gson.Gson
2324
import net.ankio.auto.BuildConfig
@@ -36,15 +37,18 @@ import net.ankio.auto.ui.dialog.BottomSheetDialogBuilder
3637
import net.ankio.auto.ui.fragment.components.BookCardComponent
3738
import net.ankio.auto.ui.fragment.components.MonthlyCardComponent
3839
import net.ankio.auto.ui.fragment.components.StatusCardComponent
40+
import net.ankio.auto.ui.vm.HomeActivityVm
3941
import net.ankio.auto.utils.PrefManager
4042
import org.ezbook.server.intent.BillInfoIntent
4143

4244
class HomeFragment : BaseFragment<FragmentPluginHomeBinding>() {
4345
private val gson = Gson()
46+
private val vm: HomeActivityVm by activityViewModels()
4447

4548
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
4649
super.onViewCreated(view, savedInstanceState)
4750
val statusCard: StatusCardComponent = binding.activeCard.bindAs()
51+
statusCard.setVm(vm)
4852

4953

5054
val monthlyCard: MonthlyCardComponent = binding.monthlyCard.bindAs()

app/src/main/java/net/ankio/auto/ui/fragment/components/StatusCardComponent.kt

Lines changed: 45 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,100 +17,82 @@ package net.ankio.auto.ui.fragment.components
1717

1818
import androidx.annotation.ColorInt
1919
import androidx.annotation.DrawableRes
20-
import net.ankio.auto.BuildConfig
20+
import androidx.lifecycle.findViewTreeLifecycleOwner
2121
import net.ankio.auto.R
22-
import net.ankio.auto.constant.WorkMode
2322
import net.ankio.auto.databinding.CardStatusBinding
24-
import net.ankio.auto.service.CoreService
25-
import net.ankio.auto.service.OcrService
2623
import net.ankio.auto.ui.api.BaseComponent
2724
import net.ankio.auto.ui.theme.DynamicColors
28-
import net.ankio.auto.update.AppUpdateHelper
29-
import net.ankio.auto.update.RuleUpdateHelper
3025
import net.ankio.auto.ui.utils.toDrawable
26+
import net.ankio.auto.ui.vm.HomeActivityVm
27+
import net.ankio.auto.ui.vm.StatusData
3128
import net.ankio.auto.utils.PrefManager
32-
import net.ankio.auto.xposed.XposedModule
3329
import java.util.Locale
3430

31+
/**
32+
* 状态卡组件,展示工作状态、规则/应用版本,点击检查更新。
33+
* 需通过 [setVm] 注入 [HomeActivityVm],UI 数据与点击逻辑均走 VM。
34+
*/
3535
class StatusCardComponent(binding: CardStatusBinding) :
3636
BaseComponent<CardStatusBinding>(binding) {
3737

38+
private var vm: HomeActivityVm? = null
39+
40+
/** 注入 ViewModel,必须在 [onComponentCreate] 之后由宿主调用 */
41+
fun setVm(vm: HomeActivityVm) {
42+
this.vm = vm
43+
setupVm()
44+
}
45+
3846
override fun onComponentCreate() {
3947
super.onComponentCreate()
48+
binding.cardContentRule.setBackgroundColor(DynamicColors.SurfaceColor3)
49+
binding.cardContentApp.setBackgroundColor(DynamicColors.SurfaceColor3)
4050

41-
// 整卡点击:检查规则更新
51+
// 点击:检查更新(走 VM)
4252
binding.cardContentRule.setOnClickListener {
43-
launch {
44-
RuleUpdateHelper.checkAndShow(context, true) { updateRuleTexts() }
45-
}
53+
vm?.checkRuleUpdate(fromUser = true)
4654
}
47-
4855
binding.cardContentApp.setOnClickListener {
49-
launch {
50-
AppUpdateHelper.checkAndShow(context, true)
51-
}
56+
vm?.checkAppUpdate(fromUser = true)
5257
}
5358

54-
// 长按:强制规则更新
59+
// 长按:强制规则更新后检查
5560
binding.cardContentRule.setOnLongClickListener {
5661
PrefManager.ruleVersion = ""
57-
launch {
58-
RuleUpdateHelper.checkAndShow(context, true) { updateRuleTexts() }
59-
}
62+
vm?.checkRuleUpdate(fromUser = true)
6063
true
6164
}
6265
binding.cardContentApp.setOnLongClickListener {
63-
launch {
64-
AppUpdateHelper.checkAndShow(context, true)
65-
}
66+
vm?.checkAppUpdate(fromUser = true)
6667
true
6768
}
68-
69-
binding.cardContentRule.setBackgroundColor(DynamicColors.SurfaceColor3)
70-
binding.cardContentApp.setBackgroundColor(DynamicColors.SurfaceColor3)
71-
// 初始化状态显示
72-
updateActiveStatus()
73-
updateRuleTexts()
7469
}
7570

7671
override fun onComponentResume() {
7772
super.onComponentResume()
78-
updateActiveStatus()
79-
updateRuleTexts()
73+
vm?.refreshStatus(context)
8074
}
8175

82-
/**
83-
* 当前模式是否处于"工作中"
84-
*/
85-
private fun isCurrentModeActive(): Boolean {
86-
return when (PrefManager.workMode) {
87-
WorkMode.Ocr -> OcrService.serverStarted
88-
WorkMode.LSPatch -> CoreService.isRunning(context)
89-
WorkMode.Xposed -> XposedModule.active()
76+
/** 绑定 VM 后调用:注册观察、首次刷新 */
77+
private fun setupVm() {
78+
val v = vm ?: return
79+
val owner = binding.root.findViewTreeLifecycleOwner() ?: return
80+
v.refreshStatus(context)
81+
v.statusData.observe(owner) { data ->
82+
applyStatusData(data)
9083
}
9184
}
9285

93-
/**
94-
* 统一更新激活状态显示(第一行:工作状态 + 模式标签)
95-
*/
96-
private fun updateActiveStatus() {
97-
val isActive = isCurrentModeActive()
98-
val versionName = BuildConfig.VERSION_NAME
99-
100-
// 工作状态文本
101-
val statusText = if (isActive) {
102-
context.getString(R.string.status_working)
103-
} else {
104-
context.getString(R.string.status_inactive)
105-
}
106-
binding.titleText.text = statusText
107-
108-
binding.modeText.text = PrefManager.workMode.name.uppercase()
109-
// 调试模式时显示小标签,与 mode 标签风格统一
86+
/** 根据 [StatusData] 更新 UI */
87+
private fun applyStatusData(data: StatusData) {
88+
binding.titleText.text =
89+
if (data.isActive) context.getString(R.string.status_working)
90+
else context.getString(R.string.status_inactive)
91+
binding.modeText.text = data.workMode.name.uppercase(Locale.getDefault())
11092
binding.debugTag.visibility =
111-
if (PrefManager.debugMode) android.view.View.VISIBLE else android.view.View.GONE
93+
if (data.debugMode) android.view.View.VISIBLE else android.view.View.GONE
11294

113-
if (isActive) {
95+
if (data.isActive) {
11496
setActive(
11597
backgroundColor = DynamicColors.SecondaryContainer,
11698
textColor = DynamicColors.OnPrimaryContainer,
@@ -123,21 +105,15 @@ class StatusCardComponent(binding: CardStatusBinding) :
123105
drawable = R.drawable.home_active_error
124106
)
125107
}
126-
binding.subtitleText.text = "v$versionName"
127-
val channelValue = PrefManager.appChannel.lowercase(Locale.getDefault())
108+
binding.subtitleText.text = "v${data.versionName}"
109+
binding.ruleVersionText.text = data.ruleVersion
110+
binding.ruleUpdateText.text = data.ruleUpdate
111+
128112
val values = context.resources.getStringArray(R.array.update_channel_values)
129113
val texts = context.resources.getStringArray(R.array.update_channel_texts)
130-
val channelIndex = values.indexOf(channelValue).coerceAtLeast(0)
114+
val channelIndex = values.indexOf(data.channelValue).coerceAtLeast(0)
131115
binding.channelText.text =
132-
texts.getOrElse(channelIndex) { texts.firstOrNull() ?: channelValue }
133-
}
134-
135-
/**
136-
* 更新第三、四行:规则版本与规则更新时间
137-
*/
138-
private fun updateRuleTexts() {
139-
binding.ruleVersionText.text = PrefManager.ruleVersion
140-
binding.ruleUpdateText.text = PrefManager.ruleUpdate
116+
texts.getOrElse(channelIndex) { texts.firstOrNull() ?: data.channelValue }
141117
}
142118

143119
private fun setActive(

0 commit comments

Comments
 (0)