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 4569bc6..e41913b 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ApplicationManage.kt @@ -85,6 +85,7 @@ import com.bintianqi.owndroid.fileUriFlow import com.bintianqi.owndroid.getFile import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.ui.Animations +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.NavIcon @@ -295,8 +296,7 @@ private fun Home( else -> R.string.unknown } ), - style = typography.headlineMedium, - modifier = Modifier.padding(start = 5.dp) + style = typography.headlineMedium ) }, text = { @@ -306,10 +306,11 @@ private fun Home( 3 -> blockUninstall else -> false } - Text( - text = stringResource(R.string.current_state, stringResource(if(enabled) R.string.enabled else R.string.disabled)), - modifier = Modifier.padding(start = 5.dp, top = 5.dp, bottom = 5.dp) - ) + Column { + Text(stringResource(R.string.current_state, stringResource(if(enabled) R.string.enabled else R.string.disabled))) + Spacer(Modifier.padding(vertical = 4.dp)) + if(appControlAction == 1) Text(stringResource(R.string.info_suspend_app)) + } }, confirmButton = { TextButton( @@ -374,6 +375,7 @@ private fun UserCtrlDisabledPkg(pkgName:String) { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_disable_user_control) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -799,6 +801,7 @@ private fun KeepUninstalledApp(pkgName: String) { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_keep_uninstalled_apps) Spacer(Modifier.padding(vertical = 30.dp)) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt index 6637749..88f9b95 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/ManagedProfile.kt @@ -63,6 +63,7 @@ import com.bintianqi.owndroid.R import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem import com.bintianqi.owndroid.ui.CopyTextButton +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.TopBar @@ -271,6 +272,7 @@ private fun SuspendPersonalApp() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_profile_maximum_time_off) } } @@ -321,6 +323,7 @@ private fun IntentFilter() { ) { Text(stringResource(R.string.clear_cross_profile_filters)) } + InfoCard(R.string.info_cross_profile_intent_filter) } } 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 2f1bd5d..df130bb 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Network.kt @@ -111,6 +111,7 @@ import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.SubPageItem @@ -241,14 +242,23 @@ private fun Switches() { val dpm = context.getDPM() val receiver = context.getReceiver() val deviceOwner = context.isDeviceOwner - Column(modifier = Modifier.fillMaxSize().padding(start = 20.dp, end = 16.dp)) { + var dialog by remember { mutableIntStateOf(0) } + Column(modifier = Modifier.fillMaxSize()) { Spacer(Modifier.padding(vertical = 5.dp)) 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) }, padding = false + { dpm.hasLockdownAdminConfiguredNetworks(receiver) }, { dpm.setConfiguredNetworksLockdownState(receiver,it) }, + onClickBlank = { dialog = 1 } ) } } + if(dialog != 0) AlertDialog( + text = { Text(stringResource(R.string.info_lockdown_admin_configured_network)) }, + confirmButton = { + TextButton(onClick = { dialog = 0 }) { Text(stringResource(R.string.confirm)) } + }, + onDismissRequest = { dialog = 0 } + ) } @SuppressLint("NewApi") @@ -292,6 +302,7 @@ private fun WifiSecLevel() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_minimum_wifi_security_level) } } @@ -418,6 +429,7 @@ private fun PrivateDNS() { Text(stringResource(R.string.set_to_opportunistic)) } } + InfoCard(R.string.info_private_dns_mode_oppertunistic) Spacer(Modifier.padding(vertical = 10.dp)) var inputHost by remember { mutableStateOf(dpm.getGlobalPrivateDnsHost(receiver) ?: "") } OutlinedTextField( @@ -450,6 +462,8 @@ private fun PrivateDNS() { ) { Text(stringResource(R.string.set_dns_host)) } + InfoCard(R.string.info_set_private_dns_host) + Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -522,6 +536,7 @@ fun AlwaysOnVPNPackage(navCtrl: NavHostController) { ) { Text(stringResource(R.string.clear_current_config)) } + InfoCard(R.string.info_always_on_vpn) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -621,6 +636,7 @@ private fun RecommendedGlobalProxy() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_recommended_global_proxy) } } @@ -667,6 +683,7 @@ private fun NetworkLog() { Text(stringResource(R.string.delete_logs)) } } + InfoCard(R.string.info_network_log) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt index 3084454..26bc8bd 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Password.kt @@ -32,6 +32,7 @@ import android.app.admin.DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY import android.content.Context import android.content.Intent import android.os.Build.VERSION +import android.os.UserManager import android.widget.Toast import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ScrollState @@ -83,6 +84,7 @@ import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.SubPageItem @@ -200,6 +202,7 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState) { }, text = { val focusMgr = LocalFocusManager.current + val um = context.getSystemService(Context.USER_SERVICE) as UserManager Column { OutlinedTextField( value = input, @@ -217,13 +220,18 @@ private fun Home(navCtrl:NavHostController, scrollState: ScrollState) { keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }), textStyle = typography.bodyLarge, - modifier = Modifier.fillMaxWidth().padding(bottom = 5.dp) + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp) ) - when(dialog) { - 4 -> Text(stringResource(R.string.max_pwd_fail_desc)) - 5 -> Text(stringResource(R.string.pwd_history_desc)) - } - Text(stringResource(R.string.zero_means_no_restriction)) + Text(stringResource( + when(dialog) { + 1 -> R.string.info_screen_timeout + 2 -> R.string.info_required_strong_auth_timeout + 3 -> R.string.info_password_expiration_timeout + 4 -> if(um.isSystemUser) R.string.info_max_failed_password_system_user else R.string.info_max_failed_password_other_user + 5 -> R.string.info_password_history_length + else -> R.string.password + } + )) } }, confirmButton = { @@ -435,6 +443,7 @@ private fun ResetPassword() { Text(stringResource(R.string.reset_password)) } } + InfoCard(R.string.info_reset_password) Spacer(Modifier.padding(vertical = 30.dp)) } if(confirmDialog) { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index 3c2e5b4..e544e3e 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -102,9 +102,8 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { SwitchItem( R.string.dhizuku, "", null, { sharedPref.getBoolean("dhizuku", false) }, - { - toggleDhizukuMode(it, context) - } + { toggleDhizukuMode(it, context) }, + onClickBlank = { dialog = 4 } ) } SubPageItem( @@ -154,6 +153,7 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { 1 -> R.string.enrollment_specific_id 2 -> R.string.org_name 3 -> R.string.org_id + 4 -> R.string.dhizuku else -> R.string.permission } )) @@ -163,35 +163,41 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { LaunchedEffect(Unit) { if(dialog == 1) input = dpm.enrollmentSpecificId } - OutlinedTextField( - value = input, - onValueChange = { input = it }, readOnly = dialog == 1, modifier = Modifier.fillMaxWidth(), - label = { - Text(stringResource( - when(dialog){ - 1 -> R.string.enrollment_specific_id - 2 -> R.string.org_name - 3 -> R.string.org_id - else -> R.string.permission + Column { + if(dialog != 4) OutlinedTextField( + value = input, + onValueChange = { input = it }, readOnly = dialog == 1, + label = { + Text(stringResource( + when(dialog){ + 1 -> R.string.enrollment_specific_id + 2 -> R.string.org_name + 3 -> R.string.org_id + else -> R.string.permission + } + )) + }, + trailingIcon = { + if(dialog == 1) IconButton(onClick = { writeClipBoard(context, input) }) { + Icon(painter = painterResource(R.drawable.content_copy_fill0), contentDescription = stringResource(R.string.copy)) } - )) - }, - trailingIcon = { - if(dialog == 1) IconButton(onClick = { writeClipBoard(context, input) }) { - Icon(painter = painterResource(R.drawable.content_copy_fill0), contentDescription = stringResource(R.string.copy)) - } - }, - supportingText = { - if(dialog == 3) Text(stringResource(R.string.length_6_to_64)) - }, - keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), - keyboardActions = KeyboardActions { focusMgr.clearFocus() }, - textStyle = typography.bodyLarge - ) + }, + supportingText = { + if(dialog == 3) Text(stringResource(R.string.length_6_to_64)) + }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions { focusMgr.clearFocus() }, + textStyle = typography.bodyLarge, + modifier = Modifier.fillMaxWidth().padding(bottom = if(dialog == 2) 0.dp else 10.dp) + ) + if(dialog == 1) Text(stringResource(R.string.info_enrollment_specific_id)) + if(dialog == 3) Text(stringResource(R.string.info_org_id)) + if(dialog == 4) Text(stringResource(R.string.info_dhizuku)) + } }, onDismissRequest = { dialog = 0 }, dismissButton = { - TextButton( + if(dialog != 4) TextButton( onClick = { dialog = 0 } ) { Text(stringResource(R.string.cancel)) @@ -261,6 +267,8 @@ private fun LockScreenInfo() { value = infoText, label = { Text(stringResource(R.string.device_owner_lock_screen_info)) }, onValueChange = { infoText = it }, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions { focusMgr.clearFocus() }, modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp) ) Button( @@ -283,6 +291,8 @@ private fun LockScreenInfo() { ) { Text(text = stringResource(R.string.reset)) } + Spacer(Modifier.padding(vertical = 10.dp)) + InfoCard(R.string.info_lock_screen_info) } } @@ -366,6 +376,7 @@ private fun ProfileOwner() { Text(stringResource(R.string.deactivate)) } } + InfoCard(R.string.profile_owner) } if(deactivateDialog && VERSION.SDK_INT >= 24) { AlertDialog( @@ -469,6 +480,7 @@ fun DeviceInfo() { val context = LocalContext.current val dpm = context.getDPM() val receiver = context.getReceiver() + var dialog by remember { mutableIntStateOf(0) } Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.device_info), style = typography.headlineLarge) @@ -489,16 +501,21 @@ fun DeviceInfo() { if(VERSION.SDK_INT >= 24) { encryptionStatus[DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER] = R.string.es_active_per_user } CardItem(R.string.encryption_status, encryptionStatus[dpm.storageEncryptionStatus] ?: R.string.unknown) if(VERSION.SDK_INT >= 28) { - CardItem(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported.yesOrNo()) + CardItem(R.string.support_device_id_attestation, dpm.isDeviceIdAttestationSupported.yesOrNo()) { dialog = 1 } } if (VERSION.SDK_INT >= 30) { - CardItem(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported.yesOrNo()) + CardItem(R.string.support_unique_device_attestation, dpm.isUniqueDeviceAttestationSupported.yesOrNo()) { dialog = 2 } } val adminList = dpm.activeAdmins if(adminList != null) { CardItem(R.string.activated_device_admin, adminList.map { it.flattenToShortString() }.joinToString("\n")) } } + if(dialog != 0) AlertDialog( + text = { Text(stringResource(if(dialog == 1) R.string.info_device_id_attestation else R.string.info_unique_device_attestation)) }, + confirmButton = { TextButton(onClick = { dialog = 0 }) { Text(stringResource(R.string.confirm)) } }, + onDismissRequest = { dialog = 0 } + ) } @SuppressLint("NewApi") @@ -547,6 +564,7 @@ private fun SupportMsg() { Text(text = stringResource(R.string.reset)) } } + InfoCard(R.string.info_short_support_message) Spacer(Modifier.padding(vertical = 8.dp)) OutlinedTextField( value = longMsg, @@ -577,6 +595,7 @@ private fun SupportMsg() { Text(text = stringResource(R.string.reset)) } } + InfoCard(R.string.info_long_support_message) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -591,8 +610,6 @@ private fun TransferOwnership() { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.transfer_ownership), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) - Text(text = stringResource(R.string.transfer_ownership_desc)) - Spacer(Modifier.padding(vertical = 5.dp)) OutlinedTextField( value = component, onValueChange = { component = it }, label = { Text(stringResource(R.string.target_component_name)) }, modifier = Modifier.fillMaxWidth(), @@ -616,7 +633,8 @@ private fun TransferOwnership() { ) { Text(stringResource(R.string.transfer)) } - Spacer(Modifier.padding(vertical = 30.dp)) + Spacer(Modifier.padding(vertical = 10.dp)) + InfoCard(R.string.info_transfer_ownership) } } 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 49a2b7b..9f221bf 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/SystemManager.kt @@ -38,6 +38,7 @@ import android.app.admin.SystemUpdatePolicy.TYPE_POSTPONE import android.content.ComponentName import android.content.Context import android.content.Intent +import android.icu.text.IDNA import android.net.Uri import android.os.Build.VERSION import android.os.UserManager @@ -116,6 +117,7 @@ import com.bintianqi.owndroid.selectedPackage import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.Information import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.RadioButtonItem @@ -261,30 +263,31 @@ private fun Switches() { val deviceOwner = context.isDeviceOwner val profileOwner = context.isProfileOwner val um = context.getSystemService(Context.USER_SERVICE) as UserManager - Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(start = 20.dp, end = 16.dp)) { - Spacer(Modifier.padding(vertical = 10.dp)) + var dialog by remember { mutableIntStateOf(0) } + Column(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) { + Spacer(Modifier.padding(vertical = 5.dp)) if(deviceOwner || profileOwner) { SwitchItem(R.string.disable_cam,"", R.drawable.photo_camera_fill0, - { dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) }, padding = false + { dpm.getCameraDisabled(null) }, { dpm.setCameraDisabled(receiver,it) } ) } if(deviceOwner || profileOwner) { SwitchItem(R.string.disable_screen_capture, "", R.drawable.screenshot_fill0, - { dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) }, padding = false + { dpm.getScreenCaptureDisabled(null) }, { dpm.setScreenCaptureDisabled(receiver,it) } ) } 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) }, padding = false + { dpm.isStatusBarDisabled}, { dpm.setStatusBarDisabled(receiver,it) } ) } if(deviceOwner || (VERSION.SDK_INT >= 23 && profileOwner && um.isSystemUser) || dpm.isOrgProfile(receiver)) { if(VERSION.SDK_INT >= 30) { SwitchItem(R.string.auto_time, "", R.drawable.schedule_fill0, - { dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) }, padding = false + { dpm.getAutoTimeEnabled(receiver) }, { dpm.setAutoTimeEnabled(receiver,it) } ) SwitchItem(R.string.auto_timezone, "", R.drawable.globe_fill0, - { dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) }, padding = false + { dpm.getAutoTimeZoneEnabled(receiver) }, { dpm.setAutoTimeZoneEnabled(receiver,it) } ) }else{ SwitchItem(R.string.require_auto_time, "", R.drawable.schedule_fill0, { dpm.autoTimeRequired}, { dpm.setAutoTimeRequired(receiver,it) }, padding = false) @@ -292,32 +295,49 @@ private fun Switches() { } if(deviceOwner || (profileOwner && (VERSION.SDK_INT < 24 || (VERSION.SDK_INT >= 24 && !dpm.isManagedProfile(receiver))))) { SwitchItem(R.string.master_mute, "", R.drawable.volume_up_fill0, - { dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) }, padding = false + { dpm.isMasterVolumeMuted(receiver) }, { dpm.setMasterVolumeMuted(receiver,it) } ) } if(VERSION.SDK_INT >= 26 && (deviceOwner || profileOwner)) { SwitchItem(R.string.backup_service, "", R.drawable.backup_fill0, - { dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) }, padding = false + { dpm.isBackupServiceEnabled(receiver) }, { dpm.setBackupServiceEnabled(receiver,it) }, + onClickBlank = { dialog = 1 } ) } if(VERSION.SDK_INT >= 24 && profileOwner && dpm.isManagedProfile(receiver)) { SwitchItem(R.string.disable_bt_contact_share, "", R.drawable.account_circle_fill0, - { dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) }, padding = false + { dpm.getBluetoothContactSharingDisabled(receiver) }, { dpm.setBluetoothContactSharingDisabled(receiver,it) }, ) } if(VERSION.SDK_INT >= 30 && deviceOwner) { SwitchItem(R.string.common_criteria_mode , "",R.drawable.security_fill0, - { dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) }, padding = false + { dpm.isCommonCriteriaModeEnabled(receiver) }, { dpm.setCommonCriteriaModeEnabled(receiver,it) }, + onClickBlank = { dialog = 2 } ) } if(VERSION.SDK_INT >= 31 && (deviceOwner || dpm.isOrgProfile(receiver)) && dpm.canUsbDataSignalingBeDisabled()) { SwitchItem( R.string.disable_usb_signal, "", R.drawable.usb_fill0, { !dpm.isUsbDataSignalingEnabled }, - { dpm.isUsbDataSignalingEnabled = !it }, padding = false + { dpm.isUsbDataSignalingEnabled = !it }, ) } Spacer(Modifier.padding(vertical = 30.dp)) } + if(dialog != 0) AlertDialog( + text = { + Text(stringResource( + when(dialog) { + 1 -> R.string.info_backup_service + 2 -> R.string.info_common_criteria_mode + else -> R.string.options + } + )) + }, + confirmButton = { + TextButton(onClick = { dialog = 0 }) { Text(stringResource(R.string.confirm)) } + }, + onDismissRequest = { dialog = 0 } + ) } @Composable @@ -329,9 +349,9 @@ private fun Keyguard() { val profileOwner = context.isProfileOwner Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) - Text(text = stringResource(R.string.keyguard), style = typography.headlineLarge) - Spacer(Modifier.padding(vertical = 5.dp)) if(VERSION.SDK_INT >= 23) { + Text(text = stringResource(R.string.keyguard), style = typography.headlineLarge) + Spacer(Modifier.padding(vertical = 5.dp)) Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() @@ -355,7 +375,8 @@ private fun Keyguard() { Text(stringResource(R.string.enable)) } } - Spacer(Modifier.padding(vertical = 15.dp)) + InfoCard(R.string.info_disable_keyguard) + Spacer(Modifier.padding(vertical = 12.dp)) } Text(text = stringResource(R.string.lock_now), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 2.dp)) @@ -377,6 +398,9 @@ private fun Keyguard() { ) { Text(stringResource(R.string.lock_now)) } + if(VERSION.SDK_INT >= 26 && profileOwner && dpm.isManagedProfile(receiver)) { + InfoCard(R.string.info_evict_credential_encryption_key) + } Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -420,7 +444,7 @@ private fun RebootDialog(status: MutableState) { AlertDialog( onDismissRequest = { status.value = false }, title = { Text(stringResource(R.string.reboot)) }, - text = { Text(stringResource(R.string.confirm_reboot)) }, + text = { Text(stringResource(R.string.info_reboot)) }, dismissButton = { TextButton(onClick = { status.value = false }) { Text(stringResource(R.string.cancel)) @@ -431,7 +455,7 @@ private fun RebootDialog(status: MutableState) { onClick = { dpm.reboot(receiver) }, colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error) ) { - Text(stringResource(R.string.reboot)) + Text(stringResource(R.string.confirm)) } }, modifier = Modifier.fillMaxWidth() @@ -445,35 +469,28 @@ private fun EditTime() { val dpm = context.getDPM() val receiver = context.getReceiver() val focusMgr = LocalFocusManager.current + var inputTime by remember { mutableStateOf("") } Column(modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp).verticalScroll(rememberScrollState())) { Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.edit_time), style = typography.headlineLarge) Spacer(Modifier.padding(vertical = 5.dp)) - var inputTime by remember { mutableStateOf("") } - Text(text = stringResource(R.string.from_epoch_to_target_time)) - Spacer(Modifier.padding(vertical = 5.dp)) OutlinedTextField( value = inputTime, label = { Text(stringResource(R.string.time_unit_ms)) }, - onValueChange = { inputTime = it}, + onValueChange = { inputTime = it }, + supportingText = { Text(stringResource(R.string.info_edit_time)) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = {focusMgr.clearFocus() }), modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.padding(vertical = 5.dp)) Button( - onClick = { dpm.setTime(receiver,inputTime.toLong()) }, + onClick = { dpm.setTime(receiver, inputTime.toLong()) }, modifier = Modifier.fillMaxWidth(), enabled = inputTime != "" ) { Text(stringResource(R.string.apply)) } - Button( - onClick = { inputTime = System.currentTimeMillis().toString() }, - modifier = Modifier.fillMaxWidth() - ) { - Text(stringResource(R.string.get_current_time)) - } } } @SuppressLint("NewApi") @@ -553,6 +570,7 @@ private fun PermissionPolicy() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_permission_policy) } } @@ -595,8 +613,7 @@ private fun MTEPolicy() { ) { Text(stringResource(R.string.apply)) } - Spacer(Modifier.padding(vertical = 5.dp)) - Information { Text(stringResource(R.string.mte_policy_desc)) } + InfoCard(R.string.info_mte_policy) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -641,6 +658,7 @@ private fun NearbyStreamingPolicy() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_nearby_app_streaming_policy) var notificationPolicy by remember { mutableIntStateOf(dpm.nearbyNotificationStreamingPolicy) } Spacer(Modifier.padding(vertical = 10.dp)) Text(text = stringResource(R.string.nearby_notification_streaming), style = typography.titleLarge) @@ -675,6 +693,7 @@ private fun NearbyStreamingPolicy() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_nearby_notification_streaming_policy) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -840,6 +859,7 @@ private fun LockTaskMode(navCtrl: NavHostController) { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_lock_task_packages) var startLockTaskApp by rememberSaveable { mutableStateOf("") } var startLockTaskActivity by rememberSaveable { mutableStateOf("") } var specifyActivity by rememberSaveable { mutableStateOf(false) } @@ -909,6 +929,7 @@ private fun LockTaskMode(navCtrl: NavHostController) { ) { Text(stringResource(R.string.start)) } + InfoCard(R.string.info_start_lock_task_mode) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -1040,6 +1061,7 @@ private fun SecurityLogs() { Text(stringResource(R.string.delete_logs)) } } + InfoCard(R.string.info_security_log) Spacer(Modifier.padding(vertical = 5.dp)) Button( onClick = { @@ -1074,6 +1096,7 @@ private fun SecurityLogs() { ) { Text(stringResource(R.string.pre_reboot_security_logs)) } + InfoCard(R.string.info_pre_reboot_security_log) } } @@ -1113,7 +1136,8 @@ private fun DisableAccountManagement() { dpm.setAccountManagementDisabled(receiver, inputText, true) inputText = "" refreshList() - } + }, + enabled = inputText != "" ) { Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add)) } @@ -1122,6 +1146,8 @@ private fun DisableAccountManagement() { keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }) ) + Spacer(Modifier.padding(vertical = 10.dp)) + InfoCard(R.string.info_disable_account_management) } } @@ -1216,11 +1242,9 @@ fun FactoryResetProtection() { Text(stringResource(R.string.apply)) } Spacer(Modifier.padding(vertical = 10.dp)) - if(unsupported) { - Information { - Text(stringResource(R.string.frp_policy_not_supported)) - } - } + if(unsupported) Text(stringResource(R.string.frp_policy_not_supported)) + Spacer(Modifier.padding(vertical = 6.dp)) + InfoCard(R.string.info_frp_policy) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -1280,7 +1304,7 @@ private fun WipeData() { Text("WipeData") } } - if (VERSION.SDK_INT >= 34 && (context.isDeviceOwner || dpm.isOrgProfile(receiver))) { + if (VERSION.SDK_INT >= 34 && context.isDeviceOwner) { Button( onClick = { focusMgr.clearFocus() @@ -1302,7 +1326,10 @@ private fun WipeData() { Text(text = stringResource(R.string.warning), color = colorScheme.error) }, text = { - Text(text = stringResource(R.string.wipe_data_warning), color = colorScheme.error) + Text( + text = stringResource(if(userManager.isSystemUser) R.string.wipe_data_warning else R.string.info_wipe_data_in_managed_user), + color = colorScheme.error + ) }, onDismissRequest = { warning = false }, confirmButton = { diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt index 5283e09..38953b2 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/UserManager.kt @@ -72,6 +72,7 @@ import com.bintianqi.owndroid.toggle import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.CardItem import com.bintianqi.owndroid.ui.CheckBoxItem +import com.bintianqi.owndroid.ui.InfoCard import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.SubPageItem import com.bintianqi.owndroid.ui.SwitchItem @@ -295,6 +296,7 @@ private fun UserOperation() { ) { Text(stringResource(R.string.delete)) } + InfoCard(R.string.info_user_operation) Spacer(Modifier.padding(vertical = 30.dp)) } } @@ -412,6 +414,7 @@ private fun AffiliationID() { ) { Text(stringResource(R.string.apply)) } + InfoCard(R.string.info_affiliated_id) Spacer(Modifier.padding(vertical = 30.dp)) } } diff --git a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt index 136e07a..b1cb904 100644 --- a/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt +++ b/app/src/main/java/com/bintianqi/owndroid/ui/Components.kt @@ -3,7 +3,6 @@ package com.bintianqi.owndroid.ui import android.widget.Toast import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -12,6 +11,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Info import androidx.compose.material3.* import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.typography @@ -23,7 +23,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController @@ -161,7 +160,7 @@ fun SwitchItem( modifier = Modifier .fillMaxWidth() .clickable(enabled = onClickBlank != null, onClick = onClickBlank?:{}) - .padding(top = 5.dp, bottom = 5.dp, start = if(padding) 30.dp else 0.dp, end = if(padding) 12.dp else 0.dp) + .padding(top = 5.dp, bottom = 5.dp, start = if(padding) 25.dp else 0.dp, end = if(padding) 15.dp else 0.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, @@ -231,16 +230,27 @@ fun CopyTextButton(@StringRes label: Int, content: String) { } @Composable -fun CardItem(@StringRes title: Int, @StringRes text: Int) { - CardItem(title, stringResource(text)) +fun CardItem(@StringRes title: Int, @StringRes text: Int, onClickInfo: (() -> Unit)? = null) { + CardItem(title, stringResource(text), onClickInfo) } @Composable -fun CardItem(@StringRes title: Int, text: String) { +fun CardItem(@StringRes title: Int, text: String, onClickInfo: (() -> Unit)? = null) { Card(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)) { - Text(text = stringResource(title), style = typography.titleLarge, modifier = Modifier.padding(start = 8.dp, top = 6.dp)) - SelectionContainer { - Text(text = text, modifier = Modifier.padding(start = 8.dp, bottom = 6.dp)) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Column(modifier = Modifier.fillMaxWidth(0.85F)) { + Text(text = stringResource(title), style = typography.titleLarge, modifier = Modifier.padding(start = 8.dp, top = 6.dp)) + SelectionContainer { + Text(text = text, modifier = Modifier.padding(start = 8.dp, bottom = 6.dp)) + } + } + if(onClickInfo != null) IconButton(onClick = onClickInfo) { + Icon(imageVector = Icons.Outlined.Info, contentDescription = null) + } } } } @@ -263,3 +273,17 @@ fun ListItem(text: String, onDelete: () -> Unit) { } } } + +@Composable +fun InfoCard(@StringRes strID: Int) { + Column( + modifier = Modifier + .padding(vertical = 8.dp) + .clip(RoundedCornerShape(10)) + .background(color = colorScheme.tertiaryContainer) + .padding(8.dp) + ) { + Icon(imageVector = Icons.Outlined.Info, contentDescription = null, modifier = Modifier.padding(vertical = 4.dp)) + Text(stringResource(strID)) + } +} diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6b19396..b3ac381 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -87,7 +87,6 @@ Отключить управление аккаунтами Тип аккаунта Передача прав владения - Передать привилегии владельца устройства или профиля другому приложению. Имя целевого компонента Информация на экране блокировки Сообщение поддержки @@ -140,20 +139,16 @@ Отчет об ошибке Запросить отчет об ошибке? Перезагрузить - Подтвердить перезагрузку? Изменить время Изменить часовой пояс Посмотреть все идентификаторы часовых поясов Скрыть все идентификаторы часовых поясов Идентификатор часового пояса Автоматический часовой пояс должен быть отключен перед установкой пользовательского часового пояса. - От Эпохи (01.01.1970 00:00:00 UTC) до желаемого времени (мс) - Текущее время Политика разрешений Автоматически разрешать Автоматически запрещать Политика MTE - MTE: Memory Tagging Extension, требуется Android 14 и ARMv9 Политика потоковой передачи Nearby App Политика потоковой передачи Nearby Политика потоковой передачи уведомлений Nearby @@ -490,14 +485,11 @@ Информация о пароле Оставьте пустым, чтобы удалить пароль Максимальное количество неудачных попыток ввода пароля - При достижении этого предела устройство будет сброшено к заводским настройкам Максимальное количество неудачных попыток Время истечения срока действия пароля Время ожидания экрана Время ожидания строгой аутентификации - 0 означает отсутствие ограничений Длина истории паролей - Пользователь не может установить пароли из указанного диапазона истории Нет (разрешено отсутствие пароля) Низкая (графический ключ и повторение символов разрешены) Средняя (повторение запрещено, минимум 4 символа) @@ -609,4 +601,6 @@ Версия Код версии Анализ информации APK... + + \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 8aec01c..462c1d3 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -88,7 +88,6 @@ Hesap Yönetimini Devre Dışı Bırak Hesap Türleri Sahipliği Devret - Cihaz sahibi veya profil sahibi ayrıcalığını başka bir uygulamaya devredin. Target component name Ekran Kilidi Bilgisi Destek Mesajı @@ -141,20 +140,16 @@ Hata raporu Hata raporu iste? Yeniden başlat - Yeniden başlatmayı onayla? Zamanı düzenle Saat dilimini düzenle Tüm saat dilimi kimliklerini görüntüle Tüm saat dilimi kimliklerini gizle Saat dilimi kimliği Özel bir saat dilimi ayarlamadan önce otomatik saat dilimi devre dışı bırakılmalıdır - Epoch\'tan (1970/1/1 00:00:00 UTC) ayarlanmak istenen zamana kadar (ms) - Geçerli saat İzin politikası Otomatik ver Otomatik reddet MTE politikası - MTE: Bellek Etiketleme Uzantısı, Android 14 ve ARMv9 gerektirir Yakındaki uygulama akış politikası Yakındaki akış politikası Yakındaki bildirim akış politikası @@ -486,14 +481,11 @@ Şifre Bilgisi Şifreyi kaldırmak için boş bırakın Maksimum hatalı şifre - Bu sınıra ulaşıldığında, cihaz fabrika ayarlarına sıfırlanacaktır Maksimum hatalı deneme Şifre geçerlilik süresi Ekran zaman aşımı Gereken güçlü doğrulama zaman aşımı - 0 means no restriction Şifre geçmişi uzunluğu - Belirtilen aralıktaki geçmiş şifreler kullanıcı tarafından ayarlanamaz Yok (Şifreye izin verilmez) Düşük (Hareket şifresi ve karakter tekrarı izinli) Orta (Tekrar yasak, en az 4 karakter) @@ -604,4 +596,6 @@ Version name Version code Parsing APK info... + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4368a4c..de3a63a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -83,7 +83,6 @@ 禁用账号管理 账号类型 转移所有权 - 把Device owner或Profile owner权限转移到另一个应用 目标组件名 锁屏提示信息 提供支持的消息 @@ -136,20 +135,16 @@ 错误报告 请求错误报告? 重启 - 确定重启? 修改时间 修改时区 显示所有时区ID 隐藏所有时区ID 时区ID 在设置时区前需要关闭自动时区 - 从Epoch(1970/1/1 00:00:00 UTC)到你想设置的时间(毫秒) - 获取当前时间 权限策略 自动允许 自动拒绝 MTE策略 - MTE:内存标记拓展,安卓14和ARMv9的高端功能 附近应用传输 附近流式传输策略 附近通知传输 @@ -478,14 +473,11 @@ 密码信息 留空以清除密码 最大密码错误次数 - 达到该限制会恢复出厂设置 错误次数 密码失效超时 屏幕超时 要求强验证超时 - 0表示不做限制 密码历史长度 - 用户无法设置指定历史范围内之前曾设置过的密码 无(允许不设密码) 低(允许图案和连续性) 中(无连续性,至少4位) @@ -596,4 +588,52 @@ 版本名 版本号 解析APK信息中... + + Dhizuku可以分享Device owner权限给其余应用 + 指示设备是否除了密钥证明之外还支持设备标识符证明 + 如果设备上的StrongBox Keymaster可以配置单独的证明证书并且可以使用该证书签署证明记录,则返回true(只有StrongBox安全级别的Keymaster才能使用单独的证明证书进行证明) + 设置组织ID后才能获取设备注册专用ID + 不同组织ID的设备注册专用ID不同,恢复出厂设置或删除工作资料后不变 + 在锁屏界面上显示的一段简短的消息。将会覆盖用户当前设置的锁屏信息,并且防止用户在系统设置中设置新的锁屏信息 + 用户试图使用被管理员禁用的功能时会显示此消息。不应多于200字 + 在设备管理员设置中会显示此消息,不应多于20000字 + 转移Device owner或Profile owner权限到另一个app,当前的设备策略会迁移到新的设备管理员 + 禁用后用户不能使用备份服务备份或恢复数据。在受管理用户中,需要Device owner和Profile owner同时启用此功能才能使用备份服务 + 当设备处于通用标准模式时,某些设备功能会进行调整以满足通用标准模式所要求的安全级别。 + 禁用锁屏相当于把锁屏方式设置为“无”。在已设置密码或图案时使用这个功能无效。如果在锁屏禁用时设置密码或图案,锁屏将启用 + 从密钥环中移除用户的凭证加密密钥。用户需要再次输入凭证才能将密钥存储回密钥环中。为了保护用户数据,用户将重新启动 + 打电话时不能使用此功能 + 输入以毫秒为单位的UNIX时间 + 设置应用申请运行时权限时的默认选择 + 设置内存标记扩展(Memory Tagging Extension)策略。重启设备以应用更改。 + 应用流式传输:当app在虚拟显示器上启动,传输这个app的视频流到附近的设备。 + 通知流式传输:预安装的应用把通知数据传输到附近的设备。 + 只有在列表中的应用才能开启锁定任务模式。 + 如果你在锁定任务功能中允许了通知,成功启动锁定任务模式后,OwnDroid会发送一条通知,你可以点击那条通知以退出锁定任务模式。 + 设备上不能有非附属用户 + 并非所有设备都支持重启前安全日志 + 当某个帐户类型的帐户管理被禁用时,将无法添加或删除该类型的帐户。 + 恢复出厂设置保护(Factory reset protection)策略用于防止不受信任的重置(Fastboot或Recovery)。需要设备支持持久数据块服务(Persistent data block service) + 此用户的所有数据将会被清除,但是用户不会被删除。 + 控制用户是否可以更改管理员配置的网络。启用此锁定后,用户仍然可以配置和连接到其他Wi-Fi,或使用其他Wi-Fi功能(如网络共享)。 + 指定Wi-Fi网络所需的最低安全等级。设备将无法连接到低于最低安全等级的网络。如果当前网络不满足要求,则会断开连接。 + 在此模式下,DNS子系统将在尝试以明文形式进行域名解析之前,尝试与网络提供的DNS服务器进行TLS握手。 + 将对DNS服务器执行连接检查,以确保其有效。\n如果将VPN与私人DNS结合使用,则私人DNS必须可从VPN内部和外部访问。否则设备可能会失去解析域名的能力,因为到DNS服务器的系统流量可能不会通过VPN。 + 通过一个指定的app,为当前用户设置一个保持打开的VPN连接。自动授权连接并在重启后保留。\n启用锁定:如果VPN未连接,则禁止使用网络。 + 这个代理只是一个建议,一些app有可能忽略它。 + 网络日志包含DNS查询和connect()库调用记录 + 用户可以关闭工作资料,如果关闭工作资料的时间超过了在这里设置的时间,会挂起个人应用。设置的时间不能小于72小时,如果小于72小时,按72小时算。 + 添加跨资料Intent过滤器,使工作资料中可以发送的指定Intent到主用户,反之亦然。仅支持Activity Intent + 挂起的应用无法被打开,通知会被隐藏,不会在最近任务中显示,不能弹窗,不能发送Toast。\n有些应用无法被挂起,比如Device admin、启动器和默认拨号应用。 + 用户无法清除这些应用的存储空间,也无法强制停止应用 + 这个列表中的应用的APK将会一直保留,即使没有任何用户安装这个应用 + 推荐使用用户序列号来标识用户,如果要使用UID,UID可以是运行在目标用户中任意应用的UID + 当Device owner创建并管理用户时,新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后,受管理用户成为附属于Device owner的用户 + 设置一个新的密码,密码的长度需要4位或以上,不输入密码将会清除现有的密码。长度在6位或以下的纯数字密码将会设置为PIN码。 + 设置设备锁定前用户活动的最大时间。这限制了用户可以设置的时间长度。\n值为0表示不做限制。 + 重新启动密码过期倒计时。值为0表示不做限制。 + 如果设置为大于零的值,将会在输入一定次数的错误的锁屏密码后将设备恢复出厂设置 + 如果设置为大于零的值,将会在输入一定次数的错误的锁屏密码后清除当前用户的所有数据 + 设置后,用户将无法输入与历史记录中任何密码相同的新密码。当前密码将保留,直到用户设置新密码为止,因此更改不会立即生效。值为0表示不做限制。 + 如果用户在这段时间内没有使用强认证(密码、PIN或图案)解锁设备,则要求使用强认证解锁设备。值为0表示OwnDroid不参与控制超时。一般来说,最少1小时,最多72小时。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc28a69..6c45c46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -89,7 +89,6 @@ Disable account management Account type Transfer Ownership - Transfer device owner or profile owner privilege to another app. Target component name Lockscreen info Support Message @@ -145,20 +144,16 @@ Bug report Request bug report? Reboot - Confirm reboot? Edit time Edit timezone View all timezones IDs Hide all timezones IDs Timezone ID Auto timezone should be disabled before set a custom timezone. - From Epoch(1970/1/1 00:00:00 UTC) to the time you want to set(ms) - Current time Permission policy Auto grant Auto deny MTE policy - MTE: Memory Tagging Extension, require Android 14 and ARMv9 Nearby App streaming policy Nearby streaming policy Nearby notification streaming policy @@ -491,14 +486,11 @@ Password Info Keep empty to remove password Max failed passwords - When reach this limit, the device will be factory reset Maximum failed attempts Password expiration timeout Screen timeout Required strong auth timeout - 0 means no restriction Password history length - Historical passwords within the specified range cannot be set by user None (No password allowed) Low (Gesture password and characters repetition allowed) Medium (Repetition disallowed, 4 characters at least) @@ -609,4 +601,52 @@ Version name Version code Parsing APK info... + + Dhizuku is a tool that can share Device owner permissions to other application. + Indicates if the device supports attestation of device identifiers in addition to key attestation. + Yes if the StrongBox Keymaster implementation on the device was provisioned with an individual attestation certificate and can sign attestation records using it (only Keymaster with StrongBox security level can use an individual attestation certificate). + Sets the Enterprise ID. This is a requirement for generating an enrollment-specific ID for the device. + The identifier would be consistent even if the work profile is removed and create again (to the same Organization ID), or the device is factory reset and re-enrolled. + Show a brief message on your lock screen.\nOverrides any owner information manually set by the user and prevents the user from further changing it. + This will be displayed to the user in settings screens where functionality has been disabled by the admin. If the message is longer than 200 characters it may be truncated + This will be displayed to the user in the device administrators settings screen. If the message is longer than 20000 characters it may be truncated. + Changes the current administrator to another one. All policies from the current administrator are migrated to the new administrator. + Each user has its own backup service which manages the backup and restore mechanisms in that user. Disabling the backup service will prevent data from being backed up or restored.\nFor a managed user its backup functionality is only enabled if both the device owner and the profile owner have enabled the backup service. + When the device is in Common Criteria mode, certain device functionalities are tuned to meet the higher security level required by Common Criteria certification. + Setting the keyguard to disabled has the same effect as choosing "None" as the screen lock type. However, this call has no effect if a password, pin or pattern is currently set.\nIf a password, pin or pattern is set after the keyguard was disabled, the keyguard stops being disabled. + Evict the user\'s credential encryption key from the keyring. The user\'s credential will need to be entered again in order to derive the credential encryption key that will be stored back in the keyring for future use. In order to secure user data, the user will be stopped and restarted. + You can\'t use this function if there is an ongoing call on the device. + Input UNIX time in milliseconds + Set the default response for future runtime permission requests by applications. + Set the Memory Tagging Extension policy. Reboot the device to apply changes. + App streaming is when the device starts an app on a virtual display and sends a video stream of the app to nearby devices. + Notification streaming is sending notification data from pre-installed apps to nearby devices. + Only packages in lock task packages list can be launched in lock task mode. + If you allow notifications in Lock task features, OwnDroid will pop a notification when lock task mode start successfully. You can exit lock task mode by click that notification. + If a Device owner use this function, all users should be affiliated. + Not all devices support pre-reboot security logs. + When account management is disabled for an account type, adding or removing an account of that type will not be possible. + FRP can protect the device after untrusted factory reset (in Recovery or Fastboot).\nTo enable this feature, the device must support persistent data block service. + All data of this user will be wiped, but that user won\'t be removed. + Control whether the user can change networks configured by the admin.\nWhen this lockdown is enabled, the user can still configure and connect to other Wi-Fi networks, or use other Wi-Fi capabilities such as tethering. + Specify the minimum security level required for Wi-Fi networks. The device may not connect to networks that do not meet the minimum security level. If the current network does not meet the minimum security level set, it will be disconnected. + In this mode, the DNS subsystem will attempt a TLS handshake to the network-supplied resolver prior to attempting name resolution in cleartext. + It will perform a connectivity check to the resolver, to ensure it is valid.\nIn case a VPN is used in conjunction with Private DNS resolver, the Private DNS resolver must be reachable both from within and outside the VPN. Otherwise, the device may lose the ability to resolve hostnames as system traffic to the resolver may not go through the VPN. + Configure an always-on VPN connection through a specific application for the current user. This connection is automatically granted and persisted after a reboot.\nEnable lockdown: Disallow networking when the VPN is not connected. + This proxy is only a recommendation and it is possible that some apps will ignore it. + Network logs contain DNS lookup and connect() library call events.\nUse this function in work profile will only retrieve network logs in work profile.\nThere shouldn\'t be unaffiliated user on this device if used by Device owner. + Set maximum time the profile is allowed to be turned off. If the profile is turned off for longer, personal apps are suspended on the device.\nThe minimum non-zero value corresponds to 72 hours. If an admin sets a smaller non-zero vaulue, 72 hours will be set instead. + Add cross profile intent filters so that some intents sent in the managed profile can also be resolved in the parent, or vice versa. Only activity intents are supported. + A suspended package will not be able to start activities. Its notifications will be hidden, it will not show up in recent activities, will not be able to show toasts or dialogs or ring the device.\nSome apps cannot be suspended, such as device admins, the active launcher and the default dialer. + User will not be able to clear app data or force-stop packages. + Set a list of apps to keep around as APKs even if no user has currently installed it. + It is recommended to specify a user with serial number, you can also use UID, the UID should be any of the apps in the target user. + When Device owner create a managed user, the managed user isn\'t affiliated. In order to make the managed user affiliated with the Device owner, you should set same affiliated IDs in main user and managed user + Set a new lockscreen password. The length of this password must be at least 4 digits. Keep it empty to remove password.\nIf you set a numeric password that length is 6 or lower, it will set as PIN + Set the maximum time for user activity until the device will lock. This limits the length that the user can set.\nA value of 0 means there is no restriction. + Restart the countdown for password expiration.\nA value of 0 means there is no restriction. + Setting this to a value greater than zero enables a policy that the device will be factory reset after too many incorrect device-unlock passwords have been entered. + Setting this to a value greater than zero enables a policy that will wipe this user after too many incorrect unlock password have been entered. + After setting this, the user will not be able to enter a new password that is the same as any password in the history. Note that the current password will remain until the user has set a new one, so the change does not take place immediately.\nA value of 0 means there is no restriction. + Determine for how long the user will be able to use secondary, non strong auth for authentication, since last strong method authentication (password, pin or pattern) was used. After the returned timeout the user is required to use strong authentication method.\nA value of 0 means the admin is not participating in controlling the timeout. The minimum and maximum timeouts are platform-defined and are typically 1 hour and 72 hours, respectively.