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.expandVertically
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
@@ -31,6 +32,7 @@ import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -38,12 +40,15 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.lifecycle.viewmodel.compose.viewModel
|
||||
import coil.compose.AsyncImage
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import coil.request.ImageRequest
|
||||
import com.dergoogler.mmrl.ui.component.LabelItem
|
||||
import com.dergoogler.mmrl.ui.component.LabelItemDefaults
|
||||
@@ -314,6 +320,9 @@ private fun SuperUserContent(
|
||||
scope: CoroutineScope
|
||||
) {
|
||||
val expandedGroups = remember { mutableStateOf(setOf<Int>()) }
|
||||
val density = LocalDensity.current
|
||||
val targetSizePx = remember(density) { with(density) { 36.dp.roundToPx() } }
|
||||
val context = LocalContext.current
|
||||
|
||||
PullToRefreshBox(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
@@ -329,6 +338,7 @@ private fun SuperUserContent(
|
||||
filteredAndSortedAppGroups.forEachIndexed { _, appGroup ->
|
||||
item(key = "${appGroup.uid}-${appGroup.mainApp.packageName}") {
|
||||
AppGroupItem(
|
||||
expandedGroups = expandedGroups,
|
||||
appGroup = appGroup,
|
||||
isSelected = appGroup.packageNames.any { viewModel.selectedApps.contains(it) },
|
||||
onToggleSelection = {
|
||||
@@ -357,32 +367,46 @@ private fun SuperUserContent(
|
||||
)
|
||||
}
|
||||
|
||||
if (appGroup.apps.size <= 1) return@forEachIndexed
|
||||
|
||||
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(
|
||||
visible = expandedGroups.value.contains(appGroup.uid) && appGroup.apps.size > 1,
|
||||
visible = expandedGroups.value.contains(appGroup.uid),
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
ListItem(
|
||||
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)
|
||||
)
|
||||
}
|
||||
)
|
||||
listItemContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,7 +821,8 @@ private fun AppGroupItem(
|
||||
onToggleSelection: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
viewModel: SuperUserViewModel
|
||||
viewModel: SuperUserViewModel,
|
||||
expandedGroups: MutableState<Set<Int>>
|
||||
) {
|
||||
val mainApp = appGroup.mainApp
|
||||
|
||||
@@ -818,9 +843,27 @@ private fun AppGroupItem(
|
||||
} else {
|
||||
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)) {
|
||||
if (appGroup.allowSu) {
|
||||
@@ -853,7 +896,7 @@ private fun AppGroupItem(
|
||||
)
|
||||
}
|
||||
if (appGroup.apps.size > 1) {
|
||||
Natives.getUserName(appGroup.uid)?.let {
|
||||
appGroup.userName?.let {
|
||||
LabelItem(
|
||||
text = it,
|
||||
style = LabelItemDefaults.style.copy(
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.text.Collator
|
||||
import java.util.*
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
@@ -27,6 +26,8 @@ import java.util.concurrent.TimeUnit
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import com.sukisu.zako.IKsuInterface
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
enum class AppCategory(val displayNameRes: Int, val persistKey: String) {
|
||||
ALL(com.sukisu.ultra.R.string.category_all_apps, "ALL"),
|
||||
@@ -77,31 +78,36 @@ class SuperUserViewModel : ViewModel() {
|
||||
private const val BATCH_SIZE = 20
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
data class AppInfo(
|
||||
val label: String,
|
||||
val packageInfo: PackageInfo,
|
||||
val profile: Natives.Profile?,
|
||||
) : Parcelable {
|
||||
val packageName: String get() = packageInfo.packageName
|
||||
val uid: Int get() = packageInfo.applicationInfo!!.uid
|
||||
@IgnoredOnParcel
|
||||
val packageName: String = packageInfo.packageName
|
||||
@IgnoredOnParcel
|
||||
val uid: Int = packageInfo.applicationInfo!!.uid
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
data class AppGroup(
|
||||
val uid: Int,
|
||||
val apps: List<AppInfo>,
|
||||
val profile: Natives.Profile?
|
||||
) : Parcelable {
|
||||
val mainApp: AppInfo get() = apps.first()
|
||||
val packageNames: List<String> get() = apps.map { it.packageName }
|
||||
val allowSu: Boolean get() = profile?.allowSu == true
|
||||
|
||||
val userName: String? get() = Natives.getUserName(uid)
|
||||
val hasCustomProfile: Boolean
|
||||
get() = profile?.let {
|
||||
if (it.allowSu) !it.rootUseDefault else !it.nonRootUseDefault
|
||||
} ?: false
|
||||
@IgnoredOnParcel
|
||||
val mainApp: AppInfo = apps.first()
|
||||
@IgnoredOnParcel
|
||||
val packageNames: List<String> = apps.map { it.packageName }
|
||||
@IgnoredOnParcel
|
||||
val allowSu: Boolean = profile?.allowSu == true
|
||||
@IgnoredOnParcel
|
||||
val userName: String? = Natives.getUserName(uid)
|
||||
@IgnoredOnParcel
|
||||
val hasCustomProfile : Boolean = profile?.let { if (it.allowSu) !it.rootUseDefault else !it.nonRootUseDefault } ?: false
|
||||
}
|
||||
|
||||
private val appProcessingThreadPool = ThreadPoolExecutor(
|
||||
|
||||
Reference in New Issue
Block a user