optimize some functions in App manager

optimize Permitted accessibility services and Permitted IME
use state to store their package list instead of create global variables
update related document
This commit is contained in:
BinTianqi
2024-05-28 20:46:17 +08:00
parent bb516b4acb
commit 3fd00a5ce0
5 changed files with 286 additions and 206 deletions

View File

@@ -681,7 +681,7 @@ pm list packages
如果是Device owner需要API30或以上如果是Profile owner需要API33或以上
谷歌并没有在文档中说明Profile owner调用此功能所需的API等级但是[MinoriceOWO](https://github.com/MinoriceOwO)发现Profile owner在API33以下调用这个功能会导致OwnDroid崩溃[(issue #12)](https://github.com/BinTianqi/OwnDroid/issues/12)。[commit d4e8473](https://github.com/BinTianqi/OwnDroid/commit/d4e8473218a6d91bf3608133061f8e636e48cdbb)中已对API33以下的Profile owner隐藏该功能入口
用户无法清除这些应用的存储空间和缓存,但是可以在这里清除
用户无法清除这些应用的存储空间也无法强制停止应用OwnDroid中的[清除应用存储](#清除应用存储)不受影响
### 权限管理
@@ -729,21 +729,22 @@ pm list permissions
作用未知
### 许可的无障碍应用&输入法
### 许可的无障碍服务&输入法
需要的权限Device owner或Profile owner
无障碍应用:强制启用无障碍应用
- 允许所有
- 允许指定app
输入法:强制启用输入法,但是不强制用户使用输入法
无论如何,系统的无障碍服务和输入法都是允许的
### 保持卸载的应用
### 卸载后保留的应用
需要Device owner
需要API28或以上
作用未知
这个列表中的应用的APK将会一直保留即使没有任何用户安装这个应用
### 清除应用存储

View File

@@ -33,6 +33,7 @@ import androidx.compose.material3.*
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
@@ -57,12 +58,6 @@ import java.io.IOException
import java.io.InputStream
import java.util.concurrent.Executors
private var credentialList = mutableSetOf<String>()
private var crossProfilePkg = mutableSetOf<String>()
private var keepUninstallPkg = mutableListOf<String>()
private var permittedIme = mutableListOf<String>()
private var permittedAccessibility = mutableListOf<String>()
private var dialogConfirmButtonAction = {}
private var dialogDismissButtonAction = {}
private var dialogGetStatus = { false }
@@ -79,9 +74,9 @@ fun ApplicationManage(navCtrl:NavHostController, pkgName: MutableState<String>,
"CrossProfilePackage" to R.string.cross_profile_package,
"CrossProfileWidget" to R.string.cross_profile_widget,
"CredentialManagePolicy" to R.string.credential_manage_policy,
"Accessibility" to R.string.permitted_accessibility_app,
"Accessibility" to R.string.permitted_accessibility_services,
"IME" to R.string.permitted_ime,
"KeepUninstalled" to R.string.keep_uninstalled_pkgs,
"KeepUninstalled" to R.string.keep_uninstalled_packages,
"InstallApp" to R.string.install_app,
"UninstallApp" to R.string.uninstall_app,
"ClearAppData" to R.string.clear_app_storage,
@@ -255,13 +250,13 @@ private fun Home(
SubPageItem(R.string.credential_manage_policy, "", R.drawable.license_fill0) { navCtrl.navigate("CredentialManagePolicy") }
}
if(isProfileOwner(dpm)||isDeviceOwner(dpm)) {
SubPageItem(R.string.permitted_accessibility_app, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
SubPageItem(R.string.permitted_accessibility_services, "", R.drawable.settings_accessibility_fill0) { navCtrl.navigate("Accessibility") }
}
if(isDeviceOwner(dpm)||isProfileOwner(dpm)) {
SubPageItem(R.string.permitted_ime, "", R.drawable.keyboard_fill0) { navCtrl.navigate("IME") }
}
if(VERSION.SDK_INT>=28&&isDeviceOwner(dpm)) {
SubPageItem(R.string.keep_uninstalled_pkgs, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
SubPageItem(R.string.keep_uninstalled_packages, "", R.drawable.delete_fill0) { navCtrl.navigate("KeepUninstalled") }
}
if(VERSION.SDK_INT>=28) {
SubPageItem(R.string.clear_app_storage, "", R.drawable.mop_fill0) {
@@ -284,13 +279,13 @@ private fun UserCtrlDisabledPkg(pkgName:String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
var pkgList = dpm.getUserControlDisabledPackages(receiver)
var listText by remember{mutableStateOf("")}
val pkgList = remember { mutableStateListOf<String>() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
val refresh = {
pkgList = dpm.getUserControlDisabledPackages(receiver)
listText = pkgList.toText()
pkgList.clear()
dpm.getUserControlDisabledPackages(receiver).forEach { pkgList.add(it) }
}
LaunchedEffect(Unit) { refresh() }
var inited by remember{mutableStateOf(false)}
if(!inited) { refresh();inited=true }
Spacer(Modifier.padding(vertical = 10.dp))
@@ -300,19 +295,15 @@ private fun UserCtrlDisabledPkg(pkgName:String) {
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) {
Text(text = if(listText=="") { stringResource(R.string.none)}else{listText})
Text(text = if(pkgList.isEmpty()) stringResource(R.string.none) else pkgList.toText())
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
if(pkgName!="") {
pkgList.add(pkgName)
dpm.setUserControlDisabledPackages(receiver,pkgList)
refresh()
}else{
Toast.makeText(context, R.string.fail, Toast.LENGTH_SHORT).show()
}
pkgList.add(pkgName)
dpm.setUserControlDisabledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) {
@@ -320,13 +311,9 @@ private fun UserCtrlDisabledPkg(pkgName:String) {
}
Button(
onClick = {
val result = if(pkgName!="") { pkgList.remove(pkgName)}else{false}
if(result) {
dpm.setUserControlDisabledPackages(receiver,pkgList)
refresh()
}else{
Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show()
}
pkgList.remove(pkgName)
dpm.setUserControlDisabledPackages(receiver,pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
) {
@@ -429,25 +416,25 @@ private fun PermissionManage(pkgName: String, navCtrl: NavHostController) {
private fun CrossProfilePkg(pkgName: String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
val receiver = ComponentName(context, Receiver::class.java)
val crossProfilePkg = remember { mutableStateListOf<String>() }
val refresh = {
crossProfilePkg.clear()
dpm.getCrossProfilePackages(receiver).forEach { crossProfilePkg += it }
}
LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.cross_profile_package), style = typography.headlineLarge)
var list by remember{mutableStateOf("")}
val refresh = {
crossProfilePkg = dpm.getCrossProfilePackages(receiver)
list = crossProfilePkg.toText()
}
LaunchedEffect(Unit) { refresh() }
Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) {
Text(text = if(list=="") stringResource(R.string.none) else list)
Text(text = if(crossProfilePkg.isEmpty()) stringResource(R.string.none) else crossProfilePkg.toText())
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
if(pkgName!="") { crossProfilePkg.add(pkgName) }
dpm.setCrossProfilePackages(receiver, crossProfilePkg)
crossProfilePkg.add(pkgName)
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
@@ -456,8 +443,8 @@ private fun CrossProfilePkg(pkgName: String) {
}
Button(
onClick = {
if(pkgName!="") { crossProfilePkg.remove(pkgName) }
dpm.setCrossProfilePackages(receiver, crossProfilePkg)
crossProfilePkg.remove(pkgName)
dpm.setCrossProfilePackages(receiver, crossProfilePkg.toSet())
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
@@ -465,6 +452,16 @@ private fun CrossProfilePkg(pkgName: String) {
Text(stringResource(R.string.remove))
}
}
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))
}
}
@@ -474,26 +471,27 @@ private fun CrossProfileWidget(pkgName: String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
var pkgList: MutableList<String>
var list by remember{mutableStateOf("")}
val refresh = {
pkgList = dpm.getCrossProfileWidgetProviders(receiver)
list = pkgList.toText()
val pkgList = remember { mutableStateListOf<String>() }
val refresh = {
pkgList.clear()
dpm.getCrossProfileWidgetProviders(receiver).forEach {
pkgList += it
}
LaunchedEffect(Unit) { refresh()}
}
LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.cross_profile_widget), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) {
Text(text = if(list=="") stringResource(R.string.none) else list)
Text(text = if(pkgList.isEmpty()) stringResource(R.string.none) else pkgList.toText())
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
if(pkgName!="") { dpm.addCrossProfileWidgetProvider(receiver,pkgName) }
if(pkgName != "") { dpm.addCrossProfileWidgetProvider(receiver, pkgName) }
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
@@ -502,7 +500,7 @@ private fun CrossProfileWidget(pkgName: String) {
}
Button(
onClick = {
if(pkgName!="") { dpm.removeCrossProfileWidgetProvider(receiver,pkgName) }
if(pkgName != "") { dpm.removeCrossProfileWidgetProvider(receiver, pkgName) }
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
@@ -510,6 +508,16 @@ private fun CrossProfileWidget(pkgName: String) {
Text(stringResource(R.string.remove))
}
}
Button(
onClick = {
pkgList.forEach {
dpm.removeCrossProfileWidgetProvider(receiver, it)
}
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
}
Spacer(Modifier.padding(vertical = 10.dp))
}
}
@@ -519,17 +527,29 @@ private fun CrossProfileWidget(pkgName: String) {
private fun CredentialManagePolicy(pkgName: String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val focusMgr = LocalFocusManager.current
var policy:PackagePolicy?
var policyType by remember{mutableIntStateOf(-1)}
var credentialListText by remember{mutableStateOf("")}
var policy: PackagePolicy?
var policyType by remember{ mutableIntStateOf(-1) }
val credentialList = remember { mutableStateListOf<String>() }
val refreshPolicy = {
policy = dpm.credentialManagerPolicy
policyType = policy?.policyType ?: -1
credentialList = policy?.packageNames ?: mutableSetOf()
credentialList = credentialList.toMutableSet()
(policy?.packageNames ?: mutableSetOf()).forEach { credentialList += it }
}
LaunchedEffect(Unit) { refreshPolicy(); credentialListText = credentialList.toText() }
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.fail, Toast.LENGTH_SHORT).show()
} finally {
refreshPolicy()
}
}
LaunchedEffect(Unit) { refreshPolicy() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.credential_manage_policy), style = typography.headlineLarge)
@@ -545,8 +565,8 @@ private fun CredentialManagePolicy(pkgName: String) {
)
RadioButtonItem(
stringResource(R.string.whitelist),
{policyType==PACKAGE_POLICY_ALLOWLIST},
{policyType=PACKAGE_POLICY_ALLOWLIST}
{ policyType==PACKAGE_POLICY_ALLOWLIST },
{ policyType=PACKAGE_POLICY_ALLOWLIST }
)
RadioButtonItem(
stringResource(R.string.whitelist_and_system_app),
@@ -554,18 +574,18 @@ private fun CredentialManagePolicy(pkgName: String) {
{ policyType=PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM }
)
Spacer(Modifier.padding(vertical = 5.dp))
AnimatedVisibility(policyType!=-1) {
AnimatedVisibility(policyType != -1) {
Column {
Text(stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())) {
Text(text = if(credentialListText!="") credentialListText else stringResource(R.string.none))
Text(text = if(credentialList.isEmpty()) stringResource(R.string.none) else credentialList.toText())
}
Spacer(Modifier.padding(vertical = 10.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
if(pkgName!="") { credentialList.add(pkgName) }
credentialListText = credentialList.toText()
credentialList.add(pkgName)
apply()
},
modifier = Modifier.fillMaxWidth(0.49F)
) {
@@ -573,36 +593,24 @@ private fun CredentialManagePolicy(pkgName: String) {
}
Button(
onClick = {
if(pkgName!="") { credentialList.remove(pkgName) }
credentialListText = credentialList.toText()
credentialList.remove(pkgName)
apply()
},
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.remove))
}
}
}
}
Button(
onClick = {
focusMgr.clearFocus()
try{
if(policyType!=-1&&credentialList.isNotEmpty()) {
dpm.credentialManagerPolicy = PackagePolicy(policyType, credentialList)
}else{
dpm.credentialManagerPolicy = null
}
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
}catch(e:java.lang.IllegalArgumentException) {
Toast.makeText(context, R.string.fail, Toast.LENGTH_SHORT).show()
}finally {
refreshPolicy()
credentialListText = credentialList.toText()
Button(
onClick = {
credentialList.clear()
apply()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
}
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.apply))
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
@@ -613,51 +621,82 @@ private fun PermittedAccessibility(pkgName: String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
val focusMgr = LocalFocusManager.current
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
val pkgList = remember { mutableStateListOf<String>() }
var allowAll by remember { mutableStateOf(false) }
val refresh = {
pkgList.clear()
val getList = dpm.getPermittedAccessibilityServices(receiver)
if(getList != null) {
allowAll = false
getList.forEach { pkgList += it }
} else {
allowAll = true
}
}
LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.permitted_accessibility_app), style = typography.headlineLarge)
var listText by remember{ mutableStateOf("") }
LaunchedEffect(Unit) {
val getList = dpm.getPermittedAccessibilityServices(receiver)
if(getList!=null) { permittedAccessibility = getList }
listText = permittedAccessibility.toText()
}
Text(text = stringResource(R.string.permitted_accessibility_services), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) {
Text(text = if(listText=="") stringResource(R.string.none) else listText)
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = { permittedAccessibility.add(pkgName); listText = permittedAccessibility.toText() },
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.add))
}
Button(
onClick = { permittedAccessibility.remove(pkgName); listText = permittedAccessibility.toText() },
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.remove))
}
}
Button(
onClick = {
focusMgr.clearFocus()
Toast.makeText(
context,
if(dpm.setPermittedAccessibilityServices(receiver, permittedAccessibility)) R.string.success else R.string.fail ,
Toast.LENGTH_SHORT
).show()
val getList = dpm.getPermittedAccessibilityServices(receiver)
if(getList!=null) { permittedAccessibility = getList }
listText = permittedAccessibility.toText()
},
modifier = Modifier.fillMaxWidth()
Row(
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp)
) {
Text(text = stringResource(R.string.apply))
Text(stringResource(R.string.allow_all), style = typography.titleLarge)
Switch(
checked = allowAll,
onCheckedChange = {
dpm.setPermittedAccessibilityServices(receiver, if(it) null else listOf())
refresh()
}
)
}
AnimatedVisibility(!allowAll) {
Column {
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize()) {
if (pkgList.isEmpty()) {
Text(stringResource(R.string.only_system_accessibility_allowed))
} else {
Text(stringResource(R.string.permitted_packages_is) + pkgList.toText())
}
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
pkgList.add(pkgName)
dpm.setPermittedAccessibilityServices(receiver, pkgList)
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(
onClick = {
pkgList.clear()
dpm.setPermittedAccessibilityServices(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
}
}
}
Information {
Text(stringResource(R.string.system_accessibility_always_allowed))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
@@ -668,51 +707,82 @@ private fun PermittedIME(pkgName: String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
val focusMgr = LocalFocusManager.current
val permittedIme = remember { mutableStateListOf<String>() }
var allowAll by remember { mutableStateOf(false) }
val refresh = {
permittedIme.clear()
val getList = dpm.getPermittedInputMethods(receiver)
if(getList != null) {
allowAll = false
getList.forEach { permittedIme += it }
} else {
allowAll = true
}
}
LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.permitted_ime), style = typography.headlineLarge)
var imeListText by remember{ mutableStateOf("") }
LaunchedEffect(Unit) {
val getList = dpm.getPermittedInputMethods(receiver)
if(getList!=null) { permittedIme = getList }
imeListText = permittedIme.toText()
}
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())) {
Text(text = if(imeListText=="") stringResource(R.string.none) else imeListText)
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = { permittedIme.add(pkgName); imeListText = permittedIme.toText() },
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.add))
}
Button(
onClick = { permittedIme.remove(pkgName); imeListText = permittedIme.toText() },
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.remove))
}
}
Button(
onClick = {
focusMgr.clearFocus()
Toast.makeText(
context,
if(dpm.setPermittedInputMethods(receiver, permittedIme)) R.string.success else R.string.fail,
Toast.LENGTH_SHORT
).show()
val getList = dpm.getPermittedInputMethods(receiver)
if(getList!=null) { permittedIme = getList }
imeListText = permittedIme.toText()
},
modifier = Modifier.fillMaxWidth()
Row(
horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(horizontal = 6.dp, vertical = 8.dp)
) {
Text(stringResource(R.string.apply))
Text(stringResource(R.string.allow_all), style = typography.titleLarge)
Switch(
checked = allowAll,
onCheckedChange = {
dpm.setPermittedInputMethods(receiver, if(it) null else listOf())
refresh()
}
)
}
AnimatedVisibility(!allowAll) {
Column {
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())) {
if(permittedIme.isEmpty()) {
Text(stringResource(R.string.only_system_ime_allowed))
} else {
Text(stringResource(R.string.permitted_packages_is) + permittedIme.toText())
}
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
permittedIme.add(pkgName)
dpm.setPermittedInputMethods(receiver, permittedIme)
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(
onClick = {
permittedIme.clear()
dpm.setPermittedInputMethods(receiver, permittedIme)
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.clear_list))
}
}
}
Information {
Text(stringResource(R.string.system_ime_always_allowed))
}
Spacer(Modifier.padding(vertical = 30.dp))
}
@@ -724,27 +794,27 @@ private fun KeepUninstalledApp(pkgName: String) {
val context = LocalContext.current
val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val receiver = ComponentName(context,Receiver::class.java)
val focusMgr = LocalFocusManager.current
val pkgList = remember { mutableStateListOf<String>() }
val refresh = {
pkgList.clear()
dpm.getKeepUninstalledPackages(receiver)?.forEach { pkgList += it }
}
LaunchedEffect(Unit) { refresh() }
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) {
Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.keep_uninstalled_pkgs), style = typography.headlineLarge)
var listText by remember{ mutableStateOf("") }
LaunchedEffect(Unit) {
val getList = dpm.getKeepUninstalledPackages(receiver)
if(getList!=null) { keepUninstallPkg = getList }
listText = keepUninstallPkg.toText()
}
Text(text = stringResource(R.string.keep_uninstalled_packages), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp))
Text(text = stringResource(R.string.app_list_is))
SelectionContainer(modifier = Modifier.horizontalScroll(rememberScrollState()).animateContentSize(scrollAnim())) {
Text(text = if(listText=="") stringResource(R.string.none) else listText)
Text(text = if(pkgList.isEmpty()) stringResource(R.string.none) else pkgList.toText())
}
Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
keepUninstallPkg.add(pkgName)
listText = keepUninstallPkg.toText()
pkgList.add(pkgName)
dpm.setKeepUninstalledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.49F)
) {
@@ -752,8 +822,9 @@ private fun KeepUninstalledApp(pkgName: String) {
}
Button(
onClick = {
keepUninstallPkg.remove(pkgName)
listText = keepUninstallPkg.toText()
pkgList.remove(pkgName)
dpm.setKeepUninstalledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth(0.96F)
) {
@@ -762,16 +833,13 @@ private fun KeepUninstalledApp(pkgName: String) {
}
Button(
onClick = {
focusMgr.clearFocus()
dpm.setKeepUninstalledPackages(receiver, keepUninstallPkg)
val getList = dpm.getKeepUninstalledPackages(receiver)
if(getList!=null) { keepUninstallPkg = getList }
listText = keepUninstallPkg.toText()
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
pkgList.clear()
dpm.setKeepUninstalledPackages(receiver, pkgList)
refresh()
},
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.apply))
) {
Text(stringResource(R.string.clear_list))
}
Spacer(Modifier.padding(vertical = 30.dp))
}

View File

@@ -65,15 +65,14 @@ fun NavIcon(operation: () -> Unit) {
@Composable
fun Information(content: @Composable ()->Unit) {
Column(modifier = Modifier.fillMaxWidth().padding(start = 5.dp)) {
Column(modifier = Modifier.fillMaxWidth().padding(start = 5.dp, top = 20.dp)) {
Icon(
painter = painterResource(R.drawable.info_fill0),
contentDescription = "info",
tint = colorScheme.onBackground.copy(alpha = 0.8F)
)
Spacer(Modifier.padding(vertical = 1.dp))
Row {
Spacer(Modifier.padding(horizontal = 1.dp))
Column(modifier = Modifier.padding(start = 2.dp)) {
content()
}
}

View File

@@ -48,6 +48,7 @@
<string name="current_status_is">当前状态:</string>
<string name="start">开始</string>
<string name="unknown_error">未知错误</string>
<string name="allow_all">允许全部</string>
<!--Permissions-->
<string name="click_to_activate">点击以激活</string>
@@ -268,7 +269,7 @@
<string name="not_installed">未安装</string>
<string name="block_uninstall">防卸载</string>
<string name="ucd">禁止用户控制</string>
<string name="ucd_desc">用户将无法清除应用的存储空间和缓存</string>
<string name="ucd_desc">用户将无法清除这些应用的存储空间或强制停止这些应用</string>
<string name="app_list_is">应用列表:</string>
<string name="clear_list">清空列表</string>
<string name="permission_manage">权限管理</string>
@@ -276,9 +277,14 @@
<string name="cross_profile_widget">跨资料微件</string>
<string name="credential_manage_policy">凭据管理策略</string>
<string name="whitelist_and_system_app">白名单和系统应用</string>
<string name="permitted_accessibility_app">许可的无障碍应用</string>
<string name="permitted_packages_is">许可的应用\n</string>
<string name="permitted_accessibility_services">许可的无障碍服务</string>
<string name="only_system_accessibility_allowed">只允许系统无障碍服务</string>
<string name="system_accessibility_always_allowed">系统的无障碍服务不受影响</string>
<string name="only_system_ime_allowed">只允许系统输入法</string>
<string name="system_ime_always_allowed">系统输入法不受影响</string>
<string name="permitted_ime">许可的输入法</string>
<string name="keep_uninstalled_pkgs">保持卸载的应用</string>
<string name="keep_uninstalled_packages">卸载后保留的应用</string>
<string name="clear_data">数据清除</string>
<string name="clear_app_storage">清除应用存储</string>
<string name="app_storage_will_be_cleared">这个应用的存储空间将被清空</string>

View File

@@ -51,6 +51,7 @@
<string name="current_status_is">Current status:&#160;</string>
<string name="start">Start</string>
<string name="unknown_error">Unknown error</string>
<string name="allow_all">Allow all</string>
<!--Permissions-->
<string name="click_to_activate">Click to activate</string>
@@ -283,7 +284,7 @@
<string name="block_uninstall">Block uninstall</string>
<!--ucd: user control disabled-->
<string name="ucd">Disable user control</string>
<string name="ucd_desc">If you set this, you cannot clear storage or cache of the app. </string>
<string name="ucd_desc">If you set this, you cannot clear these apps\' storage or force stop them. </string>
<string name="app_list_is">App list: </string>
<string name="clear_list">Clear list</string>
<string name="permission_manage">Permission manage</string>
@@ -291,9 +292,14 @@
<string name="cross_profile_widget">Cross profile widget</string>
<string name="credential_manage_policy">Credential manage policy</string>
<string name="whitelist_and_system_app">Whitelist and system app</string>
<string name="permitted_accessibility_app">Permitted accessibility app</string>
<string name="permitted_packages_is">Permitted packages: \n</string>
<string name="permitted_accessibility_services">Permitted accessibility services</string>
<string name="only_system_accessibility_allowed">Only system accessibility services are allowed</string>
<string name="system_accessibility_always_allowed">System accessibility services are always allowed.</string>
<string name="only_system_ime_allowed">Only system input methods are allowed</string>
<string name="system_ime_always_allowed">System input methods are always allowed.</string>
<string name="permitted_ime">Permitted IME</string>
<string name="keep_uninstalled_pkgs">Keep uninstalled packages</string>
<string name="keep_uninstalled_packages">Keep uninstalled packages</string>
<string name="clear_data">Clear data</string>
<string name="clear_app_storage">Clear app storage</string>
<string name="app_storage_will_be_cleared">This app\'s storage will be cleared</string>