Skip to content

Commit 700e4db

Browse files
Merge branch 'dev' into feat/susfs-universal-control
2 parents de196ae + b72f3d6 commit 700e4db

6 files changed

Lines changed: 346 additions & 58 deletions

File tree

app/src/main/java/com/abk/kernel/ui/screens/RuntimeScreens.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,6 @@ fun InstalledModulesScreen(
273273
val modules = remember(state.abkRuntimeStatus?.modules, query) {
274274
state.abkRuntimeStatus?.modules.orEmpty()
275275
.filter { it.matchesRuntimeModuleQuery(query) }
276-
.sortedWith(
277-
compareBy<AbkRuntimeModule> { it.typeOrder() }
278-
.thenBy { !it.enabled }
279-
.thenBy { it.displayName().lowercase() }
280-
)
281276
}
282277
val groupedModules = remember(modules) { groupRuntimeModulesForDisplay(modules) }
283278

@@ -1378,13 +1373,6 @@ private fun AbkRuntimeModule.normalizedType(): String =
13781373
private fun AbkRuntimeModule.canUninstallRuntimeModule(): Boolean =
13791374
normalizedType() == "standard" && controllable && !readonly
13801375

1381-
private fun AbkRuntimeModule.typeOrder(): Int = when (normalizedType()) {
1382-
"builtin" -> 0
1383-
"standard" -> 1
1384-
"kpm" -> 2
1385-
else -> 3
1386-
}
1387-
13881376
private data class RuntimeModuleDisplayGroup(
13891377
val groupName: String? = null,
13901378
val groupDescription: String? = null,

app/src/main/java/com/abk/kernel/utils/RootUtils.kt

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -757,23 +757,41 @@ object RootUtils {
757757
val packageManager = context.packageManager
758758
val apps = installedApplications(packageManager)
759759
val grantedUids = AbkKsuNative.grantedUids()
760+
return prepareRootGrantAppsForDisplay(
761+
apps = apps
762+
.asSequence()
763+
.filter { it.packageName.isNotBlank() }
764+
.mapNotNull { appInfo ->
765+
val packageName = appInfo.packageName ?: return@mapNotNull null
766+
val uid = appInfo.uid
767+
RootGrantApp(
768+
packageName = packageName,
769+
label = runCatching {
770+
packageManager.getApplicationLabel(appInfo).toString()
771+
}.getOrDefault(packageName),
772+
uid = uid,
773+
userName = AbkKsuNative.userName(uid),
774+
isSystemApp = (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0,
775+
profile = buildRootGrantListProfile(packageName, uid, grantedUids)
776+
)
777+
}
778+
.toList(),
779+
selfPackageName = context.packageName
780+
)
781+
}
782+
783+
internal fun prepareRootGrantAppsForDisplay(
784+
apps: List<RootGrantApp>,
785+
selfPackageName: String
786+
): List<RootGrantApp> {
787+
val cleanSelfPackage = selfPackageName.trim()
760788
return apps
761789
.asSequence()
762-
.filter { it.packageName.isNotBlank() }
763-
.mapNotNull { appInfo ->
764-
val packageName = appInfo.packageName ?: return@mapNotNull null
765-
val uid = appInfo.uid
766-
RootGrantApp(
767-
packageName = packageName,
768-
label = runCatching {
769-
packageManager.getApplicationLabel(appInfo).toString()
770-
}.getOrDefault(packageName),
771-
uid = uid,
772-
userName = AbkKsuNative.userName(uid),
773-
isSystemApp = (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0,
774-
profile = buildRootGrantListProfile(packageName, uid, grantedUids)
775-
)
790+
.filterNot {
791+
cleanSelfPackage.isNotBlank() &&
792+
it.packageName.equals(cleanSelfPackage, ignoreCase = true)
776793
}
794+
.filter { it.packageName.isNotBlank() }
777795
.distinctBy { "${it.uid}:${it.packageName}" }
778796
.sortedWith(
779797
compareByDescending<RootGrantApp> { it.profile.allowSu }

app/src/main/java/com/abk/kernel/viewmodel/RuntimeCoordinator.kt

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ class RuntimeCoordinator(
112112
managerAccessState = access.toUiState(),
113113
managerAccessError = if (access.hasNativeManagerPermission) access.diagnostic else runtimeError,
114114
hasNativeManagerPermission = access.hasNativeManagerPermission,
115-
abkRuntimeStatus = runtimeStatus,
115+
abkRuntimeStatus = runtimeStatus.copy(
116+
modules = sortRuntimeModulesForDisplay(runtimeStatus.modules)
117+
),
116118
abkRuntimeLoading = false,
117119
abkRuntimeError = if (access.hasNativeManagerPermission) null else runtimeError
118120
)
@@ -368,33 +370,9 @@ class RuntimeCoordinator(
368370
updateState { state ->
369371
if (result.first) {
370372
val savedProfile = profile.copy(name = cleanPackage)
371-
state.copy(
373+
state.applySavedRootGrantProfile(cleanPackage, savedProfile).copy(
372374
rootGrantSavingPackage = null,
373-
rootGrantError = null,
374-
rootGrantApps = state.rootGrantApps.map { app ->
375-
if (app.packageName == cleanPackage) {
376-
app.copy(
377-
profile = app.profile.copy(
378-
name = cleanPackage,
379-
currentUid = app.uid,
380-
allowSu = savedProfile.allowSu
381-
),
382-
profileLoaded = false
383-
)
384-
} else {
385-
app
386-
}
387-
},
388-
rootGrantDetailApp = state.rootGrantDetailApp?.let { app ->
389-
if (app.packageName == cleanPackage) {
390-
app.copy(
391-
profile = savedProfile,
392-
profileLoaded = true
393-
)
394-
} else {
395-
app
396-
}
397-
}
375+
rootGrantError = null
398376
)
399377
} else {
400378
state.copy(
@@ -403,7 +381,6 @@ class RuntimeCoordinator(
403381
)
404382
}
405383
}
406-
if (result.first) refreshRootGrantApps(force = true)
407384
}
408385
}
409386

@@ -458,8 +435,11 @@ class RuntimeCoordinator(
458435
)
459436
}
460437
} else {
461-
updateState { it.copy(abkRuntimeModuleActionId = null) }
462-
refreshAbkRuntimeStatus()
438+
updateState {
439+
it.applyRuntimeModuleEnabled(cleanId, enabled).copy(
440+
abkRuntimeModuleActionId = null
441+
)
442+
}
463443
}
464444
}
465445
}
@@ -499,8 +479,11 @@ class RuntimeCoordinator(
499479
)
500480
}
501481
} else {
502-
updateState { it.copy(abkRuntimeModuleActionId = null) }
503-
refreshAbkRuntimeStatus()
482+
updateState {
483+
it.applyRuntimeModulePendingUninstall(cleanId, pending).copy(
484+
abkRuntimeModuleActionId = null
485+
)
486+
}
504487
}
505488
}
506489
}
@@ -721,3 +704,80 @@ private operator fun <A, B, C, D> RuntimeQuadruple<A, B, C, D>.component1() = fi
721704
private operator fun <A, B, C, D> RuntimeQuadruple<A, B, C, D>.component2() = second
722705
private operator fun <A, B, C, D> RuntimeQuadruple<A, B, C, D>.component3() = third
723706
private operator fun <A, B, C, D> RuntimeQuadruple<A, B, C, D>.component4() = fourth
707+
708+
internal fun MainUiState.applySavedRootGrantProfile(
709+
packageName: String,
710+
savedProfile: RootGrantProfile
711+
): MainUiState {
712+
val cleanPackage = packageName.trim()
713+
if (cleanPackage.isBlank()) return this
714+
val detailMatches = rootGrantDetailApp?.packageName == cleanPackage
715+
return copy(
716+
rootGrantApps = rootGrantApps.map { app ->
717+
if (app.packageName != cleanPackage) {
718+
app
719+
} else {
720+
val profileLoaded = app.profileLoaded || detailMatches
721+
val profile = if (profileLoaded) {
722+
savedProfile.copy(
723+
name = cleanPackage,
724+
currentUid = app.uid
725+
)
726+
} else {
727+
app.profile.copy(
728+
name = cleanPackage,
729+
currentUid = app.uid,
730+
allowSu = savedProfile.allowSu
731+
)
732+
}
733+
app.copy(
734+
profile = profile,
735+
profileLoaded = profileLoaded
736+
)
737+
}
738+
},
739+
rootGrantDetailApp = rootGrantDetailApp?.let { app ->
740+
if (app.packageName == cleanPackage) {
741+
app.copy(
742+
profile = savedProfile.copy(
743+
name = cleanPackage,
744+
currentUid = app.uid
745+
),
746+
profileLoaded = true
747+
)
748+
} else {
749+
app
750+
}
751+
}
752+
)
753+
}
754+
755+
internal fun MainUiState.applyRuntimeModuleEnabled(
756+
moduleId: String,
757+
enabled: Boolean
758+
): MainUiState = applyRuntimeModulePatch(moduleId) { module ->
759+
module.copy(enabled = enabled)
760+
}
761+
762+
internal fun MainUiState.applyRuntimeModulePendingUninstall(
763+
moduleId: String,
764+
pending: Boolean
765+
): MainUiState = applyRuntimeModulePatch(moduleId) { module ->
766+
module.copy(remove = pending)
767+
}
768+
769+
private inline fun MainUiState.applyRuntimeModulePatch(
770+
moduleId: String,
771+
transform: (AbkRuntimeModule) -> AbkRuntimeModule
772+
): MainUiState {
773+
val cleanId = moduleId.trim()
774+
val status = abkRuntimeStatus ?: return this
775+
if (cleanId.isBlank()) return this
776+
return copy(
777+
abkRuntimeStatus = status.copy(
778+
modules = status.modules.map { module ->
779+
if (module.id == cleanId) transform(module) else module
780+
}
781+
)
782+
)
783+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.abk.kernel.viewmodel
2+
3+
import com.abk.kernel.data.model.AbkRuntimeModule
4+
5+
internal fun sortRuntimeModulesForDisplay(modules: List<AbkRuntimeModule>): List<AbkRuntimeModule> =
6+
modules.sortedWith(
7+
compareBy<AbkRuntimeModule> { it.runtimeTypeOrder() }
8+
.thenBy { !it.enabled }
9+
.thenBy { it.runtimeDisplayName().lowercase() }
10+
)
11+
12+
internal fun AbkRuntimeModule.runtimeDisplayName(): String =
13+
name.ifBlank { id.ifBlank { runtimeRepoName() } }
14+
15+
private fun AbkRuntimeModule.runtimeRepoName(): String =
16+
repoUrl
17+
.trim()
18+
.trimEnd('/')
19+
.removeSuffix(".git")
20+
.substringAfterLast('/')
21+
.ifBlank { "unknown" }
22+
23+
private fun AbkRuntimeModule.runtimeTypeOrder(): Int = when (normalizedType()) {
24+
"builtin" -> 0
25+
"standard" -> 1
26+
"kpm" -> 2
27+
else -> 3
28+
}

app/src/test/java/com/abk/kernel/utils/RootUtilsRootGrantProfileTest.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.abk.kernel.utils
22

33
import com.abk.kernel.data.model.ROOT_PROFILE_FLAG_NO_NEW_PRIVS
4+
import com.abk.kernel.data.model.RootGrantApp
5+
import com.abk.kernel.data.model.RootGrantProfile
46
import org.junit.Assert.assertEquals
57
import org.junit.Assert.assertFalse
68
import org.junit.Assert.assertTrue
@@ -59,4 +61,34 @@ class RootUtilsRootGrantProfileTest {
5961
assertEquals(10123, first.currentUid)
6062
assertEquals(10123, second.currentUid)
6163
}
64+
65+
@Test
66+
fun prepareRootGrantAppsForDisplayExcludesSelfAndKeepsDisplaySorting() {
67+
val displayed = RootUtils.prepareRootGrantAppsForDisplay(
68+
apps = listOf(
69+
RootGrantApp(
70+
packageName = "com.abk.kernel",
71+
label = "ABK",
72+
uid = 1000,
73+
profile = RootGrantProfile(allowSu = true)
74+
),
75+
RootGrantApp(
76+
packageName = "com.example.beta",
77+
label = "Beta",
78+
uid = 1002,
79+
profile = RootGrantProfile(allowSu = false)
80+
),
81+
RootGrantApp(
82+
packageName = "com.example.alpha",
83+
label = "Alpha",
84+
uid = 1001,
85+
profile = RootGrantProfile(allowSu = true)
86+
)
87+
),
88+
selfPackageName = "com.abk.kernel"
89+
)
90+
91+
assertEquals(listOf("com.example.alpha", "com.example.beta"), displayed.map { it.packageName })
92+
assertTrue(displayed.none { it.packageName == "com.abk.kernel" })
93+
}
6294
}

0 commit comments

Comments
 (0)