From 32f43ce801091804a8ea973fb55a5af3356d016a Mon Sep 17 00:00:00 2001 From: BinTianqi Date: Tue, 13 Aug 2024 21:03:03 +0800 Subject: [PATCH] Move AlwaysOnVpn from App manager to Network Use Shizuku to list users and list accounts Add package selector for Lock task mode Specify an Activity before start lock task mode Improve UI design --- .../java/com/bintianqi/owndroid/Setting.kt | 22 ++--- .../owndroid/dpm/ApplicationManage.kt | 46 ---------- .../com/bintianqi/owndroid/dpm/Network.kt | 91 ++++++++++++++++++- .../bintianqi/owndroid/dpm/ShizukuActivate.kt | 22 +++++ .../bintianqi/owndroid/dpm/SystemManager.kt | 91 +++++++++++++++---- app/src/main/res/values-tr/strings.xml | 5 +- app/src/main/res/values-zh-rCN/strings.xml | 3 + app/src/main/res/values/strings.xml | 5 +- 8 files changed, 202 insertions(+), 83 deletions(-) diff --git a/app/src/main/java/com/bintianqi/owndroid/Setting.kt b/app/src/main/java/com/bintianqi/owndroid/Setting.kt index 0d93298..b016648 100644 --- a/app/src/main/java/com/bintianqi/owndroid/Setting.kt +++ b/app/src/main/java/com/bintianqi/owndroid/Setting.kt @@ -67,11 +67,11 @@ private fun Home(navCtrl: NavHostController) { @Composable private fun Options() { val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { SwitchItem( R.string.show_dangerous_features, "", R.drawable.warning_fill0, { sharedPref.getBoolean("dangerous_features", false) }, - { sharedPref.edit().putBoolean("dangerous_features", it).apply() } + { sharedPref.edit().putBoolean("dangerous_features", it).apply() }, padding = false ) } } @@ -79,15 +79,15 @@ private fun Options() { @Composable private fun ThemeSettings(materialYou:MutableState, blackTheme:MutableState) { val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { - if(VERSION.SDK_INT>=31) { + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { + if(VERSION.SDK_INT >= 31) { SwitchItem( R.string.material_you_color, stringResource(R.string.dynamic_color_desc), null, { sharedPref.getBoolean("material_you",true) }, { sharedPref.edit().putBoolean("material_you", it).apply() materialYou.value = it - } + }, padding = false ) } if(isSystemInDarkTheme()) { @@ -97,7 +97,7 @@ private fun ThemeSettings(materialYou:MutableState, blackTheme:MutableS { sharedPref.edit().putBoolean("black_theme", it).apply() blackTheme.value = it - } + }, padding = false ) } } @@ -107,30 +107,30 @@ private fun ThemeSettings(materialYou:MutableState, blackTheme:MutableS private fun AuthSettings() { val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) var auth by remember{ mutableStateOf(sharedPref.getBoolean("auth",false)) } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { SwitchItem( R.string.lock_owndroid, "", null, auth, { sharedPref.edit().putBoolean("auth", it).apply() auth = sharedPref.getBoolean("auth", false) - } + }, padding = false ) if(auth) { SwitchItem( R.string.enable_bio_auth, "", null, { sharedPref.getBoolean("bio_auth", false) }, - { sharedPref.edit().putBoolean("bio_auth", it).apply() } + { sharedPref.edit().putBoolean("bio_auth", it).apply() }, padding = false ) SwitchItem( R.string.lock_in_background, stringResource(R.string.developing), null, { sharedPref.getBoolean("lock_in_background", false) }, - { sharedPref.edit().putBoolean("lock_in_background", it).apply() } + { sharedPref.edit().putBoolean("lock_in_background", it).apply() }, padding = false ) } SwitchItem( R.string.protect_storage, "", null, { sharedPref.getBoolean("protect_storage", false) }, - { sharedPref.edit().putBoolean("protect_storage", it).apply() } + { sharedPref.edit().putBoolean("protect_storage", it).apply() }, padding = false ) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt index 7301b76..32c38d9 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt @@ -147,7 +147,6 @@ fun ApplicationManage(navCtrl:NavHostController, dialogStatus: MutableIntState) composable(route = "Home") { Home(localNavCtrl, pkgName, dialogStatus) } - composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(pkgName) } composable(route = "UserControlDisabled") { UserCtrlDisabledPkg(pkgName) } composable(route = "PermissionManage") { PermissionManage(pkgName) } composable(route = "CrossProfilePackage") { CrossProfilePkg(pkgName) } @@ -245,9 +244,6 @@ private fun Home( onClickBlank = { appControlAction = 3; appControlDialog = true } ) } - if(VERSION.SDK_INT >= 24 && (deviceOwner || profileOwner)) { - SubPageItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") } - } if((VERSION.SDK_INT >= 33 && profileOwner) || (VERSION.SDK_INT >= 30 && deviceOwner)) { SubPageItem(R.string.ucd, "", R.drawable.do_not_touch_fill0) { navCtrl.navigate("UserControlDisabled") } } @@ -349,48 +345,6 @@ private fun Home( } } -@SuppressLint("NewApi") -@Composable -fun AlwaysOnVPNPackage(pkgName: String) { - val context = LocalContext.current - val dpm = context.getDPM() - val receiver = context.getReceiver() - var lockdown by remember { mutableStateOf(false) } - var pkg by remember { mutableStateOf("") } - val refresh = { pkg = dpm.getAlwaysOnVpnPackage(receiver) } - LaunchedEffect(Unit) { refresh() } - val setAlwaysOnVpn: (String?, Boolean)->Unit = { vpnPkg: String?, lockdownEnabled: Boolean -> - try { - dpm.setAlwaysOnVpnPackage(receiver, vpnPkg, lockdownEnabled) - Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() - } catch(e: UnsupportedOperationException) { - Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() - } catch(e: NameNotFoundException) { - Toast.makeText(context, R.string.not_installed, Toast.LENGTH_SHORT).show() - } - } - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.always_on_vpn), style = typography.headlineLarge, modifier = Modifier.padding(vertical = 8.dp)) - Text(text = stringResource(R.string.current_app_is) + pkg, modifier = Modifier.padding(vertical = 8.dp)) - SwitchItem(R.string.enable_lockdown, "", null, lockdown, { lockdown = it }, padding = false) - Spacer(Modifier.padding(vertical = 5.dp)) - Button( - onClick = { setAlwaysOnVpn(pkgName, lockdown); refresh() }, - modifier = Modifier.fillMaxWidth() - ) { - Text(stringResource(R.string.apply)) - } - Spacer(Modifier.padding(vertical = 5.dp)) - Button( - onClick = { setAlwaysOnVpn(null, false); refresh() }, - modifier = Modifier.fillMaxWidth() - ) { - Text(stringResource(R.string.clear_current_config)) - } - Spacer(Modifier.padding(vertical = 30.dp)) - } -} @SuppressLint("NewApi") @Composable diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt index 118183a..347db2d 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -16,6 +16,7 @@ import android.app.admin.WifiSsidPolicy import android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST import android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST import android.content.Context +import android.content.pm.PackageManager.NameNotFoundException import android.net.ProxyInfo import android.net.Uri import android.net.wifi.WifiSsid @@ -42,6 +43,7 @@ import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -50,15 +52,16 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset 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.text.selection.SelectionContainer import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold @@ -69,17 +72,21 @@ import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -91,6 +98,7 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.R +import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.toText import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem @@ -130,6 +138,7 @@ fun Network(navCtrl: NavHostController) { composable(route = "MinWifiSecurityLevel") { WifiSecLevel() } composable(route = "WifiSsidPolicy") { WifiSsidPolicy() } composable(route = "PrivateDNS") { PrivateDNS() } + composable(route = "AlwaysOnVpn") { AlwaysOnVPNPackage(navCtrl) } composable(route = "RecommendedGlobalProxy") { RecommendedGlobalProxy() } composable(route = "NetworkLog") { NetworkLog() } composable(route = "WifiAuthKeypair") { WifiAuthKeypair() } @@ -178,6 +187,9 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState, wifiMacDia if(VERSION.SDK_INT >= 29 && deviceOwner) { SubPageItem(R.string.private_dns, "", R.drawable.dns_fill0) { navCtrl.navigate("PrivateDNS") } } + if(VERSION.SDK_INT >= 24 && (deviceOwner || profileOwner)) { + SubPageItem(R.string.always_on_vpn, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("AlwaysOnVpn") } + } if(deviceOwner) { SubPageItem(R.string.recommended_global_proxy, "", R.drawable.vpn_key_fill0) { navCtrl.navigate("RecommendedGlobalProxy") } } @@ -200,17 +212,17 @@ private fun Switches() { val dpm = context.getDPM() val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner - Column(modifier = Modifier.fillMaxSize()) { + Column(modifier = Modifier.fillMaxSize().padding(start = 20.dp, end = 16.dp)) { Spacer(Modifier.padding(vertical = 5.dp)) if(VERSION.SDK_INT >= 33 && deviceOwner) { SwitchItem( R.string.preferential_network_service, stringResource(R.string.developing), R.drawable.globe_fill0, - { dpm.isPreferentialNetworkServiceEnabled }, { dpm.isPreferentialNetworkServiceEnabled = it } + { dpm.isPreferentialNetworkServiceEnabled }, { dpm.isPreferentialNetworkServiceEnabled = it }, padding = false ) } if(VERSION.SDK_INT>=30 && (deviceOwner || dpm.isOrgProfile(receiver))) { SwitchItem(R.string.lockdown_admin_configured_network, "", R.drawable.wifi_password_fill0, - { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) } + { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) }, padding = false ) } } @@ -438,6 +450,77 @@ private fun PrivateDNS() { } } +@SuppressLint("NewApi") +@Composable +fun AlwaysOnVPNPackage(navCtrl: NavHostController) { + val context = LocalContext.current + val dpm = context.getDPM() + val receiver = context.getReceiver() + var lockdown by rememberSaveable { mutableStateOf(false) } + var pkgName by rememberSaveable { mutableStateOf("") } + val focusMgr = LocalFocusManager.current + val refresh = { pkgName = dpm.getAlwaysOnVpnPackage(receiver) ?: "" } + LaunchedEffect(Unit) { refresh() } + val updatePackage by selectedPackage.collectAsState() + LaunchedEffect(updatePackage) { + if(selectedPackage.value != "") { + pkgName = selectedPackage.value + selectedPackage.value = "" + } + } + val setAlwaysOnVpn: (String?, Boolean)->Boolean = { vpnPkg: String?, lockdownEnabled: Boolean -> + try { + dpm.setAlwaysOnVpnPackage(receiver, vpnPkg, lockdownEnabled) + Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show() + true + } catch(e: UnsupportedOperationException) { + Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() + false + } catch(e: NameNotFoundException) { + Toast.makeText(context, R.string.not_installed, Toast.LENGTH_SHORT).show() + false + } + } + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { + Spacer(Modifier.padding(vertical = 10.dp)) + Text(text = stringResource(R.string.always_on_vpn), style = typography.headlineLarge, modifier = Modifier.padding(vertical = 8.dp)) + OutlinedTextField( + value = pkgName, + onValueChange = { pkgName = it }, + label = { Text(stringResource(R.string.package_name)) }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + trailingIcon = { + Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + modifier = Modifier + .clip(RoundedCornerShape(50)) + .clickable(onClick = { + focusMgr.clearFocus() + navCtrl.navigate("PackageSelector") + }) + .padding(3.dp)) + }, + modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) + ) + SwitchItem(R.string.enable_lockdown, "", null, lockdown, { lockdown = it }, padding = false) + Spacer(Modifier.padding(vertical = 5.dp)) + Button( + onClick = { if(setAlwaysOnVpn(pkgName, lockdown)) refresh() }, + modifier = Modifier.fillMaxWidth() + ) { + Text(stringResource(R.string.apply)) + } + Spacer(Modifier.padding(vertical = 5.dp)) + Button( + onClick = { if(setAlwaysOnVpn(null, false)) refresh() }, + modifier = Modifier.fillMaxWidth() + ) { + Text(stringResource(R.string.clear_current_config)) + } + Spacer(Modifier.padding(vertical = 30.dp)) + } +} + @Composable private fun RecommendedGlobalProxy() { val context = LocalContext.current diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt index 5f3d0d7..f07adaa 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ShizukuActivate.kt @@ -114,6 +114,28 @@ fun ShizukuActivate() { ) { Text(text = stringResource(R.string.list_owners)) } + Button( + onClick = { + coScope.launch{ + outputText = service!!.execute("pm list users") + outputTextScrollState.animateScrollTo(0) + } + }, + enabled = enabled + ) { + Text(text = stringResource(R.string.list_users)) + } + Button( + onClick = { + coScope.launch{ + outputText = service!!.execute("dumpsys account") + outputTextScrollState.animateScrollTo(0) + } + }, + enabled = enabled + ) { + Text(text = stringResource(R.string.list_accounts)) + } Spacer(Modifier.padding(vertical = 5.dp)) AnimatedVisibility(showDeviceAdminButton && showDeviceOwnerButton) { 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 57b11b5..28496d6 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -35,6 +35,7 @@ import android.app.admin.SystemUpdatePolicy import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC import android.app.admin.SystemUpdatePolicy.TYPE_INSTALL_WINDOWED import android.app.admin.SystemUpdatePolicy.TYPE_POSTPONE +import android.content.ComponentName import android.content.Context import android.content.Intent import android.net.Uri @@ -45,16 +46,17 @@ import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width 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.text.selection.SelectionContainer @@ -62,6 +64,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.OutlinedTextField @@ -79,12 +82,15 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -100,6 +106,7 @@ import com.bintianqi.owndroid.StopLockTaskModeReceiver import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.prepareForNotification +import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.toText import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.ui.Animations @@ -117,7 +124,7 @@ import java.util.concurrent.Executors import kotlin.math.pow @Composable -fun SystemManage(navCtrl:NavHostController) { +fun SystemManage(navCtrl: NavHostController) { val localNavCtrl = rememberNavController() val backStackEntry by localNavCtrl.currentBackStackEntryAsState() val scrollState = rememberScrollState() @@ -151,7 +158,7 @@ fun SystemManage(navCtrl:NavHostController) { composable(route = "PermissionPolicy") { PermissionPolicy() } composable(route = "MTEPolicy") { MTEPolicy() } composable(route = "NearbyStreamingPolicy") { NearbyStreamingPolicy() } - composable(route = "LockTaskMode") { LockTaskMode() } + composable(route = "LockTaskMode") { LockTaskMode(navCtrl) } composable(route = "CaCert") { CaCert() } composable(route = "SecurityLogs") { SecurityLogs() } composable(route = "SystemUpdatePolicy") { SysUpdatePolicy() } @@ -239,53 +246,53 @@ private fun Switches() { val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { Spacer(Modifier.padding(vertical = 10.dp)) if(deviceOwner || profileOwner) { SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0, - { dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) } + { dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) }, padding = false ) } if(deviceOwner || profileOwner) { SwitchItem(R.string.disable_screen_capture, "", R.drawable.screenshot_fill0, - { dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) } + { dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) }, padding = false ) } if(VERSION.SDK_INT >= 34 && (deviceOwner || (profileOwner && dpm.isAffiliatedUser))) { SwitchItem(R.string.disable_status_bar, "", R.drawable.notifications_fill0, - { dpm.isStatusBarDisabled}, { dpm.setStatusBarDisabled(receiver,it) } + { dpm.isStatusBarDisabled}, { dpm.setStatusBarDisabled(receiver,it) }, padding = false ) } if(deviceOwner || dpm.isOrgProfile(receiver)) { if(VERSION.SDK_INT >= 30) { SwitchItem(R.string.auto_time, "", R.drawable.schedule_fill0, - { dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) } + { dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) }, padding = false ) SwitchItem(R.string.auto_timezone, "", R.drawable.globe_fill0, - { dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) } + { dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) }, padding = false ) }else{ - SwitchItem(R.string.require_auto_time, "", R.drawable.schedule_fill0, { dpm.autoTimeRequired}, { dpm.setAutoTimeRequired(receiver,it) }) + SwitchItem(R.string.require_auto_time, "", R.drawable.schedule_fill0, { dpm.autoTimeRequired}, { dpm.setAutoTimeRequired(receiver,it) }, padding = false) } } if(deviceOwner || profileOwner) { SwitchItem(R.string.master_mute, "", R.drawable.volume_up_fill0, - { dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) } + { dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) }, padding = false ) } if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { SwitchItem(R.string.backup_service, "", R.drawable.backup_fill0, - { dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) } + { dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) }, padding = false ) } if(VERSION.SDK_INT >= 23 && (deviceOwner || profileOwner)) { SwitchItem(R.string.disable_bt_contact_share, "", R.drawable.account_circle_fill0, - { dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) } + { dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) }, padding = false ) } if(VERSION.SDK_INT >= 30 && deviceOwner) { SwitchItem(R.string.common_criteria_mode , "",R.drawable.security_fill0, - { dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) } + { dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) }, padding = false ) } if(VERSION.SDK_INT >= 31 && (deviceOwner || dpm.isOrgProfile(receiver))) { @@ -297,7 +304,7 @@ private fun Switches() { } else { Toast.makeText(context, R.string.unsupported, Toast.LENGTH_SHORT).show() } - } + }, padding = false ) } Spacer(Modifier.padding(vertical = 30.dp)) @@ -657,15 +664,16 @@ private fun NearbyStreamingPolicy() { @SuppressLint("NewApi") @Composable -private fun LockTaskMode() { +private fun LockTaskMode(navCtrl: NavHostController) { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current val coroutine = rememberCoroutineScope() + var appSelectorRequest by rememberSaveable { mutableIntStateOf(0) } Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { val lockTaskFeatures = remember { mutableStateListOf() } - var custom by remember { mutableStateOf(false) } + var custom by rememberSaveable { mutableStateOf(false) } val refreshFeature = { var calculate = dpm.getLockTaskFeatures(receiver) lockTaskFeatures.clear() @@ -756,7 +764,7 @@ private fun LockTaskMode() { } val lockTaskPackages = remember { mutableStateListOf() } - var inputLockTaskPkg by remember { mutableStateOf("") } + var inputLockTaskPkg by rememberSaveable { 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) @@ -772,6 +780,17 @@ private fun LockTaskMode() { label = { Text(stringResource(R.string.package_name)) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + trailingIcon = { + Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + modifier = Modifier + .clip(RoundedCornerShape(50)) + .clickable(onClick = { + focusMgr.clearFocus() + appSelectorRequest = 1 + navCtrl.navigate("PackageSelector") + }) + .padding(3.dp)) + }, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) ) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { @@ -797,7 +816,16 @@ private fun LockTaskMode() { ) { Text(stringResource(R.string.apply)) } - var startLockTaskApp by remember { mutableStateOf("") } + var startLockTaskApp by rememberSaveable { mutableStateOf("") } + var startLockTaskActivity by rememberSaveable { mutableStateOf("") } + var specifyActivity by rememberSaveable { mutableStateOf(false) } + val updatePackage by selectedPackage.collectAsState() + LaunchedEffect(updatePackage) { + if(updatePackage != "") { + if(appSelectorRequest == 1) inputLockTaskPkg = updatePackage else startLockTaskApp = updatePackage + selectedPackage.value = "" + } + } Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.start_lock_task_mode), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) @@ -807,8 +835,30 @@ private fun LockTaskMode() { label = { Text(stringResource(R.string.package_name)) }, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + trailingIcon = { + Icon(painter = painterResource(R.drawable.checklist_fill0), contentDescription = null, + modifier = Modifier + .clip(RoundedCornerShape(50)) + .clickable(onClick = { + focusMgr.clearFocus() + appSelectorRequest = 2 + navCtrl.navigate("PackageSelector") + }) + .padding(3.dp)) + }, modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp) ) + CheckBoxItem(R.string.specify_activity, specifyActivity, { specifyActivity = it }) + AnimatedVisibility(specifyActivity) { + OutlinedTextField( + value = startLockTaskActivity, + onValueChange = { startLockTaskActivity = it }, + label = { Text("Activity") }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), + modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp) + ) + } Button( modifier = Modifier.fillMaxWidth(), onClick = { @@ -818,7 +868,8 @@ private fun LockTaskMode() { } val options = ActivityOptions.makeBasic().setLockTaskEnabled(true) val packageManager = context.packageManager - val launchIntent = packageManager.getLaunchIntentForPackage(startLockTaskApp) + val launchIntent = if(specifyActivity) Intent().setComponent(ComponentName(startLockTaskApp, startLockTaskActivity)) + else packageManager.getLaunchIntentForPackage(startLockTaskApp) if (launchIntent != null) { coroutine.launch { prepareForNotification(context) { diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f0c285c..4c2c4d0 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -109,6 +109,8 @@ İzni Kontrol Et Sahipleri Listele + List users + List accounts Shizuku Başlatılmadı. İzin Verildi (Kabuk) İzin Verildi (Root) @@ -160,8 +162,9 @@ Lock task mode Görev kilitleme özelliği Lock task packages + Specify Activity Start lock task mode - App not allowed + App is 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 a0a8520..bcf5583 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -104,6 +104,8 @@ 检查Shizuku 列出Owners + 列出用户 + 列出账号 服务未启动 已授权(Shell) 已授权(Root) @@ -155,6 +157,7 @@ 锁定任务模式 锁定任务功能 锁定任务应用 + 指定Activity 启动锁定任务模式 应用未被允许 禁用全部 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cb6a54f..e5d0210 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -111,6 +111,8 @@ Shizuku Check permission List owners + List users + List accounts Shizuku not started. dpm set-device-owner com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver dpm set-active-admin com.bintianqi.owndroid/com.bintianqi.owndroid.Receiver @@ -164,8 +166,9 @@ Lock task mode Lock task feature Lock task packages + Specify Activity Start lock task mode - App not allowed + App is not allowed Disable all Allow system info