mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
380 lines
19 KiB
Kotlin
380 lines
19 KiB
Kotlin
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->"未知"
|
||
}
|
||
}
|