permission picker in AppManage

This commit is contained in:
BinTianqi
2024-04-05 18:25:27 +08:00
parent aa5fbd0b33
commit 1d8178c96e
9 changed files with 165 additions and 18 deletions

View File

@@ -691,7 +691,7 @@ adb shell pm list permissions
从允许或拒绝改为由用户决定会保持当前的状态 从允许或拒绝改为由用户决定会保持当前的状态
有一些权限无法修改,比如安装应用 只能修改运行时权限(可以通过对话框授权的权限)
在API31或以上Profile owner不能再修改传感器相关权限如果能修改传感器相关权限说明这个设备是完全受管理设备Device owner 在API31或以上Profile owner不能再修改传感器相关权限如果能修改传感器相关权限说明这个设备是完全受管理设备Device owner

View File

@@ -31,7 +31,6 @@ _我正在为这个App取一个新的名字......_
### 正在开发的功能 ### 正在开发的功能
- 应用管理:应用权限选择器(现在只能手动输入权限名称)
- 用户管理:用户选择器(现在只能手动输入用户序列号) - 用户管理:用户选择器(现在只能手动输入用户序列号)
- 安全日志和网络日志 - 安全日志和网络日志

View File

@@ -11,8 +11,8 @@ android {
applicationId = "com.binbin.androidowner" applicationId = "com.binbin.androidowner"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 22 versionCode = 23
versionName = "4.5" versionName = "4.6"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {

View File

@@ -108,6 +108,7 @@ fun MyScaffold(){
composable(route = "AppSetting", content = { AppSetting(navCtrl)}) composable(route = "AppSetting", content = { AppSetting(navCtrl)})
composable(route = "Network", content = {Network(navCtrl)}) composable(route = "Network", content = {Network(navCtrl)})
composable(route = "PackageSelector"){PackageSelector(navCtrl)} composable(route = "PackageSelector"){PackageSelector(navCtrl)}
composable(route = "PermissionPicker"){PermissionPicker(navCtrl)}
} }
LaunchedEffect(Unit){ LaunchedEffect(Unit){
val profileInited = sharedPref.getBoolean("ManagedProfileActivated",false) val profileInited = sharedPref.getBoolean("ManagedProfileActivated",false)

View File

@@ -0,0 +1,96 @@
package com.binbin.androidowner
import android.Manifest
import android.os.Build.VERSION
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import com.binbin.androidowner.dpm.applySelectedPermission
import com.binbin.androidowner.dpm.selectedPermission
import com.binbin.androidowner.ui.NavIcon
import com.binbin.androidowner.ui.theme.bgColor
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PermissionPicker(navCtrl: NavHostController){
Scaffold(
topBar = {
TopAppBar(
title = {Text(text = stringResource(R.string.permission_picker))},
navigationIcon = {NavIcon{navCtrl.navigateUp()}},
colors = TopAppBarDefaults.topAppBarColors(containerColor = bgColor)
)
}
){ paddingValues->
LazyColumn(
modifier = Modifier.fillMaxSize().padding(top = paddingValues.calculateTopPadding()).background(bgColor)
){
items(permissionList()){
Column(
modifier = Modifier
.fillMaxWidth()
.clickable{
selectedPermission = it.first
applySelectedPermission = true
navCtrl.navigateUp()
}
.padding(vertical = 6.dp, horizontal = 8.dp)
){
Text(text = it.first)
Text(text = stringResource(it.second), modifier = Modifier.alpha(0.8F))
}
}
items(1){ Spacer(Modifier.padding(vertical = 30.dp)) }
}
}
}
private fun permissionList():List<Pair<String,Int>>{
val list = mutableListOf<Pair<String,Int>>()
list.add(Pair(Manifest.permission.READ_EXTERNAL_STORAGE,R.string.permission_READ_EXTERNAL_STORAGE))
list.add(Pair(Manifest.permission.WRITE_EXTERNAL_STORAGE,R.string.permission_WRITE_EXTERNAL_STORAGE))
if(VERSION.SDK_INT>=33){
list.add(Pair(Manifest.permission.READ_MEDIA_AUDIO,R.string.permission_READ_MEDIA_AUDIO))
list.add(Pair(Manifest.permission.READ_MEDIA_VIDEO,R.string.permission_READ_MEDIA_VIDEO))
list.add(Pair(Manifest.permission.READ_MEDIA_IMAGES,R.string.permission_READ_MEDIA_IMAGES))
}
list.add(Pair(Manifest.permission.CAMERA,R.string.permission_CAMERA))
list.add(Pair(Manifest.permission.RECORD_AUDIO,R.string.permission_RECORD_AUDIO))
list.add(Pair(Manifest.permission.ACCESS_COARSE_LOCATION,R.string.permission_ACCESS_COARSE_LOCATION))
list.add(Pair(Manifest.permission.ACCESS_FINE_LOCATION,R.string.permission_ACCESS_FINE_LOCATION))
if(VERSION.SDK_INT>=29){
list.add(Pair(Manifest.permission.ACCESS_BACKGROUND_LOCATION,R.string.permission_ACCESS_BACKGROUND_LOCATION))
}
list.add(Pair(Manifest.permission.READ_CONTACTS,R.string.permission_READ_CONTACTS))
list.add(Pair(Manifest.permission.WRITE_CONTACTS,R.string.permission_WRITE_CONTACTS))
list.add(Pair(Manifest.permission.READ_CALENDAR,R.string.permission_READ_CALENDAR))
list.add(Pair(Manifest.permission.WRITE_CALENDAR,R.string.permission_WRITE_CALENDAR))
list.add(Pair(Manifest.permission.CALL_PHONE,R.string.permission_CALL_PHONE))
list.add(Pair(Manifest.permission.READ_PHONE_STATE,R.string.permission_READ_PHONE_STATE))
list.add(Pair(Manifest.permission.READ_SMS,R.string.permission_READ_SMS))
list.add(Pair(Manifest.permission.RECEIVE_SMS,R.string.permission_RECEIVE_SMS))
list.add(Pair(Manifest.permission.SEND_SMS,R.string.permission_SEND_SMS))
list.add(Pair(Manifest.permission.READ_CALL_LOG,R.string.permission_READ_CALL_LOG))
list.add(Pair(Manifest.permission.WRITE_CALL_LOG,R.string.permission_WRITE_CALL_LOG))
list.add(Pair(Manifest.permission.BODY_SENSORS,R.string.permission_BODY_SENSORS))
if(VERSION.SDK_INT>=33){
list.add(Pair(Manifest.permission.BODY_SENSORS_BACKGROUND,R.string.permission_BODY_SENSORS_BACKGROUND))
}
if(VERSION.SDK_INT>29){
list.add(Pair(Manifest.permission.ACTIVITY_RECOGNITION,R.string.permission_ACTIVITY_RECOGNITION))
}
if(VERSION.SDK_INT>=33){
list.add(Pair(Manifest.permission.POST_NOTIFICATIONS,R.string.permission_POST_NOTIFICATIONS))
}
//list.add(Pair(Manifest.permission.,R.string.))
return list
}

View File

@@ -152,6 +152,7 @@ fun PackageSelector(navCtrl:NavHostController){
PackageItem(it, navCtrl) PackageItem(it, navCtrl)
} }
} }
items(1){Spacer(Modifier.padding(vertical = 30.dp))}
}else{ }else{
items(1){ items(1){
Spacer(Modifier.padding(top = 5.dp)) Spacer(Modifier.padding(top = 5.dp))

View File

@@ -64,6 +64,7 @@ private var crossProfilePkg = mutableSetOf<String>()
private var keepUninstallPkg = mutableListOf<String>() private var keepUninstallPkg = mutableListOf<String>()
private var permittedIme = mutableListOf<String>() private var permittedIme = mutableListOf<String>()
private var permittedAccessibility = mutableListOf<String>() private var permittedAccessibility = mutableListOf<String>()
@Composable @Composable
fun ApplicationManage(navCtrl:NavHostController){ fun ApplicationManage(navCtrl:NavHostController){
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
@@ -98,8 +99,8 @@ fun ApplicationManage(navCtrl:NavHostController){
Column(modifier = Modifier.fillMaxSize().padding(top = paddingValues.calculateTopPadding())){ Column(modifier = Modifier.fillMaxSize().padding(top = paddingValues.calculateTopPadding())){
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
while(true){ while(true){
if(applySelectedPackage){pkgName = selectedPackage; applySelectedPackage = false} if(applySelectedPackage){ pkgName = selectedPackage; applySelectedPackage = false; applySelectedPermission = true}
delay(200) delay(100)
} }
} }
if(backStackEntry?.destination?.route!="InstallApp"){ if(backStackEntry?.destination?.route!="InstallApp"){
@@ -131,7 +132,7 @@ fun ApplicationManage(navCtrl:NavHostController){
composable(route = "Home"){Home(localNavCtrl,pkgName)} composable(route = "Home"){Home(localNavCtrl,pkgName)}
composable(route = "BlockUninstall"){BlockUninstall(pkgName)} composable(route = "BlockUninstall"){BlockUninstall(pkgName)}
composable(route = "UserControlDisabled"){UserCtrlDisabledPkg(pkgName)} composable(route = "UserControlDisabled"){UserCtrlDisabledPkg(pkgName)}
composable(route = "PermissionManage"){PermissionManage(pkgName)} composable(route = "PermissionManage"){PermissionManage(pkgName,navCtrl)}
composable(route = "CrossProfilePackage"){CrossProfilePkg(pkgName)} composable(route = "CrossProfilePackage"){CrossProfilePkg(pkgName)}
composable(route = "CrossProfileWidget"){CrossProfileWidget(pkgName)} composable(route = "CrossProfileWidget"){CrossProfileWidget(pkgName)}
composable(route = "CredentialManagePolicy"){CredentialManagePolicy(pkgName)} composable(route = "CredentialManagePolicy"){CredentialManagePolicy(pkgName)}
@@ -155,8 +156,7 @@ private fun Home(navCtrl:NavHostController, pkgName: String){
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java) val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){ if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
Text(text = stringResource(R.string.scope_is_work_profile), textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp)) Text(text = stringResource(R.string.scope_is_work_profile), textAlign = TextAlign.Center,modifier = Modifier.fillMaxWidth())
Spacer(Modifier.padding(vertical = 5.dp))
} }
SubPageItem(R.string.app_info,"",R.drawable.open_in_new){ SubPageItem(R.string.app_info,"",R.drawable.open_in_new){
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
@@ -343,38 +343,57 @@ private fun BlockUninstall(pkgName: String){
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
private fun PermissionManage(pkgName: String){ private fun PermissionManage(pkgName: String, navCtrl: NavHostController){
val myContext = LocalContext.current val myContext = LocalContext.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)
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
var inputPermission by remember{mutableStateOf(selectedPermission)}
var currentState by remember{mutableStateOf(myContext.getString(R.string.unknown))}
val grantState = mapOf( val grantState = mapOf(
PERMISSION_GRANT_STATE_DEFAULT to stringResource(R.string.decide_by_user), PERMISSION_GRANT_STATE_DEFAULT to stringResource(R.string.decide_by_user),
PERMISSION_GRANT_STATE_GRANTED to stringResource(R.string.granted), PERMISSION_GRANT_STATE_GRANTED to stringResource(R.string.granted),
PERMISSION_GRANT_STATE_DENIED to stringResource(R.string.denied) PERMISSION_GRANT_STATE_DENIED to stringResource(R.string.denied)
) )
LaunchedEffect(Unit) {
while(true){
if(applySelectedPermission){inputPermission = selectedPermission; applySelectedPermission = false}
delay(100)
}
}
LaunchedEffect(pkgName) {
if(pkgName!=""){currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!}
}
Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){ Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())){
var inputPermission by remember{mutableStateOf("android.permission.")}
var currentState by remember{mutableStateOf(grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)])}
Spacer(Modifier.padding(vertical = 10.dp)) Spacer(Modifier.padding(vertical = 10.dp))
Text(text = stringResource(R.string.permission_manage), style = typography.headlineLarge) Text(text = stringResource(R.string.permission_manage), style = typography.headlineLarge)
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
OutlinedTextField( OutlinedTextField(
value = inputPermission, value = inputPermission,
label = { Text(stringResource(R.string.permission))}, label = { Text(stringResource(R.string.permission))},
onValueChange = {inputPermission = it}, onValueChange = {
inputPermission = it; selectedPermission = inputPermission
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
},
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.focusable().fillMaxWidth() modifier = Modifier.focusable().fillMaxWidth(),
trailingIcon = {
Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(50))
.clickable(onClick = {navCtrl.navigate("PermissionPicker")})
.padding(3.dp))
}
) )
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Text(stringResource(R.string.current_state, currentState?:stringResource(R.string.unknown))) Text(stringResource(R.string.current_state, currentState))
Spacer(Modifier.padding(vertical = 5.dp)) Spacer(Modifier.padding(vertical = 5.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){ Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button( Button(
onClick = { onClick = {
myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_GRANTED) myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_GRANTED)
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)] currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
}, },
modifier = Modifier.fillMaxWidth(0.49F) modifier = Modifier.fillMaxWidth(0.49F)
) { ) {
@@ -383,7 +402,7 @@ private fun PermissionManage(pkgName: String){
Button( Button(
onClick = { onClick = {
myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_DENIED) myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_DENIED)
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)] currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
}, },
Modifier.fillMaxWidth(0.96F) Modifier.fillMaxWidth(0.96F)
) { ) {
@@ -393,7 +412,7 @@ private fun PermissionManage(pkgName: String){
Button( Button(
onClick = { onClick = {
myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_DEFAULT) myDpm.setPermissionGrantState(myComponent,pkgName,inputPermission, PERMISSION_GRANT_STATE_DEFAULT)
currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)] currentState = grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)]!!
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
@@ -410,6 +429,7 @@ private fun CrossProfilePkg(pkgName: String){
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)
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))
Text(text = stringResource(R.string.cross_profile_package), style = typography.headlineLarge) Text(text = stringResource(R.string.cross_profile_package), style = typography.headlineLarge)
var list by remember{mutableStateOf("")} var list by remember{mutableStateOf("")}
val refresh = { val refresh = {

View File

@@ -7,6 +7,8 @@ import androidx.activity.result.ActivityResultLauncher
var selectedPackage = "" var selectedPackage = ""
var applySelectedPackage = false var applySelectedPackage = false
var selectedPermission = ""
var applySelectedPermission = false
lateinit var getCaCert: ActivityResultLauncher<Intent> lateinit var getCaCert: ActivityResultLauncher<Intent>
lateinit var createManagedProfile: ActivityResultLauncher<Intent> lateinit var createManagedProfile: ActivityResultLauncher<Intent>
lateinit var getApk: ActivityResultLauncher<Intent> lateinit var getApk: ActivityResultLauncher<Intent>

View File

@@ -244,6 +244,7 @@
<string name="show_system_app">显示系统应用</string> <string name="show_system_app">显示系统应用</string>
<string name="show_priv_app">显示priv-app</string> <string name="show_priv_app">显示priv-app</string>
<string name="show_apex_app">显示apex应用</string> <string name="show_apex_app">显示apex应用</string>
<string name="permission_picker">权限选择器</string>
<string name="suspend">挂起</string> <string name="suspend">挂起</string>
<string name="hide">隐藏</string> <string name="hide">隐藏</string>
<string name="isapphidden_desc">如果隐藏,有可能是没安装</string> <string name="isapphidden_desc">如果隐藏,有可能是没安装</string>
@@ -472,4 +473,31 @@
<string name="blackTheme_desc">需要打开夜间模式</string> <string name="blackTheme_desc">需要打开夜间模式</string>
<string name="need_relaunch">需要重启应用</string> <string name="need_relaunch">需要重启应用</string>
<!--AndroidPermission-->
<string name="permission_READ_EXTERNAL_STORAGE">读取外部存储</string>
<string name="permission_WRITE_EXTERNAL_STORAGE">写入外部存储</string>
<string name="permission_READ_MEDIA_AUDIO">读取音频</string>
<string name="permission_READ_MEDIA_VIDEO">读取视频</string>
<string name="permission_READ_MEDIA_IMAGES">读取图片</string>
<string name="permission_CAMERA">相机</string>
<string name="permission_RECORD_AUDIO">录音</string>
<string name="permission_READ_CONTACTS">读取联系人</string>
<string name="permission_WRITE_CONTACTS">写入联系人</string>
<string name="permission_READ_CALENDAR">读取日历</string>
<string name="permission_WRITE_CALENDAR">写入日历</string>
<string name="permission_ACCESS_COARSE_LOCATION">粗略位置</string>
<string name="permission_ACCESS_FINE_LOCATION">准确位置</string>
<string name="permission_ACCESS_BACKGROUND_LOCATION">后台获取位置</string>
<string name="permission_CALL_PHONE">打电话</string>
<string name="permission_READ_PHONE_STATE">读取手机状态</string>
<string name="permission_READ_SMS">读取短信</string>
<string name="permission_RECEIVE_SMS">接收短信</string>
<string name="permission_SEND_SMS">发送短信</string>
<string name="permission_READ_CALL_LOG">读取通话记录</string>
<string name="permission_WRITE_CALL_LOG">写入通话记录</string>
<string name="permission_BODY_SENSORS">传感器</string>
<string name="permission_BODY_SENSORS_BACKGROUND">后台使用传感器</string>
<string name="permission_ACTIVITY_RECOGNITION">查看使用情况</string>
<string name="permission_POST_NOTIFICATIONS">发送通知</string>
</resources> </resources>