manager: increase appGroup performance & add appGroup status notice
This commit is contained in:
@@ -6,6 +6,7 @@ import androidx.compose.animation.core.*
|
|||||||
import androidx.compose.animation.expandHorizontally
|
import androidx.compose.animation.expandHorizontally
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
@@ -31,6 +32,7 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
|||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -38,12 +40,15 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.draw.scale
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
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.TextAlign
|
||||||
@@ -51,6 +56,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
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 coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
|
import coil.compose.rememberAsyncImagePainter
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.dergoogler.mmrl.ui.component.LabelItem
|
import com.dergoogler.mmrl.ui.component.LabelItem
|
||||||
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
||||||
@@ -314,6 +320,9 @@ private fun SuperUserContent(
|
|||||||
scope: CoroutineScope
|
scope: CoroutineScope
|
||||||
) {
|
) {
|
||||||
val expandedGroups = remember { mutableStateOf(setOf<Int>()) }
|
val expandedGroups = remember { mutableStateOf(setOf<Int>()) }
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val targetSizePx = remember(density) { with(density) { 36.dp.roundToPx() } }
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
PullToRefreshBox(
|
PullToRefreshBox(
|
||||||
modifier = Modifier.padding(innerPadding),
|
modifier = Modifier.padding(innerPadding),
|
||||||
@@ -329,6 +338,7 @@ private fun SuperUserContent(
|
|||||||
filteredAndSortedAppGroups.forEachIndexed { _, appGroup ->
|
filteredAndSortedAppGroups.forEachIndexed { _, appGroup ->
|
||||||
item(key = "${appGroup.uid}-${appGroup.mainApp.packageName}") {
|
item(key = "${appGroup.uid}-${appGroup.mainApp.packageName}") {
|
||||||
AppGroupItem(
|
AppGroupItem(
|
||||||
|
expandedGroups = expandedGroups,
|
||||||
appGroup = appGroup,
|
appGroup = appGroup,
|
||||||
isSelected = appGroup.packageNames.any { viewModel.selectedApps.contains(it) },
|
isSelected = appGroup.packageNames.any { viewModel.selectedApps.contains(it) },
|
||||||
onToggleSelection = {
|
onToggleSelection = {
|
||||||
@@ -357,32 +367,46 @@ private fun SuperUserContent(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (appGroup.apps.size <= 1) return@forEachIndexed
|
||||||
|
|
||||||
items(appGroup.apps, key = { "${it.packageName}-${it.uid}" }) { app ->
|
items(appGroup.apps, key = { "${it.packageName}-${it.uid}" }) { app ->
|
||||||
|
val painter = rememberAsyncImagePainter(
|
||||||
|
model = ImageRequest.Builder(context)
|
||||||
|
.data(app.packageInfo)
|
||||||
|
.size(targetSizePx)
|
||||||
|
.crossfade(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
|
val listItemContent = remember(app.packageName, appGroup.uid) {
|
||||||
|
@Composable {
|
||||||
|
ListItem(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable { navigator.navigate(AppProfileScreenDestination(app)) }
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(start = 10.dp),
|
||||||
|
headlineContent = { Text(app.label, style = MaterialTheme.typography.bodyMedium) },
|
||||||
|
supportingContent = { Text(app.packageName, style = MaterialTheme.typography.bodySmall) },
|
||||||
|
leadingContent = {
|
||||||
|
Image(
|
||||||
|
painter = painter,
|
||||||
|
contentDescription = app.label,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(4.dp)
|
||||||
|
.size(36.dp),
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = expandedGroups.value.contains(appGroup.uid) && appGroup.apps.size > 1,
|
visible = expandedGroups.value.contains(appGroup.uid),
|
||||||
enter = fadeIn() + expandVertically(),
|
enter = fadeIn() + expandVertically(),
|
||||||
exit = fadeOut() + shrinkVertically()
|
exit = fadeOut() + shrinkVertically()
|
||||||
) {
|
) {
|
||||||
ListItem(
|
listItemContent()
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(start = 10.dp)
|
|
||||||
.clickable {
|
|
||||||
navigator.navigate(AppProfileScreenDestination(app))
|
|
||||||
},
|
|
||||||
headlineContent = { Text(app.label, style = MaterialTheme.typography.bodyMedium) },
|
|
||||||
supportingContent = { Text(app.packageName, style = MaterialTheme.typography.bodySmall) },
|
|
||||||
leadingContent = {
|
|
||||||
AsyncImage(
|
|
||||||
model = ImageRequest.Builder(LocalContext.current)
|
|
||||||
.data(app.packageInfo)
|
|
||||||
.crossfade(true)
|
|
||||||
.build(),
|
|
||||||
contentDescription = app.label,
|
|
||||||
modifier = Modifier.padding(4.dp).width(36.dp).height(36.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -797,7 +821,8 @@ private fun AppGroupItem(
|
|||||||
onToggleSelection: () -> Unit,
|
onToggleSelection: () -> Unit,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
viewModel: SuperUserViewModel
|
viewModel: SuperUserViewModel,
|
||||||
|
expandedGroups: MutableState<Set<Int>>
|
||||||
) {
|
) {
|
||||||
val mainApp = appGroup.mainApp
|
val mainApp = appGroup.mainApp
|
||||||
|
|
||||||
@@ -818,9 +843,27 @@ private fun AppGroupItem(
|
|||||||
} else {
|
} else {
|
||||||
mainApp.packageName
|
mainApp.packageName
|
||||||
}
|
}
|
||||||
Text(summaryText)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(summaryText)
|
||||||
|
|
||||||
|
if (appGroup.apps.size > 1) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.KeyboardArrowDown,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.rotate(
|
||||||
|
animateFloatAsState(
|
||||||
|
targetValue = if (expandedGroups.value.contains(appGroup.uid)) 180f else 0f,
|
||||||
|
animationSpec = tween(200, easing = LinearOutSlowInEasing),
|
||||||
|
label = ""
|
||||||
|
).value
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
FlowRow(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
if (appGroup.allowSu) {
|
if (appGroup.allowSu) {
|
||||||
@@ -853,7 +896,7 @@ private fun AppGroupItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (appGroup.apps.size > 1) {
|
if (appGroup.apps.size > 1) {
|
||||||
Natives.getUserName(appGroup.uid)?.let {
|
appGroup.userName?.let {
|
||||||
LabelItem(
|
LabelItem(
|
||||||
text = it,
|
text = it,
|
||||||
style = LabelItemDefaults.style.copy(
|
style = LabelItemDefaults.style.copy(
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import com.topjohnwu.superuser.Shell
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.LinkedBlockingQueue
|
import java.util.concurrent.LinkedBlockingQueue
|
||||||
@@ -27,6 +26,8 @@ import java.util.concurrent.TimeUnit
|
|||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
import com.sukisu.zako.IKsuInterface
|
import com.sukisu.zako.IKsuInterface
|
||||||
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
enum class AppCategory(val displayNameRes: Int, val persistKey: String) {
|
enum class AppCategory(val displayNameRes: Int, val persistKey: String) {
|
||||||
ALL(com.sukisu.ultra.R.string.category_all_apps, "ALL"),
|
ALL(com.sukisu.ultra.R.string.category_all_apps, "ALL"),
|
||||||
@@ -77,31 +78,36 @@ class SuperUserViewModel : ViewModel() {
|
|||||||
private const val BATCH_SIZE = 20
|
private const val BATCH_SIZE = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class AppInfo(
|
data class AppInfo(
|
||||||
val label: String,
|
val label: String,
|
||||||
val packageInfo: PackageInfo,
|
val packageInfo: PackageInfo,
|
||||||
val profile: Natives.Profile?,
|
val profile: Natives.Profile?,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val packageName: String get() = packageInfo.packageName
|
@IgnoredOnParcel
|
||||||
val uid: Int get() = packageInfo.applicationInfo!!.uid
|
val packageName: String = packageInfo.packageName
|
||||||
|
@IgnoredOnParcel
|
||||||
|
val uid: Int = packageInfo.applicationInfo!!.uid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class AppGroup(
|
data class AppGroup(
|
||||||
val uid: Int,
|
val uid: Int,
|
||||||
val apps: List<AppInfo>,
|
val apps: List<AppInfo>,
|
||||||
val profile: Natives.Profile?
|
val profile: Natives.Profile?
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
val mainApp: AppInfo get() = apps.first()
|
@IgnoredOnParcel
|
||||||
val packageNames: List<String> get() = apps.map { it.packageName }
|
val mainApp: AppInfo = apps.first()
|
||||||
val allowSu: Boolean get() = profile?.allowSu == true
|
@IgnoredOnParcel
|
||||||
|
val packageNames: List<String> = apps.map { it.packageName }
|
||||||
val userName: String? get() = Natives.getUserName(uid)
|
@IgnoredOnParcel
|
||||||
val hasCustomProfile: Boolean
|
val allowSu: Boolean = profile?.allowSu == true
|
||||||
get() = profile?.let {
|
@IgnoredOnParcel
|
||||||
if (it.allowSu) !it.rootUseDefault else !it.nonRootUseDefault
|
val userName: String? = Natives.getUserName(uid)
|
||||||
} ?: false
|
@IgnoredOnParcel
|
||||||
|
val hasCustomProfile : Boolean = profile?.let { if (it.allowSu) !it.rootUseDefault else !it.nonRootUseDefault } ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
private val appProcessingThreadPool = ThreadPoolExecutor(
|
private val appProcessingThreadPool = ThreadPoolExecutor(
|
||||||
|
|||||||
Reference in New Issue
Block a user