mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
add user manage section
This commit is contained in:
50
Readme.md
50
Readme.md
@@ -1,26 +1,46 @@
|
||||
# 简介
|
||||
# Android Owner
|
||||
|
||||
使用安卓的DeviceAdmin和DeviceOwner API管理你的设备!
|
||||
### 简介
|
||||
|
||||
欢迎大家提交Issue
|
||||
使用安卓的Device Admin和Device Owner,全方位掌控你的设备。
|
||||
这个应用使用Kotlin + Jetpack Compose,有不完全的质感设计3配色
|
||||
|
||||
记得给我个小星星,欢迎大家提交Issue
|
||||
|
||||
### 功能
|
||||
|
||||
- 添加用户限制
|
||||
- 禁止使用蓝牙
|
||||
- 禁止使用WiFi
|
||||
- 禁止安装应用
|
||||
- ......
|
||||
- 设备控制
|
||||
- 禁用相机
|
||||
- 禁止截屏
|
||||
- 全局静音
|
||||
- 关闭USB信号(需设备支持)
|
||||
- 管理系统更新策略
|
||||
- 清除数据
|
||||
- 管理应用
|
||||
- 隐藏应用
|
||||
- 停用应用
|
||||
- 禁止卸载应用
|
||||
- ......
|
||||
- 设备控制
|
||||
- 禁用相机
|
||||
- 禁止截屏
|
||||
- 隐藏状态栏
|
||||
- 用户限制
|
||||
- 网络和互联网:禁止使用流量、WiFi、VPN、私人DNS
|
||||
- 其他连接:禁止使用蓝牙、位置信息、NFC、USB(MTP)
|
||||
- 应用:禁止安装卸载应用、禁止清除应用的存储空间
|
||||
- 显示与音量:禁止调整亮度、音量
|
||||
- 用户和工作资料:禁止添加/切换/移除用户,禁止添加/移除工作资料
|
||||
- 杂项:禁止自动填充服务、禁止调试
|
||||
- 密码
|
||||
- 修改密码
|
||||
- 最大密码错误次数
|
||||
- 密码失效超时时间
|
||||
|
||||
这个应用需要的权限十分敏感,如果操作不慎,可能会让你的设备丢失数据或者让你无法解锁你的设备
|
||||
### 这个应用十分危险!!!
|
||||
|
||||
但是你无需担心,这个应用不是恶意软件
|
||||
在使用各个功能之前,请仔细阅读相应的说明。红色的按钮一定要谨慎使用!
|
||||
如果操作不慎,可能会意外地丢失数据或者让你无法解锁你的设备!
|
||||
|
||||
### 即将加入的功能
|
||||
|
||||
- Profile Owner相关
|
||||
- Managed Profile,工作资料和多用户相关
|
||||
- 安装/卸载应用,清除应用的缓存和存储空间
|
||||
- 应用管理的包选择器(目前只能手动输入包名)
|
||||
- 适配手表
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "com.binbin.androidowner"
|
||||
minSdk = 23
|
||||
targetSdk = 34
|
||||
versionCode = 4
|
||||
versionName = "1.3"
|
||||
versionCode = 5
|
||||
versionName = "1.4"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.binbin.androidowner
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentSender
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.net.Uri
|
||||
import android.os.Build.VERSION
|
||||
@@ -32,6 +35,8 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
|
||||
@Composable
|
||||
@@ -50,7 +55,9 @@ fun ApplicationManage(myDpm:DevicePolicyManager, myComponent:ComponentName,myCon
|
||||
pkgName = it
|
||||
},
|
||||
label = { Text("包名") },
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
)
|
||||
if(VERSION.SDK_INT>=24){
|
||||
val isSuspended = {
|
||||
@@ -80,20 +87,20 @@ fun ApplicationManage(myDpm:DevicePolicyManager, myComponent:ComponentName,myCon
|
||||
.padding(8.dp),
|
||||
horizontalArrangement = Arrangement.SpaceAround
|
||||
) {
|
||||
Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,false)}) {
|
||||
Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,false)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("取消防卸载")
|
||||
}
|
||||
Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,true)}) {
|
||||
Button(onClick = {myDpm.setUninstallBlocked(myComponent,pkgName,true)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("防卸载")
|
||||
}
|
||||
}
|
||||
Button(
|
||||
/*Button(
|
||||
onClick = {
|
||||
uninstallApp(myContext,pkgName)
|
||||
uninstallPkg(pkgName,myContext)
|
||||
}) {
|
||||
Text("卸载")
|
||||
}
|
||||
Spacer(Modifier.padding(5.dp))
|
||||
}*/
|
||||
Spacer(Modifier.padding(20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,3 +164,39 @@ private fun uninstallApp(context: Context, packageName: String) {
|
||||
uninstallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(uninstallIntent)
|
||||
}
|
||||
|
||||
fun isAppInstalled(context: Context, packageName: String): Boolean {
|
||||
return try {
|
||||
context.packageManager.getApplicationInfo(packageName, 0)
|
||||
true
|
||||
} catch (e: NameNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun installPackage(context: Context, inputStream: InputStream, packageName: String?): Boolean {
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL
|
||||
)
|
||||
params.setAppPackageName(packageName)
|
||||
val sessionId = packageInstaller.createSession(params)
|
||||
val session = packageInstaller.openSession(sessionId)
|
||||
val out = session.openWrite("COSU", 0, -1)
|
||||
val buffer = ByteArray(65536)
|
||||
var c: Int
|
||||
while (inputStream.read(buffer).also { c = it } != -1) {
|
||||
out.write(buffer, 0, c)
|
||||
}
|
||||
session.fsync(out)
|
||||
inputStream.close()
|
||||
out.close()
|
||||
session.commit(createIntentSender(context, sessionId))
|
||||
return true
|
||||
}
|
||||
|
||||
private fun createIntentSender(context: Context, sessionId: Int): IntentSender {
|
||||
val pendingIntent = PendingIntent.getBroadcast(context, sessionId, Intent(), PendingIntent.FLAG_IMMUTABLE)
|
||||
return pendingIntent.intentSender
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Build.VERSION
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -34,6 +35,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
@@ -71,27 +73,60 @@ fun DeviceControl(myDpm: DevicePolicyManager, myComponent: ComponentName,myConte
|
||||
.padding(horizontal = 6.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(15))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(8.dp),
|
||||
.padding(vertical = 5.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Button(onClick = {myDpm.setKeyguardDisabled(myComponent,true)}) {
|
||||
Button(
|
||||
onClick = {
|
||||
if(myDpm.setKeyguardDisabled(myComponent,true)){
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("禁用锁屏(需无密码)")
|
||||
}
|
||||
Spacer(Modifier.padding(horizontal = 5.dp))
|
||||
Button(onClick = {myDpm.setKeyguardDisabled(myComponent,false)}) {
|
||||
Button(
|
||||
onClick = {
|
||||
if(myDpm.setKeyguardDisabled(myComponent,false)){
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("启用锁屏")
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceAround,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 4.dp, horizontal = 6.dp)
|
||||
.clip(RoundedCornerShape(15))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(vertical = 4.dp),
|
||||
) {
|
||||
if(VERSION.SDK_INT>=24){
|
||||
Button(onClick = {myDpm.reboot(myComponent)}) {
|
||||
Button(onClick = {myDpm.reboot(myComponent)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("重启")
|
||||
}
|
||||
Button(onClick = {myDpm.lockNow()}, enabled = myDpm.isAdminActive(myComponent)) {
|
||||
Text("锁屏")
|
||||
}
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT>=24){
|
||||
val wifimac = try {
|
||||
myDpm.getWifiMacAddress(myComponent).toString()
|
||||
}catch(e:SecurityException){
|
||||
"没有权限"
|
||||
}
|
||||
Text("WiFi MAC: $wifimac")
|
||||
Text(text = "WiFi MAC: $wifimac",modifier=Modifier.fillMaxWidth(), textAlign = TextAlign.Center)
|
||||
}
|
||||
if(VERSION.SDK_INT<24){
|
||||
Text("重启和WiFi Mac需要API24")
|
||||
@@ -106,10 +141,7 @@ fun DeviceControl(myDpm: DevicePolicyManager, myComponent: ComponentName,myConte
|
||||
if(VERSION.SDK_INT<34){
|
||||
Text("隐藏状态栏需要API34")
|
||||
}
|
||||
Button(onClick = {myDpm.lockNow()}) {
|
||||
Text("锁屏")
|
||||
}
|
||||
Button(onClick = {myDpm.uninstallAllUserCaCerts(myComponent)}) {
|
||||
Button(onClick = {myDpm.uninstallAllUserCaCerts(myComponent)},modifier = Modifier.align(Alignment.CenterHorizontally), enabled = isDeviceOwner(myDpm)) {
|
||||
Text(text = "清除用户Ca证书")
|
||||
}
|
||||
SysUpdatePolicy(myDpm,myComponent,myContext)
|
||||
@@ -135,7 +167,8 @@ fun DeviceControl(myDpm: DevicePolicyManager, myComponent: ComponentName,myConte
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = if(confirmed){MaterialTheme.colorScheme.primary}else{MaterialTheme.colorScheme.error},
|
||||
contentColor = if(confirmed){MaterialTheme.colorScheme.onPrimary}else{MaterialTheme.colorScheme.onError}
|
||||
)
|
||||
),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text(text = if(confirmed){"取消"}else{"确定"})
|
||||
}
|
||||
@@ -165,6 +198,7 @@ fun DeviceControl(myDpm: DevicePolicyManager, myComponent: ComponentName,myConte
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +241,7 @@ private fun DeviceCtrlItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
if(myDpm.isDeviceOwnerApp("com.binbin.androidowner")){
|
||||
if(isDeviceOwner(myDpm)){
|
||||
isEnabled = getMethod()
|
||||
}
|
||||
Switch(
|
||||
@@ -216,7 +250,7 @@ private fun DeviceCtrlItem(
|
||||
setMethod(!isEnabled)
|
||||
isEnabled=getMethod()
|
||||
},
|
||||
enabled = myDpm.isDeviceOwnerApp("com.binbin.androidowner")
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.binbin.androidowner
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.app.admin.SystemUpdatePolicy
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
@@ -78,7 +77,7 @@ fun MyScaffold(mainDpm:DevicePolicyManager, mainComponent:ComponentName, mainCon
|
||||
"HomePage" to R.string.app_name,
|
||||
"DeviceControl" to R.string.device_ctrl,
|
||||
"Permissions" to R.string.permission,
|
||||
"UIControl" to R.string.ui_ctrl,
|
||||
"UserManage" to R.string.user_manage,
|
||||
"ApplicationManage" to R.string.app_manage,
|
||||
"UserRestriction" to R.string.user_restrict,
|
||||
"Password" to R.string.password
|
||||
@@ -130,6 +129,7 @@ fun MyScaffold(mainDpm:DevicePolicyManager, mainComponent:ComponentName, mainCon
|
||||
composable(route = "Permissions", content = { DpmPermissions(mainDpm,mainComponent,mainContext,navCtrl)})
|
||||
composable(route = "ApplicationManage", content = { ApplicationManage(mainDpm,mainComponent,mainContext)})
|
||||
composable(route = "UserRestriction", content = { UserRestriction(mainDpm,mainComponent)})
|
||||
composable(route = "UserManage", content = { UserManage(mainDpm,mainComponent,mainContext)})
|
||||
composable(route = "Password", content = { Password(mainDpm,mainComponent,mainContext)})
|
||||
}
|
||||
}
|
||||
@@ -179,6 +179,7 @@ fun HomePage(navCtrl:NavHostController,myDpm:DevicePolicyManager,myComponent:Com
|
||||
HomePageItem(R.string.device_ctrl, R.drawable.mobile_phone_fill0, R.string.device_ctrl_desc, "DeviceControl", navCtrl)
|
||||
HomePageItem(R.string.app_manage, R.drawable.apps_fill0, R.string.apps_ctrl_description, "ApplicationManage", navCtrl)
|
||||
HomePageItem(R.string.user_restrict, R.drawable.manage_accounts_fill0, R.string.user_restrict_desc, "UserRestriction", navCtrl)
|
||||
HomePageItem(R.string.user_manage,R.drawable.account_circle_fill0,R.string.user_manage_desc,"UserManage",navCtrl)
|
||||
HomePageItem(R.string.password, R.drawable.password_fill0,R.string.security_desc, "Password",navCtrl)
|
||||
}
|
||||
}
|
||||
@@ -230,3 +231,7 @@ fun RadioButtonItem(
|
||||
Text(text)
|
||||
}
|
||||
}
|
||||
|
||||
fun isDeviceOwner(dpm:DevicePolicyManager): Boolean {
|
||||
return dpm.isDeviceOwnerApp("com.binbin.androidowner")
|
||||
}
|
||||
|
||||
@@ -12,9 +12,11 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
@@ -25,6 +27,8 @@ import androidx.compose.material.icons.outlined.Check
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
@@ -37,6 +41,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
@@ -105,7 +110,8 @@ fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte
|
||||
if(myDpm.clearResetPasswordToken(myComponent)){ Toast.makeText(myContext, "清除成功", Toast.LENGTH_SHORT).show()
|
||||
}else{ Toast.makeText(myContext, "清除失败", Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("清除")
|
||||
}
|
||||
@@ -114,7 +120,8 @@ fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte
|
||||
if(myDpm.setResetPasswordToken(myComponent, myByteArray)){ Toast.makeText(myContext, "设置成功", Toast.LENGTH_SHORT).show()
|
||||
}else{ Toast.makeText(myContext, "设置失败", Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
modifier = Modifier.padding(end = 8.dp)
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("设置")
|
||||
}
|
||||
@@ -124,12 +131,14 @@ fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte
|
||||
try{ activateToken(myContext)
|
||||
}catch(e:NullPointerException){ Toast.makeText(myContext, "请先设置令牌", Toast.LENGTH_SHORT).show() }
|
||||
}else{ Toast.makeText(myContext, "已经激活", Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("激活")
|
||||
}
|
||||
}
|
||||
Text("没有密码时会自动激活令牌")
|
||||
Text("有可能无法设置密码重置令牌,因机而异,AVD上能用")
|
||||
}
|
||||
}
|
||||
Column(
|
||||
@@ -143,10 +152,10 @@ fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte
|
||||
TextField(
|
||||
value = newPwd,
|
||||
onValueChange = {newPwd=it},
|
||||
enabled = !confirmed,
|
||||
enabled = !confirmed&& isDeviceOwner(myDpm),
|
||||
label = { Text("密码")},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
)
|
||||
Text(text = stringResource(R.string.reset_pwd_desc), modifier = Modifier.padding(vertical = 5.dp))
|
||||
Row {
|
||||
@@ -155,7 +164,8 @@ fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte
|
||||
if(newPwd.length>=4||newPwd.isEmpty()){ confirmed=!confirmed
|
||||
}else{ Toast.makeText(myContext, "需要4位数字或字母", Toast.LENGTH_SHORT).show() }
|
||||
},
|
||||
modifier = Modifier.padding(end = 10.dp)
|
||||
modifier = Modifier.padding(end = 10.dp),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("确认密码")
|
||||
}
|
||||
@@ -187,13 +197,13 @@ fun Password(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext:Conte
|
||||
}
|
||||
}
|
||||
}
|
||||
PasswordItem(R.string.max_pwd_fail,R.string.max_pwd_fail_desc,R.string.max_pwd_fail_textfield, focusMgr,false,
|
||||
PasswordItem(R.string.max_pwd_fail,R.string.max_pwd_fail_desc,R.string.max_pwd_fail_textfield, myDpm,focusMgr,false,
|
||||
{myDpm.getMaximumFailedPasswordsForWipe(null).toString()},{ic -> myDpm.setMaximumFailedPasswordsForWipe(myComponent, ic.toInt()) })
|
||||
PasswordItem(R.string.pwd_timeout,R.string.pwd_timeout_desc,R.string.pwd_timeout_textfield, focusMgr,true,
|
||||
PasswordItem(R.string.pwd_timeout,R.string.pwd_timeout_desc,R.string.pwd_timeout_textfield, myDpm,focusMgr,true,
|
||||
{myDpm.getPasswordExpiration(null).toString()},{ic -> myDpm.setPasswordExpirationTimeout(myComponent, ic.toLong()) })
|
||||
PasswordItem(R.string.pwd_history,R.string.pwd_history_desc,R.string.pwd_history_textfield, focusMgr,true,
|
||||
PasswordItem(R.string.pwd_history,R.string.pwd_history_desc,R.string.pwd_history_textfield,myDpm, focusMgr,true,
|
||||
{myDpm.getPasswordHistoryLength(null).toString()},{ic -> myDpm.setPasswordHistoryLength(myComponent, ic.toInt()) })
|
||||
|
||||
Spacer(Modifier.padding(vertical = 20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +212,7 @@ fun PasswordItem(
|
||||
itemName:Int,
|
||||
itemDesc:Int,
|
||||
textFieldLabel:Int,
|
||||
myDpm:DevicePolicyManager,
|
||||
focusMgr:FocusManager,
|
||||
allowZero:Boolean,
|
||||
getMethod:()->String,
|
||||
@@ -215,7 +226,7 @@ fun PasswordItem(
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp)
|
||||
) {
|
||||
var inputContent by remember{ mutableStateOf(getMethod()) }
|
||||
var inputContent by remember{ mutableStateOf(if(isDeviceOwner(myDpm)){getMethod()}else{""}) }
|
||||
var inputContentEdited by remember{ mutableStateOf(false) }
|
||||
var ableToApply by remember{ mutableStateOf(true) }
|
||||
Text(text = stringResource(itemName), style = MaterialTheme.typography.titleLarge)
|
||||
@@ -240,29 +251,20 @@ fun PasswordItem(
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
)
|
||||
if(ableToApply){
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Check,
|
||||
contentDescription = null,
|
||||
tint = if(inputContentEdited){MaterialTheme.colorScheme.onError}else{MaterialTheme.colorScheme.onPrimary},
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(20))
|
||||
.background(
|
||||
color = if (inputContentEdited) {
|
||||
MaterialTheme.colorScheme.error
|
||||
} else {
|
||||
MaterialTheme.colorScheme.primary
|
||||
}
|
||||
)
|
||||
.clickable(onClick = {
|
||||
focusMgr.clearFocus()
|
||||
setMethod(inputContent)
|
||||
inputContentEdited = inputContent != getMethod()
|
||||
})
|
||||
.padding(8.dp)
|
||||
IconButton(
|
||||
onClick = { focusMgr.clearFocus() ; setMethod(inputContent) ; inputContentEdited=inputContent!=getMethod() },
|
||||
enabled = isDeviceOwner(myDpm)&&ableToApply,
|
||||
colors = IconButtonDefaults.iconButtonColors(
|
||||
contentColor = if(inputContentEdited){MaterialTheme.colorScheme.onError}else{MaterialTheme.colorScheme.onPrimary},
|
||||
containerColor = if(inputContentEdited){MaterialTheme.colorScheme.error}else{MaterialTheme.colorScheme.primary},
|
||||
disabledContentColor = Color.Transparent,
|
||||
disabledContainerColor = Color.Transparent
|
||||
)
|
||||
) {
|
||||
Icon(imageVector = Icons.Outlined.Check, contentDescription = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
|
||||
|
||||
@Composable
|
||||
fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myContext:Context,navCtrl:NavHostController){
|
||||
//da:DeviceAdmin do:DeviceOwner
|
||||
@@ -53,7 +54,7 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp)
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(15))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp),
|
||||
@@ -78,15 +79,33 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon
|
||||
Text("撤销")
|
||||
}
|
||||
}else{
|
||||
Button(onClick = { ActivateDeviceAdmin(myComponent, myContext) }) {
|
||||
/*Button(onClick = { activateDeviceAdmin(myContext,myComponent) }) {
|
||||
Text("激活")
|
||||
}*/
|
||||
}
|
||||
}
|
||||
if(!isda){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.background(color = MaterialTheme.colorScheme.tertiaryContainer)
|
||||
.padding(10.dp),
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Text("你可以在adb shell中使用以下命令激活Device Admin")
|
||||
SelectionContainer {
|
||||
Text("dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
|
||||
color = MaterialTheme.colorScheme.onTertiaryContainer)
|
||||
}
|
||||
Text("或者进入设置 -> 安全 -> 更多安全设置 -> 设备管理应用 -> Android Owner")
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp)
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(15))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp),
|
||||
@@ -112,38 +131,59 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon
|
||||
}
|
||||
}
|
||||
}
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
if(isdo||isda){
|
||||
Text(
|
||||
text = "注意!在这里撤销权限不会清除配置。比如:被停用的应用会保持停用状态",
|
||||
color = MaterialTheme.colorScheme.onErrorContainer,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(15))
|
||||
.background(color = MaterialTheme.colorScheme.errorContainer)
|
||||
.padding(6.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.padding(5.dp))
|
||||
if(!isda){
|
||||
Text("你可以在adb shell中使用以下命令激活Device Admin")
|
||||
SelectionContainer {
|
||||
Text("dpm set-active-admin com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver")
|
||||
if(isdo){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(15))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text(text = "设备信息", style = MaterialTheme.typography.titleLarge)
|
||||
if(VERSION.SDK_INT>=30){
|
||||
val orgDevice = myDpm.isOrganizationOwnedDeviceWithManagedProfile
|
||||
Text("由组织拥有的工作资料设备:$orgDevice")
|
||||
}
|
||||
if(VERSION.SDK_INT>=34&&(myDpm.isProfileOwnerApp("com.binbin.androidowner")||myDpm.isManagedProfile(myComponent))){
|
||||
val financed = myDpm.isDeviceFinanced
|
||||
Text("Financed Device : $financed")
|
||||
}
|
||||
}
|
||||
Text("或者进入设置 -> 安全 -> 更多安全设置 -> 设备管理应用 -> Android Owner")
|
||||
}
|
||||
if(!isdo){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.background(color = MaterialTheme.colorScheme.tertiaryContainer)
|
||||
.padding(10.dp),
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Text("你可以在adb shell中使用以下命令激活Device Owner")
|
||||
SelectionContainer {
|
||||
Text("dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver")
|
||||
Text(text = "dpm set-device-owner com.binbin.androidowner/com.binbin.androidowner.MyDeviceAdminReceiver",
|
||||
color = MaterialTheme.colorScheme.onTertiaryContainer)
|
||||
}
|
||||
if(!isda){
|
||||
Text("使用此命令也会激活Device Admin")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isdo&&VERSION.SDK_INT>=24){
|
||||
DeviceOwnerInfo(R.string.owner_lockscr_info,R.string.place_holder,R.string.owner_lockscr_info,focusManager,myContext,
|
||||
{myDpm.deviceOwnerLockScreenInfo},{content -> myDpm.setDeviceOwnerLockScreenInfo(myComponent,content)})
|
||||
@@ -152,8 +192,6 @@ fun DpmPermissions(myDpm: DevicePolicyManager, myComponent: ComponentName, myCon
|
||||
DeviceOwnerInfo(R.string.long_support_msg,R.string.long_support_msg_desc,R.string.message,focusManager,myContext,
|
||||
{myDpm.getLongSupportMessage(myComponent)},{content -> myDpm.setLongSupportMessage(myComponent,content)})
|
||||
}
|
||||
DeviceOwnerInfo(R.string.profile_name,R.string.unknown_feature,R.string.profile_name,focusManager,myContext,
|
||||
{null},{content -> myDpm.setProfileName(myComponent,content)})
|
||||
Spacer(Modifier.padding(vertical = 20.dp))
|
||||
}
|
||||
}
|
||||
@@ -171,7 +209,7 @@ fun DeviceOwnerInfo(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 10.dp)
|
||||
.padding(vertical = 5.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp)
|
||||
@@ -216,13 +254,10 @@ fun DeviceOwnerInfo(
|
||||
}
|
||||
}
|
||||
|
||||
fun ActivateDeviceAdmin(myComponent: ComponentName,myContext: Context){
|
||||
fun activateDeviceAdmin(inputContext:Context,inputComponent:ComponentName){
|
||||
val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)
|
||||
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, myComponent)
|
||||
intent.putExtra(
|
||||
DevicePolicyManager.EXTRA_ADD_EXPLANATION,
|
||||
"在这里激活Android Owner"
|
||||
)
|
||||
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, inputComponent)
|
||||
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "在这里激活Android Owner")
|
||||
intent.setFlags(FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(myContext,intent,null)
|
||||
startActivity(inputContext,intent,null)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ fun SysUpdatePolicy(myDpm:DevicePolicyManager,myComponent:ComponentName,myContex
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
val sysUpdateInfo = myDpm.getPendingSystemUpdate(myComponent)
|
||||
val sysUpdateInfo = if(isDeviceOwner(myDpm)){myDpm.getPendingSystemUpdate(myComponent)}else{null}
|
||||
if(sysUpdateInfo!=null){
|
||||
Text("Update first available: ${Date(sysUpdateInfo.receivedTime)}")
|
||||
Text("Hash code: ${sysUpdateInfo.hashCode()}")
|
||||
@@ -115,7 +115,7 @@ fun SysUpdatePolicy(myDpm:DevicePolicyManager,myComponent:ComponentName,myContex
|
||||
}
|
||||
Button(
|
||||
onClick = {myDpm.setSystemUpdatePolicy(myComponent,policy);Toast.makeText(myContext, "成功!", Toast.LENGTH_SHORT).show()},
|
||||
enabled = myDpm.isDeviceOwnerApp("com.binbin.androidowner")
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("应用")
|
||||
}
|
||||
|
||||
@@ -1,46 +1,13 @@
|
||||
package com.binbin.androidowner;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.PackageInstaller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import java.util.List;
|
||||
|
||||
public class Test {
|
||||
public static void installPackage(Context context, InputStream inputStream)
|
||||
throws IOException {
|
||||
PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
|
||||
int sessionId = packageInstaller.createSession(new PackageInstaller
|
||||
.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));
|
||||
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
|
||||
|
||||
long sizeBytes = 0;
|
||||
|
||||
OutputStream out;
|
||||
out = session.openWrite("my_app_session", 0, sizeBytes);
|
||||
|
||||
int total = 0;
|
||||
byte[] buffer = new byte[65536];
|
||||
int c;
|
||||
while ((c = inputStream.read(buffer)) != -1) {
|
||||
total += c;
|
||||
out.write(buffer, 0, c);
|
||||
}
|
||||
session.fsync(out);
|
||||
inputStream.close();
|
||||
out.close();
|
||||
|
||||
// fake intent
|
||||
IntentSender statusReceiver = null;
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
|
||||
1337111117, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
session.commit(pendingIntent.getIntentSender());
|
||||
session.close();
|
||||
public static List<UserHandle> returnUsers(Context myContext){
|
||||
UserManager userManager = (UserManager) myContext.getSystemService(Context.USER_SERVICE);
|
||||
return userManager.getUserProfiles();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,31 +2,248 @@ package com.binbin.androidowner
|
||||
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.ComponentName
|
||||
import android.os.Build
|
||||
import android.content.Context
|
||||
import android.os.Build.VERSION
|
||||
import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
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.shape.RoundedCornerShape
|
||||
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
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.UserManagerCompat
|
||||
|
||||
@Composable
|
||||
fun User(myDpm:DevicePolicyManager,myComponent:ComponentName){
|
||||
Column {
|
||||
Column {
|
||||
Text(text = "参数", style = MaterialTheme.typography.titleLarge)
|
||||
if(Build.VERSION.SDK_INT>=34&&(myDpm.isProfileOwnerApp("com.binbin.androidowner")||myDpm.isManagedProfile(myComponent))){
|
||||
val financed = myDpm.isDeviceFinanced
|
||||
Text("Financed Device : $financed")
|
||||
fun UserManage(myDpm:DevicePolicyManager,myComponent:ComponentName,myContext: Context){
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
//val myUM = myContext.getSystemService(Context.USER_SERVICE)
|
||||
val currentUser = android.os.Process.myUserHandle()
|
||||
val userList = Test.returnUsers(myContext)
|
||||
Text("因为我的模拟器的多用户无法使用,部分功能并未测试")
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp)
|
||||
) {
|
||||
Text(text = "用户信息", style = MaterialTheme.typography.titleLarge)
|
||||
Text("用户个数:${userList.size}")
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Text("用户已解锁:${UserManagerCompat.isUserUnlocked(myContext)}")
|
||||
if(VERSION.SDK_INT>=24){
|
||||
Text("支持多用户:${UserManager.supportsMultipleUsers()}")
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
if(VERSION.SDK_INT>=31){
|
||||
Text("Headless system user: ${UserManager.isHeadlessSystemUserMode()}")
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
if (VERSION.SDK_INT >= 28&& isDeviceOwner(myDpm)) {
|
||||
val logoutable = myDpm.isLogoutEnabled
|
||||
Text(text = "用户可以退出 : $logoutable")
|
||||
val ephemeralUser = myDpm.isEphemeralUser(myComponent)
|
||||
Text(text = "临时用户: $ephemeralUser")
|
||||
Text("切换用户后或设备重启后会删除临时用户")
|
||||
val affiliatedUser = myDpm.isAffiliatedUser
|
||||
Text(text = "Affiliated User: $affiliatedUser")
|
||||
}
|
||||
Spacer(Modifier.padding(5.dp))
|
||||
Text("切换用户后或设备重启后会删除临时用户")
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp)
|
||||
) {
|
||||
Text(text = "用户操作", style = MaterialTheme.typography.titleLarge)
|
||||
if(VERSION.SDK_INT>28){
|
||||
var resultForLogout by remember{ mutableIntStateOf(-1) }
|
||||
var resultForStop by remember{ mutableIntStateOf(-1) }
|
||||
Text("登出用户需要成为次级用户的Profile Owner")
|
||||
Button(onClick = {resultForLogout = myDpm.logoutUser(myComponent)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("登出用户")
|
||||
}
|
||||
if(resultForLogout!=-1){
|
||||
Text(userOperationResultCode(resultForLogout))
|
||||
}
|
||||
Button(onClick = {resultForStop = myDpm.stopUser(myComponent,currentUser)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("停止用户")
|
||||
}
|
||||
if(resultForStop!=-1){
|
||||
Text(userOperationResultCode(resultForStop))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val success = myDpm.removeUser(myComponent,currentUser)
|
||||
if(success){
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("移除用户")
|
||||
}
|
||||
}
|
||||
|
||||
if(VERSION.SDK_INT>=24){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp)
|
||||
) {
|
||||
var userName by remember{ mutableStateOf("") }
|
||||
Text(text = "创建用户", style = MaterialTheme.typography.titleLarge)
|
||||
TextField(
|
||||
value = userName,
|
||||
onValueChange = {userName=it},
|
||||
label = {Text("用户名")},
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
)
|
||||
var selectedFlag by remember{ mutableIntStateOf(0) }
|
||||
RadioButtonItem("无",{selectedFlag==0},{selectedFlag=0})
|
||||
RadioButtonItem("跳过创建用户向导",{selectedFlag==DevicePolicyManager.SKIP_SETUP_WIZARD},{selectedFlag=DevicePolicyManager.SKIP_SETUP_WIZARD})
|
||||
if(VERSION.SDK_INT>=28){
|
||||
RadioButtonItem("临时用户",{selectedFlag==DevicePolicyManager.MAKE_USER_EPHEMERAL},{selectedFlag=DevicePolicyManager.MAKE_USER_EPHEMERAL})
|
||||
RadioButtonItem("启用所有系统应用",{selectedFlag==DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED},{selectedFlag=DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED})
|
||||
}
|
||||
var newUserHandle: UserHandle? by remember{ mutableStateOf(null) }
|
||||
Row {
|
||||
Button(
|
||||
onClick = {newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag)},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("创建")
|
||||
}
|
||||
if(newUserHandle!=null){
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
if(myDpm.switchUser(myComponent,newUserHandle)){
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
} else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text("切换至新用户")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}else{
|
||||
Text("创建用户需安卓7")
|
||||
}
|
||||
UserSessionMessage("用户名","用户名",myDpm,myContext,{null},{msg -> myDpm.setProfileName(myComponent, msg.toString())})
|
||||
if(VERSION.SDK_INT>=28){
|
||||
UserSessionMessage("用户会话开始消息","消息",myDpm,myContext,{myDpm.getStartUserSessionMessage(myComponent)},{msg -> myDpm.setStartUserSessionMessage(myComponent,msg)})
|
||||
UserSessionMessage("用户会话结束消息","消息",myDpm,myContext,{myDpm.getEndUserSessionMessage(myComponent)},{msg -> myDpm.setEndUserSessionMessage(myComponent,msg)})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 30.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserSessionMessage(
|
||||
text:String,
|
||||
textField:String,
|
||||
myDpm:DevicePolicyManager,
|
||||
myContext: Context,
|
||||
get:()->CharSequence?,
|
||||
setMsg:(msg:CharSequence?)->Unit
|
||||
){
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(10))
|
||||
.background(color = MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(10.dp)
|
||||
) {
|
||||
val focusMgr = LocalFocusManager.current
|
||||
var msg:CharSequence? by remember{ mutableStateOf(null) }
|
||||
if(isDeviceOwner(myDpm)){
|
||||
msg = if(get()==null){""}else{get()}
|
||||
}
|
||||
Text(text = text, style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onPrimaryContainer)
|
||||
TextField(
|
||||
value = msg.toString(),
|
||||
onValueChange = {msg=it},
|
||||
label = {Text(textField)},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
|
||||
modifier = Modifier.padding(vertical = 6.dp),
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
)
|
||||
Row {
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
setMsg(msg)
|
||||
msg = if(get()==null){""}else{get()}
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("应用")
|
||||
}
|
||||
Spacer(Modifier.padding(horizontal = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
setMsg(null)
|
||||
msg = if(get()==null){""}else{get()}
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
) {
|
||||
Text("使用默认")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun userOperationResultCode(result:Int): String {
|
||||
return when(result){
|
||||
UserManager.USER_OPERATION_SUCCESS->"USER_OPERATION_SUCCESS"
|
||||
UserManager.USER_OPERATION_ERROR_UNKNOWN->"USER_OPERATION_ERROR_UNKNOWN"
|
||||
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"USER_OPERATION_ERROR_MANAGED_PROFILE"
|
||||
UserManager.USER_OPERATION_ERROR_CURRENT_USER->"USER_OPERATION_ERROR_CURRENT_USER"
|
||||
else->"Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -21,6 +22,7 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -35,7 +37,7 @@ import androidx.compose.ui.unit.dp
|
||||
@Composable
|
||||
fun UserRestriction(myDpm: DevicePolicyManager, myComponent: ComponentName){
|
||||
val verticalScrolling = rememberScrollState()
|
||||
var currentSection by remember{ mutableStateOf(0) }
|
||||
var currentSection by remember{ mutableIntStateOf(0) }
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
@@ -171,6 +173,7 @@ fun UserRestriction(myDpm: DevicePolicyManager, myComponent: ComponentName){
|
||||
if(VERSION.SDK_INT<34){
|
||||
Text("以下功能需要安卓14或以上:2G信号、启用设备管理器、超宽频段无线电")
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,11 +119,13 @@
|
||||
<string name="support_msg">提供支持的短消息</string>
|
||||
<string name="support_msg_desc">如果你禁用了某个功能,用户尝试使用这个功能时会看见这个消息(可多行)</string>
|
||||
<string name="message">消息</string>
|
||||
<string name="profile_name">预设名</string>
|
||||
<string name="profile_name">用户名</string>
|
||||
<string name="long_support_msg">提供支持的长消息</string>
|
||||
<string name="long_support_msg_desc">都是显示短消息,长消息不知道在哪里显示</string>
|
||||
<string name="disable_bt_contact_share">禁止蓝牙分享通讯录</string>
|
||||
<string name="sys_update_policy">系统更新策略</string>
|
||||
<string name="sys_update_policy_desc">管理系统更新策略</string>
|
||||
<string name="disable_keyguard">禁止锁屏(需无密码)</string>
|
||||
<string name="user_manage">用户管理</string>
|
||||
<string name="user_manage_desc">查看用户状态,添加用户</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user