Move Disable account management to System manager

Add ListItem, optimize UI
Upgrade Kotlin to 2.0.21
This commit is contained in:
BinTianqi
2024-11-09 14:45:06 +08:00
parent e6fa6f1305
commit 0d431addd5
8 changed files with 265 additions and 359 deletions

View File

@@ -2,7 +2,7 @@ plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
alias(libs.plugins.compose) alias(libs.plugins.compose)
kotlin("plugin.serialization") version "2.0.0" kotlin("plugin.serialization") version "2.0.21"
} }
android { android {

View File

@@ -22,7 +22,6 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -34,7 +33,6 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
@@ -88,6 +86,7 @@ import com.bintianqi.owndroid.getFile
import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.selectedPackage
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SubPageItem
@@ -184,8 +183,8 @@ private fun Home(
val profileOwner = context.isProfileOwner val profileOwner = context.isProfileOwner
var suspend by remember { mutableStateOf(false) } var suspend by remember { mutableStateOf(false) }
suspend = try{ if(VERSION.SDK_INT >= 24) dpm.isPackageSuspended(receiver, pkgName) else false } suspend = try{ if(VERSION.SDK_INT >= 24) dpm.isPackageSuspended(receiver, pkgName) else false }
catch(e:NameNotFoundException) { false } catch(_: NameNotFoundException) { false }
catch(e:IllegalArgumentException) { false } catch(_: IllegalArgumentException) { false }
var hide by remember { mutableStateOf(false) } var hide by remember { mutableStateOf(false) }
hide = dpm.isApplicationHidden(receiver, pkgName) hide = dpm.isApplicationHidden(receiver, pkgName)
var blockUninstall by remember { mutableStateOf(false) } var blockUninstall by remember { mutableStateOf(false) }
@@ -202,8 +201,8 @@ private fun Home(
when(appControlAction) { when(appControlAction) {
1 -> { 1 -> {
suspend = try{ if(VERSION.SDK_INT >= 24) dpm.isPackageSuspended(receiver, pkgName) else false } suspend = try{ if(VERSION.SDK_INT >= 24) dpm.isPackageSuspended(receiver, pkgName) else false }
catch(e:NameNotFoundException) { false } catch(_: NameNotFoundException) { false }
catch(e:IllegalArgumentException) { false } catch(_: IllegalArgumentException) { false }
} }
2 -> hide = dpm.isApplicationHidden(receiver,pkgName) 2 -> hide = dpm.isApplicationHidden(receiver,pkgName)
3 -> blockUninstall = dpm.isUninstallBlocked(receiver,pkgName) 3 -> blockUninstall = dpm.isUninstallBlocked(receiver,pkgName)
@@ -347,48 +346,33 @@ private fun UserCtrlDisabledPkg(pkgName:String) {
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
val refresh = { val refresh = {
pkgList.clear() pkgList.clear()
dpm.getUserControlDisabledPackages(receiver).forEach { pkgList.add(it) } pkgList.addAll(dpm.getUserControlDisabledPackages(receiver))
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
var inited by remember{mutableStateOf(false)}
if(!inited) { refresh();inited=true }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.ucd), style = typography.headlineLarge) Text(text = stringResource(R.string.ucd), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.ucd_desc)) Text(text = stringResource(R.string.ucd_desc))
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is)) Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
Text(text = if(pkgList.isEmpty()) stringResource(R.string.none) else pkgList.joinToString(separator = "\n")) if(pkgList.isEmpty()) Text(stringResource(R.string.none))
for(i in pkgList) {
ListItem(i) { pkgList -= i }
}
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { pkgList += pkgName },
pkgList.add(pkgName) modifier = Modifier.fillMaxWidth()
dpm.setUserControlDisabledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { onClick = { dpm.setUserControlDisabledPackages(receiver, pkgList); refresh() },
pkgList.remove(pkgName) modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
dpm.setUserControlDisabledPackages(receiver,pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.apply))
}
}
Button(
onClick = { dpm.setUserControlDisabledPackages(receiver, listOf()); refresh() },
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
} }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
@@ -503,47 +487,33 @@ private fun CrossProfilePkg(pkgName: String) {
val crossProfilePkg = remember { mutableStateListOf<String>() } val crossProfilePkg = remember { mutableStateListOf<String>() }
val refresh = { val refresh = {
crossProfilePkg.clear() crossProfilePkg.clear()
dpm.getCrossProfilePackages(receiver).forEach { crossProfilePkg += it } crossProfilePkg.addAll(dpm.getCrossProfilePackages(receiver))
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.cross_profile_package), style = typography.headlineLarge) Text(text = stringResource(R.string.cross_profile_package), style = typography.headlineLarge)
Text(text = stringResource(R.string.app_list_is)) Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
Text(text = if(crossProfilePkg.isEmpty()) stringResource(R.string.none) else crossProfilePkg.joinToString(separator = "\n")) if(crossProfilePkg.isEmpty()) Text(stringResource(R.string.none))
for(i in crossProfilePkg) {
ListItem(i) { crossProfilePkg -= i }
}
} }
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { crossProfilePkg += pkgName },
crossProfilePkg.add(pkgName) modifier = Modifier.fillMaxWidth()
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { onClick = {
crossProfilePkg.remove(pkgName)
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet()) dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
refresh() refresh()
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.apply))
}
}
Button(
onClick = {
crossProfilePkg.clear()
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
} }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
@@ -557,9 +527,7 @@ private fun CrossProfileWidget(pkgName: String) {
val pkgList = remember { mutableStateListOf<String>() } val pkgList = remember { mutableStateListOf<String>() }
val refresh = { val refresh = {
pkgList.clear() pkgList.clear()
dpm.getCrossProfileWidgetProviders(receiver).forEach { pkgList.addAll(dpm.getCrossProfileWidgetProviders(receiver))
pkgList += it
}
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
@@ -567,39 +535,24 @@ private fun CrossProfileWidget(pkgName: String) {
Text(text = stringResource(R.string.cross_profile_widget), style = typography.headlineLarge) Text(text = stringResource(R.string.cross_profile_widget), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is)) Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
Text(text = if(pkgList.isEmpty()) stringResource(R.string.none) else pkgList.joinToString(separator = "\n")) if(pkgList.isEmpty()) Text(stringResource(R.string.none))
for(i in pkgList) {
ListItem(i) {
dpm.removeCrossProfileWidgetProvider(receiver, i)
refresh()
}
}
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = {
if(pkgName != "") { dpm.addCrossProfileWidgetProvider(receiver, pkgName) } if(pkgName != "") { dpm.addCrossProfileWidgetProvider(receiver, pkgName) }
refresh() refresh()
}, },
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.add))
}
Button(
onClick = {
if(pkgName != "") { dpm.removeCrossProfileWidgetProvider(receiver, pkgName) }
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.remove))
}
}
Button(
onClick = {
pkgList.forEach {
dpm.removeCrossProfileWidgetProvider(receiver, it)
}
},
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.clear_list)) Text(stringResource(R.string.add))
} }
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
} }
@@ -612,25 +565,12 @@ private fun CredentialManagePolicy(pkgName: String) {
val dpm = context.getDPM() val dpm = context.getDPM()
var policy: PackagePolicy? var policy: PackagePolicy?
var policyType by remember{ mutableIntStateOf(-1) } var policyType by remember{ mutableIntStateOf(-1) }
val credentialList = remember { mutableStateListOf<String>() } val pkgList = remember { mutableStateListOf<String>() }
val refreshPolicy = { val refreshPolicy = {
policy = dpm.credentialManagerPolicy policy = dpm.credentialManagerPolicy
policyType = policy?.policyType ?: -1 policyType = policy?.policyType ?: -1
(policy?.packageNames ?: mutableSetOf()).forEach { credentialList += it } pkgList.clear()
} pkgList.addAll(policy?.packageNames ?: setOf())
val apply = {
try {
if(policyType != -1 && credentialList.isNotEmpty()) {
dpm.credentialManagerPolicy = PackagePolicy(policyType, credentialList.toSet())
}else{
dpm.credentialManagerPolicy = null
}
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch(e:java.lang.IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} finally {
refreshPolicy()
}
} }
LaunchedEffect(Unit) { refreshPolicy() } LaunchedEffect(Unit) { refreshPolicy() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
@@ -660,38 +600,36 @@ private fun CredentialManagePolicy(pkgName: String) {
AnimatedVisibility(policyType != -1) { AnimatedVisibility(policyType != -1) {
Column { Column {
Text(stringResource(R.string.app_list_is)) Text(stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
Text(text = if(credentialList.isEmpty()) stringResource(R.string.none) else credentialList.joinToString(separator = "\n")) if(pkgList.isEmpty()) Text(stringResource(R.string.none))
for(i in pkgList) {
ListItem(i) { pkgList -= i }
}
} }
Spacer(Modifier.padding(vertical = 10.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { pkgList += pkgName },
credentialList.add(pkgName) modifier = Modifier.fillMaxWidth()
apply()
},
modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { onClick = {
credentialList.remove(pkgName) try {
apply() if(policyType != -1 && pkgList.isNotEmpty()) {
}, dpm.credentialManagerPolicy = PackagePolicy(policyType, pkgList.toSet())
modifier = Modifier.fillMaxWidth(0.96F) } else {
) { dpm.credentialManagerPolicy = null
Text(stringResource(R.string.remove))
} }
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} finally {
refreshPolicy()
} }
Button(
onClick = {
credentialList.clear()
apply()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) { ) {
Text(stringResource(R.string.clear_list)) Text(stringResource(R.string.apply))
} }
} }
} }
@@ -705,16 +643,12 @@ private fun PermittedAccessibility(pkgName: String) {
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val pkgList = remember { mutableStateListOf<String>() } val pkgList = remember { mutableStateListOf<String>() }
var allowAll by remember { mutableStateOf(false) } var allowAll by remember { mutableStateOf(true) }
val refresh = { val refresh = {
pkgList.clear() pkgList.clear()
val getList = dpm.getPermittedAccessibilityServices(receiver) val getList = dpm.getPermittedAccessibilityServices(receiver)
if(getList != null) { allowAll = getList == null
allowAll = false pkgList.addAll(getList ?: listOf())
getList.forEach { pkgList += it }
} else {
allowAll = true
}
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
@@ -736,45 +670,28 @@ private fun PermittedAccessibility(pkgName: String) {
} }
AnimatedVisibility(!allowAll) { AnimatedVisibility(!allowAll) {
Column { Column {
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
if (pkgList.isEmpty()) { Text(stringResource(if(pkgList.isEmpty()) R.string.only_system_accessibility_allowed else R.string.permitted_packages_is))
Text(stringResource(R.string.only_system_accessibility_allowed)) if(pkgList.isEmpty()) Text(stringResource(R.string.none))
} else { for(i in pkgList) {
Text(stringResource(R.string.permitted_packages_is) + pkgList.joinToString(separator = "\n")) ListItem(i) { pkgList -= i }
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { pkgList += pkgName },
pkgList.add(pkgName) modifier = Modifier.fillMaxWidth()
dpm.setPermittedAccessibilityServices(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { onClick = {
pkgList.remove(pkgName)
dpm.setPermittedAccessibilityServices(receiver, pkgList) dpm.setPermittedAccessibilityServices(receiver, pkgList)
refresh() refresh()
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.apply))
}
}
Button(
onClick = {
pkgList.clear()
dpm.setPermittedAccessibilityServices(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
} }
} }
} }
@@ -790,17 +707,13 @@ private fun PermittedIME(pkgName: String) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
val permittedIme = remember { mutableStateListOf<String>() } val pkgList = remember { mutableStateListOf<String>() }
var allowAll by remember { mutableStateOf(false) } var allowAll by remember { mutableStateOf(true) }
val refresh = { val refresh = {
permittedIme.clear() pkgList.clear()
val getList = dpm.getPermittedInputMethods(receiver) val getList = dpm.getPermittedInputMethods(receiver)
if(getList != null) { allowAll = getList == null
allowAll = false pkgList.addAll(getList ?: listOf())
getList.forEach { permittedIme += it }
} else {
allowAll = true
}
} }
LaunchedEffect(Unit) { refresh() } LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
@@ -816,45 +729,27 @@ private fun PermittedIME(pkgName: String) {
) )
AnimatedVisibility(!allowAll) { AnimatedVisibility(!allowAll) {
Column { Column {
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
if(permittedIme.isEmpty()) { Text(stringResource(if(pkgList.isEmpty()) R.string.only_system_ime_allowed else R.string.permitted_packages_is))
Text(stringResource(R.string.only_system_ime_allowed)) for(i in pkgList) {
} else { ListItem(i) { pkgList -= i }
Text(stringResource(R.string.permitted_packages_is) + permittedIme.joinToString(separator = "\n"))
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { pkgList += pkgName },
permittedIme.add(pkgName) modifier = Modifier.fillMaxWidth()
dpm.setPermittedInputMethods(receiver, permittedIme)
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { onClick = {
permittedIme.remove(pkgName) dpm.setPermittedInputMethods(receiver, pkgList)
dpm.setPermittedInputMethods(receiver, permittedIme)
refresh() refresh()
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.apply))
}
}
Button(
onClick = {
permittedIme.clear()
dpm.setPermittedInputMethods(receiver, permittedIme)
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
} }
} }
} }
@@ -882,41 +777,27 @@ private fun KeepUninstalledApp(pkgName: String) {
Text(text = stringResource(R.string.keep_uninstalled_packages), style = typography.headlineLarge) Text(text = stringResource(R.string.keep_uninstalled_packages), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is)) Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
Text(text = if(pkgList.isEmpty()) stringResource(R.string.none) else pkgList.joinToString(separator = "\n")) if(pkgList.isEmpty()) Text(stringResource(R.string.none))
for(i in pkgList) {
ListItem(i) { pkgList -= i }
}
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { onClick = { pkgList += pkgName },
pkgList.add(pkgName) modifier = Modifier.fillMaxWidth()
dpm.setKeepUninstalledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { onClick = {
pkgList.remove(pkgName)
dpm.setKeepUninstalledPackages(receiver, pkgList) dpm.setKeepUninstalledPackages(receiver, pkgList)
refresh() refresh()
}, },
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.apply))
}
}
Button(
onClick = {
pkgList.clear()
dpm.setKeepUninstalledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
} }
Spacer(Modifier.padding(vertical = 30.dp)) Spacer(Modifier.padding(vertical = 30.dp))
} }
@@ -1075,7 +956,7 @@ private fun DefaultDialerAppDialog(status: MutableIntState, pkgName: String) {
try{ try{
dpm.setDefaultDialerApplication(pkgName) dpm.setDefaultDialerApplication(pkgName)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
}catch(e:IllegalArgumentException) { }catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} }
status.intValue = 0 status.intValue = 0
@@ -1110,7 +991,7 @@ private fun EnableSystemAppDialog(status: MutableIntState, pkgName: String) {
try { try {
dpm.enableSystemApp(receiver, pkgName) dpm.enableSystemApp(receiver, pkgName)
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch(e: IllegalArgumentException) { } catch(_: IllegalArgumentException) {
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
} }
status.intValue = 0 status.intValue = 0

View File

@@ -73,7 +73,6 @@ fun DpmPermissions(navCtrl:NavHostController) {
composable(route = "ProfileOwner") { ProfileOwner() } composable(route = "ProfileOwner") { ProfileOwner() }
composable(route = "DeviceOwner") { DeviceOwner() } composable(route = "DeviceOwner") { DeviceOwner() }
composable(route = "DeviceInfo") { DeviceInfo() } composable(route = "DeviceInfo") { DeviceInfo() }
composable(route = "DisableAccountManagement") { DisableAccountManagement() }
composable(route = "LockScreenInfo") { LockScreenInfo() } composable(route = "LockScreenInfo") { LockScreenInfo() }
composable(route = "SupportMsg") { SupportMsg() } composable(route = "SupportMsg") { SupportMsg() }
composable(route = "TransformOwnership") { TransferOwnership() } composable(route = "TransformOwnership") { TransferOwnership() }
@@ -135,9 +134,6 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) {
if(enrollmentSpecificId != "") { if(enrollmentSpecificId != "") {
SubPageItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 } SubPageItem(R.string.enrollment_specific_id, "", R.drawable.id_card_fill0) { dialog = 1 }
} }
if(deviceOwner || profileOwner) {
SubPageItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { localNavCtrl.navigate("DisableAccountManagement") }
}
if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 24 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.device_owner_lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { localNavCtrl.navigate("LockScreenInfo") } SubPageItem(R.string.device_owner_lock_screen_info, "", R.drawable.screen_lock_portrait_fill0) { localNavCtrl.navigate("LockScreenInfo") }
} }
@@ -526,6 +522,7 @@ private fun SupportMsg() {
value = shortMsg, value = shortMsg,
label = { Text(stringResource(R.string.short_support_msg)) }, label = { Text(stringResource(R.string.short_support_msg)) },
onValueChange = { shortMsg = it }, onValueChange = { shortMsg = it },
minLines = 2,
modifier = Modifier.fillMaxWidth().padding(bottom = 2.dp) modifier = Modifier.fillMaxWidth().padding(bottom = 2.dp)
) )
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
@@ -555,6 +552,7 @@ private fun SupportMsg() {
value = longMsg, value = longMsg,
label = { Text(stringResource(R.string.long_support_msg)) }, label = { Text(stringResource(R.string.long_support_msg)) },
onValueChange = { longMsg = it }, onValueChange = { longMsg = it },
minLines = 3,
modifier = Modifier.fillMaxWidth().padding(bottom = 2.dp) modifier = Modifier.fillMaxWidth().padding(bottom = 2.dp)
) )
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
@@ -583,59 +581,6 @@ private fun SupportMsg() {
} }
} }
@Composable
private fun DisableAccountManagement() {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.disable_account_management), style = typography.headlineLarge)
Text(stringResource(R.string.unknown_effect))
var accountList by remember{ mutableStateOf("") }
val refreshList = {
val noManageAccount = dpm.accountTypesWithManagementDisabled
accountList = ""
if (noManageAccount != null) {
var count = noManageAccount.size
for(each in noManageAccount) { count -= 1; accountList += each; if(count>0) { accountList += "\n" } }
}
}
var inited by remember { mutableStateOf(false) }
if(!inited) { refreshList(); inited=true }
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = if(accountList=="") stringResource(R.string.none) else accountList)
var inputText by remember{ mutableStateOf("") }
OutlinedTextField(
value = inputText,
onValueChange = { inputText = it },
label = { Text(stringResource(R.string.account_type)) },
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() })
)
Button(
onClick={
dpm.setAccountManagementDisabled(receiver, inputText, true)
refreshList()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.add))
}
Button(
onClick={
dpm.setAccountManagementDisabled(receiver, inputText, false)
refreshList()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.remove))
}
}
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
private fun TransferOwnership() { private fun TransferOwnership() {

View File

@@ -60,10 +60,13 @@ import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@@ -114,6 +117,7 @@ import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.Information
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
@@ -166,6 +170,7 @@ fun SystemManage(navCtrl: NavHostController) {
composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } composable(route = "LockTaskMode") { LockTaskMode(navCtrl) }
composable(route = "CaCert") { CaCert() } composable(route = "CaCert") { CaCert() }
composable(route = "SecurityLogs") { SecurityLogs() } composable(route = "SecurityLogs") { SecurityLogs() }
composable(route = "DisableAccountManagement") { DisableAccountManagement() }
composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() }
composable(route = "InstallSystemUpdate") { InstallSystemUpdate() } composable(route = "InstallSystemUpdate") { InstallSystemUpdate() }
composable(route = "WipeData") { WipeData() } composable(route = "WipeData") { WipeData() }
@@ -228,6 +233,9 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState, rebootDia
if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 26 && !dhizuku && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.security_logs, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogs") } SubPageItem(R.string.security_logs, "", R.drawable.description_fill0) { navCtrl.navigate("SecurityLogs") }
} }
if(deviceOwner || profileOwner) {
SubPageItem(R.string.disable_account_management, "", R.drawable.account_circle_fill0) { navCtrl.navigate("DisableAccountManagement") }
}
if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 23 && (deviceOwner || dpm.isOrgProfile(receiver))) {
SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") } SubPageItem(R.string.system_update_policy, "", R.drawable.system_update_fill0) { navCtrl.navigate("SystemUpdatePolicy") }
} }
@@ -578,7 +586,7 @@ private fun MTEPolicy() {
try { try {
dpm.mtePolicy = selectedMtePolicy dpm.mtePolicy = selectedMtePolicy
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} catch(e:java.lang.UnsupportedOperationException) { } catch(_: java.lang.UnsupportedOperationException) {
Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show()
} }
selectedMtePolicy = dpm.mtePolicy selectedMtePolicy = dpm.mtePolicy
@@ -778,10 +786,11 @@ private fun LockTaskMode(navCtrl: NavHostController) {
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.lock_task_packages), style = typography.headlineLarge) Text(text = stringResource(R.string.lock_task_packages), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
SelectionContainer(modifier = Modifier.animateContentSize()) { Column(modifier = Modifier.animateContentSize()) {
var listText = "" if(lockTaskPackages.isEmpty()) Text(text = stringResource(R.string.none))
lockTaskPackages.forEach { listText += "\n" + it } for(i in lockTaskPackages) {
Text(text = stringResource(R.string.app_list_is) + if(listText == "") stringResource(R.string.none) else listText) ListItem(i) { lockTaskPackages -= i }
}
} }
OutlinedTextField( OutlinedTextField(
value = inputLockTaskPkg, value = inputLockTaskPkg,
@@ -804,13 +813,19 @@ private fun LockTaskMode(navCtrl: NavHostController) {
) )
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button( Button(
onClick = { lockTaskPackages.add(inputLockTaskPkg) }, onClick = {
lockTaskPackages.add(inputLockTaskPkg)
inputLockTaskPkg = ""
},
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
Text(stringResource(R.string.add)) Text(stringResource(R.string.add))
} }
Button( Button(
onClick = { lockTaskPackages.remove(inputLockTaskPkg) }, onClick = {
lockTaskPackages.remove(inputLockTaskPkg)
inputLockTaskPkg = ""
},
modifier = Modifier.fillMaxWidth(0.96F) modifier = Modifier.fillMaxWidth(0.96F)
) { ) {
Text(stringResource(R.string.remove)) Text(stringResource(R.string.remove))
@@ -1062,6 +1077,54 @@ private fun SecurityLogs() {
} }
} }
@Composable
private fun DisableAccountManagement() {
val context = LocalContext.current
val dpm = context.getDPM()
val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.disable_account_management), style = typography.headlineLarge)
val list = remember { mutableStateListOf<String>() }
fun refreshList() {
list.clear()
dpm.accountTypesWithManagementDisabled?.forEach { list += it }
}
LaunchedEffect(Unit) { refreshList() }
Spacer(Modifier.padding(vertical = 5.dp))
Column(modifier = Modifier.animateContentSize()) {
if(list.isEmpty()) Text(stringResource(R.string.none))
for(i in list) {
ListItem(i) {
dpm.setAccountManagementDisabled(receiver, i, false)
refreshList()
}
}
}
var inputText by remember{ mutableStateOf("") }
OutlinedTextField(
value = inputText,
onValueChange = { inputText = it },
label = { Text(stringResource(R.string.account_type)) },
trailingIcon = {
IconButton(
onClick = {
dpm.setAccountManagementDisabled(receiver, inputText, true)
inputText = ""
refreshList()
}
) {
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
}
},
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() })
)
}
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
fun FactoryResetProtection() { fun FactoryResetProtection() {
@@ -1078,7 +1141,7 @@ fun FactoryResetProtection() {
var policy: FactoryResetProtectionPolicy? = FactoryResetProtectionPolicy.Builder().build() var policy: FactoryResetProtectionPolicy? = FactoryResetProtectionPolicy.Builder().build()
try { try {
policy = dpm.getFactoryResetProtectionPolicy(receiver) policy = dpm.getFactoryResetProtectionPolicy(receiver)
} catch(e: UnsupportedOperationException) { } catch(_: UnsupportedOperationException) {
unsupported = true unsupported = true
policy = null policy = null
} finally { } finally {
@@ -1106,34 +1169,32 @@ fun FactoryResetProtection() {
AnimatedVisibility(usePolicy) { AnimatedVisibility(usePolicy) {
Column { Column {
CheckBoxItem(R.string.enable_frp, enabled, { enabled = it }) CheckBoxItem(R.string.enable_frp, enabled, { enabled = it })
Text(stringResource(R.string.account_list_is) + "\n") Text(stringResource(R.string.account_list_is))
Text( Column(modifier = Modifier.animateContentSize()) {
text = if(accountList.isEmpty()) stringResource(R.string.none) else accountList.joinToString(separator = "\n"), if(accountList.isEmpty()) Text(stringResource(R.string.none))
modifier = Modifier.animateContentSize() for(i in accountList) {
) ListItem(i) { accountList -= i }
}
}
OutlinedTextField( OutlinedTextField(
value = inputAccount, value = inputAccount,
label = { Text(stringResource(R.string.account)) },
onValueChange = { inputAccount = it }, onValueChange = { inputAccount = it },
label = { Text(stringResource(R.string.account)) },
trailingIcon = {
IconButton(
onClick = {
accountList += inputAccount
inputAccount = ""
}
) {
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = { accountList.add(inputAccount) },
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.add))
}
Button(
onClick = { accountList.remove(inputAccount) },
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.remove))
}
}
} }
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))

View File

@@ -15,6 +15,7 @@ import android.os.UserManager
import android.provider.MediaStore import android.provider.MediaStore
import android.widget.Toast import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.ScrollState import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -29,10 +30,13 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@@ -68,6 +72,7 @@ import com.bintianqi.owndroid.toggle
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CardItem
import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CheckBoxItem
import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SubPageItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.ui.TopBar import com.bintianqi.owndroid.ui.TopBar
@@ -360,57 +365,48 @@ private fun AffiliationID() {
val receiver = context.getReceiver() val receiver = context.getReceiver()
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var input by remember { mutableStateOf("") } var input by remember { mutableStateOf("") }
val affiliationID = remember { mutableStateListOf<String>() } val list = remember { mutableStateListOf<String>() }
val list = affiliationID.joinToString(separator = "\n")
val refreshIds = { val refreshIds = {
affiliationID.clear() list.clear()
affiliationID.addAll(dpm.getAffiliationIds(receiver)) list.addAll(dpm.getAffiliationIds(receiver))
} }
LaunchedEffect(Unit) { refreshIds() } LaunchedEffect(Unit) { refreshIds() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.affiliation_id), style = typography.headlineLarge) Text(text = stringResource(R.string.affiliation_id), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(list != "") { Column(modifier = Modifier.animateContentSize()) {
SelectionContainer { Text(text = list) } if(list.isEmpty()) Text(stringResource(R.string.none))
}else{ for(i in list) {
Text(text = stringResource(R.string.none)) ListItem(i) { list -= i }
}
} }
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
OutlinedTextField( OutlinedTextField(
value = input, value = input,
onValueChange = {input = it}, onValueChange = { input = it },
label = { Text("ID") }, label = { Text("ID") },
trailingIcon = {
IconButton(
onClick = {
list += input
input = ""
}
) {
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
}
},
modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp), modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }) keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() })
) )
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = { affiliationID.add(input) },
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.add))
}
Button(
onClick = { affiliationID.remove(input) },
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.remove))
}
}
Button( Button(
onClick = { onClick = {
if("" in affiliationID) { list.removeAll(listOf(""))
Toast.makeText(context, R.string.include_empty_string, Toast.LENGTH_SHORT).show() dpm.setAffiliationIds(receiver, list.toSet())
} else if(affiliationID.isEmpty()) {
Toast.makeText(context, R.string.cannot_be_empty, Toast.LENGTH_SHORT).show()
} else {
dpm.setAffiliationIds(receiver, affiliationID.toSet())
refreshIds()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
} refreshIds()
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {

View File

@@ -3,11 +3,15 @@ package com.bintianqi.owndroid.ui
import android.widget.Toast import android.widget.Toast
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.icons.Icons
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
@@ -240,3 +244,22 @@ fun CardItem(@StringRes title: Int, text: String) {
} }
} }
} }
@Composable
fun ListItem(text: String, onDelete: () -> Unit) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).clip(RoundedCornerShape(15)).background(colorScheme.surfaceVariant)
) {
Text(text = text, modifier = Modifier.padding(start = 12.dp))
IconButton(
onClick = onDelete
) {
Icon(
painter = painterResource(R.drawable.close_fill0),
contentDescription = stringResource(R.string.delete)
)
}
}
}

View File

@@ -222,7 +222,7 @@
<!--Network--> <!--Network-->
<string name="network">Network</string> <string name="network">Network</string>
<string name="wifi_mac_addr">Wi-Fi Mac address</string> <string name="wifi_mac_addr">Wi-Fi Mac address</string>
<string name="min_wifi_security_level">Min Wi-Fi security level</string> <string name="min_wifi_security_level">Minimum Wi-Fi security level</string>
<string name="wifi_security_open">Open</string> <string name="wifi_security_open">Open</string>
<string name="lockdown_admin_configured_network">Lockdown admin configured network</string> <string name="lockdown_admin_configured_network">Lockdown admin configured network</string>
<string name="wifi_ssid_policy">WiFi SSID policy</string> <string name="wifi_ssid_policy">WiFi SSID policy</string>

View File

@@ -1,6 +1,6 @@
[versions] [versions]
agp = "8.7.2" agp = "8.7.2"
kotlin = "2.0.0" kotlin = "2.0.21"
androidx-activity-compose = "1.9.0" androidx-activity-compose = "1.9.0"
navigation-compose = "2.7.7" navigation-compose = "2.7.7"