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