mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
set keepUninstalledPkgs and userIcon
This commit is contained in:
36
Guide.md
36
Guide.md
@@ -345,9 +345,11 @@ MTE: Memory Tagging Extension(内存标记拓展)[安卓开发者:MTE](htt
|
|||||||
|
|
||||||
或者清除所有用户证书
|
或者清除所有用户证书
|
||||||
|
|
||||||
### 安全日志
|
### 安全日志&&重启前安全日志
|
||||||
|
|
||||||
需要的权限:Device owner
|
需要的权限:Device owner或由组织拥有的工作资料的Profile owner
|
||||||
|
|
||||||
|
如果被Device owner使用,设备上不能有非附属用户,否则不会有输出
|
||||||
|
|
||||||
需要API24或以上
|
需要API24或以上
|
||||||
|
|
||||||
@@ -462,6 +464,10 @@ API34或以上将不能在系统用户中使用WipeData,如果要恢复出厂
|
|||||||
|
|
||||||
需要的权限:Device owner或工作资料的Profile owner
|
需要的权限:Device owner或工作资料的Profile owner
|
||||||
|
|
||||||
|
工作资料中使用此功能只会记录工作资料的网络日志
|
||||||
|
|
||||||
|
如果被Device owner使用,设备上不能有非附属用户
|
||||||
|
|
||||||
需要API26或以上
|
需要API26或以上
|
||||||
|
|
||||||
功能开发中
|
功能开发中
|
||||||
@@ -673,7 +679,17 @@ adb shell pm list permissions
|
|||||||
|
|
||||||
需要的权限:Device owner或Profile owner
|
需要的权限:Device owner或Profile owner
|
||||||
|
|
||||||
设置许可的无障碍应用和输入法
|
无障碍应用:强制启用无障碍应用
|
||||||
|
|
||||||
|
输入法:强制启用输入法,但是不强制用户使用输入法
|
||||||
|
|
||||||
|
### 保持卸载的应用
|
||||||
|
|
||||||
|
需要Device owner
|
||||||
|
|
||||||
|
需要API28或以上
|
||||||
|
|
||||||
|
作用未知
|
||||||
|
|
||||||
### 清除应用存储
|
### 清除应用存储
|
||||||
|
|
||||||
@@ -878,6 +894,16 @@ Device owner无论在何时都是附属于设备的用户
|
|||||||
|
|
||||||
需要Device owner或Profile owner
|
需要Device owner或Profile owner
|
||||||
|
|
||||||
|
### 用户图标
|
||||||
|
|
||||||
|
选择一个图片并设置为当前用户的图标
|
||||||
|
|
||||||
|
需要Device owner或Profile owner
|
||||||
|
|
||||||
|
需要API23或以上
|
||||||
|
|
||||||
|
尽量选择一个正方形的图片,分辨率不要太大,以免产生问题
|
||||||
|
|
||||||
### 用户会话开始/结束消息
|
### 用户会话开始/结束消息
|
||||||
|
|
||||||
用户会话开始消息:切换到非系统用户时的消息
|
用户会话开始消息:切换到非系统用户时的消息
|
||||||
@@ -892,13 +918,13 @@ Device owner无论在何时都是附属于设备的用户
|
|||||||
|
|
||||||
### 密码信息
|
### 密码信息
|
||||||
|
|
||||||
当前密码复杂度:参考要求密码复杂度
|
当前密码复杂度:参考[密码复杂度要求](#密码复杂度要求)(需要API29或以上)
|
||||||
|
|
||||||
密码达到要求:当前密码复杂度是否达到了要求的密码复杂度
|
密码达到要求:当前密码复杂度是否达到了要求的密码复杂度
|
||||||
|
|
||||||
密码已错误次数:你能看到这个数字的时候,这个数字一定是0
|
密码已错误次数:你能看到这个数字的时候,这个数字一定是0
|
||||||
|
|
||||||
个人与工作应用密码一致:需要是工作资料的Profile owner
|
个人与工作应用密码一致:需要是工作资料的Profile owner,需要API28或以上
|
||||||
|
|
||||||
### 密码重置令牌
|
### 密码重置令牌
|
||||||
|
|
||||||
|
|||||||
11
Readme.md
11
Readme.md
@@ -13,9 +13,9 @@
|
|||||||
|
|
||||||
### 缺点
|
### 缺点
|
||||||
|
|
||||||
不支持一些功能。如果追求功能齐全,谷歌官方的 [TestDPC](https://github.com/googlesamples/android-testdpc) 可能更适合你
|
功能不完整。如果追求功能齐全,谷歌官方的 [TestDPC](https://github.com/googlesamples/android-testdpc) 可能更适合你
|
||||||
|
|
||||||
### 功能简介
|
### 功能
|
||||||
|
|
||||||
适配了一些预见式返回动画(需安卓13或14),可能不太稳定,如果有问题请向我反馈
|
适配了一些预见式返回动画(需安卓13或14),可能不太稳定,如果有问题请向我反馈
|
||||||
|
|
||||||
@@ -27,12 +27,7 @@
|
|||||||
- 用户管理
|
- 用户管理
|
||||||
- 密码与锁屏
|
- 密码与锁屏
|
||||||
|
|
||||||
所有功能的详细介绍请看 [用户指南](Guide.md)
|
[用户指南](Guide.md)(一定要看,应用里只有功能的简单介绍)
|
||||||
|
|
||||||
### 这个应用十分危险!!!
|
|
||||||
|
|
||||||
在使用各个功能之前,请仔细阅读相应的说明。红色的按钮一定要谨慎使用!
|
|
||||||
如果操作不慎,可能会意外地丢失数据或者让你无法解锁你的设备!
|
|
||||||
|
|
||||||
### 正在开发的功能
|
### 正在开发的功能
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId = "com.binbin.androidowner"
|
applicationId = "com.binbin.androidowner"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 14
|
versionCode = 15
|
||||||
versionName = "3.1"
|
versionName = "3.2"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ import java.util.concurrent.Executors
|
|||||||
|
|
||||||
private var credentialList = mutableSetOf<String>()
|
private var credentialList = mutableSetOf<String>()
|
||||||
private var crossProfilePkg = mutableSetOf<String>()
|
private var crossProfilePkg = mutableSetOf<String>()
|
||||||
|
private var keepUninstallPkg = mutableListOf<String>()
|
||||||
|
private var permittedIme = mutableListOf<String>()
|
||||||
|
private var permittedAccessibility = mutableListOf<String>()
|
||||||
@Composable
|
@Composable
|
||||||
fun ApplicationManage(){
|
fun ApplicationManage(){
|
||||||
val myContext = LocalContext.current
|
val myContext = LocalContext.current
|
||||||
@@ -218,7 +221,6 @@ fun ApplicationManage(){
|
|||||||
var inputPermission by remember{mutableStateOf("android.permission.")}
|
var inputPermission by remember{mutableStateOf("android.permission.")}
|
||||||
var currentState by remember{mutableStateOf(grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)])}
|
var currentState by remember{mutableStateOf(grantState[myDpm.getPermissionGrantState(myComponent,pkgName,inputPermission)])}
|
||||||
Text(text = "权限管理", style = typography.titleLarge)
|
Text(text = "权限管理", style = typography.titleLarge)
|
||||||
Text(text = "查看系统支持的权限:adb shell pm list permissions", style = bodyTextStyle)
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = inputPermission,
|
value = inputPermission,
|
||||||
label = { Text("权限")},
|
label = { Text("权限")},
|
||||||
@@ -258,6 +260,7 @@ fun ApplicationManage(){
|
|||||||
Text("由用户决定")
|
Text("由用户决定")
|
||||||
}
|
}
|
||||||
Text(text ="设为允许或拒绝后,用户不能改变状态", style = bodyTextStyle)
|
Text(text ="设为允许或拒绝后,用户不能改变状态", style = bodyTextStyle)
|
||||||
|
if(VERSION.SDK_INT>=31){Text(text = "可以修改传感器相关权限:${myDpm.canAdminGrantSensorsPermissions()}", style = bodyTextStyle)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,85 +416,140 @@ fun ApplicationManage(){
|
|||||||
if(isProfileOwner(myDpm)||isDeviceOwner(myDpm)){
|
if(isProfileOwner(myDpm)||isDeviceOwner(myDpm)){
|
||||||
Column(modifier = sections()) {
|
Column(modifier = sections()) {
|
||||||
Text(text = "许可的无障碍应用", style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
|
Text(text = "许可的无障碍应用", style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
|
||||||
var list = mutableListOf("")
|
|
||||||
var listText by remember{ mutableStateOf("") }
|
var listText by remember{ mutableStateOf("") }
|
||||||
val refreshList = {
|
val refreshList = {
|
||||||
list = myDpm.getPermittedAccessibilityServices(myComponent) ?: mutableListOf("")
|
|
||||||
listText = ""
|
listText = ""
|
||||||
var count = list.size
|
var count = permittedAccessibility.size
|
||||||
for(eachAccessibility in list) {
|
for(eachAccessibility in permittedAccessibility){ count-=1; listText+=eachAccessibility; if(count>0){listText+="\n"} }
|
||||||
count -= 1
|
|
||||||
listText += eachAccessibility
|
|
||||||
if(count>0) { listText += "\n" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var inited by remember{mutableStateOf(false)}
|
var inited by remember{mutableStateOf(false)}
|
||||||
if(!inited){refreshList(); inited=true}
|
if(!inited){
|
||||||
|
val getList = myDpm.getPermittedAccessibilityServices(myComponent)
|
||||||
|
if(getList!=null){ permittedAccessibility = getList }
|
||||||
|
refreshList(); inited=true
|
||||||
|
}
|
||||||
Text(text = if(listText!=""){listText}else{"无"}, style = bodyTextStyle)
|
Text(text = if(listText!=""){listText}else{"无"}, style = bodyTextStyle)
|
||||||
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
|
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = { permittedAccessibility.add(pkgName); refreshList()},
|
||||||
focusMgr.clearFocus()
|
|
||||||
list.plus(pkgName)
|
|
||||||
Toast.makeText(myContext, if(myDpm.setPermittedAccessibilityServices(myComponent,list)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
|
||||||
refreshList()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
modifier = Modifier.fillMaxWidth(0.49F)
|
||||||
) {
|
) {
|
||||||
Text("添加")
|
Text("添加")
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = { permittedAccessibility.remove(pkgName); refreshList() },
|
||||||
focusMgr.clearFocus()
|
|
||||||
list.remove(pkgName)
|
|
||||||
Toast.makeText(myContext, if(myDpm.setPermittedAccessibilityServices(myComponent,list)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
|
||||||
refreshList()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
modifier = Modifier.fillMaxWidth(0.96F)
|
||||||
) {
|
) {
|
||||||
Text("移除")
|
Text("移除")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
focusMgr.clearFocus()
|
||||||
|
Toast.makeText(myContext, if(myDpm.setPermittedAccessibilityServices(myComponent, permittedAccessibility)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||||
|
val getList = myDpm.getPermittedAccessibilityServices(myComponent)
|
||||||
|
if(getList!=null){ permittedAccessibility = getList }
|
||||||
|
refreshList()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(text = "应用")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
if(isDeviceOwner(myDpm)||isProfileOwner(myDpm)){
|
||||||
Column(modifier = sections()) {
|
Column(modifier = sections()) {
|
||||||
Text(text = "许可的输入法", style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
|
Text(text = "许可的输入法", style = typography.titleLarge,color = colorScheme.onPrimaryContainer)
|
||||||
var imeList = mutableListOf<String>()
|
|
||||||
var imeListText by remember{ mutableStateOf("") }
|
var imeListText by remember{ mutableStateOf("") }
|
||||||
val refreshList = {
|
val refreshList = {
|
||||||
if(myDpm.getPermittedInputMethods(myComponent)!=null){ imeList = myDpm.getPermittedInputMethods(myComponent)!! }
|
|
||||||
imeListText = ""
|
imeListText = ""
|
||||||
for(eachIme in imeList){ imeListText += "$eachIme \n" }
|
for(eachIme in permittedIme){ imeListText += "$eachIme \n" }
|
||||||
}
|
}
|
||||||
var inited by remember{mutableStateOf(false)}
|
var inited by remember{mutableStateOf(false)}
|
||||||
if(!inited){refreshList();inited=true}
|
if(!inited){
|
||||||
|
val getList = myDpm.getPermittedInputMethods(myComponent)
|
||||||
|
if(getList!=null){ permittedIme = getList }
|
||||||
|
refreshList();inited=true
|
||||||
|
}
|
||||||
Text(text = if(imeListText!=""){imeListText}else{"无"}, style = bodyTextStyle)
|
Text(text = if(imeListText!=""){imeListText}else{"无"}, style = bodyTextStyle)
|
||||||
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
|
Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween){
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = { permittedIme.add(pkgName); refreshList() },
|
||||||
focusMgr.clearFocus()
|
|
||||||
imeList.plus(pkgName)
|
|
||||||
Toast.makeText(myContext, if(myDpm.setPermittedInputMethods(myComponent, imeList)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
|
||||||
refreshList()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
modifier = Modifier.fillMaxWidth(0.49F)
|
||||||
) {
|
) {
|
||||||
Text("添加")
|
Text("添加")
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = { permittedIme.remove(pkgName); refreshList()},
|
||||||
focusMgr.clearFocus()
|
|
||||||
imeList.remove(pkgName)
|
|
||||||
Toast.makeText(myContext, if(myDpm.setPermittedInputMethods(myComponent, imeList)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
|
||||||
refreshList()
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
modifier = Modifier.fillMaxWidth(0.96F)
|
||||||
) {
|
) {
|
||||||
Text("移除")
|
Text("移除")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
Toast.makeText(myContext, if(myDpm.setPermittedInputMethods(myComponent, permittedIme)){"成功"}else{"失败"}, Toast.LENGTH_SHORT).show()
|
||||||
|
val getList = myDpm.getPermittedInputMethods(myComponent)
|
||||||
|
if(getList!=null){ permittedIme = getList }
|
||||||
|
refreshList()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("应用")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
|
||||||
|
Column(modifier = sections()){
|
||||||
|
Text(text = "保持卸载的应用", style = typography.titleLarge)
|
||||||
|
Text(text = "作用未知", style = bodyTextStyle)
|
||||||
|
var listText by remember{mutableStateOf("")}
|
||||||
|
val refresh = {
|
||||||
|
listText = ""
|
||||||
|
var count = keepUninstallPkg.size
|
||||||
|
for(each in keepUninstallPkg){ count-=1; listText+=each; if(count>0){listText+="\n"} }
|
||||||
|
}
|
||||||
|
var inited by remember{mutableStateOf(false)}
|
||||||
|
if(!inited){
|
||||||
|
val getList = myDpm.getKeepUninstalledPackages(myComponent)
|
||||||
|
if(getList!=null){ keepUninstallPkg = getList }
|
||||||
|
refresh(); inited=true
|
||||||
|
}
|
||||||
|
Text(text = if(listText==""){"无"}else{listText}, style = bodyTextStyle)
|
||||||
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween){
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
keepUninstallPkg.add(pkgName)
|
||||||
|
refresh()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(0.49F)
|
||||||
|
){
|
||||||
|
Text("添加")
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
keepUninstallPkg.remove(pkgName)
|
||||||
|
refresh()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth(0.96F)
|
||||||
|
){
|
||||||
|
Text("移除")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
focusMgr.clearFocus()
|
||||||
|
myDpm.setKeepUninstalledPackages(myComponent, keepUninstallPkg)
|
||||||
|
val getList = myDpm.getKeepUninstalledPackages(myComponent)
|
||||||
|
if(getList!=null){ keepUninstallPkg = getList }
|
||||||
|
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
){
|
||||||
|
Text("应用")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,7 +567,7 @@ fun ApplicationManage(){
|
|||||||
myDpm.clearApplicationUserData(myComponent,pkgName,executor,onClear)
|
myDpm.clearApplicationUserData(myComponent,pkgName,executor,onClear)
|
||||||
},
|
},
|
||||||
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm),
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp)
|
||||||
) {
|
) {
|
||||||
Text("清除应用存储")
|
Text("清除应用存储")
|
||||||
}
|
}
|
||||||
@@ -584,10 +642,14 @@ fun ApplicationManage(){
|
|||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
if(apkUri!=null){
|
||||||
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||||
intent.setData(apkUri)
|
intent.setData(apkUri)
|
||||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
myContext.startActivity(intent)
|
myContext.startActivity(intent)
|
||||||
|
}else{
|
||||||
|
Toast.makeText(myContext, "请先选择APK", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
modifier = Modifier.fillMaxWidth(0.96F)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -485,16 +485,31 @@ fun DeviceControl(){
|
|||||||
onClick = {
|
onClick = {
|
||||||
val log = myDpm.retrieveSecurityLogs(myComponent)
|
val log = myDpm.retrieveSecurityLogs(myComponent)
|
||||||
if(log!=null){
|
if(log!=null){
|
||||||
for(i in log){ Log.d("NetLog",i.toString()) }
|
for(i in log){ Log.d("SecureLog",i.toString()) }
|
||||||
Toast.makeText(myContext,"已输出至Log",Toast.LENGTH_SHORT).show()
|
Toast.makeText(myContext,"已输出至Log",Toast.LENGTH_SHORT).show()
|
||||||
}else{
|
}else{
|
||||||
Log.d("NetLog","无")
|
Log.d("Secure5Log","无")
|
||||||
Toast.makeText(myContext,"无",Toast.LENGTH_SHORT).show()
|
Toast.makeText(myContext,"无日志",Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text("收集")
|
Text("安全日志")
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
val log = myDpm.retrievePreRebootSecurityLogs(myComponent)
|
||||||
|
if(log!=null){
|
||||||
|
for(i in log){ Log.d("SecureLog",i.toString()) }
|
||||||
|
Toast.makeText(myContext,"已输出至Log",Toast.LENGTH_SHORT).show()
|
||||||
|
}else{
|
||||||
|
Log.d("SecureLog","无")
|
||||||
|
Toast.makeText(myContext,"无日志",Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("重启前安全日志")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
@@ -50,7 +49,9 @@ lateinit var getCaCert: ActivityResultLauncher<Intent>
|
|||||||
lateinit var createUser:ActivityResultLauncher<Intent>
|
lateinit var createUser:ActivityResultLauncher<Intent>
|
||||||
lateinit var createManagedProfile:ActivityResultLauncher<Intent>
|
lateinit var createManagedProfile:ActivityResultLauncher<Intent>
|
||||||
lateinit var getApk:ActivityResultLauncher<Intent>
|
lateinit var getApk:ActivityResultLauncher<Intent>
|
||||||
lateinit var apkUri: Uri
|
lateinit var getUserIcon:ActivityResultLauncher<Intent>
|
||||||
|
var userIconUri:Uri? = null
|
||||||
|
var apkUri: Uri? = null
|
||||||
var caCert = byteArrayOf()
|
var caCert = byteArrayOf()
|
||||||
|
|
||||||
@ExperimentalMaterial3Api
|
@ExperimentalMaterial3Api
|
||||||
@@ -58,10 +59,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
getUserIcon = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
userIconUri = it.data?.data
|
||||||
|
if(userIconUri==null){ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
||||||
|
}
|
||||||
getApk = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
getApk = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
val uri = it.data?.data
|
apkUri = it.data?.data
|
||||||
if(uri!=null){ apkUri = uri }
|
if(apkUri==null){ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
||||||
else{ Toast.makeText(applicationContext, "空URI", Toast.LENGTH_SHORT).show() }
|
|
||||||
}
|
}
|
||||||
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
getCaCert = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
uriToStream(applicationContext,it.data?.data){stream->
|
uriToStream(applicationContext,it.data?.data){stream->
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ fun Password(){
|
|||||||
}
|
}
|
||||||
val pwdFailedAttempts = myDpm.currentFailedPasswordAttempts
|
val pwdFailedAttempts = myDpm.currentFailedPasswordAttempts
|
||||||
Text(text = "密码已错误次数:$pwdFailedAttempts",style=bodyTextStyle)
|
Text(text = "密码已错误次数:$pwdFailedAttempts",style=bodyTextStyle)
|
||||||
if(VERSION.SDK_INT>=28&&(myDpm.isManagedProfile(myComponent)||myDpm.isProfileOwnerApp("com.binbin.androidowner"))){
|
if(VERSION.SDK_INT>=28&&isProfileOwner(myDpm)&&myDpm.isManagedProfile(myComponent)){
|
||||||
val unifiedPwd = myDpm.isUsingUnifiedPassword(myComponent)
|
val unifiedPwd = myDpm.isUsingUnifiedPassword(myComponent)
|
||||||
Text("个人与工作应用密码一致:$unifiedPwd",style=bodyTextStyle)
|
Text("个人与工作应用密码一致:$unifiedPwd",style=bodyTextStyle)
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,6 @@ fun Password(){
|
|||||||
}
|
}
|
||||||
if(isWear){ Text(text = "(可以水平滚动)",style=typography.bodyMedium) }
|
if(isWear){ Text(text = "(可以水平滚动)",style=typography.bodyMedium) }
|
||||||
Text("没有密码时会自动激活令牌",style=bodyTextStyle)
|
Text("没有密码时会自动激活令牌",style=bodyTextStyle)
|
||||||
Text("有可能无法设置密码重置令牌,因机而异",style=bodyTextStyle)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ class MyDeviceAdminReceiver : DeviceAdminReceiver() {
|
|||||||
override fun onTransferOwnershipComplete(context: Context, bundle: PersistableBundle?) {
|
override fun onTransferOwnershipComplete(context: Context, bundle: PersistableBundle?) {
|
||||||
super.onTransferOwnershipComplete(context, bundle)
|
super.onTransferOwnershipComplete(context, bundle)
|
||||||
if(bundle!=null){
|
if(bundle!=null){
|
||||||
Toast.makeText(context,"转移控制权完毕,附加内容长度:${bundle.size()}",Toast.LENGTH_SHORT).show()
|
Toast.makeText(context,"转移所有权完毕,附加内容长度:${bundle.size()}",Toast.LENGTH_SHORT).show()
|
||||||
Log.d("TransferOwnerShip",bundle.toString())
|
Log.d("TransferOwnerShip",bundle.toString())
|
||||||
}else{
|
}else{
|
||||||
Toast.makeText(context,"转移控制权完毕,无附加内容}",Toast.LENGTH_SHORT).show()
|
Toast.makeText(context,"转移所有权完毕,无附加内容}",Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ fun AppSetting(navCtrl:NavHostController){
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { shareLink(myContext, "https://github.com/BinTianqi/AndroidOwner/Guide.md") }
|
.clickable { shareLink(myContext, "https://github.com/BinTianqi/AndroidOwner/blob/master/Guide.md") }
|
||||||
.padding(start = 8.dp, bottom = 4.dp)
|
.padding(start = 8.dp, bottom = 4.dp)
|
||||||
){
|
){
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ package com.binbin.androidowner
|
|||||||
import android.app.admin.DevicePolicyManager
|
import android.app.admin.DevicePolicyManager
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -32,6 +35,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.os.UserManagerCompat
|
import androidx.core.os.UserManagerCompat
|
||||||
|
|
||||||
|
|
||||||
var affiliationID = mutableSetOf<String>()
|
var affiliationID = mutableSetOf<String>()
|
||||||
@Composable
|
@Composable
|
||||||
fun UserManage() {
|
fun UserManage() {
|
||||||
@@ -290,31 +294,62 @@ fun UserManage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserSessionMessage("用户名","用户名",true,myDpm,myContext,{null},{msg -> myDpm.setProfileName(myComponent, msg.toString())})
|
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)
|
||||||
|
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){
|
if(VERSION.SDK_INT>=28){
|
||||||
UserSessionMessage("用户会话开始消息","消息",false,myDpm,myContext,{myDpm.getStartUserSessionMessage(myComponent)},{msg -> myDpm.setStartUserSessionMessage(myComponent,msg)})
|
UserSessionMessage("用户会话开始消息", "消息", false, {myDpm.getStartUserSessionMessage(myComponent)}) {msg-> myDpm.setStartUserSessionMessage(myComponent, msg)}
|
||||||
UserSessionMessage("用户会话结束消息","消息",false,myDpm,myContext,{myDpm.getEndUserSessionMessage(myComponent)},{msg -> myDpm.setEndUserSessionMessage(myComponent,msg)})
|
UserSessionMessage("用户会话结束消息", "消息", false, {myDpm.getEndUserSessionMessage(myComponent)}) {msg-> myDpm.setEndUserSessionMessage(myComponent, msg)}
|
||||||
}
|
}
|
||||||
Spacer(Modifier.padding(vertical = 30.dp))
|
Spacer(Modifier.padding(vertical = 30.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun UserSessionMessage(
|
private fun UserSessionMessage(text:String, textField:String, profileOwner:Boolean, get: ()->CharSequence?, setMsg:(msg: CharSequence?)->Unit){
|
||||||
text:String,
|
|
||||||
textField:String,
|
|
||||||
profileOwner:Boolean,
|
|
||||||
myDpm:DevicePolicyManager,
|
|
||||||
myContext: Context,
|
|
||||||
get:()->CharSequence?,
|
|
||||||
setMsg:(msg:CharSequence?)->Unit
|
|
||||||
){
|
|
||||||
Column(
|
Column(
|
||||||
modifier = sections()
|
modifier = sections()
|
||||||
) {
|
) {
|
||||||
|
val myContext = LocalContext.current
|
||||||
|
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var msg by remember{ mutableStateOf(if(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)){ if(get()==null){""}else{get().toString()} }else{""}) }
|
var msg by remember{ mutableStateOf(if(isDeviceOwner(myDpm)||(isProfileOwner(myDpm)&&profileOwner)){ if(get()==null){""}else{get().toString()} }else{""}) }
|
||||||
val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE)
|
val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
|
||||||
val isWear = sharedPref.getBoolean("isWear",false)
|
val isWear = sharedPref.getBoolean("isWear",false)
|
||||||
Text(text = text, style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
Text(text = text, style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
|
||||||
TextField(
|
TextField(
|
||||||
|
|||||||
Reference in New Issue
Block a user