Add some AppManage feature

This commit is contained in:
BinTianqi
2024-02-02 12:23:08 +08:00
parent d335f56c42
commit f2a0a983e9
3 changed files with 101 additions and 44 deletions

View File

@@ -3,80 +3,93 @@ package com.binbin.androidowner
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException import android.content.pm.PackageManager.NameNotFoundException
import android.net.Uri
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Looper
import android.provider.Settings
import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable import androidx.compose.runtime.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
import java.util.concurrent.Executors
@Composable @Composable
fun ApplicationManage(){ fun ApplicationManage(){
val myContext = LocalContext.current val myContext = LocalContext.current
val focusMgr = LocalFocusManager.current
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java) val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
var pkgName by remember { mutableStateOf("") } var pkgName by remember { mutableStateOf("") }
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false) val isWear = sharedPref.getBoolean("isWear",false)
Column( val bodyTextStyle = if(isWear){ typography.bodyMedium }else{ typography.bodyLarge }
modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()), Column(modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField( TextField(
value = pkgName, value = pkgName,
onValueChange = { pkgName = it }, onValueChange = { pkgName = it },
label = { Text("包名") }, label = { Text("包名") },
enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm), enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm),
modifier = Modifier.fillMaxWidth().padding(horizontal = if(isWear){2.dp}else{12.dp},vertical = if(isWear){2.dp}else{6.dp}) modifier = Modifier.fillMaxWidth().padding(horizontal = if(isWear){2.dp}else{12.dp},vertical = if(isWear){2.dp}else{6.dp}),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
) )
if(VERSION.SDK_INT>=24){ if(VERSION.SDK_INT>=24){
val isSuspended = { try{ myDpm.isPackageSuspended(myComponent,pkgName) }catch(e:NameNotFoundException){ false } } val isSuspended = { try{ myDpm.isPackageSuspended(myComponent,pkgName) }catch(e:NameNotFoundException){ false } }
AppManageItem(R.string.suspend,R.string.place_holder,myDpm, isSuspended) { b -> myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName), b) } AppManageItem(R.string.suspend,R.string.place_holder, isSuspended) { b -> myDpm.setPackagesSuspended(myComponent, arrayOf(pkgName), b) }
} }
AppManageItem(R.string.hide,R.string.isapphidden_desc,myDpm, {myDpm.isApplicationHidden(myComponent,pkgName)}, {b -> myDpm.setApplicationHidden(myComponent,pkgName,b)}) AppManageItem(R.string.hide,R.string.isapphidden_desc, {myDpm.isApplicationHidden(myComponent,pkgName)}, {b -> myDpm.setApplicationHidden(myComponent,pkgName,b)})
if(VERSION.SDK_INT>=30){ if(VERSION.SDK_INT>=30){
AppManageItem(R.string.user_ctrl_disabled,R.string.user_ctrl_disabled_desc,myDpm, {pkgName in myDpm.getUserControlDisabledPackages(myComponent)}, AppManageItem(R.string.user_ctrl_disabled,R.string.user_ctrl_disabled_desc,{pkgName in myDpm.getUserControlDisabledPackages(myComponent)},
{b->myDpm.setUserControlDisabledPackages(myComponent, mutableListOf(if(b){pkgName}else{null}))}) {b->myDpm.setUserControlDisabledPackages(myComponent, mutableListOf(if(b){pkgName}else{null}))})
} }
if(VERSION.SDK_INT>=24){
AppManageItem(
R.string.always_on_vpn,R.string.experimental_feature,{pkgName == myDpm.getAlwaysOnVpnPackage(myComponent)},
{b ->
try{ myDpm.setAlwaysOnVpnPackage(myComponent,pkgName,b) }
catch(e:java.lang.UnsupportedOperationException){ Toast.makeText(myContext, "不支持", Toast.LENGTH_SHORT).show() }
catch(e:NameNotFoundException){ Toast.makeText(myContext, "未安装", Toast.LENGTH_SHORT).show() }
}
)
}
Row( Row(
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
modifier = if(isWear){sections().horizontalScroll(rememberScrollState())}else{sections()} modifier = if(isWear){sections().horizontalScroll(rememberScrollState())}else{sections()}
) { ) {
Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,false)}, enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm), Button(onClick = {focusMgr.clearFocus();myDpm.setUninstallBlocked(myComponent,pkgName,false)}, enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm),
modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.48F)}) { modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.48F)}) {
Text("允许卸载") Text("允许卸载")
} }
if(isWear){Spacer(Modifier.padding(horizontal = 3.dp))} if(isWear){Spacer(Modifier.padding(horizontal = 3.dp))}
Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,true)}, enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm), Button(onClick = {focusMgr.clearFocus();myDpm.setUninstallBlocked(myComponent,pkgName,true)}, enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm),
modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.92F)}) { modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.92F)}) {
Text("防卸载") Text("防卸载")
} }
} }
Column(modifier = sections()) { Column(modifier = sections()) {
Text(text = "许可的输入法", style = typography.titleLarge,color = MaterialTheme.colorScheme.onPrimaryContainer) Text(text = "许可的输入法", style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
var imeList = mutableListOf<String>() var imeList = mutableListOf<String>()
var imeListText by remember{ mutableStateOf("") } var imeListText by remember{ mutableStateOf("") }
val refreshList = { val refreshList = {
@@ -94,6 +107,7 @@ fun ApplicationManage(){
Button( Button(
onClick = { onClick = {
imeList.plus(pkgName) imeList.plus(pkgName)
focusMgr.clearFocus()
myDpm.setPermittedInputMethods(myComponent, imeList) myDpm.setPermittedInputMethods(myComponent, imeList)
refreshList() refreshList()
}, },
@@ -106,6 +120,7 @@ fun ApplicationManage(){
Button( Button(
onClick = { onClick = {
imeList.remove(pkgName) imeList.remove(pkgName)
focusMgr.clearFocus()
myDpm.setPermittedInputMethods(myComponent,imeList) myDpm.setPermittedInputMethods(myComponent,imeList)
refreshList() refreshList()
}, },
@@ -115,6 +130,59 @@ fun ApplicationManage(){
Text("从列表中移除") Text("从列表中移除")
}} }}
} }
Column(modifier = sections()){
Text(text = "清除应用存储", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button(
onClick = {
val executor = Executors.newCachedThreadPool()
val onClear = DevicePolicyManager.OnClearApplicationUserDataListener { pkg: String, succeed: Boolean ->
Looper.prepare()
focusMgr.clearFocus()
val toastText = if(pkg!=""){"$pkg\n"}else{""} + "数据清除" + if(succeed){"成功"}else{"失败"}
Toast.makeText(myContext, toastText, Toast.LENGTH_SHORT).show()
Looper.loop()
}
if(VERSION.SDK_INT>=28){
myDpm.clearApplicationUserData(myComponent,pkgName,executor,onClear)
}
},
enabled = (isDeviceOwner(myDpm)||isProfileOwner(myDpm))&&VERSION.SDK_INT>=28,
modifier = Modifier.fillMaxWidth(0.48F)
) {
Text("清除")
}
Button(
onClick = {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.setData(Uri.parse("package:$pkgName"))
startActivity(myContext,intent,null)
},
modifier = Modifier.fillMaxWidth(0.92F)
){
Text("详情")
}
}
if(VERSION.SDK_INT<28){
Text(text = "清除存储需API28", style = bodyTextStyle)
}
}
if(VERSION.SDK_INT>=34){
Button(
onClick = {
try{
myDpm.setDefaultDialerApplication(pkgName)
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}catch(e:IllegalArgumentException){
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
) {
Text("设为默认拨号应用")
}
}
Spacer(Modifier.padding(30.dp)) Spacer(Modifier.padding(30.dp))
} }
} }
@@ -123,10 +191,11 @@ fun ApplicationManage(){
private fun AppManageItem( private fun AppManageItem(
itemName:Int, itemName:Int,
itemDesc:Int, itemDesc:Int,
myDpm: DevicePolicyManager,
getMethod:()->Boolean, getMethod:()->Boolean,
setMethod:(b:Boolean)->Unit setMethod:(b:Boolean)->Unit
){ ){
val myDpm = LocalContext.current.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val focusMgr = LocalFocusManager.current
var isEnabled by remember{ mutableStateOf(false) } var isEnabled by remember{ mutableStateOf(false) }
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){ isEnabled = getMethod() } if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){ isEnabled = getMethod() }
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
@@ -137,14 +206,8 @@ private fun AppManageItem(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Column { Column {
Text( Text(text = stringResource(itemName), style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
text = stringResource(itemName), if(itemDesc!=R.string.place_holder){ Text(stringResource(itemDesc)) }
style = typography.titleLarge,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
if(itemDesc!=R.string.place_holder){
Text(stringResource(itemDesc))
}
} }
Switch( Switch(
checked = isEnabled, checked = isEnabled,
@@ -159,21 +222,18 @@ private fun AppManageItem(
modifier = sections() modifier = sections()
) { ) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
Text( Text(text = stringResource(itemName))
text = stringResource(itemName)
)
Switch( Switch(
checked = isEnabled, checked = isEnabled,
onCheckedChange = { onCheckedChange = {
setMethod(!isEnabled) setMethod(!isEnabled)
isEnabled = getMethod() isEnabled = getMethod()
focusMgr.clearFocus()
}, },
enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm) enabled = isDeviceOwner(myDpm)|| isProfileOwner(myDpm)
) )
} }
if(itemDesc!=R.string.place_holder){ if(itemDesc!=R.string.place_holder){ Text(text = stringResource(itemDesc), style = typography.bodyMedium) }
Text(text = stringResource(itemDesc), style = if(!sharedPref.getBoolean("isWear",false)){typography.bodyLarge}else{typography.bodyMedium})
}
} }
} }
} }

View File

@@ -388,10 +388,7 @@ fun DeviceOwnerInfo(
onValueChange = { inputContent=it }, onValueChange = { inputContent=it },
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp) modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
) )
Row( Row(modifier = Modifier.fillMaxWidth().padding(vertical = if(isWear){2.dp}else{6.dp}), horizontalArrangement = Arrangement.SpaceBetween) {
modifier = Modifier.padding(vertical = if(isWear){2.dp}else{6.dp}),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button( Button(
onClick = { onClick = {
output(inputContent.toString()) output(inputContent.toString())
@@ -403,7 +400,6 @@ fun DeviceOwnerInfo(
) { ) {
Text(text = "应用") Text(text = "应用")
} }
Spacer(Modifier.padding(horizontal = 4.dp))
Button( Button(
onClick = { onClick = {
output(null) output(null)
@@ -416,7 +412,6 @@ fun DeviceOwnerInfo(
Text(text = "重置") Text(text = "重置")
} }
} }
} }
} }

View File

@@ -108,4 +108,6 @@
<string name="disable_bt_contact_share">禁止蓝牙分享联系人</string> <string name="disable_bt_contact_share">禁止蓝牙分享联系人</string>
<string name="user_manage">用户管理</string> <string name="user_manage">用户管理</string>
<string name="setting">设置</string> <string name="setting">设置</string>
<string name="always_on_vpn">VPN常开</string>
<string name="experimental_feature">实验性功能</string>
</resources> </resources>