mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
fix user operation problems
This commit is contained in:
@@ -31,6 +31,10 @@
|
||||
- 显示与音量:禁止调整亮度、音量
|
||||
- 用户和工作资料:禁止添加/切换/移除用户,禁止添加/移除工作资料
|
||||
- 杂项:禁止自动填充服务、禁止调试
|
||||
- 用户管理
|
||||
- 添加用户并切换至新用户
|
||||
- 修改当前用户的名称
|
||||
- 设置切换用户时的提示
|
||||
- 密码
|
||||
- 修改密码
|
||||
- 最大密码错误次数
|
||||
|
||||
@@ -52,16 +52,16 @@ android {
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
implementation("androidx.activity:activity-compose:1.7.0")
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||
implementation("androidx.activity:activity-compose:1.8.2")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.08.00"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3:1.1.1")
|
||||
implementation("androidx.compose.material3:material3:1.1.2")
|
||||
implementation("androidx.navigation:navigation-compose:2.7.6")
|
||||
implementation("com.google.accompanist:accompanist-systemuicontroller:0.33.2-alpha")
|
||||
implementation("com.google.accompanist:accompanist-systemuicontroller:0.34.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
|
||||
@@ -160,7 +160,7 @@ fun MyScaffold(){
|
||||
composable(route = "Permissions", content = { DpmPermissions(navCtrl)})
|
||||
composable(route = "ApplicationManage", content = { ApplicationManage()})
|
||||
composable(route = "UserRestriction", content = { UserRestriction()})
|
||||
composable(route = "UserManage", content = { UserManage()})
|
||||
composable(route = "UserManage", content = { UserManage(navCtrl)})
|
||||
composable(route = "Password", content = { Password()})
|
||||
composable(route = "AppSetting", content = { AppSetting(navCtrl)})
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
||||
super.onEnabled(context, intent)
|
||||
Toast.makeText(context, "已启用", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
/*override fun onReceive(context: Context, intent: Intent) {
|
||||
super.onReceive(context, intent)
|
||||
Toast.makeText(context, "已接收", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}*/
|
||||
override fun onDisableRequested(context: Context, intent: Intent): CharSequence {
|
||||
Toast.makeText(context, "撤销授权", Toast.LENGTH_SHORT).show()
|
||||
return "这是取消时的提示"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.binbin.androidowner;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import java.util.List;
|
||||
|
||||
public class Test {
|
||||
public static List<UserHandle> returnUsers(Context myContext){
|
||||
UserManager userManager = (UserManager) myContext.getSystemService(Context.USER_SERVICE);
|
||||
return userManager.getUserProfiles();
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import android.os.UserHandle
|
||||
import android.os.UserManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -17,10 +18,12 @@ 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.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
@@ -30,32 +33,33 @@ import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
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
|
||||
import androidx.navigation.NavHostController
|
||||
|
||||
|
||||
@Composable
|
||||
fun UserManage(){
|
||||
fun UserManage(navCtrl:NavHostController){
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
//val myUM = myContext.getSystemService(Context.USER_SERVICE)
|
||||
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 currentUser = android.os.Process.myUserHandle()
|
||||
val userList = Test.returnUsers(myContext)
|
||||
val userManager = myContext.getSystemService(Context.USER_SERVICE) as UserManager
|
||||
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||
val isWear = sharedPref.getBoolean("isWear",false)
|
||||
Column(modifier = sections()) {
|
||||
Text(text = "用户信息", style = MaterialTheme.typography.titleLarge,color = MaterialTheme.colorScheme.onPrimaryContainer)
|
||||
Text("用户个数:${userList.size}",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
|
||||
Text("用户已解锁:${UserManagerCompat.isUserUnlocked(myContext)}",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
if(VERSION.SDK_INT>=24){
|
||||
Text("支持多用户:${UserManager.supportsMultipleUsers()}",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
@@ -75,49 +79,162 @@ fun UserManage(){
|
||||
Text(text = "次级用户: $affiliatedUser",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = if(isWear){2.dp}else{5.dp}))
|
||||
Text("切换用户后或设备重启后会删除临时用户",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
Text("当前UID:${android.os.Process.myUid()}")
|
||||
Text("当前UserID:${getCurrentUserId()}")
|
||||
Text("当前用户序列号:${userManager.getSerialNumberForUser(android.os.Process.myUserHandle())}")
|
||||
}
|
||||
|
||||
Column(modifier = sections()) {
|
||||
Text(text = "用户操作", style = MaterialTheme.typography.titleLarge,color = MaterialTheme.colorScheme.onPrimaryContainer)
|
||||
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()})
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.clickable(enabled = VERSION.SDK_INT>=24&&isDeviceOwner(myDpm)){useUid=!useUid}
|
||||
.padding(end = 12.dp)
|
||||
){
|
||||
Checkbox(
|
||||
checked = useUid,
|
||||
onCheckedChange = {useUid=it},
|
||||
enabled = VERSION.SDK_INT>=24&& isDeviceOwner(myDpm)
|
||||
)
|
||||
Text(text = "使用UID(不靠谱)",modifier = Modifier.padding(bottom = 2.dp))
|
||||
}
|
||||
if(VERSION.SDK_INT>28){
|
||||
var resultForLogout by remember{ mutableIntStateOf(-1) }
|
||||
var resultForStop by remember{ mutableIntStateOf(-1) }
|
||||
Text("登出用户需要成为次级用户的Profile Owner",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
Button(onClick = {resultForLogout = myDpm.logoutUser(myComponent)}, enabled = isProfileOwner(myDpm)) {
|
||||
Text("登出用户")
|
||||
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("登出当前用户")
|
||||
}
|
||||
}
|
||||
if(resultForLogout!=-1){
|
||||
Text(userOperationResultCode(resultForLogout))
|
||||
}
|
||||
Button(onClick = {myDpm.switchUser(myComponent,currentUser)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("切换用户")
|
||||
}
|
||||
Button(onClick = {resultForStop = myDpm.stopUser(myComponent,currentUser)}, enabled = isDeviceOwner(myDpm)) {
|
||||
Text("停止用户")
|
||||
}
|
||||
if(resultForStop!=-1){
|
||||
Text(userOperationResultCode(resultForStop))
|
||||
}
|
||||
Button(onClick = {myDpm.setProfileEnabled(myComponent)}, enabled = isProfileOwner(myDpm)||isDeviceOwner(myDpm)) {
|
||||
Text(text = "启用资料")
|
||||
Button(
|
||||
onClick = {
|
||||
focusMgr.clearFocus()
|
||||
val result = myDpm.startUserInBackground(myComponent,userHandleById)
|
||||
Toast.makeText(myContext, userOperationResultCode(result), Toast.LENGTH_SHORT).show()
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
){
|
||||
Text("在后台启动用户")
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val success = myDpm.removeUser(myComponent,currentUser)
|
||||
if(success){
|
||||
focusMgr.clearFocus()
|
||||
if(myDpm.switchUser(myComponent,userHandleById)){
|
||||
focusMgr.clearFocus()
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm)
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("移除用户")
|
||||
Text("切换至用户")
|
||||
}
|
||||
Button(onClick = { createWorkProfile(myContext)}) {
|
||||
Text("创建工作资料")
|
||||
Row(
|
||||
modifier = if(isWear){
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())}else{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.48F)
|
||||
) {
|
||||
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.92F)
|
||||
) {
|
||||
Text("移除用户")
|
||||
}
|
||||
}
|
||||
if(VERSION.SDK_INT<28){
|
||||
Text("停止用户需API28")
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = sections()) {
|
||||
Text(text = "工作资料", style = MaterialTheme.typography.titleLarge)
|
||||
Row(
|
||||
modifier = if(isWear){
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState())}else{Modifier.fillMaxWidth()},
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
){
|
||||
Button(
|
||||
onClick = { createWorkProfile(myContext)},
|
||||
modifier = Modifier.fillMaxWidth(0.48F)
|
||||
) {
|
||||
Text("创建")
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
try{
|
||||
myDpm.setProfileEnabled(myComponent)
|
||||
}catch(e:SecurityException){
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isProfileOwner(myDpm)||isDeviceOwner(myDpm),
|
||||
modifier = Modifier.fillMaxWidth(0.95F)
|
||||
) {
|
||||
Text(text = "启用")
|
||||
}
|
||||
}
|
||||
Text("可能无法创建工作资料",style = if(isWear){MaterialTheme.typography.bodyMedium}else{MaterialTheme.typography.bodyLarge})
|
||||
}
|
||||
@@ -143,12 +260,23 @@ fun UserManage(){
|
||||
RadioButtonItem("启用所有系统应用",{selectedFlag==DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED},{selectedFlag=DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED})
|
||||
}
|
||||
var newUserHandle: UserHandle? by remember{ mutableStateOf(null) }
|
||||
Row(modifier = if(isWear){Modifier.horizontalScroll(rememberScrollState())}else{Modifier}) {
|
||||
Row(
|
||||
modifier = if(isWear){Modifier.fillMaxWidth().horizontalScroll(rememberScrollState())}else{Modifier.fillMaxWidth()},
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Button(
|
||||
onClick = {newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag);focusMgr.clearFocus()},
|
||||
onClick = {
|
||||
newUserHandle=myDpm.createAndManageUser(myComponent,userName,myComponent,null,selectedFlag)
|
||||
focusMgr.clearFocus()
|
||||
if(newUserHandle!=null){
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
enabled = isDeviceOwner(myDpm),
|
||||
modifier = if(!isWear){
|
||||
if(newUserHandle==null){Modifier.fillMaxWidth(1F)}else{Modifier.fillMaxWidth(0.48F)}
|
||||
if(newUserHandle==null){Modifier.fillMaxWidth(1F)}else{Modifier.fillMaxWidth(0.4F)}
|
||||
}else{Modifier}
|
||||
) {
|
||||
Text("创建")
|
||||
@@ -159,17 +287,20 @@ fun UserManage(){
|
||||
onClick = {
|
||||
if(myDpm.switchUser(myComponent,newUserHandle)){
|
||||
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||
navCtrl.navigate("HomePage")
|
||||
} else{
|
||||
Toast.makeText(myContext, "失败", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.92F)}
|
||||
modifier = if(isWear){Modifier}else{Modifier.fillMaxWidth(0.97F)}
|
||||
) {
|
||||
Text("切换至新用户")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(newUserHandle!=null){
|
||||
Text("新用户的序列号:${userManager.getSerialNumberForUser(newUserHandle)}")
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Text("创建用户需安卓7")
|
||||
@@ -243,11 +374,11 @@ fun UserSessionMessage(
|
||||
|
||||
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"
|
||||
UserManager.USER_OPERATION_SUCCESS->"成功"
|
||||
UserManager.USER_OPERATION_ERROR_UNKNOWN->"未知结果(失败)"
|
||||
UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE->"失败:受管理的资料"
|
||||
UserManager.USER_OPERATION_ERROR_CURRENT_USER->"失败:当前用户"
|
||||
else->"未知"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,3 +392,13 @@ private fun createWorkProfile(myContext: Context) {
|
||||
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,"hello")
|
||||
myContext.startActivity(intent)
|
||||
}
|
||||
|
||||
fun getCurrentUserId(): Int {
|
||||
try {
|
||||
val uh = UserHandle::class.java.getDeclaredMethod("myUserId")
|
||||
uh.isAccessible = true
|
||||
val userId = uh.invoke(null)
|
||||
if (userId is Int) { return userId }
|
||||
} catch (ignored: Exception) { }
|
||||
return -1
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ fun UserRestriction(){
|
||||
color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
if(isProfileOwner(myDpm)){
|
||||
Text(text = "Profile owner无法更改部分功能",
|
||||
Text(text = "Profile owner无法使用部分功能",
|
||||
style = if(!isWear){typography.bodyLarge}else{typography.bodyMedium})
|
||||
}
|
||||
if(isWear){
|
||||
@@ -221,7 +221,7 @@ private fun UserRestrictionItem(
|
||||
}
|
||||
}catch(e:SecurityException){
|
||||
if(isProfileOwner(myDpm)){
|
||||
Toast.makeText(myContext, "Profile owner 无法更改", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(myContext, "需要DeviceOwner", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
strictState = myDpm.getUserRestrictions(myComponent).getBoolean(restriction)
|
||||
|
||||
Reference in New Issue
Block a user