mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
feat: improve InstalledAppsScreen caching/perf (#2963)
Co-authored-by: planshim <100317079+planshim@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -30,7 +31,7 @@ fun InstalledAppsScreen(
|
||||
onAppClick: (InstalledApp) -> Unit,
|
||||
viewModel: InstalledAppsViewModel = koinViewModel()
|
||||
) {
|
||||
val installedApps by viewModel.apps.collectAsStateWithLifecycle(initialValue = null)
|
||||
val installedApps by viewModel.apps.collectAsStateWithLifecycle()
|
||||
|
||||
Column {
|
||||
LazyColumnWithScrollbar(
|
||||
@@ -38,38 +39,40 @@ fun InstalledAppsScreen(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = if (installedApps.isNullOrEmpty()) Arrangement.Center else Arrangement.Top,
|
||||
) {
|
||||
installedApps?.let { installedApps ->
|
||||
if (installedApps.isNotEmpty()) {
|
||||
items(
|
||||
installedApps,
|
||||
key = { it.currentPackageName }
|
||||
) { installedApp ->
|
||||
viewModel.packageInfoMap[installedApp.currentPackageName].let { packageInfo ->
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onAppClick(installedApp) },
|
||||
leadingContent = {
|
||||
AppIcon(
|
||||
packageInfo,
|
||||
contentDescription = null,
|
||||
Modifier.size(36.dp)
|
||||
)
|
||||
},
|
||||
headlineContent = { AppLabel(packageInfo, defaultText = null) },
|
||||
supportingContent = { Text(installedApp.currentPackageName) }
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(R.string.no_patched_apps_found),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
}
|
||||
val apps = installedApps
|
||||
if (apps == null) {
|
||||
item(key = "LOADING") {
|
||||
LoadingIndicator()
|
||||
}
|
||||
} else if (apps.isNotEmpty()) {
|
||||
items(
|
||||
apps,
|
||||
key = { "APP-" + it.currentPackageName },
|
||||
contentType = { "APP" },
|
||||
) { installedApp ->
|
||||
val packageInfo = viewModel.packageInfoMap[installedApp.currentPackageName]
|
||||
|
||||
} ?: item { LoadingIndicator() }
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { onAppClick(installedApp) },
|
||||
leadingContent = {
|
||||
AppIcon(
|
||||
packageInfo,
|
||||
contentDescription = null,
|
||||
Modifier.size(36.dp)
|
||||
)
|
||||
},
|
||||
headlineContent = { AppLabel(packageInfo, defaultText = null) },
|
||||
supportingContent = { Text(installedApp.currentPackageName) }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item(key = "NONE") {
|
||||
Text(
|
||||
text = stringResource(R.string.no_patched_apps_found),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,16 @@ import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.data.room.apps.installed.InstallType
|
||||
import app.revanced.manager.domain.installer.RootServiceException
|
||||
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
||||
import app.revanced.manager.domain.installer.RootInstaller
|
||||
import app.revanced.manager.domain.installer.RootServiceException
|
||||
import app.revanced.manager.domain.repository.InstalledAppRepository
|
||||
import app.revanced.manager.util.PM
|
||||
import app.revanced.manager.util.collectEach
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@@ -20,32 +23,42 @@ class InstalledAppsViewModel(
|
||||
private val pm: PM,
|
||||
private val rootInstaller: RootInstaller
|
||||
) : ViewModel() {
|
||||
val apps = installedAppsRepository.getAll().flowOn(Dispatchers.IO)
|
||||
val apps = installedAppsRepository.getAll()
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(),
|
||||
initialValue = null,
|
||||
)
|
||||
|
||||
val packageInfoMap = mutableStateMapOf<String, PackageInfo?>()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
apps.collectEach { installedApp ->
|
||||
packageInfoMap[installedApp.currentPackageName] = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
if (
|
||||
installedApp.installType == InstallType.MOUNT && !rootInstaller.isAppInstalled(installedApp.currentPackageName)
|
||||
) {
|
||||
installedAppsRepository.delete(installedApp)
|
||||
return@withContext null
|
||||
}
|
||||
} catch (_: RootServiceException) { }
|
||||
apps.filterNotNull().collectLatest(::fetchPackageInfos)
|
||||
}
|
||||
}
|
||||
|
||||
val packageInfo = pm.getPackageInfo(installedApp.currentPackageName)
|
||||
|
||||
if (packageInfo == null && installedApp.installType != InstallType.MOUNT) {
|
||||
installedAppsRepository.delete(installedApp)
|
||||
private suspend fun fetchPackageInfos(apps: List<InstalledApp>) {
|
||||
for (app in apps) {
|
||||
packageInfoMap[app.currentPackageName] = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
if (app.installType == InstallType.MOUNT &&
|
||||
!rootInstaller.isAppInstalled(app.currentPackageName)
|
||||
) {
|
||||
installedAppsRepository.delete(app)
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
packageInfo
|
||||
} catch (_: RootServiceException) {
|
||||
}
|
||||
|
||||
val packageInfo = pm.getPackageInfo(app.currentPackageName)
|
||||
|
||||
if (packageInfo == null && app.installType != InstallType.MOUNT) {
|
||||
installedAppsRepository.delete(app)
|
||||
return@withContext null
|
||||
}
|
||||
|
||||
packageInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user