From 8f8ed1ec572ec8b792a45d84586c5a4da03fc901 Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Wed, 17 Jul 2024 11:58:20 +0800 Subject: [PATCH] start lock task mode (#36) --- .../main/java/com/bintianqi/owndroid/Utils.kt | 4 + .../bintianqi/owndroid/dpm/SystemManager.kt | 199 +++++++++++------- app/src/main/res/values-tr/strings.xml | 4 + app/src/main/res/values-zh-rCN/strings.xml | 4 + app/src/main/res/values/strings.xml | 4 + 5 files changed, 141 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/com/bintianqi/owndroid/Utils.kt b/app/src/main/java/com/bintianqi/owndroid/Utils.kt index 9085970..22dfb03 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Utils.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Utils.kt @@ -55,6 +55,10 @@ fun Set.toText(): String{ return output } +fun MutableList.toggle(status: Boolean, element: Int) { + if(status) add(element) else remove(element) +} + fun writeClipBoard(context: Context, string: String):Boolean{ val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager try { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt index 12facc7..730ea96 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -1,6 +1,8 @@ package com.bintianqi.owndroid.dpm import android.annotation.SuppressLint +import android.app.ActivityOptions +import android.app.AlertDialog import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY import android.app.admin.DevicePolicyManager.InstallSystemUpdateCallback @@ -8,7 +10,6 @@ import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_ST import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD -import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW import android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO @@ -97,6 +98,7 @@ import com.bintianqi.owndroid.Receiver import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.toText +import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.Information @@ -108,6 +110,7 @@ import com.bintianqi.owndroid.uriToStream import java.util.Date import java.util.TimeZone import java.util.concurrent.Executors +import kotlin.math.pow @Composable fun SystemManage(navCtrl:NavHostController) { @@ -144,7 +147,7 @@ fun SystemManage(navCtrl:NavHostController) { composable(route = "PermissionPolicy") { PermissionPolicy() } composable(route = "MTEPolicy") { MTEPolicy() } composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy() } - composable(route = "LockTaskFeatures") { LockTaskFeatures() } + composable(route = "LockTaskMode") { LockTaskMode() } composable(route = "CaCert") { CaCert() } composable(route = "SecurityLogs") { SecurityLogs() } composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } @@ -196,7 +199,7 @@ private fun Home(navCtrl: NavHostController, scrollState: ScrollState, rebootDia SubPageItem(R.string.nearby_streaming_policy, "", R.drawable.share_fill0) { navCtrl.navigate("NearbyStreamingPolicy") } } if(VERSION.SDK_INT >= 28 && isDeviceOwner(dpm)) { - SubPageItem(R.string.lock_task_feature, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskFeatures") } + SubPageItem(R.string.lock_task_mode, "", R.drawable.lock_fill0) { navCtrl.navigate("LockTaskMode") } } if(isDeviceOwner(dpm) || isProfileOwner(dpm)) { SubPageItem(R.string.ca_cert, "", R.drawable.license_fill0) { navCtrl.navigate("CaCert") } @@ -642,41 +645,29 @@ private fun NearbyStreamingPolicy() { @SuppressLint("NewApi") @Composable -private fun LockTaskFeatures() { +private fun LockTaskMode() { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context,Receiver::class.java) val focusMgr = LocalFocusManager.current Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { - 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) } - var inited by remember { mutableStateOf(false) } + val lockTaskFeatures = remember { mutableStateListOf() } var custom by remember { mutableStateOf(false) } val refreshFeature = { var calculate = dpm.getLockTaskFeatures(receiver) - if(calculate!=0) { - 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] } + lockTaskFeatures.clear() + if(calculate != 0) { + var sq = 10 + while(sq >= 1) { + val current = (2).toDouble().pow(sq.toDouble()).toInt() + if(calculate - current >= 0) { + lockTaskFeatures += current + calculate -= current + } + sq-- + } + if(calculate - 1 >= 0) { lockTaskFeatures += 1 } + custom = true }else{ custom = false } @@ -684,57 +675,87 @@ private fun LockTaskFeatures() { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.lock_task_feature), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) - if(!inited) { refreshFeature(); custom=dpm.getLockTaskFeatures(receiver)!=0; inited= true } + LaunchedEffect(Unit) { refreshFeature() } RadioButtonItem(stringResource(R.string.disable_all), !custom, { custom = false }) RadioButtonItem(stringResource(R.string.custom), custom, { custom = true }) AnimatedVisibility(custom) { Column { - CheckBoxItem(stringResource(R.string.ltf_sys_info), sysInfo, { sysInfo = it }) - CheckBoxItem(stringResource(R.string.ltf_notifications), notifications, { notifications = it }) - CheckBoxItem(stringResource(R.string.ltf_home), home, { home = it }) - CheckBoxItem(stringResource(R.string.ltf_overview), overview, { overview = it }) - CheckBoxItem(stringResource(R.string.ltf_global_actions), globalAction, { globalAction = it }) - CheckBoxItem(stringResource(R.string.ltf_keyguard), keyGuard, { keyGuard = it }) - if(VERSION.SDK_INT >= 30) { CheckBoxItem(stringResource(R.string.ltf_block_activity_start_in_task), blockAct, { blockAct = it }) } + CheckBoxItem( + stringResource(R.string.ltf_sys_info), + LOCK_TASK_FEATURE_SYSTEM_INFO in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_SYSTEM_INFO) } + ) + CheckBoxItem( + stringResource(R.string.ltf_notifications), + LOCK_TASK_FEATURE_NOTIFICATIONS in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_NOTIFICATIONS) } + ) + CheckBoxItem( + stringResource(R.string.ltf_home), + LOCK_TASK_FEATURE_HOME in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_HOME) } + ) + CheckBoxItem( + stringResource(R.string.ltf_overview), + LOCK_TASK_FEATURE_OVERVIEW in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_OVERVIEW) } + ) + CheckBoxItem( + stringResource(R.string.ltf_global_actions), + LOCK_TASK_FEATURE_GLOBAL_ACTIONS in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_GLOBAL_ACTIONS) } + ) + CheckBoxItem( + stringResource(R.string.ltf_keyguard), + LOCK_TASK_FEATURE_KEYGUARD in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_KEYGUARD) } + ) + if(VERSION.SDK_INT >= 30) { + CheckBoxItem( + stringResource(R.string.ltf_block_activity_start_in_task), + LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK in lockTaskFeatures, + { lockTaskFeatures.toggle(it, LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK) } + ) + } } } Button( modifier = Modifier.fillMaxWidth(), onClick = { - var result = lockTaskPolicyList[0] + var result = 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] } + lockTaskFeatures.forEach { result += it } + } + try { + dpm.setLockTaskFeatures(receiver,result) + Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() + } catch (e: IllegalArgumentException) { + AlertDialog.Builder(context) + .setTitle("Error") + .setMessage(e.message) + .setPositiveButton(R.string.confirm) { dialog, _ -> dialog.dismiss() } + .show() } - dpm.setLockTaskFeatures(receiver,result) refreshFeature() - Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() } ) { Text(stringResource(R.string.apply)) } + + val lockTaskPackages = remember { mutableStateListOf() } + var inputLockTaskPkg by remember { mutableStateOf("") } + LaunchedEffect(Unit) { lockTaskPackages.addAll(dpm.getLockTaskPackages(receiver)) } + Spacer(Modifier.padding(vertical = 10.dp)) + Text(text = stringResource(R.string.lock_task_packages), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) - val whitelist = dpm.getLockTaskPackages(receiver).toMutableList() - var listText by remember { mutableStateOf("") } - var inputPkg by remember { mutableStateOf("") } - val refreshWhitelist = { - inputPkg = "" - listText = "" - listText = whitelist.toText() - } - LaunchedEffect(Unit) { refreshWhitelist() } - Text(text = stringResource(R.string.whitelist_app), style = typography.titleLarge) SelectionContainer(modifier = Modifier.animateContentSize()) { - Text(text = if(listText == "") stringResource(R.string.none) else listText) + var listText = "" + lockTaskPackages.forEach { listText += "\n" + it } + Text(text = stringResource(R.string.app_list_is) + if(listText == "") stringResource(R.string.none) else listText) } OutlinedTextField( - value = inputPkg, - onValueChange = { inputPkg = it }, + value = inputLockTaskPkg, + onValueChange = { inputLockTaskPkg = it }, label = { Text(stringResource(R.string.package_name)) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), @@ -743,11 +764,8 @@ private fun LockTaskFeatures() { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Button( onClick = { - focusMgr.clearFocus() - whitelist.add(inputPkg) - dpm.setLockTaskPackages(receiver,whitelist.toTypedArray()) + lockTaskPackages.add(inputLockTaskPkg) Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() - refreshWhitelist() }, modifier = Modifier.fillMaxWidth(0.49F) ) { @@ -755,21 +773,54 @@ private fun LockTaskFeatures() { } Button( onClick = { - focusMgr.clearFocus() - if(inputPkg in whitelist) { - whitelist.remove(inputPkg) - dpm.setLockTaskPackages(receiver,whitelist.toTypedArray()) - Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() - } else { - Toast.makeText(context, R.string.not_exist, Toast.LENGTH_SHORT).show() - } - refreshWhitelist() + Toast.makeText(context, if(lockTaskPackages.remove(inputLockTaskPkg)) R.string.success else R.string.not_exist, Toast.LENGTH_SHORT).show() }, modifier = Modifier.fillMaxWidth(0.96F) ) { Text(stringResource(R.string.remove)) } } + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + dpm.setLockTaskPackages(receiver, lockTaskPackages.toTypedArray()) + Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() + } + ) { + Text(stringResource(R.string.apply)) + } + var startLockTaskApp by remember { mutableStateOf("") } + Spacer(Modifier.padding(vertical = 10.dp)) + Text(text = stringResource(R.string.start_lock_task_mode), style = typography.headlineLarge) + Spacer(Modifier.padding(vertical = 5.dp)) + OutlinedTextField( + value = startLockTaskApp, + onValueChange = { startLockTaskApp = it }, + label = { Text(stringResource(R.string.package_name)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) + ) + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { + if(!dpm.getLockTaskPackages(receiver).contains(startLockTaskApp)) { + Toast.makeText(context, R.string.app_not_allowed, Toast.LENGTH_SHORT).show() + return@Button + } + val options = ActivityOptions.makeBasic().setLockTaskEnabled(true) + val packageManager = context.packageManager + val launchIntent = packageManager.getLaunchIntentForPackage(startLockTaskApp) + if (launchIntent != null) { + context.startActivity(launchIntent, options.toBundle()) + Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(context, R.string.failed, Toast.LENGTH_SHORT).show() + } + } + ) { + Text(stringResource(R.string.start)) + } Spacer(Modifier.padding(vertical = 30.dp)) } } diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 0c93aef..a0c3e39 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -151,7 +151,11 @@ Yakındaki akış politikası Yakındaki bildirim akış politikası Yalnızca aynı yönetilen hesap + Lock task mode Görev kilitleme özelliği + Lock task packages + Start lock task mode + App not allowed Hepsini devre dışı bırak diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index bd14fee..9674f59 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -146,7 +146,11 @@ 附近流式传输策略 附近通知传输 在足够安全时启用 + 锁定任务模式 锁定任务功能 + 锁定任务应用 + 启动锁定任务模式 + 应用未被允许 禁用全部 允许状态栏信息 允许通知 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ab75ac8..c4651f5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -157,7 +157,11 @@ Nearby streaming policy Nearby notification streaming policy Same managed account only + Lock task mode Lock task feature + Lock task packages + Start lock task mode + App not allowed Disable all Allow system info