Add a ManagedProfile page

This commit is contained in:
BinTianqi
2024-02-08 19:08:22 +08:00
parent 54023bee59
commit 2e71451eb9
9 changed files with 179 additions and 56 deletions

View File

@@ -11,8 +11,8 @@ android {
applicationId = "com.binbin.androidowner"
minSdk = 21
targetSdk = 34
versionCode = 11
versionName = "2.4"
versionCode = 12
versionName = "2.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@@ -15,6 +15,7 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"/>
<application
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

View File

@@ -76,7 +76,7 @@ fun ApplicationManage(){
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
)
}
if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
if(VERSION.SDK_INT>=24&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
val isSuspended: Boolean = try{ myDpm.isPackageSuspended(myComponent,pkgName) }
catch(e:NameNotFoundException){ false }
catch(w:NameNotFoundException){ false }
@@ -472,7 +472,6 @@ private fun AppManageItem(
val myDpm = LocalContext.current.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val focusMgr = LocalFocusManager.current
var isEnabled by remember{ mutableStateOf(false) }
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){ isEnabled = getMethod() }
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
if(!sharedPref.getBoolean("isWear",false)){
Row(

View File

@@ -492,7 +492,7 @@ fun DeviceControl(){
Text(text = "清除数据",style = typography.titleLarge,modifier = Modifier.padding(6.dp),color = colorScheme.onErrorContainer)
RadioButtonItem("默认",{flag==0},{flag=0}, colorScheme.onErrorContainer)
RadioButtonItem("WIPE_EXTERNAL_STORAGE",{flag==WIPE_EXTERNAL_STORAGE},{flag=WIPE_EXTERNAL_STORAGE}, colorScheme.onErrorContainer)
if(VERSION.SDK_INT>=22){
if(VERSION.SDK_INT>=22&&isDeviceOwner(myDpm)){
RadioButtonItem("WIPE_RESET_PROTECTION_DATA",{flag==WIPE_RESET_PROTECTION_DATA},{flag=WIPE_RESET_PROTECTION_DATA}, colorScheme.onErrorContainer)
}
if(VERSION.SDK_INT>=28){ RadioButtonItem("WIPE_EUICC",{flag==WIPE_EUICC},{flag=WIPE_EUICC}, colorScheme.onErrorContainer) }
@@ -533,6 +533,9 @@ fun DeviceControl(){
}
}
}
if(VERSION.SDK_INT>=24&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
Text("将会删除工作资料")
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}

View File

@@ -23,6 +23,7 @@ import androidx.compose.material.icons.outlined.Home
import androidx.compose.material3.*
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -100,6 +101,7 @@ fun MyScaffold(){
"HomePage" to R.string.app_name,
"DeviceControl" to R.string.device_ctrl,
"Network" to R.string.network,
"ManagedProfile" to R.string.work_profile,
"Permissions" to R.string.permission,
"UserManage" to R.string.user_manage,
"ApplicationManage" to R.string.app_manage,
@@ -160,6 +162,7 @@ fun MyScaffold(){
){
composable(route = "HomePage", content = { HomePage(navCtrl)})
composable(route = "DeviceControl", content = { DeviceControl()})
composable(route = "ManagedProfile", content = {ManagedProfile(navCtrl)})
composable(route = "Permissions", content = { DpmPermissions(navCtrl)})
composable(route = "ApplicationManage", content = { ApplicationManage()})
composable(route = "UserRestriction", content = { UserRestriction()})
@@ -216,6 +219,7 @@ fun HomePage(navCtrl:NavHostController){
}
HomePageItem(R.string.device_ctrl, R.drawable.mobile_phone_fill0, "DeviceControl", navCtrl)
if(VERSION.SDK_INT>=26){HomePageItem(R.string.network, R.drawable.wifi_fill0, "Network",navCtrl)}
HomePageItem(R.string.work_profile, R.drawable.work_fill0, "ManagedProfile",navCtrl)
HomePageItem(R.string.app_manage, R.drawable.apps_fill0, "ApplicationManage", navCtrl)
HomePageItem(R.string.user_restrict, R.drawable.manage_accounts_fill0, "UserRestriction", navCtrl)
HomePageItem(R.string.user_manage,R.drawable.account_circle_fill0,"UserManage",navCtrl)
@@ -311,6 +315,7 @@ fun isProfileOwner(dpm:DevicePolicyManager): Boolean {
@SuppressLint("ModifierFactoryExtensionFunction", "ComposableModifierFactory")
@Composable
@Stable
fun sections(bgColor:Color=MaterialTheme.colorScheme.primaryContainer):Modifier{
val backgroundColor = if(isSystemInDarkTheme()){bgColor.copy(0.4F)}else{bgColor.copy(0.6F)}
return if(!LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE).getBoolean("isWear",false)){

View File

@@ -0,0 +1,122 @@
package com.binbin.androidowner
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build.VERSION
import android.os.Bundle
import android.os.Parcelable
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Column
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.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
@Composable
fun ManagedProfile(navCtrl:NavHostController){
val myContext = LocalContext.current
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
val focusMgr = LocalFocusManager.current
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false)
val bodyTextStyle = if(isWear){ typography.bodyMedium}else{ typography.bodyLarge}
Column(modifier = Modifier.verticalScroll(rememberScrollState())){
Column(modifier = sections()){
Text(text = "信息", style = typography.titleLarge)
if(VERSION.SDK_INT>=30){
Text(text = "由组织拥有的工作资料:${myDpm.isOrganizationOwnedDeviceWithManagedProfile}", style = bodyTextStyle)
}
}
Column(modifier = sections()) {
Text(text = "工作资料", style = typography.titleLarge)
if(VERSION.SDK_INT>=24){
if(isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
Text(text = "已是工作资料")
}else{
Text(text = "可以创建工作资料:${myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE)}", style = bodyTextStyle)
}
}
if(isDeviceOwner(myDpm)){
Text(text = "Device owner不能创建工作资料", style = bodyTextStyle)
}
if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(ACTION_PROVISION_MANAGED_PROFILE))){
var skipEncrypt by remember{mutableStateOf(false)}
if(VERSION.SDK_INT>=24){CheckBoxItem("跳过加密",{skipEncrypt},{skipEncrypt=!skipEncrypt})}
Button(
onClick = {
val intent = Intent(ACTION_PROVISION_MANAGED_PROFILE)
if(VERSION.SDK_INT>=23){
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,myComponent)
}else{
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,"com.binbin.androidowner")
}
if(VERSION.SDK_INT>=24){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION,skipEncrypt)}
if(VERSION.SDK_INT>=33){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE,true)}
createManagedProfile.launch(intent)
},
modifier = Modifier.fillMaxWidth()
) {
Text("创建")
}
}
if(isProfileOwner(myDpm)&&(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isManagedProfile(myComponent)))){
Button(
onClick = {
myDpm.setProfileEnabled(myComponent)
navCtrl.navigateUp()
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "激活")
}
}
}
if(VERSION.SDK_INT>=30&&isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile){
Column(modifier = sections()){
var time by remember{mutableStateOf("")}
time = myDpm.getManagedProfileMaximumTimeOff(myComponent).toString()
Text(text = "资料关闭时间", style = typography.titleLarge)
Text(text = "工作资料处于关闭状态的时间达到该限制后会停用个人应用0为无限制(单位:毫秒)", style = bodyTextStyle)
TextField(
value = time, onValueChange = {time=it}, modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
label = {Text("时间(ms)")},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
)
Text(text = "不能少于72小时")
Button(
onClick = {
myDpm.setManagedProfileMaximumTimeOff(myComponent,time.toLong())
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}
) {
Text("应用")
}
}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}

View File

@@ -28,6 +28,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat.startActivity
@@ -160,6 +161,7 @@ fun DpmPermissions(navCtrl:NavHostController){
if(!isda){
Text(text = "使用此命令也会激活Device Admin", style = bodyTextStyle)
}
Text(text = "成为Device owner后将不能新建工作资料", style = bodyTextStyle)
}
}
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)||myDpm.isAdminActive(myComponent)){
@@ -175,8 +177,6 @@ fun DpmPermissions(navCtrl:NavHostController){
modifier = sections()
) {
Text(text = "设备信息", style = typography.titleLarge,color = titleColor)
val orgDevice = myDpm.isOrganizationOwnedDeviceWithManagedProfile
Text("由组织拥有的受管理资料设备:$orgDevice",style=bodyTextStyle)
if(VERSION.SDK_INT>=34&&(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&myDpm.isOrganizationOwnedDeviceWithManagedProfile))){
val financed = myDpm.isDeviceFinanced
Text("企业资产 : $financed",style=bodyTextStyle)
@@ -186,9 +186,7 @@ fun DpmPermissions(navCtrl:NavHostController){
Text("设备策略管理器角色:${if(dpmRole==null){"null"}else{""}}",style=bodyTextStyle)
if(dpmRole!=null){
Row(modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState())){
SelectionContainer {
Text(dpmRole)
}
SelectionContainer { Text(dpmRole) }
}
}
}
@@ -234,6 +232,33 @@ fun DpmPermissions(navCtrl:NavHostController){
}
}
}
if((VERSION.SDK_INT>=26&&isDeviceOwner(myDpm))||(VERSION.SDK_INT>=24&&isProfileOwner(myDpm))){
Column(modifier = sections()){
var orgName by remember{
mutableStateOf(
if(myDpm.getOrganizationName(myComponent).toString()=="null"){ "" }else{ myDpm.getOrganizationName(myComponent).toString() }
)
}
Text(text = "组织名称", style = typography.titleLarge)
TextField(
value = orgName, onValueChange = {orgName=it}, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp),
label = {Text("组织名称")},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusManager.clearFocus()})
)
Button(
onClick = {
myDpm.setOrganizationName(myComponent,orgName)
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth()
){
Text("应用")
}
}
}
if(isDeviceOwner(myDpm) || isProfileOwner(myDpm)){
Column(modifier = sections()) {
Text(text = "不受控制的账号类型", style = typography.titleLarge,color = titleColor)
@@ -267,12 +292,16 @@ fun DpmPermissions(navCtrl:NavHostController){
keyboardActions = KeyboardActions(onDone = {focusManager.clearFocus()})
)
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
Button(onClick={focusManager.clearFocus()
myDpm.setAccountManagementDisabled(myComponent,inputText,true)
noManageAccount=myDpm.accountTypesWithManagementDisabled
refreshList()
},modifier = Modifier.fillMaxWidth(0.49f)){
Text("列表")
Button(
onClick={
focusManager.clearFocus()
myDpm.setAccountManagementDisabled(myComponent,inputText,true)
noManageAccount=myDpm.accountTypesWithManagementDisabled
refreshList()
},
modifier = Modifier.fillMaxWidth(0.49f)
){
Text("添加")
}
Button(
onClick={focusManager.clearFocus()
@@ -282,7 +311,7 @@ fun DpmPermissions(navCtrl:NavHostController){
},
modifier = Modifier.fillMaxWidth(0.96F)
){
Text("从列表中移除")
Text("移除")
}
}
}
@@ -370,7 +399,7 @@ fun DeviceOwnerInfo(
fm.clearFocus()
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
modifier = if(isWear){Modifier.fillMaxWidth(0.48F)}else{Modifier.fillMaxWidth(0.6F)}
modifier = if(isWear){Modifier.fillMaxWidth(0.49F)}else{Modifier.fillMaxWidth(0.6F)}
) {
Text(text = "应用")
}
@@ -381,7 +410,7 @@ fun DeviceOwnerInfo(
fm.clearFocus()
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth(0.95F)
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(text = "重置")
}

View File

@@ -192,43 +192,6 @@ fun UserManage(navCtrl:NavHostController){
}
}
Column(modifier = sections()) {
Text(text = "工作资料", style = typography.titleLarge)
if(VERSION.SDK_INT>=24){Text(text = "可以创建工作资料:${myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)}", style = bodyTextStyle)}
if(isDeviceOwner(myDpm)){Text(text = "Device owner不能创建工作资料", style = bodyTextStyle)}
if(VERSION.SDK_INT<24||(VERSION.SDK_INT>=24&&myDpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE))){
var skipEncrypt by remember{mutableStateOf(false)}
if(VERSION.SDK_INT>=24){CheckBoxItem("跳过加密",{skipEncrypt},{skipEncrypt=!skipEncrypt})}
Button(
onClick = {
val intent = Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)
if(VERSION.SDK_INT>=23){
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,myComponent)
}else{
intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,"com.binbin.androidowner")
}
if(VERSION.SDK_INT>=24){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION,skipEncrypt)}
if(VERSION.SDK_INT>=33){intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE,true)}
createManagedProfile.launch(intent)
},
modifier = Modifier.fillMaxWidth()
) {
Text("创建")
}
}
if(isProfileOwner(myDpm)){
Button(
onClick = {
myDpm.setProfileEnabled(myComponent)
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
modifier = Modifier.fillMaxWidth()
) {
Text(text = "启用")
}
}
}
if(VERSION.SDK_INT>=24){
Column(modifier = sections()) {
var userName by remember{ mutableStateOf("") }

View File

@@ -122,4 +122,5 @@
<string name="network">网络</string>
<string name="developing">功能开发中</string>
<string name="wifi_lockdown">WiFi锁定</string>
<string name="work_profile">工作资料</string>
</resources>