Fix CA certs manager and User restrictions manager

Add authentication to app installer
Change version to 6.4 (36)
This commit is contained in:
BinTianqi
2025-02-08 19:07:23 +08:00
parent 5e109d74b1
commit 9528d3eb8d
9 changed files with 119 additions and 112 deletions

View File

@@ -15,7 +15,7 @@ android {
} }
} }
namespace = "com.bintianqi.owndroid" namespace = "com.bintianqi.owndroid"
compileSdk = 34 compileSdk = 35
lint.checkReleaseBuilds = false lint.checkReleaseBuilds = false
lint.disable += "All" lint.disable += "All"
@@ -24,9 +24,8 @@ android {
applicationId = "com.bintianqi.owndroid" applicationId = "com.bintianqi.owndroid"
minSdk = 21 minSdk = 21
targetSdk = 35 targetSdk = 35
compileSdk = 35 versionCode = 36
versionCode = 35 versionName = "6.4"
versionName = "6.3"
multiDexEnabled = false multiDexEnabled = false
} }

View File

@@ -10,12 +10,13 @@ import android.content.pm.PackageInstaller
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.biometric.BiometricPrompt
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -49,6 +50,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -63,7 +65,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.net.URLDecoder import java.net.URLDecoder
class AppInstallerActivity:ComponentActivity() { class AppInstallerActivity:FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge() enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -82,7 +84,7 @@ class AppInstallerActivity:ComponentActivity() {
installing, sessionMode, { vm.sessionMode.value = it }, installing, sessionMode, { vm.sessionMode.value = it },
packages, { uri -> vm.packages.update { it.minus(uri) } }, packages, { uri -> vm.packages.update { it.minus(uri) } },
{ uris -> vm.packages.update { it.plus(uris) } }, { uris -> vm.packages.update { it.plus(uris) } },
vm::startInstallationProcess, writtenPackages, writingPackage, { vm.startInstallationProcess(this) }, writtenPackages, writingPackage,
result, { vm.result.value = null } result, { vm.result.value = null }
) )
} }
@@ -237,7 +239,19 @@ class AppInstallerViewModel(application: Application): AndroidViewModel(applicat
val writtenPackages = MutableStateFlow(setOf<Uri>()) val writtenPackages = MutableStateFlow(setOf<Uri>())
val writingPackage = MutableStateFlow<Uri?>(null) val writingPackage = MutableStateFlow<Uri?>(null)
fun startInstallationProcess() { fun startInstallationProcess(activity: FragmentActivity) {
startAuth(activity, object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
startInstall()
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Toast.makeText(activity, R.string.failed_to_authenticate, Toast.LENGTH_SHORT).show()
}
})
}
private fun startInstall() {
if(installing.value) return if(installing.value) return
installing.value = true installing.value = true
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {

View File

@@ -1,5 +1,6 @@
package com.bintianqi.owndroid package com.bintianqi.owndroid
import android.annotation.SuppressLint
import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager
import android.os.Build.VERSION import android.os.Build.VERSION
import android.os.Bundle import android.os.Bundle
@@ -53,6 +54,7 @@ import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
@@ -119,7 +121,7 @@ import com.bintianqi.owndroid.dpm.UpdateNetwork
import com.bintianqi.owndroid.dpm.UserOperation import com.bintianqi.owndroid.dpm.UserOperation
import com.bintianqi.owndroid.dpm.UserOptions import com.bintianqi.owndroid.dpm.UserOptions
import com.bintianqi.owndroid.dpm.UserRestriction import com.bintianqi.owndroid.dpm.UserRestriction
import com.bintianqi.owndroid.dpm.UserRestrictionItem import com.bintianqi.owndroid.dpm.UserRestrictionScreen
import com.bintianqi.owndroid.dpm.UserSessionMessage import com.bintianqi.owndroid.dpm.UserSessionMessage
import com.bintianqi.owndroid.dpm.Users import com.bintianqi.owndroid.dpm.Users
import com.bintianqi.owndroid.dpm.Wifi import com.bintianqi.owndroid.dpm.Wifi
@@ -137,7 +139,6 @@ import com.bintianqi.owndroid.dpm.isDeviceOwner
import com.bintianqi.owndroid.dpm.isProfileOwner import com.bintianqi.owndroid.dpm.isProfileOwner
import com.bintianqi.owndroid.dpm.setDefaultAffiliationID import com.bintianqi.owndroid.dpm.setDefaultAffiliationID
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.MyScaffold
import com.bintianqi.owndroid.ui.theme.OwnDroidTheme import com.bintianqi.owndroid.ui.theme.OwnDroidTheme
import com.rosan.dhizuku.api.Dhizuku import com.rosan.dhizuku.api.Dhizuku
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -196,6 +197,17 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
LaunchedEffect(backToHome) { LaunchedEffect(backToHome) {
if(backToHome) { navCtrl.navigateUp(); backToHomeStateFlow.value = false } if(backToHome) { navCtrl.navigateUp(); backToHomeStateFlow.value = false }
} }
val userRestrictions by vm.userRestrictions.collectAsStateWithLifecycle()
fun onUserRestrictionsChange(id: String, status: Boolean) {
try {
if(status) dpm.addUserRestriction(receiver, id)
else dpm.clearUserRestriction(receiver, id)
@SuppressLint("NewApi")
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
} catch(_: Exception) {
context.showOperationResultToast(false)
}
}
@Suppress("NewApi") NavHost( @Suppress("NewApi") NavHost(
navController = navCtrl, navController = navCtrl,
startDestination = "HomePage", startDestination = "HomePage",
@@ -268,24 +280,36 @@ fun Home(activity: FragmentActivity, vm: MyViewModel) {
composable(route = "Applications") { ApplicationManage(navCtrl, vm) } composable(route = "Applications") { ApplicationManage(navCtrl, vm) }
composable(route = "UserRestriction") { UserRestriction(navCtrl) } composable(route = "UserRestriction") { UserRestriction(navCtrl, vm) }
composable(route = "UR-Internet") { composable(route = "UR-Internet") {
MyScaffold(R.string.network_and_internet, 0.dp, navCtrl) { RestrictionData.internet.forEach { UserRestrictionItem(it, vm) } } UserRestrictionScreen(R.string.network_and_internet, RestrictionData.internet, userRestrictions, ::onUserRestrictionsChange) {
navCtrl.navigateUp()
}
} }
composable(route = "UR-Connectivity") { composable(route = "UR-Connectivity") {
MyScaffold(R.string.connectivity, 0.dp, navCtrl) { RestrictionData.connectivity.forEach { UserRestrictionItem(it, vm) } } UserRestrictionScreen(R.string.connectivity, RestrictionData.connectivity, userRestrictions, ::onUserRestrictionsChange) {
navCtrl.navigateUp()
}
} }
composable(route = "UR-Applications") { composable(route = "UR-Applications") {
MyScaffold(R.string.applications, 0.dp, navCtrl) { RestrictionData.applications.forEach { UserRestrictionItem(it, vm) } } UserRestrictionScreen(R.string.applications, RestrictionData.applications, userRestrictions, ::onUserRestrictionsChange) {
navCtrl.navigateUp()
}
} }
composable(route = "UR-Users") { composable(route = "UR-Users") {
MyScaffold(R.string.users, 0.dp, navCtrl) { RestrictionData.users.forEach { UserRestrictionItem(it, vm) } } UserRestrictionScreen(R.string.users, RestrictionData.users, userRestrictions, ::onUserRestrictionsChange) {
navCtrl.navigateUp()
}
} }
composable(route = "UR-Media") { composable(route = "UR-Media") {
MyScaffold(R.string.media, 0.dp, navCtrl) { RestrictionData.media.forEach { UserRestrictionItem(it, vm) } } UserRestrictionScreen(R.string.media, RestrictionData.media, userRestrictions, ::onUserRestrictionsChange) {
navCtrl.navigateUp()
}
} }
composable(route = "UR-Other") { composable(route = "UR-Other") {
MyScaffold(R.string.other, 0.dp, navCtrl) { RestrictionData.other.forEach { UserRestrictionItem(it, vm) } } UserRestrictionScreen(R.string.other, RestrictionData.other, userRestrictions, ::onUserRestrictionsChange) {
navCtrl.navigateUp()
}
} }
composable(route = "Users") { Users(navCtrl) } composable(route = "Users") { Users(navCtrl) }

View File

@@ -137,7 +137,6 @@ import com.bintianqi.owndroid.ui.NavIcon
import com.bintianqi.owndroid.ui.RadioButtonItem import com.bintianqi.owndroid.ui.RadioButtonItem
import com.bintianqi.owndroid.ui.SwitchItem import com.bintianqi.owndroid.ui.SwitchItem
import com.bintianqi.owndroid.uriToStream import com.bintianqi.owndroid.uriToStream
import com.bintianqi.owndroid.yesOrNo
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
@@ -1227,63 +1226,24 @@ fun CACert(navCtrl: NavHostController) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
var exist by remember { mutableStateOf(false) } var dialog by remember { mutableStateOf(false) }
var fileUri by remember { mutableStateOf<Uri?>(null) } var caCertByteArray = remember { byteArrayOf() }
var caCertByteArray by remember { mutableStateOf(ByteArray(100000)) } val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
val getFileLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> uri ?: return@rememberLauncherForActivityResult
result.data?.data?.let { uri ->
uriToStream(context, uri) { uriToStream(context, uri) {
val array = it.readBytes() caCertByteArray = it.readBytes()
caCertByteArray = if(array.size < 10000) {
array
} else {
byteArrayOf()
}
}
} }
dialog = true
} }
MyScaffold(R.string.ca_cert, 8.dp, navCtrl) { MyScaffold(R.string.ca_cert, 8.dp, navCtrl) {
Text(
text = if(fileUri == null) { stringResource(R.string.please_select_ca_cert) }
else { stringResource(R.string.cert_installed, stringResource(exist.yesOrNo)) },
modifier = Modifier.animateContentSize()
)
Spacer(Modifier.padding(vertical = 5.dp))
Button( Button(
onClick = { onClick = {
val caCertIntent = Intent(Intent.ACTION_GET_CONTENT) getFileLauncher.launch("*/*")
caCertIntent.setType("*/*")
caCertIntent.addCategory(Intent.CATEGORY_OPENABLE)
getFileLauncher.launch(caCertIntent)
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)
) { ) {
Text(stringResource(R.string.select_ca_cert)) Text(stringResource(R.string.select_ca_cert))
} }
AnimatedVisibility(fileUri != null) {
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Button(
onClick = {
context.showOperationResultToast(dpm.installCaCert(receiver, caCertByteArray))
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
},
modifier = Modifier.fillMaxWidth(0.49F)
) {
Text(stringResource(R.string.install))
}
Button(
onClick = {
dpm.uninstallCaCert(receiver, caCertByteArray)
exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
context.showOperationResultToast(true)
},
enabled = exist,
modifier = Modifier.fillMaxWidth(0.96F)
) {
Text(stringResource(R.string.uninstall))
}
}
}
Button( Button(
onClick = { onClick = {
dpm.uninstallAllUserCaCerts(receiver) dpm.uninstallAllUserCaCerts(receiver)
@@ -1293,6 +1253,28 @@ fun CACert(navCtrl: NavHostController) {
) { ) {
Text(stringResource(R.string.uninstall_all_user_ca_cert)) Text(stringResource(R.string.uninstall_all_user_ca_cert))
} }
if(dialog) {
val exist = dpm.hasCaCertInstalled(receiver, caCertByteArray)
AlertDialog(
confirmButton = {
TextButton({
if(exist) {
dpm.uninstallCaCert(receiver, caCertByteArray)
} else {
val result = dpm.installCaCert(receiver, caCertByteArray)
context.showOperationResultToast(result)
}
dialog = false
}) {
Text(stringResource(if(exist) R.string.uninstall else R.string.install))
}
},
dismissButton = {
TextButton({ dialog = false }) { Text(stringResource(R.string.cancel)) }
},
onDismissRequest = { dialog = false }
)
}
} }
} }

View File

@@ -1,22 +1,20 @@
package com.bintianqi.owndroid.dpm package com.bintianqi.owndroid.dpm
import android.os.Build.VERSION import android.os.Build
import android.os.Bundle
import android.os.UserManager import android.os.UserManager
import android.widget.Toast
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.bintianqi.owndroid.MyViewModel import com.bintianqi.owndroid.MyViewModel
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
@@ -31,15 +29,19 @@ data class Restriction(
val requiresApi: Int = 0 val requiresApi: Int = 0
) )
@RequiresApi(24)
@Composable @Composable
fun UserRestriction(navCtrl:NavHostController) { fun UserRestriction(navCtrl:NavHostController, vm: MyViewModel) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
LaunchedEffect(Unit) {
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
}
MyScaffold(R.string.user_restriction, 0.dp, navCtrl) { MyScaffold(R.string.user_restriction, 0.dp, navCtrl) {
Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp)) Text(text = stringResource(R.string.switch_to_disable_feature), modifier = Modifier.padding(start = 16.dp))
if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) } if(context.isProfileOwner) { Text(text = stringResource(R.string.profile_owner_is_restricted), modifier = Modifier.padding(start = 16.dp)) }
if(context.isProfileOwner && (VERSION.SDK_INT < 24 || dpm.isManagedProfile(receiver))) { if(context.isProfileOwner && dpm.isManagedProfile(receiver)) {
Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp)) Text(text = stringResource(R.string.some_features_invalid_in_work_profile), modifier = Modifier.padding(start = 16.dp))
} }
Spacer(Modifier.padding(vertical = 2.dp)) Spacer(Modifier.padding(vertical = 2.dp))
@@ -54,30 +56,19 @@ fun UserRestriction(navCtrl:NavHostController) {
@RequiresApi(24) @RequiresApi(24)
@Composable @Composable
fun UserRestrictionItem(restriction: Restriction, vm: MyViewModel) { fun UserRestrictionScreen(
val context = LocalContext.current title: Int, items: List<Restriction>, restrictions: Bundle,
val userRestrictions by vm.userRestrictions.collectAsStateWithLifecycle() onRestrictionChange: (String, Boolean) -> Unit, onNavigateUp: () -> Unit
Box(modifier = Modifier.padding(start = 22.dp, end = 16.dp)) { ) {
MyScaffold(title, 0.dp, onNavigateUp, false) {
items.filter { Build.VERSION.SDK_INT >= it.requiresApi }.forEach { restriction ->
SwitchItem( SwitchItem(
restriction.name, restriction.id, restriction.icon, restriction.name, restriction.id, restriction.icon,
userRestrictions.getBoolean(restriction.id), restrictions.getBoolean(restriction.id), { onRestrictionChange(restriction.id, it) }, padding = true
{
val dpm = context.getDPM()
val receiver = context.getReceiver()
try {
if(it) {
dpm.addUserRestriction(receiver, restriction.id)
} else {
dpm.clearUserRestriction(receiver, restriction.id)
}
vm.userRestrictions.value = dpm.getUserRestrictions(receiver)
} catch(_: SecurityException) {
if(context.isProfileOwner) {
Toast.makeText(context, R.string.require_device_owner, Toast.LENGTH_SHORT).show()
}
}
}, padding = false
) )
/*Box(modifier = Modifier.padding(start = 22.dp, end = 16.dp)) {
}*/
}
} }
} }

View File

@@ -5,6 +5,7 @@
<string name="disable">Отключить</string> <string name="disable">Отключить</string>
<string name="enable">Включить</string> <string name="enable">Включить</string>
<string name="success">Успешно</string> <string name="success">Успешно</string>
<string name="failure">Failure</string> <!--TODO-->
<string name="failed">Ошибка</string> <string name="failed">Ошибка</string>
<string name="add">Добавить</string> <string name="add">Добавить</string>
<string name="remove">Удалить</string> <string name="remove">Удалить</string>
@@ -69,7 +70,7 @@
<string name="overview">Обзор</string> <string name="overview">Обзор</string>
<string name="features">Особенности</string> <string name="features">Особенности</string>
<string name="default_str">По умолчанию</string> <string name="default_str">По умолчанию</string>
<string name="timeout">Timeout</string> <!--TODO-->
<!--Разрешения--> <!--Разрешения-->
<string name="click_to_activate">Нажмите для активации</string> <string name="click_to_activate">Нажмите для активации</string>
@@ -194,8 +195,6 @@
<string name="ltf_keyguard">Разрешить блокировку экрана</string> <string name="ltf_keyguard">Разрешить блокировку экрана</string>
<string name="ltf_block_activity_start_in_task">Блокировать запуск активности в задаче</string> <string name="ltf_block_activity_start_in_task">Блокировать запуск активности в задаче</string>
<string name="ca_cert">CA-сертификат</string> <string name="ca_cert">CA-сертификат</string>
<string name="please_select_ca_cert">Выберите сертификат</string>
<string name="cert_installed">Сертификат установлен: %1$s</string>
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">Выберите сертификат...</string> <string name="select_ca_cert" tools:ignore="TypographyEllipsis">Выберите сертификат...</string>
<string name="uninstall_all_user_ca_cert">Удалить все пользовательские CA-сертификаты</string> <string name="uninstall_all_user_ca_cert">Удалить все пользовательские CA-сертификаты</string>
<string name="security_logging">Журнал безопасности</string> <string name="security_logging">Журнал безопасности</string>
@@ -413,6 +412,7 @@
<string name="silent_uninstall">Тихое удаление</string> <string name="silent_uninstall">Тихое удаление</string>
<string name="request_uninstall">Запросить удаление</string> <string name="request_uninstall">Запросить удаление</string>
<string name="install_app">Установить приложение</string> <string name="install_app">Установить приложение</string>
<string name="search">Search</string> <!--TODO-->
<!--Ограничения пользователя--> <!--Ограничения пользователя-->
<string name="user_restriction">Ограничения пользователя</string> <string name="user_restriction">Ограничения пользователя</string>
@@ -423,7 +423,6 @@
<string name="connectivity">Другие подключения</string> <string name="connectivity">Другие подключения</string>
<string name="media">Медиа</string> <string name="media">Медиа</string>
<string name="other">Другое</string> <string name="other">Другое</string>
<string name="require_device_owner">Требуется владелец устройства</string>
<string name="config_mobile_network">Настроить мобильную сеть</string> <string name="config_mobile_network">Настроить мобильную сеть</string>
<string name="config_wifi">Настроить Wi-Fi</string> <string name="config_wifi">Настроить Wi-Fi</string>
<string name="data_roaming">Роуминг данных</string> <string name="data_roaming">Роуминг данных</string>
@@ -609,7 +608,7 @@
<string name="lock_in_background">Блокировать при переключении в фоновый режим</string> <string name="lock_in_background">Блокировать при переключении в фоновый режим</string>
<string name="clear_storage">Очистить хранилище</string> <string name="clear_storage">Очистить хранилище</string>
<string name="skipped_authentication">Авторизация пропущена, поскольку она недоступна.</string> <string name="skipped_authentication">Авторизация пропущена, поскольку она недоступна.</string>
<string name="failed_to_authenticate">Failed to authenticate</string> <!--TODO-->
<string name="api_key">API ключ</string> <string name="api_key">API ключ</string>
<string name="api_key_exist">API ключ уже сущетвует, установка нового перезапишет текущий</string> <string name="api_key_exist">API ключ уже сущетвует, установка нового перезапишет текущий</string>

View File

@@ -6,6 +6,7 @@
<string name="disable">Devre Dışı Bırak</string> <string name="disable">Devre Dışı Bırak</string>
<string name="enable">Etkinleştir</string> <string name="enable">Etkinleştir</string>
<string name="success">Başarılı</string> <string name="success">Başarılı</string>
<string name="failure">Failure</string> <!--TODO-->
<string name="failed">Başarısız</string> <string name="failed">Başarısız</string>
<string name="add">Ekle</string> <string name="add">Ekle</string>
<string name="remove">Kaldır</string> <string name="remove">Kaldır</string>
@@ -70,6 +71,7 @@
<string name="overview">Overview</string> <string name="overview">Overview</string>
<string name="features">Features</string> <string name="features">Features</string>
<string name="default_str">Default</string> <string name="default_str">Default</string>
<string name="timeout">Timeout</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string> <string name="click_to_activate">Etkinleştirmek İçin Tıklayın</string>
@@ -200,8 +202,6 @@
<string name="ltf_keyguard">Ekran kilidine izin ver</string> <string name="ltf_keyguard">Ekran kilidine izin ver</string>
<string name="ltf_block_activity_start_in_task">Görevde etkinlik başlatmayı engelle</string> <string name="ltf_block_activity_start_in_task">Görevde etkinlik başlatmayı engelle</string>
<string name="ca_cert">CA sertifikası</string> <!--TODO--> <string name="ca_cert">CA sertifikası</string> <!--TODO-->
<string name="please_select_ca_cert">Lütfen bir sertifika seçin</string> <!--TODO-->
<string name="cert_installed">Yüklenen sertifika: %1$s</string> <!--TODO-->
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">Sertifika seç...</string> <!--TODO--> <string name="select_ca_cert" tools:ignore="TypographyEllipsis">Sertifika seç...</string> <!--TODO-->
<string name="uninstall_all_user_ca_cert">Tüm kullanıcı sertifikalarını kaldır</string> <!--TODO--> <string name="uninstall_all_user_ca_cert">Tüm kullanıcı sertifikalarını kaldır</string> <!--TODO-->
<string name="security_logging">Security logging</string> <!--TODO--> <string name="security_logging">Security logging</string> <!--TODO-->
@@ -428,7 +428,6 @@
<string name="connectivity">Diğer bağlantı</string> <string name="connectivity">Diğer bağlantı</string>
<string name="media">Medya</string> <string name="media">Medya</string>
<string name="other">Diğer</string> <string name="other">Diğer</string>
<string name="require_device_owner">Cihaz sahibi gerektirir</string>
<string name="config_mobile_network">Mobil ağı yapılandır</string> <string name="config_mobile_network">Mobil ağı yapılandır</string>
<string name="config_wifi">Wi-Fi\'yi yapılandır</string> <string name="config_wifi">Wi-Fi\'yi yapılandır</string>
<string name="data_roaming">Veri dolaşımı</string> <string name="data_roaming">Veri dolaşımı</string>
@@ -610,6 +609,7 @@
<string name="lock_in_background">Arka plana geçince kilitle</string> <string name="lock_in_background">Arka plana geçince kilitle</string>
<string name="clear_storage">Depolamayı temizle</string> <string name="clear_storage">Depolamayı temizle</string>
<string name="skipped_authentication">Skipped authentication because it is unavailable.</string> <!--TODO--> <string name="skipped_authentication">Skipped authentication because it is unavailable.</string> <!--TODO-->
<string name="failed_to_authenticate">Failed to authenticate</string> <!--TODO-->
<!--TODO--> <!--TODO-->
<string name="api_key">API key</string> <string name="api_key">API key</string>

View File

@@ -5,6 +5,7 @@
<string name="disable">禁用</string> <string name="disable">禁用</string>
<string name="enable">启用</string> <string name="enable">启用</string>
<string name="success">成功</string> <string name="success">成功</string>
<string name="failure">失败</string>
<string name="failed">失败</string> <string name="failed">失败</string>
<string name="add">添加</string> <string name="add">添加</string>
<string name="remove">移除</string> <string name="remove">移除</string>
@@ -66,6 +67,7 @@
<string name="overview">概览</string> <string name="overview">概览</string>
<string name="features">功能</string> <string name="features">功能</string>
<string name="default_str">默认</string> <string name="default_str">默认</string>
<string name="timeout">超时</string>
<!--Permissions--> <!--Permissions-->
<string name="click_to_activate">点击以激活</string> <string name="click_to_activate">点击以激活</string>
@@ -191,8 +193,6 @@
<string name="package_name">包名</string> <string name="package_name">包名</string>
<string name="not_exist">不存在</string> <string name="not_exist">不存在</string>
<string name="ca_cert">CA证书</string> <string name="ca_cert">CA证书</string>
<string name="please_select_ca_cert">请选择CA证书</string>
<string name="cert_installed">证书已安装:%1$s</string>
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">选择证书...</string> <string name="select_ca_cert" tools:ignore="TypographyEllipsis">选择证书...</string>
<string name="uninstall_all_user_ca_cert">卸载所有用户证书</string> <string name="uninstall_all_user_ca_cert">卸载所有用户证书</string>
<string name="security_logging">安全日志</string> <string name="security_logging">安全日志</string>
@@ -415,7 +415,6 @@
<string name="connectivity">更多连接</string> <string name="connectivity">更多连接</string>
<string name="media">媒体</string> <string name="media">媒体</string>
<string name="other">其他</string> <string name="other">其他</string>
<string name="require_device_owner">需要DeviceOwner</string>
<string name="config_mobile_network">配置移动数据</string> <string name="config_mobile_network">配置移动数据</string>
<string name="config_wifi">配置Wi-Fi</string> <string name="config_wifi">配置Wi-Fi</string>
<string name="data_roaming">数据漫游</string> <string name="data_roaming">数据漫游</string>
@@ -597,6 +596,7 @@
<string name="lock_in_background">处于后台时锁定</string> <string name="lock_in_background">处于后台时锁定</string>
<string name="clear_storage">清除存储空间</string> <string name="clear_storage">清除存储空间</string>
<string name="skipped_authentication">验证已跳过,因为不可用</string> <string name="skipped_authentication">验证已跳过,因为不可用</string>
<string name="failed_to_authenticate">验证失败</string>
<string name="api_key">API密钥</string> <string name="api_key">API密钥</string>
<string name="api_key_exist">API密钥已存在设置新的密钥将会覆盖当前密钥</string> <string name="api_key_exist">API密钥已存在设置新的密钥将会覆盖当前密钥</string>

View File

@@ -222,8 +222,6 @@
<string name="ltf_keyguard">Allow keyguard</string> <string name="ltf_keyguard">Allow keyguard</string>
<string name="ltf_block_activity_start_in_task">Block activity start in task</string> <string name="ltf_block_activity_start_in_task">Block activity start in task</string>
<string name="ca_cert">CA certificate</string> <string name="ca_cert">CA certificate</string>
<string name="please_select_ca_cert">Please select a certificate</string>
<string name="cert_installed">Certificate installed: %1$s</string>
<string name="select_ca_cert" tools:ignore="TypographyEllipsis">Select certificate...</string> <string name="select_ca_cert" tools:ignore="TypographyEllipsis">Select certificate...</string>
<string name="uninstall_all_user_ca_cert">Uninstall all user CA certificate</string> <string name="uninstall_all_user_ca_cert">Uninstall all user CA certificate</string>
<string name="security_logging">Security logging</string> <string name="security_logging">Security logging</string>
@@ -454,7 +452,6 @@
<string name="connectivity">Other connection</string> <string name="connectivity">Other connection</string>
<string name="media">Media</string> <string name="media">Media</string>
<string name="other">Other</string> <string name="other">Other</string>
<string name="require_device_owner">Require device owner</string>
<string name="config_mobile_network">Configure mobile network</string> <string name="config_mobile_network">Configure mobile network</string>
<string name="config_wifi">Configure Wi-Fi</string> <string name="config_wifi">Configure Wi-Fi</string>
<string name="data_roaming">Data roaming</string> <string name="data_roaming">Data roaming</string>
@@ -637,6 +634,7 @@
<string name="lock_in_background">Lock when switch to background</string> <string name="lock_in_background">Lock when switch to background</string>
<string name="clear_storage">Clear storage</string> <string name="clear_storage">Clear storage</string>
<string name="skipped_authentication">Skipped authentication because it is unavailable.</string> <string name="skipped_authentication">Skipped authentication because it is unavailable.</string>
<string name="failed_to_authenticate">Failed to authenticate</string>
<string name="api_key">API key</string> <string name="api_key">API key</string>
<string name="api_key_exist">The API key already exists, setting a new key will overwrite the current key.</string> <string name="api_key_exist">The API key already exists, setting a new key will overwrite the current key.</string>