Optimize Applications and PackageChooser

This commit is contained in:
BinTianqi
2025-09-17 18:29:52 +08:00
parent e7c7a3b3c6
commit 80c1ddb36c
13 changed files with 787 additions and 802 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,6 @@ import com.bintianqi.owndroid.createShortcuts
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuBinderWrapper
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive

View File

@@ -127,7 +127,6 @@ import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.Privilege
import com.bintianqi.owndroid.R
@@ -153,6 +152,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
@@ -886,7 +886,10 @@ fun NetworkStats.toBucketList(): List<NetworkStats.Bucket> {
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(23)
@Composable
fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkStatsViewer) -> Unit) {
fun NetworkStatsScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkStatsViewer) -> Unit
) {
val context = LocalContext.current
val privilege by Privilege.status.collectAsStateWithLifecycle()
val fm = LocalFocusManager.current
@@ -1053,16 +1056,14 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
) {
var uidText by rememberSaveable { mutableStateOf(context.getString(NetworkStatsUID.All.strRes)) }
var readOnly by rememberSaveable { mutableStateOf(true) }
if(!readOnly && uidText.toIntOrNull() != null) uid = uidText.toInt()
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) {
it ?: return@rememberLauncherForActivityResult
if(VERSION.SDK_INT >= 24 && readOnly) {
try {
uid = context.packageManager.getPackageUid(it, 0)
uidText = "$it ($uid)"
} catch(_: NameNotFoundException) {
context.showOperationResultToast(false)
}
if (!readOnly && uidText.toIntOrNull() != null) uid = uidText.toInt()
if (VERSION.SDK_INT >= 24) LaunchedEffect(Unit) {
val pkg = chosenPackage.receive()
try {
uid = context.packageManager.getPackageUid(pkg, 0)
uidText = "$uid ($pkg)"
} catch(_: NameNotFoundException) {
context.showOperationResultToast(false)
}
}
OutlinedTextField(
@@ -1093,7 +1094,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
onClick = {
readOnly = true
activeTextField = NetworkStatsActiveTextField.None
choosePackage.launch(null)
onChoosePackage()
}
)
DropdownMenuItem(
@@ -1457,15 +1458,18 @@ fun PrivateDnsScreen(onNavigateUp: () -> Unit) {
@RequiresApi(24)
@Composable
fun AlwaysOnVpnPackageScreen(onNavigateUp: () -> Unit) {
fun AlwaysOnVpnPackageScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit, onNavigateUp: () -> Unit
) {
val context = LocalContext.current
var lockdown by rememberSaveable { mutableStateOf(false) }
var pkgName by rememberSaveable { mutableStateOf("") }
val focusMgr = LocalFocusManager.current
val refresh = { pkgName = Privilege.DPM.getAlwaysOnVpnPackage(Privilege.DAR) ?: "" }
LaunchedEffect(Unit) { refresh() }
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
result?.let { pkgName = it }
fun refresh() {
pkgName = Privilege.DPM.getAlwaysOnVpnPackage(Privilege.DAR) ?: ""
}
LaunchedEffect(Unit) {
refresh()
pkgName = chosenPackage.receive()
}
val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean ->
try {
@@ -1483,21 +1487,8 @@ fun AlwaysOnVpnPackageScreen(onNavigateUp: () -> Unit) {
}
}
MyScaffold(R.string.always_on_vpn, onNavigateUp) {
OutlinedTextField(
value = pkgName,
onValueChange = { pkgName = it },
label = { Text(stringResource(R.string.package_name)) },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable { choosePackage.launch(null) }
.padding(3.dp))
},
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp)
)
PackageNameTextField(pkgName, onChoosePackage,
Modifier.padding(vertical = 4.dp)) { pkgName = it }
SwitchItem(R.string.enable_lockdown, state = lockdown, onCheckedChange = { lockdown = it }, padding = false)
Spacer(Modifier.padding(vertical = 5.dp))
Button(
@@ -2067,7 +2058,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
keyboardActions = KeyboardActions { fm.clearFocus() }
)
if(VERSION.SDK_INT >= 33) Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), Arrangement.SpaceBetween) {
val fr = FocusRequester()
val fr = remember { FocusRequester() }
OutlinedTextField(
mtuV4, { mtuV4 = it }, Modifier.fillMaxWidth(0.49F),
label = { Text("MTU (IPv4)") },
@@ -2206,7 +2197,7 @@ fun AddApnSettingScreen(origin: ApnSetting?, onNavigateUp: () -> Unit) {
if(dialog != 0) {
var address by remember { mutableStateOf((if(dialog == 1) proxyAddress else mmsProxyAddress)) }
var port by remember { mutableStateOf((if(dialog == 1) proxyPort else mmsProxyPort)) }
val fr = FocusRequester()
val fr = remember { FocusRequester() }
AlertDialog(
title = { Text(if(dialog == 1) "Proxy" else "MMS proxy") },
text = {

View File

@@ -6,7 +6,6 @@ import android.content.Context
import android.content.pm.PackageManager
import android.os.Build.VERSION
import android.os.PersistableBundle
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.annotation.Keep
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
@@ -84,10 +83,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.DHIZUKU_CLIENTS_FILE
import com.bintianqi.owndroid.DhizukuClientInfo
import com.bintianqi.owndroid.DhizukuPermissions
@@ -113,6 +110,7 @@ import com.google.accompanist.drawablepainter.rememberDrawablePainter
import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.DhizukuRequestPermissionListener
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
@@ -731,30 +729,19 @@ fun DelegatedAdminsScreen(onNavigateUp: () -> Unit, onNavigate: (AddDelegatedAdm
@RequiresApi(26)
@Composable
fun AddDelegatedAdminScreen(data: AddDelegatedAdmin, onNavigateUp: () -> Unit) {
fun AddDelegatedAdminScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
data: AddDelegatedAdmin, onNavigateUp: () -> Unit
) {
val updateMode = data.pkg.isNotEmpty()
val fm = LocalFocusManager.current
var input by remember { mutableStateOf(data.pkg) }
val scopes = remember { mutableStateListOf(*data.scopes.toTypedArray()) }
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
result?.let { input = it }
LaunchedEffect(Unit) {
input = chosenPackage.receive()
}
MySmallTitleScaffold(if(updateMode) R.string.place_holder else R.string.add_delegated_admin, onNavigateUp, 0.dp) {
OutlinedTextField(
value = input, onValueChange = { input = it },
label = { Text(stringResource(R.string.package_name)) },
trailingIcon = {
if(!updateMode) IconButton({ choosePackage.launch(null) }) {
Icon(painterResource(R.drawable.list_fill0), null)
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions { fm.clearFocus() },
readOnly = updateMode,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp, horizontal = HorizontalPadding)
)
PackageNameTextField(input, onChoosePackage,
Modifier.padding(HorizontalPadding, 8.dp)) { input = it }
DelegatedScope.entries.filter { VERSION.SDK_INT >= it.requiresApi }.forEach { scope ->
val checked = scope in scopes
Row(

View File

@@ -112,13 +112,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.NotificationUtils
import com.bintianqi.owndroid.Privilege
@@ -143,6 +141,7 @@ import com.bintianqi.owndroid.ui.Notes
import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.uriToStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -1134,7 +1133,9 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
@OptIn(ExperimentalMaterial3Api::class)
@RequiresApi(28)
@Composable
fun LockTaskModeScreen(onNavigateUp: () -> Unit) {
fun LockTaskModeScreen(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit, onNavigateUp: () -> Unit
) {
val coroutine = rememberCoroutineScope()
val pagerState = rememberPagerState { 3 }
var tabIndex by remember { mutableIntStateOf(0) }
@@ -1177,8 +1178,8 @@ fun LockTaskModeScreen(onNavigateUp: () -> Unit) {
.padding(horizontal = HorizontalPadding)
.padding(bottom = 80.dp)
) {
if(page == 0) StartLockTaskMode()
else LockTaskPackages()
if(page == 0) StartLockTaskMode(chosenPackage, onChoosePackage)
else LockTaskPackages(chosenPackage, onChoosePackage)
}
} else {
Column(
@@ -1197,33 +1198,20 @@ fun LockTaskModeScreen(onNavigateUp: () -> Unit) {
@RequiresApi(28)
@Composable
private fun ColumnScope.StartLockTaskMode() {
private fun ColumnScope.StartLockTaskMode(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit
) {
val context = LocalContext.current
val focusMgr = LocalFocusManager.current
var startLockTaskApp by rememberSaveable { mutableStateOf("") }
var startLockTaskActivity by rememberSaveable { mutableStateOf("") }
var specifyActivity by rememberSaveable { mutableStateOf(false) }
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
result?.let { startLockTaskApp = it }
LaunchedEffect(Unit) {
startLockTaskApp = chosenPackage.receive()
}
Spacer(Modifier.padding(vertical = 5.dp))
OutlinedTextField(
value = startLockTaskApp,
onValueChange = { startLockTaskApp = it },
label = { Text(stringResource(R.string.package_name)) },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable { choosePackage.launch(null) }
.padding(3.dp))
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 3.dp)
)
PackageNameTextField(startLockTaskApp, onChoosePackage,
Modifier.padding(vertical = 3.dp), { startLockTaskApp = it })
CheckBoxItem(R.string.specify_activity, specifyActivity) { specifyActivity = it }
AnimatedVisibility(specifyActivity) {
OutlinedTextField(
@@ -1264,37 +1252,23 @@ private fun ColumnScope.StartLockTaskMode() {
@RequiresApi(26)
@Composable
private fun ColumnScope.LockTaskPackages() {
private fun ColumnScope.LockTaskPackages(
chosenPackage: Channel<String>, onChoosePackage: () -> Unit
) {
val context = LocalContext.current
val focusMgr = LocalFocusManager.current
val lockTaskPackages = remember { mutableStateListOf<String>() }
var input by rememberSaveable { mutableStateOf("") }
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
result?.let { input = it }
LaunchedEffect(Unit) {
lockTaskPackages.addAll(Privilege.DPM.getLockTaskPackages(Privilege.DAR))
input = chosenPackage.receive()
}
LaunchedEffect(Unit) { lockTaskPackages.addAll(Privilege.DPM.getLockTaskPackages(Privilege.DAR)) }
Spacer(Modifier.padding(vertical = 5.dp))
if(lockTaskPackages.isEmpty()) Text(text = stringResource(R.string.none))
for(i in lockTaskPackages) {
ListItem(i) { lockTaskPackages -= i }
}
OutlinedTextField(
value = input,
onValueChange = { input = it },
label = { Text(stringResource(R.string.package_name)) },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
trailingIcon = {
Icon(painter = painterResource(R.drawable.list_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable { choosePackage.launch(null) }
.padding(3.dp))
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 3.dp)
)
PackageNameTextField(input, onChoosePackage,
Modifier.padding(vertical = 3.dp), { input = it })
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {