manager: Add pull-to-refresh functionality

This commit is contained in:
ShirkNeko
2025-10-07 01:29:00 +08:00
parent ce3a7ec189
commit c4ff89c13d
3 changed files with 131 additions and 22 deletions

View File

@@ -20,6 +20,7 @@ import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Block
import androidx.compose.material.icons.outlined.TaskAlt
import androidx.compose.material.icons.outlined.Warning
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
@@ -74,6 +75,13 @@ fun HomeScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<HomeViewModel>()
val coroutineScope = rememberCoroutineScope()
val pullRefreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing,
onRefresh = {
viewModel.onPullRefresh(context)
}
)
LaunchedEffect(key1 = navigator) {
viewModel.loadUserSettings(context)
coroutineScope.launch {
@@ -81,6 +89,21 @@ fun HomeScreen(navigator: DestinationsNavigator) {
delay(100)
viewModel.loadExtendedData(context)
}
// 启动数据变化监听
coroutineScope.launch {
while (true) {
delay(5000) // 每5秒检查一次
viewModel.autoRefreshIfNeeded(context)
}
}
}
// 监听数据刷新状态流
LaunchedEffect(viewModel.dataRefreshTrigger) {
viewModel.dataRefreshTrigger.collect { _ ->
// 数据刷新时的额外处理可以在这里添加
}
}
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
@@ -102,6 +125,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
.pullRefresh(pullRefreshState)
) {
Column(
modifier = Modifier

View File

@@ -20,6 +20,8 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class HomeViewModel : ViewModel() {
@@ -90,7 +92,13 @@ class HomeViewModel : ViewModel() {
var isRefreshing by mutableStateOf(false)
private set
// 数据刷新状态流,用于监听变化
private val _dataRefreshTrigger = MutableStateFlow(0L)
val dataRefreshTrigger: StateFlow<Long> = _dataRefreshTrigger
private var loadingJobs = mutableListOf<Job>()
private var lastRefreshTime = 0L
private val refreshCooldown = 2000L
fun loadUserSettings(context: Context) {
viewModelScope.launch(Dispatchers.IO) {
@@ -262,10 +270,20 @@ class HomeViewModel : ViewModel() {
loadingJobs.add(job)
}
fun refreshData(context: Context) {
fun refreshData(context: Context, forceRefresh: Boolean = false) {
val currentTime = System.currentTimeMillis()
// 如果不是强制刷新,检查冷却时间
if (!forceRefresh && currentTime - lastRefreshTime < refreshCooldown) {
return
}
lastRefreshTime = currentTime
viewModelScope.launch {
isRefreshing = true
try {
// 取消正在进行的加载任务
loadingJobs.forEach { it.cancel() }
loadingJobs.clear()
@@ -274,9 +292,17 @@ class HomeViewModel : ViewModel() {
isCoreDataLoaded = false
isExtendedDataLoaded = false
// 重新加载
// 触发数据刷新状态流
_dataRefreshTrigger.value = currentTime
// 重新加载用户设置
loadUserSettings(context)
// 重新加载核心数据
loadCoreData()
delay(100)
// 重新加载扩展数据
loadExtendedData(context)
// 检查更新
@@ -291,10 +317,64 @@ class HomeViewModel : ViewModel() {
} catch (_: Exception) {
}
}
} catch (_: Exception) {
// 静默处理错误
} finally {
isRefreshing = false
}
}
}
// 手动触发刷新(下拉刷新使用)
fun onPullRefresh(context: Context) {
refreshData(context, forceRefresh = true)
}
// 自动刷新数据(当检测到变化时)
fun autoRefreshIfNeeded(context: Context) {
viewModelScope.launch {
// 检查是否需要刷新数据
val needsRefresh = checkIfDataNeedsRefresh()
if (needsRefresh) {
refreshData(context)
}
}
}
private suspend fun checkIfDataNeedsRefresh(): Boolean {
return withContext(Dispatchers.IO) {
try {
// 检查KSU状态是否发生变化
val currentKsuVersion = try {
if (Natives.becomeManager(ksuApp.packageName ?: "com.sukisu.ultra")) {
Natives.version
} else null
} catch (_: Exception) {
null
}
// 如果KSU版本发生变化需要刷新
if (currentKsuVersion != systemStatus.ksuVersion) {
return@withContext true
}
// 检查模块数量是否发生变化
val currentModuleCount = try {
getModuleCount()
} catch (_: Exception) {
systemInfo.moduleCount
}
if (currentModuleCount != systemInfo.moduleCount) {
return@withContext true
}
false
} catch (_: Exception) {
false
}
}
}
private suspend fun loadBasicSystemInfo(context: Context): Tuple5<String, String, String, Pair<String, Long>, String> {
return withContext(Dispatchers.IO) {

View File

@@ -1,3 +1,8 @@
libksud.so
libkernelsu.so
libsusfsd.so
libuid_scanner.so
libzakosign.so
libandroidx.graphics.path.so
libmmrl-file-manager.so
libmmrl-kernelsu.so