From c4ff89c13d9da16d0183cb08a398cd5d6299f3b4 Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Tue, 7 Oct 2025 01:29:00 +0800 Subject: [PATCH] manager: Add pull-to-refresh functionality --- .../java/com/sukisu/ultra/ui/screen/Home.kt | 24 ++++ .../ultra/ui/viewmodel/HomeViewModel.kt | 122 +++++++++++++++--- manager/app/src/main/jniLibs/.gitignore | 7 +- 3 files changed, 131 insertions(+), 22 deletions(-) diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt index fc13dfce..dcec2877 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt @@ -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() 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 diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt index 3628615a..38ed74a3 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt @@ -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 = _dataRefreshTrigger + private var loadingJobs = mutableListOf() + private var lastRefreshTime = 0L + private val refreshCooldown = 2000L fun loadUserSettings(context: Context) { viewModelScope.launch(Dispatchers.IO) { @@ -262,37 +270,109 @@ 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 - // 取消正在进行的加载任务 - loadingJobs.forEach { it.cancel() } - loadingJobs.clear() + try { + // 取消正在进行的加载任务 + loadingJobs.forEach { it.cancel() } + loadingJobs.clear() - // 重置状态 - isCoreDataLoaded = false - isExtendedDataLoaded = false + // 重置状态 + isCoreDataLoaded = false + isExtendedDataLoaded = false - // 重新加载 - loadCoreData() - delay(100) - loadExtendedData(context) + // 触发数据刷新状态流 + _dataRefreshTrigger.value = currentTime - // 检查更新 - val settingsPrefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - val checkUpdate = settingsPrefs.getBoolean("check_update", true) - if (checkUpdate) { - try { - val newVersionInfo = withContext(Dispatchers.IO) { - checkNewVersion() + // 重新加载用户设置 + loadUserSettings(context) + + // 重新加载核心数据 + loadCoreData() + delay(100) + + // 重新加载扩展数据 + loadExtendedData(context) + + // 检查更新 + val settingsPrefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) + val checkUpdate = settingsPrefs.getBoolean("check_update", true) + if (checkUpdate) { + try { + val newVersionInfo = withContext(Dispatchers.IO) { + checkNewVersion() + } + latestVersionInfo = newVersionInfo + } catch (_: Exception) { } - latestVersionInfo = newVersionInfo - } catch (_: Exception) { } + } catch (_: Exception) { + // 静默处理错误 + } finally { + isRefreshing = false } + } + } - 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 + } } } diff --git a/manager/app/src/main/jniLibs/.gitignore b/manager/app/src/main/jniLibs/.gitignore index 41fe0b59..939b9306 100644 --- a/manager/app/src/main/jniLibs/.gitignore +++ b/manager/app/src/main/jniLibs/.gitignore @@ -1,3 +1,8 @@ libksud.so libkernelsu.so -libandroidx.graphics.path.so \ No newline at end of file +libsusfsd.so +libuid_scanner.so +libzakosign.so +libandroidx.graphics.path.so +libmmrl-file-manager.so +libmmrl-kernelsu.so