lock task features

This commit is contained in:
BinTianqi
2024-02-04 21:48:43 +08:00
parent 8d4f8b2da8
commit 5bc3f40d6e
4 changed files with 191 additions and 8 deletions

View File

@@ -11,6 +11,8 @@
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS"/> <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"/> <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"/> <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES"/>
<uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"/>
<application <application
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"

View File

@@ -7,6 +7,7 @@ import android.content.Context
import android.os.Build.VERSION import android.os.Build.VERSION
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -25,9 +26,9 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import java.lang.IllegalArgumentException
@Composable @Composable
fun DeviceControl(){ fun DeviceControl(){
@@ -201,6 +202,126 @@ fun DeviceControl(){
Text(text = "Wifi安全等级需API33", modifier = Modifier.padding(vertical = 3.dp)) Text(text = "Wifi安全等级需API33", modifier = Modifier.padding(vertical = 3.dp))
} }
if(VERSION.SDK_INT>=28&&isDeviceOwner(myDpm)){
Column(modifier = sections()){
val lockTaskPolicyList = mutableListOf(
LOCK_TASK_FEATURE_NONE,
LOCK_TASK_FEATURE_SYSTEM_INFO,
LOCK_TASK_FEATURE_NOTIFICATIONS,
LOCK_TASK_FEATURE_HOME,
LOCK_TASK_FEATURE_OVERVIEW,
LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
LOCK_TASK_FEATURE_KEYGUARD
)
var sysInfo by remember{mutableStateOf(false)}
var notifications by remember{mutableStateOf(false)}
var home by remember{mutableStateOf(false)}
var overview by remember{mutableStateOf(false)}
var globalAction by remember{mutableStateOf(false)}
var keyGuard by remember{mutableStateOf(false)}
var blockAct by remember{mutableStateOf(false)}
if(VERSION.SDK_INT>=30){lockTaskPolicyList.add(LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK)}
val refreshFeature = {
var calculate = myDpm.getLockTaskFeatures(myComponent)
if(VERSION.SDK_INT>=30&&calculate-lockTaskPolicyList[7]>=0){blockAct=true;calculate-=lockTaskPolicyList[7]}
if(calculate-lockTaskPolicyList[6]>=0){keyGuard=true;calculate-=lockTaskPolicyList[6]}
if(calculate-lockTaskPolicyList[5]>=0){globalAction=true;calculate-=lockTaskPolicyList[5]}
if(calculate-lockTaskPolicyList[4]>=0){overview=true;calculate-=lockTaskPolicyList[4]}
if(calculate-lockTaskPolicyList[3]>=0){home=true;calculate-=lockTaskPolicyList[3]}
if(calculate-lockTaskPolicyList[2]>=0){notifications=true;calculate-=lockTaskPolicyList[2]}
if(calculate-lockTaskPolicyList[1]>=0){sysInfo=true;calculate-=lockTaskPolicyList[1]}
}
Text(text = "锁定任务模式", style = typography.titleLarge, color = colorScheme.onPrimaryContainer)
var inited by remember{mutableStateOf(false)}
var custom by remember{mutableStateOf(false)}
if(!inited){ refreshFeature();custom=myDpm.getLockTaskFeatures(myComponent)!=0;inited=true }
Text(text = "在锁定任务模式下:", style = bodyTextStyle)
RadioButtonItem("禁用全部",{!custom},{custom=false})
RadioButtonItem("自定义",{custom},{custom=true})
AnimatedVisibility(custom) {
Column {
CheckBoxItem("允许状态栏信息",{sysInfo},{sysInfo=!sysInfo})
CheckBoxItem("允许通知",{notifications},{notifications=!notifications})
CheckBoxItem("允许返回主屏幕",{home},{home=!home})
CheckBoxItem("允许打开后台应用概览",{overview},{overview=!overview})
CheckBoxItem("允许全局行为(比如长按电源键对话框)",{globalAction},{globalAction=!globalAction})
CheckBoxItem("允许锁屏(如果没有选择此项,即使有密码也不会锁屏)",{keyGuard},{keyGuard=!keyGuard})
if(VERSION.SDK_INT>=30){ CheckBoxItem("阻止启动未允许的应用",{blockAct},{blockAct=!blockAct}) }
}
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {
var result = lockTaskPolicyList[0]
if(custom){
if(blockAct&&VERSION.SDK_INT>=30){result+=lockTaskPolicyList[7]}
if(keyGuard){result+=lockTaskPolicyList[6]}
if(globalAction){result+=lockTaskPolicyList[5]}
if(overview){result+=lockTaskPolicyList[4]}
if(home){result+=lockTaskPolicyList[3]}
if(notifications){result+=lockTaskPolicyList[2]}
if(sysInfo){result+=lockTaskPolicyList[1]}
}
myDpm.setLockTaskFeatures(myComponent,result)
refreshFeature()
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}
) {
Text("应用")
}
Spacer(Modifier.padding(vertical = 4.dp))
val whitelist = myDpm.getLockTaskPackages(myComponent).toMutableList()
var listText by remember{mutableStateOf("")}
var inputPkg by remember{mutableStateOf("")}
val refreshWhitelist = {
listText=""
var currentItem = whitelist.size
for(each in whitelist){
currentItem-=1
listText += each
if(currentItem>0){listText += "\n"}
}
}
refreshWhitelist()
Text(text = "白名单应用", style = typography.titleLarge)
if(listText!=""){ Text(listText) }else{ Text(("")) }
TextField(
value = inputPkg,
onValueChange = {inputPkg=it},
label = {Text("包名")},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus()}),
modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp)
)
Button(
onClick = {
whitelist.add(inputPkg)
myDpm.setLockTaskPackages(myComponent,whitelist.toTypedArray())
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
refreshWhitelist()
},
modifier = Modifier.fillMaxWidth()
) {
Text("加入白名单")
}
Button(
onClick = {
if(inputPkg in whitelist){
whitelist.remove(inputPkg)
myDpm.setLockTaskPackages(myComponent,whitelist.toTypedArray())
Toast.makeText(myContext, "成功", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(myContext, "不存在", Toast.LENGTH_SHORT).show()
}
refreshWhitelist()
},
modifier = Modifier.fillMaxWidth()
) {
Text("从白名单中移除")
}
}
}
if(VERSION.SDK_INT>=29&&isDeviceOwner(myDpm)){ if(VERSION.SDK_INT>=29&&isDeviceOwner(myDpm)){
Column(modifier = sections()){ Column(modifier = sections()){
Text(text = "私人DNS", style = typography.titleLarge) Text(text = "私人DNS", style = typography.titleLarge)
@@ -245,6 +366,8 @@ fun DeviceControl(){
Toast.makeText(myContext, operationResult[result], Toast.LENGTH_SHORT).show() Toast.makeText(myContext, operationResult[result], Toast.LENGTH_SHORT).show()
}catch(e:IllegalArgumentException){ }catch(e:IllegalArgumentException){
Toast.makeText(myContext, "无效主机名", Toast.LENGTH_SHORT).show() Toast.makeText(myContext, "无效主机名", Toast.LENGTH_SHORT).show()
}catch(e:SecurityException){
Toast.makeText(myContext, "安全错误", Toast.LENGTH_SHORT).show()
}finally { }finally {
status = dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)] status = dnsStatus[myDpm.getGlobalPrivateDnsMode(myComponent)]
} }

View File

@@ -4,9 +4,13 @@ import android.annotation.SuppressLint
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.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.* import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -34,12 +38,21 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.binbin.androidowner.ui.theme.AndroidOwnerTheme import com.binbin.androidowner.ui.theme.AndroidOwnerTheme
/*lateinit var getOtaPackage: ActivityResultLauncher<Intent>
var installOta = false
lateinit var otaUri:Uri*/
@ExperimentalMaterial3Api @ExperimentalMaterial3Api
class MainActivity : ComponentActivity() { 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)
/*otaUri = Uri.EMPTY
getOtaPackage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val data = it.data?.data
installOta = true
otaUri = data ?: Uri.EMPTY
}*/
setContent { setContent {
AndroidOwnerTheme { AndroidOwnerTheme {
MyScaffold() MyScaffold()

View File

@@ -1,9 +1,12 @@
package com.binbin.androidowner package com.binbin.androidowner
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback
import android.app.admin.SystemUpdateInfo import android.app.admin.SystemUpdateInfo
import android.app.admin.SystemUpdatePolicy import android.app.admin.SystemUpdatePolicy
import android.content.ComponentName import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build.VERSION import android.os.Build.VERSION
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
@@ -23,6 +26,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import java.util.Date import java.util.Date
import java.util.concurrent.Executors
@Composable @Composable
fun SysUpdatePolicy(){ fun SysUpdatePolicy(){
@@ -30,21 +34,24 @@ fun SysUpdatePolicy(){
val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val myDpm = myContext.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager
val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java) val myComponent = ComponentName(myContext,MyDeviceAdminReceiver::class.java)
val focusMgr = LocalFocusManager.current val focusMgr = LocalFocusManager.current
val sharedPref = myContext.getSharedPreferences("data", Context.MODE_PRIVATE)
val isWear = sharedPref.getBoolean("isWear",false)
val bodyTextStyle = if(isWear){ typography.bodyMedium}else{typography.bodyLarge}
Column { Column {
if(VERSION.SDK_INT>=26){ if(VERSION.SDK_INT>=26&&isDeviceOwner(myDpm)){
val sysUpdateInfo = myDpm.getPendingSystemUpdate(myComponent)
Column(modifier = sections()) { Column(modifier = sections()) {
val sysUpdateInfo = if(isDeviceOwner(myDpm)){myDpm.getPendingSystemUpdate(myComponent)}else{null}
if(sysUpdateInfo!=null){ if(sysUpdateInfo!=null){
Text("Update first available: ${Date(sysUpdateInfo.receivedTime)}") Text(text = "Update first available: ${Date(sysUpdateInfo.receivedTime)}", style = bodyTextStyle)
Text("Hash code: ${sysUpdateInfo.hashCode()}") Text(text = "Hash code: ${sysUpdateInfo.hashCode()}", style = bodyTextStyle)
val securityStateDesc = when(sysUpdateInfo.securityPatchState){ val securityStateDesc = when(sysUpdateInfo.securityPatchState){
SystemUpdateInfo.SECURITY_PATCH_STATE_UNKNOWN->"SECURITY_PATCH_STATE_UNKNOWN" SystemUpdateInfo.SECURITY_PATCH_STATE_UNKNOWN->"SECURITY_PATCH_STATE_UNKNOWN"
SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE->"SECURITY_PATCH_STATE_TRUE" SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE->"SECURITY_PATCH_STATE_TRUE"
else->"SECURITY_PATCH_STATE_FALSE" else->"SECURITY_PATCH_STATE_FALSE"
} }
Text("Security patch state: $securityStateDesc") Text(text = "Security patch state: $securityStateDesc", style = bodyTextStyle)
}else{ }else{
Text("暂无更新信息") Text(text = "暂无系统更新", style = bodyTextStyle)
} }
} }
} }
@@ -80,7 +87,7 @@ fun SysUpdatePolicy(){
) )
} }
Spacer(Modifier.padding(vertical = 3.dp)) Spacer(Modifier.padding(vertical = 3.dp))
Text("请输入一天中的分钟0~1440") Text(text = "请输入一天中的分钟0~1440", style = bodyTextStyle)
} }
val policy = val policy =
when(selectedPolicy){ when(selectedPolicy){
@@ -97,5 +104,43 @@ fun SysUpdatePolicy(){
Text("应用") Text("应用")
} }
}} }}
/*if(VERSION.SDK_INT>=29){
Column(modifier = sections()){
var resultUri by remember{mutableStateOf(otaUri)}
Text(text = "安装系统更新", style = typography.titleLarge)
Button(
onClick = {
val getUri = Intent(Intent.ACTION_GET_CONTENT)
getUri.setType("application/zip")
getUri.addCategory(Intent.CATEGORY_OPENABLE)
getOtaPackage.launch(getUri)
},
modifier = Modifier.fillMaxWidth(),
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
) {
Text("选择OTA包")
}
Button(
onClick = {resultUri = otaUri},
modifier = Modifier.fillMaxWidth(),
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
) {
Text("查看OTA包详情")
}
Text("URI: $resultUri")
if(installOta){
Button(
onClick = {
val sysUpdateExecutor = Executors.newCachedThreadPool()
val sysUpdateCallback:InstallSystemUpdateCallback = InstallSystemUpdateCallback
myDpm.installSystemUpdate(myComponent,resultUri,sysUpdateExecutor,sysUpdateCallback)
},
enabled = isDeviceOwner(myDpm)||isProfileOwner(myDpm)
){
Text("安装")
}
}
}
}*/
} }
} }