mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
feat: encrypted backup
Fixes bug where sometimes restore of backup can fail Adds support for encrypted backups with better error messages
This commit is contained in:
@@ -129,6 +129,7 @@ import com.zaneschepke.wireguardautotunnel.ui.theme.OffWhite
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.Straw
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.WireguardAutoTunnelTheme
|
||||
import com.zaneschepke.wireguardautotunnel.util.FileUtils
|
||||
import com.zaneschepke.wireguardautotunnel.util.LocaleUtil
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.installApk
|
||||
@@ -139,6 +140,10 @@ import com.zaneschepke.wireguardautotunnel.viewmodel.ConfigEditViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SharedAppViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.SplitTunnelViewModel
|
||||
import com.zaneschepke.wireguardautotunnel.viewmodel.TunnelViewModel
|
||||
import de.raphaelebner.roomdatabasebackup.core.OnCompleteListener.Companion.EXIT_CODE_ERROR
|
||||
import de.raphaelebner.roomdatabasebackup.core.OnCompleteListener.Companion.EXIT_CODE_ERROR_DECRYPTION_ERROR
|
||||
import de.raphaelebner.roomdatabasebackup.core.OnCompleteListener.Companion.EXIT_CODE_ERROR_RESTORE_BACKUP_IS_ENCRYPTED
|
||||
import de.raphaelebner.roomdatabasebackup.core.OnCompleteListener.Companion.EXIT_CODE_ERROR_WRONG_DECRYPTION_PASSWORD
|
||||
import de.raphaelebner.roomdatabasebackup.core.RoomBackup
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
@@ -150,6 +155,7 @@ import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.orbitmvi.orbit.compose.collectAsState
|
||||
import timber.log.Timber
|
||||
import xyz.teamgravity.pin_lock_compose.PinManager
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@@ -173,9 +179,9 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
handleIncomingIntent(intent)
|
||||
roomBackup = RoomBackup(this).database(appDatabase).enableLogDebug(true).maxFileCount(5)
|
||||
|
||||
roomBackup = RoomBackup(this)
|
||||
handleIncomingIntent(intent)
|
||||
|
||||
installSplashScreen().apply {
|
||||
setKeepOnScreenCondition { !viewModel.container.stateFlow.value.isAppLoaded }
|
||||
@@ -591,62 +597,76 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
fun performBackup() = lifecycleScope.launch {
|
||||
fun performBackup(encrypt: Boolean = false, password: String? = null) {
|
||||
roomBackup
|
||||
.database(appDatabase)
|
||||
.backupLocation(RoomBackup.BACKUP_FILE_LOCATION_CUSTOM_DIALOG)
|
||||
.enableLogDebug(true)
|
||||
.maxFileCount(5)
|
||||
.apply {
|
||||
onCompleteListener { success, _, _ ->
|
||||
lifecycleScope.launch {
|
||||
val sideEffect =
|
||||
if (success) {
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(
|
||||
R.string.backup_success,
|
||||
getString(R.string.restarting_app),
|
||||
),
|
||||
ToastType.Success,
|
||||
)
|
||||
} else {
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(R.string.backup_failed),
|
||||
ToastType.Error,
|
||||
)
|
||||
}
|
||||
viewModel.postSideEffect(sideEffect)
|
||||
}
|
||||
if (encrypt && !password.isNullOrBlank()) {
|
||||
backupIsEncrypted(true)
|
||||
customEncryptPassword(password)
|
||||
}
|
||||
}
|
||||
.onCompleteListener { success, _, _ ->
|
||||
lifecycleScope.launch {
|
||||
val sideEffect =
|
||||
if (success) {
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(R.string.backup_success),
|
||||
ToastType.Success,
|
||||
)
|
||||
} else {
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(R.string.backup_failed),
|
||||
ToastType.Error,
|
||||
)
|
||||
}
|
||||
viewModel.postSideEffect(sideEffect)
|
||||
}
|
||||
}
|
||||
.backup()
|
||||
}
|
||||
|
||||
fun performRestore() = lifecycleScope.launch {
|
||||
fun performRestore(encrypt: Boolean = false, password: String? = null) {
|
||||
roomBackup
|
||||
.database(appDatabase)
|
||||
.enableLogDebug(true)
|
||||
.backupLocation(RoomBackup.BACKUP_FILE_LOCATION_CUSTOM_DIALOG)
|
||||
.apply {
|
||||
onCompleteListener { success, _, _ ->
|
||||
lifecycleScope.launch {
|
||||
val sideEffect =
|
||||
if (success) {
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(
|
||||
R.string.restore_success,
|
||||
getString(R.string.restarting_app),
|
||||
),
|
||||
ToastType.Success,
|
||||
)
|
||||
} else {
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(R.string.restore_failed),
|
||||
ToastType.Error,
|
||||
)
|
||||
if (encrypt && !password.isNullOrBlank()) {
|
||||
backupIsEncrypted(true)
|
||||
customEncryptPassword(password)
|
||||
}
|
||||
}
|
||||
.onCompleteListener { success, message, exitCode ->
|
||||
lifecycleScope.launch {
|
||||
if (success) {
|
||||
viewModel.postSideEffect(
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.StringResource(R.string.restore_success),
|
||||
ToastType.Success,
|
||||
)
|
||||
)
|
||||
roomBackup.restartApp(Intent(this@MainActivity, MainActivity::class.java))
|
||||
} else {
|
||||
Timber.w("Restore failed, exitCode=$exitCode, message=$message")
|
||||
|
||||
val errorMessage =
|
||||
when (exitCode) {
|
||||
EXIT_CODE_ERROR_WRONG_DECRYPTION_PASSWORD ->
|
||||
getString(R.string.restore_failed_wrong_password)
|
||||
|
||||
EXIT_CODE_ERROR,
|
||||
EXIT_CODE_ERROR_DECRYPTION_ERROR,
|
||||
EXIT_CODE_ERROR_RESTORE_BACKUP_IS_ENCRYPTED ->
|
||||
getString(R.string.restore_failed_invalid_file)
|
||||
|
||||
else -> getString(R.string.restore_failed)
|
||||
}
|
||||
viewModel.postSideEffect(sideEffect)
|
||||
if (success) restartApp()
|
||||
|
||||
viewModel.postSideEffect(
|
||||
GlobalSideEffect.Snackbar(
|
||||
StringValue.DynamicString(errorMessage),
|
||||
ToastType.Error,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -666,13 +686,20 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private fun handleIncomingIntent(intent: Intent?) {
|
||||
intent ?: return
|
||||
|
||||
when (intent.action) {
|
||||
Intent.ACTION_VIEW,
|
||||
Intent.ACTION_EDIT,
|
||||
Intent.ACTION_SEND -> {
|
||||
val uri: Uri? = intent.data
|
||||
uri?.let { viewModel.importFromUri(it) }
|
||||
val uri: Uri? = intent.data ?: return
|
||||
val name = uri?.lastPathSegment?.lowercase() ?: return
|
||||
if (
|
||||
!name.endsWith(FileUtils.CONF_FILE_EXTENSION) &&
|
||||
!name.endsWith(FileUtils.ZIP_FILE_EXTENSION)
|
||||
) {
|
||||
Timber.d("Ignoring non-config URI in handleIncomingIntent: $uri")
|
||||
return
|
||||
}
|
||||
viewModel.importFromUri(uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-6
@@ -52,6 +52,7 @@ import com.zaneschepke.wireguardautotunnel.ui.common.label.GroupLabel
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.text.DescriptionText
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.BackupBottomSheet
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.components.BackupEncryptionDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.settings.proxy.compoents.AppModeBottomSheet
|
||||
import com.zaneschepke.wireguardautotunnel.util.StringValue
|
||||
import com.zaneschepke.wireguardautotunnel.util.extensions.asString
|
||||
@@ -88,6 +89,9 @@ fun SettingsScreen(
|
||||
}
|
||||
|
||||
var showBackupSheet by rememberSaveable { mutableStateOf(false) }
|
||||
var showEncryptionDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var isRestoreAction by remember { mutableStateOf(false) }
|
||||
|
||||
var showAppModeSheet by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val appMode = uiState.settings.tunnelMode
|
||||
@@ -110,13 +114,42 @@ fun SettingsScreen(
|
||||
action()
|
||||
}
|
||||
|
||||
if (showBackupSheet)
|
||||
if (showBackupSheet) {
|
||||
BackupBottomSheet(
|
||||
{ performBackupRestore { (context as? MainActivity)?.performBackup() } },
|
||||
{ performBackupRestore { (context as? MainActivity)?.performRestore() } },
|
||||
) {
|
||||
showBackupSheet = false
|
||||
}
|
||||
onBackup = {
|
||||
showBackupSheet = false
|
||||
isRestoreAction = false
|
||||
showEncryptionDialog = true
|
||||
},
|
||||
onRestore = {
|
||||
showBackupSheet = false
|
||||
isRestoreAction = true
|
||||
showEncryptionDialog = true
|
||||
},
|
||||
onDismiss = { showBackupSheet = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showEncryptionDialog) {
|
||||
BackupEncryptionDialog(
|
||||
isRestore = isRestoreAction,
|
||||
onConfirm = { encrypt, password ->
|
||||
showEncryptionDialog = false
|
||||
|
||||
if (isRestoreAction) {
|
||||
performBackupRestore {
|
||||
(context as? MainActivity)?.performRestore(encrypt, password)
|
||||
}
|
||||
} else {
|
||||
performBackupRestore {
|
||||
(context as? MainActivity)?.performBackup(encrypt, password)
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismiss = { showEncryptionDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showAppModeSheet)
|
||||
AppModeBottomSheet(sharedViewModel::setAppMode, uiState.settings.tunnelMode) {
|
||||
showAppModeSheet = false
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.settings.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Visibility
|
||||
import androidx.compose.material.icons.outlined.VisibilityOff
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.ThemedSwitch
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.textbox.CustomTextField
|
||||
|
||||
@Composable
|
||||
fun BackupEncryptionDialog(
|
||||
isRestore: Boolean,
|
||||
onConfirm: (encrypt: Boolean, password: String?) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
var encrypt by remember { mutableStateOf(false) }
|
||||
var password by remember { mutableStateOf("") }
|
||||
var confirmPassword by remember { mutableStateOf("") }
|
||||
var showPasswordError by remember { mutableStateOf(false) }
|
||||
var passwordVisible by remember { mutableStateOf(false) }
|
||||
var confirmPasswordVisible by remember { mutableStateOf(false) }
|
||||
|
||||
InfoDialog(
|
||||
title =
|
||||
if (isRestore) {
|
||||
stringResource(R.string.restore)
|
||||
} else {
|
||||
stringResource(R.string.backup)
|
||||
},
|
||||
confirmText =
|
||||
if (isRestore) {
|
||||
stringResource(R.string.restore)
|
||||
} else {
|
||||
stringResource(R.string.backup)
|
||||
},
|
||||
onAttest = {
|
||||
if (!isRestore && encrypt && password != confirmPassword) {
|
||||
showPasswordError = true
|
||||
return@InfoDialog
|
||||
}
|
||||
if (encrypt && password.isBlank()) {
|
||||
return@InfoDialog
|
||||
}
|
||||
|
||||
val finalPassword = if (encrypt) password else null
|
||||
onConfirm(encrypt, finalPassword)
|
||||
},
|
||||
onDismiss = onDismiss,
|
||||
body = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(stringResource(R.string.encrypted))
|
||||
ThemedSwitch(checked = encrypt, onClick = { encrypt = it })
|
||||
}
|
||||
|
||||
if (encrypt) {
|
||||
CustomTextField(
|
||||
value = password,
|
||||
onValueChange = {
|
||||
password = it
|
||||
showPasswordError = false
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
label = { Text(stringResource(R.string.password)) },
|
||||
visualTransformation =
|
||||
if (passwordVisible) VisualTransformation.None
|
||||
else PasswordVisualTransformation(),
|
||||
trailing = {
|
||||
IconButton(onClick = { passwordVisible = !passwordVisible }) {
|
||||
Icon(
|
||||
imageVector =
|
||||
if (passwordVisible) Icons.Outlined.VisibilityOff
|
||||
else Icons.Outlined.Visibility,
|
||||
contentDescription =
|
||||
if (passwordVisible) stringResource(R.string.hide_password)
|
||||
else stringResource(R.string.show_password),
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
if (!isRestore) {
|
||||
CustomTextField(
|
||||
value = confirmPassword,
|
||||
onValueChange = {
|
||||
confirmPassword = it
|
||||
showPasswordError = false
|
||||
},
|
||||
label = { Text(stringResource(R.string.confirm_password)) },
|
||||
visualTransformation =
|
||||
if (confirmPasswordVisible) VisualTransformation.None
|
||||
else PasswordVisualTransformation(),
|
||||
trailing = {
|
||||
IconButton(
|
||||
onClick = { confirmPasswordVisible = !confirmPasswordVisible }
|
||||
) {
|
||||
Icon(
|
||||
imageVector =
|
||||
if (confirmPasswordVisible) Icons.Outlined.VisibilityOff
|
||||
else Icons.Outlined.Visibility,
|
||||
contentDescription =
|
||||
if (confirmPasswordVisible)
|
||||
stringResource(R.string.hide_password)
|
||||
else stringResource(R.string.show_password),
|
||||
)
|
||||
}
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = showPasswordError,
|
||||
)
|
||||
}
|
||||
|
||||
if (showPasswordError) {
|
||||
Text(
|
||||
text = stringResource(R.string.passwords_do_not_match),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -58,7 +58,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -245,7 +245,7 @@
|
||||
<string name="root_required_template">%1$s (vyžaduje root)</string>
|
||||
<string name="recommended_template">%1$s (doporučeno)</string>
|
||||
<string name="hint_template">(%1$s)</string>
|
||||
<string name="backup_success">Úspěšně zazálohováno. %1$s</string>
|
||||
<string name="backup_success">Úspěšně zazálohováno</string>
|
||||
<string name="config_error_template">Špatná konfigurace. %1$s v umístění: %2$s.</string>
|
||||
<string name="donation_dev_message">Jako jediný vývojář neúnavně pracuji na tom, aby se WG Tunnel stal nejlepším bezplatným a open-source WireGuard klientem pro Android, ale to je možné pouze s vaší podporou.</string>
|
||||
<string name="google_donation_message">Bohužel, kvůli pravidlům společnosti Google nejsou odkazy na darování povoleny ve verzi této aplikace z Obchodu Play. Projděte si prosím webové stránky projektu, abyste zjistili, kde můžete přispět.</string>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
<string name="auto_tunnel_running">Auto-Tunnel läuft</string>
|
||||
<string name="auto_tunnel_not_running">Auto-Tunnel läuft nicht</string>
|
||||
<string name="tunnel_monitoring">Tunnelüberwachung</string>
|
||||
<string name="backup_success">Backuperfolg. %1$s</string>
|
||||
<string name="backup_success">Backuperfolg</string>
|
||||
<string name="restore_success">Wiederherstellerfolg. %1$s</string>
|
||||
<string name="restarting_app">Starte App neu, um Änderungen anzuwenden …</string>
|
||||
<string name="restore_failed">Wiederherstellung aus Backup fehlgeschlagen.</string>
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
<string name="auto_tunnel_running">El túnel automático está activo</string>
|
||||
<string name="auto_tunnel_not_running">El túnel automático no está activo</string>
|
||||
<string name="tunnel_monitoring">Monitoreo del túnel</string>
|
||||
<string name="backup_success">Copia de seguridad realizada con éxito. %1$s</string>
|
||||
<string name="backup_success">Copia de seguridad realizada con éxito</string>
|
||||
<string name="restore_success">Restauración completada. %1$s</string>
|
||||
<string name="restarting_app">Reiniciando la app para aplicar los cambios…</string>
|
||||
<string name="restore_failed">No se pudo restaurar la copia de seguridad.</string>
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
<string name="recommended_template">%1$s (soovitatav)</string>
|
||||
<string name="hint_template">(%1$s)</string>
|
||||
<string name="tunnel_monitoring">Tunnelo monitooring</string>
|
||||
<string name="backup_success">Varundus õnnestus. %1$s</string>
|
||||
<string name="backup_success">Varundus õnnestus</string>
|
||||
<string name="restore_success">Taastamine õnnestus. %1$s</string>
|
||||
<string name="restarting_app">Muudatuste jõustamiseks taaskäivitan rakenduse…</string>
|
||||
<string name="restore_failed">Varukoopiast taastamine ei õnnestunud.</string>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
<string name="mimic_quic">Imiter QUIC</string>
|
||||
<string name="show_qr">Afficher le QR</string>
|
||||
<string name="wifi_settings">Paramètres Wi-Fi</string>
|
||||
<string name="backup_success">Sauvegarde réussie. %1$s</string>
|
||||
<string name="backup_success">Sauvegarde réussie</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Échec de la création de la sauvegarde.</string>
|
||||
<string name="location_permissions">Permissions de localisation</string>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="wifi_settings">Wi-Fi beállítások</string>
|
||||
<string name="tunnel_on_wifi">Alagút Wi-Fi-n</string>
|
||||
<string name="add_peer">Peer hozzáadása</string>
|
||||
<string name="backup_success">Sikeres mentés. %1$s</string>
|
||||
<string name="backup_success">Sikeres mentés</string>
|
||||
<string name="persistent_keepalive">Kapcsolatmegőrzés</string>
|
||||
<string name="info">Infó</string>
|
||||
<string name="exclude">Kizárás</string>
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
<string name="auto_tunnel_channel_description">Saluran untuk notifikasi status terowongan otomatis</string>
|
||||
<string name="show_qr">Tampilkan QR</string>
|
||||
<string name="wifi_settings">Pengaturan Wi-Fi</string>
|
||||
<string name="backup_success">Pencadangan berhasil. %1$s</string>
|
||||
<string name="backup_success">Pencadangan berhasil</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Kecualikan</string>
|
||||
<string name="backup_failed">Gagal membuat cadangan.</string>
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
<string name="restore_failed">Impossibile ripristinare dal backup.</string>
|
||||
<string name="restarting_app">Riavviando l\'app per applicare le modifiche…</string>
|
||||
<string name="restore_success">Ripristino riuscito. %1$s</string>
|
||||
<string name="backup_success">Bakcup riuscito. %1$s</string>
|
||||
<string name="backup_success">Bakcup riuscito</string>
|
||||
<string name="current_template">Corrente: %1$s</string>
|
||||
<string name="root_required_template">%1$s (root richiesto)</string>
|
||||
<string name="recommended_template">%1$s (raccomandato)</string>
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
<string name="auto_tunnel_channel_description">A channel for auto-tunnel state notifications</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="junk_packet_minimum_size">Junk packet minimum size</string>
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
<string name="auto_tunnel_channel_description">A channel for auto-tunnel state notifications</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="junk_packet_minimum_size">Junk packet minimum size</string>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<string name="wifi_settings">Wi-Fi 설정</string>
|
||||
<string name="tunnel_on_wifi">Wi-Fi에서 터널 사용</string>
|
||||
<string name="add_peer">피어 추가</string>
|
||||
<string name="backup_success">백업 성공. %1$s</string>
|
||||
<string name="backup_success">백업 성공</string>
|
||||
<string name="persistent_keepalive">지속적 연결 유지</string>
|
||||
<string name="info">정보</string>
|
||||
<string name="exclude">제외</string>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
<string name="website">App website</string>
|
||||
<string name="mimic_quic">Mimic QUIC</string>
|
||||
<string name="wifi_settings">Wi-Fi instellingen</string>
|
||||
<string name="backup_success">Backup succesvol. %1$s</string>
|
||||
<string name="backup_success">Backup succesvol</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Backup maken mislukt.</string>
|
||||
<string name="unknown">Onbekend</string>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
<string name="auto_tunnel_running">Autotunel jest uruchomiony</string>
|
||||
<string name="auto_tunnel_not_running">Autotunel nie jest uruchomiony</string>
|
||||
<string name="tunnel_monitoring">Monitorowanie tunelu</string>
|
||||
<string name="backup_success">Powodzenie tworzenia kopii zapasowej. %1$s</string>
|
||||
<string name="backup_success">Powodzenie tworzenia kopii zapasowej</string>
|
||||
<string name="restore_success">Powodzenie przywracania. %1$s</string>
|
||||
<string name="restarting_app">Ponowne uruchomienie aplikacji w celu zastosowania zmian…</string>
|
||||
<string name="restore_failed">Nie udało się przywrócić danych z kopii zapasowej.</string>
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
<string name="auto_tunnel_channel_description">A channel for auto-tunnel state notifications</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="location_permissions">Location Permissions</string>
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
<string name="auto_tunnel_channel_description">A channel for auto-tunnel state notifications</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="location_permissions">Location Permissions</string>
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
<string name="backup_application">Резервирование данных</string>
|
||||
<string name="restore_application">Восстановление данных</string>
|
||||
<string name="tunnel_monitoring">Отслеживание туннеля</string>
|
||||
<string name="backup_success">Резервное копирование выполнено. %1$s</string>
|
||||
<string name="backup_success">Резервное копирование выполнено</string>
|
||||
<string name="restore_success">Восстановление выполнено. %1$s</string>
|
||||
<string name="root_required_template">%1$s (требуется root)</string>
|
||||
<string name="recommended_template">%1$s (рекомендуется)</string>
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
<string name="auto_tunnel_channel_description">A channel for auto-tunnel state notifications</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="junk_packet_minimum_size">Junk packet minimálna veľkosť</string>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
<string name="mimic_quic">Mimic QUIC</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="location_permissions">Location Permissions</string>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Exclude</string>
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
<string name="mimic_quic">Mimic QUIC</string>
|
||||
<string name="show_qr">Show QR</string>
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
<string name="location_permissions">Location Permissions</string>
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
<string name="mimic_quic">Імітація QUIC</string>
|
||||
<string name="show_qr">Показати QR-код</string>
|
||||
<string name="wifi_settings">Параметри Wi-Fi</string>
|
||||
<string name="backup_success">Рез. копіювання успішне. %1$s</string>
|
||||
<string name="backup_success">Рез. копіювання успішне</string>
|
||||
<string name="info">Інформація</string>
|
||||
<string name="backup_failed">Не вдалося створити резервну копію.</string>
|
||||
<string name="location_permissions">Дозволи на доступ до геолокації</string>
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
<string name="auto_tunnel_running">آٹو ٹنل چل رہا ہے</string>
|
||||
<string name="auto_tunnel_not_running">آٹو ٹنل نہیں چل رہا ہے</string>
|
||||
<string name="tunnel_monitoring">ٹنل کی نگرانی</string>
|
||||
<string name="backup_success">بیک اپ کامیاب۔ %1$s</string>
|
||||
<string name="backup_success">بیک اپ کامیاب۔</string>
|
||||
<string name="restore_success">بحالی کامیاب۔ %1$s</string>
|
||||
<string name="restarting_app">تبدیلیاں لاگو کرنے کے لیے ایپ کو دوبارہ شروع کیا جا رہا ہے…</string>
|
||||
<string name="restore_failed">بیک اپ سے بحالی ناکام۔</string>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<string name="wifi_settings">Wi-Fi settings</string>
|
||||
<string name="tunnel_on_wifi">Tunnel on Wi-Fi</string>
|
||||
<string name="add_peer">Add peer</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="persistent_keepalive">Persistent keepalive</string>
|
||||
<string name="info">Info</string>
|
||||
<string name="exclude">Loại trừ</string>
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
<string name="auto_tunnel_running">自动隧道运行中</string>
|
||||
<string name="auto_tunnel_not_running">自动隧道未运行</string>
|
||||
<string name="tunnel_monitoring">隧道监控</string>
|
||||
<string name="backup_success">成功备份:%1$s</string>
|
||||
<string name="backup_success">成功备份</string>
|
||||
<string name="restore_success">成功恢复:%1$s</string>
|
||||
<string name="restarting_app">正重启应用来应用更改…</string>
|
||||
<string name="restore_failed">未能从备份恢复。</string>
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
<string name="restarting_app">正在重啟應用程式以應用變更…</string>
|
||||
<string name="restore_application">從備份還原</string>
|
||||
<string name="restore_success">還原成功。 %1$s</string>
|
||||
<string name="backup_success">備份成功。%1$s</string>
|
||||
<string name="backup_success">備份成功</string>
|
||||
<string name="backup_application">備份應用程式資料</string>
|
||||
<string name="restore_failed">還原備份失敗。</string>
|
||||
<string name="backup_failed">建立備份失敗。</string>
|
||||
|
||||
@@ -231,8 +231,8 @@
|
||||
<string name="auto_tunnel_running">Auto-tunnel is running</string>
|
||||
<string name="auto_tunnel_not_running">Auto-tunnel is not running</string>
|
||||
<string name="tunnel_monitoring">Tunnel monitoring</string>
|
||||
<string name="backup_success">Backup success. %1$s</string>
|
||||
<string name="restore_success">Restore success. %1$s</string>
|
||||
<string name="backup_success">Backup success</string>
|
||||
<string name="restore_success">Restore success</string>
|
||||
<string name="restarting_app">Restarting app to apply changes…</string>
|
||||
<string name="restore_failed">Failed to restore from backup.</string>
|
||||
<string name="backup_failed">Failed to create backup.</string>
|
||||
@@ -525,4 +525,14 @@
|
||||
<string name="remote_control">Remote control</string>
|
||||
<string name="remote_control_desc">Allow other apps (like Tasker) to control tunnels</string>
|
||||
<string name="endpoint_template">endpoint: %1$s</string>
|
||||
|
||||
<string name="encrypted">Encrypted</string>
|
||||
<string name="confirm_password">Confirm password</string>
|
||||
<string name="passwords_do_not_match">Passwords do not match</string>
|
||||
|
||||
<string name="backup">Backup</string>
|
||||
<string name="restore">Restore</string>
|
||||
<string name="hide_password">Hide password</string>
|
||||
<string name="restore_failed_wrong_password">Restore failed. Wrong password</string>
|
||||
<string name="restore_failed_invalid_file">Restore failed. Select a valid backup file (.sqlite3 or .sqlite3.aes)</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user