mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-27 04:16:00 +00:00
@@ -177,7 +177,6 @@ import com.bintianqi.owndroid.dpm.ResetPassword
|
|||||||
import com.bintianqi.owndroid.dpm.ResetPasswordScreen
|
import com.bintianqi.owndroid.dpm.ResetPasswordScreen
|
||||||
import com.bintianqi.owndroid.dpm.ResetPasswordToken
|
import com.bintianqi.owndroid.dpm.ResetPasswordToken
|
||||||
import com.bintianqi.owndroid.dpm.ResetPasswordTokenScreen
|
import com.bintianqi.owndroid.dpm.ResetPasswordTokenScreen
|
||||||
import com.bintianqi.owndroid.dpm.Restriction
|
|
||||||
import com.bintianqi.owndroid.dpm.SecurityLogging
|
import com.bintianqi.owndroid.dpm.SecurityLogging
|
||||||
import com.bintianqi.owndroid.dpm.SecurityLoggingScreen
|
import com.bintianqi.owndroid.dpm.SecurityLoggingScreen
|
||||||
import com.bintianqi.owndroid.dpm.SetDefaultDialer
|
import com.bintianqi.owndroid.dpm.SetDefaultDialer
|
||||||
@@ -216,8 +215,6 @@ import com.bintianqi.owndroid.dpm.UsersOptions
|
|||||||
import com.bintianqi.owndroid.dpm.UsersOptionsScreen
|
import com.bintianqi.owndroid.dpm.UsersOptionsScreen
|
||||||
import com.bintianqi.owndroid.dpm.UsersScreen
|
import com.bintianqi.owndroid.dpm.UsersScreen
|
||||||
import com.bintianqi.owndroid.dpm.WiFi
|
import com.bintianqi.owndroid.dpm.WiFi
|
||||||
import com.bintianqi.owndroid.dpm.WifiAuthKeypair
|
|
||||||
import com.bintianqi.owndroid.dpm.WifiAuthKeypairScreen
|
|
||||||
import com.bintianqi.owndroid.dpm.WifiScreen
|
import com.bintianqi.owndroid.dpm.WifiScreen
|
||||||
import com.bintianqi.owndroid.dpm.WifiSecurityLevel
|
import com.bintianqi.owndroid.dpm.WifiSecurityLevel
|
||||||
import com.bintianqi.owndroid.dpm.WifiSecurityLevelScreen
|
import com.bintianqi.owndroid.dpm.WifiSecurityLevelScreen
|
||||||
@@ -601,12 +598,24 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
|||||||
::navigateUp)
|
::navigateUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Password> { PasswordScreen(::navigateUp, ::navigate) }
|
composable<Password> { PasswordScreen(vm, ::navigateUp, ::navigate) }
|
||||||
composable<PasswordInfo> { PasswordInfoScreen(::navigateUp) }
|
composable<PasswordInfo> {
|
||||||
composable<ResetPasswordToken> { ResetPasswordTokenScreen(::navigateUp) }
|
PasswordInfoScreen(vm::getPasswordComplexity, vm::isPasswordComplexitySufficient,
|
||||||
composable<ResetPassword> { ResetPasswordScreen(::navigateUp) }
|
vm::isUsingUnifiedPassword, ::navigateUp)
|
||||||
composable<RequiredPasswordComplexity> { RequiredPasswordComplexityScreen(::navigateUp) }
|
}
|
||||||
composable<KeyguardDisabledFeatures> { KeyguardDisabledFeaturesScreen(::navigateUp) }
|
composable<ResetPasswordToken> {
|
||||||
|
ResetPasswordTokenScreen(vm::getRpTokenState, vm::setRpToken,
|
||||||
|
vm::createActivateRpTokenIntent, vm::clearRpToken, ::navigateUp)
|
||||||
|
}
|
||||||
|
composable<ResetPassword> { ResetPasswordScreen(vm::resetPassword, ::navigateUp) }
|
||||||
|
composable<RequiredPasswordComplexity> {
|
||||||
|
RequiredPasswordComplexityScreen(vm::getRequiredPasswordComplexity,
|
||||||
|
vm::setRequiredPasswordComplexity, ::navigateUp)
|
||||||
|
}
|
||||||
|
composable<KeyguardDisabledFeatures> {
|
||||||
|
KeyguardDisabledFeaturesScreen(vm::getKeyguardDisableConfig,
|
||||||
|
vm::setKeyguardDisableConfig, ::navigateUp)
|
||||||
|
}
|
||||||
composable<RequiredPasswordQuality> { RequiredPasswordQualityScreen(::navigateUp) }
|
composable<RequiredPasswordQuality> { RequiredPasswordQualityScreen(::navigateUp) }
|
||||||
|
|
||||||
composable<Settings> { SettingsScreen(::navigateUp, ::navigate) }
|
composable<Settings> { SettingsScreen(::navigateUp, ::navigate) }
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.bintianqi.owndroid
|
package com.bintianqi.owndroid
|
||||||
|
|
||||||
import android.accounts.Account
|
import android.accounts.Account
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.ActivityOptions
|
import android.app.ActivityOptions
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.app.KeyguardManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.admin.DeviceAdminInfo
|
import android.app.admin.DeviceAdminInfo
|
||||||
import android.app.admin.DeviceAdminReceiver
|
import android.app.admin.DeviceAdminReceiver
|
||||||
@@ -68,15 +68,20 @@ import com.bintianqi.owndroid.dpm.HardwareProperties
|
|||||||
import com.bintianqi.owndroid.dpm.IntentFilterDirection
|
import com.bintianqi.owndroid.dpm.IntentFilterDirection
|
||||||
import com.bintianqi.owndroid.dpm.IntentFilterOptions
|
import com.bintianqi.owndroid.dpm.IntentFilterOptions
|
||||||
import com.bintianqi.owndroid.dpm.IpMode
|
import com.bintianqi.owndroid.dpm.IpMode
|
||||||
|
import com.bintianqi.owndroid.dpm.KeyguardDisableConfig
|
||||||
|
import com.bintianqi.owndroid.dpm.KeyguardDisableMode
|
||||||
import com.bintianqi.owndroid.dpm.NetworkStatsData
|
import com.bintianqi.owndroid.dpm.NetworkStatsData
|
||||||
import com.bintianqi.owndroid.dpm.NetworkStatsTarget
|
import com.bintianqi.owndroid.dpm.NetworkStatsTarget
|
||||||
|
import com.bintianqi.owndroid.dpm.PasswordComplexity
|
||||||
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
|
import com.bintianqi.owndroid.dpm.PendingSystemUpdateInfo
|
||||||
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceInfo
|
import com.bintianqi.owndroid.dpm.PreferentialNetworkServiceInfo
|
||||||
import com.bintianqi.owndroid.dpm.PrivateDnsConfiguration
|
import com.bintianqi.owndroid.dpm.PrivateDnsConfiguration
|
||||||
|
import com.bintianqi.owndroid.dpm.PrivateDnsMode
|
||||||
import com.bintianqi.owndroid.dpm.ProxyMode
|
import com.bintianqi.owndroid.dpm.ProxyMode
|
||||||
import com.bintianqi.owndroid.dpm.ProxyType
|
import com.bintianqi.owndroid.dpm.ProxyType
|
||||||
import com.bintianqi.owndroid.dpm.QueryNetworkStatsParams
|
import com.bintianqi.owndroid.dpm.QueryNetworkStatsParams
|
||||||
import com.bintianqi.owndroid.dpm.RecommendedProxyConf
|
import com.bintianqi.owndroid.dpm.RecommendedProxyConf
|
||||||
|
import com.bintianqi.owndroid.dpm.RpTokenState
|
||||||
import com.bintianqi.owndroid.dpm.SsidPolicy
|
import com.bintianqi.owndroid.dpm.SsidPolicy
|
||||||
import com.bintianqi.owndroid.dpm.SsidPolicyType
|
import com.bintianqi.owndroid.dpm.SsidPolicyType
|
||||||
import com.bintianqi.owndroid.dpm.SystemOptionsStatus
|
import com.bintianqi.owndroid.dpm.SystemOptionsStatus
|
||||||
@@ -113,7 +118,6 @@ import java.time.ZoneId
|
|||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.reflect.jvm.jvmErasure
|
import kotlin.reflect.jvm.jvmErasure
|
||||||
import kotlin.system.measureTimeMillis
|
|
||||||
|
|
||||||
class MyViewModel(application: Application): AndroidViewModel(application) {
|
class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||||
val myRepo = getApplication<MyApplication>().myRepo
|
val myRepo = getApplication<MyApplication>().myRepo
|
||||||
@@ -1472,8 +1476,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
fun getPrivateDns(): PrivateDnsConfiguration {
|
fun getPrivateDns(): PrivateDnsConfiguration {
|
||||||
|
val mode = DPM.getGlobalPrivateDnsMode(DAR)
|
||||||
return PrivateDnsConfiguration(
|
return PrivateDnsConfiguration(
|
||||||
DPM.getGlobalPrivateDnsMode(DAR), DPM.getGlobalPrivateDnsHost(DAR) ?: ""
|
PrivateDnsMode.entries.find { it.id == mode }!!, DPM.getGlobalPrivateDnsHost(DAR) ?: ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@Suppress("PrivateApi")
|
@Suppress("PrivateApi")
|
||||||
@@ -1483,7 +1488,8 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
val field = DevicePolicyManager::class.java.getDeclaredField("mService")
|
val field = DevicePolicyManager::class.java.getDeclaredField("mService")
|
||||||
field.isAccessible = true
|
field.isAccessible = true
|
||||||
val dpm = field.get(DPM) as IDevicePolicyManager
|
val dpm = field.get(DPM) as IDevicePolicyManager
|
||||||
val result = dpm.setGlobalPrivateDns(DAR, conf.mode, conf.host)
|
val host = if (conf.mode == PrivateDnsMode.Host) conf.host else null
|
||||||
|
val result = dpm.setGlobalPrivateDns(DAR, conf.mode.id, host)
|
||||||
result == DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR
|
result == DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
@@ -1645,6 +1651,107 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
|||||||
fun removeApnConfig(id: Int): Boolean {
|
fun removeApnConfig(id: Int): Boolean {
|
||||||
return DPM.removeOverrideApn(DAR, id)
|
return DPM.removeOverrideApn(DAR, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(29)
|
||||||
|
fun getPasswordComplexity(): PasswordComplexity {
|
||||||
|
val complexity = DPM.passwordComplexity
|
||||||
|
return PasswordComplexity.entries.find { it.id == complexity }!!
|
||||||
|
}
|
||||||
|
fun isPasswordComplexitySufficient(): Boolean {
|
||||||
|
return DPM.isActivePasswordSufficient
|
||||||
|
}
|
||||||
|
@RequiresApi(28)
|
||||||
|
fun isUsingUnifiedPassword(): Boolean {
|
||||||
|
return DPM.isUsingUnifiedPassword(DAR)
|
||||||
|
}
|
||||||
|
// Reset password token
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun getRpTokenState(): RpTokenState {
|
||||||
|
return try {
|
||||||
|
RpTokenState(true, DPM.isResetPasswordTokenActive(DAR))
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
RpTokenState(false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun setRpToken(token: String): Boolean {
|
||||||
|
return DPM.setResetPasswordToken(DAR, token.encodeToByteArray())
|
||||||
|
}
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun clearRpToken(): Boolean {
|
||||||
|
return DPM.clearResetPasswordToken(DAR)
|
||||||
|
}
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun createActivateRpTokenIntent(): Intent? {
|
||||||
|
val km = application.getSystemService(KeyguardManager::class.java)
|
||||||
|
val title = application.getString(R.string.activate_reset_password_token)
|
||||||
|
return km.createConfirmDeviceCredentialIntent(title, "")
|
||||||
|
}
|
||||||
|
fun resetPassword(password: String, token: String, flags: Int): Boolean {
|
||||||
|
return if (VERSION.SDK_INT >= 26) {
|
||||||
|
DPM.resetPasswordWithToken(DAR, password, token.encodeToByteArray(), flags)
|
||||||
|
} else {
|
||||||
|
DPM.resetPassword(password, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@RequiresApi(31)
|
||||||
|
fun getRequiredPasswordComplexity(): PasswordComplexity {
|
||||||
|
val complexity = DPM.requiredPasswordComplexity
|
||||||
|
return PasswordComplexity.entries.find { it.id == complexity }!!
|
||||||
|
}
|
||||||
|
@RequiresApi(31)
|
||||||
|
fun setRequiredPasswordComplexity(complexity: PasswordComplexity) {
|
||||||
|
DPM.requiredPasswordComplexity = complexity.id
|
||||||
|
}
|
||||||
|
fun getKeyguardDisableConfig(): KeyguardDisableConfig {
|
||||||
|
val flags = DPM.getKeyguardDisabledFeatures(DAR)
|
||||||
|
val mode = when (flags) {
|
||||||
|
DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE -> KeyguardDisableMode.None
|
||||||
|
DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL -> KeyguardDisableMode.All
|
||||||
|
else -> KeyguardDisableMode.Custom
|
||||||
|
}
|
||||||
|
return KeyguardDisableConfig(mode, flags)
|
||||||
|
}
|
||||||
|
fun setKeyguardDisableConfig(config: KeyguardDisableConfig) {
|
||||||
|
val flags = when (config.mode) {
|
||||||
|
KeyguardDisableMode.None -> DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
|
||||||
|
KeyguardDisableMode.All -> DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
|
||||||
|
else -> config.flags
|
||||||
|
}
|
||||||
|
DPM.setKeyguardDisabledFeatures(DAR, flags)
|
||||||
|
}
|
||||||
|
fun getMaxTimeToLock(): Long {
|
||||||
|
return DPM.getMaximumTimeToLock(DAR)
|
||||||
|
}
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun getRequiredStrongAuthTimeout(): Long {
|
||||||
|
return DPM.getRequiredStrongAuthTimeout(DAR)
|
||||||
|
}
|
||||||
|
fun getPasswordExpirationTimeout(): Long {
|
||||||
|
return DPM.getPasswordExpirationTimeout(DAR)
|
||||||
|
}
|
||||||
|
fun getMaxFailedPasswordsForWipe(): Int {
|
||||||
|
return DPM.getMaximumFailedPasswordsForWipe(DAR)
|
||||||
|
}
|
||||||
|
fun getPasswordHistoryLength(): Int {
|
||||||
|
return DPM.getPasswordHistoryLength(DAR)
|
||||||
|
}
|
||||||
|
fun setMaxTimeToLock(time: Long) {
|
||||||
|
DPM.setMaximumTimeToLock(DAR, time)
|
||||||
|
}
|
||||||
|
@RequiresApi(26)
|
||||||
|
fun setRequiredStrongAuthTimeout(time: Long) {
|
||||||
|
DPM.setRequiredStrongAuthTimeout(DAR, time)
|
||||||
|
}
|
||||||
|
fun setPasswordExpirationTimeout(time: Long) {
|
||||||
|
DPM.setPasswordExpirationTimeout(DAR, time)
|
||||||
|
}
|
||||||
|
fun setMaxFailedPasswordsForWipe(times: Int) {
|
||||||
|
DPM.setMaximumFailedPasswordsForWipe(DAR, times)
|
||||||
|
}
|
||||||
|
fun setPasswordHistoryLength(length: Int) {
|
||||||
|
DPM.setPasswordHistoryLength(DAR, length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ThemeSettings(
|
data class ThemeSettings(
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ object UserRestrictionsRepository {
|
|||||||
)
|
)
|
||||||
|
|
||||||
fun getData(id: String): Pair<Int, List<Restriction>> {
|
fun getData(id: String): Pair<Int, List<Restriction>> {
|
||||||
val category = UserRestrictionCategory.entries.find { it.id == id }!!
|
val category = UserRestrictionCategory.valueOf(id)
|
||||||
return category.title to when (category) {
|
return category.title to when (category) {
|
||||||
UserRestrictionCategory.Network -> network
|
UserRestrictionCategory.Network -> network
|
||||||
UserRestrictionCategory.Connectivity -> connectivity
|
UserRestrictionCategory.Connectivity -> connectivity
|
||||||
@@ -100,11 +100,11 @@ object UserRestrictionsRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class UserRestrictionCategory(val id: String, val title: Int, val icon: Int) {
|
enum class UserRestrictionCategory(val title: Int, val icon: Int) {
|
||||||
Network("network", R.string.network, R.drawable.language_fill0),
|
Network(R.string.network, R.drawable.language_fill0),
|
||||||
Connectivity("connectivity", R.string.connectivity, R.drawable.devices_other_fill0),
|
Connectivity(R.string.connectivity, R.drawable.devices_other_fill0),
|
||||||
Applications("applications", R.string.applications, R.drawable.apps_fill0),
|
Applications(R.string.applications, R.drawable.apps_fill0),
|
||||||
Media("media", R.string.media, R.drawable.volume_up_fill0),
|
Media(R.string.media, R.drawable.volume_up_fill0),
|
||||||
Users("users", R.string.users, R.drawable.manage_accounts_fill0),
|
Users(R.string.users, R.drawable.manage_accounts_fill0),
|
||||||
Other("other", R.string.other, R.drawable.more_horiz_fill0)
|
Other(R.string.other, R.drawable.more_horiz_fill0)
|
||||||
}
|
}
|
||||||
@@ -1346,12 +1346,11 @@ fun NetworkStatsViewerScreen(
|
|||||||
|
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
enum class PrivateDnsMode(val id: Int, val text: Int) {
|
enum class PrivateDnsMode(val id: Int, val text: Int) {
|
||||||
Off(DevicePolicyManager.PRIVATE_DNS_MODE_OFF, R.string.off),
|
|
||||||
Opportunistic(DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, R.string.automatic),
|
Opportunistic(DevicePolicyManager.PRIVATE_DNS_MODE_OPPORTUNISTIC, R.string.automatic),
|
||||||
Host(DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.string.enabled)
|
Host(DevicePolicyManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, R.string.enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PrivateDnsConfiguration(val mode: Int, val host: String)
|
data class PrivateDnsConfiguration(val mode: PrivateDnsMode, val host: String)
|
||||||
|
|
||||||
@Serializable object PrivateDns
|
@Serializable object PrivateDns
|
||||||
|
|
||||||
@@ -1363,11 +1362,11 @@ fun PrivateDnsScreen(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
var mode by remember { mutableStateOf(PrivateDnsMode.Off) }
|
var mode by remember { mutableStateOf(PrivateDnsMode.Opportunistic) }
|
||||||
var inputHost by remember { mutableStateOf("") }
|
var inputHost by remember { mutableStateOf("") }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
val conf = getPrivateDns()
|
val conf = getPrivateDns()
|
||||||
mode = PrivateDnsMode.entries.find { it.id == conf.mode } ?: PrivateDnsMode.Off
|
mode = conf.mode
|
||||||
inputHost = conf.host
|
inputHost = conf.host
|
||||||
}
|
}
|
||||||
MyScaffold(R.string.private_dns, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.private_dns, onNavigateUp, 0.dp) {
|
||||||
@@ -1383,7 +1382,7 @@ fun PrivateDnsScreen(
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
focusMgr.clearFocus()
|
focusMgr.clearFocus()
|
||||||
val result = setPrivateDns(PrivateDnsConfiguration(mode.id, inputHost))
|
val result = setPrivateDns(PrivateDnsConfiguration(mode, inputHost))
|
||||||
context.showOperationResultToast(result)
|
context.showOperationResultToast(result)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)
|
modifier = Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)
|
||||||
|
|||||||
@@ -1,23 +1,8 @@
|
|||||||
package com.bintianqi.owndroid.dpm
|
package com.bintianqi.owndroid.dpm
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.KeyguardManager
|
import android.app.Activity
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS
|
import android.app.admin.DevicePolicyManager
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_IRIS
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS
|
|
||||||
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
|
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH
|
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW
|
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM
|
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE
|
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
|
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
|
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
|
||||||
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK
|
import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK
|
||||||
@@ -28,15 +13,17 @@ 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_DO_NOT_ASK_CREDENTIALS_ON_BOOT
|
||||||
import android.app.admin.DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY
|
import android.app.admin.DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
import android.os.UserManager
|
import android.os.UserManager
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
@@ -63,9 +50,9 @@ import androidx.compose.ui.text.input.ImeAction
|
|||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.bintianqi.owndroid.HorizontalPadding
|
import com.bintianqi.owndroid.HorizontalPadding
|
||||||
|
import com.bintianqi.owndroid.MyViewModel
|
||||||
import com.bintianqi.owndroid.Privilege
|
import com.bintianqi.owndroid.Privilege
|
||||||
import com.bintianqi.owndroid.R
|
import com.bintianqi.owndroid.R
|
||||||
import com.bintianqi.owndroid.SP
|
import com.bintianqi.owndroid.SP
|
||||||
@@ -86,7 +73,7 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
@Composable
|
@Composable
|
||||||
fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
fun PasswordScreen(vm: MyViewModel,onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
||||||
var dialog by remember { mutableIntStateOf(0) }
|
var dialog by remember { mutableIntStateOf(0) }
|
||||||
@@ -119,13 +106,13 @@ fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
|||||||
var input by remember { mutableStateOf("") }
|
var input by remember { mutableStateOf("") }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
input = when (dialog) {
|
input = when (dialog) {
|
||||||
1 -> Privilege.DPM.getMaximumTimeToLock(Privilege.DAR).toString()
|
1 -> vm.getMaxTimeToLock()
|
||||||
2 -> Privilege.DPM.getRequiredStrongAuthTimeout(Privilege.DAR).toString()
|
2 -> vm.getRequiredStrongAuthTimeout()
|
||||||
3 -> Privilege.DPM.getPasswordExpirationTimeout(Privilege.DAR).toString()
|
3 -> vm.getPasswordExpirationTimeout()
|
||||||
4 -> Privilege.DPM.getMaximumFailedPasswordsForWipe(Privilege.DAR).toString()
|
4 -> vm.getMaxFailedPasswordsForWipe()
|
||||||
5 -> Privilege.DPM.getPasswordHistoryLength(Privilege.DAR).toString()
|
5 -> vm.getPasswordHistoryLength()
|
||||||
else -> ""
|
else -> 0
|
||||||
}
|
}.toString()
|
||||||
}
|
}
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
title = {
|
title = {
|
||||||
@@ -178,14 +165,15 @@ fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
when(dialog) {
|
when(dialog) {
|
||||||
1 -> Privilege.DPM.setMaximumTimeToLock(Privilege.DAR, input.toLong())
|
1 -> vm.setMaxTimeToLock(input.toLong())
|
||||||
2 -> Privilege.DPM.setRequiredStrongAuthTimeout(Privilege.DAR, input.toLong())
|
2 -> vm.setRequiredStrongAuthTimeout(input.toLong())
|
||||||
3 -> Privilege.DPM.setPasswordExpirationTimeout(Privilege.DAR, input.toLong())
|
3 -> vm.setPasswordExpirationTimeout(input.toLong())
|
||||||
4 -> Privilege.DPM.setMaximumFailedPasswordsForWipe(Privilege.DAR, input.toInt())
|
4 -> vm.setMaxFailedPasswordsForWipe(input.toInt())
|
||||||
5 -> Privilege.DPM.setPasswordHistoryLength(Privilege.DAR, input.toInt())
|
5 -> vm.setPasswordHistoryLength(input.toInt())
|
||||||
}
|
}
|
||||||
dialog = 0
|
dialog = 0
|
||||||
}
|
},
|
||||||
|
enabled = input.toLongOrNull() != null
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.apply))
|
Text(stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
@@ -202,26 +190,30 @@ fun PasswordScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(29)
|
||||||
|
enum class PasswordComplexity(val id: Int, val text: Int) {
|
||||||
|
None(DevicePolicyManager.PASSWORD_COMPLEXITY_NONE, R.string.none),
|
||||||
|
Low(DevicePolicyManager.PASSWORD_COMPLEXITY_LOW, R.string.low),
|
||||||
|
Medium(DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM, R.string.medium),
|
||||||
|
High(DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH, R.string.high)
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable object PasswordInfo
|
@Serializable object PasswordInfo
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
|
fun PasswordInfoScreen(
|
||||||
|
getComplexity: () -> PasswordComplexity, isSufficient: () -> Boolean, isUnified: () -> Boolean,
|
||||||
|
onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
||||||
var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity
|
var dialog by remember { mutableIntStateOf(0) } // 0:none, 1:password complexity
|
||||||
MyScaffold(R.string.password_info, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.password_info, onNavigateUp, 0.dp) {
|
||||||
if (VERSION.SDK_INT >= 29) {
|
if (VERSION.SDK_INT >= 29) {
|
||||||
val text = when(Privilege.DPM.passwordComplexity) {
|
InfoItem(R.string.current_password_complexity, getComplexity().text, true) { dialog = 1 }
|
||||||
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
|
|
||||||
}
|
}
|
||||||
InfoItem(R.string.current_password_complexity, text, true) { dialog = 1 }
|
InfoItem(R.string.password_sufficient, isSufficient().yesOrNo)
|
||||||
}
|
|
||||||
InfoItem(R.string.password_sufficient, Privilege.DPM.isActivePasswordSufficient.yesOrNo)
|
|
||||||
if(VERSION.SDK_INT >= 28 && privilege.work) {
|
if(VERSION.SDK_INT >= 28 && privilege.work) {
|
||||||
InfoItem(R.string.unified_password, Privilege.DPM.isUsingUnifiedPassword(Privilege.DAR).yesOrNo)
|
InfoItem(R.string.unified_password, isUnified().yesOrNo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(dialog != 0) AlertDialog(
|
if(dialog != 0) AlertDialog(
|
||||||
@@ -235,63 +227,60 @@ fun PasswordInfoScreen(onNavigateUp: () -> Unit) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class RpTokenState(val set: Boolean, val active: Boolean)
|
||||||
|
|
||||||
@Serializable object ResetPasswordToken
|
@Serializable object ResetPasswordToken
|
||||||
|
|
||||||
@RequiresApi(26)
|
@RequiresApi(26)
|
||||||
@Composable
|
@Composable
|
||||||
fun ResetPasswordTokenScreen(onNavigateUp: () -> Unit) {
|
fun ResetPasswordTokenScreen(
|
||||||
|
getState: () -> RpTokenState, setToken: (String) -> Boolean, getIntent: () -> Intent?,
|
||||||
|
clearToken: () -> Boolean, onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var token by remember { mutableStateOf("") }
|
var token by remember { mutableStateOf("") }
|
||||||
val tokenByteArray = token.toByteArray()
|
var state by remember { mutableStateOf(getState()) }
|
||||||
val focusMgr = LocalFocusManager.current
|
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
|
context.popToast(R.string.token_activated)
|
||||||
|
state = getState()
|
||||||
|
}
|
||||||
|
}
|
||||||
MyScaffold(R.string.reset_password_token, onNavigateUp) {
|
MyScaffold(R.string.reset_password_token, onNavigateUp) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = token, onValueChange = { token = it },
|
token, { token = it }, Modifier.fillMaxWidth(),
|
||||||
label = { Text(stringResource(R.string.token)) },
|
label = { Text(stringResource(R.string.token)) },
|
||||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
supportingText = { Text("${token.length}/32") }
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
|
||||||
supportingText = {
|
|
||||||
AnimatedVisibility(tokenByteArray.size < 32) {
|
|
||||||
Text(stringResource(R.string.token_must_longer_than_32_byte))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
)
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
try {
|
val result = setToken(token)
|
||||||
context.showOperationResultToast(Privilege.DPM.setResetPasswordToken(Privilege.DAR, tokenByteArray))
|
context.showOperationResultToast(result)
|
||||||
} catch(_:SecurityException) {
|
if (result) state = getState()
|
||||||
context.popToast(R.string.security_exception)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp),
|
modifier = Modifier.fillMaxWidth().padding(bottom = 10.dp),
|
||||||
enabled = tokenByteArray.size >= 32
|
enabled = token.length >= 32
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.set))
|
Text(stringResource(R.string.set))
|
||||||
}
|
}
|
||||||
Row(
|
if (state.set && !state.active) Button(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = {
|
onClick = {
|
||||||
if(!Privilege.DPM.isResetPasswordTokenActive(Privilege.DAR)) {
|
getIntent()?.let { launcher.launch(it) }
|
||||||
try { activateToken(context) }
|
|
||||||
catch(_:NullPointerException) { context.popToast(R.string.please_set_a_token) }
|
|
||||||
} else { context.popToast(R.string.token_already_activated) }
|
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth(0.49F)
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.activate))
|
Text(stringResource(R.string.activate))
|
||||||
}
|
}
|
||||||
Button(
|
if (state.set) Button(
|
||||||
onClick = { context.showOperationResultToast(Privilege.DPM.clearResetPasswordToken(Privilege.DAR)) },
|
onClick = {
|
||||||
modifier = Modifier.fillMaxWidth(0.96F)
|
val result = clearToken()
|
||||||
|
context.showOperationResultToast(result)
|
||||||
|
state = getState()
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.clear))
|
Text(stringResource(R.string.clear))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
Spacer(Modifier.padding(vertical = 5.dp))
|
||||||
Notes(R.string.activate_token_not_required_when_no_password)
|
Notes(R.string.activate_token_not_required_when_no_password)
|
||||||
}
|
}
|
||||||
@@ -300,147 +289,81 @@ fun ResetPasswordTokenScreen(onNavigateUp: () -> Unit) {
|
|||||||
@Serializable object ResetPassword
|
@Serializable object ResetPassword
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ResetPasswordScreen(onNavigateUp: () -> Unit) {
|
fun ResetPasswordScreen(resetPassword: (String, String, Int) -> Boolean, onNavigateUp: () -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val focusMgr = LocalFocusManager.current
|
|
||||||
var password by remember { mutableStateOf("") }
|
var password by remember { mutableStateOf("") }
|
||||||
var useToken by remember { mutableStateOf(false) }
|
|
||||||
var token by remember { mutableStateOf("") }
|
var token by remember { mutableStateOf("") }
|
||||||
val tokenByteArray = token.toByteArray()
|
var flags by remember { mutableIntStateOf(0) }
|
||||||
var flag by remember { mutableIntStateOf(0) }
|
var confirmPassword by remember { mutableStateOf("") }
|
||||||
var confirmDialog by remember { mutableStateOf(false) }
|
|
||||||
MyScaffold(R.string.reset_password, onNavigateUp) {
|
MyScaffold(R.string.reset_password, onNavigateUp) {
|
||||||
if (VERSION.SDK_INT >= 26) {
|
if (VERSION.SDK_INT >= 26) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = token, onValueChange = { token = it },
|
token, { token = it }, Modifier.fillMaxWidth().padding(bottom = 5.dp),
|
||||||
label = { Text(stringResource(R.string.token)) },
|
label = { Text(stringResource(R.string.token)) },
|
||||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next)
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = password,
|
password, { password = it }, Modifier.fillMaxWidth(),
|
||||||
onValueChange = { password = it },
|
|
||||||
label = { Text(stringResource(R.string.password)) },
|
label = { Text(stringResource(R.string.password)) },
|
||||||
|
isError = password.length in 1..3,
|
||||||
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Next),
|
||||||
|
visualTransformation = PasswordVisualTransformation()
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
confirmPassword, { confirmPassword = it }, Modifier.fillMaxWidth(),
|
||||||
|
label = { Text(stringResource(R.string.confirm_password)) },
|
||||||
|
isError = confirmPassword != password,
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
|
||||||
keyboardActions = KeyboardActions(onDone = { focusMgr.clearFocus() }),
|
visualTransformation = PasswordVisualTransformation()
|
||||||
supportingText = { Text(stringResource(R.string.reset_pwd_desc)) },
|
|
||||||
visualTransformation = PasswordVisualTransformation(),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
)
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
Spacer(Modifier.padding(vertical = 5.dp))
|
||||||
if(VERSION.SDK_INT >= 23) {
|
if(VERSION.SDK_INT >= 23) {
|
||||||
CheckBoxItem(
|
CheckBoxItem(
|
||||||
R.string.do_not_ask_credentials_on_boot,
|
R.string.do_not_ask_credentials_on_boot,
|
||||||
flag and RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT != 0
|
flags and RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT != 0
|
||||||
) { flag = flag xor RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT }
|
) { flags = flags xor RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT }
|
||||||
}
|
}
|
||||||
CheckBoxItem(
|
CheckBoxItem(
|
||||||
R.string.reset_password_require_entry,
|
R.string.reset_password_require_entry,
|
||||||
flag and RESET_PASSWORD_REQUIRE_ENTRY != 0
|
flags and RESET_PASSWORD_REQUIRE_ENTRY != 0
|
||||||
) { flag = flag xor RESET_PASSWORD_REQUIRE_ENTRY }
|
) { flags = flags xor RESET_PASSWORD_REQUIRE_ENTRY }
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
Spacer(Modifier.padding(vertical = 5.dp))
|
||||||
if(VERSION.SDK_INT >= 26) {
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
useToken = true
|
context.showOperationResultToast(resetPassword(password, token, flags))
|
||||||
confirmDialog = true
|
|
||||||
focusMgr.clearFocus()
|
|
||||||
},
|
},
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
colors = ButtonDefaults.buttonColors(colorScheme.error, colorScheme.onError),
|
||||||
enabled = tokenByteArray.size >=32 && password.length !in 1..3,
|
modifier = Modifier.fillMaxWidth(),
|
||||||
modifier = Modifier.fillMaxWidth()
|
enabled = password == confirmPassword
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.reset_password_with_token))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(VERSION.SDK_INT <= 30) {
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
useToken = false
|
|
||||||
confirmDialog = true
|
|
||||||
focusMgr.clearFocus()
|
|
||||||
},
|
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = colorScheme.error, contentColor = colorScheme.onError),
|
|
||||||
enabled = password.length !in 1..3,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.reset_password))
|
Text(stringResource(R.string.reset_password))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Notes(R.string.info_reset_password)
|
Notes(R.string.info_reset_password)
|
||||||
}
|
}
|
||||||
if(confirmDialog) {
|
|
||||||
var confirmPassword by remember { mutableStateOf("") }
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = { confirmDialog = false },
|
|
||||||
title = { Text(stringResource(R.string.reset_password)) },
|
|
||||||
text = {
|
|
||||||
val dialogFocusMgr = LocalFocusManager.current
|
|
||||||
OutlinedTextField(
|
|
||||||
value = confirmPassword,
|
|
||||||
onValueChange = { confirmPassword = it },
|
|
||||||
label = { Text(stringResource(R.string.confirm_password)) },
|
|
||||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password, imeAction = ImeAction.Done),
|
|
||||||
keyboardActions = KeyboardActions(onDone = { dialogFocusMgr.clearFocus() }),
|
|
||||||
visualTransformation = PasswordVisualTransformation(),
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
val success = if(VERSION.SDK_INT >= 26 && useToken) {
|
|
||||||
Privilege.DPM.resetPasswordWithToken(Privilege.DAR, password, tokenByteArray, flag)
|
|
||||||
} else {
|
|
||||||
Privilege.DPM.resetPassword(password, flag)
|
|
||||||
}
|
|
||||||
context.showOperationResultToast(success)
|
|
||||||
password = ""
|
|
||||||
confirmDialog = false
|
|
||||||
},
|
|
||||||
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error),
|
|
||||||
enabled = confirmPassword == password
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.confirm))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(onClick = { confirmDialog = false }) {
|
|
||||||
Text(stringResource(R.string.cancel))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable object RequiredPasswordComplexity
|
@Serializable object RequiredPasswordComplexity
|
||||||
|
|
||||||
@RequiresApi(31)
|
@RequiresApi(31)
|
||||||
@Composable
|
@Composable
|
||||||
fun RequiredPasswordComplexityScreen(onNavigateUp: () -> Unit) {
|
fun RequiredPasswordComplexityScreen(
|
||||||
|
getComplexity: () -> PasswordComplexity, setComplexity: (PasswordComplexity) -> Unit,
|
||||||
|
onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val passwordComplexity = mapOf(
|
var complexity by remember { mutableStateOf(PasswordComplexity.None) }
|
||||||
PASSWORD_COMPLEXITY_NONE to R.string.none,
|
LaunchedEffect(Unit) { complexity = getComplexity() }
|
||||||
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 = Privilege.DPM.requiredPasswordComplexity }
|
|
||||||
MyScaffold(R.string.required_password_complexity, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.required_password_complexity, onNavigateUp, 0.dp) {
|
||||||
passwordComplexity.forEach {
|
PasswordComplexity.entries.forEach {
|
||||||
FullWidthRadioButtonItem(it.value, selectedItem == it.key) { selectedItem = it.key }
|
FullWidthRadioButtonItem(it.text, complexity == it) { complexity = it }
|
||||||
}
|
}
|
||||||
Spacer(Modifier.padding(vertical = 5.dp))
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
Privilege.DPM.requiredPasswordComplexity = selectedItem
|
setComplexity(complexity)
|
||||||
selectedItem = Privilege.DPM.requiredPasswordComplexity
|
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = HorizontalPadding)
|
modifier = Modifier.fillMaxWidth().padding(HorizontalPadding, 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.apply))
|
Text(text = stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
@@ -448,58 +371,63 @@ fun RequiredPasswordComplexityScreen(onNavigateUp: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class KeyguardDisabledFeature(val id: Int, val text: Int, val requiresApi: Int = 0)
|
||||||
|
@Suppress("InlinedApi")
|
||||||
|
val keyguardDisabledFeatures = listOf(
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL, R.string.disable_keyguard_features_widgets),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA, R.string.disable_keyguard_features_camera),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS, R.string.disable_keyguard_features_notification),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS, R.string.disable_keyguard_features_unredacted_notification),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS, R.string.disable_keyguard_features_trust_agents),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT, R.string.disable_keyguard_features_fingerprint),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_FACE, R.string.disable_keyguard_features_face, 28),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_IRIS, R.string.disable_keyguard_features_iris, 28),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_BIOMETRICS, R.string.disable_keyguard_features_biometrics, 28),
|
||||||
|
KeyguardDisabledFeature(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL, R.string.disable_keyguard_features_shortcuts, 34)
|
||||||
|
).filter { VERSION.SDK_INT >= it.requiresApi }
|
||||||
|
|
||||||
|
enum class KeyguardDisableMode(val text: Int) {
|
||||||
|
None(R.string.enable_all), Custom(R.string.custom), All(R.string.disable_all)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class KeyguardDisableConfig(val mode: KeyguardDisableMode, val flags: Int)
|
||||||
|
|
||||||
|
|
||||||
@Serializable object KeyguardDisabledFeatures
|
@Serializable object KeyguardDisabledFeatures
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun KeyguardDisabledFeaturesScreen(onNavigateUp: () -> Unit) {
|
fun KeyguardDisabledFeaturesScreen(
|
||||||
|
getConfig: () -> KeyguardDisableConfig, setConfig: (KeyguardDisableConfig) -> Unit,
|
||||||
|
onNavigateUp: () -> Unit
|
||||||
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var flag by remember { mutableIntStateOf(0) }
|
var mode by remember { mutableStateOf(KeyguardDisableMode.None) }
|
||||||
var mode by remember { mutableIntStateOf(0) } // 0:Enable all, 1:Disable all, 2:Custom
|
var flags by remember { mutableIntStateOf(0) }
|
||||||
val flagsLiat = mutableListOf(
|
LaunchedEffect(Unit) {
|
||||||
R.string.disable_keyguard_features_widgets to KEYGUARD_DISABLE_WIDGETS_ALL,
|
val config = getConfig()
|
||||||
R.string.disable_keyguard_features_camera to KEYGUARD_DISABLE_SECURE_CAMERA,
|
mode = config.mode
|
||||||
R.string.disable_keyguard_features_notification to KEYGUARD_DISABLE_SECURE_NOTIFICATIONS,
|
flags = config.flags
|
||||||
R.string.disable_keyguard_features_unredacted_notification to KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS,
|
|
||||||
R.string.disable_keyguard_features_trust_agents to KEYGUARD_DISABLE_TRUST_AGENTS,
|
|
||||||
R.string.disable_keyguard_features_fingerprint to KEYGUARD_DISABLE_FINGERPRINT
|
|
||||||
)
|
|
||||||
if(VERSION.SDK_INT >= 28) {
|
|
||||||
flagsLiat +=R.string.disable_keyguard_features_face to KEYGUARD_DISABLE_FACE
|
|
||||||
flagsLiat += R.string.disable_keyguard_features_iris to KEYGUARD_DISABLE_IRIS
|
|
||||||
flagsLiat += R.string.disable_keyguard_features_biometrics to KEYGUARD_DISABLE_BIOMETRICS
|
|
||||||
}
|
}
|
||||||
if(VERSION.SDK_INT >= 34) flagsLiat += R.string.disable_keyguard_features_shortcuts to KEYGUARD_DISABLE_SHORTCUTS_ALL
|
|
||||||
fun refresh() {
|
|
||||||
flag = Privilege.DPM.getKeyguardDisabledFeatures(Privilege.DAR)
|
|
||||||
mode = when(flag) {
|
|
||||||
KEYGUARD_DISABLE_FEATURES_NONE -> 0
|
|
||||||
KEYGUARD_DISABLE_FEATURES_ALL -> 1
|
|
||||||
else -> 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LaunchedEffect(mode) { if(mode != 2) flag = Privilege.DPM.getKeyguardDisabledFeatures(Privilege.DAR) }
|
|
||||||
LaunchedEffect(Unit) { refresh() }
|
|
||||||
MyScaffold(R.string.disable_keyguard_features, onNavigateUp) {
|
MyScaffold(R.string.disable_keyguard_features, onNavigateUp) {
|
||||||
FullWidthRadioButtonItem(R.string.enable_all, mode == 0) { mode = 0 }
|
KeyguardDisableMode.entries.forEach {
|
||||||
FullWidthRadioButtonItem(R.string.disable_all, mode == 1) { mode = 1 }
|
FullWidthRadioButtonItem(it.text, mode == it) { mode = it }
|
||||||
FullWidthRadioButtonItem(R.string.custom, mode == 2) { mode = 2 }
|
}
|
||||||
AnimatedVisibility(mode == 2) {
|
Spacer(Modifier.height(8.dp))
|
||||||
|
AnimatedVisibility(mode == KeyguardDisableMode.Custom) {
|
||||||
Column {
|
Column {
|
||||||
flagsLiat.forEach {
|
keyguardDisabledFeatures.forEach {
|
||||||
FullWidthCheckBoxItem(it.first, flag and it.second == it.second) { checked ->
|
FullWidthCheckBoxItem(it.text, flags and it.id == it.id) { checked ->
|
||||||
flag = if(checked) flag or it.second else flag and (flag xor it.second)
|
flags = flags xor it.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
val disabledFeatures = if(mode == 0) KEYGUARD_DISABLE_FEATURES_NONE else if(mode == 1) KEYGUARD_DISABLE_FEATURES_ALL else flag
|
setConfig(KeyguardDisableConfig(mode, flags))
|
||||||
Privilege.DPM.setKeyguardDisabledFeatures(Privilege.DAR, disabledFeatures)
|
|
||||||
refresh()
|
|
||||||
context.showOperationResultToast(true)
|
context.showOperationResultToast(true)
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp, horizontal = HorizontalPadding)
|
modifier = Modifier.fillMaxWidth().padding(HorizontalPadding, 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.apply))
|
Text(text = stringResource(R.string.apply))
|
||||||
}
|
}
|
||||||
@@ -538,14 +466,3 @@ fun RequiredPasswordQualityScreen(onNavigateUp: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activateToken(context: Context) {
|
|
||||||
val desc = context.getString(R.string.activate_reset_password_token_here)
|
|
||||||
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
|
||||||
val confirmIntent = keyguardManager.createConfirmDeviceCredentialIntent(context.getString(R.string.app_name), desc)
|
|
||||||
if (confirmIntent != null) {
|
|
||||||
startActivity(context, confirmIntent, null)
|
|
||||||
} else {
|
|
||||||
context.showOperationResultToast(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -146,9 +146,6 @@ fun SystemManagerScreen(
|
|||||||
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
val privilege by Privilege.status.collectAsStateWithLifecycle()
|
||||||
/** 1: reboot, 2: bug report, 3: org name, 4: org id, 5: enrollment specific id*/
|
/** 1: reboot, 2: bug report, 3: org name, 4: org id, 5: enrollment specific id*/
|
||||||
var dialog by remember { mutableIntStateOf(0) }
|
var dialog by remember { mutableIntStateOf(0) }
|
||||||
var enrollmentSpecificId by remember {
|
|
||||||
mutableStateOf(if (VERSION.SDK_INT >= 31 && (privilege.device || privilege.profile)) Privilege.DPM.enrollmentSpecificId else "")
|
|
||||||
}
|
|
||||||
MyScaffold(R.string.system, onNavigateUp, 0.dp) {
|
MyScaffold(R.string.system, onNavigateUp, 0.dp) {
|
||||||
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SystemOptions) }
|
FunctionItem(R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SystemOptions) }
|
||||||
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) }
|
FunctionItem(R.string.keyguard, icon = R.drawable.screen_lock_portrait_fill0) { onNavigate(Keyguard) }
|
||||||
@@ -195,7 +192,7 @@ fun SystemManagerScreen(
|
|||||||
if(VERSION.SDK_INT >= 31) {
|
if(VERSION.SDK_INT >= 31) {
|
||||||
FunctionItem(R.string.org_id, icon = R.drawable.corporate_fare_fill0) { dialog = 4 }
|
FunctionItem(R.string.org_id, icon = R.drawable.corporate_fare_fill0) { dialog = 4 }
|
||||||
}
|
}
|
||||||
if(enrollmentSpecificId != "") {
|
if (VERSION.SDK_INT >= 31) {
|
||||||
FunctionItem(R.string.enrollment_specific_id, icon = R.drawable.id_card_fill0) { dialog = 5 }
|
FunctionItem(R.string.enrollment_specific_id, icon = R.drawable.id_card_fill0) { dialog = 5 }
|
||||||
}
|
}
|
||||||
if(VERSION.SDK_INT >= 24 && (privilege.device || privilege.org)) {
|
if(VERSION.SDK_INT >= 24 && (privilege.device || privilege.org)) {
|
||||||
@@ -249,7 +246,10 @@ fun SystemManagerScreen(
|
|||||||
text = {
|
text = {
|
||||||
val focusMgr = LocalFocusManager.current
|
val focusMgr = LocalFocusManager.current
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (dialog == 5 && VERSION.SDK_INT >= 31) input = vm.getEnrollmentSpecificId()
|
if (dialog == 5 && VERSION.SDK_INT >= 31) {
|
||||||
|
val id = vm.getEnrollmentSpecificId()
|
||||||
|
input = id.ifEmpty { context.getString(R.string.none) }
|
||||||
|
}
|
||||||
if (dialog == 3 && VERSION.SDK_INT >= 24) input = vm.getOrgName()
|
if (dialog == 3 && VERSION.SDK_INT >= 24) input = vm.getOrgName()
|
||||||
}
|
}
|
||||||
Column {
|
Column {
|
||||||
@@ -290,7 +290,6 @@ fun SystemManagerScreen(
|
|||||||
if (dialog == 3 && VERSION.SDK_INT >= 24) vm.setOrgName(input)
|
if (dialog == 3 && VERSION.SDK_INT >= 24) vm.setOrgName(input)
|
||||||
if (dialog == 4 && VERSION.SDK_INT >= 31) {
|
if (dialog == 4 && VERSION.SDK_INT >= 31) {
|
||||||
context.showOperationResultToast(vm.setOrgId(input))
|
context.showOperationResultToast(vm.setOrgId(input))
|
||||||
enrollmentSpecificId = vm.getEnrollmentSpecificId()
|
|
||||||
}
|
}
|
||||||
dialog = 0
|
dialog = 0
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ fun UserRestrictionScreen(
|
|||||||
Spacer(Modifier.padding(vertical = 2.dp))
|
Spacer(Modifier.padding(vertical = 2.dp))
|
||||||
UserRestrictionCategory.entries.forEach {
|
UserRestrictionCategory.entries.forEach {
|
||||||
FunctionItem(it.title, icon = it.icon) {
|
FunctionItem(it.title, icon = it.icon) {
|
||||||
onNavigate(UserRestrictionOptions(it.id))
|
onNavigate(UserRestrictionOptions(it.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -493,7 +493,7 @@
|
|||||||
<string name="reset_password_token">Сбросить токен пароля</string>
|
<string name="reset_password_token">Сбросить токен пароля</string>
|
||||||
<string name="token">Токен</string>
|
<string name="token">Токен</string>
|
||||||
<string name="token_must_longer_than_32_byte">Токен должен быть длиннее 32 байт</string>
|
<string name="token_must_longer_than_32_byte">Токен должен быть длиннее 32 байт</string>
|
||||||
<string name="token_already_activated">Токен уже активирован</string>
|
<string name="token_activated">Тoken activated</string><!--TODO-->
|
||||||
<string name="clear">Очистить</string>
|
<string name="clear">Очистить</string>
|
||||||
<string name="set">Установить</string>
|
<string name="set">Установить</string>
|
||||||
<string name="please_set_a_token">Установите токен</string>
|
<string name="please_set_a_token">Установите токен</string>
|
||||||
@@ -524,7 +524,6 @@
|
|||||||
<string name="password_quality_biometrics_weak">Биометрия (слабая)</string>
|
<string name="password_quality_biometrics_weak">Биометрия (слабая)</string>
|
||||||
<string name="password_quality_numeric_complex">Сложный числовой (без повторений)</string>
|
<string name="password_quality_numeric_complex">Сложный числовой (без повторений)</string>
|
||||||
<string name="required_password_quality">Требуемое качество пароля</string>
|
<string name="required_password_quality">Требуемое качество пароля</string>
|
||||||
<string name="activate_reset_password_token_here">Активируйте токен сброса пароля здесь.</string>
|
|
||||||
|
|
||||||
|
|
||||||
<!--Настройки и о программе-->
|
<!--Настройки и о программе-->
|
||||||
@@ -631,8 +630,6 @@
|
|||||||
<string name="info_wipe_data_in_managed_user">Все данные этого пользователя будут стерты, но сам пользователь не будет удален.</string>
|
<string name="info_wipe_data_in_managed_user">Все данные этого пользователя будут стерты, но сам пользователь не будет удален.</string>
|
||||||
<string name="info_lockdown_admin_configured_network">Контролировать, может ли пользователь изменять сети, настроенные администратором.\nКогда эта блокировка включена, пользователь по-прежнему может настраивать другие сети Wi-Fi и подключаться к ним, а также использовать другие возможности Wi-Fi, такие как режим модема.</string>
|
<string name="info_lockdown_admin_configured_network">Контролировать, может ли пользователь изменять сети, настроенные администратором.\nКогда эта блокировка включена, пользователь по-прежнему может настраивать другие сети Wi-Fi и подключаться к ним, а также использовать другие возможности Wi-Fi, такие как режим модема.</string>
|
||||||
<string name="info_minimum_wifi_security_level">Указать минимальный уровень безопасности, требуемый для сетей Wi-Fi. Устройство может не подключаться к сетям, которые не соответствуют минимальному уровню безопасности. Если текущая сеть не соответствует установленному минимальному уровню безопасности, соединение будет разорвано.</string>
|
<string name="info_minimum_wifi_security_level">Указать минимальный уровень безопасности, требуемый для сетей Wi-Fi. Устройство может не подключаться к сетям, которые не соответствуют минимальному уровню безопасности. Если текущая сеть не соответствует установленному минимальному уровню безопасности, соединение будет разорвано.</string>
|
||||||
<string name="info_private_dns_mode_oppertunistic">В этом режиме подсистема DNS попытается установить TLS-соединение с DNS-сервером, предоставленным сетью, прежде чем пытаться разрешить имена в открытом виде.</string>
|
|
||||||
<string name="info_set_private_dns_host">Будет выполнена проверка соединения с DNS-сервером, чтобы убедиться в его работоспособности.\nВ случае использования VPN совместно с частным DNS-сервером, частный DNS-сервер должен быть доступен как изнутри, так и снаружи VPN. В противном случае устройство может потерять возможность разрешать имена хостов, так как системный трафик к DNS-серверу может не проходить через VPN.</string>
|
|
||||||
<string name="info_always_on_vpn">Настроить постоянное VPN-соединение через определенное приложение для текущего пользователя. Это соединение автоматически предоставляется и сохраняется после перезагрузки.\nВключить блокировку: Запретить сетевое подключение, когда VPN не подключен.</string>
|
<string name="info_always_on_vpn">Настроить постоянное VPN-соединение через определенное приложение для текущего пользователя. Это соединение автоматически предоставляется и сохраняется после перезагрузки.\nВключить блокировку: Запретить сетевое подключение, когда VPN не подключен.</string>
|
||||||
<string name="info_recommended_global_proxy">Этот прокси-сервер является только рекомендацией, и некоторые приложения могут его игнорировать.</string>
|
<string name="info_recommended_global_proxy">Этот прокси-сервер является только рекомендацией, и некоторые приложения могут его игнорировать.</string>
|
||||||
<string name="info_network_log">Журналы сети содержат события поиска DNS и вызовы библиотеки connect().\nИспользование этой функции в рабочем профиле позволит получить журналы сети только в рабочем профиле.\nНа этом устройстве не должно быть неаффилированных пользователей, если оно используется владельцем устройства.</string>
|
<string name="info_network_log">Журналы сети содержат события поиска DNS и вызовы библиотеки connect().\nИспользование этой функции в рабочем профиле позволит получить журналы сети только в рабочем профиле.\nНа этом устройстве не должно быть неаффилированных пользователей, если оно используется владельцем устройства.</string>
|
||||||
|
|||||||
@@ -517,7 +517,7 @@
|
|||||||
<string name="reset_password_token">Parola Sıfırlama Jetonu</string>
|
<string name="reset_password_token">Parola Sıfırlama Jetonu</string>
|
||||||
<string name="token">Jeton</string>
|
<string name="token">Jeton</string>
|
||||||
<string name="token_must_longer_than_32_byte">Jeton 32 bayttan uzun olmalıdır</string>
|
<string name="token_must_longer_than_32_byte">Jeton 32 bayttan uzun olmalıdır</string>
|
||||||
<string name="token_already_activated">Jeton zaten etkinleştirildi</string>
|
<string name="token_activated">Token activated</string><!--TODO-->
|
||||||
<string name="clear">Temizle</string>
|
<string name="clear">Temizle</string>
|
||||||
<string name="set">Ayarla</string>
|
<string name="set">Ayarla</string>
|
||||||
<string name="please_set_a_token">Lütfen bir jeton ayarlayın</string>
|
<string name="please_set_a_token">Lütfen bir jeton ayarlayın</string>
|
||||||
@@ -548,7 +548,6 @@
|
|||||||
<string name="password_quality_biometrics_weak">Biyometrik (Zayıf)</string>
|
<string name="password_quality_biometrics_weak">Biyometrik (Zayıf)</string>
|
||||||
<string name="password_quality_numeric_complex">Sayısal karmaşık (tekrar eden karakterler olmadan)</string>
|
<string name="password_quality_numeric_complex">Sayısal karmaşık (tekrar eden karakterler olmadan)</string>
|
||||||
<string name="required_password_quality">Gerekli Parola Kalitesi</string>
|
<string name="required_password_quality">Gerekli Parola Kalitesi</string>
|
||||||
<string name="activate_reset_password_token_here">Parola sıfırlama jetonunu burada etkinleştir.</string>
|
|
||||||
|
|
||||||
<!--Settings&About-->
|
<!--Settings&About-->
|
||||||
<string name="settings">Ayarlar</string>
|
<string name="settings">Ayarlar</string>
|
||||||
|
|||||||
@@ -505,7 +505,8 @@
|
|||||||
<string name="reset_password_token">密码重置令牌</string>
|
<string name="reset_password_token">密码重置令牌</string>
|
||||||
<string name="token">令牌</string>
|
<string name="token">令牌</string>
|
||||||
<string name="token_must_longer_than_32_byte">令牌必须大于32字节</string>
|
<string name="token_must_longer_than_32_byte">令牌必须大于32字节</string>
|
||||||
<string name="token_already_activated">令牌已经激活</string>
|
<string name="activate_reset_password_token">激活密码重置令牌</string>
|
||||||
|
<string name="token_activated">令牌已激活</string>
|
||||||
<string name="clear">清除</string>
|
<string name="clear">清除</string>
|
||||||
<string name="set">设置</string>
|
<string name="set">设置</string>
|
||||||
<string name="please_set_a_token">请先设置令牌</string>
|
<string name="please_set_a_token">请先设置令牌</string>
|
||||||
@@ -525,7 +526,7 @@
|
|||||||
<string name="disable_keyguard_features_trust_agents">禁用可信代理</string>
|
<string name="disable_keyguard_features_trust_agents">禁用可信代理</string>
|
||||||
<string name="disable_keyguard_features_fingerprint">禁用指纹解锁</string>
|
<string name="disable_keyguard_features_fingerprint">禁用指纹解锁</string>
|
||||||
<string name="disable_keyguard_features_face">禁用人脸解锁</string>
|
<string name="disable_keyguard_features_face">禁用人脸解锁</string>
|
||||||
<string name="disable_keyguard_features_iris">禁用虹膜解锁(?)</string>
|
<string name="disable_keyguard_features_iris">禁用虹膜解锁</string>
|
||||||
<string name="disable_keyguard_features_biometrics">禁用生物识别</string>
|
<string name="disable_keyguard_features_biometrics">禁用生物识别</string>
|
||||||
<string name="disable_keyguard_features_shortcuts">禁用快捷方式</string>
|
<string name="disable_keyguard_features_shortcuts">禁用快捷方式</string>
|
||||||
<string name="password_quality_unspecified">未指定</string>
|
<string name="password_quality_unspecified">未指定</string>
|
||||||
@@ -536,7 +537,6 @@
|
|||||||
<string name="password_quality_biometrics_weak">生物识别(弱)</string>
|
<string name="password_quality_biometrics_weak">生物识别(弱)</string>
|
||||||
<string name="password_quality_numeric_complex">复杂数字(无连续性)</string>
|
<string name="password_quality_numeric_complex">复杂数字(无连续性)</string>
|
||||||
<string name="required_password_quality">密码质量要求</string>
|
<string name="required_password_quality">密码质量要求</string>
|
||||||
<string name="activate_reset_password_token_here">在这里激活密码重置令牌</string>
|
|
||||||
|
|
||||||
<!--Settings&About-->
|
<!--Settings&About-->
|
||||||
<string name="settings">设置</string>
|
<string name="settings">设置</string>
|
||||||
|
|||||||
@@ -540,8 +540,9 @@
|
|||||||
<string name="unified_password">Unified password</string>
|
<string name="unified_password">Unified password</string>
|
||||||
<string name="reset_password_token">Reset password token</string>
|
<string name="reset_password_token">Reset password token</string>
|
||||||
<string name="token">Token</string>
|
<string name="token">Token</string>
|
||||||
<string name="token_must_longer_than_32_byte">The token must be longer than 32-byte</string>
|
<string name="token_must_longer_than_32_byte">The token must be longer than 32 byte</string>
|
||||||
<string name="token_already_activated">Token already activated</string>
|
<string name="activate_reset_password_token">Activate reset password token</string>
|
||||||
|
<string name="token_activated">Token activated</string>
|
||||||
<string name="clear">Clear</string>
|
<string name="clear">Clear</string>
|
||||||
<string name="set">Set</string>
|
<string name="set">Set</string>
|
||||||
<string name="please_set_a_token">Please set a token</string>
|
<string name="please_set_a_token">Please set a token</string>
|
||||||
@@ -572,7 +573,6 @@
|
|||||||
<string name="password_quality_biometrics_weak">Biometrics (Weak)</string>
|
<string name="password_quality_biometrics_weak">Biometrics (Weak)</string>
|
||||||
<string name="password_quality_numeric_complex">Numeric complex (No repeating characters)</string>
|
<string name="password_quality_numeric_complex">Numeric complex (No repeating characters)</string>
|
||||||
<string name="required_password_quality">Required password quality</string>
|
<string name="required_password_quality">Required password quality</string>
|
||||||
<string name="activate_reset_password_token_here">Activate reset password token here. </string>
|
|
||||||
|
|
||||||
<!--Settings&About-->
|
<!--Settings&About-->
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user