manager: replace SwipeRefresh with PullRefreshIndicator & refactor so… (#288)

- replace SwipeRefresh with PullRefreshIndicator
- optimize pull refresh
- refactor some code
- fix install bottom in module page again
This commit is contained in:
TinyHai
2023-03-02 12:35:41 +08:00
committed by GitHub
parent 8bbfe0c26d
commit 7846b2a440
6 changed files with 170 additions and 112 deletions

View File

@@ -62,10 +62,10 @@ dependencies {
val accompanistVersion = "0.28.0" val accompanistVersion = "0.28.0"
val composeDestinationsVersion = "1.7.27-beta" val composeDestinationsVersion = "1.7.27-beta"
implementation(platform("androidx.compose:compose-bom:2022.12.00")) implementation(platform("androidx.compose:compose-bom:2022.12.00"))
debugImplementation("androidx.compose.ui:ui-test-manifest") debugImplementation("androidx.compose.ui:ui-test-manifest")
debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-tooling")
implementation("androidx.activity:activity-compose:1.6.1") implementation("androidx.activity:activity-compose:1.6.1")
implementation("androidx.compose.material:material:1.4.0-beta02")
implementation("androidx.compose.material:material-icons-extended") implementation("androidx.compose.material:material-icons-extended")
implementation("androidx.compose.material3:material3") implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui")
@@ -75,7 +75,6 @@ dependencies {
implementation("androidx.navigation:navigation-compose:2.5.3") implementation("androidx.navigation:navigation-compose:2.5.3")
implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion") implementation("com.google.accompanist:accompanist-drawablepainter:$accompanistVersion")
implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion") implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion")
implementation("com.google.accompanist:accompanist-swiperefresh:$accompanistVersion")
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion") implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
implementation("io.github.raamcosta.compose-destinations:animations-core:$composeDestinationsVersion") implementation("io.github.raamcosta.compose-destinations:animations-core:$composeDestinationsVersion")

View File

@@ -16,12 +16,14 @@ import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import com.ramcosta.composedestinations.DestinationsNavHost import com.ramcosta.composedestinations.DestinationsNavHost
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.screen.BottomBarDestination import me.weishu.kernelsu.ui.screen.BottomBarDestination
import me.weishu.kernelsu.ui.screen.NavGraphs import me.weishu.kernelsu.ui.screen.NavGraphs
import me.weishu.kernelsu.ui.screen.appCurrentDestinationAsState import me.weishu.kernelsu.ui.screen.appCurrentDestinationAsState
import me.weishu.kernelsu.ui.screen.destinations.Destination import me.weishu.kernelsu.ui.screen.destinations.Destination
import me.weishu.kernelsu.ui.screen.startAppDestination import me.weishu.kernelsu.ui.screen.startAppDestination
import me.weishu.kernelsu.ui.theme.KernelSUTheme import me.weishu.kernelsu.ui.theme.KernelSUTheme
import me.weishu.kernelsu.ui.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.LocalSnackbarHost
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -38,7 +40,10 @@ class MainActivity : ComponentActivity() {
bottomBar = { BottomBar(navController) }, bottomBar = { BottomBar(navController) },
snackbarHost = { SnackbarHost(snackbarHostState) } snackbarHost = { SnackbarHost(snackbarHostState) }
) { innerPadding -> ) { innerPadding ->
CompositionLocalProvider(LocalSnackbarHost provides snackbarHostState) { CompositionLocalProvider(
LocalSnackbarHost provides snackbarHostState,
LocalDialogHost provides rememberDialogHostState(),
) {
DestinationsNavHost( DestinationsNavHost(
modifier = Modifier.padding(innerPadding), modifier = Modifier.padding(innerPadding),
navGraph = NavGraphs.root, navGraph = NavGraphs.root,

View File

@@ -8,8 +8,12 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
@@ -17,14 +21,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -32,7 +35,6 @@ import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.ConfirmDialog import me.weishu.kernelsu.ui.component.ConfirmDialog
import me.weishu.kernelsu.ui.component.DialogResult import me.weishu.kernelsu.ui.component.DialogResult
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination
import me.weishu.kernelsu.ui.util.* import me.weishu.kernelsu.ui.util.*
import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
@@ -42,9 +44,6 @@ import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel
@Composable @Composable
fun ModuleScreen(navigator: DestinationsNavigator) { fun ModuleScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<ModuleViewModel>() val viewModel = viewModel<ModuleViewModel>()
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (viewModel.moduleList.isEmpty()) { if (viewModel.moduleList.isEmpty()) {
@@ -53,9 +52,10 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} }
val isSafeMode = Natives.isSafeMode() val isSafeMode = Natives.isSafeMode()
val isKSUVersionInvalid = Natives.getVersion() < 0
val hasMagisk = hasMagisk() val hasMagisk = hasMagisk()
val hideInstallButton = isSafeMode || hasMagisk val hideInstallButton = isSafeMode || isKSUVersionInvalid || hasMagisk
Scaffold(topBar = { Scaffold(topBar = {
TopBar() TopBar()
@@ -91,54 +91,46 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} }
}) { innerPadding -> }) { innerPadding ->
val dialogState = rememberDialogHostState() ConfirmDialog()
ConfirmDialog(dialogState)
when {
isKSUVersionInvalid -> {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.require_kernel_version_8))
}
}
hasMagisk -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(24.dp),
contentAlignment = Alignment.Center
) {
Text(
stringResource(R.string.module_magisk_conflict),
textAlign = TextAlign.Center,
)
}
}
else -> {
ModuleList(
viewModel = viewModel,
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
)
}
}
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun ModuleList(viewModel: ModuleViewModel, modifier: Modifier = Modifier) {
val failedEnable = stringResource(R.string.module_failed_to_enable) val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable) val failedDisable = stringResource(R.string.module_failed_to_disable)
val failedUninstall = stringResource(R.string.module_uninstall_failed) val failedUninstall = stringResource(R.string.module_uninstall_failed)
val successUninstall = stringResource(R.string.module_uninstall_success) val successUninstall = stringResource(R.string.module_uninstall_success)
val swipeState = rememberSwipeRefreshState(viewModel.isRefreshing)
// TODO: Replace SwipeRefresh with RefreshIndicator when it's ready
if (Natives.getVersion() < 8) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.require_kernel_version_8))
}
return@Scaffold
}
if (hasMagisk) {
Box(modifier = Modifier.fillMaxSize().padding(24.dp), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_magisk_conflict))
}
return@Scaffold
}
SwipeRefresh(
state = swipeState, onRefresh = {
scope.launch { viewModel.fetchModuleList() }
}, modifier = Modifier
.padding(innerPadding)
.padding(16.dp)
.fillMaxSize()
) {
val isOverlayAvailable = overlayFsAvailable()
if (!isOverlayAvailable) {
swipeState.isRefreshing = false
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_overlay_fs_not_available))
}
return@SwipeRefresh
}
val isEmpty = viewModel.moduleList.isEmpty()
if (isEmpty) {
swipeState.isRefreshing = false
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_empty))
}
} else {
LazyColumn(verticalArrangement = Arrangement.spacedBy(15.dp),
contentPadding = remember { PaddingValues(bottom = 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) }) {
items(viewModel.moduleList) { module ->
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
val reboot = stringResource(id = R.string.reboot) val reboot = stringResource(id = R.string.reboot)
val rebootToApply = stringResource(id = R.string.reboot_to_apply) val rebootToApply = stringResource(id = R.string.reboot_to_apply)
val moduleStr = stringResource(id = R.string.module) val moduleStr = stringResource(id = R.string.module)
@@ -146,16 +138,19 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
val cancel = stringResource(id = android.R.string.cancel) val cancel = stringResource(id = android.R.string.cancel)
val moduleUninstallConfirm = val moduleUninstallConfirm =
stringResource(id = R.string.module_uninstall_confirm) stringResource(id = R.string.module_uninstall_confirm)
ModuleItem(module, isChecked, onUninstall = {
scope.launch { val dialogHost = LocalDialogHost.current
val dialogResult = dialogState.showDialog( val snackBarHost = LocalSnackbarHost.current
suspend fun onModuleUninstall(module: ModuleViewModel.ModuleInfo) {
val dialogResult = dialogHost.showDialog(
moduleStr, moduleStr,
content = moduleUninstallConfirm.format(module.name), content = moduleUninstallConfirm.format(module.name),
confirm = uninstall, confirm = uninstall,
dismiss = cancel dismiss = cancel
) )
if (dialogResult != DialogResult.Confirmed) { if (dialogResult != DialogResult.Confirmed) {
return@launch return
} }
val success = uninstallModule(module.id) val success = uninstallModule(module.id)
@@ -172,13 +167,39 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} else { } else {
null null
} }
val result = snackBarHost.showSnackbar( val result = snackBarHost.showSnackbar(message, actionLabel = actionLabel)
message, actionLabel = actionLabel
)
if (result == SnackbarResult.ActionPerformed) { if (result == SnackbarResult.ActionPerformed) {
reboot() reboot()
} }
} }
val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing,
onRefresh = { viewModel.fetchModuleList() }
)
Box(modifier.pullRefresh(refreshState).padding(16.dp)) {
if (viewModel.isOverlayAvailable) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(15.dp),
contentPadding = remember { PaddingValues(bottom = 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) },
) {
val isEmpty = viewModel.moduleList.isEmpty()
if (isEmpty) {
item {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(stringResource(R.string.module_empty))
}
}
} else {
items(viewModel.moduleList) { module ->
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
val scope = rememberCoroutineScope()
ModuleItem(module, isChecked, onUninstall = {
scope.launch { onModuleUninstall(module) }
}, onCheckChanged = { }, onCheckChanged = {
val success = toggleModule(module.id, !isChecked) val success = toggleModule(module.id, !isChecked)
if (success) { if (success) {
@@ -203,8 +224,20 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} }
} }
} }
} else {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(stringResource(R.string.module_overlay_fs_not_available))
} }
} }
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(
Alignment.TopCenter
)
)
}
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)

View File

@@ -2,7 +2,6 @@ package me.weishu.kernelsu.ui.screen
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.widget.Toast
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
@@ -17,12 +16,12 @@ import com.alorma.compose.settings.ui.*
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.weishu.kernelsu.ui.util.getBugreportFile
import me.weishu.kernelsu.BuildConfig import me.weishu.kernelsu.BuildConfig
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SimpleDialog import me.weishu.kernelsu.ui.component.SimpleDialog
import me.weishu.kernelsu.ui.component.rememberDialogHostState
import me.weishu.kernelsu.ui.util.LinkifyText import me.weishu.kernelsu.ui.util.LinkifyText
import me.weishu.kernelsu.ui.util.LocalDialogHost
import me.weishu.kernelsu.ui.util.getBugreportFile
/** /**
@@ -42,9 +41,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
) { paddingValues -> ) { paddingValues ->
val dialogState = rememberDialogHostState() SimpleDialog {
SimpleDialog(dialogState) {
SupportCard() SupportCard()
} }
@@ -75,12 +72,13 @@ fun SettingScreen(navigator: DestinationsNavigator) {
val about = stringResource(id = R.string.about) val about = stringResource(id = R.string.about)
val ok = stringResource(id = android.R.string.ok) val ok = stringResource(id = android.R.string.ok)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val dialogHost = LocalDialogHost.current
SettingsMenuLink(title = { SettingsMenuLink(title = {
Text(about) Text(about)
}, },
onClick = { onClick = {
scope.launch { scope.launch {
dialogState.showDialog(about, content = "unused", confirm = ok) dialogHost.showDialog(about, content = "unused", confirm = ok)
} }
} }
) )

View File

@@ -3,11 +3,16 @@ package me.weishu.kernelsu.ui.screen
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@@ -15,8 +20,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives import me.weishu.kernelsu.Natives
@@ -26,7 +29,7 @@ import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
import java.util.* import java.util.*
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Destination @Destination
@Composable @Composable
fun SuperUserScreen() { fun SuperUserScreen() {
@@ -87,19 +90,19 @@ fun SuperUserScreen() {
) )
} }
) { innerPadding -> ) { innerPadding ->
val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
// TODO: Replace SwipeRefresh with RefreshIndicator when it's ready val refreshState = rememberPullRefreshState(
SwipeRefresh( refreshing = viewModel.isRefreshing,
state = rememberSwipeRefreshState(viewModel.isRefreshing), onRefresh = { scope.launch { viewModel.fetchAppList() } },
onRefresh = { )
scope.launch { viewModel.fetchAppList() } Box(
},
modifier = Modifier modifier = Modifier
.padding(innerPadding) .padding(innerPadding)
.fillMaxSize() .pullRefresh(refreshState)
) { ) {
LazyColumn { val failMessage = stringResource(R.string.superuser_failed_to_grant_root)
LazyColumn(Modifier.fillMaxSize()) {
items(viewModel.appList, key = { it.packageName }) { app -> items(viewModel.appList, key = { it.packageName }) { app ->
var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) } var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) }
AppItem(app, isChecked) { checked -> AppItem(app, isChecked) { checked ->
@@ -112,6 +115,12 @@ fun SuperUserScreen() {
} }
} }
} }
PullRefreshIndicator(
refreshing = viewModel.isRefreshing,
state = refreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
} }
} }
} }

View File

@@ -7,9 +7,11 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.launch
import me.weishu.kernelsu.ui.util.listModules import me.weishu.kernelsu.ui.util.listModules
import me.weishu.kernelsu.ui.util.overlayFsAvailable
import org.json.JSONArray import org.json.JSONArray
import java.text.Collator import java.text.Collator
import java.util.* import java.util.*
@@ -36,6 +38,9 @@ class ModuleViewModel : ViewModel() {
var isRefreshing by mutableStateOf(false) var isRefreshing by mutableStateOf(false)
private set private set
var isOverlayAvailable by mutableStateOf(overlayFsAvailable())
private set
val moduleList by derivedStateOf { val moduleList by derivedStateOf {
val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id) val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
modules.sortedWith(comparator).also { modules.sortedWith(comparator).also {
@@ -43,12 +48,16 @@ class ModuleViewModel : ViewModel() {
} }
} }
suspend fun fetchModuleList() { fun fetchModuleList() {
withContext(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
isRefreshing = true isRefreshing = true
val oldModuleList = modules
val start = SystemClock.elapsedRealtime() val start = SystemClock.elapsedRealtime()
kotlin.runCatching { kotlin.runCatching {
isOverlayAvailable = overlayFsAvailable()
val result = listModules() val result = listModules()
@@ -76,6 +85,11 @@ class ModuleViewModel : ViewModel() {
isRefreshing = false isRefreshing = false
} }
// when both old and new is kotlin.collections.EmptyList
// moduleList update will don't trigger
if (oldModuleList === modules) {
isRefreshing = false
}
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules") Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}, modules: $modules")
} }