mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
Move Disable account management to System manager
Add ListItem, optimize UI Upgrade Kotlin to 2.0.21
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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(
|
|
||||||
onClick = {
|
|
||||||
pkgList.add(pkgName)
|
|
||||||
dpm.setUserControlDisabledPackages(receiver, pkgList)
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
pkgList.remove(pkgName)
|
|
||||||
dpm.setUserControlDisabledPackages(receiver,pkgList)
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.remove))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button(
|
Button(
|
||||||
onClick = { dpm.setUserControlDisabledPackages(receiver, listOf()); refresh() },
|
onClick = { pkgList += pkgName },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear_list))
|
Text(stringResource(R.string.add))
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = { dpm.setUserControlDisabledPackages(receiver, pkgList); refresh() },
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
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) {
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
ListItem(i) { crossProfilePkg -= i }
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
crossProfilePkg.add(pkgName)
|
|
||||||
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
crossProfilePkg.remove(pkgName)
|
|
||||||
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.remove))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Button(
|
||||||
|
onClick = { crossProfilePkg += pkgName },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.add))
|
||||||
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
crossProfilePkg.clear()
|
|
||||||
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
|
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
|
||||||
refresh()
|
refresh()
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear_list))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
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(
|
|
||||||
onClick = {
|
|
||||||
if(pkgName != "") { dpm.addCrossProfileWidgetProvider(receiver, pkgName) }
|
|
||||||
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(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
pkgList.forEach {
|
if(pkgName != "") { dpm.addCrossProfileWidgetProvider(receiver, pkgName) }
|
||||||
dpm.removeCrossProfileWidgetProvider(receiver, it)
|
refresh()
|
||||||
}
|
|
||||||
},
|
},
|
||||||
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) {
|
||||||
Spacer(Modifier.padding(vertical = 10.dp))
|
ListItem(i) { pkgList -= i }
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
credentialList.add(pkgName)
|
|
||||||
apply()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
credentialList.remove(pkgName)
|
|
||||||
apply()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.remove))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = { pkgList += pkgName },
|
||||||
credentialList.clear()
|
|
||||||
apply()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear_list))
|
Text(stringResource(R.string.add))
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
try {
|
||||||
|
if(policyType != -1 && pkgList.isNotEmpty()) {
|
||||||
|
dpm.credentialManagerPolicy = PackagePolicy(policyType, pkgList.toSet())
|
||||||
|
} else {
|
||||||
|
dpm.credentialManagerPolicy = null
|
||||||
|
}
|
||||||
|
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||||
|
} catch(_: IllegalArgumentException) {
|
||||||
|
Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show()
|
||||||
|
} finally {
|
||||||
|
refreshPolicy()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
|
) {
|
||||||
|
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 = { pkgList += pkgName },
|
||||||
onClick = {
|
modifier = Modifier.fillMaxWidth()
|
||||||
pkgList.add(pkgName)
|
) {
|
||||||
dpm.setPermittedAccessibilityServices(receiver, pkgList)
|
Text(stringResource(R.string.add))
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
pkgList.remove(pkgName)
|
|
||||||
dpm.setPermittedAccessibilityServices(receiver, pkgList)
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.remove))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
pkgList.clear()
|
|
||||||
dpm.setPermittedAccessibilityServices(receiver, pkgList)
|
dpm.setPermittedAccessibilityServices(receiver, pkgList)
|
||||||
refresh()
|
refresh()
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear_list))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 = { pkgList += pkgName },
|
||||||
onClick = {
|
modifier = Modifier.fillMaxWidth()
|
||||||
permittedIme.add(pkgName)
|
) {
|
||||||
dpm.setPermittedInputMethods(receiver, permittedIme)
|
Text(stringResource(R.string.add))
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
permittedIme.remove(pkgName)
|
|
||||||
dpm.setPermittedInputMethods(receiver, permittedIme)
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.remove))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
permittedIme.clear()
|
dpm.setPermittedInputMethods(receiver, pkgList)
|
||||||
dpm.setPermittedInputMethods(receiver, permittedIme)
|
|
||||||
refresh()
|
refresh()
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear_list))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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 = { pkgList += pkgName },
|
||||||
onClick = {
|
modifier = Modifier.fillMaxWidth()
|
||||||
pkgList.add(pkgName)
|
) {
|
||||||
dpm.setKeepUninstalledPackages(receiver, pkgList)
|
Text(stringResource(R.string.add))
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
pkgList.remove(pkgName)
|
|
||||||
dpm.setKeepUninstalledPackages(receiver, pkgList)
|
|
||||||
refresh()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.remove))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
pkgList.clear()
|
|
||||||
dpm.setKeepUninstalledPackages(receiver, pkgList)
|
dpm.setKeepUninstalledPackages(receiver, pkgList)
|
||||||
refresh()
|
refresh()
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear_list))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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.success, Toast.LENGTH_SHORT).show()
|
||||||
Toast.makeText(context, R.string.cannot_be_empty, Toast.LENGTH_SHORT).show()
|
refreshIds()
|
||||||
} else {
|
|
||||||
dpm.setAffiliationIds(receiver, affiliationID.toSet())
|
|
||||||
refreshIds()
|
|
||||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user