mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
Add delegated admin in a new screen
Some UI improvements Info of password complexity
This commit is contained in:
@@ -62,6 +62,8 @@ import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.toRoute
|
||||
import com.bintianqi.owndroid.dpm.Accounts
|
||||
import com.bintianqi.owndroid.dpm.AccountsScreen
|
||||
import com.bintianqi.owndroid.dpm.AddDelegatedAdmin
|
||||
import com.bintianqi.owndroid.dpm.AddDelegatedAdminScreen
|
||||
import com.bintianqi.owndroid.dpm.AddNetwork
|
||||
import com.bintianqi.owndroid.dpm.AddNetworkScreen
|
||||
import com.bintianqi.owndroid.dpm.AffiliationId
|
||||
@@ -167,7 +169,7 @@ import com.bintianqi.owndroid.dpm.SystemManager
|
||||
import com.bintianqi.owndroid.dpm.SystemManagerScreen
|
||||
import com.bintianqi.owndroid.dpm.SystemOptions
|
||||
import com.bintianqi.owndroid.dpm.SystemOptionsScreen
|
||||
import com.bintianqi.owndroid.dpm.SystemUpdatePolicy
|
||||
import com.bintianqi.owndroid.dpm.SystemUpdatePolicyScreen
|
||||
import com.bintianqi.owndroid.dpm.TransferOwnership
|
||||
import com.bintianqi.owndroid.dpm.TransferOwnershipScreen
|
||||
import com.bintianqi.owndroid.dpm.UserInfo
|
||||
@@ -264,6 +266,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
}
|
||||
val userRestrictions by vm.userRestrictions.collectAsStateWithLifecycle()
|
||||
fun navigateUp() { navController.navigateUp() }
|
||||
fun navigate(destination: Any) { navController.navigate(destination) }
|
||||
@Suppress("NewApi") NavHost(
|
||||
navController = navController,
|
||||
startDestination = Home,
|
||||
@@ -290,13 +293,14 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<DeviceAdmin> { DeviceAdminScreen(::navigateUp) }
|
||||
composable<ProfileOwner> { ProfileOwnerScreen(::navigateUp) }
|
||||
composable<DeviceOwner> { DeviceOwnerScreen(::navigateUp) }
|
||||
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp) }
|
||||
composable<DelegatedAdmins> { DelegatedAdminsScreen(::navigateUp, ::navigate) }
|
||||
composable<AddDelegatedAdmin>{ AddDelegatedAdminScreen(it.toRoute(), ::navigateUp) }
|
||||
composable<DeviceInfo> { DeviceInfoScreen(::navigateUp) }
|
||||
composable<LockScreenInfo> { LockScreenInfoScreen(::navigateUp) }
|
||||
composable<SupportMessage> { SupportMessageScreen(::navigateUp) }
|
||||
composable<TransferOwnership> { TransferOwnershipScreen(::navigateUp) }
|
||||
|
||||
composable<SystemManager> { SystemManagerScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<SystemManager> { SystemManagerScreen(::navigateUp, ::navigate) }
|
||||
composable<SystemOptions> { SystemOptionsScreen(::navigateUp) }
|
||||
composable<Keyguard> { KeyguardScreen(::navigateUp) }
|
||||
composable<HardwareMonitor> { HardwareMonitorScreen(::navigateUp) }
|
||||
@@ -311,12 +315,12 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<CaCert> { CaCertScreen(::navigateUp) }
|
||||
composable<SecurityLogging> { SecurityLoggingScreen(::navigateUp) }
|
||||
composable<DisableAccountManagement> { DisableAccountManagementScreen(::navigateUp) }
|
||||
composable<SetSystemUpdatePolicy> { SystemUpdatePolicy(::navigateUp) }
|
||||
composable<SetSystemUpdatePolicy> { SystemUpdatePolicyScreen(::navigateUp) }
|
||||
composable<InstallSystemUpdate> { InstallSystemUpdateScreen(::navigateUp) }
|
||||
composable<FrpPolicy> { FrpPolicyScreen(::navigateUp) }
|
||||
composable<WipeData> { WipeDataScreen(::navigateUp) }
|
||||
|
||||
composable<Network> { NetworkScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<Network> { NetworkScreen(::navigateUp, ::navigate) }
|
||||
composable<WiFi> {
|
||||
WifiScreen(::navigateUp, { navController.navigate(it) }) {
|
||||
val dest = navController.graph.findNode(AddNetwork)!!.id
|
||||
@@ -327,7 +331,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<AddNetwork> { AddNetworkScreen(it.arguments!!, ::navigateUp) }
|
||||
composable<WifiSecurityLevel> { WifiSecurityLevelScreen(::navigateUp) }
|
||||
composable<WifiSsidPolicyScreen> { WifiSsidPolicyScreen(::navigateUp) }
|
||||
composable<QueryNetworkStats> { NetworkStatsScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<QueryNetworkStats> { NetworkStatsScreen(::navigateUp, ::navigate) }
|
||||
composable<NetworkStatsViewer>(mapOf(serializableNavTypePair<List<NetworkStatsViewer.Data>>())) {
|
||||
NetworkStatsViewerScreen(it.toRoute()) { navController.navigateUp() }
|
||||
}
|
||||
@@ -339,7 +343,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<PreferentialNetworkService> { PreferentialNetworkServiceScreen(::navigateUp) }
|
||||
composable<OverrideApn> { OverrideApnScreen(::navigateUp) }
|
||||
|
||||
composable<WorkProfile> { WorkProfileScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<WorkProfile> { WorkProfileScreen(::navigateUp, ::navigate) }
|
||||
composable<OrganizationOwnedProfile> { OrganizationOwnedProfileScreen(::navigateUp) }
|
||||
composable<CreateWorkProfile> { CreateWorkProfileScreen(::navigateUp) }
|
||||
composable<SuspendPersonalApp> { SuspendPersonalAppScreen(::navigateUp) }
|
||||
@@ -370,7 +374,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
}
|
||||
}
|
||||
|
||||
composable<Users> { UsersScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<Users> { UsersScreen(::navigateUp, ::navigate) }
|
||||
composable<UserInfo> { UserInfoScreen(::navigateUp) }
|
||||
composable<UsersOptions> { UsersOptionsScreen(::navigateUp) }
|
||||
composable<UserOperation> { UserOperationScreen(::navigateUp) }
|
||||
@@ -379,7 +383,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<UserSessionMessage> { UserSessionMessageScreen(::navigateUp) }
|
||||
composable<AffiliationId> { AffiliationIdScreen(::navigateUp) }
|
||||
|
||||
composable<Password> { PasswordScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<Password> { PasswordScreen(::navigateUp, ::navigate) }
|
||||
composable<PasswordInfo> { PasswordInfoScreen(::navigateUp) }
|
||||
composable<ResetPasswordToken> { ResetPasswordTokenScreen(::navigateUp) }
|
||||
composable<ResetPassword> { ResetPasswordScreen(::navigateUp) }
|
||||
@@ -387,7 +391,7 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
|
||||
composable<KeyguardDisabledFeatures> { KeyguardDisabledFeaturesScreen(::navigateUp) }
|
||||
composable<RequiredPasswordQuality> { RequiredPasswordQualityScreen(::navigateUp) }
|
||||
|
||||
composable<Settings> { SettingsScreen(::navigateUp) { navController.navigate(it) } }
|
||||
composable<Settings> { SettingsScreen(::navigateUp, ::navigate) }
|
||||
composable<SettingsOptions> { SettingsOptionsScreen(::navigateUp) }
|
||||
composable<Appearance> {
|
||||
val theme by vm.theme.collectAsStateWithLifecycle()
|
||||
|
||||
@@ -121,6 +121,8 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.painterResource
|
||||
@@ -483,6 +485,7 @@ fun AddNetworkScreen(data: Bundle, onNavigateUp: () -> Unit) {
|
||||
@Composable
|
||||
private fun AddNetworkScreen(wifiConfig: WifiConfiguration? = null, onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val fm = LocalFocusManager.current
|
||||
var resultDialog by remember { mutableStateOf(false) }
|
||||
var createdNetworkId by remember { mutableIntStateOf(-1) }
|
||||
var createNetworkResult by remember { mutableIntStateOf(0) }
|
||||
@@ -614,21 +617,29 @@ private fun AddNetworkScreen(wifiConfig: WifiConfiguration? = null, onNavigateUp
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(visible = useStaticIp, modifier = Modifier.padding(bottom = 8.dp)) {
|
||||
val gatewayFr = FocusRequester()
|
||||
val dnsFr = FocusRequester()
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
value = ipAddress, onValueChange = { ipAddress = it },
|
||||
placeholder = { Text("192.168.1.2/24") }, label = { Text(stringResource(R.string.ip_address)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
keyboardActions = KeyboardActions { gatewayFr.requestFocus() },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = gatewayAddress, onValueChange = { gatewayAddress = it },
|
||||
placeholder = { Text("192.168.1.1") }, label = { Text(stringResource(R.string.gateway_address)) },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
keyboardActions = KeyboardActions { dnsFr.requestFocus() },
|
||||
modifier = Modifier.focusRequester(gatewayFr).fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = dnsServers, onValueChange = { dnsServers = it },
|
||||
label = { Text(stringResource(R.string.dns_servers)) }, minLines = 2,
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() },
|
||||
modifier = Modifier.focusRequester(dnsFr).fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -648,19 +659,27 @@ private fun AddNetworkScreen(wifiConfig: WifiConfiguration? = null, onNavigateUp
|
||||
}
|
||||
}
|
||||
AnimatedVisibility(visible = useHttpProxy, modifier = Modifier.padding(bottom = 8.dp)) {
|
||||
val portFr = FocusRequester()
|
||||
val exclListFr = FocusRequester()
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
value = httpProxyHost, onValueChange = { httpProxyHost = it }, label = { Text(stringResource(R.string.host)) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
keyboardActions = KeyboardActions { portFr.requestFocus() },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = httpProxyPort, onValueChange = { httpProxyPort = it }, label = { Text(stringResource(R.string.port)) },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next, keyboardType = KeyboardType.Number),
|
||||
keyboardActions = KeyboardActions { exclListFr.requestFocus() },
|
||||
modifier = Modifier.focusRequester(portFr).fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = httpProxyExclList, onValueChange = { httpProxyExclList = it }, label = { Text(stringResource(R.string.excluded_hosts)) },
|
||||
minLines = 2, placeholder = { Text("example.com\n*.example.com") },
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() },
|
||||
modifier = Modifier.focusRequester(exclListFr).fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -884,6 +903,7 @@ fun NetworkStats.toBucketList(): List<NetworkStats.Bucket> {
|
||||
fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkStatsViewer) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val deviceOwner = context.isDeviceOwner
|
||||
val fm = LocalFocusManager.current
|
||||
val nsm = context.getSystemService(NetworkStatsManager::class.java)
|
||||
val coroutine = rememberCoroutineScope()
|
||||
var activeTextField by remember { mutableStateOf(NetworkStatsActiveTextField.None) } //0:None, 1:Network type, 2:Start time, 3:End time
|
||||
@@ -999,6 +1019,8 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
|
||||
label = { Text(stringResource(R.string.subscriber_id)) },
|
||||
isError = !readOnly && subscriberId.isNullOrBlank(),
|
||||
trailingIcon = { ExpandExposedTextFieldIcon(activeTextField == NetworkStatsActiveTextField.SubscriberId) },
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() },
|
||||
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable).fillMaxWidth().padding(bottom = 4.dp)
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.bintianqi.owndroid.dpm
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.KeyguardManager
|
||||
import android.app.admin.DevicePolicyManager.ACTION_SET_NEW_PASSWORD
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE
|
||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
|
||||
@@ -29,7 +28,6 @@ import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
|
||||
import android.app.admin.DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT
|
||||
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
|
||||
@@ -67,12 +65,13 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.navigation.NavHostController
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.SharedPrefs
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CardItem
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FullWidthCheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoCard
|
||||
import com.bintianqi.owndroid.ui.MyScaffold
|
||||
@@ -219,15 +218,17 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
|
||||
val receiver = context.getReceiver()
|
||||
val deviceOwner = context.isDeviceOwner
|
||||
val profileOwner = context.isProfileOwner
|
||||
var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity
|
||||
MyScaffold(R.string.password_info, 8.dp, onNavigateUp) {
|
||||
if(VERSION.SDK_INT >= 29) {
|
||||
val passwordComplexity = mapOf(
|
||||
PASSWORD_COMPLEXITY_NONE to R.string.password_complexity_none,
|
||||
PASSWORD_COMPLEXITY_LOW to R.string.password_complexity_low,
|
||||
PASSWORD_COMPLEXITY_MEDIUM to R.string.password_complexity_medium,
|
||||
PASSWORD_COMPLEXITY_HIGH to R.string.password_complexity_high
|
||||
)
|
||||
CardItem(R.string.current_password_complexity, passwordComplexity[dpm.passwordComplexity] ?: R.string.unknown)
|
||||
val text = when(dpm.passwordComplexity) {
|
||||
PASSWORD_COMPLEXITY_NONE -> R.string.none
|
||||
PASSWORD_COMPLEXITY_LOW -> R.string.low
|
||||
PASSWORD_COMPLEXITY_MEDIUM -> R.string.medium
|
||||
PASSWORD_COMPLEXITY_HIGH -> R.string.high
|
||||
else -> R.string.unknown
|
||||
}
|
||||
CardItem(R.string.current_password_complexity, text) { dialog = 1 }
|
||||
}
|
||||
if(deviceOwner || profileOwner) {
|
||||
CardItem(R.string.password_sufficient, dpm.isActivePasswordSufficient.yesOrNo)
|
||||
@@ -236,6 +237,15 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
|
||||
CardItem(R.string.unified_password, dpm.isUsingUnifiedPassword(receiver).yesOrNo)
|
||||
}
|
||||
}
|
||||
if(dialog != 0) AlertDialog(
|
||||
text = { Text(stringResource(R.string.info_password_complexity)) },
|
||||
confirmButton = {
|
||||
TextButton({ dialog = 0 }) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
onDismissRequest = { dialog = 0 }
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable object ResetPasswordToken
|
||||
@@ -430,34 +440,29 @@ fun RequiredPasswordComplexityScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val passwordComplexity = mapOf(
|
||||
PASSWORD_COMPLEXITY_NONE to R.string.password_complexity_none,
|
||||
PASSWORD_COMPLEXITY_LOW to R.string.password_complexity_low,
|
||||
PASSWORD_COMPLEXITY_MEDIUM to R.string.password_complexity_medium,
|
||||
PASSWORD_COMPLEXITY_HIGH to R.string.password_complexity_high
|
||||
PASSWORD_COMPLEXITY_NONE to R.string.none,
|
||||
PASSWORD_COMPLEXITY_LOW to R.string.low,
|
||||
PASSWORD_COMPLEXITY_MEDIUM to R.string.medium,
|
||||
PASSWORD_COMPLEXITY_HIGH to R.string.high
|
||||
)
|
||||
var selectedItem by remember { mutableIntStateOf(PASSWORD_COMPLEXITY_NONE) }
|
||||
LaunchedEffect(Unit) { selectedItem = dpm.requiredPasswordComplexity }
|
||||
MyScaffold(R.string.required_password_complexity, 8.dp, onNavigateUp) {
|
||||
MyScaffold(R.string.required_password_complexity, 0.dp, onNavigateUp) {
|
||||
passwordComplexity.forEach {
|
||||
RadioButtonItem(it.value, selectedItem == it.key) { selectedItem = it.key }
|
||||
FullWidthRadioButtonItem(it.value, selectedItem == it.key) { selectedItem = it.key }
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.requiredPasswordComplexity = selectedItem
|
||||
selectedItem = dpm.requiredPasswordComplexity
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = { context.startActivity(Intent(ACTION_SET_NEW_PASSWORD)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.require_set_new_password))
|
||||
}
|
||||
InfoCard(R.string.info_password_complexity, 8.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,20 +499,19 @@ fun KeyguardDisabledFeaturesScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
LaunchedEffect(mode) { if(mode != 2) flag = dpm.getKeyguardDisabledFeatures(receiver) }
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.disable_keyguard_features, 8.dp, onNavigateUp) {
|
||||
RadioButtonItem(R.string.enable_all, mode == 0) { mode = 0 }
|
||||
RadioButtonItem(R.string.disable_all, mode == 1) { mode = 1 }
|
||||
RadioButtonItem(R.string.custom, mode == 2) { mode = 2 }
|
||||
MyScaffold(R.string.disable_keyguard_features, 0.dp, onNavigateUp) {
|
||||
FullWidthRadioButtonItem(R.string.enable_all, mode == 0) { mode = 0 }
|
||||
FullWidthRadioButtonItem(R.string.disable_all, mode == 1) { mode = 1 }
|
||||
FullWidthRadioButtonItem(R.string.custom, mode == 2) { mode = 2 }
|
||||
AnimatedVisibility(mode == 2) {
|
||||
Column {
|
||||
flagsLiat.forEach {
|
||||
CheckBoxItem(it.first, flag and it.second == it.second) { checked ->
|
||||
FullWidthCheckBoxItem(it.first, flag and it.second == it.second) { checked ->
|
||||
flag = if(checked) flag or it.second else flag and (flag xor it.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
val disabledFeatures = if(mode == 0) KEYGUARD_DISABLE_FEATURES_NONE else if(mode == 1) KEYGUARD_DISABLE_FEATURES_ALL else flag
|
||||
@@ -515,7 +519,7 @@ fun KeyguardDisabledFeaturesScreen(onNavigateUp: () -> Unit) {
|
||||
refresh()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
|
||||
) {
|
||||
Text(text = stringResource(R.string.apply))
|
||||
}
|
||||
|
||||
@@ -20,18 +20,17 @@ import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.MaterialTheme.colorScheme
|
||||
import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -497,7 +496,7 @@ fun DeviceOwnerScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
|
||||
@Suppress("InlinedApi")
|
||||
private enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) {
|
||||
enum class DelegatedScope(val id: String, @StringRes val string: Int, val requiresApi: Int = 0) {
|
||||
AppRestrictions(DevicePolicyManager.DELEGATION_APP_RESTRICTIONS, R.string.manage_application_restrictions),
|
||||
BlockUninstall(DevicePolicyManager.DELEGATION_BLOCK_UNINSTALL, R.string.block_uninstall),
|
||||
CertInstall(DevicePolicyManager.DELEGATION_CERT_INSTALL, R.string.manage_certificates),
|
||||
@@ -515,13 +514,10 @@ private enum class DelegatedScope(val id: String, @StringRes val string: Int, va
|
||||
|
||||
@RequiresApi(26)
|
||||
@Composable
|
||||
fun DelegatedAdminsScreen(onNavigateUp: () -> Unit) {
|
||||
fun DelegatedAdminsScreen(onNavigateUp: () -> Unit, onNavigate: (AddDelegatedAdmin) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
var dialog by rememberSaveable { mutableIntStateOf(0) } // 0:None, 1:Edit, 2:Add
|
||||
var inputPackageName by rememberSaveable { mutableStateOf("") }
|
||||
var selectedScopes by rememberSaveable { mutableStateOf(listOf<String>()) }
|
||||
val packages = remember { mutableStateMapOf<String, MutableList<DelegatedScope>>() }
|
||||
fun refresh() {
|
||||
val list = mutableMapOf<String, MutableList<DelegatedScope>>()
|
||||
@@ -542,81 +538,91 @@ fun DelegatedAdminsScreen(onNavigateUp: () -> Unit) {
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.delegated_admins, 0.dp, onNavigateUp) {
|
||||
packages.forEach { (pkg, scopes) ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { inputPackageName = pkg; selectedScopes = scopes.map { it.id }; dialog = 1 }
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(vertical = 8.dp).padding(start = 14.dp, end = 8.dp),
|
||||
Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(pkg, style = typography.titleLarge)
|
||||
Text(scopes.size.toString() + " " + stringResource(R.string.delegated_scope))
|
||||
Column {
|
||||
Text(pkg, style = typography.titleMedium)
|
||||
Text(
|
||||
scopes.size.toString() + " " + stringResource(R.string.delegated_scope),
|
||||
color = colorScheme.onSurfaceVariant, style = typography.bodyMedium
|
||||
)
|
||||
}
|
||||
IconButton({ onNavigate(AddDelegatedAdmin(pkg, scopes)) }) {
|
||||
Icon(Icons.Outlined.Edit, stringResource(R.string.edit))
|
||||
}
|
||||
}
|
||||
}
|
||||
if(packages.isEmpty())
|
||||
Text(
|
||||
stringResource(R.string.none),
|
||||
color = colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = 4.dp)
|
||||
)
|
||||
if(packages.isEmpty()) Text(
|
||||
stringResource(R.string.none),
|
||||
color = colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(vertical = 4.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { inputPackageName = ""; selectedScopes = emptyList(); dialog = 2 }
|
||||
.padding(vertical = 10.dp, horizontal = 12.dp),
|
||||
.clickable { onNavigate(AddDelegatedAdmin()) }
|
||||
.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Default.Add, null, modifier = Modifier.padding(end = 12.dp))
|
||||
Text(stringResource(R.string.add_delegated_admin), style = typography.titleLarge)
|
||||
Text(stringResource(R.string.add_delegated_admin), style = typography.titleMedium)
|
||||
}
|
||||
if(dialog != 0) {
|
||||
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
|
||||
result?.let { inputPackageName = it }
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable data class AddDelegatedAdmin(val pkg: String = "", val scopes: List<DelegatedScope> = emptyList())
|
||||
|
||||
@RequiresApi(26)
|
||||
@Composable
|
||||
fun AddDelegatedAdminScreen(data: AddDelegatedAdmin, onNavigateUp: () -> Unit) {
|
||||
val updateMode = data.pkg.isNotEmpty()
|
||||
val fm = LocalFocusManager.current
|
||||
val context = LocalContext.current
|
||||
var input by remember { mutableStateOf(data.pkg) }
|
||||
val scopes = remember { mutableStateListOf(*data.scopes.toTypedArray()) }
|
||||
val choosePackage = rememberLauncherForActivityResult(ChoosePackageContract()) { result ->
|
||||
result?.let { input = it }
|
||||
}
|
||||
MyScaffold(if(updateMode) R.string.place_holder else R.string.add_delegated_admin, 0.dp, onNavigateUp, !updateMode) {
|
||||
OutlinedTextField(
|
||||
value = input, onValueChange = { input = it },
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
trailingIcon = {
|
||||
if(!updateMode) IconButton({ choosePackage.launch(null) }) {
|
||||
Icon(painterResource(R.drawable.list_fill0), null)
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() },
|
||||
readOnly = updateMode,
|
||||
modifier = Modifier.fillMaxWidth().padding(8.dp)
|
||||
)
|
||||
DelegatedScope.entries.filter { VERSION.SDK_INT >= it.requiresApi }.forEach {scope ->
|
||||
FullWidthCheckBoxItem(scope.string, scope in scopes) {
|
||||
if(it) scopes += scope else scopes -= scope
|
||||
}
|
||||
AlertDialog(
|
||||
text = {
|
||||
val fm = LocalFocusManager.current
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
OutlinedTextField(
|
||||
value = inputPackageName, onValueChange = { inputPackageName = it },
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
trailingIcon = {
|
||||
if(dialog == 2) IconButton({ choosePackage.launch(null) }) {
|
||||
Icon(painterResource(R.drawable.list_fill0), null)
|
||||
}
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Ascii, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions { fm.clearFocus() },
|
||||
readOnly = dialog == 1,
|
||||
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)
|
||||
)
|
||||
DelegatedScope.entries.forEach { scope ->
|
||||
if(VERSION.SDK_INT >= scope.requiresApi) {
|
||||
CheckBoxItem(scope.string, scope.id in selectedScopes) {
|
||||
if(it) selectedScopes += scope.id else selectedScopes -= scope.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
dpm.setDelegatedScopes(receiver, inputPackageName, selectedScopes)
|
||||
refresh()
|
||||
dialog = 0
|
||||
},
|
||||
enabled = inputPackageName.isNotBlank()
|
||||
) {
|
||||
Text(stringResource(if(dialog == 1) R.string.apply else R.string.add))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton({ dialog = 0 }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
onDismissRequest = { dialog = 0 }
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
context.getDPM().setDelegatedScopes(context.getReceiver(), input, scopes.map { it.id })
|
||||
onNavigateUp()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
enabled = input.isNotBlank() && (!updateMode || scopes.toList() != data.scopes)
|
||||
) {
|
||||
Text(stringResource(if(updateMode) R.string.update else R.string.add))
|
||||
}
|
||||
if(updateMode) Button(
|
||||
onClick = {
|
||||
context.getDPM().setDelegatedScopes(context.getReceiver(), input, emptyList())
|
||||
onNavigateUp()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp),
|
||||
colors = ButtonDefaults.buttonColors(colorScheme.error, colorScheme.onError)
|
||||
) {
|
||||
Text(stringResource(R.string.delete))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ import com.bintianqi.owndroid.humanReadableDate
|
||||
import com.bintianqi.owndroid.parseDate
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.CheckBoxItem
|
||||
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.InfoCard
|
||||
import com.bintianqi.owndroid.ui.ListItem
|
||||
@@ -847,13 +848,13 @@ fun ContentProtectionPolicyScreen(onNavigateUp: () -> Unit) {
|
||||
var policy by remember { mutableIntStateOf(DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY) }
|
||||
fun refresh() { policy = dpm.getContentProtectionPolicy(receiver) }
|
||||
LaunchedEffect(Unit) { refresh() }
|
||||
MyScaffold(R.string.content_protection_policy, 8.dp, onNavigateUp) {
|
||||
MyScaffold(R.string.content_protection_policy, 0.dp, onNavigateUp) {
|
||||
mapOf(
|
||||
DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY to R.string.not_controlled_by_policy,
|
||||
DevicePolicyManager.CONTENT_PROTECTION_ENABLED to R.string.enabled,
|
||||
DevicePolicyManager.CONTENT_PROTECTION_DISABLED to R.string.disabled
|
||||
).forEach { (policyId, string) ->
|
||||
RadioButtonItem(string, policy == policyId) { policy = policyId }
|
||||
FullWidthRadioButtonItem(string, policy == policyId) { policy = policyId }
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -861,11 +862,11 @@ fun ContentProtectionPolicyScreen(onNavigateUp: () -> Unit) {
|
||||
refresh()
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
InfoCard(R.string.info_content_protection_policy)
|
||||
InfoCard(R.string.info_content_protection_policy, 8.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -878,21 +879,27 @@ fun PermissionPolicyScreen(onNavigateUp: () -> Unit) {
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
var selectedPolicy by remember { mutableIntStateOf(dpm.getPermissionPolicy(receiver)) }
|
||||
MyScaffold(R.string.permission_policy, 8.dp, onNavigateUp) {
|
||||
RadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT) { selectedPolicy = PERMISSION_POLICY_PROMPT }
|
||||
RadioButtonItem(R.string.auto_grant, selectedPolicy == PERMISSION_POLICY_AUTO_GRANT) { selectedPolicy = PERMISSION_POLICY_AUTO_GRANT }
|
||||
RadioButtonItem(R.string.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY) { selectedPolicy = PERMISSION_POLICY_AUTO_DENY }
|
||||
MyScaffold(R.string.permission_policy, 0.dp, onNavigateUp) {
|
||||
FullWidthRadioButtonItem(R.string.default_stringres, selectedPolicy == PERMISSION_POLICY_PROMPT) {
|
||||
selectedPolicy = PERMISSION_POLICY_PROMPT
|
||||
}
|
||||
FullWidthRadioButtonItem(R.string.auto_grant, selectedPolicy == PERMISSION_POLICY_AUTO_GRANT) {
|
||||
selectedPolicy = PERMISSION_POLICY_AUTO_GRANT
|
||||
}
|
||||
FullWidthRadioButtonItem(R.string.auto_deny, selectedPolicy == PERMISSION_POLICY_AUTO_DENY) {
|
||||
selectedPolicy = PERMISSION_POLICY_AUTO_DENY
|
||||
}
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.setPermissionPolicy(receiver,selectedPolicy)
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
InfoCard(R.string.info_permission_policy)
|
||||
InfoCard(R.string.info_permission_policy, 8.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -904,10 +911,12 @@ fun MtePolicyScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
var selectedMtePolicy by remember { mutableIntStateOf(dpm.mtePolicy) }
|
||||
MyScaffold(R.string.mte_policy, 8.dp, onNavigateUp) {
|
||||
RadioButtonItem(R.string.decide_by_user, selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY) { selectedMtePolicy = MTE_NOT_CONTROLLED_BY_POLICY }
|
||||
RadioButtonItem(R.string.enabled, selectedMtePolicy == MTE_ENABLED) { selectedMtePolicy = MTE_ENABLED }
|
||||
RadioButtonItem(R.string.disabled, selectedMtePolicy == MTE_DISABLED) { selectedMtePolicy = MTE_DISABLED }
|
||||
MyScaffold(R.string.mte_policy, 0.dp, onNavigateUp) {
|
||||
FullWidthRadioButtonItem(R.string.decide_by_user, selectedMtePolicy == MTE_NOT_CONTROLLED_BY_POLICY) {
|
||||
selectedMtePolicy = MTE_NOT_CONTROLLED_BY_POLICY
|
||||
}
|
||||
FullWidthRadioButtonItem(R.string.enabled, selectedMtePolicy == MTE_ENABLED) { selectedMtePolicy = MTE_ENABLED }
|
||||
FullWidthRadioButtonItem(R.string.disabled, selectedMtePolicy == MTE_DISABLED) { selectedMtePolicy = MTE_DISABLED }
|
||||
Button(
|
||||
onClick = {
|
||||
try {
|
||||
@@ -918,11 +927,11 @@ fun MtePolicyScreen(onNavigateUp: () -> Unit) {
|
||||
}
|
||||
selectedMtePolicy = dpm.mtePolicy
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
InfoCard(R.string.info_mte_policy)
|
||||
InfoCard(R.string.info_mte_policy, 8.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,62 +943,64 @@ fun NearbyStreamingPolicyScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
var appPolicy by remember { mutableIntStateOf(dpm.nearbyAppStreamingPolicy) }
|
||||
MyScaffold(R.string.nearby_streaming_policy, 8.dp, onNavigateUp) {
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.nearby_app_streaming), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
RadioButtonItem(
|
||||
MyScaffold(R.string.nearby_streaming_policy, 0.dp, onNavigateUp, false) {
|
||||
Text(
|
||||
stringResource(R.string.nearby_app_streaming),
|
||||
Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge
|
||||
)
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.decide_by_user,
|
||||
appPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY
|
||||
) { appPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }
|
||||
RadioButtonItem(R.string.enabled, appPolicy == NEARBY_STREAMING_ENABLED) { appPolicy = NEARBY_STREAMING_ENABLED }
|
||||
RadioButtonItem(R.string.disabled, appPolicy == NEARBY_STREAMING_DISABLED) { appPolicy = NEARBY_STREAMING_DISABLED }
|
||||
RadioButtonItem(
|
||||
FullWidthRadioButtonItem(R.string.enabled, appPolicy == NEARBY_STREAMING_ENABLED) { appPolicy = NEARBY_STREAMING_ENABLED }
|
||||
FullWidthRadioButtonItem(R.string.disabled, appPolicy == NEARBY_STREAMING_DISABLED) { appPolicy = NEARBY_STREAMING_DISABLED }
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.enable_if_secure_enough,
|
||||
appPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
|
||||
) { appPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.nearbyAppStreamingPolicy = appPolicy
|
||||
appPolicy = dpm.nearbyAppStreamingPolicy
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
InfoCard(R.string.info_nearby_app_streaming_policy)
|
||||
InfoCard(R.string.info_nearby_app_streaming_policy, 8.dp)
|
||||
var notificationPolicy by remember { mutableIntStateOf(dpm.nearbyNotificationStreamingPolicy) }
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.nearby_notification_streaming), style = typography.titleLarge)
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
RadioButtonItem(
|
||||
Text(
|
||||
stringResource(R.string.nearby_notification_streaming),
|
||||
Modifier.padding(start = 8.dp, top = 10.dp, bottom = 4.dp), style = typography.titleLarge
|
||||
)
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.decide_by_user,
|
||||
notificationPolicy == NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY
|
||||
) { notificationPolicy = NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY }
|
||||
RadioButtonItem(
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.enabled,
|
||||
notificationPolicy == NEARBY_STREAMING_ENABLED
|
||||
) { notificationPolicy = NEARBY_STREAMING_ENABLED }
|
||||
RadioButtonItem(
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.disabled,
|
||||
notificationPolicy == NEARBY_STREAMING_DISABLED
|
||||
) { notificationPolicy = NEARBY_STREAMING_DISABLED }
|
||||
RadioButtonItem(
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.enable_if_secure_enough,
|
||||
notificationPolicy == NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
|
||||
) { notificationPolicy = NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY }
|
||||
Spacer(Modifier.padding(vertical = 3.dp))
|
||||
Button(
|
||||
onClick = {
|
||||
dpm.nearbyNotificationStreamingPolicy = notificationPolicy
|
||||
notificationPolicy = dpm.nearbyNotificationStreamingPolicy
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
InfoCard(R.string.info_nearby_notification_streaming_policy)
|
||||
InfoCard(R.string.info_nearby_notification_streaming_policy, 8.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1586,7 +1597,8 @@ fun FrpPolicyScreen(onNavigateUp: () -> Unit) {
|
||||
onClick = {
|
||||
accountList += inputAccount
|
||||
inputAccount = ""
|
||||
}
|
||||
},
|
||||
enabled = inputAccount.isNotBlank()
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
|
||||
}
|
||||
@@ -1733,87 +1745,81 @@ fun WipeDataScreen(onNavigateUp: () -> Unit) {
|
||||
|
||||
@Serializable object SetSystemUpdatePolicy
|
||||
|
||||
@RequiresApi(23)
|
||||
@Composable
|
||||
fun SystemUpdatePolicy(onNavigateUp: () -> Unit) {
|
||||
fun SystemUpdatePolicyScreen(onNavigateUp: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
val receiver = context.getReceiver()
|
||||
val focusMgr = LocalFocusManager.current
|
||||
MyScaffold(R.string.system_update_policy, 8.dp, onNavigateUp) {
|
||||
if(VERSION.SDK_INT >= 23) {
|
||||
Column {
|
||||
var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) }
|
||||
RadioButtonItem(
|
||||
R.string.system_update_policy_automatic,
|
||||
selectedPolicy == TYPE_INSTALL_AUTOMATIC
|
||||
) { selectedPolicy = TYPE_INSTALL_AUTOMATIC }
|
||||
RadioButtonItem(
|
||||
R.string.system_update_policy_install_windowed,
|
||||
selectedPolicy == TYPE_INSTALL_WINDOWED
|
||||
) { selectedPolicy = TYPE_INSTALL_WINDOWED }
|
||||
RadioButtonItem(
|
||||
R.string.system_update_policy_postpone,
|
||||
selectedPolicy == TYPE_POSTPONE
|
||||
) { selectedPolicy = TYPE_POSTPONE }
|
||||
RadioButtonItem(R.string.none, selectedPolicy == null) { selectedPolicy = null }
|
||||
var windowedPolicyStart by remember { mutableStateOf("") }
|
||||
var windowedPolicyEnd by remember { mutableStateOf("") }
|
||||
AnimatedVisibility(selectedPolicy == 2) {
|
||||
Column {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = windowedPolicyStart,
|
||||
label = { Text(stringResource(R.string.start_time)) },
|
||||
onValueChange = { windowedPolicyStart = it },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = windowedPolicyEnd,
|
||||
onValueChange = {windowedPolicyEnd = it },
|
||||
label = { Text(stringResource(R.string.end_time)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth(0.96F).padding(bottom = 2.dp)
|
||||
)
|
||||
}
|
||||
Text(text = stringResource(R.string.minutes_in_one_day))
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val policy =
|
||||
when(selectedPolicy) {
|
||||
TYPE_INSTALL_AUTOMATIC-> SystemUpdatePolicy.createAutomaticInstallPolicy()
|
||||
TYPE_INSTALL_WINDOWED-> SystemUpdatePolicy.createWindowedInstallPolicy(windowedPolicyStart.toInt(), windowedPolicyEnd.toInt())
|
||||
TYPE_POSTPONE-> SystemUpdatePolicy.createPostponeInstallPolicy()
|
||||
else -> null
|
||||
}
|
||||
dpm.setSystemUpdatePolicy(receiver,policy)
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
MyScaffold(R.string.system_update_policy, 0.dp, onNavigateUp) {
|
||||
var selectedPolicy by remember { mutableStateOf(dpm.systemUpdatePolicy?.policyType) }
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.system_update_policy_automatic,
|
||||
selectedPolicy == TYPE_INSTALL_AUTOMATIC
|
||||
) { selectedPolicy = TYPE_INSTALL_AUTOMATIC }
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.system_update_policy_install_windowed,
|
||||
selectedPolicy == TYPE_INSTALL_WINDOWED
|
||||
) { selectedPolicy = TYPE_INSTALL_WINDOWED }
|
||||
FullWidthRadioButtonItem(
|
||||
R.string.system_update_policy_postpone,
|
||||
selectedPolicy == TYPE_POSTPONE
|
||||
) { selectedPolicy = TYPE_POSTPONE }
|
||||
FullWidthRadioButtonItem(R.string.none, selectedPolicy == null) { selectedPolicy = null }
|
||||
var windowedPolicyStart by remember { mutableStateOf("") }
|
||||
var windowedPolicyEnd by remember { mutableStateOf("") }
|
||||
AnimatedVisibility(selectedPolicy == 2) {
|
||||
Column(Modifier.padding(horizontal = 8.dp)) {
|
||||
Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), Arrangement.SpaceBetween) {
|
||||
OutlinedTextField(
|
||||
value = windowedPolicyStart,
|
||||
label = { Text(stringResource(R.string.start_time)) },
|
||||
onValueChange = { windowedPolicyStart = it },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth(0.49F)
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = windowedPolicyEnd,
|
||||
onValueChange = {windowedPolicyEnd = it },
|
||||
label = { Text(stringResource(R.string.end_time)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
|
||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
||||
modifier = Modifier.fillMaxWidth(0.96F).padding(bottom = 2.dp)
|
||||
)
|
||||
}
|
||||
Text(stringResource(R.string.minutes_in_one_day), color = colorScheme.onSurfaceVariant, style = typography.bodyMedium)
|
||||
}
|
||||
}
|
||||
Button(
|
||||
onClick = {
|
||||
val policy =
|
||||
when(selectedPolicy) {
|
||||
TYPE_INSTALL_AUTOMATIC-> SystemUpdatePolicy.createAutomaticInstallPolicy()
|
||||
TYPE_INSTALL_WINDOWED-> SystemUpdatePolicy.createWindowedInstallPolicy(windowedPolicyStart.toInt(), windowedPolicyEnd.toInt())
|
||||
TYPE_POSTPONE-> SystemUpdatePolicy.createPostponeInstallPolicy()
|
||||
else -> null
|
||||
}
|
||||
dpm.setSystemUpdatePolicy(receiver,policy)
|
||||
context.showOperationResultToast(true)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
if(VERSION.SDK_INT >= 26) {
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
val sysUpdateInfo = dpm.getPendingSystemUpdate(receiver)
|
||||
Column {
|
||||
Column(Modifier.padding(8.dp)) {
|
||||
if(sysUpdateInfo != null) {
|
||||
Text(text = stringResource(R.string.update_received_time, Date(sysUpdateInfo.receivedTime)))
|
||||
val securityStateDesc = when(sysUpdateInfo.securityPatchState) {
|
||||
SystemUpdateInfo.SECURITY_PATCH_STATE_UNKNOWN -> stringResource(R.string.unknown)
|
||||
SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE -> "true"
|
||||
else->"false"
|
||||
val securityPatchStateText = when(sysUpdateInfo.securityPatchState) {
|
||||
SystemUpdateInfo.SECURITY_PATCH_STATE_FALSE -> R.string.no
|
||||
SystemUpdateInfo.SECURITY_PATCH_STATE_TRUE -> R.string.yes
|
||||
else -> R.string.unknown
|
||||
}
|
||||
Text(text = stringResource(R.string.is_security_patch, securityStateDesc))
|
||||
}else{
|
||||
Text(text = stringResource(R.string.is_security_patch, stringResource(securityPatchStateText)))
|
||||
} else {
|
||||
Text(text = stringResource(R.string.no_system_update))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,10 @@ fun UsersScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||
if(bitmap != null) changeUserIconDialog = true
|
||||
}
|
||||
}
|
||||
FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) { launcher.launch("image/*") }
|
||||
FunctionItem(R.string.change_user_icon, icon = R.drawable.account_circle_fill0) {
|
||||
Toast.makeText(context, R.string.select_an_image, Toast.LENGTH_SHORT).show()
|
||||
launcher.launch("image/*")
|
||||
}
|
||||
if(changeUserIconDialog == true) ChangeUserIconDialog(bitmap!!) { changeUserIconDialog = false }
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28 && deviceOwner) {
|
||||
@@ -432,7 +435,8 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
|
||||
onClick = {
|
||||
list += input
|
||||
input = ""
|
||||
}
|
||||
},
|
||||
enabled = input.isNotEmpty()
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(R.string.add))
|
||||
}
|
||||
@@ -453,7 +457,7 @@ fun AffiliationIdScreen(onNavigateUp: () -> Unit) {
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
InfoCard(R.string.info_affiliated_id)
|
||||
InfoCard(R.string.info_affiliation_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -272,12 +272,12 @@ fun ListItem(text: String, onDelete: () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfoCard(@StringRes strID: Int) {
|
||||
fun InfoCard(@StringRes strID: Int, horizonPadding: Dp = 0.dp) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp)
|
||||
.clip(RoundedCornerShape(10))
|
||||
.padding(vertical = 8.dp, horizontal = horizonPadding)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(color = colorScheme.tertiaryContainer)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
|
||||
@@ -526,8 +526,7 @@
|
||||
<string name="serial_number_of_new_user_is">Серийный номер этого пользователя: %1$d</string>
|
||||
<string name="affiliation_id">Аффилированный идентификатор</string>
|
||||
<string name="change_user_icon">Изменить значок пользователя</string>
|
||||
<string name="file_picker_instead_gallery">Использовать выборщик файлов вместо галереи</string>
|
||||
<string name="select_picture" tools:ignore="TypographyEllipsis">Выберите изображение...</string>
|
||||
<string name="select_an_image">Select an image</string> <!--TODO-->
|
||||
<string name="fail_managed_profile">Ошибка: управляемый профиль</string>
|
||||
<string name="fail_current_user">Ошибка: текущий пользователь</string>
|
||||
<string name="user_session_msg">Сообщение о сеансе пользователя</string>
|
||||
@@ -544,10 +543,10 @@
|
||||
<string name="max_time_to_lock">Время ожидания экрана</string>
|
||||
<string name="required_strong_auth_timeout">Время ожидания строгой аутентификации</string>
|
||||
<string name="pwd_history">Длина истории паролей</string>
|
||||
<string name="password_complexity_none">Нет (разрешено отсутствие пароля)</string>
|
||||
<string name="password_complexity_low">Низкая (графический ключ и повторение символов разрешены)</string>
|
||||
<string name="password_complexity_medium">Средняя (повторение запрещено, минимум 4 символа)</string>
|
||||
<string name="password_complexity_high">Высокая (повторение запрещено, минимум 6 символов)</string>
|
||||
<!--TODO: 3 strings-->
|
||||
<string name="low">Low</string>
|
||||
<string name="medium">Medium</string>
|
||||
<string name="high">High</string>
|
||||
<string name="current_password_complexity">Текущая сложность пароля</string>
|
||||
<string name="password_sufficient">Сложность пароля достаточна</string>
|
||||
<string name="unified_password">Единый пароль</string>
|
||||
@@ -565,7 +564,6 @@
|
||||
<string name="reset_password_require_entry">Требовать ввод</string>
|
||||
<string name="reset_password_with_token">Сбросить пароль с помощью токена</string>
|
||||
<string name="required_password_complexity">Требуемая сложность пароля</string>
|
||||
<string name="require_set_new_password">Запрос на установку нового пароля</string>
|
||||
<string name="disable_keyguard_features">Функции блокировки экрана (Keyguard)</string>
|
||||
<string name="enable_all">Включить все</string>
|
||||
<string name="disable_keyguard_features_widgets">Отключить виджеты</string>
|
||||
@@ -709,7 +707,8 @@
|
||||
<string name="info_keep_uninstalled_apps">Установить список приложений, которые нужно сохранить в виде APK-файлов, даже если ни у одного пользователя в данный момент они не установлены.</string>
|
||||
<string name="info_headless_system_user_mode">Режим "безголового" системного пользователя означает, что системный пользователь запускает системные службы и некоторый системный интерфейс, но он не связан с каким-либо реальным человеком, и для связи с реальными людьми должны быть созданы дополнительные пользователи.</string>
|
||||
<string name="info_logout">If the current user is not switched by OwnDroid, this function cannot be used.</string> <!--TODO-->
|
||||
<string name="info_affiliated_id">Когда владелец устройства создает управляемого пользователя, управляемый пользователь не является аффилированным. Чтобы сделать управляемого пользователя аффилированным с владельцем устройства, вам следует установить одинаковые аффилированные идентификаторы в основном и управляемом пользователях.</string>
|
||||
<string name="info_affiliation_id">Когда владелец устройства создает управляемого пользователя, управляемый пользователь не является аффилированным. Чтобы сделать управляемого пользователя аффилированным с владельцем устройства, вам следует установить одинаковые аффилированные идентификаторы в основном и управляемом пользователях.</string>
|
||||
<!--TODO--><string name="info_password_complexity">None: no password\nLow: pattern, PIN (with repeating or ordered sequences)\nMedium: PIN (with no repeating or ordered sequences, length at least 4), alphabetic (length at least 4), alphanumeric (length at least 4)\nHigh: PIN (with no repeating or ordered sequences, length at least 8), alphabetic (length at least 6), alphanumeric (length at least 6)</string>
|
||||
<string name="info_reset_password">Установить новый пароль блокировки экрана. Длина этого пароля должна быть не менее 4 цифр. Оставьте поле пустым, чтобы удалить пароль.\nЕсли вы установите цифровой пароль длиной 6 символов или меньше, он будет установлен как PIN-код.</string>
|
||||
<string name="info_screen_timeout">Установить максимальное время бездействия пользователя, по истечении которого устройство будет заблокировано. Это ограничивает время, которое может установить пользователь.\nЗначение 0 означает отсутствие ограничений.</string>
|
||||
<string name="info_password_expiration_timeout">Перезапустить отсчет времени истечения срока действия пароля.\nЗначение 0 означает отсутствие ограничений.</string>
|
||||
|
||||
@@ -530,8 +530,7 @@
|
||||
<string name="serial_number_of_new_user_is">Bu kullanıcının seri numarası: %1$d</string>
|
||||
<string name="affiliation_id">Bağlılık ID</string>
|
||||
<string name="change_user_icon">Kullanıcı simgesini değiştir</string>
|
||||
<string name="file_picker_instead_gallery">Galeri yerine dosya seçici kullan</string>
|
||||
<string name="select_picture" tools:ignore="TypographyEllipsis">Resim seç...</string>
|
||||
<string name="select_an_image">Select an image</string> <!--TODO-->
|
||||
<string name="fail_managed_profile">Başarısız: yönetilen profil</string>
|
||||
<string name="fail_current_user">Başarısız: mevcut kullanıcı</string>
|
||||
<string name="user_session_msg">Kullanıcı oturum mesajı</string>
|
||||
@@ -546,10 +545,10 @@
|
||||
<string name="max_time_to_lock">Ekran zaman aşımı</string>
|
||||
<string name="required_strong_auth_timeout">Gereken güçlü doğrulama zaman aşımı</string>
|
||||
<string name="pwd_history">Şifre geçmişi uzunluğu</string>
|
||||
<string name="password_complexity_none">Yok (Şifreye izin verilmez)</string>
|
||||
<string name="password_complexity_low">Düşük (Hareket şifresi ve karakter tekrarı izinli)</string>
|
||||
<string name="password_complexity_medium">Orta (Tekrar yasak, en az 4 karakter)</string>
|
||||
<string name="password_complexity_high">Yüksek (Tekrar yasak, en az 6 karakter)</string>
|
||||
<!--TODO: 3 strings-->
|
||||
<string name="low">Low</string>
|
||||
<string name="medium">Medium</string>
|
||||
<string name="high">High</string>
|
||||
<string name="current_password_complexity">Mevcut şifre karmaşıklığı</string>
|
||||
<string name="password_sufficient">Şifre karmaşıklığı yeterli mi</string>
|
||||
<string name="unified_password">Birleşik şifre</string>
|
||||
@@ -567,7 +566,6 @@
|
||||
<string name="reset_password_require_entry">Giriş gerektir</string>
|
||||
<string name="reset_password_with_token">Jeton ile şifreyi sıfırla</string>
|
||||
<string name="required_password_complexity">Gereken şifre karmaşıklığı</string>
|
||||
<string name="require_set_new_password">Yeni şifre ayarlanmasını iste</string>
|
||||
<string name="disable_keyguard_features">Kilit ekranı özellikleri</string>
|
||||
<string name="enable_all">Tümünü etkinleştir</string>
|
||||
<string name="disable_keyguard_features_widgets">Widget\'ı devre dışı bırak</string>
|
||||
|
||||
@@ -516,8 +516,7 @@
|
||||
<string name="serial_number_of_new_user_is">新用户的序列号:%1$d</string>
|
||||
<string name="affiliation_id">附属用户ID</string>
|
||||
<string name="change_user_icon">更换用户头像</string>
|
||||
<string name="file_picker_instead_gallery">使用文件选择器而不是相册</string>
|
||||
<string name="select_picture" tools:ignore="TypographyEllipsis">选择图片...</string>
|
||||
<string name="select_an_image">选择一个图片</string>
|
||||
<string name="fail_managed_profile">失败:受管理的资料</string>
|
||||
<string name="fail_current_user">失败:当前用户</string>
|
||||
<string name="user_session_msg">用户会话消息</string>
|
||||
@@ -532,10 +531,9 @@
|
||||
<string name="max_time_to_lock">屏幕超时</string>
|
||||
<string name="required_strong_auth_timeout">要求强验证超时</string>
|
||||
<string name="pwd_history">密码历史长度</string>
|
||||
<string name="password_complexity_none">无(允许不设密码)</string>
|
||||
<string name="password_complexity_low">低(允许图案和连续性)</string>
|
||||
<string name="password_complexity_medium">中(无连续性,至少4位)</string>
|
||||
<string name="password_complexity_high">高(无连续性,至少6位)</string>
|
||||
<string name="low">低</string>
|
||||
<string name="medium">中</string>
|
||||
<string name="high">高</string>
|
||||
<string name="current_password_complexity">当前密码复杂度</string>
|
||||
<string name="password_sufficient">密码符合复杂度要求</string>
|
||||
<string name="unified_password">一致的密码</string>
|
||||
@@ -553,7 +551,6 @@
|
||||
<string name="reset_password_require_entry">不允许其他设备管理员重置密码直至用户输入一次密码</string>
|
||||
<string name="reset_password_with_token">使用令牌重置密码</string>
|
||||
<string name="required_password_complexity">密码复杂度要求</string>
|
||||
<string name="require_set_new_password">要求设置新密码</string>
|
||||
<string name="disable_keyguard_features">锁屏功能</string>
|
||||
<string name="enable_all">启用全部</string>
|
||||
<string name="disable_keyguard_features_widgets">禁用小工具(安卓5以下)</string>
|
||||
@@ -694,7 +691,8 @@
|
||||
<string name="info_keep_uninstalled_apps">这个列表中的应用的APK将会一直保留,即使没有任何用户安装这个应用</string>
|
||||
<string name="info_headless_system_user_mode">无头系统用户模式意味着系统用户运行系统服务和一些系统UI,但它不与任何真实的人相关联,必须创建额外的用户才能与真实的人相关联。</string>
|
||||
<string name="info_logout">如果当前用户不是由OwnDroid切换的,无法使用此功能。</string>
|
||||
<string name="info_affiliated_id">当Device owner创建并管理用户时,新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后,受管理用户成为附属于Device owner的用户</string>
|
||||
<string name="info_affiliation_id">当Device owner创建并管理用户时,新的用户不是附属用户。Device owner设置和受管理用户完全相同的附属用户ID后,受管理用户成为附属于Device owner的用户</string>
|
||||
<string name="info_password_complexity">无:无密码\n低:图案,PIN(有重复或有序序列)\n中:PIN(没有重复或有序序列,最低长度4),字母(最低长度4),字母与数字(最低长度4)\n高:PIN(没有重复或有序序列,最低长度8),字母(最低长度6),字母与数字(最低长度6)</string>
|
||||
<string name="info_reset_password">设置一个新的密码,密码的长度需要4位或以上,不输入密码将会清除现有的密码。长度在6位或以下的纯数字密码将会设置为PIN码。</string>
|
||||
<string name="info_screen_timeout">设置设备锁定前用户活动的最大时间。这限制了用户可以设置的时间长度。\n值为0表示不做限制。</string>
|
||||
<string name="info_password_expiration_timeout">重新启动密码过期倒计时。值为0表示不做限制。</string>
|
||||
|
||||
@@ -555,8 +555,7 @@
|
||||
<string name="serial_number_of_new_user_is">Serial number of this user: %1$d</string>
|
||||
<string name="affiliation_id">Affiliation ID</string>
|
||||
<string name="change_user_icon">Change user icon</string>
|
||||
<string name="file_picker_instead_gallery">Use file picker instead of gallery</string>
|
||||
<string name="select_picture" tools:ignore="TypographyEllipsis">Select image...</string>
|
||||
<string name="select_an_image">Select an image</string>
|
||||
<string name="fail_managed_profile">Failed: managed profile</string>
|
||||
<string name="fail_current_user">Failed: current user</string>
|
||||
<string name="user_session_msg">User session message</string>
|
||||
@@ -571,10 +570,9 @@
|
||||
<string name="max_time_to_lock">Screen timeout</string>
|
||||
<string name="required_strong_auth_timeout">Required strong auth timeout</string>
|
||||
<string name="pwd_history">Password history length</string>
|
||||
<string name="password_complexity_none">None (No password allowed)</string>
|
||||
<string name="password_complexity_low">Low (Gesture password and characters repetition allowed)</string>
|
||||
<string name="password_complexity_medium">Medium (Repetition disallowed, 4 characters at least)</string>
|
||||
<string name="password_complexity_high">High (Repetition disallowed, 6 characters at least)</string>
|
||||
<string name="low">Low</string>
|
||||
<string name="medium">Medium</string>
|
||||
<string name="high">High</string>
|
||||
<string name="current_password_complexity">Current password complexity</string>
|
||||
<string name="password_sufficient">Password complexity sufficient</string>
|
||||
<string name="unified_password">Unified password</string>
|
||||
@@ -592,7 +590,6 @@
|
||||
<string name="reset_password_require_entry">Require entry</string>
|
||||
<string name="reset_password_with_token">Reset password with token</string>
|
||||
<string name="required_password_complexity">Required password complexity</string>
|
||||
<string name="require_set_new_password">Request to set a new password</string>
|
||||
<string name="disable_keyguard_features">Keyguard features</string>
|
||||
<string name="enable_all">Enable all</string>
|
||||
<string name="disable_keyguard_features_widgets">Disable widget</string>
|
||||
@@ -734,7 +731,8 @@
|
||||
<string name="info_keep_uninstalled_apps">Set a list of apps to keep around as APKs even if no user has currently installed it. </string>
|
||||
<string name="info_headless_system_user_mode">Headless system user mode means the system user runs system services and some system UI, but it is not associated with any real person and additional users must be created to be associated with real persons.</string>
|
||||
<string name="info_logout">If the current user is not switched by OwnDroid, this function cannot be used.</string>
|
||||
<string name="info_affiliated_id">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</string>
|
||||
<string name="info_affiliation_id">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</string>
|
||||
<string name="info_password_complexity">None: no password\nLow: pattern, PIN (with repeating or ordered sequences)\nMedium: PIN (with no repeating or ordered sequences, length at least 4), alphabetic (length at least 4), alphanumeric (length at least 4)\nHigh: PIN (with no repeating or ordered sequences, length at least 8), alphabetic (length at least 6), alphanumeric (length at least 6)</string>
|
||||
<string name="info_reset_password">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</string>
|
||||
<string name="info_screen_timeout">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.</string>
|
||||
<string name="info_password_expiration_timeout">Restart the countdown for password expiration.\nA value of 0 means there is no restriction.</string>
|
||||
|
||||
Reference in New Issue
Block a user