[](https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/)||
-Languages in progress: Arabic, Hindi, Japanese, Spanish and [many others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed – please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us!
+Languages in progress: Arabic, Japanese, Korean, Portuguese and [others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed – please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us!
## Contribute
diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle
index 86834a8547..2028d9d4a8 100644
--- a/apps/android/app/build.gradle
+++ b/apps/android/app/build.gradle
@@ -77,7 +77,7 @@ android {
def isBundle = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("bundle") }) != null
// if (isRelease) {
// Comma separated list of languages that will be included in the apk
- android.defaultConfig.resConfigs("en", "cs", "de", "es", "fr", "it", "nl", "ru", "zh-rCN")
+ android.defaultConfig.resConfigs("en", "cs", "de", "es", "fr", "it", "nl", "pl", "ru", "zh-rCN")
// }
if (isBundle) {
defaultConfig.ndk.abiFilters 'arm64-v8a', 'armeabi-v7a'
diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
index 0c147eba16..c35ae1d418 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt
@@ -1,5 +1,6 @@
package chat.simplex.app
+import SectionItemView
import android.app.Application
import android.content.Intent
import android.net.Uri
@@ -12,8 +13,7 @@ import androidx.activity.viewModels
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
+import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Lock
import androidx.compose.runtime.*
@@ -21,12 +21,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.*
-import chat.simplex.app.model.ChatModel
-import chat.simplex.app.model.NtfManager
+import chat.simplex.app.MainActivity.Companion.enteredBackground
+import chat.simplex.app.model.*
import chat.simplex.app.model.NtfManager.Companion.getUserIdFromIntent
import chat.simplex.app.ui.theme.SimpleButton
import chat.simplex.app.ui.theme.SimpleXTheme
@@ -37,8 +38,11 @@ import chat.simplex.app.views.chat.ChatView
import chat.simplex.app.views.chatlist.*
import chat.simplex.app.views.database.DatabaseErrorView
import chat.simplex.app.views.helpers.*
+import chat.simplex.app.views.helpers.DatabaseUtils.ksAppPassword
+import chat.simplex.app.views.localauth.SetAppPasscodeView
import chat.simplex.app.views.newchat.*
import chat.simplex.app.views.onboarding.*
+import chat.simplex.app.views.usersettings.LAMode
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -93,7 +97,7 @@ class MainActivity: FragmentActivity() {
laFailed,
::runAuthenticate,
::setPerformLA,
- showLANotice = { m.controller.showLANotice(this) }
+ showLANotice = { showLANotice(m.controller.appPrefs.laNoticeShown, this) }
)
}
}
@@ -111,7 +115,8 @@ class MainActivity: FragmentActivity() {
override fun onResume() {
super.onResume()
val enteredBackgroundVal = enteredBackground.value
- if (enteredBackgroundVal == null || elapsedRealtime() - enteredBackgroundVal >= 30_000) {
+ val delay = vm.chatModel.controller.appPrefs.laLockDelay.get()
+ if (enteredBackgroundVal == null || elapsedRealtime() - enteredBackgroundVal >= delay * 1000) {
runAuthenticate()
}
}
@@ -165,16 +170,27 @@ class MainActivity: FragmentActivity() {
delay(50)
withContext(Dispatchers.Main) {
authenticate(
- generalGetString(R.string.auth_unlock),
- generalGetString(R.string.auth_log_in_using_credential),
+ if (m.controller.appPrefs.laMode.get() == LAMode.SYSTEM)
+ generalGetString(R.string.auth_unlock)
+ else
+ generalGetString(R.string.la_enter_app_passcode),
+ if (m.controller.appPrefs.laMode.get() == LAMode.SYSTEM)
+ generalGetString(R.string.auth_log_in_using_credential)
+ else
+ generalGetString(R.string.auth_unlock),
this@MainActivity,
completed = { laResult ->
when (laResult) {
LAResult.Success ->
userAuthorized.value = true
- is LAResult.Error, LAResult.Failed ->
+ is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
+ is LAResult.Error -> {
laFailed.value = true
- LAResult.Unavailable -> {
+ if (m.controller.appPrefs.laMode.get() == LAMode.PASSCODE) {
+ laFailedAlert()
+ }
+ }
+ is LAResult.Unavailable -> {
userAuthorized.value = true
m.performLA.value = false
m.controller.appPrefs.performLA.set(false)
@@ -188,21 +204,116 @@ class MainActivity: FragmentActivity() {
}
}
- private fun setPerformLA(on: Boolean) {
- vm.chatModel.controller.appPrefs.laNoticeShown.set(true)
- if (on) {
- enableLA()
- } else {
- disableLA()
+ private fun showLANotice(laNoticeShown: SharedPreference, activity: FragmentActivity) {
+ Log.d(TAG, "showLANotice")
+ if (!laNoticeShown.get()) {
+ laNoticeShown.set(true)
+ AlertManager.shared.showAlertDialog(
+ title = generalGetString(R.string.la_notice_title_simplex_lock),
+ text = generalGetString(R.string.la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled),
+ confirmText = generalGetString(R.string.la_notice_turn_on),
+ onConfirm = {
+ withBGApi { // to remove this call, change ordering of onConfirm call in AlertManager
+ showChooseLAMode(laNoticeShown, activity)
+ }
+ }
+ )
}
}
- private fun enableLA() {
+ private fun showChooseLAMode(laNoticeShown: SharedPreference, activity: FragmentActivity) {
+ Log.d(TAG, "showLANotice")
+ laNoticeShown.set(true)
+ AlertManager.shared.showAlertDialogStacked(
+ title = generalGetString(R.string.la_lock_mode),
+ text = null,
+ confirmText = generalGetString(R.string.la_lock_mode_passcode),
+ dismissText = generalGetString(R.string.la_lock_mode_system),
+ onConfirm = {
+ AlertManager.shared.hideAlert()
+ setPasscode()
+ },
+ onDismiss = {
+ AlertManager.shared.hideAlert()
+ initialEnableLA(activity)
+ }
+ )
+ }
+
+ private fun initialEnableLA(activity: FragmentActivity) {
val m = vm.chatModel
+ val appPrefs = m.controller.appPrefs
+ m.controller.appPrefs.laMode.set(LAMode.SYSTEM)
authenticate(
generalGetString(R.string.auth_enable_simplex_lock),
generalGetString(R.string.auth_confirm_credential),
- this@MainActivity,
+ activity,
+ completed = { laResult ->
+ when (laResult) {
+ LAResult.Success -> {
+ m.performLA.value = true
+ appPrefs.performLA.set(true)
+ laTurnedOnAlert()
+ }
+ is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
+ is LAResult.Error -> {
+ m.performLA.value = false
+ appPrefs.performLA.set(false)
+ laFailedAlert()
+ }
+ is LAResult.Unavailable -> {
+ m.performLA.value = false
+ appPrefs.performLA.set(false)
+ m.showAdvertiseLAUnavailableAlert.value = true
+ }
+ }
+ }
+ )
+ }
+
+ private fun setPasscode() {
+ val chatModel = vm.chatModel
+ val appPrefs = chatModel.controller.appPrefs
+ ModalManager.shared.showCustomModal { close ->
+ Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
+ SetAppPasscodeView(
+ submit = {
+ chatModel.performLA.value = true
+ appPrefs.performLA.set(true)
+ appPrefs.laMode.set(LAMode.PASSCODE)
+ laTurnedOnAlert()
+ },
+ cancel = {
+ chatModel.performLA.value = false
+ appPrefs.performLA.set(false)
+ laPasscodeNotSetAlert()
+ },
+ close)
+ }
+ }
+ }
+
+ private fun setPerformLA(on: Boolean, activity: FragmentActivity) {
+ vm.chatModel.controller.appPrefs.laNoticeShown.set(true)
+ if (on) {
+ enableLA(activity)
+ } else {
+ disableLA(activity)
+ }
+ }
+
+ private fun enableLA(activity: FragmentActivity) {
+ val m = vm.chatModel
+ authenticate(
+ if (m.controller.appPrefs.laMode.get() == LAMode.SYSTEM)
+ generalGetString(R.string.auth_enable_simplex_lock)
+ else
+ generalGetString(R.string.new_passcode),
+ if (m.controller.appPrefs.laMode.get() == LAMode.SYSTEM)
+ generalGetString(R.string.auth_confirm_credential)
+ else
+ "",
+ activity,
completed = { laResult ->
val prefPerformLA = m.controller.appPrefs.performLA
when (laResult) {
@@ -211,11 +322,13 @@ class MainActivity: FragmentActivity() {
prefPerformLA.set(true)
laTurnedOnAlert()
}
- is LAResult.Error, LAResult.Failed -> {
+ is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
+ is LAResult.Error -> {
m.performLA.value = false
prefPerformLA.set(false)
+ laFailedAlert()
}
- LAResult.Unavailable -> {
+ is LAResult.Unavailable -> {
m.performLA.value = false
prefPerformLA.set(false)
laUnavailableInstructionAlert()
@@ -225,24 +338,33 @@ class MainActivity: FragmentActivity() {
)
}
- private fun disableLA() {
+ private fun disableLA(activity: FragmentActivity) {
val m = vm.chatModel
authenticate(
- generalGetString(R.string.auth_disable_simplex_lock),
- generalGetString(R.string.auth_confirm_credential),
- this@MainActivity,
+ if (m.controller.appPrefs.laMode.get() == LAMode.SYSTEM)
+ generalGetString(R.string.auth_disable_simplex_lock)
+ else
+ generalGetString(R.string.la_enter_app_passcode),
+ if (m.controller.appPrefs.laMode.get() == LAMode.SYSTEM)
+ generalGetString(R.string.auth_confirm_credential)
+ else
+ generalGetString(R.string.auth_disable_simplex_lock),
+ activity,
completed = { laResult ->
val prefPerformLA = m.controller.appPrefs.performLA
when (laResult) {
LAResult.Success -> {
m.performLA.value = false
prefPerformLA.set(false)
+ ksAppPassword.remove()
}
- is LAResult.Error, LAResult.Failed -> {
+ is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
+ is LAResult.Error -> {
m.performLA.value = true
prefPerformLA.set(true)
+ laFailedAlert()
}
- LAResult.Unavailable -> {
+ is LAResult.Unavailable -> {
m.performLA.value = false
prefPerformLA.set(false)
laUnavailableTurningOffAlert()
@@ -264,7 +386,7 @@ fun MainPage(
userAuthorized: MutableState,
laFailed: MutableState,
runAuthenticate: () -> Unit,
- setPerformLA: (Boolean) -> Unit,
+ setPerformLA: (Boolean, FragmentActivity) -> Unit,
showLANotice: () -> Unit
) {
var showChatDatabaseError by rememberSaveable {
@@ -392,6 +514,14 @@ fun MainPage(
if (invitation != null) IncomingCallAlertView(invitation, chatModel)
AlertManager.shared.showInView()
}
+
+ DisposableEffectOnRotate {
+ // When using lock delay = 0 and screen rotates, the app will be locked which is not useful.
+ // Let's prolong the unlocked period to 3 sec for screen rotation to take place
+ if (chatModel.controller.appPrefs.laLockDelay.get() == 0) {
+ enteredBackground.value = elapsedRealtime() + 3000
+ }
+ }
}
fun processNotificationIntent(intent: Intent?, chatModel: ChatModel) {
diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexService.kt b/apps/android/app/src/main/java/chat/simplex/app/SimplexService.kt
index 4f5eb464b5..63e31cb226 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/SimplexService.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/SimplexService.kt
@@ -326,4 +326,4 @@ class SimplexService: Service() {
private fun getPreferences(context: Context): SharedPreferences = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE)
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt
index b5fd441123..7f295029d9 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt
@@ -20,7 +20,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import androidx.fragment.app.FragmentActivity
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.ui.theme.*
@@ -84,6 +83,8 @@ class AppPreferences(val context: Context) {
set = fun(action: CallOnLockScreen) { _callOnLockScreen.set(action.name) }
)
val performLA = mkBoolPreference(SHARED_PREFS_PERFORM_LA, false)
+ val laMode = mkEnumPreference(SHARED_PREFS_LA_MODE, LAMode.SYSTEM) { LAMode.values().firstOrNull { it.name == this } }
+ val laLockDelay = mkIntPreference(SHARED_PREFS_LA_LOCK_DELAY, 30)
val laNoticeShown = mkBoolPreference(SHARED_PREFS_LA_NOTICE_SHOWN, false)
val webrtcIceServers = mkStrPreference(SHARED_PREFS_WEBRTC_ICE_SERVERS, null)
val privacyProtectScreen = mkBoolPreference(SHARED_PREFS_PRIVACY_PROTECT_SCREEN, true)
@@ -142,6 +143,8 @@ class AppPreferences(val context: Context) {
val initialRandomDBPassphrase = mkBoolPreference(SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE, false)
val encryptedDBPassphrase = mkStrPreference(SHARED_PREFS_ENCRYPTED_DB_PASSPHRASE, null)
val initializationVectorDBPassphrase = mkStrPreference(SHARED_PREFS_INITIALIZATION_VECTOR_DB_PASSPHRASE, null)
+ val encryptedAppPassphrase = mkStrPreference(SHARED_PREFS_ENCRYPTED_APP_PASSPHRASE, null)
+ val initializationVectorAppPassphrase = mkStrPreference(SHARED_PREFS_INITIALIZATION_VECTOR_APP_PASSPHRASE, null)
val encryptionStartedAt = mkDatePreference(SHARED_PREFS_ENCRYPTION_STARTED_AT, null, true)
val confirmDBUpgrades = mkBoolPreference(SHARED_PREFS_CONFIRM_DB_UPGRADES, false)
@@ -184,6 +187,12 @@ class AppPreferences(val context: Context) {
set = fun(value) = sharedPreferences.edit().putString(prefName, value).apply()
)
+ private fun mkEnumPreference(prefName: String, default: T, construct: String.() -> T?): SharedPreference =
+ SharedPreference(
+ get = fun() = sharedPreferences.getString(prefName, default.toString())?.construct() ?: default,
+ set = fun(value) = sharedPreferences.edit().putString(prefName, value.toString()).apply()
+ )
+
/**
* Provide `[commit] = true` to save preferences right now, not after some unknown period of time.
* So in case of a crash this value will be saved 100%
@@ -210,6 +219,8 @@ class AppPreferences(val context: Context) {
private const val SHARED_PREFS_WEBRTC_POLICY_RELAY = "WebrtcPolicyRelay"
private const val SHARED_PREFS_WEBRTC_CALLS_ON_LOCK_SCREEN = "CallsOnLockScreen"
private const val SHARED_PREFS_PERFORM_LA = "PerformLA"
+ private const val SHARED_PREFS_LA_MODE = "LocalAuthenticationMode"
+ private const val SHARED_PREFS_LA_LOCK_DELAY = "LocalAuthenticationLockDelay"
private const val SHARED_PREFS_LA_NOTICE_SHOWN = "LANoticeShown"
private const val SHARED_PREFS_WEBRTC_ICE_SERVERS = "WebrtcICEServers"
private const val SHARED_PREFS_PRIVACY_PROTECT_SCREEN = "PrivacyProtectScreen"
@@ -246,6 +257,8 @@ class AppPreferences(val context: Context) {
private const val SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE = "InitialRandomDBPassphrase"
private const val SHARED_PREFS_ENCRYPTED_DB_PASSPHRASE = "EncryptedDBPassphrase"
private const val SHARED_PREFS_INITIALIZATION_VECTOR_DB_PASSPHRASE = "InitializationVectorDBPassphrase"
+ private const val SHARED_PREFS_ENCRYPTED_APP_PASSPHRASE = "EncryptedAppPassphrase"
+ private const val SHARED_PREFS_INITIALIZATION_VECTOR_APP_PASSPHRASE = "InitializationVectorAppPassphrase"
private const val SHARED_PREFS_ENCRYPTION_STARTED_AT = "EncryptionStartedAt"
private const val SHARED_PREFS_CONFIRM_DB_UPGRADES = "ConfirmDBUpgrades"
private const val SHARED_PREFS_CURRENT_THEME = "CurrentTheme"
@@ -1707,43 +1720,6 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
)
}
- fun showLANotice(activity: FragmentActivity) {
- Log.d(TAG, "showLANotice")
- if (!appPrefs.laNoticeShown.get()) {
- appPrefs.laNoticeShown.set(true)
- AlertManager.shared.showAlertDialog(
- title = generalGetString(R.string.la_notice_title_simplex_lock),
- text = generalGetString(R.string.la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled),
- confirmText = generalGetString(R.string.la_notice_turn_on),
- onConfirm = {
- authenticate(
- generalGetString(R.string.auth_enable_simplex_lock),
- generalGetString(R.string.auth_confirm_credential),
- activity,
- completed = { laResult ->
- when (laResult) {
- LAResult.Success -> {
- chatModel.performLA.value = true
- appPrefs.performLA.set(true)
- laTurnedOnAlert()
- }
- is LAResult.Error, LAResult.Failed -> {
- chatModel.performLA.value = false
- appPrefs.performLA.set(false)
- }
- LAResult.Unavailable -> {
- chatModel.performLA.value = false
- appPrefs.performLA.set(false)
- chatModel.showAdvertiseLAUnavailableAlert.value = true
- }
- }
- }
- )
- }
- )
- }
- }
-
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
val powerManager = context.getSystemService(Application.POWER_SERVICE) as PowerManager
return powerManager.isIgnoringBatteryOptimizations(context.packageName)
@@ -3422,7 +3398,6 @@ class AutoAccept(val acceptIncognito: Boolean, val autoReply: MsgContent?) {
@Serializable
data class CoreVersionInfo(
val version: String,
- val buildTimestamp: String,
val simplexmqVersion: String,
val simplexmqCommit: String
)
@@ -3669,4 +3644,4 @@ sealed class XFTPErrorType {
@Serializable @SerialName("HAS_FILE") object HAS_FILE: XFTPErrorType()
@Serializable @SerialName("FILE_IO") object FILE_IO: XFTPErrorType()
@Serializable @SerialName("INTERNAL") object INTERNAL: XFTPErrorType()
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt
index d0a00450f6..79ab4eead4 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Shape.kt
@@ -8,4 +8,4 @@ val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp)
-)
\ No newline at end of file
+)
diff --git a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt
index 7590ecdcfb..8b05e28925 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/ui/theme/Theme.kt
@@ -74,4 +74,4 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) {
shapes = Shapes,
content = content
)
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt
index b60df05f9e..fbd765213e 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt
@@ -148,4 +148,4 @@ fun ProfileNameField(name: MutableState, focusRequester: FocusRequester?
singleLine = true,
cursorBrush = SolidColor(HighOrLowlight)
)
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt b/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt
index 2a4b397840..a88f07d3c5 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt
@@ -106,4 +106,4 @@ class CallManager(val chatModel: ChatModel) {
chatModel.controller.ntfManager.cancelCallNotification()
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt b/apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt
index 009ffdf6e1..456f50d0bd 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/call/SoundPlayer.kt
@@ -48,4 +48,4 @@ class SoundPlayer {
companion object {
val shared = SoundPlayer()
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt
index fd88ccdc34..58c13e8bb5 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/VerifyCodeView.kt
@@ -134,4 +134,4 @@ private fun splitToParts(s: String, length: Int): String {
return (0..(s.length - 1) / length)
.map { s.drop(it * length).take(length) }
.joinToString(separator = "\n")
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt
index 5ab80774c6..4c9bbae077 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CICallItemView.kt
@@ -154,4 +154,4 @@ fun AcceptCallButton(cInfo: ChatInfo, acceptCall: (Contact) -> Unit) {
// Image(systemName: "phone.arrow.down.left").foregroundColor(.secondary)
// }
// }
-//}
\ No newline at end of file
+//}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt
index 86647a0ef1..89f0a07743 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt
@@ -21,6 +21,7 @@ import androidx.compose.ui.text.capitalize
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.unit.*
+import androidx.fragment.app.FragmentActivity
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.model.*
@@ -36,7 +37,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
@Composable
-fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: Boolean) {
+fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) -> Unit, stopped: Boolean) {
val newChatSheetState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) }
val userPickerState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) }
val showNewChatSheet = {
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt
index 7a616301b7..ff6229e9e6 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt
@@ -42,9 +42,9 @@ fun DatabaseEncryptionView(m: ChatModel) {
val prefs = m.controller.appPrefs
val useKeychain = remember { mutableStateOf(prefs.storeDBPassphrase.get()) }
val initialRandomDBPassphrase = remember { mutableStateOf(prefs.initialRandomDBPassphrase.get()) }
- val storedKey = remember { val key = DatabaseUtils.getDatabaseKey(); mutableStateOf(key != null && key != "") }
+ val storedKey = remember { val key = DatabaseUtils.ksDatabasePassword.get(); mutableStateOf(key != null && key != "") }
// Do not do rememberSaveable on current key to prevent saving it on disk in clear text
- val currentKey = remember { mutableStateOf(if (initialRandomDBPassphrase.value) DatabaseUtils.getDatabaseKey() ?: "" else "") }
+ val currentKey = remember { mutableStateOf(if (initialRandomDBPassphrase.value) DatabaseUtils.ksDatabasePassword.get() ?: "" else "") }
val newKey = rememberSaveable { mutableStateOf("") }
val confirmNewKey = rememberSaveable { mutableStateOf("") }
@@ -89,7 +89,7 @@ fun DatabaseEncryptionView(m: ChatModel) {
prefs.initialRandomDBPassphrase.set(false)
initialRandomDBPassphrase.value = false
if (useKeychain.value) {
- DatabaseUtils.setDatabaseKey(newKey.value)
+ DatabaseUtils.ksDatabasePassword.set(newKey.value)
}
resetFormAfterEncryption(m, initialRandomDBPassphrase, currentKey, newKey, confirmNewKey, storedKey, useKeychain.value)
operationEnded(m, progressIndicator) {
@@ -150,7 +150,7 @@ fun DatabaseEncryptionLayout(
text = generalGetString(R.string.notifications_will_be_hidden) + "\n" + storeSecurelyDanger(),
confirmText = generalGetString(R.string.remove_passphrase),
onConfirm = {
- DatabaseUtils.removeDatabaseKey()
+ DatabaseUtils.ksDatabasePassword.remove()
setUseKeychain(false, useKeychain, prefs)
storedKey.value = false
},
@@ -522,4 +522,4 @@ fun PreviewDatabaseEncryptionLayout() {
onConfirmEncrypt = {},
)
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt
index 05a8288ada..a59e2820be 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt
@@ -38,7 +38,7 @@ fun DatabaseErrorView(
) {
val progressIndicator = remember { mutableStateOf(false) }
val dbKey = remember { mutableStateOf("") }
- var storedDBKey by remember { mutableStateOf(DatabaseUtils.getDatabaseKey()) }
+ var storedDBKey by remember { mutableStateOf(DatabaseUtils.ksDatabasePassword.get()) }
var useKeychain by remember { mutableStateOf(appPreferences.storeDBPassphrase.get()) }
val context = LocalContext.current
val restoreDbFromBackup = remember { mutableStateOf(shouldShowRestoreDbButton(appPreferences, context)) }
@@ -49,7 +49,7 @@ fun DatabaseErrorView(
}
fun saveAndRunChatOnClick() {
- DatabaseUtils.setDatabaseKey(dbKey.value)
+ DatabaseUtils.ksDatabasePassword.set(dbKey.value)
storedDBKey = dbKey.value
appPreferences.storeDBPassphrase.set(true)
useKeychain = true
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt
index 4c9bcf6b9f..869a52cd23 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt
@@ -52,7 +52,7 @@ fun DatabaseView(
) {
val context = LocalContext.current
val progressIndicator = remember { mutableStateOf(false) }
- val runChat = remember { mutableStateOf(m.chatRunning.value ?: true) }
+ val runChat = remember { m.chatRunning }
val prefs = m.controller.appPrefs
val useKeychain = remember { mutableStateOf(prefs.storeDBPassphrase.get()) }
val chatArchiveName = remember { mutableStateOf(prefs.chatArchiveName.get()) }
@@ -76,7 +76,7 @@ fun DatabaseView(
) {
DatabaseLayout(
progressIndicator.value,
- runChat.value,
+ runChat.value != false,
m.chatDbChanged.value,
useKeychain.value,
m.chatDbEncrypted.value,
@@ -388,7 +388,7 @@ fun chatArchiveTitle(chatArchiveTime: Instant, chatLastStart: Instant): String {
return stringResource(if (chatArchiveTime < chatLastStart) R.string.old_database_archive else R.string.new_database_archive)
}
-private fun startChat(m: ChatModel, runChat: MutableState, chatLastStart: MutableState, chatDbChanged: MutableState) {
+private fun startChat(m: ChatModel, runChat: MutableState, chatLastStart: MutableState, chatDbChanged: MutableState) {
withApi {
try {
if (chatDbChanged.value) {
@@ -417,7 +417,7 @@ private fun startChat(m: ChatModel, runChat: MutableState, chatLastStar
}
}
-private fun stopChatAlert(m: ChatModel, runChat: MutableState, context: Context) {
+private fun stopChatAlert(m: ChatModel, runChat: MutableState, context: Context) {
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.stop_chat_question),
text = generalGetString(R.string.stop_chat_to_export_import_or_delete_chat_database),
@@ -434,7 +434,7 @@ private fun exportProhibitedAlert() {
)
}
-private fun authStopChat(m: ChatModel, runChat: MutableState, context: Context) {
+private fun authStopChat(m: ChatModel, runChat: MutableState, context: Context) {
if (m.controller.appPrefs.performLA.get()) {
authenticate(
generalGetString(R.string.auth_stop_chat),
@@ -442,12 +442,13 @@ private fun authStopChat(m: ChatModel, runChat: MutableState, context:
context as FragmentActivity,
completed = { laResult ->
when (laResult) {
- LAResult.Success, LAResult.Unavailable -> {
+ LAResult.Success, is LAResult.Unavailable -> {
stopChat(m, runChat, context)
}
is LAResult.Error -> {
+ runChat.value = true
}
- LAResult.Failed -> {
+ is LAResult.Failed -> {
runChat.value = true
}
}
@@ -458,7 +459,7 @@ private fun authStopChat(m: ChatModel, runChat: MutableState, context:
}
}
-private fun stopChat(m: ChatModel, runChat: MutableState, context: Context) {
+private fun stopChat(m: ChatModel, runChat: MutableState, context: Context) {
withApi {
try {
m.controller.apiStopChat()
@@ -592,7 +593,7 @@ private fun importArchive(
try {
val config = ArchiveConfig(archivePath, parentTempDirectory = context.cacheDir.toString())
m.controller.apiImportArchive(config)
- DatabaseUtils.removeDatabaseKey()
+ DatabaseUtils.ksDatabasePassword.remove()
appFilesCountAndSize.value = directoryFileCountAndSize(getAppFilesDirectory(context))
operationEnded(m, progressIndicator) {
AlertManager.shared.showAlertMsg(generalGetString(R.string.chat_database_imported), generalGetString(R.string.restart_the_app_to_use_imported_chat_database))
@@ -647,7 +648,7 @@ private fun deleteChat(m: ChatModel, progressIndicator: MutableState) {
try {
m.controller.apiDeleteStorage()
m.chatDbDeleted.value = true
- DatabaseUtils.removeDatabaseKey()
+ DatabaseUtils.ksDatabasePassword.remove()
m.controller.appPrefs.storeDBPassphrase.set(true)
operationEnded(m, progressIndicator) {
AlertManager.shared.showAlertMsg(generalGetString(R.string.chat_database_deleted), generalGetString(R.string.restart_the_app_to_create_a_new_chat_profile))
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt
index b723c69465..c82f2773d4 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DatabaseUtils.kt
@@ -3,6 +3,7 @@ package chat.simplex.app.views.helpers
import android.util.Log
import chat.simplex.app.*
import chat.simplex.app.model.AppPreferences
+import chat.simplex.app.model.SharedPreference
import chat.simplex.app.views.usersettings.Cryptor
import kotlinx.serialization.*
import java.io.File
@@ -16,30 +17,36 @@ object DatabaseUtils {
}
private const val DATABASE_PASSWORD_ALIAS: String = "databasePassword"
+ private const val APP_PASSWORD_ALIAS: String = "appPassword"
+
+ val ksDatabasePassword = KeyStoreItem(DATABASE_PASSWORD_ALIAS, appPreferences.encryptedDBPassphrase, appPreferences.initializationVectorDBPassphrase)
+ val ksAppPassword = KeyStoreItem(APP_PASSWORD_ALIAS, appPreferences.encryptedAppPassphrase, appPreferences.initializationVectorAppPassphrase)
+
+ class KeyStoreItem(private val alias: String, val passphrase: SharedPreference, val initVector: SharedPreference) {
+ fun get(): String? {
+ return cryptor.decryptData(
+ passphrase.get()?.toByteArrayFromBase64() ?: return null,
+ initVector.get()?.toByteArrayFromBase64() ?: return null,
+ alias,
+ )
+ }
+
+ fun set(key: String) {
+ val data = cryptor.encryptText(key, alias)
+ passphrase.set(data.first.toBase64String())
+ initVector.set(data.second.toBase64String())
+ }
+
+ fun remove() {
+ cryptor.deleteKey(alias)
+ passphrase.set(null)
+ initVector.set(null)
+ }
+ }
private fun hasDatabase(rootDir: String): Boolean =
File(rootDir + File.separator + "files_chat.db").exists() && File(rootDir + File.separator + "files_agent.db").exists()
- fun getDatabaseKey(): String? {
- return cryptor.decryptData(
- appPreferences.encryptedDBPassphrase.get()?.toByteArrayFromBase64() ?: return null,
- appPreferences.initializationVectorDBPassphrase.get()?.toByteArrayFromBase64() ?: return null,
- DATABASE_PASSWORD_ALIAS,
- )
- }
-
- fun setDatabaseKey(key: String) {
- val data = cryptor.encryptText(key, DATABASE_PASSWORD_ALIAS)
- appPreferences.encryptedDBPassphrase.set(data.first.toBase64String())
- appPreferences.initializationVectorDBPassphrase.set(data.second.toBase64String())
- }
-
- fun removeDatabaseKey() {
- cryptor.deleteKey(DATABASE_PASSWORD_ALIAS)
- appPreferences.encryptedDBPassphrase.set(null)
- appPreferences.initializationVectorDBPassphrase.set(null)
- }
-
fun useDatabaseKey(): String {
Log.d(TAG, "useDatabaseKey ${appPreferences.storeDBPassphrase.get()}")
var dbKey = ""
@@ -47,10 +54,10 @@ object DatabaseUtils {
if (useKeychain) {
if (!hasDatabase(SimplexApp.context.dataDir.absolutePath)) {
dbKey = randomDatabasePassword()
- setDatabaseKey(dbKey)
+ ksDatabasePassword.set(dbKey)
appPreferences.initialRandomDBPassphrase.set(true)
} else {
- dbKey = getDatabaseKey() ?: ""
+ dbKey = ksDatabasePassword.get() ?: ""
}
}
return dbKey
@@ -101,4 +108,4 @@ data class UpMigration(
sealed class MTRError {
@Serializable @SerialName("noDown") class NoDown(val dbMigrations: List): MTRError()
@Serializable @SerialName("different") class Different(val appMigration: String, val dbMigration: String): MTRError()
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt
index 51c9870897..18253a3a69 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultBasicTextField.kt
@@ -225,4 +225,4 @@ fun DefaultConfigurableTextField(
}
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt
index eb52ed137a..818f6b247b 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/LocalAuthentication.kt
@@ -1,36 +1,66 @@
package chat.simplex.app.views.helpers
-import android.content.Context
import android.os.Build.VERSION.SDK_INT
-import android.widget.Toast
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricManager.Authenticators.*
import androidx.biometric.BiometricPrompt
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import chat.simplex.app.R
+import chat.simplex.app.SimplexApp
+import chat.simplex.app.views.helpers.DatabaseUtils.ksAppPassword
+import chat.simplex.app.views.localauth.LocalAuthView
+import chat.simplex.app.views.usersettings.LAMode
sealed class LAResult {
object Success: LAResult()
class Error(val errString: CharSequence): LAResult()
- object Failed: LAResult()
- object Unavailable: LAResult()
+ class Failed(val errString: CharSequence? = null): LAResult()
+ class Unavailable(val errString: CharSequence? = null): LAResult()
+}
+
+data class LocalAuthRequest (
+ val title: String?,
+ val reason: String,
+ val password: String,
+ val completed: (LAResult) -> Unit
+) {
+ companion object {
+ val sample = LocalAuthRequest(generalGetString(R.string.la_enter_app_passcode), generalGetString(R.string.la_authenticate), "") { }
+ }
}
fun authenticate(
promptTitle: String,
promptSubtitle: String,
activity: FragmentActivity,
+ usingLAMode: LAMode = SimplexApp.context.chatModel.controller.appPrefs.laMode.get(),
completed: (LAResult) -> Unit
) {
- when {
- SDK_INT in 28..29 ->
- // KeyguardManager.isDeviceSecure()? https://developer.android.com/training/sign-in/biometric-auth#declare-supported-authentication-types
- authenticateWithBiometricManager(promptTitle, promptSubtitle, activity, completed, BIOMETRIC_WEAK or DEVICE_CREDENTIAL)
- SDK_INT > 29 ->
- authenticateWithBiometricManager(promptTitle, promptSubtitle, activity, completed, BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
- else ->
- completed(LAResult.Unavailable)
+ when (usingLAMode) {
+ LAMode.SYSTEM -> when {
+ SDK_INT in 28..29 ->
+ // KeyguardManager.isDeviceSecure()? https://developer.android.com/training/sign-in/biometric-auth#declare-supported-authentication-types
+ authenticateWithBiometricManager(promptTitle, promptSubtitle, activity, completed, BIOMETRIC_WEAK or DEVICE_CREDENTIAL)
+ SDK_INT > 29 ->
+ authenticateWithBiometricManager(promptTitle, promptSubtitle, activity, completed, BIOMETRIC_STRONG or DEVICE_CREDENTIAL)
+ else -> completed(LAResult.Unavailable())
+ }
+ LAMode.PASSCODE -> {
+ val password = ksAppPassword.get() ?: return completed(LAResult.Unavailable(generalGetString(R.string.la_no_app_password)))
+ ModalManager.shared.showCustomModal(animated = false) { close ->
+ Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
+ LocalAuthView(SimplexApp.context.chatModel, LocalAuthRequest(promptTitle, promptSubtitle, password) {
+ close()
+ completed(it)
+ })
+ }
+ }
+ }
}
}
@@ -66,7 +96,7 @@ private fun authenticateWithBiometricManager(
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
- completed(LAResult.Failed)
+ completed(LAResult.Failed())
}
}
)
@@ -78,9 +108,7 @@ private fun authenticateWithBiometricManager(
.build()
biometricPrompt.authenticate(promptInfo)
}
- else -> {
- completed(LAResult.Unavailable)
- }
+ else -> completed(LAResult.Unavailable())
}
}
@@ -89,6 +117,18 @@ fun laTurnedOnAlert() = AlertManager.shared.showAlertMsg(
generalGetString(R.string.auth_you_will_be_required_to_authenticate_when_you_start_or_resume)
)
+fun laPasscodeNotSetAlert() = AlertManager.shared.showAlertMsg(
+ generalGetString(R.string.lock_not_enabled),
+ generalGetString(R.string.you_can_turn_on_lock)
+)
+
+fun laFailedAlert() {
+ AlertManager.shared.showAlertMsg(
+ title = generalGetString(R.string.la_auth_failed),
+ text = generalGetString(R.string.la_could_not_be_verified)
+ )
+}
+
fun laUnavailableInstructionAlert() = AlertManager.shared.showAlertMsg(
generalGetString(R.string.auth_unavailable),
generalGetString(R.string.auth_device_authentication_is_not_enabled_you_can_turn_on_in_settings_once_enabled)
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt
index 252c5e4edc..ccb00f4613 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt
@@ -640,4 +640,20 @@ fun DisposableEffectOnGone(always: () -> Unit = {}, whenDispose: () -> Unit = {}
}
}
}
-}
\ No newline at end of file
+}
+
+@Composable
+fun DisposableEffectOnRotate(always: () -> Unit = {}, whenDispose: () -> Unit = {}, whenRotate: () -> Unit) {
+ val context = LocalContext.current
+ DisposableEffect(Unit) {
+ always()
+ val activity = context as? Activity ?: return@DisposableEffect onDispose {}
+ val orientation = activity.resources.configuration.orientation
+ onDispose {
+ whenDispose()
+ if (orientation != activity.resources.configuration.orientation) {
+ whenRotate()
+ }
+ }
+ }
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt
new file mode 100644
index 0000000000..25772c701a
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/LocalAuthView.kt
@@ -0,0 +1,22 @@
+package chat.simplex.app.views.localauth
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.res.stringResource
+import chat.simplex.app.R
+import chat.simplex.app.model.ChatModel
+import chat.simplex.app.views.helpers.*
+
+@Composable
+fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) {
+ val passcode = rememberSaveable { mutableStateOf("") }
+ PasscodeView(passcode, authRequest.title ?: stringResource(R.string.la_enter_app_passcode), authRequest.reason, stringResource(R.string.submit_passcode),
+ submit = {
+ val r: LAResult = if (passcode.value == authRequest.password) LAResult.Success else LAResult.Error(generalGetString(R.string.incorrect_passcode))
+ authRequest.completed(r)
+ },
+ cancel = {
+ authRequest.completed(LAResult.Error(generalGetString(R.string.authentication_cancelled)))
+ })
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt
new file mode 100644
index 0000000000..e6ef8de892
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasscodeView.kt
@@ -0,0 +1,100 @@
+package chat.simplex.app.views.localauth
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import chat.simplex.app.R
+import chat.simplex.app.ui.theme.DEFAULT_PADDING
+import chat.simplex.app.ui.theme.SimpleButton
+import chat.simplex.app.views.helpers.*
+
+@Composable
+fun PasscodeView(
+ passcode: MutableState,
+ title: String,
+ reason: String? = null,
+ submitLabel: String,
+ submitEnabled: ((String) -> Boolean)? = null,
+ submit: () -> Unit,
+ cancel: () -> Unit,
+) {
+ @Composable
+ fun VerticalLayout() {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(title, style = MaterialTheme.typography.h1)
+ if (reason != null) {
+ Text(reason, Modifier.padding(top = 5.dp), style = MaterialTheme.typography.subtitle1)
+ }
+ }
+ PasscodeEntry(passcode, true)
+ Row {
+ SimpleButton(generalGetString(R.string.cancel_verb), icon = Icons.Default.Close, click = cancel)
+ Spacer(Modifier.size(20.dp))
+ SimpleButton(submitLabel, icon = Icons.Default.Done, disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
+ }
+ }
+ }
+
+ @Composable
+ fun HorizontalLayout() {
+ Row(Modifier.padding(horizontal = DEFAULT_PADDING), horizontalArrangement = Arrangement.Center) {
+ Column(
+ Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING * 4),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Text(title, style = MaterialTheme.typography.h1)
+ if (reason != null) {
+ Text(reason, Modifier.padding(top = 5.dp), style = MaterialTheme.typography.subtitle1)
+ }
+ }
+ PasscodeEntry(passcode, false)
+ }
+
+ Column(
+ Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, top = DEFAULT_PADDING * 4),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ // Just to fill space to correctly calculate the height
+ Column {
+ Text("", style = MaterialTheme.typography.h1)
+ if (reason != null) {
+ Text("", Modifier.padding(top = 5.dp), style = MaterialTheme.typography.subtitle1)
+ }
+ PasscodeView(remember { mutableStateOf("") })
+ }
+ BoxWithConstraints {
+ val s = minOf(maxWidth, maxHeight) / 3.5f
+ Column(
+ Modifier.padding(start = 30.dp).height(s * 3),
+ verticalArrangement = Arrangement.SpaceEvenly
+ ) {
+ SimpleButton(generalGetString(R.string.cancel_verb), icon = Icons.Default.Close, click = cancel)
+ SimpleButton(submitLabel, icon = Icons.Default.Done, disabled = submitEnabled?.invoke(passcode.value) == false || passcode.value.length < 4, click = submit)
+ }
+ }
+ }
+ }
+ }
+
+ if (LocalContext.current.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ VerticalLayout()
+ } else {
+ HorizontalLayout()
+ }
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt
new file mode 100644
index 0000000000..d42264e301
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/PasswordEntry.kt
@@ -0,0 +1,183 @@
+package chat.simplex.app.views.localauth
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.outlined.Backspace
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.*
+import chat.simplex.app.model.ChatModel
+import chat.simplex.app.ui.theme.HighOrLowlight
+
+@Composable
+fun PasscodeEntry(
+ password: MutableState,
+ vertical: Boolean,
+) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ PasscodeView(password)
+ BoxWithConstraints {
+ if (vertical) {
+ VerticalPasswordGrid(password)
+ } else {
+ HorizontalPasswordGrid(password)
+ }
+ }
+ }
+}
+
+@Composable
+fun PasscodeView(password: MutableState) {
+ var showPasscode by rememberSaveable { mutableStateOf(false) }
+ Text(
+ if (password.value.isEmpty()) " " else remember(password.value, showPasscode) { splitPassword(showPasscode, password.value) },
+ Modifier.padding(vertical = 10.dp).clickable { showPasscode = !showPasscode },
+ style = MaterialTheme.typography.body1
+ )
+}
+
+@Composable
+private fun BoxWithConstraintsScope.VerticalPasswordGrid(password: MutableState) {
+ val s = minOf(maxWidth, maxHeight) / 4 - 1.dp
+ Column(Modifier.width(IntrinsicSize.Min)) {
+ DigitsRow(s, 1, 2, 3, password)
+ Divider()
+ DigitsRow(s, 4, 5, 6, password)
+ Divider()
+ DigitsRow(s, 7, 8, 9, password)
+ Divider()
+ Row(Modifier.requiredHeight(s)) {
+ PasswordEdit(s, Icons.Default.Close) {
+ password.value = ""
+ }
+ VerticalDivider()
+ PasswordDigit(s, 0, password)
+ VerticalDivider()
+ PasswordEdit(s, Icons.Outlined.Backspace) {
+ password.value = password.value.dropLast(1)
+ }
+ }
+ }
+}
+
+@Composable
+private fun BoxWithConstraintsScope.HorizontalPasswordGrid(password: MutableState) {
+ val s = minOf(maxWidth, maxHeight) / 3.5f - 1.dp
+ Column(Modifier.width(IntrinsicSize.Min)) {
+ Row(Modifier.height(IntrinsicSize.Min)) {
+ DigitsRow(s, 1, 2, 3, password);
+ VerticalDivider()
+ PasswordEdit(s, Icons.Default.Close) {
+ password.value = ""
+ }
+ }
+ Divider()
+ Row(Modifier.height(IntrinsicSize.Min)) {
+ DigitsRow(s, 4, 5, 6, password)
+ VerticalDivider()
+ PasswordDigit(s, 0, password)
+ }
+ Divider()
+ Row(Modifier.height(IntrinsicSize.Min)) {
+ DigitsRow(s, 7, 8, 9, password)
+ VerticalDivider()
+ PasswordEdit(s, Icons.Outlined.Backspace) {
+ password.value = password.value.dropLast(1)
+ }
+ }
+ }
+}
+
+private fun splitPassword(showPassword: Boolean, password: String): String {
+ val n = if (password.length < 8) 8 else 4
+ return password.mapIndexed { index, c -> (if (showPassword) c.toString() else "●") + (if ((index + 1) % n == 0) " " else "") }.joinToString("")
+}
+
+@Composable
+private fun DigitsRow(size: Dp, d1: Int, d2: Int, d3: Int, password: MutableState) {
+ Row(Modifier.height(size)) {
+ PasswordDigit(size, d1, password)
+ VerticalDivider()
+ PasswordDigit(size, d2, password)
+ VerticalDivider()
+ PasswordDigit(size, d3, password)
+ }
+}
+
+@Composable
+private fun PasswordDigit(size: Dp, d: Int, password: MutableState) {
+ val s = d.toString()
+ return PasswordButton(size, action = {
+ if (password.value.length < 16) {
+ password.value += s
+ }
+ }) {
+ Text(
+ s,
+ style = TextStyle(
+ fontWeight = FontWeight.Normal,
+ fontSize = 30.sp,
+ letterSpacing = (-0.5).sp
+ ),
+ color = HighOrLowlight
+ )
+ }
+}
+
+@Composable
+private fun PasswordEdit(size: Dp, image: ImageVector, action: () -> Unit) {
+ PasswordButton(size, action) {
+ Icon(image, null, tint = HighOrLowlight)
+ }
+}
+
+@Composable
+private fun PasswordButton(size: Dp, action: () -> Unit, content: @Composable BoxScope.() -> Unit) {
+ return Box(
+ Modifier.size(size)
+ .background(MaterialTheme.colors.background, RoundedCornerShape(50))
+ .clickable { action() },
+ contentAlignment = Alignment.Center
+ ) {
+ content()
+ }
+}
+
+@Composable
+fun VerticalDivider(
+ modifier: Modifier = Modifier,
+ color: Color = MaterialTheme.colors.onSurface.copy(alpha = DividerAlpha),
+ thickness: Dp = 1.dp,
+ startIndent: Dp = 0.dp
+) {
+ val indentMod = if (startIndent.value != 0f) {
+ Modifier.padding(top = startIndent)
+ } else {
+ Modifier
+ }
+ val targetThickness = if (thickness == Dp.Hairline) {
+ (1f / LocalDensity.current.density).dp
+ } else {
+ thickness
+ }
+ Box(
+ modifier.then(indentMod)
+ .fillMaxHeight()
+ .width(targetThickness)
+ .background(color = color)
+ )
+}
+
+private const val DividerAlpha = 0.12f
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt
new file mode 100644
index 0000000000..0506c592dd
--- /dev/null
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/localauth/SetAppPasscodeView.kt
@@ -0,0 +1,48 @@
+package chat.simplex.app.views.localauth
+
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import chat.simplex.app.R
+import chat.simplex.app.views.helpers.DatabaseUtils.ksAppPassword
+import chat.simplex.app.views.helpers.generalGetString
+
+@Composable
+fun SetAppPasscodeView(
+ submit: () -> Unit,
+ cancel: () -> Unit,
+ close: () -> Unit
+) {
+ val passcode = rememberSaveable { mutableStateOf("") }
+ var enteredPassword by rememberSaveable { mutableStateOf("") }
+ var confirming by rememberSaveable { mutableStateOf(false) }
+
+ @Composable
+ fun SetPasswordView(title: String, submitLabel: String, submitEnabled: (((String) -> Boolean))? = null, submit: () -> Unit) {
+ PasscodeView(passcode, title = title, submitLabel = submitLabel, submitEnabled = submitEnabled, submit = submit) {
+ close()
+ cancel()
+ }
+ }
+
+ if (confirming) {
+ SetPasswordView(
+ generalGetString(R.string.confirm_passcode),
+ generalGetString(R.string.confirm_verb),
+ submitEnabled = { pwd -> pwd == enteredPassword }
+ ) {
+ if (passcode.value == enteredPassword) {
+ ksAppPassword.set(passcode.value)
+ enteredPassword = ""
+ passcode.value = ""
+ close()
+ submit()
+ }
+ }
+ } else {
+ SetPasswordView(generalGetString(R.string.new_passcode), generalGetString(R.string.save_verb)) {
+ enteredPassword = passcode.value
+ passcode.value = ""
+ confirming = true
+ }
+ }
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt b/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt
index 3e13b7cf5f..33c4e7ea1a 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt
@@ -70,4 +70,4 @@ fun PreviewHowItWorks() {
SimpleXTheme {
HowItWorks(user = null)
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt
index 827d5e8836..da340dc890 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Appearance.kt
@@ -236,6 +236,7 @@ private fun LangSelector(state: State, onSelected: (String) -> Unit) {
"fr" to "Français",
"it" to "Italiano",
"nl" to "Nederlands",
+ "pl" to "Polski",
"ru" to "Русский",
"zh-CN" to "简体中文"
)
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt
index 55a6dee8ab..e6776a84d5 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/DeveloperView.kt
@@ -56,4 +56,3 @@ fun DeveloperView(
}
}
}
-
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt
index a755313126..6571b8c4af 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt
@@ -87,4 +87,4 @@ private fun HiddenProfileLayout(
}
SectionTextFooter(stringResource(R.string.to_reveal_profile_enter_password))
}
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt
index 597a4b4df5..50cffd2976 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/PrivacySettings.kt
@@ -7,22 +7,41 @@ import SectionTextFooter
import SectionView
import android.view.WindowManager
import androidx.compose.foundation.layout.*
+import androidx.compose.material.*
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import chat.simplex.app.R
import chat.simplex.app.model.*
+import chat.simplex.app.ui.theme.HighOrLowlight
+import chat.simplex.app.ui.theme.SimplexGreen
import chat.simplex.app.views.helpers.*
+import chat.simplex.app.views.helpers.DatabaseUtils.ksAppPassword
+import chat.simplex.app.views.localauth.SetAppPasscodeView
+
+enum class LAMode {
+ SYSTEM,
+ PASSCODE;
+
+ val text: String
+ get() = when (this) {
+ SYSTEM -> generalGetString(R.string.la_mode_system)
+ PASSCODE -> generalGetString(R.string.la_mode_passcode)
+ }
+}
@Composable
fun PrivacySettingsView(
chatModel: ChatModel,
- setPerformLA: (Boolean) -> Unit
+ showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
+ setPerformLA: (Boolean, FragmentActivity) -> Unit
) {
Column(
Modifier.fillMaxWidth(),
@@ -31,7 +50,7 @@ fun PrivacySettingsView(
val simplexLinkMode = chatModel.controller.appPrefs.simplexLinkMode
AppBarTitle(stringResource(R.string.your_privacy))
SectionView(stringResource(R.string.settings_section_title_device)) {
- ChatLockItem(chatModel.performLA, setPerformLA)
+ ChatLockItem(chatModel, showSettingsModal, setPerformLA)
SectionDivider()
val context = LocalContext.current
SettingsPreferenceItem(Icons.Outlined.VisibilityOff, stringResource(R.string.protect_app_screen), chatModel.controller.appPrefs.privacyProtectScreen) { on ->
@@ -52,10 +71,12 @@ fun PrivacySettingsView(
SectionDivider()
SettingsPreferenceItem(Icons.Outlined.TravelExplore, stringResource(R.string.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
SectionDivider()
- SectionItemView { SimpleXLinkOptions(chatModel.simplexLinkMode, onSelected = {
- simplexLinkMode.set(it)
- chatModel.simplexLinkMode.value = it
- }) }
+ SectionItemView {
+ SimpleXLinkOptions(chatModel.simplexLinkMode, onSelected = {
+ simplexLinkMode.set(it)
+ chatModel.simplexLinkMode.value = it
+ })
+ }
}
if (chatModel.simplexLinkMode.value == SimplexLinkMode.BROWSER) {
SectionTextFooter(stringResource(R.string.simplex_link_mode_browser_warning))
@@ -83,3 +104,236 @@ private fun SimpleXLinkOptions(simplexLinkModeState: State, onS
onSelected = onSelected
)
}
+
+private val laDelays = listOf(10, 30, 60, 180, 0)
+
+@Composable
+fun SimplexLockView(
+ chatModel: ChatModel,
+ currentLAMode: SharedPreference,
+ setPerformLA: (Boolean, FragmentActivity) -> Unit
+) {
+ val performLA = remember { chatModel.performLA }
+ val laMode = remember { chatModel.controller.appPrefs.laMode.state }
+ val laLockDelay = remember { chatModel.controller.appPrefs.laLockDelay }
+ val showChangePasscode = remember { derivedStateOf { performLA.value && currentLAMode.state.value == LAMode.PASSCODE } }
+ val activity = LocalContext.current as FragmentActivity
+
+ fun resetLAEnabled(onOff: Boolean) {
+ chatModel.controller.appPrefs.performLA.set(onOff)
+ chatModel.performLA.value = onOff
+ }
+
+ fun disableUnavailableLA() {
+ resetLAEnabled(false)
+ currentLAMode.set(LAMode.SYSTEM)
+ laUnavailableInstructionAlert()
+ }
+
+ fun toggleLAMode(toLAMode: LAMode) {
+ authenticate(
+ if (toLAMode == LAMode.SYSTEM) {
+ generalGetString(R.string.la_enter_app_passcode)
+ } else {
+ generalGetString(R.string.chat_lock)
+ },
+ generalGetString(R.string.change_lock_mode), activity
+ ) { laResult ->
+ when (laResult) {
+ is LAResult.Error -> {
+ laFailedAlert()
+ }
+ is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
+ LAResult.Success -> {
+ when (toLAMode) {
+ LAMode.SYSTEM -> {
+ authenticate(generalGetString(R.string.auth_enable_simplex_lock), promptSubtitle = "", activity, toLAMode) { laResult ->
+ when (laResult) {
+ LAResult.Success -> {
+ currentLAMode.set(toLAMode)
+ ksAppPassword.remove()
+ laTurnedOnAlert()
+ }
+ is LAResult.Unavailable, is LAResult.Error -> {
+ laFailedAlert()
+ }
+ is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
+ }
+ }
+ }
+ LAMode.PASSCODE -> {
+ ModalManager.shared.showCustomModal { close ->
+ Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
+ SetAppPasscodeView(
+ submit = {
+ laLockDelay.set(30)
+ currentLAMode.set(toLAMode)
+ passcodeAlert(generalGetString(R.string.passcode_set))
+ },
+ cancel = {},
+ close
+ )
+ }
+ }
+ }
+ }
+ }
+ is LAResult.Unavailable -> disableUnavailableLA()
+ }
+ }
+ }
+
+ fun changeLAPassword() {
+ authenticate(generalGetString(R.string.la_current_app_passcode), generalGetString(R.string.la_change_app_passcode), activity) { laResult ->
+ when (laResult) {
+ LAResult.Success -> {
+ ModalManager.shared.showCustomModal { close ->
+ Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
+ SetAppPasscodeView(
+ submit = {
+ passcodeAlert(generalGetString(R.string.passcode_changed))
+ }, cancel = {
+ passcodeAlert(generalGetString(R.string.passcode_not_changed))
+ }, close
+ )
+ }
+ }
+ }
+ is LAResult.Error -> laFailedAlert()
+ is LAResult.Failed -> {}
+ is LAResult.Unavailable -> disableUnavailableLA()
+ }
+ }
+ }
+
+ Column(
+ Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.Start
+ ) {
+ AppBarTitle(stringResource(R.string.chat_lock))
+ SectionView {
+ EnableLock(performLA) { performLAToggle ->
+ performLA.value = performLAToggle
+ chatModel.controller.appPrefs.laNoticeShown.set(true)
+ if (performLAToggle) {
+ when (currentLAMode.state.value) {
+ LAMode.SYSTEM -> {
+ setPerformLA(true, activity)
+ }
+ LAMode.PASSCODE -> {
+ ModalManager.shared.showCustomModal { close ->
+ Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
+ SetAppPasscodeView(
+ submit = {
+ laLockDelay.set(30)
+ chatModel.controller.appPrefs.performLA.set(true)
+ passcodeAlert(generalGetString(R.string.passcode_set))
+ },
+ cancel = {
+ resetLAEnabled(false)
+ }, close
+ )
+ }
+ }
+ }
+ }
+ } else {
+ setPerformLA(false, activity)
+ }
+ }
+ SectionDivider()
+ SectionItemView {
+ LockModeSelector(laMode) { newLAMode ->
+ if (laMode.value == newLAMode) return@LockModeSelector
+ if (chatModel.controller.appPrefs.performLA.get()) {
+ toggleLAMode(newLAMode)
+ } else {
+ currentLAMode.set(newLAMode)
+ }
+ }
+ }
+
+ if (performLA.value) {
+ SectionDivider()
+ SectionItemView {
+ LockDelaySelector(remember { laLockDelay.state }) { laLockDelay.set(it) }
+ }
+ if (showChangePasscode.value && laMode.value == LAMode.PASSCODE) {
+ SectionDivider()
+ SectionItemView({ changeLAPassword() }) {
+ Text(generalGetString(R.string.la_change_app_passcode))
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun EnableLock(performLA: MutableState, onCheckedChange: (Boolean) -> Unit) {
+ SectionItemView {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ stringResource(R.string.enable_lock), Modifier
+ .padding(end = 24.dp)
+ .fillMaxWidth()
+ .weight(1F)
+ )
+ Switch(
+ checked = performLA.value,
+ onCheckedChange = onCheckedChange,
+ colors = SwitchDefaults.colors(
+ checkedThumbColor = MaterialTheme.colors.primary,
+ uncheckedThumbColor = HighOrLowlight
+ )
+ )
+ }
+ }
+}
+
+@Composable
+private fun LockModeSelector(state: State, onSelected: (LAMode) -> Unit) {
+ val values by remember { mutableStateOf(LAMode.values().map { it to it.text }) }
+ ExposedDropDownSettingRow(
+ generalGetString(R.string.lock_mode),
+ values,
+ state,
+ icon = null,
+ enabled = remember { mutableStateOf(true) },
+ onSelected = onSelected
+ )
+}
+
+@Composable
+private fun LockDelaySelector(state: State, onSelected: (Int) -> Unit) {
+ val delays = remember { if (laDelays.contains(state.value)) laDelays else listOf(state.value) + laDelays }
+ val values by remember { mutableStateOf(delays.map { it to laDelayText(it) }) }
+ ExposedDropDownSettingRow(
+ generalGetString(R.string.lock_after),
+ values,
+ state,
+ icon = null,
+ enabled = remember { mutableStateOf(true) },
+ onSelected = onSelected
+ )
+}
+
+private fun laDelayText(t: Int): String {
+ val m = t / 60
+ val s = t % 60
+ return if (t == 0) {
+ generalGetString(R.string.la_immediately)
+ } else if (m == 0 || s != 0) {
+ // there are no options where both minutes and seconds are needed
+ generalGetString(R.string.la_seconds).format(s)
+ } else {
+ generalGetString(R.string.la_minutes).format(m)
+ }
+}
+
+private fun passcodeAlert(title: String) {
+ AlertManager.shared.showAlertMsg(
+ title = title,
+ text = generalGetString(R.string.la_please_remember_to_store_password)
+ )
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt
index e07b3443d0..ca03fbf388 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt
@@ -42,7 +42,7 @@ import chat.simplex.app.views.onboarding.SimpleXInfo
import chat.simplex.app.views.onboarding.WhatsNewView
@Composable
-fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) {
+fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean, FragmentActivity) -> Unit) {
val user = chatModel.currentUser.value
val stopped = chatModel.chatRunning.value == false
@@ -126,7 +126,7 @@ fun SettingsLayout(
incognito: MutableState,
incognitoPref: SharedPreference,
userDisplayName: String,
- setPerformLA: (Boolean) -> Unit,
+ setPerformLA: (Boolean, FragmentActivity) -> Unit,
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showSettingsModalWithSearch: (@Composable (ChatModel, MutableState) -> Unit) -> Unit,
@@ -174,7 +174,7 @@ fun SettingsLayout(
SectionDivider()
SettingsActionItem(Icons.Outlined.Videocam, stringResource(R.string.settings_audio_video_calls), showSettingsModal { CallSettingsView(it, showModal) }, disabled = stopped)
SectionDivider()
- SettingsActionItem(Icons.Outlined.Lock, stringResource(R.string.privacy_and_security), showSettingsModal { PrivacySettingsView(it, setPerformLA) }, disabled = stopped)
+ SettingsActionItem(Icons.Outlined.Lock, stringResource(R.string.privacy_and_security), showSettingsModal { PrivacySettingsView(it, showSettingsModal, setPerformLA) }, disabled = stopped)
SectionDivider()
SettingsActionItem(Icons.Outlined.LightMode, stringResource(R.string.appearance_settings), showSettingsModal { AppearanceView(it) }, disabled = stopped)
SectionDivider()
@@ -294,13 +294,20 @@ fun MaintainIncognitoState(chatModel: ChatModel) {
)
}
-@Composable fun ChatLockItem(performLA: MutableState, setPerformLA: (Boolean) -> Unit) {
- SectionItemView() {
+@Composable
+fun ChatLockItem(
+ chatModel: ChatModel,
+ showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
+ setPerformLA: (Boolean, FragmentActivity) -> Unit
+) {
+ val performLA = remember { chatModel.performLA }
+ val currentLAMode = remember { chatModel.controller.appPrefs.laMode }
+ SectionItemView(showSettingsModal { SimplexLockView(chatModel, currentLAMode, setPerformLA) }) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
- Icons.Outlined.Lock,
+ if (performLA.value) Icons.Filled.Lock else Icons.Outlined.Lock,
contentDescription = stringResource(R.string.chat_lock),
- tint = HighOrLowlight,
+ tint = if (performLA.value) SimplexGreen else HighOrLowlight,
)
Spacer(Modifier.padding(horizontal = 4.dp))
Text(
@@ -309,14 +316,7 @@ fun MaintainIncognitoState(chatModel: ChatModel) {
.fillMaxWidth()
.weight(1F)
)
- Switch(
- checked = performLA.value,
- onCheckedChange = { setPerformLA(it) },
- colors = SwitchDefaults.colors(
- checkedThumbColor = MaterialTheme.colors.primary,
- uncheckedThumbColor = HighOrLowlight
- )
- )
+ Text(if (performLA.value) remember { currentLAMode.state }.value.text else generalGetString(androidx.compose.ui.R.string.off), color = HighOrLowlight)
}
}
}
@@ -517,7 +517,7 @@ private fun runAuth(context: Context, onFinish: (success: Boolean) -> Unit) {
generalGetString(R.string.auth_log_in_using_credential),
context as FragmentActivity,
completed = { laResult ->
- onFinish(laResult == LAResult.Success || laResult == LAResult.Unavailable)
+ onFinish(laResult == LAResult.Success || laResult is LAResult.Unavailable)
}
)
}
@@ -538,7 +538,7 @@ fun PreviewSettingsLayout() {
incognito = remember { mutableStateOf(false) },
incognitoPref = SharedPreference({ false }, {}),
userDisplayName = "Alice",
- setPerformLA = {},
+ setPerformLA = { _, _ -> },
showModal = { {} },
showSettingsModal = { {} },
showSettingsModalWithSearch = { },
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt
index 2bf1ee352f..8a4675b5ce 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt
@@ -388,4 +388,4 @@ private fun showMuteProfileAlert(showMuteProfileAlert: SharedPreference
showMuteProfileAlert.set(false)
},
)
-}
\ No newline at end of file
+}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt
index ee2443d72a..64a541a3a8 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/VersionInfoView.kt
@@ -23,7 +23,6 @@ fun VersionInfoView(info: CoreVersionInfo) {
Text(String.format(stringResource(R.string.app_version_name), BuildConfig.VERSION_NAME))
Text(String.format(stringResource(R.string.app_version_code), BuildConfig.VERSION_CODE))
Text(String.format(stringResource(R.string.core_version), info.version))
- Text(String.format(stringResource(R.string.core_build_timestamp), info.buildTimestamp))
val simplexmqCommit = if (info.simplexmqCommit.length >= 7) info.simplexmqCommit.substring(startIndex = 0, endIndex = 7) else info.simplexmqCommit
Text(String.format(stringResource(R.string.core_simplexmq_version), info.simplexmqVersion, simplexmqCommit))
}
diff --git a/apps/android/app/src/main/res/values-cs/strings.xml b/apps/android/app/src/main/res/values-cs/strings.xml
index 92a2185384..449f875cea 100644
--- a/apps/android/app/src/main/res/values-cs/strings.xml
+++ b/apps/android/app/src/main/res/values-cs/strings.xml
@@ -655,7 +655,6 @@
Verze aplikaceVerze aplikace: v%sVerze jádra: v%s
- Jádro sestaveno: %sSmazat adresu\?Všechny vaše kontakty zůstanou připojeny.Žádosti o kontakt
diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/android/app/src/main/res/values-de/strings.xml
index 4f27d31916..e9ae2b7b0f 100644
--- a/apps/android/app/src/main/res/values-de/strings.xml
+++ b/apps/android/app/src/main/res/values-de/strings.xml
@@ -1000,7 +1000,6 @@
App Build: %sApp VersionApp Version: v%s
- Core übersetzt am: %sCore Version: v%sProfil hinzufügenAlle Chats und Nachrichten werden gelöscht! Dies kann nicht rückgängig gemacht werden!
diff --git a/apps/android/app/src/main/res/values-es/strings.xml b/apps/android/app/src/main/res/values-es/strings.xml
index bf9683ae60..23da22ba23 100644
--- a/apps/android/app/src/main/res/values-es/strings.xml
+++ b/apps/android/app/src/main/res/values-es/strings.xml
@@ -250,10 +250,9 @@
EmailConectarConectar mediante enlace
- Base de Datos
-\ny Contraseña
+ Base de Datos y
+\nContraseñaContribuye
- Core compilado: %sCore versión: v%sSolicitud del contactoEliminar imagen
@@ -454,7 +453,7 @@
Invitar al grupoPara comprobar el cifrado de extremo a extremo con su contacto compare (o escanee) el código en sus dispositivos.La base de datos no está cifrada. Escribe una contraseña para protegerla.
- Asegúrate de que las direcciones del servidor SMP tienen el formato correcto, están separadas por líneas y no duplicadas.
+ Asegúrate de que las direcciones del servidor SMP tienen el formato correcto, están separadas por líneas y no están duplicadas.Notificación instantáneaConfiguración de redNo se usarán hosts .onion
@@ -547,7 +546,7 @@
videollamada (sin cifrado e2e)sin cifrado e2eImportar base de datos
- MENSAJES
+ MENSAJES Y ARCHIVOS¿Importar base de datos\?Sin archivos recibidos o enviadosMensajes
@@ -741,7 +740,7 @@
ActivarCompartirenvío no autorizado
- Escribe el nombre del contacto
+ Escribe un nombre para el contactoUsar proxy SOCKS (puerto 9050)Error desconocidoEl rol cambiará a \"%s\". Se notificará a todos los miembros del grupo.
@@ -749,7 +748,7 @@
Los mensajes enviados se eliminarán una vez transcurrido el tiempo establecido.Mensajes de chat SimpleXno leído
- Escribe el nombre del contacto…
+ Escribe un nombre para el contacto…¿Cambiar dirección de recepción\?Usar cámara¡El contacto con el que has compartido este enlace NO podrá conectarse!
@@ -826,7 +825,7 @@
perfil de grupo actualizadoTiempo de espera de la conexión TCP agotadoTema
- Establecer preferencias de grupo
+ Establece preferencias de grupoSOPORTE SIMPLEX CHATEscribe la contraseña para exportarActualizar
@@ -1059,4 +1058,22 @@
El vídeo se recibirá cuando tu contacto termine de subirlo.Solo se pueden enviar 10 vídeos de forma simultáneaEsperando el vídeo
+ Error guardando servidores SMP
+ Error cargando servidores XFTP
+ Error cargando servidores SMP
+ Asegúrate de que las direcciones del servidor XFTP tienen el formato correcto, están separadas por líneas y no están duplicadas.
+ El servidor requiere autorización para subir, comprueba la contraseña
+ Comparar archivo
+ Crear archivo
+ Eliminar archivo
+ Subir archivo
+ Servidores XFTP
+ Tus servidores XFTP
+ Puerto
+ puerto %d
+ Establece Usar hosts .onion en No si el proxy SOCKS no los admite.
+ Descargar archivo
+ Usar proxy SOCKS
+ Host
+ Configuración proxy SOCKS
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-fr/strings.xml b/apps/android/app/src/main/res/values-fr/strings.xml
index ff45b454f3..8921f5e409 100644
--- a/apps/android/app/src/main/res/values-fr/strings.xml
+++ b/apps/android/app/src/main/res/values-fr/strings.xml
@@ -549,7 +549,7 @@
Fonctionnalités expérimentalesSOCKS PROXYTHEMES
- MESSAGES
+ MESSAGES ET FICHIERSAPPELSImporter la base de donnéesNouvelle archive de base de données
@@ -925,7 +925,6 @@
simplexmq : v%s (%2s)Build de l\'app : %sVersion de l\'app : v%s
- Cœur compilé le : %sVersion du cœur : v%sNombre de PINGToutes les discussions et tous les messages seront supprimés - il est impossible de revenir en arrière !
@@ -1058,4 +1057,22 @@
La vidéo ne sera reçue que lorsque votre contact sera en ligne. Veuillez patienter ou vérifier plus tard !Requête de réception de la vidéoSeulement 10 vidéos peuvent être envoyées en même temps
+ Créer un fichier
+ Supprimer le fichier
+ Erreur lors de la sauvegarde des serveurs XFTP
+ Assurez-vous que les adresses des serveurs XFTP sont au bon format, séparées par des lignes et qu\'elles ne sont pas dupliquées.
+ Le serveur requiert une autorisation pour uploader, vérifiez le mot de passe
+ Transférer le fichier
+ Serveurs XFTP
+ Vos serveurs XFTP
+ Comparer le fichier
+ Télécharger le fichier
+ Erreur lors du chargement des serveurs SMP
+ Erreur lors du chargement des serveurs XFTP
+ Héberger
+ Port
+ port %d
+ Paramètres de proxy SOCKS
+ Utiliser un proxy SOCKS
+ Définissez Utiliser les hôtes .onion sur Non si le proxy SOCKS ne les prend pas en charge.
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-hi/strings.xml b/apps/android/app/src/main/res/values-hi/strings.xml
index 4b5d0b471d..bafcf87767 100644
--- a/apps/android/app/src/main/res/values-hi/strings.xml
+++ b/apps/android/app/src/main/res/values-hi/strings.xml
@@ -180,4 +180,107 @@
वापस लौटानाए + बीआपका यादृच्छिक प्रोफ़ाइल
+ चैट वरीयताएँ
+ %d दिनों
+ ध्वनि संदेशों की अनुमति दें\?
+ साफ़
+ ऑडियो बंद
+ ऑडियो चालू
+ गायब होने वाले संदेश
+ अधिक सुधार जल्द ही आ रहे हैं!
+ नकलना
+ भूमिका बदलें
+ %d घंटा
+ प्रमाणीकरण अनुपलब्ध
+ संपादित
+ समूह मिटाएं\?
+ संपर्क पहले से मौजूद है
+ संपर्क नाम
+ संपर्क छुपाया गया:
+ मिटाना
+ जब आप पृष्ठभूमि में 30 सेकंड के बाद ऐप को प्रारंभ या फिर से शुरू करते हैं तो आपको प्रमाणित करने की आवश्यकता होगी।
+ प्रतिमा प्राप्त करने को कहा
+ वापस
+ बंद करें बटन
+ पता बनाना
+ संपर्क आमंत्रित नहीं कर सकते!
+ संपर्क जांचा गया
+ संपर्क वरीयताएँ
+ आपकी भूमिका को %s में बदल दिया
+ साफ़
+ गायब होने वाले संदेश
+ प्रदर्शित होने वाला नाम:
+ अँधेरा
+ अपने संपर्कों को ध्वनि संदेश भेजने की अनुमति दें।
+ %d दिन
+ %d घंटे
+ चीनी और स्पेनिश इंटरफ़ेस
+ विवरण
+ ऐप केवल तभी सूचनाएं प्राप्त कर सकता है जब वह चल रहा हो, कोई पृष्ठभूमि सेवा प्रारंभ नहीं की जाएगी
+ संपादन करना
+ विकेन्द्रीकृत
+ कॉल समाप्त
+ कॉल चल रहा है
+ छवियों को स्वत: स्वीकार करें
+ चैट संग्रह
+ चैट रोक दी गई है
+ चैट संग्रह
+ आप इस समूह से संदेश प्राप्त करना बंद कर देंगे। चैट इतिहास संरक्षित किया जाएगा।
+ %s की भूमिका को %s में बदला
+ पूर्ण
+ इस चैट में गायब होने वाले संदेश प्रतिबंधित हैं।
+ इस चैट में गायब होने वाले संदेश प्रतिबंधित हैं।
+ डेवलपर्स के साथ चैट करें
+ संपर्क अभी तक जुड़ा नहीं है!
+ चैट कंसोल
+ आपके सभी संपर्क जुड़े रहेंगे।
+ चैट प्रोफ़ाइल
+ संपर्क अनुरोध
+ बनाएं
+ कॉल त्रुटि
+ कॉल चल रहा है
+ हमेशा रिले का प्रयोग करें
+ ऑडियो कॉल
+ ऑडियो और वीडियो कॉल
+ कॉल पहले ही खत्म हो चुकी है!
+ अक्षम करना
+ संपर्क में ई2ई एन्क्रिप्शन है
+ संपर्क में कोई ई2ई एन्क्रिप्शन नहीं है
+ चैट चल रही है
+ चैट रोक दी गई है
+ पता बदल रहा है…
+ %d मिनट
+ उपकरण
+ रचनाकार
+ समूह भूमिका बदलें\?
+ %d सप्ताह
+ संपर्क अनुरोधों को स्वत: स्वीकार करें
+ खराब संदेश हैश
+ ध्वनि संदेशों को केवल तभी अनुमति दें यदि आपका संपर्क उन्हें अनुमति देता है।
+ जिस संपर्क से आपने यह लिंक प्राप्त किया है, उसे एक यादृच्छिक प्रोफ़ाइल भेजी जाएगी
+ कॉल कर रहा है…
+ रद्द %s
+ आपके लिए पता बदल दिया
+ प्रत्येक 10 मिनट में 1 मिनट तक नए संदेशों की जाँच करता है
+ %d सेकंड
+ %d महीना
+ %d महीने
+ डेवलपर उपकरण
+ आपके संपर्क को एक यादृच्छिक प्रोफ़ाइल भेजी जाएगी
+ पता बदल रहा है…
+ स्वागत संदेश जोड़ें
+ अपने संपर्कों को भेजे गए संदेशों को अपरिवर्तनीय रूप से हटाने की अनुमति दें।
+ ध्वनि संदेश भेजने की अनुमति दें।
+ संपर्कों को आमंत्रित नहीं कर सकते!
+ %s के लिए पता बदल रहा है…
+ दोबारा मत दिखाओ
+ आप और आपका संपर्क दोनों ध्वनि संदेश भेज सकते हैं।
+ विकास करना
+ आवाज़ बंद करना
+ अपने संपर्कों को गायब होने वाले संदेश भेजने की अनुमति दें।
+ फ़ाइल स्थानांतरण रद्द करें\?
+ ऑडियो और वीडियो कॉल
+ अधिक सुधार जल्द ही आ रहे हैं!
+ वीडियो प्राप्त करने के लिए कहा
+ संपर्क और सभी संदेशों को हटा दिया जाएगा - इसे पूर्ववत नहीं किया जा सकता!
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-it/strings.xml b/apps/android/app/src/main/res/values-it/strings.xml
index f25b2b56a6..ef1aadaccb 100644
--- a/apps/android/app/src/main/res/values-it/strings.xml
+++ b/apps/android/app/src/main/res/values-it/strings.xml
@@ -728,7 +728,7 @@
Importare il database della chat\?Importa databaseModalità incognito
- MESSAGGI
+ MESSAGGI E FILENuovo archivio databaseVecchio archivio del databaseRiavvia l\'app per creare un profilo di chat nuovo.
@@ -927,7 +927,6 @@
simplexmq: v%s (%2s)Build dell\'app: %sVersione app: v%s
- Core compilato il: %sI server per le nuove connessioni del profilo di chat attualeAggiungi profiloEliminare il profilo di chat\?
@@ -1058,4 +1057,22 @@
Il video verrà ricevuto quando il tuo contatto sarà in linea, attendi o controlla più tardi!In attesa del videoIn attesa del video
+ Errore nel caricamento dei server XFTP
+ Errore nel salvataggio dei server XFTP
+ Il server richiede l\'autorizzazione per l\'invio, controlla la password
+ Confronta file
+ Crea file
+ Scarica file
+ Invia file
+ Server XFTP
+ I tuoi server XFTP
+ Impostazioni proxy SOCKS
+ Usa proxy SOCKS
+ Host
+ Porta
+ porta %d
+ Imposta Usa gli host .onion su No se il proxy SOCKS non li supporta.
+ Elimina file
+ Errore nel caricamento dei server SMP
+ Assicurati che gli indirizzi del server XFTP siano nel formato corretto, uno per riga e non doppi.
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-ja/strings.xml b/apps/android/app/src/main/res/values-ja/strings.xml
index fa9a28cdab..849783d910 100644
--- a/apps/android/app/src/main/res/values-ja/strings.xml
+++ b/apps/android/app/src/main/res/values-ja/strings.xml
@@ -540,7 +540,6 @@
マークダウン (書式編集) ガイドサーバを手動で入力接続にオニオンのホストが必要となります。
- コアのビルド@: %sアドレスを作成アドレスを削除しますか?表示の名前:
diff --git a/apps/android/app/src/main/res/values-ko/strings.xml b/apps/android/app/src/main/res/values-ko/strings.xml
index ae64af765f..79771994c9 100644
--- a/apps/android/app/src/main/res/values-ko/strings.xml
+++ b/apps/android/app/src/main/res/values-ko/strings.xml
@@ -10,7 +10,7 @@
이미 추가된 연락처에요.연결연결 오류 (인증)
- 대기열 생성
+ 대기열 만들기데이터베이스를 초기화할 수 없어요앱이 백그라운드에서 항상 실행되어요. 대신 메시지가 도착하자마자 바로 알림이 와요.10분마다 최대 1분간 새 메시지 확인
@@ -43,10 +43,10 @@
채팅을 지울까요\?연결 요청 완료링크를 통해 연결
- 프리셋 서버 추가
+ 프리셋 서버 추가하기서버 추가…채팅 콘솔
- 서버 주소를 확인 후 다시 시도하십시오.
+ 서버 주소를 확인 후 다시 시도해 주세요.ICE 서버 설정기여고급 네트워크 설정
@@ -97,8 +97,8 @@
채팅 기록 보관함내 역할이 %s 역할로 변경됨.주소 바꾸는 중…
- 주소 바꾸기…
- %s의 주소 바꾸기…
+ 주소 바꾸는 중…
+ %s의 주소 바꾸는 중…연결됨완료연결됨
@@ -135,7 +135,7 @@
중국어 및 스페인어 인터페이스SimpleX에 대하여수락
- 일회용 초대 링크 생성
+ 일회용 초대 링크 만들기주소 생성1일SimpleX에 대하여
@@ -176,7 +176,7 @@
배터리에 가장 좋음. 앱이 실행 중일 때만 알림을 받게 되며 백그라운드에서 실행되지 않습니다.설정을 통해 비활성화할 수 있어요. – 앱이 실행되는 동안 알림이 표시되요.나와 대화 상대 모두 자동 삭제되는 메시지를 보낼 수 있어요.
- QR 코드 스캔: QR 코드를 보여주는 사람과 연결해요.
+ QR 코드 스캔: QR 코드를 보여주는 사람과 연결할 수 있어요.데이터베이스 암호를 저장하고 있는 암호키 저장소에 접근할 수 없습니다배터리 많이 사용! 백그라운드에서 항상 실행돼요. 메시지를 수신하자마자 알림이 떠요.통화 종료됨 %1$s
@@ -206,7 +206,6 @@
대화 상대와 메시지가 삭제돼요. 삭제 후 되돌릴 수 없어요!대화 상대와 종단간 암호화됨대화 상대와 아직 연결되지 않았어요!
- 코어 빌드: %s%1$s에 생성 완료일회용 초대 링크 생성비밀 그룹 생성
@@ -253,7 +252,7 @@
다음 기간 이후 자동 삭제위 다음 :데이터베이스 삭제
- 데이터베이스는 임의의 비밀번호로 암호화되었습니다. 내보내기 기능 사용 전 비밀번호를 변경해 주세요.
+ 데이터베이스는 임의의 비밀번호로 암호화되었어요. 내보내기 기능 사용 전 비밀번호를 변경해 주세요.파일과 미디어를 삭제할까요\?현재 비밀번호…데이터베이스 암호화 완료!
@@ -764,4 +763,128 @@
공유한 링크를 통해서만 나에게 연결할 수 있어요.그룹 프로필 업데이트됨프로필 비밀번호
+ 보이기
+ 저장하기
+ QR 코드 스캔하기
+ 답장
+ 초기화
+ 저장하기
+ 저장하고 모든 대화 상대에게 알리기
+ 저장하고 그룹 멤버들에게 알리기
+ 저장하고 대화 상대에게 알리기
+ 지우기
+ 아카이브 저장하기
+ 색상 저장하기
+ 암호 저장소에 비밀번호 저장하기
+ 데이터베이스 백업 복원하기
+ 데이터베이스 백업을 복원한 후 이전 비밀번호를 입력해 주세요. 이 작업은 되돌릴 수 없어요.
+ 데이터베이스 백업을 복원할까요\?
+ 키스토어에서 암호를 찾을 수 없어요. 직접 입력해 주세요. 백업 도구를 사용하여 복원했을 때 이 문제가 발생할 수 있는데, 그런 경우가 아니라면 개발자에게 알려주세요.
+ 저장하고 그룹 프로필 업데이트하기
+ 환영 메시지를 저장할까요\?
+ 그룹 프로필 저장하기
+ 색상 초기화
+ 강퇴하기
+ 코드 스캔하기
+ 대화 상대의 앱에서 보안 코드를 스캔해 주세요.
+ 저장된 WebRTC ICE 서버가 제거될 거예요.
+ 비밀번호 저장하고 채팅 열기
+ 역할
+ 설정을 저장할까요\?
+ 프로필 비밀번호 저장하기
+ 가져온 채팅 데이터베이스를 사용하려면 앱을 다시 실행해 주세요.
+ 새 프로필을 만드려면 앱을 다시 실행해 주세요.
+ 암호 저장소에서 비밀번호를 삭제할까요\?
+ 채팅 기능 실행하기
+ 복원하기
+ 대화 상대가 파일 전송을 취소했어요.
+ 보안 대기열
+ 파일 업로드하기
+ 파일 비교
+ 파일 삭제하기
+ 파일 다운로드하기
+ 공유
+ 라이브 메시지 보내기
+ QR코드 보기
+ 파일 만들기
+ 나
+ 파일 보내기는 아직 지원되지 않아요.
+ %1$s를 통해
+ SimpleX 연락처 주소
+ SimpleX 그룹 링크
+ SimpleX 일회용 초대 링크
+ SimpleX 링크
+ 브라우저를 통해
+ 브라우저에서 링크를 열면 익명성 또는 보안에 문제가 생길 수 있어요. 신뢰할 수 없는 SimpleX 링크는 빨간색으로 표시되어요.
+ 링크 전체
+ 보낸 사람이 연결 요청을 삭제했을 수 있어요.
+ 즉각적인 알림이 비활성화되었어요!
+ 미리보기 표시
+ 알림 미리보기
+ 알림 서비스
+ 메시지 받는 중…
+ SimpleX 채팅 서비스
+ 파일 공유…
+ 이미지 공유…
+ 메시지 공유…
+ 라이브 메시지 보내기 - 입력 과정을 실시간으로 상대에게 보여줘요.
+ 보내기
+ 초대 링크 공유
+ 연락하고자 하는 사람이 앱에서 QR 코드를 스캔할 수 있어요.
+ 보안 코드
+ 서버 QR코드 스캔
+ 일부 서버가 테스트에 통과하지 못했습니다 :
+ 서버 테스트 실패!
+ 환영 메시지
+ 링크 공유
+ 표시하기
+ 연락처 선택
+ 멤버 초대 건너뛰기
+ 다음을 통해 보내기
+ XFTP로 동영상 및 파일 보내기
+ 멈추기
+ %s의 역할을 %s로 변경했어요.
+ 콘솔용
+ 적어도 하나의 숨겨지지 않은 사용자 프로필이 있어야 해요.
+ 그룹 설정 지정하기
+ %s의 주소를 바꿨어요
+ 주소를 바꿨어요
+ 그룹 프로필 업데이트됨
+ 적어도 하나의 사용자 프로필이 있어야 해요.
+ 서버 테스트하기
+ 서버 사용하기
+ 새로운 대화에 사용
+ 잘못된 서버 주소에요!
+ 비밀
+ 종단 간 암호화 안 됨
+ 테마
+ 내 역할이 %s로 변경되었어요.
+ 나감
+ 도움말
+ 설정
+ 링크 미리보기 보내기
+ SOCKS 프록시
+ SIMPLEX CHAT 도와주기
+ 나
+ 실험적 기능
+ 표시 :
+ 연락처 이름 설정
+ 개발자에게 이메일 보내기
+ 서버를 수동으로 입력
+ 미리 설정된 서버
+ 서버 저장하기
+ 서버 테스트하기
+ 현재 채팅 프로필의 새로운 연결을 위한 서버
+ 서버를 저장할까요\?
+ GitHub에서 별 주기
+ 개발자 옵션 보기
+ 메시지 및 파일
+ 익명 모드
+ 실험적
+ 내보낼 비밀번호 설정
+ SMP 서버
+ 미리 설정된 서버 주소
+ 내 서버
+ 내 서버 주소
+ %1$s을(를) 강퇴했어요.
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-lt/strings.xml b/apps/android/app/src/main/res/values-lt/strings.xml
index 41214385de..30600ff520 100644
--- a/apps/android/app/src/main/res/values-lt/strings.xml
+++ b/apps/android/app/src/main/res/values-lt/strings.xml
@@ -159,7 +159,6 @@
Neteisingas saugumo kodas!Įrašyti serveriusĮrašyti serverius\?
- Branduolys sudarytas: %sSukurti adresąIštrinti adresąKlaida įrašant naudotojo slaptažodį
diff --git a/apps/android/app/src/main/res/values-nl/strings.xml b/apps/android/app/src/main/res/values-nl/strings.xml
index a2a9e5577e..8abc6ca752 100644
--- a/apps/android/app/src/main/res/values-nl/strings.xml
+++ b/apps/android/app/src/main/res/values-nl/strings.xml
@@ -174,7 +174,6 @@
BijdragenICE servers configurerenVerbinding
- Core gebouwd op: %sCore versie: v%sverbondenVerbinden…
@@ -494,7 +493,7 @@
Gebruikerlink voorbeeld afbeeldingGEBRUIKER
- BERICHTEN
+ BERICHTEN EN BESTANDEN📱 mobiel: tik op Openen in mobiele app en tik vervolgens op Verbinden in de app.Gebruiker wordt uit de groep verwijderd, dit kan niet ongedaan worden gemaakt!Fout bij bezorging van bericht
@@ -1057,4 +1056,22 @@
Wachten op videoVideoDe video wordt ontvangen wanneer uw contactpersoon het uploaden heeft voltooid.
+ Fout bij opslaan van XFTP-servers
+ Fout bij het laden van XFTP servers
+ Server vereist autorisatie om te uploaden, wachtwoord controleren
+ Bestand vergelijken
+ Bestand verwijderen
+ Bestand downloaden
+ Upload bestand
+ XFTP servers
+ Uw XFTP servers
+ Host
+ Poort
+ SOCKS proxy instellingen
+ Gebruik SOCKS proxy
+ Stel Use .onion hosts in op Nee als de SOCKS-proxy deze niet ondersteunt.
+ Bestand maken
+ Fout bij het laden van SMP servers
+ Zorg ervoor dat XFTP server adressen de juiste indeling hebben, regel gescheiden zijn en niet gedupliceerd zijn.
+ poort %d
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-pl/strings.xml b/apps/android/app/src/main/res/values-pl/strings.xml
index 826d81cdf4..4869140726 100644
--- a/apps/android/app/src/main/res/values-pl/strings.xml
+++ b/apps/android/app/src/main/res/values-pl/strings.xml
@@ -344,7 +344,6 @@
\nUwaga: jeśli masz wiele połączeń, zużycie baterii i ruchu może być znacznie wyższe, a niektóre połączenia mogą się nie udać.Profil czatuPołączenie
- Kompilacja rdzenia: %sWersja rdzenia: v%sBłąd zapisu serwerów ICESerwery ICE (po jednym na linię)
@@ -536,7 +535,7 @@
POMOCImportuj bazę danychTryb incognito
- WIADOMOŚCI
+ WIADOMOŚCI I PLIKINowe archiwum bazy danychStare archiwum bazy danychChroń ekran aplikacji
@@ -1058,4 +1057,22 @@
Dołączysz do grupy, do której odnosi się ten link i połączysz się z jej członkami.Twoje serwery SMPZostaniesz połączony, gdy Twoje żądanie połączenia zostanie zaakceptowane, proszę czekać lub sprawdzić później!
+ Błąd ładowania serwerów SMP
+ Błąd ładowania serwerów XFTP
+ Błąd zapisu serwerów XFTP
+ Utwórz plik
+ Pobierz plik
+ Serwer wymaga autoryzacji do przesłania, sprawdź hasło
+ Prześlij plik
+ Porównaj plik
+ Usuń plik
+ Serwery XFTP
+ Twoje serwery XFTP
+ Host
+ Port
+ port %d
+ Ustaw Użyj hostów .onion na Nie jeśli proxy SOCKS ich nie obsługuje.
+ Ustawienia PROXY SOCKS
+ Użyj proxy SOCKS
+ Upewnij się, że adresy serwerów XFTP są w poprawnym formacie, rozdzielone liniami i nie są zduplikowane.
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-pt-rBR/strings.xml b/apps/android/app/src/main/res/values-pt-rBR/strings.xml
index 52724438dd..97e242bbc2 100644
--- a/apps/android/app/src/main/res/values-pt-rBR/strings.xml
+++ b/apps/android/app/src/main/res/values-pt-rBR/strings.xml
@@ -664,7 +664,6 @@
Os hosts Onion serão necessários para a conexão.Os hosts Onion serão necessários para a conexão.Versão principal: v%s
- Núcleo construído em: %sLeia mais no nosso repositório do GitHub.Pode ser mudado mais tarde via configurações.%1$s quer se conectar com você via
diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml
index dc70794646..5b5939b69c 100644
--- a/apps/android/app/src/main/res/values-ru/strings.xml
+++ b/apps/android/app/src/main/res/values-ru/strings.xml
@@ -1025,7 +1025,6 @@
Отдельное TCP-соединение (и авторизация SOCKS) будет использоваться для каждого контакта и члена группы.
\nОбратите внимание: если у Вас много контактов, потребление батареи и трафика может быть значительно выше, и некоторые соединения могут не работать.Отдельное TCP-соединение (и авторизация SOCKS) будет использоваться для каждого профиля чата, который Вы имеете в приложении.
- Ядро скомпилировано: %sВерсия ядра: v%sУдалить профиль чата\?Удалить профиль чата для
diff --git a/apps/android/app/src/main/res/values-zh-rCN/strings.xml b/apps/android/app/src/main/res/values-zh-rCN/strings.xml
index d23d5148a9..e74fe34815 100644
--- a/apps/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/apps/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -37,7 +37,7 @@
删除所有文件消息在此后删除消息
- 消息
+ 消息和文件添加个人资料所有聊天记录和消息将被删除——这一行为无法撤销!所有聊天记录和消息将被删除——这一行为无法撤销!只有您的消息会被删除。
@@ -103,7 +103,7 @@
应用程序版本:v%s仅在运行时应用程序可以接受通知,不会启动后台服务一个随机资料将发送给您的联系人
- 身份验证不可用
+ 身份认证不可用自动接受图像附件语音通话
@@ -363,7 +363,6 @@
端到端加密语音通话ICE 服务器(每行一个)创建地址
- 核心构建于:%s核心版本: v%s显示名:全名:
@@ -1058,4 +1057,22 @@
视频已发送要求接收视频视频将在您的联系人完成上传后收到。
+ 服务器需要授权来上传,检查密码
+ 上传文件
+ XFTP 服务器
+ 您的 XFTP 服务器
+ 如果 SOCKS 代理不支持它们,请将 Use .onion hosts 设置为否。
+ 使用 SOCKS 代理
+ 端口
+ 删除文件
+ 对比文件
+ 主机
+ 确保 XFTP 服务器地址格式正确、行分隔且不重复。
+ 创建文件
+ 下载文件
+ 加载 SMP 服务器时出错
+ 加载 XFTP 服务器时出错
+ 保存 SMP 服务器时出错
+ 端口 %d
+ SOCKS 代理设置
\ No newline at end of file
diff --git a/apps/android/app/src/main/res/values-zh-rTW/strings.xml b/apps/android/app/src/main/res/values-zh-rTW/strings.xml
index e7a54a18b6..c2a37deb32 100644
--- a/apps/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/apps/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -425,7 +425,6 @@
刪除地址顏色如何使用你的伺服器
- 核心建立於:%s使用連結連接💻 桌面版:於應用程式內掃描一個已存在的二維碼,透過二維碼掃描。設定
diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml
index 7a4526d43c..6aa7a83b46 100644
--- a/apps/android/app/src/main/res/values/strings.xml
+++ b/apps/android/app/src/main/res/values/strings.xml
@@ -165,6 +165,20 @@
SimpleX LockTo protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled.Turn on
+ SimpleX Lock mode
+ System authentication
+ Passcode entry
+ Authentication failed
+ You could not be verified; please try again.
+ No app passcode
+ Enter Passcode
+ Current Passcode
+ Change passcode
+ Authenticate
+ Immediately
+ %d seconds
+ %d minutes
+ Please remember or store it securely - there is no way to recover a lost password!SimpleX Lock turned on
@@ -179,6 +193,8 @@
Device authentication is disabled. Turning off SimpleX Lock.Stop chatOpen chat console
+ SimpleX Lock not enabled!
+ You can turn on SimpleX Lock via Settings.Message delivery error
@@ -540,7 +556,6 @@
App version: v%sApp build: %sCore version: v%s
- Core built at: %ssimplexmq: v%s (%2s)Show:Hide:
@@ -735,6 +750,20 @@
Auto-accept imagesSend link previewsApp data backup
+ Enable lock
+ Lock mode
+ Lock after
+ Submit
+ Confirm Passcode
+ Incorrect passcode
+ New Passcode
+ Authentication cancelled
+ System
+ Passcode
+ Passcode set!
+ Passcode changed!
+ Passcode not changed!
+ Change lock modeYOU
diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
index 5be4979f12..dd21ea26c0 100644
--- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
@@ -186,7 +186,7 @@ struct SimplexLockView: View {
.onChange(of: laMode) { _ in
if performLAModeReset {
performLAModeReset = false
- } else if performLA {
+ } else if prefPerformLA {
toggleLAMode()
} else {
updateLAMode()
diff --git a/apps/ios/Shared/Views/UserSettings/VersionView.swift b/apps/ios/Shared/Views/UserSettings/VersionView.swift
index 9f13e4a95b..0fc2b4cb3e 100644
--- a/apps/ios/Shared/Views/UserSettings/VersionView.swift
+++ b/apps/ios/Shared/Views/UserSettings/VersionView.swift
@@ -18,7 +18,6 @@ struct VersionView: View {
Text("App build: \(appBuild ?? "?")")
if let info = versionInfo {
Text("Core version: v\(info.version)")
- Text("Core built at: \(info.buildTimestamp)")
if let v = try? AttributedString(markdown: "simplexmq: v\(info.simplexmqVersion) ([\(info.simplexmqCommit.prefix(7))](https://github.com/simplex-chat/simplexmq/commit/\(info.simplexmqCommit)))") {
Text(v)
}
diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff
index 41845a1d1f..226358a008 100644
--- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff
+++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff
@@ -947,11 +947,6 @@
Kopírovatchat item action
-
- Core built at: %@
- Jádro sestavené na: %@
- No comment provided by engineer.
- Core version: v%@Verze jádra: v%@
diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff
index 0340acbc5e..eca31200f1 100644
--- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff
+++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff
@@ -947,11 +947,6 @@
Kopierenchat item action
-
- Core built at: %@
- Core übersetzt am: %@
- No comment provided by engineer.
- Core version: v%@Core Version: v%@
diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
index 849f5130dd..524d253fbf 100644
--- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
+++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
@@ -955,11 +955,6 @@
Copychat item action
-
- Core built at: %@
- Core built at: %@
- No comment provided by engineer.
- Core version: v%@Core version: v%@
diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff
index 7f717e831d..9deefbc2d7 100644
--- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff
+++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff
@@ -144,6 +144,7 @@
%lld minutes
+ %lld minutosNo comment provided by engineer.
@@ -153,6 +154,7 @@
%lld seconds
+ %lld segundosNo comment provided by engineer.
@@ -550,6 +552,7 @@
Authentication cancelled
+ Autenticación canceladaPIN entry
@@ -659,6 +662,7 @@
Change Passcode
+ Cambiar contraseñaNo comment provided by engineer.
@@ -668,6 +672,7 @@
Change lock mode
+ Cambiar el modo de bloqueoauthentication reason
@@ -677,6 +682,7 @@
Change passcode
+ Cambiar contraseñaauthentication reason
@@ -786,6 +792,7 @@
Compare file
+ Comparar archivoserver test step
@@ -805,6 +812,7 @@
Confirm Passcode
+ Confirmar contraseñaNo comment provided by engineer.
@@ -947,11 +955,6 @@
Copiarchat item action
-
- Core built at: %@
- Core compilado en: %@
- No comment provided by engineer.
- Core version: v%@Versión Core: v%@
@@ -969,6 +972,7 @@
Create file
+ Crear archivoserver test step
@@ -1008,6 +1012,7 @@
Current Passcode
+ Contraseña actualNo comment provided by engineer.
@@ -1081,8 +1086,7 @@
Database passphrase & export
- Base de datos
-y Contraseña
+ Base de datos y contraseñaNo comment provided by engineer.
@@ -1196,6 +1200,7 @@ y Contraseña
Delete file
+ Eliminar archivoserver test step
@@ -1400,6 +1405,7 @@ y Contraseña
Download file
+ Descargar archivoserver test step
@@ -1444,6 +1450,7 @@ y Contraseña
Enable lock
+ Activar bloqueoNo comment provided by engineer.
@@ -1503,6 +1510,7 @@ y Contraseña
Enter Passcode
+ Introducir contraseñaNo comment provided by engineer.
@@ -1647,6 +1655,7 @@ y Contraseña
Error loading %@ servers
+ Error al cargar servidores %@No comment provided by engineer.
@@ -1676,6 +1685,7 @@ y Contraseña
Error saving passcode
+ Error al guardar contraseñaNo comment provided by engineer.
@@ -2055,6 +2065,7 @@ y Contraseña
Immediately
+ InmediatamenteNo comment provided by engineer.
@@ -2129,6 +2140,7 @@ y Contraseña
Incorrect passcode
+ Contraseña incorrectaPIN entry
@@ -2255,6 +2267,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
KeyChain error
+ Error en KeychainNo comment provided by engineer.
@@ -2319,10 +2332,12 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Lock after
+ Bloquear después deNo comment provided by engineer.Lock mode
+ Modo de bloqueoNo comment provided by engineer.
@@ -2497,6 +2512,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
New Passcode
+ Nueva contraseñaNo comment provided by engineer.
@@ -2541,6 +2557,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
No app password
+ Sin contraseña de la aplicaciónAuthentication unavailable
@@ -2594,6 +2611,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Off
+ ApagadoNo comment provided by engineer.
@@ -2723,22 +2741,27 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Passcode
+ ContraseñaNo comment provided by engineer.Passcode changed!
+ ¡Contraseña cambiada!No comment provided by engineer.Passcode entry
+ Entrada de contraseñaNo comment provided by engineer.Passcode not changed!
+ ¡Contraseña no cambiada!No comment provided by engineer.Passcode set!
+ ¡Contraseña guardada!No comment provided by engineer.
@@ -2813,6 +2836,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Please remember or store it securely - there is no way to recover a lost passcode!
+ Por favor, recuerda y guarda la contraseña en un lugar seguro. ¡No hay manera de recuperar una contraseña perdida!No comment provided by engineer.
@@ -3282,6 +3306,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Server requires authorization to upload, check password
+ El servidor requiere autorización para subir, comprueba la contraseñaserver test error
@@ -3386,10 +3411,12 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
SimpleX Lock mode
+ Modo Bloqueo SimpleXNo comment provided by engineer.SimpleX Lock not enabled!
+ ¡Bloqueo SimpleX no activado!No comment provided by engineer.
@@ -3479,6 +3506,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Submit
+ EnviarNo comment provided by engineer.
@@ -3493,6 +3521,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
System authentication
+ Autenticación del sistemaNo comment provided by engineer.
@@ -3577,7 +3606,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
Thanks to the users – contribute via Weblate!
- Gracias a los usuarios: ¡contribuye a través de Weblate!
+ Agradecimientos a los usuarios. ¡Contribuye a través de Weblate!No comment provided by engineer.
@@ -3612,7 +3641,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.
The group is fully decentralized – it is visible only to the members.
- El grupo está totalmente descentralizado: sólo es visible para los miembros.
+ El grupo está totalmente descentralizado y sólo es visible para los miembros.No comment provided by engineer.
@@ -3842,6 +3871,7 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
Unlock app
+ Desbloquear aplicaciónauthentication reason
@@ -3896,6 +3926,7 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
Upload file
+ Subir archivoserver test step
@@ -3965,10 +3996,12 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
Video will be received when your contact completes uploading it.
+ El video se recibirá cuando tu contacto termine de subirlo.No comment provided by engineer.Video will be received when your contact is online, please wait or check later!
+ El vídeo se recibirá cuando tu contacto esté en línea, por favor espera o compruébalo más tarde.No comment provided by engineer.
@@ -4013,6 +4046,7 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
Waiting for video
+ Esperando el vídeoNo comment provided by engineer.
@@ -4144,11 +4178,12 @@ SimpleX Lock debe estar activado.
You can start chat via app Settings / Database or by restarting the app
- Puede iniciar Chat a través de la Configuración / base de datos de la aplicación o reiniciando la aplicación
+ Puede iniciar Chat a través de la Configuración / Base de datos de la aplicación o reiniciando la aplicaciónNo comment provided by engineer.You can turn on SimpleX Lock via Settings.
+ Puedes activar el bloqueo de SimpleX a través de Configuración.No comment provided by engineer.
diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff
index 4615f04405..c8393c13db 100644
--- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff
+++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff
@@ -144,6 +144,7 @@
%lld minutes
+ %lld minutesNo comment provided by engineer.
@@ -153,6 +154,7 @@
%lld seconds
+ %lld secondesNo comment provided by engineer.
@@ -550,6 +552,7 @@
Authentication cancelled
+ Authentification interrompuePIN entry
@@ -659,6 +662,7 @@
Change Passcode
+ Modifier le code d'accèsNo comment provided by engineer.
@@ -668,6 +672,7 @@
Change lock mode
+ Modifier le mode de verrouillageauthentication reason
@@ -677,6 +682,7 @@
Change passcode
+ Modifier le code d'accèsauthentication reason
@@ -786,6 +792,7 @@
Compare file
+ Comparer le fichierserver test step
@@ -805,6 +812,7 @@
Confirm Passcode
+ Confirmer le code d'accèsNo comment provided by engineer.
@@ -947,11 +955,6 @@
Copierchat item action
-
- Core built at: %@
- Cœur compilé le : %@
- No comment provided by engineer.
- Core version: v%@Version du cœur : v%@
@@ -969,6 +972,7 @@
Create file
+ Créer un fichierserver test step
@@ -1008,6 +1012,7 @@
Current Passcode
+ Code d'accès actuelNo comment provided by engineer.
@@ -1195,6 +1200,7 @@
Delete file
+ Supprimer le fichierserver test step
@@ -1399,6 +1405,7 @@
Download file
+ Télécharger le fichierserver test step
@@ -1443,6 +1450,7 @@
Enable lock
+ Activer le verrouillageNo comment provided by engineer.
@@ -1502,6 +1510,7 @@
Enter Passcode
+ Entrer le code d'accèsNo comment provided by engineer.
@@ -1646,6 +1655,7 @@
Error loading %@ servers
+ Erreur lors du chargement des serveurs %@No comment provided by engineer.
@@ -1675,6 +1685,7 @@
Error saving passcode
+ Erreur lors de la sauvegarde du code d'accèsNo comment provided by engineer.
@@ -1784,6 +1795,7 @@
File transfer will be cancelled. If it's in progress it will be stoppped.
+ Le transfert de fichiers sera annulé. S'il est en cours, il sera interrompu.No comment provided by engineer.
@@ -2053,6 +2065,7 @@
Immediately
+ ImmédiatementNo comment provided by engineer.
@@ -2127,6 +2140,7 @@
Incorrect passcode
+ Code d'accès erronéPIN entry
@@ -2253,6 +2267,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
KeyChain error
+ Erreur du trousseau de clésNo comment provided by engineer.
@@ -2317,10 +2332,12 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
Lock after
+ Verrouillage aprèsNo comment provided by engineer.Lock mode
+ Mode de verrouillageNo comment provided by engineer.
@@ -2495,6 +2512,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
New Passcode
+ Nouveau code d'accèsNo comment provided by engineer.
@@ -2539,6 +2557,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
No app password
+ Pas de mot de passe pour l'appAuthentication unavailable
@@ -2592,6 +2611,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
Off
+ OffNo comment provided by engineer.
@@ -2721,22 +2741,27 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
Passcode
+ Code d'accèsNo comment provided by engineer.Passcode changed!
+ Code d'accès modifié !No comment provided by engineer.Passcode entry
+ Saisie du codeNo comment provided by engineer.Passcode not changed!
+ Le code d'accès n'a pas été modifié !No comment provided by engineer.Passcode set!
+ Code d'accès défini !No comment provided by engineer.
@@ -2811,6 +2836,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
Please remember or store it securely - there is no way to recover a lost passcode!
+ N'oubliez pas de le mémoriser ou de le conserver en toute sécurité - il n'y a aucun moyen de récupérer un code d'accès perdu !No comment provided by engineer.
@@ -3280,6 +3306,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
Server requires authorization to upload, check password
+ Le serveur requiert une autorisation pour uploader, vérifiez le mot de passeserver test error
@@ -3384,10 +3411,12 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
SimpleX Lock mode
+ Mode de SimpleX LockNo comment provided by engineer.SimpleX Lock not enabled!
+ SimpleX Lock n'est pas activé !No comment provided by engineer.
@@ -3477,6 +3506,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
Submit
+ SoumettreNo comment provided by engineer.
@@ -3491,6 +3521,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message
System authentication
+ Authentification du systèmeNo comment provided by engineer.
@@ -3839,6 +3870,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
Unlock app
+ Déverrouiller l'appauthentication reason
@@ -3893,6 +3925,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
Upload file
+ Transférer le fichierserver test step
@@ -3962,10 +3995,12 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
Video will be received when your contact completes uploading it.
+ La vidéo ne sera reçue que lorsque votre contact aura fini de la transférer.No comment provided by engineer.Video will be received when your contact is online, please wait or check later!
+ La vidéo ne sera reçue que lorsque votre contact sera en ligne. Veuillez patienter ou vérifier plus tard !No comment provided by engineer.
@@ -4010,6 +4045,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
Waiting for video
+ En attente de la vidéoNo comment provided by engineer.
@@ -4146,6 +4182,7 @@ SimpleX Lock doit être activé.
You can turn on SimpleX Lock via Settings.
+ Vous pouvez activer SimpleX Lock dans les Paramètres.No comment provided by engineer.
diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff
index 61b7c5d7ea..cda6d3771e 100644
--- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff
+++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff
@@ -144,6 +144,7 @@
%lld minutes
+ %lld minutiNo comment provided by engineer.
@@ -153,6 +154,7 @@
%lld seconds
+ %lld secondiNo comment provided by engineer.
@@ -550,6 +552,7 @@
Authentication cancelled
+ Autenticazione annullataPIN entry
@@ -659,6 +662,7 @@
Change Passcode
+ Cambia codice di accessoNo comment provided by engineer.
@@ -668,6 +672,7 @@
Change lock mode
+ Cambia modalità di bloccoauthentication reason
@@ -677,6 +682,7 @@
Change passcode
+ Cambia codice di accessoauthentication reason
@@ -786,6 +792,7 @@
Compare file
+ Confronta fileserver test step
@@ -805,6 +812,7 @@
Confirm Passcode
+ Conferma il codice di accessoNo comment provided by engineer.
@@ -947,11 +955,6 @@
Copiachat item action
-
- Core built at: %@
- Core compilato il: %@
- No comment provided by engineer.
- Core version: v%@Versione core: v%@
@@ -969,6 +972,7 @@
Create file
+ Crea fileserver test step
@@ -1008,6 +1012,7 @@
Current Passcode
+ Codice di accesso attualeNo comment provided by engineer.
@@ -1195,6 +1200,7 @@
Delete file
+ Elimina fileserver test step
@@ -1399,6 +1405,7 @@
Download file
+ Scarica fileserver test step
@@ -1443,6 +1450,7 @@
Enable lock
+ Attiva bloccoNo comment provided by engineer.
@@ -1502,6 +1510,7 @@
Enter Passcode
+ Inserisci il codice di accessoNo comment provided by engineer.
@@ -1646,6 +1655,7 @@
Error loading %@ servers
+ Errore nel caricamento dei server %@No comment provided by engineer.
@@ -1675,6 +1685,7 @@
Error saving passcode
+ Errore nel salvataggio del codice di accessoNo comment provided by engineer.
@@ -2054,6 +2065,7 @@
Immediately
+ ImmediatamenteNo comment provided by engineer.
@@ -2128,6 +2140,7 @@
Incorrect passcode
+ Codice di accesso erratoPIN entry
@@ -2254,6 +2267,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
KeyChain error
+ Errore del portachiaviNo comment provided by engineer.
@@ -2318,10 +2332,12 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
Lock after
+ Blocca dopoNo comment provided by engineer.Lock mode
+ Modalità di bloccoNo comment provided by engineer.
@@ -2496,6 +2512,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
New Passcode
+ Nuovo codice di accessoNo comment provided by engineer.
@@ -2540,6 +2557,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
No app password
+ Nessuna password dell'appAuthentication unavailable
@@ -2593,6 +2611,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
Off
+ OffNo comment provided by engineer.
@@ -2722,22 +2741,27 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
Passcode
+ Codice di accessoNo comment provided by engineer.Passcode changed!
+ Codice di accesso cambiato!No comment provided by engineer.Passcode entry
+ Inserimento del codice di accessoNo comment provided by engineer.Passcode not changed!
+ Codice di accesso non cambiato!No comment provided by engineer.Passcode set!
+ Codice di accesso impostato!No comment provided by engineer.
@@ -2812,6 +2836,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
Please remember or store it securely - there is no way to recover a lost passcode!
+ Ricordalo o conservalo in modo sicuro: non c'è modo di recuperare un codice di accesso perso!No comment provided by engineer.
@@ -3281,6 +3306,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
Server requires authorization to upload, check password
+ Il server richiede l'autorizzazione per il caricamento, controllare la passwordserver test error
@@ -3385,10 +3411,12 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
SimpleX Lock mode
+ Modalità di SimpleX LockNo comment provided by engineer.SimpleX Lock not enabled!
+ SimpleX Lock non attivato!No comment provided by engineer.
@@ -3478,6 +3506,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
Submit
+ InviaNo comment provided by engineer.
@@ -3492,6 +3521,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.
System authentication
+ Autenticazione di sistemaNo comment provided by engineer.
@@ -3840,6 +3870,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
Unlock app
+ Sblocca l'appauthentication reason
@@ -3894,6 +3925,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
Upload file
+ Invia fileserver test step
@@ -3963,10 +3995,12 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
Video will be received when your contact completes uploading it.
+ Il video verrà ricevuto quando il tuo contatto completerà l'invio.No comment provided by engineer.Video will be received when your contact is online, please wait or check later!
+ Il video verrà ricevuto quando il tuo contatto sarà in linea, attendi o controlla più tardi!No comment provided by engineer.
@@ -4011,6 +4045,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
Waiting for video
+ In attesa del videoNo comment provided by engineer.
@@ -4147,6 +4182,7 @@ SimpleX Lock deve essere attivato.
You can turn on SimpleX Lock via Settings.
+ Puoi attivare SimpleX Lock tramite le impostazioni.No comment provided by engineer.
diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff
index 81b58cc34e..5bac85622e 100644
--- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff
+++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff
@@ -144,6 +144,7 @@
%lld minutes
+ %lld minutenNo comment provided by engineer.
@@ -153,6 +154,7 @@
%lld seconds
+ %lld secondenNo comment provided by engineer.
@@ -550,6 +552,7 @@
Authentication cancelled
+ Verificatie geannuleerdPIN entry
@@ -654,11 +657,12 @@
Change
- Wijziging
+ VeranderenNo comment provided by engineer.Change Passcode
+ Toegangscode wijzigenNo comment provided by engineer.
@@ -668,6 +672,7 @@
Change lock mode
+ Wijzig de vergrendelings modusauthentication reason
@@ -677,6 +682,7 @@
Change passcode
+ Toegangscode wijzigenauthentication reason
@@ -786,6 +792,7 @@
Compare file
+ Bestand vergelijkenserver test step
@@ -805,6 +812,7 @@
Confirm Passcode
+ Bevestig toegangscodeNo comment provided by engineer.
@@ -947,11 +955,6 @@
Kopiërenchat item action
-
- Core built at: %@
- Core built at: %@
- No comment provided by engineer.
- Core version: v%@Core versie: v% @
@@ -969,6 +972,7 @@
Create file
+ Bestand makenserver test step
@@ -1008,6 +1012,7 @@
Current Passcode
+ Huidige toegangscodeNo comment provided by engineer.
@@ -1195,6 +1200,7 @@
Delete file
+ Verwijder bestandserver test step
@@ -1399,6 +1405,7 @@
Download file
+ Download bestandserver test step
@@ -1443,6 +1450,7 @@
Enable lock
+ Vergrendeling inschakelenNo comment provided by engineer.
@@ -1502,6 +1510,7 @@
Enter Passcode
+ Voer toegangscode inNo comment provided by engineer.
@@ -1646,6 +1655,7 @@
Error loading %@ servers
+ Fout bij het laden van %@ serversNo comment provided by engineer.
@@ -1675,6 +1685,7 @@
Error saving passcode
+ Fout bij opslaan van toegangscodeNo comment provided by engineer.
@@ -2054,6 +2065,7 @@
Immediately
+ OnmiddellijkNo comment provided by engineer.
@@ -2128,6 +2140,7 @@
Incorrect passcode
+ Onjuiste toegangscodePIN entry
@@ -2254,6 +2267,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
KeyChain error
+ Keychain foutNo comment provided by engineer.
@@ -2318,10 +2332,12 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
Lock after
+ Vergrendelen naNo comment provided by engineer.Lock mode
+ Vergrendeling modusNo comment provided by engineer.
@@ -2496,6 +2512,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
New Passcode
+ Nieuwe toegangscodeNo comment provided by engineer.
@@ -2540,6 +2557,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
No app password
+ Geen app wachtwoordAuthentication unavailable
@@ -2593,6 +2611,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
Off
+ UitNo comment provided by engineer.
@@ -2722,22 +2741,27 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
Passcode
+ ToegangscodeNo comment provided by engineer.Passcode changed!
+ Toegangscode gewijzigd!No comment provided by engineer.Passcode entry
+ Toegangscode invoerNo comment provided by engineer.Passcode not changed!
+ Toegangscode niet gewijzigd!No comment provided by engineer.Passcode set!
+ Toegangscode ingesteld!No comment provided by engineer.
@@ -2812,6 +2836,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
Please remember or store it securely - there is no way to recover a lost passcode!
+ Onthoud het of bewaar het veilig - er is geen manier om een verloren toegangscode te herstellen!No comment provided by engineer.
@@ -3281,6 +3306,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
Server requires authorization to upload, check password
+ Server vereist autorisatie om te uploaden, wachtwoord controlerenserver test error
@@ -3385,10 +3411,12 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
SimpleX Lock mode
+ SimpleX Vergrendel modusNo comment provided by engineer.SimpleX Lock not enabled!
+ SimpleX vergrendeling niet ingeschakeld!No comment provided by engineer.
@@ -3478,6 +3506,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
Submit
+ IndienenNo comment provided by engineer.
@@ -3492,6 +3521,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.
System authentication
+ Systeem authenticatieNo comment provided by engineer.
@@ -3840,6 +3870,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link
Unlock app
+ Ontgrendel appauthentication reason
@@ -3894,6 +3925,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link
Upload file
+ Upload bestandserver test step
@@ -3963,10 +3995,12 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link
Video will be received when your contact completes uploading it.
+ De video wordt ontvangen wanneer uw contactpersoon het uploaden heeft voltooid.No comment provided by engineer.Video will be received when your contact is online, please wait or check later!
+ De video wordt ontvangen wanneer uw contact online is, even geduld a.u.b. of kijk later!No comment provided by engineer.
@@ -4011,6 +4045,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link
Waiting for video
+ Wachten op videoNo comment provided by engineer.
@@ -4147,6 +4182,7 @@ SimpleX Lock moet ingeschakeld zijn.
You can turn on SimpleX Lock via Settings.
+ Je kunt SimpleX Vergrendeling aanzetten via Instellingen.No comment provided by engineer.
diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000000..66e480e241
--- /dev/null
+++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/Shared/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "colors" : [
+ {
+ "idiom" : "universal",
+ "locale" : "pl"
+ }
+ ],
+ "properties" : {
+ "localizable" : true
+ },
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json
new file mode 100644
index 0000000000..73c00596a7
--- /dev/null
+++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/Shared/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff
index ee7cd38aa1..8958378467 100644
--- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff
+++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff
@@ -5,2139 +5,2229 @@
-
+
-
+ No comment provided by engineer.
-
+
-
+ No comment provided by engineer.
-
+
-
+ No comment provided by engineer.
-
+
-
+ No comment provided by engineer.
-
+ (
- (
+ (No comment provided by engineer.
-
+ (can be copied)
- (można skopiować)
+ (można skopiować)No comment provided by engineer.
-
+ !1 colored!
- !1 kolorowy!
+ !1 kolorowy!No comment provided by engineer.
-
+ #secret#
- #sekret#
+ #sekret#No comment provided by engineer.
-
+ %@
- %@
+ %@No comment provided by engineer.
-
+ %@ %@
- %@ %@
+ %@ %@No comment provided by engineer.
-
+ %@ / %@
- %@ / %@
+ %@ / %@No comment provided by engineer.
-
+ %@ is connected!
- %@ jest połączony!
+ %@ jest połączony!notification title
-
+ %@ is not verified
- %@ nie jest zweryfikowany
+ %@ nie jest zweryfikowanyNo comment provided by engineer.
-
+ %@ is verified
- %@ jest zweryfikowany
+ %@ jest zweryfikowanyNo comment provided by engineer.
-
+
+ %@ servers
+ %@ serwery
+ No comment provided by engineer.
+
+ %@ wants to connect!
- %@ chce się połączyć!
+ %@ chce się połączyć!notification title
-
+ %d days
- %d dni
+ %d dnimessage ttl
-
+ %d hours
- %d godzin
+ %d godzinmessage ttl
-
+ %d min
- %d min
+ %d minmessage ttl
-
+ %d months
- %d miesięcy
+ %d miesięcymessage ttl
-
+ %d sec
- %d sek
+ %d sekmessage ttl
-
+ %d skipped message(s)
- %d pominięte wiadomość(i)
+ %d pominięte wiadomość(i)integrity error chat item
-
+ %lld
- %lld
+ %lldNo comment provided by engineer.
-
+ %lld %@
- %lld %@
+ %lld %@No comment provided by engineer.
-
+ %lld contact(s) selected
- %lld wybrany(e) kontakt(y)
+ %lld wybrany(e) kontakt(y)No comment provided by engineer.
-
+ %lld file(s) with total size of %@
- %lld plik(i) o całkowitym rozmiarze %@
+ %lld plik(i) o całkowitym rozmiarze %@No comment provided by engineer.
-
+ %lld members
- %lld członków
+ %lld członkówNo comment provided by engineer.
-
+
+ %lld minutes
+ %lld minut
+ No comment provided by engineer.
+
+ %lld second(s)
- %lld sekund(y)
+ %lld sekund(y)No comment provided by engineer.
-
+
+ %lld seconds
+ %lld sekund
+ No comment provided by engineer.
+
+ %lldd
- %lldd
+ %llddNo comment provided by engineer.
-
+ %lldh
- %lldh
+ %lldhNo comment provided by engineer.
-
+ %lldk
- %lldk
+ %lldkNo comment provided by engineer.
-
+ %lldm
- %lldm
+ %lldmNo comment provided by engineer.
-
+ %lldmth
- %lldmies
+ %lldmiesNo comment provided by engineer.
-
+ %llds
- %llds
+ %lldsNo comment provided by engineer.
-
+ %lldw
- %lldt
+ %lldtNo comment provided by engineer.
-
+ (
- (
+ (No comment provided by engineer.
-
+ )
- )
+ )No comment provided by engineer.
-
+ **Add new contact**: to create your one-time QR Code or link for your contact.
- **Dodaj nowy kontakt**: aby stworzyć swój jednorazowy kod QR lub link dla kontaktu.
+ **Dodaj nowy kontakt**: aby stworzyć swój jednorazowy kod QR lub link dla kontaktu.No comment provided by engineer.
-
+ **Create link / QR code** for your contact to use.
- **Utwórz link / kod QR**, aby Twój kontakt mógł z niego skorzystać.
+ **Utwórz link / kod QR**, aby Twój kontakt mógł z niego skorzystać.No comment provided by engineer.
-
+ **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have.
- **Bardziej prywatny**: sprawdzanie nowych wiadomości co 20 minut. Token urządzenia jest współdzielony z serwerem SimpleX Chat, ale nie informacje o liczbie kontaktów lub wiadomości.
+ **Bardziej prywatny**: sprawdzanie nowych wiadomości co 20 minut. Token urządzenia jest współdzielony z serwerem SimpleX Chat, ale nie informacje o liczbie kontaktów lub wiadomości.No comment provided by engineer.
-
+ **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app).
- **Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, sprawdzaj wiadomości okresowo w tle (zależy jak często korzystasz z aplikacji).
+ **Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, sprawdzaj wiadomości okresowo w tle (zależy jak często korzystasz z aplikacji).No comment provided by engineer.
-
+ **Paste received link** or open it in the browser and tap **Open in mobile app**.
- **Wklej otrzymany link** lub otwórz go w przeglądarce i dotknij **Otwórz w aplikacji mobilnej**.
+ **Wklej otrzymany link** lub otwórz go w przeglądarce i dotknij **Otwórz w aplikacji mobilnej**.No comment provided by engineer.
-
+ **Please note**: you will NOT be able to recover or change passphrase if you lose it.
- **Uwaga**: NIE będziesz w stanie odzyskać lub zmienić hasła, jeśli je stracisz.
+ **Uwaga**: NIE będziesz w stanie odzyskać lub zmienić hasła, jeśli je stracisz.No comment provided by engineer.
-
+ **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from.
- **Zalecane**: token urządzenia i powiadomienia są wysyłane do serwera powiadomień SimpleX Chat, ale nie treść wiadomości, rozmiar lub od kogo jest.
+ **Zalecane**: token urządzenia i powiadomienia są wysyłane do serwera powiadomień SimpleX Chat, ale nie treść wiadomości, rozmiar lub od kogo jest.No comment provided by engineer.
-
+ **Scan QR code**: to connect to your contact in person or via video call.
- **Skanuj kod QR**: aby połączyć się z kontaktem osobiście lub za pomocą połączenia wideo.
+ **Skanuj kod QR**: aby połączyć się z kontaktem osobiście lub za pomocą połączenia wideo.No comment provided by engineer.
-
+ **Warning**: Instant push notifications require passphrase saved in Keychain.
- **Uwaga**: Natychmiastowe powiadomienia push wymagają hasła zapisanego w Keychain.
+ **Uwaga**: Natychmiastowe powiadomienia push wymagają hasła zapisanego w Keychain.No comment provided by engineer.
-
+ **e2e encrypted** audio call
- **szyfrowane e2e** połączenie audio
+ **szyfrowane e2e** połączenie audioNo comment provided by engineer.
-
+ **e2e encrypted** video call
- **szyfrowane e2e** połączenie wideo
+ **szyfrowane e2e** połączenie wideoNo comment provided by engineer.
-
+ \*bold*
- \*pogrubiony*
+ \*pogrubiony*No comment provided by engineer.
-
+ ,
- ,
+ , No comment provided by engineer.
-
+ .
- .
+ .No comment provided by engineer.
-
+ 1 day
- 1 dzień
+ 1 dzieńmessage ttl
-
+ 1 hour
- 1 godzina
+ 1 godzinamessage ttl
-
+ 1 month
- 1 miesiąc
+ 1 miesiącmessage ttl
-
+ 1 week
- 1 tydzień
+ 1 tydzieńmessage ttl
-
+ 2 weeks
- 2 tygodnie
+ 2 tygodniemessage ttl
-
+ 6
- 6
+ 6No comment provided by engineer.
-
+ :
- :
+ : No comment provided by engineer.
-
+ A new contact
- Nowy kontakt
+ Nowy kontaktnotification title
-
+ A random profile will be sent to the contact that you received this link from
- Losowy profil zostanie wysłany do kontaktu, od którego otrzymałeś ten link
+ Losowy profil zostanie wysłany do kontaktu, od którego otrzymałeś ten linkNo comment provided by engineer.
-
+ A random profile will be sent to your contact
- Losowy profil zostanie wysłany do Twojego kontaktu
+ Losowy profil zostanie wysłany do Twojego kontaktuNo comment provided by engineer.
-
+ A separate TCP connection will be used **for each chat profile you have in the app**.
- Oddzielne połączenie TCP będzie używane **dla każdego profilu czatu, który masz w aplikacji**.
+ Oddzielne połączenie TCP będzie używane **dla każdego profilu czatu, który masz w aplikacji**.No comment provided by engineer.
-
+ A separate TCP connection will be used **for each contact and group member**.
**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail.
- Oddzielne połączenie TCP będzie używane **dla każdego kontaktu i członka grupy**.
+ Oddzielne połączenie TCP będzie używane **dla każdego kontaktu i członka grupy**.
**Uwaga**: jeśli masz wiele połączeń, zużycie baterii i ruchu może być znacznie wyższe, a niektóre połączenia mogą się nie udać.No comment provided by engineer.
-
+ About SimpleX
- O SimpleX
+ O SimpleXNo comment provided by engineer.
-
+ About SimpleX Chat
- O SimpleX Chat
+ O SimpleX ChatNo comment provided by engineer.
-
+ Accent color
- Kolor akcentu
+ Kolor akcentuNo comment provided by engineer.
-
+ Accept
- Akceptuj
+ Akceptujaccept contact request via notification
accept incoming call via notification
-
+ Accept contact
- Akceptuj kontakt
+ Akceptuj kontaktNo comment provided by engineer.
-
+ Accept contact request from %@?
- Zaakceptuj prośbę o kontakt od %@?
+ Zaakceptuj prośbę o kontakt od %@?notification body
-
+ Accept incognito
- Akceptuj incognito
+ Akceptuj incognitoNo comment provided by engineer.
-
+ Accept requests
- Akceptuj prośby
+ Akceptuj prośbyNo comment provided by engineer.
-
+ Add preset servers
- Dodaj gotowe serwery
+ Dodaj gotowe serweryNo comment provided by engineer.
-
+ Add profile
- Dodaj profil
+ Dodaj profilNo comment provided by engineer.
-
+ Add servers by scanning QR codes.
- Dodaj serwery, skanując kody QR.
+ Dodaj serwery, skanując kody QR.No comment provided by engineer.
-
+ Add server…
- Dodaj serwer…
+ Dodaj serwer…No comment provided by engineer.
-
+ Add to another device
- Dodaj do innego urządzenia
+ Dodaj do innego urządzeniaNo comment provided by engineer.
-
+ Add welcome message
- Dodaj wiadomość powitalną
+ Dodaj wiadomość powitalnąNo comment provided by engineer.
-
+ Admins can create the links to join groups.
- Administratorzy mogą tworzyć linki do dołączania do grup.
+ Administratorzy mogą tworzyć linki do dołączania do grup.No comment provided by engineer.
-
+ Advanced network settings
- Zaawansowane ustawienia sieci
+ Zaawansowane ustawienia sieciNo comment provided by engineer.
-
+ All chats and messages will be deleted - this cannot be undone!
- Wszystkie czaty i wiadomości zostaną usunięte - nie można tego cofnąć!
+ Wszystkie czaty i wiadomości zostaną usunięte - nie można tego cofnąć!No comment provided by engineer.
-
+ All group members will remain connected.
- Wszyscy członkowie grupy pozostaną połączeni.
+ Wszyscy członkowie grupy pozostaną połączeni.No comment provided by engineer.
-
+ All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you.
- Wszystkie wiadomości zostaną usunięte - nie można tego cofnąć! Wiadomości zostaną usunięte TYLKO dla Ciebie.
+ Wszystkie wiadomości zostaną usunięte - nie można tego cofnąć! Wiadomości zostaną usunięte TYLKO dla Ciebie.No comment provided by engineer.
-
+ All your contacts will remain connected
- Wszystkie Twoje kontakty pozostaną połączone
+ Wszystkie Twoje kontakty pozostaną połączoneNo comment provided by engineer.
-
+ Allow
- Pozwól
+ PozwólNo comment provided by engineer.
-
+ Allow disappearing messages only if your contact allows it to you.
- Zezwól na znikające wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli.
+ Zezwól na znikające wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli.No comment provided by engineer.
-
+ Allow irreversible message deletion only if your contact allows it to you.
- Zezwalaj na nieodwracalne usuwanie wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli.
+ Zezwalaj na nieodwracalne usuwanie wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli.No comment provided by engineer.
-
+ Allow sending direct messages to members.
- Zezwalaj na wysyłanie bezpośrednich wiadomości do członków.
+ Zezwalaj na wysyłanie bezpośrednich wiadomości do członków.No comment provided by engineer.
-
+ Allow sending disappearing messages.
- Zezwól na wysyłanie znikających wiadomości.
+ Zezwól na wysyłanie znikających wiadomości.No comment provided by engineer.
-
+ Allow to irreversibly delete sent messages.
- Zezwól na nieodwracalne usunięcie wysłanych wiadomości.
+ Zezwól na nieodwracalne usunięcie wysłanych wiadomości.No comment provided by engineer.
-
+ Allow to send voice messages.
- Zezwól na wysyłanie wiadomości głosowych.
+ Zezwól na wysyłanie wiadomości głosowych.No comment provided by engineer.
-
+ Allow voice messages only if your contact allows them.
- Zezwalaj na wiadomości głosowe tylko wtedy, gdy Twój kontakt na nie pozwala.
+ Zezwalaj na wiadomości głosowe tylko wtedy, gdy Twój kontakt na nie pozwala.No comment provided by engineer.
-
+ Allow voice messages?
- Zezwolić na wiadomości głosowe?
+ Zezwolić na wiadomości głosowe?No comment provided by engineer.
-
+ Allow your contacts to irreversibly delete sent messages.
- Zezwól swoim kontaktom na nieodwracalne usuwanie wysłanych wiadomości.
+ Zezwól swoim kontaktom na nieodwracalne usuwanie wysłanych wiadomości.No comment provided by engineer.
-
+ Allow your contacts to send disappearing messages.
- Zezwól swoim kontaktom na wysyłanie znikających wiadomości.
+ Zezwól swoim kontaktom na wysyłanie znikających wiadomości.No comment provided by engineer.
-
+ Allow your contacts to send voice messages.
- Zezwól swoim kontaktom na wysyłanie wiadomości głosowych.
+ Zezwól swoim kontaktom na wysyłanie wiadomości głosowych.No comment provided by engineer.
-
+ Already connected?
- Już połączony?
+ Już połączony?No comment provided by engineer.
-
+ Always use relay
- Zawsze używaj przekaźnika
+ Zawsze używaj przekaźnikaNo comment provided by engineer.
-
+ Answer call
- Odbierz połączenie
+ Odbierz połączenieNo comment provided by engineer.
-
+ App build: %@
- Kompilacja aplikacji: %@
+ Kompilacja aplikacji: %@No comment provided by engineer.
-
+ App icon
- Ikona aplikacji
+ Ikona aplikacjiNo comment provided by engineer.
-
+ App version
- Wersja aplikacji
+ Wersja aplikacjiNo comment provided by engineer.
-
+ App version: v%@
- Wersja aplikacji: v%@
+ Wersja aplikacji: v%@No comment provided by engineer.
-
+ Appearance
- Wygląd
+ WyglądNo comment provided by engineer.
-
+ Attach
- Dołącz
+ DołączNo comment provided by engineer.
-
+ Audio & video calls
- Połączenia audio i wideo
+ Połączenia audio i wideoNo comment provided by engineer.
-
+ Audio and video calls
- Połączenia audio i wideo
+ Połączenia audio i wideoNo comment provided by engineer.
-
+
+ Authentication cancelled
+ Uwierzytelnianie anulowane
+ PIN entry
+
+ Authentication failed
- Uwierzytelnianie nie powiodło się
+ Uwierzytelnianie nie powiodło sięNo comment provided by engineer.
-
+ Authentication is required before the call is connected, but you may miss calls.
- Uwierzytelnienie jest wymagane przed połączeniem, ale możesz przegapić połączenia.
+ Uwierzytelnienie jest wymagane przed połączeniem, ale możesz przegapić połączenia.No comment provided by engineer.
-
+ Authentication unavailable
- Uwierzytelnianie niedostępne
+ Uwierzytelnianie niedostępneNo comment provided by engineer.
-
+ Auto-accept contact requests
- Automatyczne akceptowanie próśb o kontakt
+ Automatyczne akceptowanie próśb o kontaktNo comment provided by engineer.
-
+ Auto-accept images
- Automatyczne akceptowanie obrazów
+ Automatyczne akceptowanie obrazówNo comment provided by engineer.
-
+ Automatically
- Automatycznie
+ AutomatycznieNo comment provided by engineer.
-
+ Back
- Wstecz
+ WsteczNo comment provided by engineer.
-
+ Both you and your contact can irreversibly delete sent messages.
- Zarówno Ty, jak i Twój kontakt możecie nieodwracalnie usunąć wysłane wiadomości.
+ Zarówno Ty, jak i Twój kontakt możecie nieodwracalnie usunąć wysłane wiadomości.No comment provided by engineer.
-
+ Both you and your contact can send disappearing messages.
- Zarówno Ty, jak i Twój kontakt możecie wysyłać znikające wiadomości.
+ Zarówno Ty, jak i Twój kontakt możecie wysyłać znikające wiadomości.No comment provided by engineer.
-
+ Both you and your contact can send voice messages.
- Zarówno Ty, jak i Twój kontakt możecie wysyłać wiadomości głosowe.
+ Zarówno Ty, jak i Twój kontakt możecie wysyłać wiadomości głosowe.No comment provided by engineer.
-
+ By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA).
- Według profilu czatu (domyślnie) lub [według połączenia](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA).
+ Według profilu czatu (domyślnie) lub [według połączenia](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA).No comment provided by engineer.
-
+ Call already ended!
- Połączenie już zakończone!
+ Połączenie już zakończone!No comment provided by engineer.
-
+ Calls
- Połączenia
+ PołączeniaNo comment provided by engineer.
-
+ Can't delete user profile!
- Nie można usunąć profilu użytkownika!
+ Nie można usunąć profilu użytkownika!No comment provided by engineer.
-
+ Can't invite contact!
- Nie można zaprosić kontaktu!
+ Nie można zaprosić kontaktu!No comment provided by engineer.
-
+ Can't invite contacts!
- Nie można zaprosić kontaktów!
+ Nie można zaprosić kontaktów!No comment provided by engineer.
-
+ Cancel
- Anuluj
+ Anulujchat item action
-
+ Cancel file transfer?
- Anulować transfer plików?
+ Anulować transfer plików?No comment provided by engineer.
-
+ Cannot access keychain to save database password
- Nie można uzyskać dostępu do pęku kluczy, aby zapisać hasło do bazy danych
+ Nie można uzyskać dostępu do pęku kluczy, aby zapisać hasło do bazy danychNo comment provided by engineer.
-
+ Cannot receive file
- Nie można odebrać pliku
+ Nie można odebrać plikuNo comment provided by engineer.
-
+ Change
- Zmień
+ ZmieńNo comment provided by engineer.
-
+
+ Change Passcode
+ Zmień kod dostępu
+ No comment provided by engineer.
+
+ Change database passphrase?
- Zmienić hasło bazy danych?
+ Zmienić hasło bazy danych?No comment provided by engineer.
-
+
+ Change lock mode
+ Zmień tryb blokady
+ authentication reason
+
+ Change member role?
- Zmienić rolę członka?
+ Zmienić rolę członka?No comment provided by engineer.
-
+
+ Change passcode
+ Zmień pin
+ authentication reason
+
+ Change receiving address
- Zmień adres odbioru
+ Zmień adres odbioruNo comment provided by engineer.
-
+ Change receiving address?
- Zmienić adres odbioru?
+ Zmienić adres odbioru?No comment provided by engineer.
-
+ Change role
- Zmień rolę
+ Zmień rolęNo comment provided by engineer.
-
+ Chat archive
- Archiwum czatu
+ Archiwum czatuNo comment provided by engineer.
-
+ Chat console
- Konsola czatu
+ Konsola czatuNo comment provided by engineer.
-
+ Chat database
- Baza danych czatu
+ Baza danych czatuNo comment provided by engineer.
-
+ Chat database deleted
- Baza danych czatu usunięta
+ Baza danych czatu usuniętaNo comment provided by engineer.
-
+ Chat database imported
- Zaimportowano bazę danych czatu
+ Zaimportowano bazę danych czatuNo comment provided by engineer.
-
+ Chat is running
- Czat jest uruchomiony
+ Czat jest uruchomionyNo comment provided by engineer.
-
+ Chat is stopped
- Czat jest zatrzymany
+ Czat jest zatrzymanyNo comment provided by engineer.
-
+ Chat preferences
- Preferencje czatu
+ Preferencje czatuNo comment provided by engineer.
-
+ Chats
- Czaty
+ CzatyNo comment provided by engineer.
-
+ Check server address and try again.
- Sprawdź adres serwera i spróbuj ponownie.
+ Sprawdź adres serwera i spróbuj ponownie.No comment provided by engineer.
-
+ Chinese and Spanish interface
- Chiński i hiszpański interfejs
+ Chiński i hiszpański interfejsNo comment provided by engineer.
-
+ Choose file
- Wybierz plik
+ Wybierz plikNo comment provided by engineer.
-
+ Choose from library
- Wybierz z biblioteki
+ Wybierz z bibliotekiNo comment provided by engineer.
-
+ Clear
- Wyczyść
+ WyczyśćNo comment provided by engineer.
-
+ Clear conversation
- Wyczyść rozmowę
+ Wyczyść rozmowęNo comment provided by engineer.
-
+ Clear conversation?
- Wyczyścić rozmowę?
+ Wyczyścić rozmowę?No comment provided by engineer.
-
+ Clear verification
- Wyczyść weryfikację
+ Wyczyść weryfikacjęNo comment provided by engineer.
-
+ Colors
- Kolory
+ KoloryNo comment provided by engineer.
-
- Compare security codes with your contacts.
- Porównaj kody bezpieczeństwa ze swoimi kontaktami.
- No comment provided by engineer.
-
-
- Configure ICE servers
- Skonfiguruj serwery ICE
- No comment provided by engineer.
-
-
- Confirm
- Potwierdź
- No comment provided by engineer.
-
-
- Confirm database upgrades
- Potwierdź aktualizacje bazy danych
- No comment provided by engineer.
-
-
- Confirm new passphrase…
- Potwierdź nowe hasło…
- No comment provided by engineer.
-
-
- Confirm password
- Potwierdź hasło
- No comment provided by engineer.
-
-
- Connect
- Połącz
+
+ Compare file
+ Porównaj plikserver test step
-
+
+ Compare security codes with your contacts.
+ Porównaj kody bezpieczeństwa ze swoimi kontaktami.
+ No comment provided by engineer.
+
+
+ Configure ICE servers
+ Skonfiguruj serwery ICE
+ No comment provided by engineer.
+
+
+ Confirm
+ Potwierdź
+ No comment provided by engineer.
+
+
+ Confirm Passcode
+ Potwierdź Pin
+ No comment provided by engineer.
+
+
+ Confirm database upgrades
+ Potwierdź aktualizacje bazy danych
+ No comment provided by engineer.
+
+
+ Confirm new passphrase…
+ Potwierdź nowe hasło…
+ No comment provided by engineer.
+
+
+ Confirm password
+ Potwierdź hasło
+ No comment provided by engineer.
+
+
+ Connect
+ Połącz
+ server test step
+
+ Connect via contact link?
- Połączyć się przez link kontaktowy?
+ Połączyć się przez link kontaktowy?No comment provided by engineer.
-
+ Connect via group link?
- Połącz się przez link grupowy?
+ Połącz się przez link grupowy?No comment provided by engineer.
-
+ Connect via link
- Połącz się przez link
+ Połącz się przez linkNo comment provided by engineer.
-
+ Connect via link / QR code
- Połącz się przez link / kod QR
+ Połącz się przez link / kod QRNo comment provided by engineer.
-
+ Connect via one-time link?
- Połączyć się przez jednorazowy link?
+ Połączyć się przez jednorazowy link?No comment provided by engineer.
-
+ Connecting to server…
- Łączenie z serwerem…
+ Łączenie z serwerem…No comment provided by engineer.
-
+ Connecting to server… (error: %@)
- Łączenie z serwerem... (błąd: %@)
+ Łączenie z serwerem... (błąd: %@)No comment provided by engineer.
-
+ Connection
- Połączenie
+ PołączenieNo comment provided by engineer.
-
+ Connection error
- Błąd połączenia
+ Błąd połączeniaNo comment provided by engineer.
-
+ Connection error (AUTH)
- Błąd połączenia (UWIERZYTELNIANIE)
+ Błąd połączenia (UWIERZYTELNIANIE)No comment provided by engineer.
-
+ Connection request
- Prośba o połączenie
+ Prośba o połączenieNo comment provided by engineer.
-
+ Connection request sent!
- Prośba o połączenie wysłana!
+ Prośba o połączenie wysłana!No comment provided by engineer.
-
+ Connection timeout
- Czas połączenia minął
+ Czas połączenia minąłNo comment provided by engineer.
-
+ Contact allows
- Kontakt pozwala
+ Kontakt pozwalaNo comment provided by engineer.
-
+ Contact already exists
- Kontakt już istnieje
+ Kontakt już istniejeNo comment provided by engineer.
-
+ Contact and all messages will be deleted - this cannot be undone!
- Kontakt i wszystkie wiadomości zostaną usunięte - nie można tego cofnąć!
+ Kontakt i wszystkie wiadomości zostaną usunięte - nie można tego cofnąć!No comment provided by engineer.
-
+ Contact hidden:
- Kontakt ukryty:
+ Kontakt ukryty:notification
-
+ Contact is connected
- Kontakt jest połączony
+ Kontakt jest połączonynotification
-
+ Contact is not connected yet!
- Kontakt nie jest jeszcze połączony!
+ Kontakt nie jest jeszcze połączony!No comment provided by engineer.
-
+ Contact name
- Nazwa kontaktu
+ Nazwa kontaktuNo comment provided by engineer.
-
+ Contact preferences
- Preferencje kontaktu
+ Preferencje kontaktuNo comment provided by engineer.
-
+ Contact requests
- Prośby kontaktu
+ Prośby kontaktuNo comment provided by engineer.
-
+ Contacts can mark messages for deletion; you will be able to view them.
- Kontakty mogą oznaczać wiadomości do usunięcia; będziesz mógł je zobaczyć.
+ Kontakty mogą oznaczać wiadomości do usunięcia; będziesz mógł je zobaczyć.No comment provided by engineer.
-
+ Copy
- Kopiuj
+ Kopiujchat item action
-
- Core built at: %@
- Kompilacja rdzenia: %@
- No comment provided by engineer.
-
-
+ Core version: v%@
- Wersja rdzenia: v%@
+ Wersja rdzenia: v%@No comment provided by engineer.
-
+