Files
OwnDroid/app/src/main/java/com/binbin/androidowner/User.kt
2024-02-24 20:28:08 +08:00

380 lines
19 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.binbin.androidowner
import android.app.admin.DevicePolicyManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Binder
import android.os.Build.VERSION
import android.os.UserHandle
import android.os.UserManager
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme.colorScheme
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.core.os.UserManagerCompat
var affiliationID = mutableSetOf<String>()
@Composable
fun UserManage() {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
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 userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false)
val bodyTextStyle = if(isWear){ typography.bodyMedium}else{ typography.bodyLarge}
val titleColor = colorScheme.onPrimaryContainer
Column(modifier = sections()) {
Text(text = "用户信息", style = typography.titleLarge, color = titleColor)
Text("用户已解锁:${UserManagerCompat.isUserUnlocked(myContext)}",style = bodyTextStyle)
if(VERSION.SDK_INT>=24){ Text("支持多用户:${UserManager.supportsMultipleUsers()}",style = bodyTextStyle) }
if(VERSION.SDK_INT>=23){ Text(text = "系统用户:${userManager.isSystemUser}", style = bodyTextStyle) }
if(VERSION.SDK_INT>=34){ Text(text = "管理员用户:${userManager.isAdminUser}", style = bodyTextStyle) }
if(VERSION.SDK_INT>=31){ Text(text = "无头系统用户: ${UserManager.isHeadlessSystemUserMode()}",style = bodyTextStyle) }
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
if (VERSION.SDK_INT >= 28) {
val logoutable = myDpm.isLogoutEnabled
Text(text = "用户可以退出 : $logoutable",style = bodyTextStyle)
if(isDeviceOwner(myDpm)|| isProfileOwner(myDpm)){
val ephemeralUser = myDpm.isEphemeralUser(myComponent)
Text(text = "临时用户: $ephemeralUser",style = bodyTextStyle)
}
Text(text = "附属用户: ${myDpm.isAffiliatedUser}",style = bodyTextStyle)
}
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
Text(text = "当前UserID${Binder.getCallingUid()/100000}",style = bodyTextStyle)
Text(text = "当前用户序列号:${userManager.getSerialNumberForUser(android.os.Process.myUserHandle())}",style = bodyTextStyle)
}
Column(modifier = sections()) {
Text(text = "用户操作", style = typography.titleLarge,color = titleColor)
var idInput by remember{ mutableStateOf("") }
var userHandleById:UserHandle by remember{ mutableStateOf(android.os.Process.myUserHandle()) }
var useUid by remember{ mutableStateOf(false) }
TextField(
value = idInput,
onValueChange = {
idInput=it
if(useUid){
if(idInput!=""&&VERSION.SDK_INT>=24){
userHandleById = UserHandle.getUserHandleForUid(idInput.toInt())
}
}else{
val userHandleBySerial = userManager.getUserForSerialNumber(idInput.toLong())
userHandleById = userHandleBySerial ?: android.os.Process.myUserHandle()
}
},
label = {Text(if(useUid){"UID"}else{"序列号"})},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
)
if(VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){
CheckBoxItem(text = "使用UID", checked = {useUid}, operation = {idInput=""; useUid = !useUid})
}
if(VERSION.SDK_INT>28){
if(isProfileOwner(myDpm)&&myDpm.isAffiliatedUser){
Button(
onClick = {
val result = myDpm.logoutUser(myComponent)
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
},
enabled = isProfileOwner(myDpm),
modifier = Modifier.fillMaxWidth()
) {
Text("登出当前用户")
}
}
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button(
onClick = {
focusMgr.clearFocus()
if(VERSION.SDK_INT>=28){
val result = myDpm.startUserInBackground(myComponent,userHandleById)
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
modifier = Modifier.fillMaxWidth(0.49F)
){
Text(if(isWear){"启动"}else{"在后台启动"})
}
Button(
onClick = {
focusMgr.clearFocus()
if(myDpm.switchUser(myComponent,userHandleById)){
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text("切换")
}
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button(
onClick = {
focusMgr.clearFocus()
try{
if(VERSION.SDK_INT>=28){
val result = myDpm.stopUser(myComponent,userHandleById)
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
}
}catch(e:IllegalArgumentException){
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm)&&VERSION.SDK_INT>=28,
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text("停止")
}
Button(
onClick = {
focusMgr.clearFocus()
if(myDpm.removeUser(myComponent,userHandleById)){
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
idInput=""
}else{
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
}
},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text("移除")
}
}
if(VERSION.SDK_INT<28){
Text(text = "停止用户需API28", style = bodyTextStyle)
}
}
if(VERSION.SDK_INT>=24){
Column(modifier = sections()) {
var userName by remember{ mutableStateOf("") }
Text(text = "创建用户", style = typography.titleLarge, color = titleColor)
TextField(
value = userName,
onValueChange = {userName=it},
label = {Text("用户名")},
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
)
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) }
Button(
onClick = {
newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag)
focusMgr.clearFocus()
Toast.makeText(myContext, if(newUserHandle!=null){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
},
enabled = isDeviceOwner(myDpm),
modifier = Modifier.fillMaxWidth()
) {
Text("创建(Owner)")
}
if(newUserHandle!=null){ Text(text = "新用户的序列号:${userManager.getSerialNumberForUser(newUserHandle)}", style = bodyTextStyle) }
}
}
if(VERSION.SDK_INT>=26&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
Column(modifier = sections()){
var input by remember{mutableStateOf("")}
var list by remember{mutableStateOf("")}
val refresh = {
list = ""
var count = affiliationID.size
for(item in affiliationID){ count-=1; list+=item; if(count>0){list+="\n"} }
}
var inited by remember{mutableStateOf(false)}
if(!inited){affiliationID = myDpm.getAffiliationIds(myComponent);refresh();inited=true}
Text(text = "附属用户ID", style = typography.titleLarge, color = titleColor)
TextField(
value = input,
onValueChange = {input = it},
label = {Text("ID")},
modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()})
)
if(list!=""){
SelectionContainer {
Text(text = list, style = bodyTextStyle)
}
}else{
Text(text = "", style = bodyTextStyle)
}
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
Button(
onClick = { affiliationID.add(input); refresh() },
modifier = Modifier.fillMaxWidth(0.49F)
){
Text("添加")
}
Button(
onClick = { affiliationID.remove(input); refresh() },
modifier = Modifier.fillMaxWidth(0.96F)
){
Text("移除")
}
}
Button(
onClick = {
if("" in affiliationID) {
Toast.makeText(myContext, "有空字符串", Toast.LENGTH_SHORT).show()
}else if(affiliationID.isEmpty()){
Toast.makeText(myContext, "不能为空", Toast.LENGTH_SHORT).show()
}else{
myDpm.setAffiliationIds(myComponent, affiliationID)
affiliationID = myDpm.getAffiliationIds(myComponent)
refresh()
Toast.makeText(myContext,"成功",Toast.LENGTH_SHORT).show()
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("应用")
}
Text(text = "如果多用户附属用户ID相同时可以让其他用户附属于主用户", style = bodyTextStyle)
}
}
UserSessionMessage("用户名", "用户名", true, {null}) {msg-> myDpm.setProfileName(myComponent, msg.toString())}
if(VERSION.SDK_INT>=23&&(isDeviceOwner(myDpm)||isProfileOwner(myDpm))){
Column(modifier = sections()){
var getContent by remember{mutableStateOf(false)}
Text(text = "用户图标", style = typography.titleLarge, color = titleColor)
Text(text = "尽量选择正方形的图片,以免产生问题", style = bodyTextStyle)
CheckBoxItem("使用文件选择器而不是相册",{getContent},{getContent=!getContent})
Button(
onClick = {
val intent = Intent(if(getContent){Intent.ACTION_GET_CONTENT}else{Intent.ACTION_PICK})
if(getContent){intent.addCategory(Intent.CATEGORY_OPENABLE)}
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
getUserIcon.launch(intent)
},
modifier = Modifier.fillMaxWidth()
) {
Text("选择图片...")
}
Button(
onClick = {
if(userIconUri!=null){
uriToStream(myContext, userIconUri){stream ->
val bitmap = BitmapFactory.decodeStream(stream)
myDpm.setUserIcon(myComponent,bitmap)
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}
}else{
Toast.makeText(myContext, "请先选择图片", Toast.LENGTH_SHORT).show()
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("应用")
}
}
}
if(VERSION.SDK_INT>=28){
UserSessionMessage("用户会话开始消息", "消息", false, {myDpm.getStartUserSessionMessage(myComponent)}) {msg-> myDpm.setStartUserSessionMessage(myComponent, msg)}
UserSessionMessage("用户会话结束消息", "消息", false, {myDpm.getEndUserSessionMessage(myComponent)}) {msg-> myDpm.setEndUserSessionMessage(myComponent, msg)}
}
Spacer(Modifier.padding(vertical = 30.dp))
}
}
@Composable
private fun UserSessionMessage(text:String, textField:String, profileOwner:Boolean, get: ()->CharSequence?, setMsg:(msg: CharSequence?)->Unit){
Column(
modifier = sections()
) {
val myContext = LocalContext.current
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val focusMgr = LocalFocusManager.current
var msg by remember{ mutableStateOf(if(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)){ if(get()==null){""}else{get().toString()} }else{""}) }
val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false)
Text(text = text, style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
TextField(
value = msg,
onValueChange = {msg=it},
label = {Text(textField)},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp),
enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)
)
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
focusMgr.clearFocus()
setMsg(msg)
msg = if(get()==null){""}else{get().toString()}
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner),
modifier = Modifier.fillMaxWidth(if(isWear){0.49F}else{0.65F})
) {
Text("应用")
}
Button(
onClick = {
focusMgr.clearFocus()
setMsg(null)
msg = get()?.toString() ?: ""
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
},
enabled = isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner),
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text("默认")
}
}
}
}
private fun userOperationResultCode(result:Int): String {
return when(result){
UserManager.USER_OPERATION_SUCCESS->"成功"
UserManager.USER_OPERATION_ERROR_UNKNOWN->"未知结果(失败)"
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"失败:受管理的资料"
UserManager.USER_OPERATION_ERROR_CURRENT_USER->"失败:当前用户"
else->"未知"
}
}