From 17bfc5dd49d3f5b352302c01e87d9be3923682b5 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Mon, 24 Aug 2026 09:57:55 -0300 Subject: [PATCH] feat(permissions): add option to pre-approve permissions under manual approval When connecting a new app with the 'Manually approve each permission' policy selected, an 'Add permission' button now lets the user pick permissions from the full supported catalog to be approved automatically, while everything else still asks for confirmation. - New AddPermissionsSheet bottom sheet with search, deduped catalog list and an 'Add' confirmation bar - Support for adding a custom sign_event kind not present in the catalog, with duplicate/existing-permission validation - Wired into LoginWithPubKey (intent path), BunkerConnectRequestScreen (NIP-46 path) and the EditPermission screen (persisted as Allow/Always rows) - New strings translated in all 14 locales --- .../nostrsigner/ui/EditPermission.kt | 41 +++ .../ui/components/AddPermissionsSheet.kt | 265 ++++++++++++++++++ .../components/BunkerConnectRequestScreen.kt | 94 +++++-- .../ui/components/LoginWithPubKey.kt | 94 +++++-- app/src/main/res/values-de/strings.xml | 6 + app/src/main/res/values-es/strings.xml | 6 + app/src/main/res/values-fr/strings.xml | 6 + app/src/main/res/values-in/strings.xml | 6 + app/src/main/res/values-it-rIT/strings.xml | 6 + app/src/main/res/values-ja/strings.xml | 6 + app/src/main/res/values-ko-rKR/strings.xml | 6 + app/src/main/res/values-pt-rBR/strings.xml | 6 + app/src/main/res/values-ru/strings.xml | 6 + app/src/main/res/values-th/strings.xml | 6 + app/src/main/res/values-tr/strings.xml | 6 + app/src/main/res/values-vi-rVN/strings.xml | 6 + app/src/main/res/values-zh/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + 18 files changed, 518 insertions(+), 60 deletions(-) create mode 100644 app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AddPermissionsSheet.kt diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditPermission.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditPermission.kt index da61c223..9dcb4849 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditPermission.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditPermission.kt @@ -51,6 +51,7 @@ import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.models.Permission import com.greenart7c3.nostrsigner.models.SignerType import com.greenart7c3.nostrsigner.ui.actions.RemoveAllPermissionsDialog +import com.greenart7c3.nostrsigner.ui.components.AddPermissionsSheet import com.greenart7c3.nostrsigner.ui.components.AmberButton import com.greenart7c3.nostrsigner.ui.components.AmberToggles import com.greenart7c3.nostrsigner.ui.components.LabeledBorderBox @@ -77,6 +78,7 @@ fun EditPermission( } var wantsToRemovePermissions by remember { mutableStateOf(false) } + var showAddPermissions by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() var checked by remember { @@ -194,6 +196,45 @@ fun EditPermission( text = stringResource(R.string.edit_permissions_description), ) + if (applicationData.key.isNotEmpty()) { + if (showAddPermissions) { + AddPermissionsSheet( + existingPermissions = permissions.map { Permission(it.type, it.kind) }, + onAdd = { addedPermissions -> + val newPermissions = addedPermissions.map { + ApplicationPermissionsEntity( + null, + applicationData.key, + it.type.uppercase(), + it.kind, + true, + RememberType.ALWAYS.screenCode, + Long.MAX_VALUE / 1000, + 0, + ) + } + permissions.addAll(newPermissions) + scope.launch(Dispatchers.IO) { + Amber.instance + .dao(account.npub) + .insertPermissions(newPermissions) + } + }, + onDismiss = { + showAddPermissions = false + }, + ) + } + + AmberButton( + modifier = Modifier.padding(bottom = 20.dp), + onClick = { + showAddPermissions = true + }, + text = stringResource(R.string.add_permission), + ) + } + permissions.forEach { permission -> PermissionRow( permission = permission, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AddPermissionsSheet.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AddPermissionsSheet.kt new file mode 100644 index 00000000..45be640b --- /dev/null +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/AddPermissionsSheet.kt @@ -0,0 +1,265 @@ +package com.greenart7c3.nostrsigner.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.BottomAppBar +import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateMapOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.greenart7c3.nostrsigner.R +import com.greenart7c3.nostrsigner.models.Permission +import com.greenart7c3.nostrsigner.models.supportedKindNumbers +import com.greenart7c3.nostrsigner.ui.theme.AmberPreview +import com.greenart7c3.nostrsigner.ui.theme.ThemePreviews + +private fun permissionKey(permission: Permission) = "${permission.type}-${permission.kind}" + +/** + * Bottom sheet that lets the user pick permissions from the full supported list to + * be approved automatically, excluding the ones the application already has. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AddPermissionsSheet( + existingPermissions: List, + onAdd: (List) -> Unit, + onDismiss: () -> Unit, +) { + val context = LocalContext.current + val sheetState = rememberModalBottomSheetState( + skipPartiallyExpanded = true, + ) + + val candidates = remember(existingPermissions) { + supportedKindNumbers + .distinctBy { permissionKey(it) } + .filterNot { candidate -> + existingPermissions.any { + it.type.equals(candidate.type, ignoreCase = true) && it.kind == candidate.kind + } + } + } + + val selectedPermissions = remember { mutableStateMapOf() } + var search by remember { mutableStateOf("") } + + // Custom sign_event kinds typed by the user, added on top of the catalog list. + val customPermissions = remember { mutableStateListOf() } + var customKind by remember { mutableStateOf("") } + val parsedCustomKind = customKind.toIntOrNull() + val customKindExists = parsedCustomKind != null && + existingPermissions.any { + it.type.equals("sign_event", ignoreCase = true) && it.kind == parsedCustomKind + } + val canAddCustomKind = parsedCustomKind != null && + !customKindExists && + customPermissions.none { it.kind == parsedCustomKind } + + val customCandidates = customPermissions.toList() + val allCandidates = customCandidates + candidates + val filteredPermissions = remember(allCandidates, search) { + if (search.isBlank()) { + allCandidates + } else { + allCandidates.filter { + it.toLocalizedString(context, true).contains(search, ignoreCase = true) || + it.kind.toString().contains(search) || + it.type.contains(search, ignoreCase = true) + } + } + } + + val hasSelection = selectedPermissions.values.any { it } + + ModalBottomSheet( + sheetState = sheetState, + onDismissRequest = onDismiss, + ) { + Scaffold( + bottomBar = { + BottomAppBar { + Column { + AmberButton( + modifier = Modifier.padding(horizontal = 16.dp), + enabled = hasSelection, + onClick = { + onAdd( + allCandidates.filter { selectedPermissions[permissionKey(it)] == true }, + ) + onDismiss() + }, + text = stringResource(R.string.add), + ) + IconRow( + center = true, + title = stringResource(R.string.go_back), + icon = ImageVector.vectorResource(R.drawable.back), + onClick = onDismiss, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + }, + ) { padding -> + Column( + Modifier + .padding(padding) + .fillMaxWidth(), + ) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + text = stringResource(R.string.add_permission_description), + style = MaterialTheme.typography.bodyMedium, + ) + + OutlinedTextField( + value = search, + onValueChange = { search = it }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + placeholder = { Text(stringResource(R.string.search_permissions)) }, + leadingIcon = { + Icon(Icons.Default.Search, contentDescription = null) + }, + singleLine = true, + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + ) { + OutlinedTextField( + value = customKind, + onValueChange = { customKind = it.filter { char -> char.isDigit() } }, + modifier = Modifier.weight(1f), + label = { Text(stringResource(R.string.custom_event_kind)) }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true, + isError = customKindExists, + supportingText = if (customKindExists) { + { Text(stringResource(R.string.custom_event_kind_already_added)) } + } else { + null + }, + ) + TextButton( + enabled = canAddCustomKind, + onClick = { + val kind = parsedCustomKind ?: return@TextButton + val permission = Permission("sign_event", kind) + customPermissions.add(permission) + selectedPermissions[permissionKey(permission)] = true + customKind = "" + }, + ) { + Text(stringResource(R.string.add)) + } + } + + Spacer(Modifier.height(8.dp)) + + if (filteredPermissions.isEmpty()) { + Text( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + text = stringResource(R.string.no_permissions_found), + textAlign = TextAlign.Center, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } else { + LazyColumn( + Modifier + .fillMaxWidth() + .padding(bottom = 8.dp), + contentPadding = PaddingValues(horizontal = 8.dp), + ) { + items( + filteredPermissions, + key = { permissionKey(it) }, + ) { item -> + val itemKey = permissionKey(item) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier + .fillMaxWidth() + .clickable { + selectedPermissions[itemKey] = !(selectedPermissions[itemKey] ?: false) + } + .padding(vertical = 2.dp), + ) { + Checkbox( + checked = selectedPermissions[itemKey] ?: false, + onCheckedChange = { + selectedPermissions[itemKey] = it + }, + ) + Text( + text = item.toLocalizedString(context, true), + style = MaterialTheme.typography.bodyMedium, + ) + } + } + } + } + } + } + } +} + +@ThemePreviews +@Composable +fun AddPermissionsSheetPreview() { + AmberPreview { + AddPermissionsSheet( + existingPermissions = listOf( + Permission("get_public_key", null), + Permission("sign_event", 1), + ), + onAdd = {}, + onDismiss = {}, + ) + } +} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt index 9a9a0213..a0d5d94c 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/BunkerConnectRequestScreen.kt @@ -155,6 +155,7 @@ fun BunkerConnectRequestScreen( var deleteAfterIndex by remember { mutableIntStateOf(DeleteAfterType.NEVER.screenCode) } var closeApp by remember { mutableStateOf(shouldCloseApp) } var showModal by remember { mutableStateOf(false) } + var showAddPermissions by remember { mutableStateOf(false) } var advancedOpen by remember { mutableStateOf(false) } val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, @@ -281,7 +282,58 @@ fun BunkerConnectRequestScreen( }, ) - if (selectedOption == 1 && localPermissions.isNotEmpty()) { + if (selectedOption == 1) { + if (localPermissions.isNotEmpty()) { + Box( + Modifier + .fillMaxWidth() + .padding(top = 4.dp), + contentAlignment = Alignment.Center, + ) { + ElevatedButton( + colors = ButtonDefaults.buttonColors().copy( + contentColor = Color.Black, + ), + shape = RoundedCornerShape(20), + content = { + Text(stringResource(R.string.permissions)) + }, + onClick = { + showModal = true + }, + ) + } + if (showModal) { + ModalBottomSheet( + sheetState = sheetState, + onDismissRequest = { + showModal = false + }, + ) { + Scaffold( + bottomBar = { + BottomAppBar { + IconRow( + center = true, + title = stringResource(R.string.go_back), + icon = ImageVector.vectorResource(R.drawable.back), + onClick = { + showModal = false + }, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + }, + ) { + EnabledPermissions( + Modifier.padding(it), + localPermissions, + ) + } + } + } + } + Box( Modifier .fillMaxWidth() @@ -294,41 +346,23 @@ fun BunkerConnectRequestScreen( ), shape = RoundedCornerShape(20), content = { - Text(stringResource(R.string.permissions)) + Text(stringResource(R.string.add_permission)) }, onClick = { - showModal = true + showAddPermissions = true }, ) } - if (showModal) { - ModalBottomSheet( - sheetState = sheetState, - onDismissRequest = { - showModal = false + if (showAddPermissions) { + AddPermissionsSheet( + existingPermissions = localPermissions, + onAdd = { addedPermissions -> + localPermissions.addAll(addedPermissions) }, - ) { - Scaffold( - bottomBar = { - BottomAppBar { - IconRow( - center = true, - title = stringResource(R.string.go_back), - icon = ImageVector.vectorResource(R.drawable.back), - onClick = { - showModal = false - }, - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, - ) { - EnabledPermissions( - Modifier.padding(it), - localPermissions, - ) - } - } + onDismiss = { + showAddPermissions = false + }, + ) } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt index 3651e0f5..521a48f0 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/LoginWithPubKey.kt @@ -306,6 +306,7 @@ fun LoginWithPubKey( ) } var showModal by remember { mutableStateOf(false) } + var showAddPermissions by remember { mutableStateOf(false) } val sheetState = rememberModalBottomSheetState( skipPartiallyExpanded = true, ) @@ -365,7 +366,58 @@ fun LoginWithPubKey( }, ) - if (selectedOption == 1 && localPermissions.isNotEmpty()) { + if (selectedOption == 1) { + if (localPermissions.isNotEmpty()) { + Box( + Modifier + .fillMaxWidth() + .padding(top = 4.dp), + contentAlignment = Alignment.Center, + ) { + ElevatedButton( + colors = ButtonDefaults.buttonColors().copy( + contentColor = Color.Black, + ), + shape = RoundedCornerShape(20), + content = { + Text(stringResource(R.string.permissions)) + }, + onClick = { + showModal = true + }, + ) + } + if (showModal) { + ModalBottomSheet( + sheetState = sheetState, + onDismissRequest = { + showModal = false + }, + ) { + Scaffold( + bottomBar = { + BottomAppBar { + IconRow( + center = true, + title = stringResource(R.string.go_back), + icon = ImageVector.vectorResource(R.drawable.back), + onClick = { + showModal = false + }, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + }, + ) { + EnabledPermissions( + Modifier.padding(it), + localPermissions, + ) + } + } + } + } + Box( Modifier .fillMaxWidth() @@ -378,41 +430,23 @@ fun LoginWithPubKey( ), shape = RoundedCornerShape(20), content = { - Text(stringResource(R.string.permissions)) + Text(stringResource(R.string.add_permission)) }, onClick = { - showModal = true + showAddPermissions = true }, ) } - if (showModal) { - ModalBottomSheet( - sheetState = sheetState, - onDismissRequest = { - showModal = false + if (showAddPermissions) { + AddPermissionsSheet( + existingPermissions = localPermissions, + onAdd = { addedPermissions -> + localPermissions.addAll(addedPermissions) }, - ) { - Scaffold( - bottomBar = { - BottomAppBar { - IconRow( - center = true, - title = stringResource(R.string.go_back), - icon = ImageVector.vectorResource(R.drawable.back), - onClick = { - showModal = false - }, - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - } - }, - ) { - EnabledPermissions( - Modifier.padding(it), - localPermissions, - ) - } - } + onDismiss = { + showAddPermissions = false + }, + ) } } } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ceafc14a..75b31de6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -50,6 +50,12 @@ Biometrien konnten den Besitzer dieses Telefons nicht authentifizieren. Fehler: %1$s Löschen Berechtigungen + Berechtigung hinzufügen + Wähle die Berechtigungen aus, die für diese Anwendung automatisch genehmigt werden sollen. + Berechtigungen suchen + Keine Berechtigungen gefunden + Benutzerdefinierte Ereignis-Art + Diese Berechtigung ist bereits in der Liste Anwendung löschen Bunker zurücksetzen In Zwischenablage kopieren diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 940a490a..f3004ada 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -52,6 +52,12 @@ La biometría no ha podido autenticar al propietario de este teléfono. Error: %1$s Borrar Permisos + Añadir permiso + Selecciona los permisos que quieres que se aprueben automáticamente para esta aplicación. + Buscar permisos + No se encontraron permisos + Tipo de evento personalizado + Este permiso ya está en la lista Eliminar aplicación Restablecer Bunker Copiar al portapapeles diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index df05225f..a83665c1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -50,6 +50,12 @@ La biométrie n\'a pas réussi à authentifier le propriétaire de ce téléphone. Erreur : %1$s Supprimer Autorisations + Ajouter une autorisation + Sélectionnez les autorisations que vous souhaitez voir approuvées automatiquement pour cette application. + Rechercher des autorisations + Aucune autorisation trouvée + Type d\'événement personnalisé + Cette autorisation est déjà dans la liste Supprimer l\'application Réinitialiser le Bunker Copier dans le presse-papiers diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index f60cf284..bfda6135 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -52,6 +52,12 @@ Biometrik gagal mengautentikasi pemilik ponsel ini. Error: %1$s Hapus Izin + Tambahkan izin + Pilih izin yang ingin disetujui otomatis untuk aplikasi ini. + Cari izin + Tidak ada izin ditemukan + Jenis event kustom + Izin ini sudah ada di daftar Hapus Aplikasi Atur Ulang Bunker Salin ke clipboard diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index dd2e1768..568405dd 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -52,6 +52,12 @@ La biometria non è riuscita ad autenticare il proprietario del telefono. Errore: %1$s Elimina Permessi + Aggiungi permesso + Seleziona i permessi che vuoi approvare automaticamente per questa applicazione. + Cerca permessi + Nessun permesso trovato + Tipo di evento personalizzato + Questo permesso è già nell\'elenco Elimina applicazione Reimposta Bunker Copia negli appunti diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 2e0bbfda..7424e89d 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -43,6 +43,12 @@ 生体認証でこの端末の所有者を確認できませんでした。エラー: %1$s 削除 権限 + 権限を追加 + このアプリケーションで自動的に承認したい権限を選択してください。 + 権限を検索 + 権限が見つかりません + カスタムイベント種別 + この権限はすでにリストにあります アプリケーションを削除 Bunker をリセット クリップボードにコピー diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 1904265d..ec70cb1a 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -52,6 +52,12 @@ 생체 인식으로 이 휴대폰 소유자 인증에 실패했습니다. 오류: %1$s 삭제 권한 + 권한 추가 + 이 애플리케이션에 대해 자동으로 승인할 권한을 선택하세요. + 권한 검색 + 권한을 찾을 수 없습니다 + 사용자 지정 이벤트 종류 + 이 권한은 이미 목록에 있습니다 애플리케이션 삭제 Bunker 초기화 클립보드에 복사 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index bb63c493..1ae947ab 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -48,6 +48,12 @@ A biometria não conseguiu autenticar o proprietário deste telefone. Erro: %1$s Excluir Permissões + Adicionar permissão + Selecione as permissões que você deseja que sejam aprovadas automaticamente para este aplicativo. + Pesquisar permissões + Nenhuma permissão encontrada + Tipo de evento personalizado + Esta permissão já está na lista Excluir aplicativo Redefinir Bunker Copiar para área de transferência diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fb895178..99e03232 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -52,6 +52,12 @@ Биометрия не смогла подтвердить владельца этого телефона. Ошибка: %1$s Удалить Разрешения + Добавить разрешение + Выберите разрешения, которые должны автоматически одобряться для этого приложения. + Поиск разрешений + Разрешения не найдены + Пользовательский тип события + Это разрешение уже есть в списке Удалить приложение Сбросить Bunker Копировать в буфер обмена diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 04f9fa6a..2a693ff0 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -43,6 +43,12 @@ ไบโอเมตริกไม่สามารถยืนยันตัวตนเจ้าของโทรศัพท์เครื่องนี้ได้ ข้อผิดพลาด: %1$s ลบ สิทธิ์ + เพิ่มสิทธิ์ + เลือกสิทธิ์ที่คุณต้องการให้อนุมัติโดยอัตโนมัติสำหรับแอปพลิเคชันนี้ + ค้นหาสิทธิ์ + ไม่พบสิทธิ์ + ชนิดเหตุการณ์แบบกำหนดเอง + สิทธิ์นี้อยู่ในรายการแล้ว ลบแอปพลิเคชัน รีเซ็ต Bunker คัดลอกไปยังคลิปบอร์ด diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3eea9b6d..e8e1f5dc 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -50,6 +50,12 @@ Biyometrik veriler bu telefonun sahibini doğrulayamadı. Hata: %1$s Sil İzinler + İzin ekle + Bu uygulama için otomatik olarak onaylanmasını istediğiniz izinleri seçin. + İzinleri ara + İzin bulunamadı + Özel etkinlik türü + Bu izin listede zaten var Uygulamayı Sil Bunker\'ı Sıfırla Panoya kopyala diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index 0537ee76..1810a44e 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -43,6 +43,12 @@ Sinh trắc học không thể xác thực chủ sở hữu điện thoại này. Lỗi: %1$s Xóa Quyền + Thêm quyền + Chọn các quyền bạn muốn được tự động phê duyệt cho ứng dụng này. + Tìm kiếm quyền + Không tìm thấy quyền nào + Loại sự kiện tùy chỉnh + Quyền này đã có trong danh sách Xóa Ứng Dụng Đặt lại Bunker Sao chép vào khay nhớ tạm diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 4f63417b..1fe0a2fe 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -48,6 +48,12 @@ 生物识别验证设备所有者身份失败:%1$s 删除 权限 + 添加权限 + 选择您希望为此应用程序自动批准的权限。 + 搜索权限 + 未找到权限 + 自定义事件类型 + 此权限已在列表中 删除应用 重置 Bunker 复制到剪贴板 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9b305009..de30106d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,6 +76,12 @@ Biometrics failed to authenticate the owner of this phone. Error: %1$s Delete Permissions + Add permission + Select the permissions you want to be approved automatically for this application. + Search permissions + No permissions found + Custom event kind + This permission is already in the list Delete Application Reset Bunker Copy to clipboard