manager: Add pull-to-refresh functionality
This commit is contained in:
@@ -20,6 +20,7 @@ import androidx.compose.material.icons.filled.*
|
|||||||
import androidx.compose.material.icons.outlined.Block
|
import androidx.compose.material.icons.outlined.Block
|
||||||
import androidx.compose.material.icons.outlined.TaskAlt
|
import androidx.compose.material.icons.outlined.TaskAlt
|
||||||
import androidx.compose.material.icons.outlined.Warning
|
import androidx.compose.material.icons.outlined.Warning
|
||||||
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@@ -74,6 +75,13 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
val viewModel = viewModel<HomeViewModel>()
|
val viewModel = viewModel<HomeViewModel>()
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
|
refreshing = viewModel.isRefreshing,
|
||||||
|
onRefresh = {
|
||||||
|
viewModel.onPullRefresh(context)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
LaunchedEffect(key1 = navigator) {
|
LaunchedEffect(key1 = navigator) {
|
||||||
viewModel.loadUserSettings(context)
|
viewModel.loadUserSettings(context)
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
@@ -81,6 +89,21 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
delay(100)
|
delay(100)
|
||||||
viewModel.loadExtendedData(context)
|
viewModel.loadExtendedData(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动数据变化监听
|
||||||
|
coroutineScope.launch {
|
||||||
|
while (true) {
|
||||||
|
delay(5000) // 每5秒检查一次
|
||||||
|
viewModel.autoRefreshIfNeeded(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听数据刷新状态流
|
||||||
|
LaunchedEffect(viewModel.dataRefreshTrigger) {
|
||||||
|
viewModel.dataRefreshTrigger.collect { _ ->
|
||||||
|
// 数据刷新时的额外处理可以在这里添加
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
@@ -102,6 +125,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
.pullRefresh(pullRefreshState)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
class HomeViewModel : ViewModel() {
|
class HomeViewModel : ViewModel() {
|
||||||
|
|
||||||
@@ -90,7 +92,13 @@ class HomeViewModel : ViewModel() {
|
|||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
// 数据刷新状态流,用于监听变化
|
||||||
|
private val _dataRefreshTrigger = MutableStateFlow(0L)
|
||||||
|
val dataRefreshTrigger: StateFlow<Long> = _dataRefreshTrigger
|
||||||
|
|
||||||
private var loadingJobs = mutableListOf<Job>()
|
private var loadingJobs = mutableListOf<Job>()
|
||||||
|
private var lastRefreshTime = 0L
|
||||||
|
private val refreshCooldown = 2000L
|
||||||
|
|
||||||
fun loadUserSettings(context: Context) {
|
fun loadUserSettings(context: Context) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
@@ -262,37 +270,109 @@ class HomeViewModel : ViewModel() {
|
|||||||
loadingJobs.add(job)
|
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 {
|
viewModelScope.launch {
|
||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
|
|
||||||
// 取消正在进行的加载任务
|
try {
|
||||||
loadingJobs.forEach { it.cancel() }
|
// 取消正在进行的加载任务
|
||||||
loadingJobs.clear()
|
loadingJobs.forEach { it.cancel() }
|
||||||
|
loadingJobs.clear()
|
||||||
|
|
||||||
// 重置状态
|
// 重置状态
|
||||||
isCoreDataLoaded = false
|
isCoreDataLoaded = false
|
||||||
isExtendedDataLoaded = false
|
isExtendedDataLoaded = false
|
||||||
|
|
||||||
// 重新加载
|
// 触发数据刷新状态流
|
||||||
loadCoreData()
|
_dataRefreshTrigger.value = currentTime
|
||||||
delay(100)
|
|
||||||
loadExtendedData(context)
|
|
||||||
|
|
||||||
// 检查更新
|
// 重新加载用户设置
|
||||||
val settingsPrefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
loadUserSettings(context)
|
||||||
val checkUpdate = settingsPrefs.getBoolean("check_update", true)
|
|
||||||
if (checkUpdate) {
|
// 重新加载核心数据
|
||||||
try {
|
loadCoreData()
|
||||||
val newVersionInfo = withContext(Dispatchers.IO) {
|
delay(100)
|
||||||
checkNewVersion()
|
|
||||||
|
// 重新加载扩展数据
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
manager/app/src/main/jniLibs/.gitignore
vendored
7
manager/app/src/main/jniLibs/.gitignore
vendored
@@ -1,3 +1,8 @@
|
|||||||
libksud.so
|
libksud.so
|
||||||
libkernelsu.so
|
libkernelsu.so
|
||||||
libandroidx.graphics.path.so
|
libsusfsd.so
|
||||||
|
libuid_scanner.so
|
||||||
|
libzakosign.so
|
||||||
|
libandroidx.graphics.path.so
|
||||||
|
libmmrl-file-manager.so
|
||||||
|
libmmrl-kernelsu.so
|
||||||
|
|||||||
Reference in New Issue
Block a user