Compare commits
38 Commits
v4.0.0-bet
...
v4.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5578183777 | ||
|
|
520800ded0 | ||
|
|
47a6a81854 | ||
|
|
2a4b7b83a4 | ||
|
|
792c442aa3 | ||
|
|
54b39e8d00 | ||
|
|
39f82e9e1a | ||
|
|
ab7fed1628 | ||
|
|
c94691c89e | ||
|
|
3c95b76e5a | ||
|
|
a977a0dd17 | ||
|
|
e1a7b02e59 | ||
|
|
3537d3871c | ||
|
|
093e7b4c78 | ||
|
|
6e9cf2ba91 | ||
|
|
7c06961ff8 | ||
|
|
2b53406ccf | ||
|
|
06c79cc2bc | ||
|
|
5e870ec30f | ||
|
|
6a6d246dc5 | ||
|
|
07191bfb61 | ||
|
|
b62895ca76 | ||
|
|
bd2a748169 | ||
|
|
06c7b9a995 | ||
|
|
42b6bf96ff | ||
|
|
aa6fb2fc12 | ||
|
|
8bfeab7071 | ||
|
|
97662b040e | ||
|
|
a9ba16b07a | ||
|
|
36d93f5b0e | ||
|
|
ba25850b73 | ||
|
|
9b75553ddc | ||
|
|
b390630f4b | ||
|
|
df329d305b | ||
|
|
59b4ce2474 | ||
|
|
9442656bbe | ||
|
|
0494cce77d | ||
|
|
909a8aaf9c |
2
apps/android/.idea/codeStyles/Project.xml
generated
@@ -13,7 +13,9 @@
|
||||
<codeStyleSettings language="XML">
|
||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "chat.simplex.app"
|
||||
minSdk 29
|
||||
targetSdk 32
|
||||
versionCode 54
|
||||
versionName "4.0-beta.1"
|
||||
versionCode 59
|
||||
versionName "4.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
ndk {
|
||||
|
||||
@@ -24,8 +24,8 @@ var TransformOperation;
|
||||
let activeCall;
|
||||
const processCommand = (function () {
|
||||
const defaultIceServers = [
|
||||
{ urls: ["stun:stun.simplex.im:5349"] },
|
||||
{ urls: ["turn:turn.simplex.im:5349"], username: "private", credential: "yleob6AVkiNI87hpR94Z" },
|
||||
{ urls: ["stun:stun.simplex.im:443"] },
|
||||
{ urls: ["turn:turn.simplex.im:443"], username: "private", credential: "yleob6AVkiNI87hpR94Z" },
|
||||
];
|
||||
function getCallConfig(encodedInsertableStreams, iceServers, relay) {
|
||||
return {
|
||||
@@ -495,6 +495,7 @@ function callCryptoFunction() {
|
||||
const initialPlainTextRequired = {
|
||||
key: 10,
|
||||
delta: 3,
|
||||
empty: 1,
|
||||
};
|
||||
const IV_LENGTH = 12;
|
||||
function encryptFrame(key) {
|
||||
@@ -505,7 +506,9 @@ function callCryptoFunction() {
|
||||
const initial = data.subarray(0, n);
|
||||
const plaintext = data.subarray(n, data.byteLength);
|
||||
try {
|
||||
const ciphertext = new Uint8Array(plaintext.length ? await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv.buffer }, key, plaintext) : 0);
|
||||
const ciphertext = plaintext.length
|
||||
? new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv.buffer }, key, plaintext))
|
||||
: new Uint8Array(0);
|
||||
frame.data = concatN(initial, ciphertext, iv).buffer;
|
||||
controller.enqueue(frame);
|
||||
}
|
||||
@@ -523,7 +526,9 @@ function callCryptoFunction() {
|
||||
const ciphertext = data.subarray(n, data.byteLength - IV_LENGTH);
|
||||
const iv = data.subarray(data.byteLength - IV_LENGTH, data.byteLength);
|
||||
try {
|
||||
const plaintext = new Uint8Array(ciphertext.length ? await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext) : 0);
|
||||
const plaintext = ciphertext.length
|
||||
? new Uint8Array(await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext))
|
||||
: new Uint8Array(0);
|
||||
frame.data = concatN(initial, plaintext).buffer;
|
||||
controller.enqueue(frame);
|
||||
}
|
||||
|
||||
@@ -22,42 +22,33 @@ Java_chat_simplex_app_SimplexAppKt_initHS(__unused JNIEnv *env, __unused jclass
|
||||
}
|
||||
|
||||
// from simplex-chat
|
||||
typedef void* chat_ctrl;
|
||||
typedef long* chat_ctrl;
|
||||
|
||||
extern char *chat_migrate_db(const char *path, const char *key);
|
||||
extern chat_ctrl chat_init_key(const char *path, const char *key);
|
||||
extern chat_ctrl chat_init(const char *path); // deprecated
|
||||
extern char *chat_migrate_init(const char *path, const char *key, chat_ctrl *ctrl);
|
||||
extern char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
|
||||
extern char *chat_recv_msg(chat_ctrl ctrl); // deprecated
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
|
||||
extern char *chat_parse_markdown(const char *str);
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_app_SimplexAppKt_chatMigrateDB(JNIEnv *env, __unused jclass clazz, jstring dbPath, jstring dbKey) {
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_chat_simplex_app_SimplexAppKt_chatMigrateInit(JNIEnv *env, __unused jclass clazz, jstring dbPath, jstring dbKey) {
|
||||
const char *_dbPath = (*env)->GetStringUTFChars(env, dbPath, JNI_FALSE);
|
||||
const char *_dbKey = (*env)->GetStringUTFChars(env, dbKey, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_migrate_db(_dbPath, _dbKey));
|
||||
jlong _ctrl = (jlong) 0;
|
||||
jstring res = (*env)->NewStringUTF(env, chat_migrate_init(_dbPath, _dbKey, &_ctrl));
|
||||
(*env)->ReleaseStringUTFChars(env, dbPath, _dbPath);
|
||||
(*env)->ReleaseStringUTFChars(env, dbKey, _dbKey);
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_chat_simplex_app_SimplexAppKt_chatInitKey(JNIEnv *env, __unused jclass clazz, jstring dbPath, jstring dbKey) {
|
||||
const char *_dbPath = (*env)->GetStringUTFChars(env, dbPath, JNI_FALSE);
|
||||
const char *_dbKey = (*env)->GetStringUTFChars(env, dbKey, JNI_FALSE);
|
||||
jlong ctrl = (jlong)chat_init_key(_dbPath, _dbKey);
|
||||
(*env)->ReleaseStringUTFChars(env, dbPath, _dbPath);
|
||||
(*env)->ReleaseStringUTFChars(env, dbKey, _dbKey);
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_chat_simplex_app_SimplexAppKt_chatInit(JNIEnv *env, __unused jclass clazz, jstring dbPath) {
|
||||
const char *_dbPath = (*env)->GetStringUTFChars(env, dbPath, JNI_FALSE);
|
||||
jlong ctrl = (jlong)chat_init(_dbPath);
|
||||
(*env)->ReleaseStringUTFChars(env, dbPath, _dbPath);
|
||||
return ctrl;
|
||||
// Creating array of Object's (boxed values can be passed, eg. Long instead of long)
|
||||
jobjectArray ret = (jobjectArray)(*env)->NewObjectArray(env, 2, (*env)->FindClass(env, "java/lang/Object"), NULL);
|
||||
// Java's String
|
||||
(*env)->SetObjectArrayElement(env, ret, 0, res);
|
||||
// Java's Long
|
||||
(*env)->SetObjectArrayElement(env, ret, 1,
|
||||
(*env)->NewObject(env, (*env)->FindClass(env, "java/lang/Long"),
|
||||
(*env)->GetMethodID(env, (*env)->FindClass(env, "java/lang/Long"), "<init>", "(J)V"),
|
||||
_ctrl));
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
|
||||
@@ -10,6 +10,7 @@ import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.onboarding.OnboardingStage
|
||||
import chat.simplex.app.views.usersettings.NotificationsMode
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.util.*
|
||||
@@ -26,9 +27,7 @@ external fun pipeStdOutToSocket(socketName: String) : Int
|
||||
|
||||
// SimpleX API
|
||||
typealias ChatCtrl = Long
|
||||
external fun chatMigrateDB(dbPath: String, dbKey: String): String
|
||||
external fun chatInitKey(dbPath: String, dbKey: String): ChatCtrl
|
||||
external fun chatInit(dbPath: String): ChatCtrl
|
||||
external fun chatMigrateInit(dbPath: String, dbKey: String): Array<Any>
|
||||
external fun chatSendCmd(ctrl: ChatCtrl, msg: String): String
|
||||
external fun chatRecvMsg(ctrl: ChatCtrl): String
|
||||
external fun chatRecvMsgWait(ctrl: ChatCtrl, timeout: Int): String
|
||||
@@ -38,20 +37,24 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
lateinit var chatController: ChatController
|
||||
|
||||
fun initChatController(useKey: String? = null, startChat: Boolean = true) {
|
||||
val dbKey = useKey ?: DatabaseUtils.getDatabaseKey() ?: ""
|
||||
val res = DatabaseUtils.migrateChatDatabase(dbKey)
|
||||
val ctrl = if (res.second is DBMigrationResult.OK) {
|
||||
chatInitKey(getFilesDirectory(applicationContext), dbKey)
|
||||
val dbKey = useKey ?: DatabaseUtils.useDatabaseKey() ?: ""
|
||||
val dbAbsolutePathPrefix = getFilesDirectory(SimplexApp.context)
|
||||
val migrated: Array<Any> = chatMigrateInit(dbAbsolutePathPrefix, dbKey)
|
||||
val res: DBMigrationResult = kotlin.runCatching {
|
||||
json.decodeFromString<DBMigrationResult>(migrated[0] as String)
|
||||
}.getOrElse { DBMigrationResult.Unknown(migrated[0] as String) }
|
||||
val ctrl = if (res is DBMigrationResult.OK) {
|
||||
migrated[1] as Long
|
||||
} else null
|
||||
if (::chatController.isInitialized) {
|
||||
chatController.ctrl = ctrl
|
||||
} else {
|
||||
chatController = ChatController(ctrl, ntfManager, applicationContext, appPreferences)
|
||||
}
|
||||
chatModel.chatDbEncrypted.value = res.first
|
||||
chatModel.chatDbStatus.value = res.second
|
||||
if (res.second != DBMigrationResult.OK) {
|
||||
Log.d(TAG, "Unable to migrate successfully: ${res.second}")
|
||||
chatModel.chatDbEncrypted.value = dbKey != ""
|
||||
chatModel.chatDbStatus.value = res
|
||||
if (res != DBMigrationResult.OK) {
|
||||
Log.d(TAG, "Unable to migrate successfully: $res")
|
||||
} else if (startChat) {
|
||||
// If we migrated successfully means previous re-encryption process on database level finished successfully too
|
||||
if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null)
|
||||
|
||||
@@ -30,6 +30,7 @@ class ChatModel(val controller: ChatController) {
|
||||
val chatDbChanged = mutableStateOf<Boolean>(false)
|
||||
val chatDbEncrypted = mutableStateOf<Boolean?>(false)
|
||||
val chatDbStatus = mutableStateOf<DBMigrationResult?>(null)
|
||||
val chatDbDeleted = mutableStateOf(false)
|
||||
val chats = mutableStateListOf<Chat>()
|
||||
|
||||
// current chat
|
||||
@@ -37,7 +38,6 @@ class ChatModel(val controller: ChatController) {
|
||||
val chatItems = mutableStateListOf<ChatItem>()
|
||||
val groupMembers = mutableStateListOf<GroupMember>()
|
||||
|
||||
var connReqInvitation: String? = null
|
||||
val terminalItems = mutableStateListOf<TerminalItem>()
|
||||
val userAddress = mutableStateOf<String?>(null)
|
||||
val userSMPServers = mutableStateOf<(List<String>)?>(null)
|
||||
@@ -1442,6 +1442,9 @@ enum class FormatColor(val color: String) {
|
||||
@Serializable
|
||||
class SndFileTransfer() {}
|
||||
|
||||
@Serializable
|
||||
class RcvFileTransfer() {}
|
||||
|
||||
@Serializable
|
||||
class FileTransferMeta() {}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import chat.simplex.app.R
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.call.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.ConnectViaLinkTab
|
||||
import chat.simplex.app.views.onboarding.OnboardingStage
|
||||
import chat.simplex.app.views.usersettings.NotificationPreviewMode
|
||||
import chat.simplex.app.views.usersettings.NotificationsMode
|
||||
@@ -87,6 +88,7 @@ class AppPreferences(val context: Context) {
|
||||
)
|
||||
val performLA = mkBoolPreference(SHARED_PREFS_PERFORM_LA, false)
|
||||
val laNoticeShown = mkBoolPreference(SHARED_PREFS_LA_NOTICE_SHOWN, false)
|
||||
val webrtcIceServers = mkStrPreference(SHARED_PREFS_WEBRTC_ICE_SERVERS, null)
|
||||
val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true)
|
||||
val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true)
|
||||
val experimentalCalls = mkBoolPreference(SHARED_PREFS_EXPERIMENTAL_CALLS, false)
|
||||
@@ -105,6 +107,7 @@ class AppPreferences(val context: Context) {
|
||||
val networkTCPKeepIntvl = mkIntPreference(SHARED_PREFS_NETWORK_TCP_KEEP_INTVL, KeepAliveOpts.defaults.keepIntvl)
|
||||
val networkTCPKeepCnt = mkIntPreference(SHARED_PREFS_NETWORK_TCP_KEEP_CNT, KeepAliveOpts.defaults.keepCnt)
|
||||
val incognito = mkBoolPreference(SHARED_PREFS_INCOGNITO, false)
|
||||
val connectViaLinkTab = mkStrPreference(SHARED_PREFS_CONNECT_VIA_LINK_TAB, ConnectViaLinkTab.SCAN.name)
|
||||
|
||||
val storeDBPassphrase = mkBoolPreference(SHARED_PREFS_STORE_DB_PASSPHRASE, true)
|
||||
val initialRandomDBPassphrase = mkBoolPreference(SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE, false)
|
||||
@@ -174,6 +177,7 @@ class AppPreferences(val context: Context) {
|
||||
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_NOTICE_SHOWN = "LANoticeShown"
|
||||
private const val SHARED_PREFS_WEBRTC_ICE_SERVERS = "WebrtcICEServers"
|
||||
private const val SHARED_PREFS_PRIVACY_ACCEPT_IMAGES = "PrivacyAcceptImages"
|
||||
private const val SHARED_PREFS_PRIVACY_LINK_PREVIEWS = "PrivacyLinkPreviews"
|
||||
private const val SHARED_PREFS_EXPERIMENTAL_CALLS = "ExperimentalCalls"
|
||||
@@ -192,6 +196,7 @@ class AppPreferences(val context: Context) {
|
||||
private const val SHARED_PREFS_NETWORK_TCP_KEEP_INTVL = "NetworkTCPKeepIntvl"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_KEEP_CNT = "NetworkTCPKeepCnt"
|
||||
private const val SHARED_PREFS_INCOGNITO = "Incognito"
|
||||
private const val SHARED_PREFS_CONNECT_VIA_LINK_TAB = "ConnectViaLinkTab"
|
||||
private const val SHARED_PREFS_STORE_DB_PASSPHRASE = "StoreDBPassphrase"
|
||||
private const val SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE = "InitialRandomDBPassphrase"
|
||||
private const val SHARED_PREFS_ENCRYPTED_DB_PASSPHRASE = "EncryptedDBPassphrase"
|
||||
@@ -385,9 +390,15 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
suspend fun apiSendMessage(type: ChatType, id: Long, file: String? = null, quotedItemId: Long? = null, mc: MsgContent): AChatItem? {
|
||||
val cmd = CC.ApiSendMessage(type, id, file, quotedItemId, mc)
|
||||
val r = sendCmd(cmd)
|
||||
if (r is CR.NewChatItem ) return r.chatItem
|
||||
Log.e(TAG, "apiSendMessage bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
return when (r) {
|
||||
is CR.NewChatItem -> r.chatItem
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiSendMessage", generalGetString(R.string.error_sending_message), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiUpdateChatItem(type: ChatType, id: Long, itemId: Long, mc: MsgContent): AChatItem? {
|
||||
@@ -475,9 +486,15 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
|
||||
suspend fun apiAddContact(): String? {
|
||||
val r = sendCmd(CC.AddContact())
|
||||
if (r is CR.Invitation) return r.connReqInvitation
|
||||
Log.e(TAG, "apiAddContact bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
return when (r) {
|
||||
is CR.Invitation -> r.connReqInvitation
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAddContact", generalGetString(R.string.connection_error), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiConnect(connReq: String): Boolean {
|
||||
@@ -509,7 +526,9 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
return false
|
||||
}
|
||||
else -> {
|
||||
apiErrorAlert("apiConnect", "Connection error", r)
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiConnect", generalGetString(R.string.connection_error), r)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -526,7 +545,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
if (e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.ContactGroups) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.cannot_delete_contact),
|
||||
String.format(generalGetString(R.string.contact_cannot_be_deleted_as_they_are_in_groups), e.errorType.contact.displayName, e.errorType.groupNames)
|
||||
String.format(generalGetString(R.string.contact_cannot_be_deleted_as_they_are_in_groups), e.errorType.contact.displayName, e.errorType.groupNames.joinToString(", "))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -581,9 +600,15 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
|
||||
suspend fun apiCreateUserAddress(): String? {
|
||||
val r = sendCmd(CC.CreateMyAddress())
|
||||
if (r is CR.UserContactLinkCreated) return r.connReqContact
|
||||
Log.e(TAG, "apiCreateUserAddress bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
return when (r) {
|
||||
is CR.UserContactLinkCreated -> r.connReqContact
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiCreateUserAddress", generalGetString(R.string.error_creating_address), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiDeleteUserAddress(): Boolean {
|
||||
@@ -606,9 +631,24 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
|
||||
suspend fun apiAcceptContactRequest(contactReqId: Long): Contact? {
|
||||
val r = sendCmd(CC.ApiAcceptContact(contactReqId))
|
||||
if (r is CR.AcceptingContactRequest) return r.contact
|
||||
Log.e(TAG, "apiAcceptContactRequest bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
return when {
|
||||
r is CR.AcceptingContactRequest -> r.contact
|
||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
|
||||
&& r.chatError.agentError is AgentErrorType.SMP
|
||||
&& r.chatError.agentError.smpErr is SMPErrorType.AUTH -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.connection_error_auth),
|
||||
generalGetString(R.string.sender_may_have_deleted_the_connection_request)
|
||||
)
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAcceptContactRequest", generalGetString(R.string.error_accepting_contact_request), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiRejectContactRequest(contactReqId: Long): Boolean {
|
||||
@@ -666,9 +706,22 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
|
||||
suspend fun apiReceiveFile(fileId: Long): AChatItem? {
|
||||
val r = sendCmd(CC.ReceiveFile(fileId))
|
||||
if (r is CR.RcvFileAccepted) return r.chatItem
|
||||
Log.e(TAG, "apiReceiveFile bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
return when (r) {
|
||||
is CR.RcvFileAccepted -> r.chatItem
|
||||
is CR.RcvFileAcceptedSndCancelled -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.cannot_receive_file),
|
||||
generalGetString(R.string.sender_cancelled_file_transfer)
|
||||
)
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiReceiveFile", generalGetString(R.string.error_receiving_file), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiNewGroup(p: GroupProfile): GroupInfo? {
|
||||
@@ -680,9 +733,15 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
|
||||
suspend fun apiAddMember(groupId: Long, contactId: Long, memberRole: GroupMemberRole): GroupMember? {
|
||||
val r = sendCmd(CC.ApiAddMember(groupId, contactId, memberRole))
|
||||
if (r is CR.SentGroupInvitation) return r.member
|
||||
Log.e(TAG, "apiAddMember bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
return when (r) {
|
||||
is CR.SentGroupInvitation -> r.member
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAddMember", generalGetString(R.string.error_adding_members), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiJoinGroup(groupId: Long) {
|
||||
@@ -699,11 +758,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
} else if (e is ChatError.ChatErrorStore && e.storeError is StoreError.GroupNotFound) {
|
||||
deleteGroup()
|
||||
AlertManager.shared.showAlertMsg(generalGetString(R.string.alert_title_no_group), generalGetString(R.string.alert_message_no_group))
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(generalGetString(R.string.alert_title_join_group_error), "$e")
|
||||
} else if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiJoinGroup", generalGetString(R.string.error_joining_group), r)
|
||||
}
|
||||
}
|
||||
else -> Log.e(TAG, "apiJoinGroup bad response: ${r.responseType} ${r.details}")
|
||||
else -> apiErrorAlert("apiJoinGroup", generalGetString(R.string.error_joining_group), r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,6 +805,30 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
}
|
||||
}
|
||||
|
||||
private fun networkErrorAlert(r: CR): Boolean {
|
||||
return when {
|
||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
|
||||
&& r.chatError.agentError is AgentErrorType.BROKER
|
||||
&& r.chatError.agentError.brokerErr is BrokerErrorType.TIMEOUT -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.connection_timeout),
|
||||
generalGetString(R.string.network_error_desc)
|
||||
)
|
||||
true
|
||||
}
|
||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
|
||||
&& r.chatError.agentError is AgentErrorType.BROKER
|
||||
&& r.chatError.agentError.brokerErr is BrokerErrorType.NETWORK -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.connection_error),
|
||||
generalGetString(R.string.network_error_desc)
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun apiErrorAlert(method: String, title: String, r: CR) {
|
||||
val errMsg = "${r.responseType}: ${r.details}"
|
||||
Log.e(TAG, "$method bad response: $errMsg")
|
||||
@@ -882,7 +965,16 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
|
||||
withCall(r, r.contact) { call ->
|
||||
chatModel.activeCall.value = call.copy(callState = CallState.OfferReceived, peerMedia = r.callType.media, sharedKey = r.sharedKey)
|
||||
val useRelay = chatModel.controller.appPrefs.webrtcPolicyRelay.get()
|
||||
chatModel.callCommand.value = WCallCommand.Offer(offer = r.offer.rtcSession, iceCandidates = r.offer.rtcIceCandidates, media = r.callType.media, aesKey = r.sharedKey, relay = useRelay)
|
||||
val iceServers = getIceServers()
|
||||
Log.d(TAG, ".callOffer iceServers $iceServers")
|
||||
chatModel.callCommand.value = WCallCommand.Offer(
|
||||
offer = r.offer.rtcSession,
|
||||
iceCandidates = r.offer.rtcIceCandidates,
|
||||
media = r.callType.media,
|
||||
aesKey = r.sharedKey,
|
||||
iceServers = iceServers,
|
||||
relay = useRelay
|
||||
)
|
||||
}
|
||||
}
|
||||
is CR.CallAnswer -> {
|
||||
@@ -1592,6 +1684,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("groupUpdated") class GroupUpdated(val toGroup: GroupInfo): CR()
|
||||
// receiving file events
|
||||
@Serializable @SerialName("rcvFileAccepted") class RcvFileAccepted(val chatItem: AChatItem): CR()
|
||||
@Serializable @SerialName("rcvFileAcceptedSndCancelled") class RcvFileAcceptedSndCancelled(val rcvFileTransfer: RcvFileTransfer): CR()
|
||||
@Serializable @SerialName("rcvFileStart") class RcvFileStart(val chatItem: AChatItem): CR()
|
||||
@Serializable @SerialName("rcvFileComplete") class RcvFileComplete(val chatItem: AChatItem): CR()
|
||||
// sending file events
|
||||
@@ -1676,6 +1769,7 @@ sealed class CR {
|
||||
is ConnectedToGroupMember -> "connectedToGroupMember"
|
||||
is GroupRemoved -> "groupRemoved"
|
||||
is GroupUpdated -> "groupUpdated"
|
||||
is RcvFileAcceptedSndCancelled -> "rcvFileAcceptedSndCancelled"
|
||||
is RcvFileAccepted -> "rcvFileAccepted"
|
||||
is RcvFileStart -> "rcvFileStart"
|
||||
is RcvFileComplete -> "rcvFileComplete"
|
||||
@@ -1761,6 +1855,7 @@ sealed class CR {
|
||||
is ConnectedToGroupMember -> "groupInfo: $groupInfo\nmember: $member"
|
||||
is GroupRemoved -> json.encodeToString(groupInfo)
|
||||
is GroupUpdated -> json.encodeToString(toGroup)
|
||||
is RcvFileAcceptedSndCancelled -> noDetails()
|
||||
is RcvFileAccepted -> json.encodeToString(chatItem)
|
||||
is RcvFileStart -> json.encodeToString(chatItem)
|
||||
is RcvFileComplete -> json.encodeToString(chatItem)
|
||||
|
||||
@@ -51,7 +51,14 @@ class CallManager(val chatModel: ChatModel) {
|
||||
)
|
||||
showCallView.value = true
|
||||
val useRelay = controller.appPrefs.webrtcPolicyRelay.get()
|
||||
callCommand.value = WCallCommand.Start (media = invitation.callType.media, aesKey = invitation.sharedKey, relay = useRelay)
|
||||
val iceServers = getIceServers()
|
||||
Log.d(TAG, "answerIncomingCall iceServers: $iceServers")
|
||||
callCommand.value = WCallCommand.Start(
|
||||
media = invitation.callType.media,
|
||||
aesKey = invitation.sharedKey,
|
||||
iceServers = iceServers,
|
||||
relay = useRelay
|
||||
)
|
||||
callInvitations.remove(invitation.contact.id)
|
||||
if (invitation.contact.id == activeCallInvitation.value?.contact?.id) {
|
||||
activeCallInvitation.value = null
|
||||
|
||||
@@ -3,11 +3,13 @@ package chat.simplex.app.views.call
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.SimplexApp
|
||||
import chat.simplex.app.model.Contact
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.net.URI
|
||||
|
||||
data class Call(
|
||||
val contact: Contact,
|
||||
@@ -115,7 +117,7 @@ sealed class WCallResponse {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate
|
||||
@Serializable class RTCIceCandidate(val candidateType: RTCIceCandidateType?)
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer
|
||||
@Serializable class RTCIceServer(val urls: List<String>, val username: String? = null, val credential: String? = null)
|
||||
@Serializable data class RTCIceServer(val urls: List<String>, val username: String? = null, val credential: String? = null)
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/type
|
||||
@Serializable
|
||||
@@ -153,4 +155,48 @@ class ConnectionState(
|
||||
val iceConnectionState: String,
|
||||
val iceGatheringState: String,
|
||||
val signalingState: String
|
||||
)
|
||||
)
|
||||
|
||||
// the servers are expected in this format:
|
||||
// stun:stun.simplex.im:443
|
||||
// turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443
|
||||
fun parseRTCIceServer(str: String): RTCIceServer? {
|
||||
var s = replaceScheme(str, "stun:")
|
||||
s = replaceScheme(s, "turn:")
|
||||
val u = runCatching { URI(s) }.getOrNull()
|
||||
if (u != null) {
|
||||
val scheme = u.scheme
|
||||
val host = u.host
|
||||
val port = u.port
|
||||
if (u.path == "" && (scheme == "stun" || scheme == "turn")) {
|
||||
val userInfo = u.userInfo?.split(":")
|
||||
return RTCIceServer(
|
||||
urls = listOf("$scheme:$host:$port"),
|
||||
username = userInfo?.getOrNull(0),
|
||||
credential = userInfo?.getOrNull(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun replaceScheme(s: String, scheme: String): String = if (s.startsWith(scheme)) s.replace(scheme, "$scheme//") else s
|
||||
|
||||
fun parseRTCIceServers(servers: List<String>): List<RTCIceServer>? {
|
||||
val iceServers: ArrayList<RTCIceServer> = ArrayList()
|
||||
for (s in servers) {
|
||||
val server = parseRTCIceServer(s)
|
||||
if (server != null) {
|
||||
iceServers.add(server)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return if (iceServers.isEmpty()) null else iceServers
|
||||
}
|
||||
|
||||
fun getIceServers(): List<RTCIceServer>? {
|
||||
val value = SimplexApp.context.chatController.appPrefs.webrtcIceServers.get() ?: return null
|
||||
val servers: List<String> = value.split("\n")
|
||||
return parseRTCIceServers(servers)
|
||||
}
|
||||
|
||||
@@ -569,7 +569,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
if (cItem.isRcvNew) {
|
||||
LaunchedEffect(cItem.id) {
|
||||
scope.launch {
|
||||
delay(750)
|
||||
delay(600)
|
||||
markRead(CC.ItemRange(cItem.id, cItem.id), null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,12 @@ fun AddGroupMembersView(groupInfo: GroupInfo, chatModel: ChatModel, close: () ->
|
||||
selectedRole = selectedRole,
|
||||
inviteMembers = {
|
||||
withApi {
|
||||
selectedContacts.forEach {
|
||||
val member = chatModel.controller.apiAddMember(groupInfo.groupId, it, selectedRole.value)
|
||||
for (contactId in selectedContacts) {
|
||||
val member = chatModel.controller.apiAddMember(groupInfo.groupId, contactId, selectedRole.value)
|
||||
if (member != null) {
|
||||
chatModel.upsertGroupMember(groupInfo, member)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
close.invoke()
|
||||
|
||||
@@ -151,7 +151,7 @@ fun GroupChatInfoLayout(
|
||||
}
|
||||
SectionDivider()
|
||||
}
|
||||
SectionItemView(height = 50.dp) {
|
||||
SectionItemView(minHeight = 50.dp) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
if (members.isNotEmpty()) {
|
||||
@@ -243,7 +243,7 @@ fun AddMembersButton(tint: Color = MaterialTheme.colors.primary, addMembers: ()
|
||||
fun MembersList(members: List<GroupMember>, showMemberInfo: (GroupMember) -> Unit) {
|
||||
Column {
|
||||
members.forEachIndexed { index, member ->
|
||||
SectionItemView(height = 50.dp) {
|
||||
SectionItemView(minHeight = 50.dp) {
|
||||
MemberRow(member, showMemberInfo)
|
||||
}
|
||||
if (index < members.lastIndex) {
|
||||
|
||||
@@ -462,7 +462,7 @@ fun ChatListNavLinkLayout(
|
||||
showMenu: MutableState<Boolean>,
|
||||
stopped: Boolean
|
||||
) {
|
||||
var modifier = Modifier.fillMaxWidth().height(88.dp)
|
||||
var modifier = Modifier.fillMaxWidth().heightIn(min = 88.dp)
|
||||
if (!stopped) modifier = modifier.combinedClickable(onClick = click, onLongClick = { showMenu.value = true })
|
||||
Surface(modifier) {
|
||||
Row(
|
||||
|
||||
@@ -14,7 +14,10 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -22,11 +25,12 @@ import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.Indigo
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.NewChatSheet
|
||||
import chat.simplex.app.views.onboarding.MakeConnection
|
||||
import chat.simplex.app.views.usersettings.SettingsView
|
||||
import chat.simplex.app.views.usersettings.simplexTeamUri
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -80,6 +84,26 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped:
|
||||
drawerContent = { SettingsView(chatModel, setPerformLA) },
|
||||
sheetPeekHeight = 0.dp,
|
||||
sheetContent = { NewChatSheet(chatModel, scaffoldCtrl) },
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
if (!stopped) {
|
||||
if (!scaffoldCtrl.expanded.value) scaffoldCtrl.expand() else scaffoldCtrl.collapse()
|
||||
}
|
||||
},
|
||||
Modifier.padding(bottom = 90.dp),
|
||||
elevation = FloatingActionButtonDefaults.elevation(
|
||||
defaultElevation = 0.dp,
|
||||
pressedElevation = 0.dp,
|
||||
hoveredElevation = 0.dp,
|
||||
focusedElevation = 0.dp,
|
||||
),
|
||||
backgroundColor = if (!stopped) MaterialTheme.colors.primary else HighOrLowlight,
|
||||
contentColor = Color.White
|
||||
) {
|
||||
Icon(Icons.Default.Edit, stringResource(R.string.add_contact_or_create_group))
|
||||
}
|
||||
},
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp),
|
||||
) {
|
||||
Box {
|
||||
@@ -91,7 +115,9 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped:
|
||||
if (chatModel.chats.isNotEmpty()) {
|
||||
ChatList(chatModel, search = searchInList)
|
||||
} else {
|
||||
MakeConnection(chatModel)
|
||||
if (!stopped) {
|
||||
OnboardingButtons(scaffoldCtrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scaffoldCtrl.expanded.value) {
|
||||
@@ -106,6 +132,50 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped:
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OnboardingButtons(scaffoldCtrl: ScaffoldController) {
|
||||
Box {
|
||||
Column(Modifier.fillMaxSize().padding(6.dp), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Bottom) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
ConnectButton(generalGetString(R.string.chat_with_developers)) {
|
||||
uriHandler.openUri(simplexTeamUri)
|
||||
}
|
||||
Spacer(Modifier.height(10.dp))
|
||||
ConnectButton(generalGetString(R.string.tap_to_start_new_chat)) {
|
||||
scaffoldCtrl.toggleSheet()
|
||||
}
|
||||
val color = MaterialTheme.colors.primary
|
||||
Canvas(modifier = Modifier.width(46.dp).height(10.dp), onDraw = {
|
||||
val trianglePath = Path().apply {
|
||||
moveTo(0.dp.toPx(), 0f)
|
||||
lineTo(16.dp.toPx(), 0.dp.toPx())
|
||||
lineTo(8.dp.toPx(), 10.dp.toPx())
|
||||
lineTo(0.dp.toPx(), 0.dp.toPx())
|
||||
}
|
||||
drawPath(
|
||||
color = color,
|
||||
path = trianglePath
|
||||
)
|
||||
})
|
||||
Spacer(Modifier.height(80.dp))
|
||||
}
|
||||
Text(stringResource(R.string.you_have_no_chats), Modifier.align(Alignment.Center), color = HighOrLowlight)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConnectButton(text: String, onClick: () -> Unit) {
|
||||
Box(
|
||||
Modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(MaterialTheme.colors.primary)
|
||||
.clickable { onClick() }
|
||||
.padding(vertical = 10.dp, horizontal = 20.dp),
|
||||
) {
|
||||
Text(text, color = Color.White)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatListToolbar(chatModel: ChatModel, scaffoldCtrl: ScaffoldController, stopped: Boolean, onSearchValueChanged: (String) -> Unit) {
|
||||
var showSearch by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -121,20 +191,14 @@ fun ChatListToolbar(chatModel: ChatModel, scaffoldCtrl: ScaffoldController, stop
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!stopped) {
|
||||
if (stopped) {
|
||||
barButtons.add {
|
||||
IconButton(onClick = { scaffoldCtrl.toggleSheet() }) {
|
||||
Icon(
|
||||
Icons.Outlined.AddCircle,
|
||||
stringResource(R.string.add_contact),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
IconButton(onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.chat_is_stopped_indication),
|
||||
generalGetString(R.string.you_can_start_chat_via_setting_or_by_restarting_the_app)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
barButtons.add {
|
||||
IconButton(onClick = { AlertManager.shared.showAlertMsg(generalGetString(R.string.chat_is_stopped_indication),
|
||||
generalGetString(R.string.you_can_start_chat_via_setting_or_by_restarting_the_app)) }) {
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.Report,
|
||||
generalGetString(R.string.chat_is_stopped_indication),
|
||||
|
||||
@@ -62,6 +62,7 @@ fun DatabaseView(
|
||||
importArchiveAlert(m, context, uri, progressIndicator)
|
||||
}
|
||||
}
|
||||
val chatDbDeleted = remember { m.chatDbDeleted }
|
||||
val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(getAppFilesDirectory(context))) }
|
||||
LaunchedEffect(m.chatRunning) {
|
||||
runChat.value = m.chatRunning.value ?: true
|
||||
@@ -79,6 +80,7 @@ fun DatabaseView(
|
||||
chatArchiveName,
|
||||
chatArchiveTime,
|
||||
chatLastStart,
|
||||
chatDbDeleted.value,
|
||||
appFilesCountAndSize,
|
||||
startChat = { startChat(m, runChat, chatLastStart, m.chatDbChanged) },
|
||||
stopChatAlert = { stopChatAlert(m, runChat, context) },
|
||||
@@ -115,6 +117,7 @@ fun DatabaseLayout(
|
||||
chatArchiveName: MutableState<String?>,
|
||||
chatArchiveTime: MutableState<Instant?>,
|
||||
chatLastStart: MutableState<Instant?>,
|
||||
chatDbDeleted: Boolean,
|
||||
appFilesCountAndSize: MutableState<Pair<Int, Long>>,
|
||||
startChat: () -> Unit,
|
||||
stopChatAlert: () -> Unit,
|
||||
@@ -137,7 +140,7 @@ fun DatabaseLayout(
|
||||
)
|
||||
|
||||
SectionView(stringResource(R.string.run_chat_section)) {
|
||||
RunChatSetting(runChat, stopped, startChat, stopChatAlert)
|
||||
RunChatSetting(runChat, stopped, chatDbDeleted, startChat, stopChatAlert)
|
||||
}
|
||||
SectionSpacer()
|
||||
|
||||
@@ -230,6 +233,7 @@ fun DatabaseLayout(
|
||||
fun RunChatSetting(
|
||||
runChat: Boolean,
|
||||
stopped: Boolean,
|
||||
chatDbDeleted: Boolean,
|
||||
startChat: () -> Unit,
|
||||
stopChatAlert: () -> Unit
|
||||
) {
|
||||
@@ -248,6 +252,7 @@ fun RunChatSetting(
|
||||
)
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
Switch(
|
||||
enabled= !chatDbDeleted,
|
||||
checked = runChat,
|
||||
onCheckedChange = { runChatSwitch ->
|
||||
if (runChatSwitch) {
|
||||
@@ -516,6 +521,7 @@ private fun deleteChat(m: ChatModel, progressIndicator: MutableState<Boolean>) {
|
||||
withApi {
|
||||
try {
|
||||
m.controller.apiDeleteStorage()
|
||||
m.chatDbDeleted.value = true
|
||||
DatabaseUtils.removeDatabaseKey()
|
||||
m.controller.appPrefs.storeDBPassphrase.set(true)
|
||||
operationEnded(m, progressIndicator) {
|
||||
@@ -569,6 +575,7 @@ fun PreviewDatabaseLayout() {
|
||||
chatArchiveName = remember { mutableStateOf("dummy_archive") },
|
||||
chatArchiveTime = remember { mutableStateOf(Clock.System.now()) },
|
||||
chatLastStart = remember { mutableStateOf(Clock.System.now()) },
|
||||
chatDbDeleted = false,
|
||||
appFilesCountAndSize = remember { mutableStateOf(0 to 0L) },
|
||||
startChat = {},
|
||||
stopChatAlert = {},
|
||||
|
||||
@@ -3,7 +3,6 @@ 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.json
|
||||
import chat.simplex.app.views.usersettings.Cryptor
|
||||
import kotlinx.serialization.*
|
||||
import java.io.File
|
||||
@@ -41,31 +40,27 @@ object DatabaseUtils {
|
||||
appPreferences.initializationVectorDBPassphrase.set(null)
|
||||
}
|
||||
|
||||
fun migrateChatDatabase(useKey: String? = null): Pair<Boolean, DBMigrationResult> {
|
||||
Log.d(TAG, "migrateChatDatabase ${appPreferences.storeDBPassphrase.get()}")
|
||||
val dbAbsolutePathPrefix = getFilesDirectory(SimplexApp.context)
|
||||
fun useDatabaseKey(): String {
|
||||
Log.d(TAG, "useDatabaseKey ${appPreferences.storeDBPassphrase.get()}")
|
||||
var dbKey = ""
|
||||
val useKeychain = appPreferences.storeDBPassphrase.get()
|
||||
if (useKey != null) {
|
||||
dbKey = useKey
|
||||
} else if (useKeychain) {
|
||||
if (useKeychain) {
|
||||
if (!hasDatabase(SimplexApp.context.dataDir.absolutePath)) {
|
||||
dbKey = randomDatabasePassword()
|
||||
setDatabaseKey(dbKey)
|
||||
appPreferences.initialRandomDBPassphrase.set(true)
|
||||
} else {
|
||||
dbKey = getDatabaseKey() ?: ""
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "migrateChatDatabase DB path: $dbAbsolutePathPrefix")
|
||||
val migrated = chatMigrateDB(dbAbsolutePathPrefix, dbKey)
|
||||
val res: DBMigrationResult = kotlin.runCatching {
|
||||
json.decodeFromString<DBMigrationResult>(migrated)
|
||||
}.getOrElse { DBMigrationResult.Unknown(migrated) }
|
||||
val encrypted = dbKey != ""
|
||||
return encrypted to res
|
||||
return dbKey
|
||||
}
|
||||
|
||||
private fun randomDatabasePassword(): String = ByteArray(32).apply { SecureRandom().nextBytes(this) }.toBase64String()
|
||||
private fun randomDatabasePassword(): String {
|
||||
val s = ByteArray(32)
|
||||
SecureRandom().nextBytes(s)
|
||||
return s.toBase64String().replace("\n", "")
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ExpandLess
|
||||
import androidx.compose.material.icons.outlined.ExpandMore
|
||||
import androidx.compose.runtime.*
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSettingRow(
|
||||
title: String,
|
||||
values: List<Pair<T, String>>,
|
||||
selection: State<T>,
|
||||
label: String? = null,
|
||||
icon: ImageVector? = null,
|
||||
iconTint: Color = HighOrLowlight,
|
||||
enabled: State<Boolean> = mutableStateOf(true),
|
||||
onSelected: (T) -> Unit
|
||||
) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
icon,
|
||||
"",
|
||||
Modifier.padding(end = 8.dp),
|
||||
tint = iconTint
|
||||
)
|
||||
}
|
||||
Text(title, color = if (enabled.value) Color.Unspecified else HighOrLowlight)
|
||||
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = {
|
||||
expanded = !expanded && enabled.value
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(start = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text(
|
||||
values.first { it.first == selection.value }.second + (if (label != null) " $label" else ""),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = HighOrLowlight
|
||||
)
|
||||
Spacer(Modifier.size(12.dp))
|
||||
Icon(
|
||||
if (!expanded) Icons.Outlined.ExpandMore else Icons.Outlined.ExpandLess,
|
||||
generalGetString(R.string.icon_descr_more_button),
|
||||
tint = HighOrLowlight
|
||||
)
|
||||
}
|
||||
ExposedDropdownMenu(
|
||||
modifier = Modifier.widthIn(min = 200.dp),
|
||||
expanded = expanded,
|
||||
onDismissRequest = {
|
||||
expanded = false
|
||||
}
|
||||
) {
|
||||
values.forEach { selectionOption ->
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onSelected(selectionOption.first)
|
||||
expanded = false
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
selectionOption.second + (if (label != null) " $label" else ""),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,11 +58,11 @@ fun <T> SectionViewSelectable(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SectionItemView(click: (() -> Unit)? = null, height: Dp = 46.dp, disabled: Boolean = false, content: (@Composable RowScope.() -> Unit)) {
|
||||
fun SectionItemView(click: (() -> Unit)? = null, minHeight: Dp = 46.dp, disabled: Boolean = false, content: (@Composable RowScope.() -> Unit)) {
|
||||
val modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.fillMaxWidth()
|
||||
.height(height)
|
||||
.sizeIn(minHeight = minHeight)
|
||||
Row(
|
||||
if (click == null || disabled) modifier else modifier.clickable(onClick = click),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -74,7 +74,7 @@ fun SectionItemView(click: (() -> Unit)? = null, height: Dp = 46.dp, disabled: B
|
||||
@Composable
|
||||
fun SectionItemViewSpaceBetween(
|
||||
click: (() -> Unit)? = null,
|
||||
height: Dp = 46.dp,
|
||||
minHeight: Dp = 46.dp,
|
||||
padding: PaddingValues = PaddingValues(horizontal = 8.dp),
|
||||
disabled: Boolean = false,
|
||||
content: (@Composable () -> Unit)
|
||||
@@ -82,7 +82,7 @@ fun SectionItemViewSpaceBetween(
|
||||
val modifier = Modifier
|
||||
.padding(padding)
|
||||
.fillMaxWidth()
|
||||
.height(height)
|
||||
.sizeIn(minHeight = minHeight)
|
||||
Row(
|
||||
if (click == null || disabled) modifier else modifier.clickable(onClick = click),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
||||
@@ -2,6 +2,8 @@ package chat.simplex.app.views.newchat
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.TheaterComedy
|
||||
@@ -12,7 +14,6 @@ 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.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -20,20 +21,16 @@ import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import chat.simplex.app.views.helpers.shareText
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun AddContactView(chatModel: ChatModel) {
|
||||
val connReq = chatModel.connReqInvitation
|
||||
if (connReq != null) {
|
||||
val cxt = LocalContext.current
|
||||
AddContactLayout(
|
||||
chatModelIncognito = chatModel.incognito.value,
|
||||
connReq = connReq,
|
||||
share = { shareText(cxt, connReq) }
|
||||
)
|
||||
}
|
||||
fun AddContactView(chatModel: ChatModel, connReqInvitation: String) {
|
||||
val cxt = LocalContext.current
|
||||
AddContactLayout(
|
||||
chatModelIncognito = chatModel.incognito.value,
|
||||
connReq = connReqInvitation,
|
||||
share = { shareText(cxt, connReqInvitation) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -41,21 +38,18 @@ fun AddContactLayout(chatModelIncognito: Boolean, connReq: String, share: () ->
|
||||
BoxWithConstraints {
|
||||
val screenHeight = maxHeight
|
||||
Column(
|
||||
Modifier.padding(bottom = 16.dp),
|
||||
Modifier.verticalScroll(rememberScrollState()).padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.add_contact),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
|
||||
modifier = Modifier
|
||||
.padding(vertical = 5.dp)
|
||||
.padding(horizontal = 8.dp)
|
||||
Modifier.padding(bottom = 16.dp),
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
Text(
|
||||
stringResource(R.string.show_QR_code_for_your_contact_to_scan_from_the_app__multiline),
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
Row(Modifier.padding(horizontal = 8.dp)) {
|
||||
Row {
|
||||
InfoAboutIncognito(
|
||||
chatModelIncognito,
|
||||
true,
|
||||
@@ -63,18 +57,27 @@ fun AddContactLayout(chatModelIncognito: Boolean, connReq: String, share: () ->
|
||||
generalGetString(R.string.your_profile_will_be_sent)
|
||||
)
|
||||
}
|
||||
QRCode(
|
||||
connReq, Modifier
|
||||
.weight(1f, fill = false)
|
||||
.aspectRatio(1f)
|
||||
.padding(vertical = 3.dp)
|
||||
)
|
||||
if (connReq.isNotEmpty()) {
|
||||
QRCode(
|
||||
connReq, Modifier
|
||||
.aspectRatio(1f)
|
||||
.padding(vertical = 3.dp)
|
||||
)
|
||||
} else {
|
||||
CircularProgressIndicator(
|
||||
Modifier
|
||||
.size(36.dp)
|
||||
.padding(4.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = HighOrLowlight,
|
||||
strokeWidth = 3.dp
|
||||
)
|
||||
}
|
||||
Text(
|
||||
stringResource(R.string.if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel),
|
||||
annotatedStringResource(R.string.if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel),
|
||||
lineHeight = 22.sp,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(bottom = if (screenHeight > 600.dp) 16.dp else 8.dp)
|
||||
.padding(bottom = if (screenHeight > 600.dp) 8.dp else 0.dp)
|
||||
)
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package chat.simplex.app.views.newchat
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.usersettings.UserAddressView
|
||||
|
||||
enum class ConnectViaLinkTab {
|
||||
SCAN, PASTE
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConnectViaLinkView(m: ChatModel) {
|
||||
val selection = remember {
|
||||
mutableStateOf(
|
||||
runCatching { ConnectViaLinkTab.valueOf(m.controller.appPrefs.connectViaLinkTab.get()!!) }.getOrDefault(ConnectViaLinkTab.SCAN)
|
||||
)
|
||||
}
|
||||
val tabTitles = ConnectViaLinkTab.values().map {
|
||||
when (it) {
|
||||
ConnectViaLinkTab.SCAN -> stringResource(R.string.scan_QR_code)
|
||||
ConnectViaLinkTab.PASTE -> stringResource(R.string.paste_the_link_you_received)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
Modifier.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
when (selection.value) {
|
||||
ConnectViaLinkTab.SCAN -> {
|
||||
ScanToConnectView(m)
|
||||
}
|
||||
ConnectViaLinkTab.PASTE -> {
|
||||
PasteToConnectView(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
TabRow(
|
||||
selectedTabIndex = selection.value.ordinal,
|
||||
backgroundColor = MaterialTheme.colors.background,
|
||||
contentColor = MaterialTheme.colors.primary,
|
||||
) {
|
||||
tabTitles.forEachIndexed { index, it ->
|
||||
Tab(
|
||||
selected = selection.value.ordinal == index,
|
||||
onClick = {
|
||||
selection.value = ConnectViaLinkTab.values()[index]
|
||||
m.controller.appPrefs.connectViaLinkTab.set(selection.value .name)
|
||||
},
|
||||
text = { Text(it, fontSize = 13.sp) },
|
||||
icon = {
|
||||
Icon(
|
||||
if (ConnectViaLinkTab.SCAN.ordinal == index) Icons.Outlined.QrCode else Icons.Outlined.Article,
|
||||
it
|
||||
)
|
||||
},
|
||||
selectedContentColor = MaterialTheme.colors.primary,
|
||||
unselectedContentColor = HighOrLowlight,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package chat.simplex.app.views.newchat
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.usersettings.UserAddressView
|
||||
|
||||
enum class CreateLinkTab {
|
||||
ONE_TIME, LONG_TERM
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
|
||||
val selection = remember { mutableStateOf(initialSelection) }
|
||||
val connReqInvitation = remember { mutableStateOf("") }
|
||||
val creatingConnReq = remember { mutableStateOf(false) }
|
||||
LaunchedEffect(selection.value) {
|
||||
if (selection.value == CreateLinkTab.ONE_TIME && connReqInvitation.value.isEmpty() && !creatingConnReq.value) {
|
||||
createInvitation(m, creatingConnReq, connReqInvitation)
|
||||
}
|
||||
}
|
||||
val tabTitles = CreateLinkTab.values().map {
|
||||
when {
|
||||
it == CreateLinkTab.ONE_TIME && connReqInvitation.value.isEmpty() -> stringResource(R.string.create_one_time_link)
|
||||
it == CreateLinkTab.ONE_TIME -> stringResource(R.string.one_time_link)
|
||||
it == CreateLinkTab.LONG_TERM -> stringResource(R.string.your_contact_address)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
Column(
|
||||
Modifier.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
when (selection.value) {
|
||||
CreateLinkTab.ONE_TIME -> {
|
||||
AddContactView(m, connReqInvitation.value)
|
||||
}
|
||||
CreateLinkTab.LONG_TERM -> {
|
||||
UserAddressView(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
TabRow(
|
||||
selectedTabIndex = selection.value.ordinal,
|
||||
backgroundColor = MaterialTheme.colors.background,
|
||||
contentColor = MaterialTheme.colors.primary,
|
||||
) {
|
||||
tabTitles.forEachIndexed { index, it ->
|
||||
Tab(
|
||||
selected = selection.value.ordinal == index,
|
||||
onClick = {
|
||||
selection.value = CreateLinkTab.values()[index]
|
||||
},
|
||||
text = { Text(it, fontSize = 13.sp) },
|
||||
icon = {
|
||||
Icon(
|
||||
if (CreateLinkTab.ONE_TIME.ordinal == index) Icons.Outlined.RepeatOne else Icons.Outlined.AllInclusive,
|
||||
it
|
||||
)
|
||||
},
|
||||
selectedContentColor = MaterialTheme.colors.primary,
|
||||
unselectedContentColor = HighOrLowlight,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createInvitation(m: ChatModel, creatingConnReq: MutableState<Boolean>, connReqInvitation: MutableState<String>) {
|
||||
creatingConnReq.value = true
|
||||
withApi {
|
||||
val connReq = m.controller.apiAddContact()
|
||||
if (connReq != null) {
|
||||
connReqInvitation.value = connReq
|
||||
} else {
|
||||
creatingConnReq.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package chat.simplex.app.views.newchat
|
||||
|
||||
import android.Manifest
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -22,32 +22,18 @@ import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chatlist.ScaffoldController
|
||||
import chat.simplex.app.views.helpers.ModalManager
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
|
||||
@Composable
|
||||
fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController) {
|
||||
val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
|
||||
if (newChatCtrl.expanded.value) BackHandler { newChatCtrl.collapse() }
|
||||
NewChatSheetLayout(
|
||||
addContact = {
|
||||
withApi {
|
||||
// show spinner
|
||||
chatModel.connReqInvitation = chatModel.controller.apiAddContact()
|
||||
// hide spinner
|
||||
if (chatModel.connReqInvitation != null) {
|
||||
newChatCtrl.collapse()
|
||||
ModalManager.shared.showModal { AddContactView(chatModel) }
|
||||
}
|
||||
}
|
||||
},
|
||||
scanCode = {
|
||||
newChatCtrl.collapse()
|
||||
ModalManager.shared.showCustomModal { close -> ScanToConnectView(chatModel, close) }
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
ModalManager.shared.showModal { CreateLinkView(chatModel, CreateLinkTab.ONE_TIME) }
|
||||
},
|
||||
pasteLink = {
|
||||
connectViaLink = {
|
||||
newChatCtrl.collapse()
|
||||
ModalManager.shared.showCustomModal { close -> PasteToConnectView(chatModel, close) }
|
||||
ModalManager.shared.showModal { ConnectViaLinkView(chatModel) }
|
||||
},
|
||||
createGroup = {
|
||||
newChatCtrl.collapse()
|
||||
@@ -59,8 +45,7 @@ fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController) {
|
||||
@Composable
|
||||
fun NewChatSheetLayout(
|
||||
addContact: () -> Unit,
|
||||
scanCode: () -> Unit,
|
||||
pasteLink: () -> Unit,
|
||||
connectViaLink: () -> Unit,
|
||||
createGroup: () -> Unit
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
@@ -73,7 +58,7 @@ fun NewChatSheetLayout(
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
Box(boxModifier) {
|
||||
ActionRowButton(
|
||||
stringResource(R.string.create_one_time_link),
|
||||
stringResource(R.string.share_one_time_link),
|
||||
stringResource(R.string.to_share_with_your_contact),
|
||||
Icons.Outlined.AddLink,
|
||||
click = addContact
|
||||
@@ -82,19 +67,10 @@ fun NewChatSheetLayout(
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
Box(boxModifier) {
|
||||
ActionRowButton(
|
||||
stringResource(R.string.paste_received_link),
|
||||
stringResource(R.string.paste_received_link_from_clipboard),
|
||||
Icons.Outlined.Article,
|
||||
click = pasteLink
|
||||
)
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
Box(boxModifier) {
|
||||
ActionRowButton(
|
||||
stringResource(R.string.scan_QR_code),
|
||||
stringResource(R.string.in_person_or_in_video_call__bracketed),
|
||||
stringResource(R.string.connect_via_link_or_qr),
|
||||
stringResource(R.string.connect_via_link_or_qr_from_clipboard_or_in_person),
|
||||
Icons.Outlined.QrCode,
|
||||
click = scanCode
|
||||
click = connectViaLink
|
||||
)
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
@@ -125,7 +101,7 @@ fun ActionRowButton(
|
||||
Column {
|
||||
Text(
|
||||
text,
|
||||
textAlign = TextAlign.Center,
|
||||
textAlign = TextAlign.Left,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = tint
|
||||
)
|
||||
@@ -133,7 +109,7 @@ fun ActionRowButton(
|
||||
if (comment != null) {
|
||||
Text(
|
||||
comment,
|
||||
textAlign = TextAlign.Center,
|
||||
textAlign = TextAlign.Left,
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
@@ -189,8 +165,7 @@ fun PreviewNewChatSheet() {
|
||||
SimpleXTheme {
|
||||
NewChatSheetLayout(
|
||||
addContact = {},
|
||||
scanCode = {},
|
||||
pasteLink = {},
|
||||
connectViaLink = {},
|
||||
createGroup = {}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,18 +3,17 @@ package chat.simplex.app.views.newchat
|
||||
import android.content.ClipboardManager
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
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.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
@@ -25,16 +24,15 @@ import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun PasteToConnectView(chatModel: ChatModel, close: () -> Unit) {
|
||||
val connectionLink = remember { mutableStateOf("")}
|
||||
fun PasteToConnectView(chatModel: ChatModel) {
|
||||
val connectionLink = remember { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
val clipboard = getSystemService(context, ClipboardManager::class.java)
|
||||
BackHandler(onBack = close)
|
||||
PasteToConnectLayout(
|
||||
chatModel.incognito.value,
|
||||
connectionLink = connectionLink,
|
||||
pasteFromClipboard = {
|
||||
connectionLink.value = clipboard?.primaryClip?.getItemAt(0)?.coerceToText(context) as String
|
||||
connectionLink.value = clipboard?.primaryClip?.getItemAt(0)?.coerceToText(context) as? String ?: return@PasteToConnectLayout
|
||||
},
|
||||
connectViaLink = { connReqUri ->
|
||||
try {
|
||||
@@ -48,9 +46,7 @@ fun PasteToConnectView(chatModel: ChatModel, close: () -> Unit) {
|
||||
text = generalGetString(R.string.this_string_is_not_a_connection_link)
|
||||
)
|
||||
}
|
||||
close()
|
||||
},
|
||||
close = close
|
||||
)
|
||||
}
|
||||
|
||||
@@ -60,52 +56,49 @@ fun PasteToConnectLayout(
|
||||
connectionLink: MutableState<String>,
|
||||
pasteFromClipboard: () -> Unit,
|
||||
connectViaLink: (String) -> Unit,
|
||||
close: () -> Unit
|
||||
) {
|
||||
ModalView(close) {
|
||||
Column(
|
||||
Column(
|
||||
Modifier.verticalScroll(rememberScrollState()).padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.connect_via_link),
|
||||
Modifier.padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.connect_via_link),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
|
||||
modifier = Modifier.padding(vertical = 5.dp)
|
||||
)
|
||||
Text(stringResource(R.string.paste_connection_link_below_to_connect))
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
Text(stringResource(R.string.paste_connection_link_below_to_connect))
|
||||
|
||||
InfoAboutIncognito(
|
||||
chatModelIncognito,
|
||||
true,
|
||||
generalGetString(R.string.incognito_random_profile_from_contact_description),
|
||||
generalGetString(R.string.profile_will_be_sent_to_contact_sending_link)
|
||||
)
|
||||
InfoAboutIncognito(
|
||||
chatModelIncognito,
|
||||
true,
|
||||
generalGetString(R.string.incognito_random_profile_from_contact_description),
|
||||
generalGetString(R.string.profile_will_be_sent_to_contact_sending_link)
|
||||
)
|
||||
|
||||
Box(Modifier.padding(top = 16.dp, bottom = 6.dp)) {
|
||||
TextEditor(Modifier.height(180.dp), text = connectionLink)
|
||||
}
|
||||
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(bottom = 6.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
) {
|
||||
if (connectionLink.value == "") {
|
||||
SimpleButton(text = "Paste", icon = Icons.Outlined.ContentPaste) {
|
||||
pasteFromClipboard()
|
||||
}
|
||||
} else {
|
||||
SimpleButton(text = "Clear", icon = Icons.Outlined.Clear) {
|
||||
connectionLink.value = ""
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.weight(1f).fillMaxWidth())
|
||||
SimpleButton(text = "Connect", icon = Icons.Outlined.Link) {
|
||||
connectViaLink(connectionLink.value)
|
||||
}
|
||||
}
|
||||
|
||||
Text(annotatedStringResource(R.string.you_can_also_connect_by_clicking_the_link))
|
||||
Box(Modifier.padding(top = 16.dp, bottom = 6.dp)) {
|
||||
TextEditor(Modifier.height(180.dp), text = connectionLink)
|
||||
}
|
||||
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(bottom = 6.dp),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
) {
|
||||
if (connectionLink.value == "") {
|
||||
SimpleButton(text = stringResource(R.string.paste_button), icon = Icons.Outlined.ContentPaste) {
|
||||
pasteFromClipboard()
|
||||
}
|
||||
} else {
|
||||
SimpleButton(text = stringResource(R.string.clear_verb), icon = Icons.Outlined.Clear) {
|
||||
connectionLink.value = ""
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.weight(1f).fillMaxWidth())
|
||||
SimpleButton(text = stringResource(R.string.connect_button), icon = Icons.Outlined.Link) {
|
||||
connectViaLink(connectionLink.value)
|
||||
}
|
||||
}
|
||||
|
||||
Text(annotatedStringResource(R.string.you_can_also_connect_by_clicking_the_link))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +123,6 @@ fun PreviewPasteToConnectTextbox() {
|
||||
e.printStackTrace()
|
||||
}
|
||||
},
|
||||
close = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package chat.simplex.app.views.newchat
|
||||
|
||||
import android.Manifest
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -16,10 +17,14 @@ import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
|
||||
@Composable
|
||||
fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
|
||||
BackHandler(onBack = close)
|
||||
fun ScanToConnectView(chatModel: ChatModel) {
|
||||
val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
|
||||
LaunchedEffect(Unit) {
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
}
|
||||
ConnectContactLayout(
|
||||
chatModelIncognito = chatModel.incognito.value,
|
||||
qrCodeScanner = {
|
||||
@@ -35,10 +40,8 @@ fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
|
||||
text = generalGetString(R.string.this_QR_code_is_not_a_link)
|
||||
)
|
||||
}
|
||||
close()
|
||||
}
|
||||
},
|
||||
close = close
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,33 +70,31 @@ suspend fun connectViaUri(chatModel: ChatModel, action: String, uri: Uri) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConnectContactLayout(chatModelIncognito: Boolean, qrCodeScanner: @Composable () -> Unit, close: () -> Unit) {
|
||||
ModalView(close) {
|
||||
Column(
|
||||
fun ConnectContactLayout(chatModelIncognito: Boolean, qrCodeScanner: @Composable () -> Unit) {
|
||||
Column(
|
||||
Modifier.verticalScroll(rememberScrollState()).padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
generalGetString(R.string.scan_QR_code),
|
||||
Modifier.padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
generalGetString(R.string.scan_QR_code),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
|
||||
modifier = Modifier.padding(vertical = 5.dp)
|
||||
)
|
||||
InfoAboutIncognito(
|
||||
chatModelIncognito,
|
||||
true,
|
||||
generalGetString(R.string.incognito_random_profile_description),
|
||||
generalGetString(R.string.your_profile_will_be_sent)
|
||||
)
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(ratio = 1F)
|
||||
) { qrCodeScanner() }
|
||||
Text(
|
||||
annotatedStringResource(R.string.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
}
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
InfoAboutIncognito(
|
||||
chatModelIncognito,
|
||||
true,
|
||||
generalGetString(R.string.incognito_random_profile_description),
|
||||
generalGetString(R.string.your_profile_will_be_sent)
|
||||
)
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(ratio = 1F)
|
||||
) { qrCodeScanner() }
|
||||
Text(
|
||||
annotatedStringResource(R.string.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +110,6 @@ fun PreviewConnectContactLayout() {
|
||||
ConnectContactLayout(
|
||||
chatModelIncognito = false,
|
||||
qrCodeScanner = { Surface {} },
|
||||
close = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
package chat.simplex.app.views.onboarding
|
||||
|
||||
import android.Manifest
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.model.User
|
||||
import chat.simplex.app.ui.theme.SimpleButton
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.*
|
||||
import chat.simplex.app.views.usersettings.simplexTeamUri
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
|
||||
@Composable
|
||||
fun MakeConnection(chatModel: ChatModel) {
|
||||
val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
|
||||
MakeConnectionLayout(
|
||||
chatModel.currentUser.value,
|
||||
createLink = {
|
||||
withApi {
|
||||
// show spinner
|
||||
chatModel.connReqInvitation = chatModel.controller.apiAddContact()
|
||||
// hide spinner
|
||||
if (chatModel.connReqInvitation != null) {
|
||||
ModalManager.shared.showModal { AddContactView(chatModel) }
|
||||
}
|
||||
}
|
||||
},
|
||||
pasteLink = {
|
||||
ModalManager.shared.showCustomModal { close -> PasteToConnectView(chatModel, close) }
|
||||
},
|
||||
scanCode = {
|
||||
ModalManager.shared.showCustomModal { close -> ScanToConnectView(chatModel, close) }
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
},
|
||||
about = {
|
||||
chatModel.onboardingStage.value = OnboardingStage.Step1_SimpleXInfo
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MakeConnectionLayout(
|
||||
user: User?,
|
||||
createLink: () -> Unit,
|
||||
pasteLink: () -> Unit,
|
||||
scanCode: () -> Unit,
|
||||
about: () -> Unit
|
||||
) {
|
||||
Surface {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(color = MaterialTheme.colors.background)
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Text(
|
||||
if (user == null) stringResource(R.string.make_private_connection)
|
||||
else String.format(stringResource(R.string.personal_welcome), user.profile.displayName),
|
||||
style = MaterialTheme.typography.h1,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
Text(
|
||||
annotatedStringResource(R.string.to_make_your_first_private_connection_choose),
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
ActionRow(
|
||||
Icons.Outlined.AddLink,
|
||||
R.string.create_1_time_link_qr_code,
|
||||
R.string.it_s_secure_to_share__only_one_contact_can_use_it,
|
||||
createLink
|
||||
)
|
||||
ActionRow(
|
||||
Icons.Outlined.Article,
|
||||
R.string.paste_the_link_you_received,
|
||||
R.string.or_open_the_link_in_the_browser_and_tap_open_in_mobile,
|
||||
pasteLink
|
||||
)
|
||||
ActionRow(
|
||||
Icons.Outlined.QrCode,
|
||||
R.string.scan_contact_s_qr_code,
|
||||
R.string.in_person_or_via_a_video_call__the_most_secure_way_to_connect,
|
||||
scanCode
|
||||
)
|
||||
Box(Modifier.fillMaxWidth().padding(bottom = 16.dp), contentAlignment = Alignment.Center) {
|
||||
Text(stringResource(R.string.or))
|
||||
}
|
||||
val uriHandler = LocalUriHandler.current
|
||||
ActionRow(
|
||||
Icons.Outlined.Tag,
|
||||
R.string.connect_with_the_developers,
|
||||
R.string.to_ask_any_questions_and_to_receive_simplex_chat_updates,
|
||||
{ uriHandler.openUri(simplexTeamUri) }
|
||||
)
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
SimpleButton(
|
||||
text = stringResource(R.string.about_simplex),
|
||||
icon = Icons.Outlined.ArrowBackIosNew,
|
||||
click = about
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActionRow(icon: ImageVector, @StringRes titleId: Int, @StringRes textId: Int, action: () -> Unit) {
|
||||
Row(
|
||||
Modifier
|
||||
.clickable { action() }
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
Icon(icon, stringResource(titleId), tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(end = 10.dp).size(40.dp))
|
||||
Column {
|
||||
Text(stringResource(titleId), color = MaterialTheme.colors.primary)
|
||||
Text(annotatedStringResource(textId))
|
||||
}
|
||||
}
|
||||
// Button(action: action, label: {
|
||||
// HStack(alignment: .top, spacing: 20) {
|
||||
// Image(systemName: icon)
|
||||
// .resizable()
|
||||
// .scaledToFit()
|
||||
// .frame(width: 30, height: 30)
|
||||
// .padding(.leading, 4)
|
||||
// .padding(.top, 6)
|
||||
// VStack(alignment: .leading) {
|
||||
// Group {
|
||||
// Text(title).font(.headline)
|
||||
// Text(text).foregroundColor(.primary)
|
||||
// }
|
||||
// .multilineTextAlignment(.leading)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .padding(.bottom)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
showBackground = true,
|
||||
name = "Dark Mode"
|
||||
)
|
||||
@Composable
|
||||
fun PreviewMakeConnection() {
|
||||
SimpleXTheme {
|
||||
MakeConnectionLayout(
|
||||
user = User.sampleData,
|
||||
createLink = {},
|
||||
pasteLink = {},
|
||||
scanCode = {},
|
||||
about = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
@@ -44,9 +45,9 @@ fun SimpleXInfoLayout(
|
||||
|
||||
Text(stringResource(R.string.next_generation_of_private_messaging), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = 16.dp))
|
||||
|
||||
InfoRow("🎭", R.string.privacy_redefined, R.string.first_platform_without_user_ids)
|
||||
InfoRow("📭", R.string.immune_to_spam_and_abuse, R.string.people_can_connect_only_via_links_you_share)
|
||||
InfoRow("🤝", R.string.decentralized, R.string.opensource_protocol_and_code_anybody_can_run_servers)
|
||||
InfoRow(painterResource(R.drawable.privacy), R.string.privacy_redefined, R.string.first_platform_without_user_ids)
|
||||
InfoRow(painterResource(R.drawable.shield), R.string.immune_to_spam_and_abuse, R.string.people_can_connect_only_via_links_you_share)
|
||||
InfoRow(painterResource(R.drawable.decentralized), R.string.decentralized, R.string.opensource_protocol_and_code_anybody_can_run_servers)
|
||||
|
||||
Spacer(
|
||||
Modifier
|
||||
@@ -85,11 +86,11 @@ fun SimpleXLogo() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoRow(emoji: String, @StringRes titleId: Int, @StringRes textId: Int) {
|
||||
private fun InfoRow(icon: Painter, @StringRes titleId: Int, @StringRes textId: Int) {
|
||||
Row(Modifier.padding(bottom = 20.dp), verticalAlignment = Alignment.Top) {
|
||||
Text(emoji, fontSize = 36.sp, modifier = Modifier
|
||||
Image(icon, contentDescription = null, modifier = Modifier
|
||||
.width(60.dp)
|
||||
.padding(end = 16.dp))
|
||||
.padding(top = 8.dp, end = 16.dp))
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Text(stringResource(titleId), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h3, lineHeight = 24.sp)
|
||||
Text(stringResource(textId), lineHeight = 24.sp, style = MaterialTheme.typography.caption)
|
||||
|
||||
@@ -6,8 +6,6 @@ import SectionView
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -17,12 +15,17 @@ import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.views.helpers.ExposedDropDownSettingRow
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
|
||||
@Composable
|
||||
fun CallSettingsView(m: ChatModel) {
|
||||
fun CallSettingsView(m: ChatModel,
|
||||
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
|
||||
) {
|
||||
CallSettingsLayout(
|
||||
webrtcPolicyRelay = m.controller.appPrefs.webrtcPolicyRelay,
|
||||
callOnLockScreen = m.controller.appPrefs.callOnLockScreen
|
||||
callOnLockScreen = m.controller.appPrefs.callOnLockScreen,
|
||||
editIceServers = showModal { RTCServersView(m) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,6 +33,7 @@ fun CallSettingsView(m: ChatModel) {
|
||||
fun CallSettingsLayout(
|
||||
webrtcPolicyRelay: Preference<Boolean>,
|
||||
callOnLockScreen: Preference<CallOnLockScreen>,
|
||||
editIceServers: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
@@ -48,18 +52,33 @@ fun CallSettingsLayout(
|
||||
}
|
||||
SectionDivider()
|
||||
|
||||
Column(Modifier.padding(start = 10.dp, top = 12.dp)) {
|
||||
Text(stringResource(R.string.call_on_lock_screen))
|
||||
Row {
|
||||
SharedPreferenceRadioButton(stringResource(R.string.no_call_on_lock_screen), lockCallState, callOnLockScreen, CallOnLockScreen.DISABLE)
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
SharedPreferenceRadioButton(stringResource(R.string.show_call_on_lock_screen), lockCallState, callOnLockScreen, CallOnLockScreen.SHOW)
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
SharedPreferenceRadioButton(stringResource(R.string.accept_call_on_lock_screen), lockCallState, callOnLockScreen, CallOnLockScreen.ACCEPT)
|
||||
}
|
||||
val enabled = remember { mutableStateOf(true) }
|
||||
SectionItemView { LockscreenOpts(lockCallState, enabled, onSelected = { callOnLockScreen.set(it); lockCallState.value = it }) }
|
||||
SectionDivider()
|
||||
SectionItemView(editIceServers) { Text(stringResource(R.string.webrtc_ice_servers)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LockscreenOpts(lockscreenOpts: State<CallOnLockScreen>, enabled: State<Boolean>, onSelected: (CallOnLockScreen) -> Unit) {
|
||||
val values = remember {
|
||||
CallOnLockScreen.values().map {
|
||||
when (it) {
|
||||
CallOnLockScreen.DISABLE -> it to generalGetString(R.string.no_call_on_lock_screen)
|
||||
CallOnLockScreen.SHOW -> it to generalGetString(R.string.show_call_on_lock_screen)
|
||||
CallOnLockScreen.ACCEPT -> it to generalGetString(R.string.accept_call_on_lock_screen)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExposedDropDownSettingRow(
|
||||
generalGetString(R.string.call_on_lock_screen),
|
||||
values,
|
||||
lockscreenOpts,
|
||||
icon = null,
|
||||
enabled = enabled,
|
||||
onSelected = onSelected
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -115,7 +115,7 @@ fun NetworkAndServersView(
|
||||
Modifier.padding(start = 16.dp, bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
SectionView {
|
||||
SectionView(generalGetString(R.string.settings_section_title_messages)) {
|
||||
SettingsActionItem(Icons.Outlined.Dns, stringResource(R.string.smp_servers), showModal { SMPServersView(it) })
|
||||
SectionDivider()
|
||||
SectionItemView {
|
||||
@@ -128,6 +128,10 @@ fun NetworkAndServersView(
|
||||
SettingsActionItem(Icons.Outlined.Cable, stringResource(R.string.network_settings), showSettingsModal { AdvancedNetworkSettingsView(it) })
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
SectionView(generalGetString(R.string.settings_section_title_calls)) {
|
||||
SettingsActionItem(Icons.Outlined.ElectricalServices, stringResource(R.string.webrtc_ice_servers), showModal { RTCServersView(it) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +146,7 @@ fun UseSocksProxySwitch(
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package chat.simplex.app.views.usersettings
|
||||
|
||||
import SectionItemViewSpaceBetween
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.OpenInNew
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.views.call.parseRTCIceServers
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun RTCServersView(
|
||||
chatModel: ChatModel
|
||||
) {
|
||||
var userRTCServers by remember {
|
||||
mutableStateOf(chatModel.controller.appPrefs.webrtcIceServers.get()?.split("\n") ?: listOf())
|
||||
}
|
||||
var isUserRTCServers by remember { mutableStateOf(userRTCServers.isNotEmpty()) }
|
||||
var editRTCServers by remember { mutableStateOf(!isUserRTCServers) }
|
||||
val userRTCServersStr = remember { mutableStateOf(if (isUserRTCServers) userRTCServers.joinToString(separator = "\n") else "") }
|
||||
fun saveUserRTCServers() {
|
||||
val srvs = userRTCServersStr.value.split("\n")
|
||||
if (srvs.isNotEmpty() && srvs.toSet().size == srvs.size && parseRTCIceServers(srvs) != null) {
|
||||
userRTCServers = srvs
|
||||
chatModel.controller.appPrefs.webrtcIceServers.set(srvs.joinToString(separator = "\n"))
|
||||
editRTCServers = false
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.error_saving_ICE_servers),
|
||||
generalGetString(R.string.ensure_ICE_server_address_are_correct_format_and_unique)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun resetRTCServers() {
|
||||
isUserRTCServers = false
|
||||
userRTCServers = listOf()
|
||||
chatModel.controller.appPrefs.webrtcIceServers.set(null)
|
||||
}
|
||||
|
||||
RTCServersLayout(
|
||||
isUserRTCServers = isUserRTCServers,
|
||||
editRTCServers = editRTCServers,
|
||||
userRTCServersStr = userRTCServersStr,
|
||||
isUserRTCServersOnOff = { switch ->
|
||||
if (switch) {
|
||||
isUserRTCServers = true
|
||||
} else if (userRTCServers.isNotEmpty()) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(R.string.use_simplex_chat_servers__question),
|
||||
text = generalGetString(R.string.saved_ICE_servers_will_be_removed),
|
||||
confirmText = generalGetString(R.string.confirm_verb),
|
||||
onConfirm = {
|
||||
resetRTCServers()
|
||||
isUserRTCServers = false
|
||||
userRTCServersStr.value = ""
|
||||
}
|
||||
)
|
||||
} else {
|
||||
isUserRTCServers = false
|
||||
userRTCServersStr.value = ""
|
||||
}
|
||||
},
|
||||
cancelEdit = {
|
||||
isUserRTCServers = userRTCServers.isNotEmpty()
|
||||
editRTCServers = !isUserRTCServers
|
||||
userRTCServersStr.value = if (isUserRTCServers) userRTCServers.joinToString(separator = "\n") else ""
|
||||
},
|
||||
saveRTCServers = ::saveUserRTCServers,
|
||||
editOn = { editRTCServers = true },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RTCServersLayout(
|
||||
isUserRTCServers: Boolean,
|
||||
editRTCServers: Boolean,
|
||||
userRTCServersStr: MutableState<String>,
|
||||
isUserRTCServersOnOff: (Boolean) -> Unit,
|
||||
cancelEdit: () -> Unit,
|
||||
saveRTCServers: () -> Unit,
|
||||
editOn: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.your_ICE_servers),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
SectionItemViewSpaceBetween(padding = PaddingValues()) {
|
||||
Text(stringResource(R.string.configure_ICE_servers), Modifier.padding(end = 24.dp))
|
||||
Switch(
|
||||
checked = isUserRTCServers,
|
||||
onCheckedChange = isUserRTCServersOnOff,
|
||||
colors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colors.primary,
|
||||
uncheckedThumbColor = HighOrLowlight
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if (!isUserRTCServers) {
|
||||
Text(stringResource(R.string.using_simplex_chat_servers), lineHeight = 22.sp)
|
||||
} else {
|
||||
Text(stringResource(R.string.enter_one_ICE_server_per_line))
|
||||
if (editRTCServers) {
|
||||
TextEditor(Modifier.height(160.dp), text = userRTCServersStr)
|
||||
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Row {
|
||||
Text(
|
||||
stringResource(R.string.cancel_verb),
|
||||
color = MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = cancelEdit)
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 8.dp))
|
||||
Text(
|
||||
stringResource(R.string.save_servers_button),
|
||||
color = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
saveRTCServers()
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
howToButton()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.height(160.dp)
|
||||
.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
border = BorderStroke(1.dp, MaterialTheme.colors.secondary)
|
||||
) {
|
||||
SelectionContainer(
|
||||
Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Text(
|
||||
userRTCServersStr.value,
|
||||
Modifier
|
||||
.padding(vertical = 5.dp, horizontal = 7.dp),
|
||||
style = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 14.sp),
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Text(
|
||||
stringResource(R.string.edit_verb),
|
||||
color = MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = editOn)
|
||||
)
|
||||
}
|
||||
Column(horizontalAlignment = Alignment.End) {
|
||||
howToButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun howToButton() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable { uriHandler.openUri("https://github.com/simplex-chat/simplex-chat/blob/stable/docs/WEBRTC.md#configure-mobile-apps") }
|
||||
) {
|
||||
Text(stringResource(R.string.how_to), color = MaterialTheme.colors.primary)
|
||||
Icon(
|
||||
Icons.Outlined.OpenInNew, stringResource(R.string.how_to), tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(horizontal = 5.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.app.views.usersettings
|
||||
|
||||
import SectionItemViewSpaceBetween
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -58,7 +59,7 @@ fun SMPServersView(chatModel: ChatModel) {
|
||||
if (userSMPServers.isNotEmpty()) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(R.string.use_simplex_chat_servers__question),
|
||||
text = generalGetString(R.string.saved_SMP_servers_will_br_removed),
|
||||
text = generalGetString(R.string.saved_SMP_servers_will_be_removed),
|
||||
confirmText = generalGetString(R.string.confirm_verb),
|
||||
onConfirm = {
|
||||
saveSMPServers(listOf())
|
||||
@@ -107,9 +108,7 @@ fun SMPServersLayout(
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
SectionItemViewSpaceBetween(padding = PaddingValues()) {
|
||||
Text(stringResource(R.string.configure_SMP_servers), Modifier.padding(end = 24.dp))
|
||||
Switch(
|
||||
checked = isUserSMPServers,
|
||||
@@ -198,7 +197,7 @@ fun SMPServersLayout(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun howToButton() {
|
||||
private fun howToButton() {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
||||
@@ -31,6 +31,8 @@ import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.TerminalView
|
||||
import chat.simplex.app.views.database.DatabaseView
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.CreateLinkTab
|
||||
import chat.simplex.app.views.newchat.CreateLinkView
|
||||
import chat.simplex.app.views.onboarding.SimpleXInfo
|
||||
|
||||
@Composable
|
||||
@@ -113,16 +115,16 @@ fun SettingsLayout(
|
||||
SectionDivider()
|
||||
SettingsIncognitoActionItem(incognitoPref, incognito, stopped) { onClickIncognitoInfo(showModal) }
|
||||
SectionDivider()
|
||||
SettingsActionItem(Icons.Outlined.QrCode, stringResource(R.string.your_simplex_contact_address), showModal { UserAddressView(it) }, disabled = stopped)
|
||||
SettingsActionItem(Icons.Outlined.QrCode, stringResource(R.string.your_simplex_contact_address), showModal { CreateLinkView(it, CreateLinkTab.LONG_TERM) }, disabled = stopped)
|
||||
SectionDivider()
|
||||
DatabaseItem(encrypted, showSettingsModal { DatabaseView(it, showSettingsModal) }, stopped)
|
||||
}
|
||||
SectionSpacer()
|
||||
|
||||
SectionView(stringResource(R.string.settings_section_title_settings)) {
|
||||
SettingsActionItem(Icons.Outlined.Bolt, stringResource(R.string.notifications), showSettingsModal { NotificationsSettingsView(it, showCustomModal) })
|
||||
SettingsActionItem(Icons.Outlined.Bolt, stringResource(R.string.notifications), showSettingsModal { NotificationsSettingsView(it, showCustomModal) }, disabled = stopped)
|
||||
SectionDivider()
|
||||
SettingsActionItem(Icons.Outlined.Videocam, stringResource(R.string.settings_audio_video_calls), showSettingsModal { CallSettingsView(it) }, disabled = stopped)
|
||||
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)
|
||||
SectionDivider()
|
||||
|
||||
@@ -61,7 +61,7 @@ fun UserAddressLayout(
|
||||
verticalArrangement = Arrangement.Top
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.your_chat_address),
|
||||
stringResource(R.string.your_contact_address),
|
||||
Modifier.padding(bottom = 16.dp),
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
@@ -77,12 +77,17 @@ fun UserAddressLayout(
|
||||
) {
|
||||
if (userAddress == null) {
|
||||
Text(
|
||||
stringResource(R.string.if_you_delete_address_you_wont_lose_contacts),
|
||||
stringResource(R.string.if_you_later_delete_address_you_wont_lose_contacts),
|
||||
Modifier.padding(bottom = 12.dp),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
SimpleButton(stringResource(R.string.create_address), icon = Icons.Outlined.QrCode, click = createAddress)
|
||||
} else {
|
||||
Text(
|
||||
stringResource(R.string.if_you_delete_address_you_wont_lose_contacts),
|
||||
Modifier.padding(bottom = 12.dp),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
QRCode(userAddress, Modifier.weight(1f, fill = false).aspectRatio(1f))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
|
||||
BIN
apps/android/app/src/main/res/drawable-hdpi/decentralized.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
apps/android/app/src/main/res/drawable-hdpi/privacy.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/android/app/src/main/res/drawable-hdpi/shield.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
apps/android/app/src/main/res/drawable-mdpi/decentralized.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
apps/android/app/src/main/res/drawable-mdpi/privacy.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
apps/android/app/src/main/res/drawable-mdpi/shield.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
apps/android/app/src/main/res/drawable-xhdpi/decentralized.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
apps/android/app/src/main/res/drawable-xhdpi/privacy.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
apps/android/app/src/main/res/drawable-xhdpi/shield.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
apps/android/app/src/main/res/drawable-xxhdpi/decentralized.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
apps/android/app/src/main/res/drawable-xxhdpi/privacy.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
apps/android/app/src/main/res/drawable-xxhdpi/shield.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
apps/android/app/src/main/res/drawable-xxxhdpi/decentralized.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
apps/android/app/src/main/res/drawable-xxxhdpi/privacy.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
apps/android/app/src/main/res/drawable-xxxhdpi/shield.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
808
apps/android/app/src/main/res/values-de/strings.xml
Normal file
@@ -0,0 +1,808 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_name"><xliff:g id="appName">SimpleX</xliff:g></string>
|
||||
|
||||
<string name="thousand_abbreviation">k</string>
|
||||
|
||||
<!-- Connect via Link - MainActivity.kt -->
|
||||
<string name="connect_via_contact_link">Über den Kontakt-Link verbinden?</string>
|
||||
<string name="connect_via_invitation_link">Über den Einladungs-Link verbinden?</string>
|
||||
<string name="profile_will_be_sent_to_contact_sending_link">Ihr Profil wird an den Kontakt gesendet von dem Sie diesen Link erhalten haben.</string>
|
||||
<string name="connect_via_link_verb">Verbinden</string>
|
||||
|
||||
<!-- Server info - ChatModel.kt -->
|
||||
<string name="server_connected">Verbunden</string>
|
||||
<string name="server_error">Fehler</string>
|
||||
<string name="server_connecting">verbinde</string>
|
||||
<string name="connected_to_server_to_receive_messages_from_contact">Sie sind mit dem Server verbunden, der für den Empfang von Nachrichten mit diesem Kontakt genutzt wird.</string>
|
||||
<string name="trying_to_connect_to_server_to_receive_messages_with_error">Beim Versuch die Verbindung mit dem Server aufzunehmen, der für den Empfang von Nachrichten mit diesem Kontakt genutzt wird, ist ein Fehler aufgetreten (Fehler: <xliff:g id="errorMsg">%1$s</xliff:g>).</string>
|
||||
<string name="trying_to_connect_to_server_to_receive_messages">Versuche die Verbindung mit dem Server aufzunehmen, der für den Empfang von Nachrichten mit diesem Kontakt genutzt wird.</string>
|
||||
|
||||
<!-- Item Content - ChatModel.kt -->
|
||||
<string name="deleted_description">Gelöscht</string>
|
||||
<string name="sending_files_not_yet_supported">Das Senden von Dateien wird noch nicht unterstützt</string>
|
||||
<string name="receiving_files_not_yet_supported">Der Empfang von Dateien wird noch nicht unterstützt</string>
|
||||
<string name="sender_you_pronoun">Sie</string>
|
||||
<string name="unknown_message_format">Unbekanntes Nachrichtenformat</string>
|
||||
<string name="invalid_message_format">Unzulässiges Nachrichtenformat</string>
|
||||
|
||||
<!-- PendingContactConnection - ChatModel.kt -->
|
||||
<string name="connection_local_display_name">Verbindung <xliff:g id="connection ID" example="1">%1$d</xliff:g></string>
|
||||
<string name="display_name_connection_established">Verbindung hergestellt</string>
|
||||
<string name="display_name_invited_to_connect">Für eine Verbindung eingeladen</string>
|
||||
<string name="display_name_connecting">verbinde …</string>
|
||||
<string name="description_you_shared_one_time_link">Sie haben einen Einmal-Link geteilt</string>
|
||||
<string name="description_you_shared_one_time_link_incognito">Sie haben Inkognito einen Einmal-Link geteilt</string>
|
||||
<string name="description_via_contact_address_link">über einen Kontaktadressen Link</string>
|
||||
<string name="description_via_contact_address_link_incognito">Inkognito über einen Kontaktadressen Link</string>
|
||||
<string name="description_via_one_time_link">über einen Einmal-Link</string>
|
||||
<string name="description_via_one_time_link_incognito">Inkognito über einen Einmal-Link</string>
|
||||
|
||||
<!-- SimpleXAPI.kt -->
|
||||
<string name="error_saving_smp_servers">Fehler beim Speichern des SMP-Server</string>
|
||||
<string name="ensure_smp_server_address_are_correct_format_and_unique">Stellen Sie sicher, dass die SMP-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind.</string>
|
||||
<string name="error_setting_network_config">Fehler bei der Aktualisierung der Netzwerk-Konfiguration.</string>
|
||||
|
||||
<!-- API Error Responses - SimpleXAPI.kt -->
|
||||
<string name="connection_timeout">Verbindungszeitüberschreitung</string>
|
||||
<string name="connection_error">Verbindungsfehler</string>
|
||||
<string name="network_error_desc">Bitte überprüfen Sie Ihre Netzwerkverbindung und versuchen Sie es erneut.</string>
|
||||
<string name="error_sending_message">Fehler beim Senden der Nachricht</string>
|
||||
<string name="error_adding_members">Fehler beim Hinzufügen von Mitgliedern</string>
|
||||
<string name="error_joining_group">Fehler beim Beitritt zur Gruppe</string>
|
||||
<string name="cannot_receive_file">Datei kann nicht empfangen werden</string>
|
||||
<string name="sender_cancelled_file_transfer">Der Absender hat die Dateiübertragung abgebrochen.</string>
|
||||
<string name="error_receiving_file">Fehler beim Empfangen der Datei</string>
|
||||
<string name="error_creating_address">Fehler beim Erstellen der Adresse</string>
|
||||
<string name="contact_already_exists">Kontakt ist bereits vorhanden</string>
|
||||
<string name="you_are_already_connected_to_vName_via_this_link">Sie sind bereits über diesen Link mit <xliff:g id="contactName" example="Alice">%1$s!</xliff:g> verbunden.</string>
|
||||
<string name="invalid_connection_link">Ungültiger Verbindungslink</string>
|
||||
<string name="please_check_correct_link_and_maybe_ask_for_a_new_one">Überprüfen Sie bitte, ob Sie den richtigen Link genutzt haben oder bitten Sie Ihren Kontakt darum, Ihnen nochmal einen Link zuzusenden.</string>
|
||||
<string name="connection_error_auth">Verbindungsfehler (AUTH)</string>
|
||||
<string name="connection_error_auth_desc">Entweder hat Ihr Kontakt die Verbindung gelöscht, oder dieser Link wurde bereits verwendet, es könnte sich um einen Fehler handeln - Bitte melden Sie es uns.\nBitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um sich neu verbinden zu können und stellen Sie sicher, dass Sie eine stabile Netzwerk-Verbindung haben.</string>
|
||||
<string name="error_accepting_contact_request">Fehler beim Akzeptieren der Kontaktanfrage</string>
|
||||
<string name="sender_may_have_deleted_the_connection_request">Der Absender hat möglicherweise die Verbindungsanfrage gelöscht.</string>
|
||||
<string name="cannot_delete_contact">Der Kontakt kann nicht gelöscht werden!</string>
|
||||
<string name="contact_cannot_be_deleted_as_they_are_in_groups">Der Kontakt mit <xliff:g id="contactName" example="Jane Doe">%1$s!</xliff:g> kann nicht gelöscht werden, da er Mitglied einer oder mehrerer dieser Gruppen ist <xliff:g id="groups" example="[team, chess club]">%2$s</xliff:g>.</string>
|
||||
<string name="error_deleting_contact">Fehler beim Löschen des Kontakts</string>
|
||||
<string name="error_deleting_group">Fehler beim Löschen der Gruppe</string>
|
||||
<string name="error_deleting_contact_request">Fehler beim Löschen der Kontakt-Anfrage</string>
|
||||
<string name="error_deleting_pending_contact_connection">Fehler beim Löschen der anstehenden Kontaktaufnahme</string>
|
||||
|
||||
<!-- background service notice - SimpleXAPI.kt -->
|
||||
<string name="icon_descr_instant_notifications">Sofortige Benachrichtigungen</string>
|
||||
<string name="service_notifications">Sofortige Benachrichtigungen!</string>
|
||||
<string name="service_notifications_disabled">Sofortige Benachrichtigungen sind deaktiviert!</string>
|
||||
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">Um Ihre Privatsphäre zu schützen kann statt der Push-Benachrichtigung der <b><xliff:g id="appName">SimpleX</xliff:g> Hintergrunddienst genutzt werden</b> – dieser benötigt ein paar Prozent Akkuleistung am Tag.</string>
|
||||
<string name="it_can_disabled_via_settings_notifications_still_shown"><b>Diese können über die Einstellungen deaktiviert werden</b> – Solange die App abläuft werden Benachrichtigungen weiterhin angezeigt.</string>
|
||||
<string name="turn_off_battery_optimization">Um diese Funktion zu nutzen, ist es nötig, die Einstellung <b>Akkuoptimierung</b> für <xliff:g id="appName">SimpleX</xliff:g> im nächsten Dialog zu <b>deaktivieren</b>. Ansonsten werden die Benachrichtigungen deaktiviert.</string>
|
||||
<string name="turning_off_service_and_periodic">Die Akkuoptimierung ist aktiv, der Hintergrunddienst und die regelmäßige Nachfrage nach neuen Nachrichten ist abgeschaltet. Sie können diese Funktion in den Einstellungen wieder aktivieren.</string>
|
||||
<string name="periodic_notifications">Regelmäßige Benachrichtigungen</string>
|
||||
<string name="periodic_notifications_disabled">Regelmäßige Benachrichtigungen sind deaktiviert!</string>
|
||||
<string name="periodic_notifications_desc">Die App holt regelmäßig neue Nachrichten ab — dies benötigt ein paar Prozent Akkuleistung am Tag. Die App nutzt keine Push-Benachrichtigungen — es werden keine Daten von Ihrem Gerät an Server gesendet.</string>
|
||||
<string name="enter_passphrase_notification_title">Passwort wird benötigt</string>
|
||||
<string name="enter_passphrase_notification_desc">Geben Sie bitte das Datenbank-Passwort ein, um Benachrichtigungen zu erhalten.</string>
|
||||
<string name="database_initialization_error_title">Die Datenbank kann nicht initialisiert werden</string>
|
||||
<string name="database_initialization_error_desc">Die Datenbank arbeitet nicht richtig. Tippen Sie für weitere Informationen.</string>
|
||||
|
||||
<!-- SimpleX Chat foreground Service -->
|
||||
<string name="simplex_service_notification_title"><xliff:g id="appNameFull">SimpleX Chat</xliff:g> Hintergrunddienst</string>
|
||||
<string name="simplex_service_notification_text">Empfange Nachrichten …</string>
|
||||
<string name="hide_notification">Verberge</string>
|
||||
|
||||
<!-- Notification channels -->
|
||||
<string name="ntf_channel_messages">SimpleX Chat Nachrichten</string>
|
||||
<string name="ntf_channel_calls">SimpleX Chat Anrufe</string>
|
||||
<string name="ntf_channel_calls_lockscreen">SimpleX Chat Anrufe (Sperrbildschirm)</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="settings_notifications_mode_title">Benachrichtigungsdienst</string>
|
||||
<string name="settings_notification_preview_mode_title">Vorschau anzeigen</string>
|
||||
<string name="settings_notification_preview_title">Benachrichtigungsvorschau</string>
|
||||
<string name="notifications_mode_off">Wird ausgeführt, wenn die App geöffnet ist</string>
|
||||
<string name="notifications_mode_periodic">Startet regelmäßig</string>
|
||||
<string name="notifications_mode_service">Immer aktiv</string>
|
||||
<string name="notifications_mode_off_desc">Die App kann Benachrichtigungen nur empfangen, wenn sie ausgeführt wird, es wird kein Hintergrunddienst gestartet.</string>
|
||||
<string name="notifications_mode_periodic_desc">Überprüft alle 10 Minuten auf neue Nachrichten für bis zu einer Minute.</string>
|
||||
<string name="notifications_mode_service_desc">Der Hintergrunddienst läuft immer – Benachrichtigungen werden angezeigt, sobald Nachrichten verfügbar sind.</string>
|
||||
<string name="notification_preview_mode_message">Nachrichtentext</string>
|
||||
<string name="notification_preview_mode_contact">Kontaktname</string>
|
||||
<string name="notification_preview_mode_hidden">Verborgen</string>
|
||||
<string name="notification_preview_mode_message_desc">Kontakt und Nachricht anzeigen</string>
|
||||
<string name="notification_preview_mode_contact_desc">Nur Kontakt anzeigen</string>
|
||||
<string name="notification_display_mode_hidden_desc">Kontakt und Nachricht verbergen</string>
|
||||
<string name="notification_preview_somebody">Kontakt verbergen:</string>
|
||||
<string name="notification_preview_new_message">neue Nachricht</string>
|
||||
|
||||
<!-- local authentication notice - SimpleXAPI.kt -->
|
||||
<string name="la_notice_title_simplex_lock">SimpleX Sperre</string>
|
||||
<string name="la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled">Um Ihre Informationen zu schützen, schalten Sie die SimpleX Sperre ein.\nSie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funktion aktiviert wird.</string>
|
||||
<string name="la_notice_turn_on">Aktivieren</string>
|
||||
|
||||
<!-- LocalAuthentication.kt -->
|
||||
<string name="auth_simplex_lock_turned_on">SimpleX Sperre aktiviert</string>
|
||||
<string name="auth_you_will_be_required_to_authenticate_when_you_start_or_resume">Sie müssen sich authentifizieren, wenn Sie die im Hintergrund befindliche App nach 30 Sekunden starten oder fortsetzen.</string>
|
||||
<string name="auth_unlock">Entsperren</string>
|
||||
<string name="auth_log_in_using_credential">Melden Sie sich mit Ihren Zugangsdaten an</string>
|
||||
<string name="auth_enable_simplex_lock">SimpleX Sperre aktivieren</string>
|
||||
<string name="auth_disable_simplex_lock">SimpleX Sperre deaktivieren</string>
|
||||
<string name="auth_confirm_credential">Bestätigen Sie Ihre Zugangsdaten</string>
|
||||
<string name="auth_error">Authentifizierungsfehler</string>
|
||||
<string name="auth_error_w_desc">Authentifizierungsfehler: <xliff:g id="desc">%1$s</xliff:g></string>
|
||||
<string name="auth_failed">Authentifizierung fehlgeschlagen</string>
|
||||
<string name="auth_unavailable">Authentifizierung nicht verfügbar</string>
|
||||
<string name="auth_device_authentication_is_not_enabled_you_can_turn_on_in_settings_once_enabled">Geräteauthentifizierung ist nicht aktiviert. Sie können die SimpleX Sperre über die Einstellungen aktivieren, sobald Sie die Geräteauthentifizierung aktiviert haben.</string>
|
||||
<string name="auth_device_authentication_is_disabled_turning_off">Geräteauthentifizierung ist deaktiviert. SimpleX Sperre ist abgeschaltet.</string>
|
||||
<string name="auth_retry">Wiederholen</string>
|
||||
<string name="auth_stop_chat">Chat beenden</string>
|
||||
<string name="auth_open_chat_console">Chat-Konsole öffnen</string>
|
||||
|
||||
<!-- Chat Actions - ChatItemView.kt (and general) -->
|
||||
<string name="reply_verb">Antwort</string>
|
||||
<string name="share_verb">Teilen</string>
|
||||
<string name="copy_verb">Kopieren</string>
|
||||
<string name="save_verb">Speichern</string>
|
||||
<string name="edit_verb">Bearbeiten</string>
|
||||
<string name="delete_verb">Löschen</string>
|
||||
<string name="delete_message__question">Nachricht löschen?</string>
|
||||
<string name="delete_message_cannot_be_undone_warning">Nachricht wird gelöscht - dies kann nicht rückgängig gemacht werden!</string>
|
||||
<string name="for_me_only">Nur für mich</string>
|
||||
<string name="for_everybody">Für alle</string>
|
||||
|
||||
<!-- CIMetaView.kt -->
|
||||
<string name="icon_descr_edited">bearbeitet</string>
|
||||
<string name="icon_descr_sent_msg_status_sent">gesendet</string>
|
||||
<string name="icon_descr_sent_msg_status_unauthorized_send">nicht autorisiertes Senden</string>
|
||||
<string name="icon_descr_sent_msg_status_send_failed">Senden fehlgeschlagen</string>
|
||||
<string name="icon_descr_received_msg_status_unread">ungelesen</string>
|
||||
|
||||
<!-- ChatListView.kt -->
|
||||
<string name="personal_welcome">Willkommen <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Willkommen!</string>
|
||||
<string name="this_text_is_available_in_settings">Dieser Text ist in den Einstellungen verfügbar.</string>
|
||||
<string name="your_chats">Ihre Chats</string>
|
||||
<string name="contact_connection_pending">verbinde …</string>
|
||||
<string name="group_preview_you_are_invited">Sie sind zu der Gruppe eingeladen</string>
|
||||
<string name="group_preview_join_as">beitreten als %s</string>
|
||||
<string name="group_connection_pending">verbinde …</string>
|
||||
<string name="tap_to_start_new_chat">Tippen Sie um einen neuen Chat zu starten</string>
|
||||
<string name="chat_with_developers">Chatten Sie mit den Entwicklern</string>
|
||||
<string name="you_have_no_chats">Sie haben keine Chats</string>
|
||||
|
||||
<!-- ComposeView.kt, helpers -->
|
||||
<string name="attach">Anhängen</string>
|
||||
<string name="icon_descr_context">Kontextsymbol</string>
|
||||
<string name="icon_descr_cancel_image_preview">Bildervorschau abbrechen</string>
|
||||
<string name="icon_descr_cancel_file_preview">Dateivorschau abbrechen</string>
|
||||
|
||||
<!-- Images - CIImageView.kt -->
|
||||
<string name="image_descr">Bild</string>
|
||||
<string name="icon_descr_waiting_for_image">Warten auf ein Bild</string>
|
||||
<string name="icon_descr_asked_to_receive">Um Empfang des Bildes gebeten</string>
|
||||
<string name="icon_descr_image_snd_complete">Bild gesendet</string>
|
||||
<string name="waiting_for_image">Warten auf ein Bild</string>
|
||||
<string name="image_will_be_received_when_contact_is_online">Das Bild wird empfangen, wenn Ihr Kontakt online ist, bitte warten oder schauen Sie später nochmal nach!</string>
|
||||
<string name="image_saved">Bild wurde im Fotoalbum gespeichert</string>
|
||||
|
||||
<!-- Files - CIFileView.kt -->
|
||||
<string name="icon_descr_file">Datei</string>
|
||||
<string name="large_file">Große Datei!</string>
|
||||
<string name="contact_sent_large_file">Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (<xliff:g id="maxFileSize">%1$s</xliff:g>).</string>
|
||||
<string name="maximum_supported_file_size">Die derzeit maximal unterstützte Dateigröße beträgt <xliff:g id="maxFileSize">%1$s</xliff:g>.</string>
|
||||
<string name="waiting_for_file">Warte auf Datei</string>
|
||||
<string name="file_will_be_received_when_contact_is_online">Die Datei wird empfangen, wenn Ihr Kontakt online ist, bitte warten oder schauen Sie später nochmal nach!</string>
|
||||
<string name="file_saved">Datei gespeichert</string>
|
||||
<string name="file_not_found">Datei nicht gefunden</string>
|
||||
<string name="error_saving_file">Fehler beim Speichern der Datei</string>
|
||||
|
||||
<!-- Chat Info Settings - ChatInfoView.kt -->
|
||||
<string name="notifications">Benachrichtigungen</string>
|
||||
|
||||
<!-- Chat Info Actions - ChatInfoView.kt -->
|
||||
<string name="delete_contact_question">Kontakt löschen?</string>
|
||||
<string name="delete_contact_all_messages_deleted_cannot_undo_warning">Der Kontakt und alle Nachrichten werden gelöscht - dies kann nicht rückgängig gemacht werden!</string>
|
||||
<string name="button_delete_contact">Kontakt löschen</string>
|
||||
<string name="text_field_set_contact_placeholder">Kontaktname festlegen…</string>
|
||||
<string name="icon_descr_server_status_connected">Verbunden</string>
|
||||
<string name="icon_descr_server_status_disconnected">Getrennt</string>
|
||||
<string name="icon_descr_server_status_error">Fehler</string>
|
||||
<string name="icon_descr_server_status_pending">Ausstehend</string>
|
||||
|
||||
<!-- Message Actions - SendMsgView.kt -->
|
||||
<string name="icon_descr_send_message">Nachricht senden</string>
|
||||
|
||||
<!-- General Actions / Responses -->
|
||||
<string name="back">Zurück</string>
|
||||
<string name="cancel_verb">Abbrechen</string>
|
||||
<string name="confirm_verb">Bestätigen</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="no_details">keine Details</string>
|
||||
<string name="add_contact">Kontakt hinzufügen</string>
|
||||
<string name="copied">In die Zwischenablage kopiert</string>
|
||||
|
||||
<!-- NewChatSheet -->
|
||||
<string name="add_contact_or_create_group">Neuen Chat starten</string>
|
||||
<string name="share_one_time_link">Erstellen Sie einen einmaligen Einladungslink</string>
|
||||
<string name="connect_via_link_or_qr">Per Link / QR-Code verbinden</string>
|
||||
<string name="scan_QR_code">QR-Code scannen</string>
|
||||
<string name="create_group">Geheime Gruppe erstellen</string>
|
||||
<string name="to_share_with_your_contact">(zum Teilen mit Ihrem Kontakt)</string>
|
||||
<string name="connect_via_link_or_qr_from_clipboard_or_in_person">(Scannen oder Einfügen aus der Zwischenablage)</string>
|
||||
<string name="only_stored_on_members_devices">(wird nur von Gruppenmitgliedern gespeichert)</string>
|
||||
|
||||
<!-- GetImageView -->
|
||||
<string name="toast_permission_denied">Berechtigung verweigert!</string>
|
||||
<string name="use_camera_button">Kamera\nverwenden</string>
|
||||
<string name="from_gallery_button">Aus dem\nFotoalbum</string>
|
||||
<string name="choose_file">Datei\nauswählen</string>
|
||||
|
||||
<!-- help - ChatHelpView.kt -->
|
||||
<string name="thank_you_for_installing_simplex">Danke, dass Sie <xliff:g id="appNameFull">SimpleX Chat</xliff:g> installiert haben!</string>
|
||||
<string name="you_can_connect_to_simplex_chat_founder">Sie können sich <font color="#0088ff">mit <xliff:g id="appNameFull">SimpleX Chat</xliff:g> Entwicklern verbinden, um Fragen zu stellen und Updates zu erhalten</font>.</string>
|
||||
<string name="to_start_a_new_chat_help_header">Um einen neuen Chat zu starten</string>
|
||||
<string name="chat_help_tap_button">Schaltfläche antippen</string>
|
||||
<string name="above_then_preposition_continuation">über, dann:</string>
|
||||
<string name="add_new_contact_to_create_one_time_QR_code"><b>Neuen Kontakt hinzufügen</b>: Um Ihren Einmal-QR-Code für Ihren Kontakt zu erstellen.</string>
|
||||
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><b>QR-Code scannen</b>: Um sich mit Ihrem Kontakt zu verbinden, der Ihnen seinen QR-Code zeigt.</string>
|
||||
<string name="to_connect_via_link_title">Über Link verbinden</string>
|
||||
<string name="if_you_received_simplex_invitation_link_you_can_open_in_browser">Wenn Sie einen <xliff:g id="appName">SimpleX Chat</xliff:g> Einladungslink erhalten haben, können Sie ihn in Ihrem Browser öffnen:</string>
|
||||
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code">💻 Desktop: Angezeigten QR-Code aus der App scannen, über <b>QR-Code scannen</b>.</string>
|
||||
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app">📱 Handy: Tippen Sie auf <b>In mobiler App öffnen</b> und dann auf <b>Verbinden</b> in der App.</string>
|
||||
|
||||
<!-- Contact Request Alert Dialogue - ChatListNavLinkView.kt -->
|
||||
<string name="accept_connection_request__question">Die Verbindungsanfrage akzeptieren?</string>
|
||||
<string name="if_you_choose_to_reject_the_sender_will_not_be_notified">Wenn Sie ablehnen, wird der Absender NICHT benachrichtigt.</string>
|
||||
<string name="accept_contact_button">Akzeptieren</string>
|
||||
<string name="accept_contact_incognito_button">Inkognito akzeptieren</string>
|
||||
<string name="reject_contact_button">Ablehnen</string>
|
||||
|
||||
<!-- Clear Chat - ChatListNavLinkView.kt -->
|
||||
<string name="clear_chat_question">Chatinhalte löschen?</string>
|
||||
<string name="clear_chat_warning">Alle Nachrichten werden gelöscht - dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht.</string>
|
||||
<string name="clear_verb">Löschen</string>
|
||||
<string name="clear_chat_button">Chatinhalte löschen</string>
|
||||
<string name="mark_read">Als gelesen markieren</string>
|
||||
|
||||
<!-- Actions - ChatListNavLinkView.kt -->
|
||||
<string name="mute_chat">Stummschalten</string>
|
||||
<string name="unmute_chat">Stummschaltung aufheben</string>
|
||||
|
||||
<!-- Pending contact connection alert dialogues -->
|
||||
<string name="you_invited_your_contact">Sie haben Ihren Kontakt eingeladen</string>
|
||||
<string name="you_accepted_connection">Sie haben die Verbindung akzeptiert</string>
|
||||
<string name="delete_pending_connection__question">Ausstehende Verbindung löschen?</string>
|
||||
<string name="contact_you_shared_link_with_wont_be_able_to_connect">Der Kontakt, mit dem Sie diesen Link geteilt haben, kann sich NICHT verbinden!</string>
|
||||
<string name="connection_you_accepted_will_be_cancelled">Die von Ihnen akzeptierte Verbindung wird abgebrochen!</string>
|
||||
|
||||
<!-- Contact Pending Alert Dialogue - ChatListNavLinkView.kt -->
|
||||
<string name="alert_title_contact_connection_pending">Ihr Kontakt ist noch nicht verbunden!</string>
|
||||
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">Ihr Kontakt muss online sein, damit die Verbindung hergestellt werden kann.\nSie können diese Verbindung abbrechen und den Kontakt entfernen (und es später nochmals mit einem neuen Link versuchen).</string>
|
||||
|
||||
<!-- Contact Request Information - ContactRequestView.kt -->
|
||||
<string name="contact_wants_to_connect_with_you">möchte sich mit Ihnen verbinden!</string>
|
||||
|
||||
<!-- Image Placeholder - ChatInfoImage.kt -->
|
||||
<string name="icon_descr_profile_image_placeholder">Platzhalter für Profilbild</string>
|
||||
<string name="image_descr_profile_image">Profilbild</string>
|
||||
|
||||
<!-- Content Descriptions -->
|
||||
<string name="icon_descr_close_button">Schließen Schaltfläche</string>
|
||||
<string name="image_descr_link_preview">Vorschaubild verlinken</string>
|
||||
<string name="icon_descr_cancel_link_preview">Link Vorschau abbrechen</string>
|
||||
<string name="icon_descr_settings">Einstellungen</string>
|
||||
<string name="image_descr_qr_code">QR Code</string>
|
||||
<string name="icon_descr_address"><xliff:g id="appName">SimpleX</xliff:g> Adresse</string>
|
||||
<string name="icon_descr_help">Hilfe</string>
|
||||
<string name="icon_descr_simplex_team"><xliff:g id="appName">SimpleX</xliff:g> Team</string>
|
||||
<string name="image_descr_simplex_logo"><xliff:g id="appName">SimpleX</xliff:g> Logo</string>
|
||||
<string name="icon_descr_email">E-Mail</string>
|
||||
<string name="icon_descr_more_button">Mehr</string>
|
||||
|
||||
<!-- Add Contact - AddContactView.kt -->
|
||||
<string name="invalid_QR_code">Ungültiger QR-Code</string>
|
||||
<string name="this_QR_code_is_not_a_link">Dieser QR-Code beschreibt keinen Link!</string>
|
||||
<string name="invalid_contact_link">Ungültiger Link!</string>
|
||||
<string name="this_link_is_not_a_valid_connection_link">Dieser Link ist kein gültiger Verbindungslink!</string>
|
||||
<string name="connection_request_sent">Verbindungsanfrage gesendet!</string>
|
||||
<string name="you_will_be_connected_when_your_connection_request_is_accepted">Sie werden verbunden, sobald Ihre Verbindungsanfrage akzeptiert wird, bitte warten oder schauen Sie später nochmal nach!</string>
|
||||
<string name="you_will_be_connected_when_your_contacts_device_is_online">Sie werden verbunden, wenn das Gerät Ihres Kontakts online ist, bitte warten oder schauen Sie später nochmal nach!</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Zeigen Sie Ihrem Kontakt den QR-Code aus der App zum Scannen.</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">Wenn Sie sich nicht persönlich treffen können, können Sie <b>den QR-Code während eines Videoanrufs anzeigen</b> oder einen Einladungslink über einen anderen Kanal mit Ihrem Kontakt teilen.</string >
|
||||
<string name="your_chat_profile_will_be_sent_to_your_contact">Ihr Chat-Profil wird\nan Ihren Kontakt gesendet.</string>
|
||||
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">Wenn Sie sich nicht persönlich treffen können, können Sie <b>den QR-Code während eines Videoanrufs scannen</b> oder Ihr Kontakt kann einen Einladungslink über einen anderen Kanal mit Ihnen teilen.</string>
|
||||
<string name="share_invitation_link">Einladungslink teilen</string>
|
||||
<string name="paste_connection_link_below_to_connect">Fügen Sie den erhaltenen Link in das Feld unten ein, um sich mit Ihrem Kontakt zu verbinden.</string>
|
||||
<string name="your_profile_will_be_sent">Ihr Chat-Profil wird an Ihren Kontakt gesendet.</string>
|
||||
|
||||
<!-- PasteToConnect.kt -->
|
||||
<string name="connect_via_link">Über einen Link verbinden</string>
|
||||
<string name="connect_button">Verbinden</string>
|
||||
<string name="paste_button">Einfügen</string>
|
||||
<string name="this_string_is_not_a_connection_link">Diese Zeichenfolge entspricht keinem gültigen Verbindungslink!</string>
|
||||
<string name="you_can_also_connect_by_clicking_the_link">Sie können sich auch verbinden, indem Sie auf den Link klicken. Wenn er im Browser geöffnet wird, klicken Sie auf die Schaltfläche <b>In mobiler App öffnen</b>.</string>
|
||||
|
||||
<!-- CreateLinkView.kt -->
|
||||
<string name="create_one_time_link">Link / QR-Code erstellen</string>
|
||||
<string name="one_time_link">Einmaliger Einladungs-Link</string>
|
||||
<string name="your_contact_address">Ihre Kontaktadresse</string>
|
||||
|
||||
<!-- settings - SettingsView.kt -->
|
||||
<string name="your_settings">Ihre Einstellungen</string>
|
||||
<string name="your_simplex_contact_address">Ihre <xliff:g id="appName">SimpleX</xliff:g> Kontaktadresse</string>
|
||||
<string name="database_passphrase_and_export">Datenbank-Passwort & -Export</string>
|
||||
<string name="about_simplex_chat">Über <xliff:g id="appNameFull">SimpleX Chat</xliff:g></string>
|
||||
<string name="how_to_use_simplex_chat">Wie man SimpleX nutzt</string>
|
||||
<string name="markdown_help">Markdown Hilfe</string>
|
||||
<string name="markdown_in_messages">Markdown in Nachrichten</string>
|
||||
<string name="chat_with_the_founder">Verbinden Sie sich mit den Entwicklern</string>
|
||||
<string name="send_us_an_email">Senden Sie uns eine E-Mail</string>
|
||||
<string name="chat_lock">SimpleX Sperre</string>
|
||||
<string name="chat_console">Chat Konsole</string>
|
||||
<string name="smp_servers">SMP-Server</string>
|
||||
<string name="install_simplex_chat_for_terminal">Installieren Sie <xliff:g id="appNameFull">SimpleX Chat</xliff:g> als Terminalanwendung</string>
|
||||
<string name="use_simplex_chat_servers__question">Verwenden Sie <xliff:g id="appNameFull">SimpleX Chat</xliff:g> Server?</string>
|
||||
<string name="saved_SMP_servers_will_be_removed">Gespeicherte SMP-Server werden entfernt.</string>
|
||||
<string name="your_SMP_servers">Ihre SMP-Server</string>
|
||||
<string name="configure_SMP_servers">SMP-Server konfigurieren</string>
|
||||
<string name="using_simplex_chat_servers">Verwendung von <xliff:g id="appNameFull">SimpleX Chat</xliff:g> Servern.</string>
|
||||
<string name="enter_one_SMP_server_per_line">SMP-Server (einer pro Zeile)</string>
|
||||
<string name="how_to">Anleitung</string>
|
||||
<string name="saved_ICE_servers_will_be_removed">Gespeicherte WebRTC ICE-Server werden entfernt.</string>
|
||||
<string name="your_ICE_servers">Ihre ICE-Server</string>
|
||||
<string name="configure_ICE_servers">Konfigurieren Sie ICE-Server</string>
|
||||
<string name="enter_one_ICE_server_per_line">ICE-Server (einer pro Zeile)</string>
|
||||
<string name="error_saving_ICE_servers">Fehler beim Speichern der ICE-Server</string>
|
||||
<string name="ensure_ICE_server_address_are_correct_format_and_unique">Stellen Sie sicher, dass die WebRTC ICE-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind.</string>
|
||||
<string name="save_servers_button">Speichern</string>
|
||||
<string name="network_and_servers">Netzwerk & Server</string>
|
||||
<string name="network_settings">Erweiterte Netzwerkeinstellungen</string>
|
||||
<string name="network_settings_title">Netzwerkeinstellungen</string>
|
||||
<string name="network_socks_toggle">SOCKS-Proxy verwenden (Port 9050)</string>
|
||||
<string name="network_enable_socks">SOCKS-Proxy verwenden?</string>
|
||||
<string name="network_enable_socks_info">Zugriff auf die Server über SOCKS-Proxy auf Port 9050? Der Proxy muss gestartet werden, bevor diese Option aktiviert wird.</string>
|
||||
<string name="network_disable_socks">Direkte Internetverbindung verwenden?</string>
|
||||
<string name="network_disable_socks_info">Wenn Sie dies bestätigen, können die Messaging-Server Ihre IP-Adresse und Ihren Provider sehen und mit welchen Servern Sie sich verbinden.</string>
|
||||
<string name="update_onion_hosts_settings_question">Einstellung für .onion-Hosts aktualisieren?</string>
|
||||
<string name="network_use_onion_hosts">Verwende .onion-Hosts</string>
|
||||
<string name="network_use_onion_hosts_prefer">Wenn verfügbar</string>
|
||||
<string name="network_use_onion_hosts_no">Nein</string>
|
||||
<string name="network_use_onion_hosts_required">Erforderlich</string>
|
||||
<string name="network_use_onion_hosts_prefer_desc">Onion-Hosts werden verwendet, wenn sie verfügbar sind.</string>
|
||||
<string name="network_use_onion_hosts_no_desc">Onion-Hosts werden nicht verwendet.</string>
|
||||
<string name="network_use_onion_hosts_required_desc">Für die Verbindung werden Onion-Hosts benötigt.</string>
|
||||
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion-Hosts werden verwendet, wenn sie verfügbar sind.</string>
|
||||
<string name="network_use_onion_hosts_no_desc_in_alert">Onion-Hosts werden nicht verwendet.</string>
|
||||
<string name="network_use_onion_hosts_required_desc_in_alert">Für die Verbindung werden Onion-Hosts benötigt.</string>
|
||||
<string name="appearance_settings">Design</string>
|
||||
|
||||
<!-- Address Items - UserAddressView.kt -->
|
||||
<string name="create_address">Adresse erstellen</string>
|
||||
<string name="delete_address__question">Adresse löschen?</string>
|
||||
<string name="all_your_contacts_will_remain_connected">Alle Ihre Kontakte bleiben verbunden.</string>
|
||||
<string name="you_can_share_your_address_anybody_will_be_able_to_connect">Sie können Ihre Adresse als Link oder als QR-Code teilen – Jeder kann sich mit Ihnen verbinden.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">Wenn Sie diese Adresse löschen, werden Sie Ihre Kontakte nicht verlieren.</string>
|
||||
<string name="if_you_later_delete_address_you_wont_lose_contacts">Wenn Sie diese Adresse später löschen, werden Sie Ihre mit dieser Adresse verbundenen Kontakte nicht verlieren.</string>
|
||||
<string name="share_link">Link teilen</string>
|
||||
<string name="delete_address">Adresse löschen</string>
|
||||
|
||||
<!-- User profile details - UserProfileView.kt -->
|
||||
<string name="display_name__field">Angezeigter Name:</string>
|
||||
<string name="full_name__field">"Vollständiger Name:</string>
|
||||
<string name="your_chat_profile">Ihr Chat-Profil</string>
|
||||
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt.\n\n<xliff:g id="appName">SimpleX</xliff:g>-Server können Ihr Profil nicht sehen.</string>
|
||||
<string name="edit_image">Bild bearbeiten</string>
|
||||
<string name="delete_image">Bild löschen</string>
|
||||
<string name="save_and_notify_contacts">Speichern (und Kontakte benachrichtigen)</string>
|
||||
|
||||
<!-- Welcome Prompts - WelcomeView.kt -->
|
||||
<string name="you_control_your_chat">Sie haben volle Kontrolle über Ihren Chat!</string>
|
||||
<string name="the_messaging_and_app_platform_protecting_your_privacy_and_security">Die Messaging- und Anwendungsplattform zum Schutz Ihrer Privatsphäre und Sicherheit.</string>
|
||||
<string name="we_do_not_store_contacts_or_messages_on_servers">Wir speichern auf den Servern keinen Ihrer Kontakte oder keine Ihrer Nachrichten (sofern einmal zugestellt).</string>
|
||||
<string name="create_profile">Profil erstellen</string>
|
||||
<string name="your_profile_is_stored_on_your_device">Ihr Profil, Ihre Kontakte und zugestellten Nachrichten werden auf Ihrem Gerät gespeichert.</string>
|
||||
<string name="profile_is_only_shared_with_your_contacts">Das Profil wird nur mit Ihren Kontakten geteilt.</string>
|
||||
<string name="display_name_cannot_contain_whitespace">Der angezeigte Name darf keine Leerzeichen enthalten.</string>
|
||||
<string name="display_name">Angezeigter Name</string>
|
||||
<string name="full_name_optional__prompt">Vollständiger Name (optional)</string>
|
||||
<string name="create_profile_button">Erstellen</string>
|
||||
<string name="about_simplex">Über SimpleX</string>
|
||||
|
||||
<!-- markdown demo - MarkdownHelpView.kt -->
|
||||
<string name="how_to_use_markdown">Wie Sie Markdowns anwenden</string>
|
||||
<string name="you_can_use_markdown_to_format_messages__prompt">Sie können Markdowns verwenden, um Nachrichten zu formatieren:</string>
|
||||
<string name="bold">fett</string>
|
||||
<string name="italic">kursiv</string>
|
||||
<string name="strikethrough">durchstreichen</string>
|
||||
<string name="a_plus_b">a + b</string>
|
||||
<string name="colored">farbig</string>
|
||||
<string name="secret">geheim</string>
|
||||
|
||||
<!-- CICallStatus - in chat items -->
|
||||
<string name="callstatus_calling">Anruf…</string>
|
||||
<string name="callstatus_missed">Anruf verpasst</string>
|
||||
<string name="callstatus_rejected">Anruf abgelehnt</string>
|
||||
<string name="callstatus_accepted">Anruf angenommen</string>
|
||||
<string name="callstatus_connecting">Anruf wird verbunden…</string>
|
||||
<string name="callstatus_in_progress">Anruf läuft</string>
|
||||
<string name="callstatus_ended">Anruf beendet <xliff:g id="duration" example="01:15">%1$s</xliff:g></string>
|
||||
<string name="callstatus_error">Fehler bei Anruf</string>
|
||||
|
||||
<!-- CallState -->
|
||||
<string name="callstate_starting">Verbindung wird gestartet…</string>
|
||||
<string name="callstate_waiting_for_answer">Warten auf Antwort…</string>
|
||||
<string name="callstate_waiting_for_confirmation">Warten auf Bestätigung…</string>
|
||||
<string name="callstate_received_answer">Antwort erhalten…</string>
|
||||
<string name="callstate_received_confirmation">Bestätigung erhalten…</string>
|
||||
<string name="callstate_connecting">Verbindung wird hergestellt…</string>
|
||||
<string name="callstate_connected">verbunden</string>
|
||||
<string name="callstate_ended">beendet</string>
|
||||
|
||||
<!-- SimpleXInfo -->
|
||||
<string name="next_generation_of_private_messaging">Die nächste Generation von privatem Messaging</string>
|
||||
<string name="privacy_redefined">Datenschutz neu definiert</string>
|
||||
<string name="first_platform_without_user_ids">Die erste Plattform ohne Benutzerkennungen – Privat per Design</string>
|
||||
<string name="immune_to_spam_and_abuse">Immun gegen Spam und Missbrauch</string>
|
||||
<string name="people_can_connect_only_via_links_you_share">Verbindungen mit Kontakten sind nur über Links möglich, die Sie oder Ihre Kontakte teilen.</string>
|
||||
<string name="decentralized">Dezentral</string>
|
||||
<string name="opensource_protocol_and_code_anybody_can_run_servers">Open-Source-Protokoll und -Code – Jeder kann seine eigenen Server nutzen.</string>
|
||||
<string name="create_your_profile">Erstellen Sie Ihr Profil</string>
|
||||
<string name="make_private_connection">Stellen Sie eine private Verbindung her</string>
|
||||
<string name="how_it_works">Wie es funktioniert</string>
|
||||
|
||||
<!-- How SimpleX Works -->
|
||||
<string name="how_simplex_works">Wie <xliff:g id="appName">SimpleX</xliff:g> funktioniert</string>
|
||||
<string name="many_people_asked_how_can_it_deliver">Viele Menschen haben gefragt: <i>Wenn <xliff:g id="appName">SimpleX</xliff:g> keine Benutzerkennungen hat, wie kann es dann Nachrichten zustellen?</i></string>
|
||||
<string name="to_protect_privacy_simplex_has_ids_for_queues">Zum Schutz Ihrer Privatsphäre verwendet <xliff:g id="appName">SimpleX</xliff:g> an Stelle von Benutzer-IDs, die von allen anderen Plattformen verwendet werden, Kennungen für Nachrichtenwarteschlangen, die für jeden Ihrer Kontakte individuell sind.</string>
|
||||
<string name="you_control_servers_to_receive_your_contacts_to_send">Sie legen fest, über welche Server Sie Ihre Nachrichten <b>empfangen</b> und an Ihre Kontakte <b>senden</b>.</string>
|
||||
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages">Nur die Endgeräte speichern Benutzerprofile, Kontakte, Gruppen und Nachrichten, die über eine <b>2-Schichten Ende-zu-Ende-Verschlüsselung</b> gesendet werden.</string>
|
||||
<string name="read_more_in_github">Erfahren Sie mehr dazu in unserem GitHub-Repository.</string>
|
||||
<string name="read_more_in_github_with_link">Erfahren Sie mehr dazu in unserem <font color="#0088ff">GitHub-Repository</font>.</string>
|
||||
|
||||
<!-- MakeConnection -->
|
||||
<string name="paste_the_link_you_received">Fügen Sie den erhaltenen Link ein</string>
|
||||
|
||||
<!-- Call -->
|
||||
<string name="incoming_video_call">Eingehender Videoanruf</string>
|
||||
<string name="incoming_audio_call">Eingehender Audioanruf</string>
|
||||
<string name="contact_wants_to_connect_via_call"><xliff:g id="contactName" example="Alice">%1$s</xliff:g> möchte sich mit Ihnen verbinden per</string>
|
||||
<string name="video_call_no_encryption">Videoanruf (nicht E2E verschlüsselt)</string>
|
||||
<string name="encrypted_video_call">E2E-verschlüsseltem Videoanruf</string>
|
||||
<string name="audio_call_no_encryption">Audioanruf (nicht E2E verschlüsselt)</string>
|
||||
<string name="encrypted_audio_call">E2E-verschlüsseltem Audioanruf</string>
|
||||
<string name="accept">Akzeptieren</string>
|
||||
<string name="reject">Ablehnen</string>
|
||||
<string name="ignore">Ignorieren</string>
|
||||
<string name="call_already_ended">Anruf ist bereits beendet!</string>
|
||||
<string name="icon_descr_video_call">Videoanruf</string>
|
||||
<string name="icon_descr_audio_call">Audioanruf</string>
|
||||
|
||||
<!-- Call settings -->
|
||||
<string name="settings_audio_video_calls">Audio- & Videoanrufe</string>
|
||||
<string name="your_calls">Ihre Anrufe</string>
|
||||
<string name="connect_calls_via_relay">Über ein Relais verbinden</string>
|
||||
<string name="call_on_lock_screen">Anrufe auf Sperrbildschirm:</string>
|
||||
<string name="accept_call_on_lock_screen">Akzeptieren</string>
|
||||
<string name="show_call_on_lock_screen">Anzeigen</string>
|
||||
<string name="no_call_on_lock_screen">Deaktivieren</string>
|
||||
<string name="your_ice_servers">Ihre ICE-Server</string>
|
||||
<string name="webrtc_ice_servers">WebRTC ICE-Server</string>
|
||||
|
||||
<!-- Call Lock Screen -->
|
||||
<string name="open_simplex_chat_to_accept_call">Öffnen Sie <xliff:g id="appNameFull">SimpleX Chat</xliff:g>, um den Anruf anzunehmen.</string>
|
||||
<string name="allow_accepting_calls_from_lock_screen">Aktivieren Sie Anrufe vom Sperrbildschirm über die Einstellungen.</string>
|
||||
<string name="open_verb">Öffnen</string>
|
||||
|
||||
<!-- Call overlay -->
|
||||
<string name="status_e2e_encrypted">E2E-verschlüsselt</string>
|
||||
<string name="status_no_e2e_encryption">Keine E2E-Verschlüsselung</string>
|
||||
<string name="status_contact_has_e2e_encryption">Kontakt hat E2E-Verschlüsselung</string>
|
||||
<string name="status_contact_has_no_e2e_encryption">Kontakt hat keine E2E-Verschlüsselung</string>
|
||||
<string name="call_connection_peer_to_peer">Peer-to-Peer</string>
|
||||
<string name="call_connection_via_relay">über Relais</string>
|
||||
<string name="icon_descr_hang_up">Auflegen</string>
|
||||
<string name="icon_descr_video_off">Video aus</string>
|
||||
<string name="icon_descr_video_on">Video an</string>
|
||||
<string name="icon_descr_audio_off">Audio aus</string>
|
||||
<string name="icon_descr_audio_on">Audio an</string>
|
||||
<string name="icon_descr_speaker_off">Lautsprecher aus</string>
|
||||
<string name="icon_descr_speaker_on">Lautsprecher an</string>
|
||||
<string name="icon_descr_flip_camera">Kamera umdrehen</string>
|
||||
|
||||
<!-- Call items -->
|
||||
<string name="icon_descr_call_pending_sent">Anstehender Anruf</string>
|
||||
<string name="icon_descr_call_missed">Verpasster Anruf</string>
|
||||
<string name="icon_descr_call_rejected">Abgelehnter Anruf</string>
|
||||
<string name="icon_descr_call_connecting">Anruf wird verbunden</string>
|
||||
<string name="icon_descr_call_progress">Laufendes Gespräch</string>
|
||||
<string name="icon_descr_call_ended">Anruf beendet</string>
|
||||
<string name="answer_call">Anruf annehmen</string>
|
||||
|
||||
<!-- Message integrity -->
|
||||
<string name="integrity_msg_skipped"><xliff:g id="connection ID" example="1">%1$d</xliff:g> übersprungene Nachricht(en)</string>
|
||||
<string name="integrity_msg_bad_hash">Ungültiger Nachrichten-Hash</string>
|
||||
<string name="integrity_msg_bad_id">Falsche Nachrichten-ID</string>
|
||||
<string name="integrity_msg_duplicate">Doppelte Nachricht</string>
|
||||
<string name="alert_title_skipped_messages">Übersprungene Nachrichten</string>
|
||||
<string name="alert_text_skipped_messages_it_can_happen_when">Dies kann unter folgenden Umständen passieren:\n1. Die Nachrichten verfallen auf dem Server, wenn sie 30 Tage lang nicht empfangen wurden.\n2. Der Server, den Sie zum Empfangen der Nachrichten von diesem Kontakt verwenden, wurde aktualisiert und neu gestartet.\n3. Die Verbindung ist kompromittiert.\nBitte nehmen Sie über die Einstellungen Verbindung mit den Entwicklern auf, um Updates zu den Servern zu erhalten.\nWir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu verhindern.</string>
|
||||
|
||||
<!-- Privacy settings -->
|
||||
<string name="privacy_and_security">Datenschutz & Sicherheit</string>
|
||||
<string name="your_privacy">Ihre Privatsphäre</string>
|
||||
<string name="auto_accept_images">Bilder automatisch akzeptieren</string>
|
||||
<string name="send_link_previews">Link-Vorschau senden</string>
|
||||
|
||||
<!-- Settings sections -->
|
||||
<string name="settings_section_title_you">DU</string>
|
||||
<string name="settings_section_title_settings">EINSTELLUNGEN</string>
|
||||
<string name="settings_section_title_help">HILFE</string>
|
||||
<string name="settings_section_title_develop">ENTWICKELN</string>
|
||||
<string name="settings_section_title_device">GERÄT</string>
|
||||
<string name="settings_section_title_chats">CHATS</string>
|
||||
<string name="settings_developer_tools">Entwicklertools</string>
|
||||
<string name="settings_experimental_features">Experimentelle Funktionen</string>
|
||||
<string name="settings_section_title_socks">SOCKS-PROXY</string>
|
||||
<string name="settings_section_title_icon">APP ICON</string>
|
||||
<string name="settings_section_title_themes">DESIGN</string>
|
||||
<string name="settings_section_title_messages">MESSAGES</string>
|
||||
<string name="settings_section_title_calls">CALLS</string>
|
||||
<string name="settings_section_title_incognito">Inkognito Modus</string>
|
||||
|
||||
<!-- DatabaseView.kt -->
|
||||
<string name="your_chat_database">Ihre Chat-Datenbank</string>
|
||||
<string name="run_chat_section">CHAT STARTEN</string>
|
||||
<string name="chat_is_running">Chat läuft</string>
|
||||
<string name="chat_is_stopped">Chat ist beendet</string>
|
||||
<string name="chat_database_section">CHAT-DATENBANK</string>
|
||||
<string name="database_passphrase">Datenbank-Passwort</string>
|
||||
<string name="export_database">Datenbank exportieren</string>
|
||||
<string name="import_database">Datenbank importieren</string>
|
||||
<string name="new_database_archive">Neues Datenbankarchiv</string>
|
||||
<string name="old_database_archive">Altes Datenbankarchiv</string>
|
||||
<string name="delete_database">Datenbank löschen</string>
|
||||
<string name="error_starting_chat">Fehler beim Starten des Chats</string>
|
||||
<string name="stop_chat_question">Chat beenden?</string>
|
||||
<string name="stop_chat_to_export_import_or_delete_chat_database">Beenden Sie den Chat, um die Chat-Datenbank zu exportieren, zu importieren oder zu löschen. Sie können keine Nachrichten empfangen oder senden, solange der Chat angehalten ist.</string>
|
||||
<string name="stop_chat_confirmation">Beenden</string>
|
||||
<string name="set_password_to_export">Passwort zum Exportieren festlegen</string>
|
||||
<string name="set_password_to_export_desc">Die Datenbank ist mit einem zufälligen Passwort verschlüsselt. Bitte ändern Sie es vor dem Exportieren.</string>
|
||||
<string name="error_stopping_chat">Fehler beim Beenden des Chats</string>
|
||||
<string name="error_exporting_chat_database">Fehler beim Exportieren der Chat-Datenbank</string>
|
||||
<string name="import_database_question">Chat-Datenbank importieren?</string>
|
||||
<string name="your_current_chat_database_will_be_deleted_and_replaced_with_the_imported_one">Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT.\nDiese Aktion kann nicht rückgängig gemacht werden - Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren.</string>
|
||||
<string name="import_database_confirmation">Importieren</string>
|
||||
<string name="error_deleting_database">Fehler beim Löschen der Chat-Datenbank</string>
|
||||
<string name="error_importing_database">Fehler beim Importieren der Chat-Datenbank</string>
|
||||
<string name="chat_database_imported">Chat-Datenbank importiert</string>
|
||||
<string name="restart_the_app_to_use_imported_chat_database">Starten Sie die App neu, um die importierte Chat-Datenbank zu verwenden.</string>
|
||||
<string name="delete_chat_profile_question">Chat-Profil löschen?</string>
|
||||
<string name="delete_chat_profile_action_cannot_be_undone_warning">Diese Aktion kann nicht rückgängig gemacht werden - Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren.</string>
|
||||
<string name="chat_database_deleted">Chat-Datenbank gelöscht</string>
|
||||
<string name="restart_the_app_to_create_a_new_chat_profile">Starten Sie die App neu, um ein neues Chat-Profil zu erstellen.</string>
|
||||
<string name="you_must_use_the_most_recent_version_of_database">Sie dürfen die neueste Version Ihrer Chat-Datenbank NUR auf einem Gerät verwenden, andernfalls erhalten Sie möglicherweise keine Nachrichten mehr von einigen Ihrer Kontakte.</string>
|
||||
<string name="stop_chat_to_enable_database_actions">Chat beenden, um Datenbankaktionen zu erlauben.</string>
|
||||
<string name="files_section">DATEIEN</string>
|
||||
<string name="delete_files_and_media">Dateien \& Medien löschen</string>
|
||||
<string name="delete_files_and_media_question">Dateien und Medien löschen?</string>
|
||||
<string name="delete_files_and_media_desc">Diese Aktion kann nicht rückgängig gemacht werden - alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten.</string>
|
||||
<string name="no_received_app_files">Keine empfangenen oder gesendeten Dateien</string>
|
||||
<string name="total_files_count_and_size">%d Datei(en) mit einem Gesamtspeicherverbrauch von %s</string>
|
||||
|
||||
<!-- DatabaseEncryptionView.kt -->
|
||||
<string name="save_passphrase_in_keychain">Passwort im Keystore sichern</string>
|
||||
<string name="database_encrypted">Datenbank verschlüsselt!</string>
|
||||
<string name="error_encrypting_database">Fehler beim Verschlüsseln der Datenbank</string>
|
||||
<string name="remove_passphrase_from_keychain">Passwort aus dem Keystore entfernen?</string>
|
||||
<string name="notifications_will_be_hidden">Benachrichtigungen werden nur solange zugestellt, bis die App angehalten wird!</string>
|
||||
<string name="remove_passphrase">Entfernen</string>
|
||||
<string name="encrypt_database">Verschlüsseln</string>
|
||||
<string name="update_database">Aktualisieren</string>
|
||||
<string name="current_passphrase">Aktuelles Passwort…</string>
|
||||
<string name="new_passphrase">Neues Passwort…</string>
|
||||
<string name="confirm_new_passphrase">Neues Passwort bestätigen…</string>
|
||||
<string name="update_database_passphrase">Datenbank-Passwort aktualisieren</string>
|
||||
<string name="enter_correct_current_passphrase">Bitte geben Sie das korrekte, aktuelle Passwort ein.</string>
|
||||
<string name="database_is_not_encrypted">Ihre Chat-Datenbank ist nicht verschlüsselt. Bitte legen Sie ein Passwort fest, um sie zu schützen.</string>
|
||||
<string name="keychain_is_storing_securely">Der Android Keystore wird verwendet, um das Passwort sicher zu speichern. Dies ermöglicht die ordentliche Funktion des Benachrichtigungsdienstes.</string>
|
||||
<string name="encrypted_with_random_passphrase">Die Datenbank wird mit einem zufälligen Passwort verschlüsselt, Sie können es ändern.</string>
|
||||
<string name="impossible_to_recover_passphrase"><b>Bitte beachten Sie</b>: Sie können das Passwort NICHT wiederherstellen oder ändern, wenn Sie es vergessen haben oder verlieren.</string>
|
||||
<string name="keychain_allows_to_receive_ntfs">Der Android Keystore wird verwendet, um das Passwort sicher zu speichern, nachdem Sie die App neu gestartet oder das Passwort geändert haben – dies ermöglicht den Empfang von Benachrichtigungen.</string>
|
||||
<string name="you_have_to_enter_passphrase_every_time">Sie müssen das Passwort jedes Mal eingeben, wenn die App startet. Es wird nicht auf dem Gerät gespeichert.</string>
|
||||
<string name="encrypt_database_question">Datenbank verschlüsseln?</string>
|
||||
<string name="change_database_passphrase_question">Datenbank-Passwort ändern?</string>
|
||||
<string name="database_will_be_encrypted">Datenbank wird verschlüsselt.</string>
|
||||
<string name="database_will_be_encrypted_and_passphrase_stored">Die Datenbank wird verschlüsselt, und das Passwort im Keystore gespeichert.</string>
|
||||
<string name="database_encryption_will_be_updated">Das Passwort für die Datenbankverschlüsselung wird aktualisiert und im Keystore gespeichert.</string>
|
||||
<string name="database_passphrase_will_be_updated">Das Passwort für die Datenbankverschlüsselung wird aktualisiert.</string>
|
||||
<string name="store_passphrase_securely">Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren.</string>
|
||||
<string name="store_passphrase_securely_without_recover">Bitte bewahren Sie das Passwort sicher auf, Sie können NICHT mehr auf den Chat zugreifen, wenn Sie es vergessen haben oder verlieren.</string>
|
||||
|
||||
<!-- DatabaseErrorView.kt -->
|
||||
<string name="wrong_passphrase">Falsches Datenbank-Passwort</string>
|
||||
<string name="encrypted_database">Verschlüsselte Datenbank</string>
|
||||
<string name="database_error">Datenbankfehler</string>
|
||||
<string name="keychain_error">Keystore Fehler</string>
|
||||
<string name="passphrase_is_different">Das Datenbank-Passwort unterscheidet sich von dem im Keystore gespeicherten.</string>
|
||||
<string name="file_with_path">Datei: %s</string>
|
||||
<string name="database_passphrase_is_required">Das Datenbank-Passwort ist erforderlich, um den Chat zu öffnen.</string>
|
||||
<string name="error_with_info">Fehler: %s</string>
|
||||
<string name="cannot_access_keychain">Die App kann nicht auf den Keystore zugreifen, um das Datenbank-Passwort zu speichern.</string>
|
||||
<string name="unknown_database_error_with_info">Unbekannter Datenbankfehler: %s</string>
|
||||
<string name="wrong_passphrase_title">Falsches Passwort!</string>
|
||||
<string name="enter_correct_passphrase">Geben Sie das korrekte Passwort ein.</string>
|
||||
<string name="unknown_error">Unbekannter Fehler</string>
|
||||
<string name="enter_passphrase">Passwort eingeben…</string>
|
||||
<string name="save_passphrase_and_open_chat">Passwort speichern und Chat öffnen</string>
|
||||
<string name="open_chat">Chat öffnen</string>
|
||||
<string name="database_backup_can_be_restored">Der Versuch, das Passwort der Datenbank zu ändern, konnte nicht abgeschlossen werden.</string>
|
||||
<string name="restore_database">Datenbanksicherung wiederherstellen</string>
|
||||
<string name="restore_database_alert_title">Datenbanksicherung wiederherstellen?</string>
|
||||
<string name="restore_database_alert_desc">Bitte geben Sie das vorherige Passwort ein, nachdem Sie die Datenbanksicherung wiederhergestellt haben. Diese Aktion kann nicht rückgängig gemacht werden.</string>
|
||||
<string name="restore_database_alert_confirm">Wiederherstellen</string>
|
||||
<string name="database_restore_error">Fehler bei der Wiederherstellung der Datenbank</string>
|
||||
|
||||
<!-- ChatModel.chatRunning interactions -->
|
||||
<string name="chat_is_stopped_indication">Chat wurde beendet</string>
|
||||
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">Sie können den Chat über die App-Einstellungen / Datenbank oder durch Neustart der App starten.</string>
|
||||
|
||||
<!-- ChatArchiveView.kt -->
|
||||
<string name="chat_archive_header">Datenbank Archiv</string>
|
||||
<string name="chat_archive_section">CHAT ARCHIV</string>
|
||||
<string name="save_archive">Archiv speichern</string>
|
||||
<string name="delete_archive">Archiv löschen</string>
|
||||
<string name="archive_created_on_ts">Erstellt am <xliff:g id="archive_ts">%1$s</xliff:g></string>
|
||||
<string name="delete_chat_archive_question">Chat Archiv löschen?</string>
|
||||
|
||||
<!-- Groups -->
|
||||
<string name="group_invitation_item_description">Einladung zur Gruppe <xliff:g id="group_name">%1$s</xliff:g></string>
|
||||
<string name="join_group_question">Der Gruppe beitreten?</string>
|
||||
<string name="you_are_invited_to_group_join_to_connect_with_group_members">Sie sind zu einer Gruppe eingeladen worden. Treten Sie bei, um sich mit Gruppenmitgliedern zu verbinden.</string>
|
||||
<string name="join_group_button">Beitreten</string>
|
||||
<string name="join_group_incognito_button">Inkognito beitreten</string>
|
||||
<string name="joining_group">Gruppe beitreten</string>
|
||||
<string name="youve_accepted_group_invitation_connecting_to_inviting_group_member">Sie sind dieser Gruppe beigetreten. Sie werden mit dem einladenden Gruppenmitglied verbunden.</string>
|
||||
<string name="leave_group_button">Verlassen</string>
|
||||
<string name="leave_group_question">Gruppe verlassen?</string>
|
||||
<string name="you_will_stop_receiving_messages_from_this_group_chat_history_will_be_preserved">Sie werden von dieser Gruppe keine Nachrichten mehr erhalten. Der Chatverlauf wird beibehalten.</string>
|
||||
<string name="icon_descr_add_members">Mitglieder einladen</string>
|
||||
<string name="icon_descr_group_inactive">Gruppe inaktiv</string>
|
||||
<string name="alert_title_group_invitation_expired">Einladung abgelaufen!</string>
|
||||
<string name="alert_message_group_invitation_expired">Die Gruppeneinladung ist nicht mehr gültig, sie wurde vom Absender entfernt.</string>
|
||||
<string name="alert_title_no_group">Die Gruppe wurde nicht gefunden!</string>
|
||||
<string name="alert_message_no_group">Diese Gruppe existiert nicht mehr.</string>
|
||||
<string name="alert_title_cant_invite_contacts">Kontakte können nicht eingeladen werden!</string>
|
||||
<string name="alert_title_cant_invite_contacts_descr">Sie verwenden ein Inkognito-Profil für diese Gruppe. Um zu verhindern, dass Sie Ihr Hauptprofil teilen, ist in diesem Fall das Einladen von Kontakten nicht erlaubt.</string>
|
||||
|
||||
<!-- CIGroupInvitationView.kt -->
|
||||
<string name="you_sent_group_invitation">Sie haben eine Gruppeneinladung gesendet</string>
|
||||
<string name="you_are_invited_to_group">Sie sind zur Gruppe eingeladen</string>
|
||||
<string name="group_invitation_tap_to_join">Zum Beitreten tippen</string>
|
||||
<string name="group_invitation_tap_to_join_incognito">Tippen, um Inkognito beizutreten</string>
|
||||
<string name="you_joined_this_group">Sie sind dieser Gruppe beigetreten</string>
|
||||
<string name="you_rejected_group_invitation">Sie haben die Gruppeneinladung abgelehnt</string>
|
||||
<string name="group_invitation_expired">Die Gruppeneinladung ist abgelaufen</string>
|
||||
|
||||
<!-- Group event chat items -->
|
||||
<string name="rcv_group_event_member_added">Sie haben <xliff:g id="member profile" example="alice (Alice)">%1$s</xliff:g> eingeladen.</string>
|
||||
<string name="rcv_group_event_member_connected">verbunden</string>
|
||||
<string name="rcv_group_event_member_left">verlassen</string>
|
||||
<string name="rcv_group_event_member_deleted">entfernte <xliff:g id="member profile" example="alice (Alice)">%1$s</xliff:g>.</string>
|
||||
<string name="rcv_group_event_user_deleted">hat Sie entfernt</string>
|
||||
<string name="rcv_group_event_group_deleted">Gruppe gelöscht</string>
|
||||
<string name="rcv_group_event_updated_group_profile">aktualisiertes Gruppenprofil</string>
|
||||
<string name="snd_group_event_member_deleted">Sie haben <xliff:g id="member profile" example="alice (Alice)">%1$s</xliff:g> entfernt.</string>
|
||||
<string name="snd_group_event_user_left">Sie haben verlassen</string>
|
||||
<string name="snd_group_event_group_profile_updated">Gruppenprofil aktualisiert</string>
|
||||
|
||||
<!-- GroupMemberRole -->
|
||||
<string name="group_member_role_member">Mitglied</string>
|
||||
<string name="group_member_role_admin">Admin</string>
|
||||
<string name="group_member_role_owner">Eigentümer</string>
|
||||
|
||||
<!-- GroupMemberStatus -->
|
||||
<string name="group_member_status_removed">entfernt</string>
|
||||
<string name="group_member_status_left">verlassen</string>
|
||||
<string name="group_member_status_group_deleted">Gruppe gelöscht</string>
|
||||
<string name="group_member_status_invited">eingeladen</string>
|
||||
<string name="group_member_status_introduced">Verbindung (erstellt)</string>
|
||||
<string name="group_member_status_intro_invitation">Verbindung (eingeladen)</string>
|
||||
<string name="group_member_status_accepted">Verbindung (akzeptiert)</string>
|
||||
<string name="group_member_status_announced">Verbindung (angekündigt)</string>
|
||||
<string name="group_member_status_connected">verbunden</string>
|
||||
<string name="group_member_status_complete">vollständig</string>
|
||||
<string name="group_member_status_creator">Ersteller</string>
|
||||
|
||||
<string name="group_member_status_connecting">verbinden</string>
|
||||
|
||||
<!-- AddGroupMembersView.kt -->
|
||||
<string name="no_contacts_to_add">Keine Kontakte zum Hinzufügen</string>
|
||||
<string name="new_member_role">Neue Mitgliedsrolle</string>
|
||||
<string name="icon_descr_expand_role">Rollenauswahl erweitern</string>
|
||||
<string name="invite_to_group_button">In Gruppe einladen</string>
|
||||
<string name="icon_descr_contact_checked">Kontakt geprüft</string>
|
||||
<string name="clear_contacts_selection_button">Löschen</string>
|
||||
<string name="num_contacts_selected"><xliff:g id="num_contacts">%1$s</xliff:g> Kontakt(e) ausgewählt</string>
|
||||
<string name="no_contacts_selected">Keine Kontakte ausgewählt</string>
|
||||
<string name="invite_prohibited">Kontakt kann nicht eingeladen werden!</string>
|
||||
<string name="invite_prohibited_description">Sie versuchen, einen Kontakt, mit dem Sie ein Inkognito-Profil geteilt haben, in die Gruppe einzuladen, in der Sie Ihr Hauptprofil verwenden.</string>
|
||||
|
||||
<!-- GroupChatInfoView.kt -->
|
||||
<string name="button_add_members">Mitglieder einladen</string>
|
||||
<string name="group_info_section_title_num_members"><xliff:g id="num_members">%1$s</xliff:g> MITGLIEDER</string>
|
||||
<string name="group_info_member_you">Sie: <xliff:g id="group_info_you">%1$s</xliff:g></string>
|
||||
<string name="button_delete_group">Gruppe löschen</string>
|
||||
<string name="delete_group_question">Gruppe löschen?</string>
|
||||
<string name="delete_group_for_all_members_cannot_undo_warning">Die Gruppe wird für alle Mitglieder gelöscht - dies kann nicht rückgängig gemacht werden!</string>
|
||||
<string name="delete_group_for_self_cannot_undo_warning">Die Gruppe wird für Sie gelöscht - dies kann nicht rückgängig gemacht werden!</string>
|
||||
<string name="button_leave_group">Gruppe verlassen</string>
|
||||
<string name="button_edit_group_profile">Gruppenprofil bearbeiten</string>
|
||||
|
||||
<!-- For Console chat info section -->
|
||||
<string name="section_title_for_console">FÜR KONSOLE</string>
|
||||
<string name="info_row_local_name">Lokaler Name</string>
|
||||
<string name="info_row_database_id">Datenbank-ID</string>
|
||||
|
||||
<!-- GroupMemberInfoView.kt -->
|
||||
<string name="button_remove_member">Mitglied entfernen</string>
|
||||
<string name="button_send_direct_message">Direktnachricht senden</string>
|
||||
<string name="member_will_be_removed_from_group_cannot_be_undone">Das Mitglied wird aus der Gruppe entfernt - dies kann nicht rückgängig gemacht werden!</string>
|
||||
<string name="remove_member_confirmation">Entfernen</string>
|
||||
<string name="member_info_section_title_member">MITGLIED</string>
|
||||
<string name="info_row_group">Gruppe</string>
|
||||
<string name="info_row_connection">Verbindung</string>
|
||||
<string name="conn_level_desc_direct">direkt</string>
|
||||
<string name="conn_level_desc_indirect">indirekt (<xliff:g id="conn_level">%1$s</xliff:g>)</string>
|
||||
|
||||
<!-- ConnectionStats -->
|
||||
<string name="conn_stats_section_title_servers">SERVER</string>
|
||||
<string name="receiving_via">Empfangen über</string>
|
||||
<string name="sending_via">Senden über</string>
|
||||
<string name="network_status">Netzwerkstatus</string>
|
||||
|
||||
<!-- AddGroupView.kt -->
|
||||
<string name="create_secret_group_title">Geheime Gruppe erstellen</string>
|
||||
<string name="group_is_decentralized">Die Gruppe ist vollständig dezentralisiert – sie ist nur für Mitglieder sichtbar.</string>
|
||||
<string name="group_display_name_field">Anzeigename der Gruppe:</string>
|
||||
<string name="group_full_name_field">Vollständiger Gruppenname:</string>
|
||||
<string name="group_unsupported_incognito_main_profile_sent">Der Inkognito-Modus wird hier nicht unterstützt - Ihr Hauptprofil wird an die Gruppenmitglieder gesendet</string>
|
||||
<string name="group_main_profile_sent">Ihr Chat-Profil wird an Gruppenmitglieder gesendet</string>
|
||||
|
||||
|
||||
<!-- GroupProfileView.kt -->
|
||||
<string name="group_profile_is_stored_on_members_devices">Das Gruppenprofil wird auf den Geräten der Mitglieder gespeichert, nicht auf den Servern.</string>
|
||||
<string name="save_group_profile">Gruppenprofil speichern</string>
|
||||
<string name="error_saving_group_profile">Fehler beim Speichern des Gruppenprofils</string>
|
||||
|
||||
<!-- AdvancedNetworkSettings.kt -->
|
||||
<string name="network_options_reset_to_defaults">Auf Standardwerte zurücksetzen</string>
|
||||
<string name="network_option_seconds_label">sek</string>
|
||||
<string name="network_option_tcp_connection_timeout">Timeout der TCP-Verbindung</string>
|
||||
<string name="network_option_protocol_timeout">Protokollzeitüberschreitung</string>
|
||||
<string name="network_option_ping_interval">PING-Intervall</string>
|
||||
<string name="network_option_enable_tcp_keep_alive">TCP-Keep-alive aktivieren</string>
|
||||
<string name="network_options_revert">Zurückkehren</string>
|
||||
<string name="network_options_save">Speichern</string>
|
||||
<string name="update_network_settings_question">Netzwerkeinstellungen aktualisieren?</string>
|
||||
<string name="updating_settings_will_reconnect_client_to_all_servers">Die Aktualisierung der Einstellungen wird den Client wieder mit allen Servern verbinden.</string>
|
||||
<string name="update_network_settings_confirmation">Aktualisieren</string>
|
||||
|
||||
<!-- Incognito mode -->
|
||||
<string name="incognito">Inkognito</string>
|
||||
<string name="incognito_random_profile">Ihr Zufallsprofil</string>
|
||||
<string name="incognito_random_profile_description">Ein zufälliges Profil wird an Ihren Kontakt gesendet.</string>
|
||||
<string name="incognito_random_profile_from_contact_description">Ein zufälliges Profil wird an den Kontakt gesendet, von dem Sie diesen Link erhalten haben.</string>
|
||||
|
||||
<string name="incognito_info_protects">Der Inkognito-Modus schützt die Privatsphäre Ihres Hauptprofilnamens und -bildes – für jeden neuen Kontakt wird ein neues Zufallsprofil erstellt.</string>
|
||||
<string name="incognito_info_allows">Es ermöglicht viele anonyme Verbindungen ohne gemeinsame Daten zwischen diesen in einem einzigen Chat-Profil zu haben.</string>
|
||||
<string name="incognito_info_share">Wenn Sie ein Inkognito-Profil mit Jemandem teilen, wird dieses Profil für die Gruppen verwendet, zu denen sie Sie eingeladen haben.</string>
|
||||
<string name="incognito_info_find">Um das für eine Inkognito-Verbindung verwendete Profil zu finden, tippen Sie oben im Chat auf den Kontakt- oder Gruppennamen.</string>
|
||||
|
||||
<!-- Default themes -->
|
||||
<string name="theme_system">System</string>
|
||||
<string name="theme_light">Hell</string>
|
||||
<string name="theme_dark">Dunkel</string>
|
||||
|
||||
<!-- Appearance.kt -->
|
||||
<string name="theme">Design</string>
|
||||
<string name="save_color">Farbe speichern</string>
|
||||
<string name="reset_color">Farben zurücksetzen</string>
|
||||
<string name="color_primary">Akzent</string>
|
||||
</resources>
|
||||
@@ -43,12 +43,24 @@
|
||||
<string name="error_setting_network_config">Ошибка при сохранении настроек сети</string>
|
||||
|
||||
<!-- API Error Responses - SimpleXAPI.kt -->
|
||||
<string name="connection_timeout">Превышено время соединения</string>
|
||||
<string name="connection_error">Ошибка соединения</string>
|
||||
<string name="network_error_desc">Пожалуйста, проверьте ваше соединение с сетью и попробуйте еще раз.</string>
|
||||
<string name="error_sending_message">Ошибка при отправке сообщения</string>
|
||||
<string name="error_adding_members">Ошибка при добавлении членов группы</string>
|
||||
<string name="error_joining_group">Ошибка при вступлении в группу</string>
|
||||
<string name="cannot_receive_file">Невозможно получить файл</string>
|
||||
<string name="sender_cancelled_file_transfer">Отправитель отменил передачу файла.</string>
|
||||
<string name="error_receiving_file">Ошибка при получении файла</string>
|
||||
<string name="error_creating_address">Ошибка при создании адреса</string>
|
||||
<string name="contact_already_exists">Существующий контакт</string>
|
||||
<string name="you_are_already_connected_to_vName_via_this_link">Вы уже соединены с <xliff:g id="contactName" example="Alice">%1$s!</xliff:g> через эту ссылку.</string>
|
||||
<string name="invalid_connection_link">Ошибка в ссылке контакта</string>
|
||||
<string name="please_check_correct_link_and_maybe_ask_for_a_new_one">Пожалуйста, проверьте, что вы использовали правильную ссылку, или попросите ваш контакт отправить вам новую.</string>
|
||||
<string name="connection_error_auth">Ошибка соединения (AUTH)</string>
|
||||
<string name="connection_error_auth_desc">Возможно, ваш контакт удалил ссылку, или она уже была использована. Если это не так, то это может быть ошибкой - пожалуйста, сообщите нам об этом.\nЧтобы установить соединение, попросите ваш контакт создать еще одну ссылку и проверьте ваше соединение с сетью.</string>
|
||||
<string name="error_accepting_contact_request">Ошибка при принятии запроса на соединение</string>
|
||||
<string name="sender_may_have_deleted_the_connection_request">Отправитель мог удалить запрос на соединение.</string>
|
||||
<string name="cannot_delete_contact">Невозможно удалить контакт!</string>
|
||||
<string name="contact_cannot_be_deleted_as_they_are_in_groups">Контакт <xliff:g id="contactName" example="Jane Doe">%1$s!</xliff:g> не может быть удален, так как является членом групп(ы) <xliff:g id="groups" example="[team, chess club]">%2$s</xliff:g>.</string>
|
||||
<string name="error_deleting_contact">Ошибка удаления контакта</string>
|
||||
@@ -152,6 +164,9 @@
|
||||
<string name="group_preview_you_are_invited">вы приглашены в группу</string>
|
||||
<string name="group_preview_join_as">вступить как %s</string>
|
||||
<string name="group_connection_pending">соединяется…</string>
|
||||
<string name="tap_to_start_new_chat">Нажмите, чтобы начать чат</string>
|
||||
<string name="chat_with_developers">Соединиться с разработчиками</string>
|
||||
<string name="you_have_no_chats">У вас нет чатов</string>
|
||||
|
||||
<!-- ComposeView.kt, helpers -->
|
||||
<string name="attach">Прикрепить</string>
|
||||
@@ -201,18 +216,17 @@
|
||||
<string name="confirm_verb">Подтвердить</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="no_details">нет описания</string>
|
||||
<string name="add_contact">Добавить контакт</string>
|
||||
<string name="add_contact">Одноразовая ссылка</string>
|
||||
<string name="copied">Скопировано в буфер обмена</string>
|
||||
|
||||
<!-- NewChatSheet -->
|
||||
<string name="add_contact_or_create_group">Начать новый разговор</string>
|
||||
<string name="create_one_time_link">Создать одноразовую ссылку</string>
|
||||
<string name="paste_received_link">Вставить полученную ссылку</string>
|
||||
<string name="scan_QR_code">Сканировать QR код</string>
|
||||
<string name="share_one_time_link">Создать ссылку-приглашение</string>
|
||||
<string name="connect_via_link_or_qr">Соединиться через ссылку / QR код</string>
|
||||
<string name="scan_QR_code">Сканировать\nQR код</string>
|
||||
<string name="create_group">Создать секретную группу</string>
|
||||
<string name="to_share_with_your_contact">(чтобы отправить вашему контакту)</string>
|
||||
<string name="in_person_or_in_video_call__bracketed">(при встрече или через видеозвонок)</string>
|
||||
<string name="paste_received_link_from_clipboard">(вставить ссылку из буфера обмена)</string>
|
||||
<string name="connect_via_link_or_qr_from_clipboard_or_in_person">(сканировать или вставить из буфера)</string>
|
||||
<string name="only_stored_on_members_devices">(хранится только у членов группы)</string>
|
||||
|
||||
<!-- GetImageView -->
|
||||
@@ -291,14 +305,22 @@
|
||||
<string name="connection_request_sent">Запрос на соединение послан!</string>
|
||||
<string name="you_will_be_connected_when_your_connection_request_is_accepted">Соединение будет установлено когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!</string>
|
||||
<string name="you_will_be_connected_when_your_contacts_device_is_online">Соединение будет установлено когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже!</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Покажите QR код вашему контакту, чтобы сосканировать его из приложения.</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">Если вы не можете встретиться лично, вы можете <b>показать QR код во время видеозвонка</b> или отправить ссылку через любой другой канал связи.</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Ваш контакт может сосканировать QR код в приложении.</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">Если вы не можете встретиться лично, вы можете <b>показать QR код во время видеозвонка</b> или поделиться ссылкой.</string>
|
||||
<string name="your_chat_profile_will_be_sent_to_your_contact">Ваш профиль будет отправлен\nвашему контакту</string>
|
||||
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">Если вы не можете встретиться лично, вы можете <b>сосканировать QR код во время видеозвонка</b>, или ваш контакт может отправить вам ссылку.</string>
|
||||
<string name="share_invitation_link">Поделиться ссылкой</string>
|
||||
<string name="paste_connection_link_below_to_connect">Чтобы соединиться, вставьте в это поле ссылку, полученную от вашего контакта.</string>
|
||||
<string name="your_profile_will_be_sent">Ваш профиль будет отправлен вашему контакту</string>
|
||||
|
||||
<!-- PasteToConnect.kt -->
|
||||
<string name="connect_button">Соединиться</string>
|
||||
<string name="paste_button">Вставить</string>
|
||||
|
||||
<!-- CreateLinkView.kt -->
|
||||
<string name="create_one_time_link">Создать одноразовую ссылку</string>
|
||||
<string name="one_time_link">Одноразовая ссылка</string>
|
||||
<string name="your_contact_address">Ваш SimpleX адрес</string>
|
||||
|
||||
<!-- settings - SettingsView.kt -->
|
||||
<string name="your_settings">Настройки</string>
|
||||
@@ -315,12 +337,18 @@
|
||||
<string name="smp_servers">SMP серверы</string>
|
||||
<string name="install_simplex_chat_for_terminal"><xliff:g id="appNameFull">SimpleX Chat</xliff:g> для терминала</string>
|
||||
<string name="use_simplex_chat_servers__question">Использовать серверы предосталенные <xliff:g id="appNameFull">SimpleX Chat</xliff:g>?</string>
|
||||
<string name="saved_SMP_servers_will_br_removed">Сохраненные SMP серверы будут удалены.</string>
|
||||
<string name="saved_SMP_servers_will_be_removed">Сохраненные SMP серверы будут удалены.</string>
|
||||
<string name="your_SMP_servers">Ваши SMP серверы</string>
|
||||
<string name="configure_SMP_servers">Настройка SMP серверов</string>
|
||||
<string name="using_simplex_chat_servers">Используются серверы предоставленные <xliff:g id="appNameFull">SimpleX Chat</xliff:g>.</string>
|
||||
<string name="enter_one_SMP_server_per_line">Введите SMP серверы, каждый сервер в отдельной строке:</string>
|
||||
<string name="how_to">Инфо</string>
|
||||
<string name="saved_ICE_servers_will_be_removed">Сохраненные WebRTC ICE серверы будут удалены.</string>
|
||||
<string name="your_ICE_servers">Ваши ICE серверы</string>
|
||||
<string name="configure_ICE_servers">Настройка ICE серверов</string>
|
||||
<string name="enter_one_ICE_server_per_line">ICE серверы (один на строке)</string>
|
||||
<string name="error_saving_ICE_servers">Ошибка при сохранении ICE серверов</string>
|
||||
<string name="ensure_ICE_server_address_are_correct_format_and_unique">Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.</string>
|
||||
<string name="save_servers_button">Сохранить</string>
|
||||
<string name="network_and_servers">Сеть и серверы</string>
|
||||
<string name="network_settings">Настройки сети</string>
|
||||
@@ -347,9 +375,9 @@
|
||||
<string name="create_address">Создать адрес</string>
|
||||
<string name="delete_address__question">Удалить адрес?</string>
|
||||
<string name="all_your_contacts_will_remain_connected">Все контакты, которые соединились через этот адрес, сохранятся.</string>
|
||||
<string name="your_chat_address">Ваш <xliff:g id="appName">SimpleX</xliff:g> адрес</string>
|
||||
<string name="you_can_share_your_address_anybody_will_be_able_to_connect">Вы можете использовать адрес как ссылку или как QR код - через него можно с вами соединиться.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">Вы сможете удалить адрес, сохранив контакты, которые через него соединились.</string>
|
||||
<string name="if_you_later_delete_address_you_wont_lose_contacts">Вы сможете удалить адрес, сохранив контакты, которые через него соединились.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">Вы можете удалить адрес, сохранив контакты, которые через него соединились.</string>
|
||||
<string name="share_link">Поделиться\nссылкой</string>
|
||||
<string name="delete_address">Удалить\nадрес</string>
|
||||
|
||||
@@ -386,7 +414,7 @@
|
||||
<string name="secret">секрет</string>
|
||||
<string name="connect_via_link">Соединиться через ссылку</string>
|
||||
<string name="this_string_is_not_a_connection_link">Эта строка не является ссылкой-приглашением!</string>
|
||||
<string name="you_can_also_connect_by_clicking_the_link">Вы также можете соединиться, открыв ссылку там, где вы её получили. Если ссылка откроется в браузере, нажмите кнопку <b>Open in mobile app</b>.</string>
|
||||
<string name="you_can_also_connect_by_clicking_the_link">Вы также можете соединиться, открыв ссылку там, где вы её получили. Если ссылка откроется в браузере, нажмите кнопку <b>Открыть в приложении</b>.</string>
|
||||
|
||||
<!-- CICallStatus -->
|
||||
<string name="callstatus_calling">входящий звонок…</string>
|
||||
@@ -430,16 +458,7 @@
|
||||
<string name="read_more_in_github_with_link">Узнайте больше из нашего <font color="#0088ff">GitHub репозитория</font>.</string>
|
||||
|
||||
<!-- MakeConnection -->
|
||||
<string name="to_make_your_first_private_connection_choose">Чтобы добавить ваш первый контакт, выберите <b>одно из</b>:</string>
|
||||
<string name="create_1_time_link_qr_code">Создать ссылку / QR код</string>
|
||||
<string name="it_s_secure_to_share__only_one_contact_can_use_it">Ей безопасно поделиться - только один контакт может использовать её.</string>
|
||||
<string name="paste_the_link_you_received">Вставьте полученную ссылку</string>
|
||||
<string name="or_open_the_link_in_the_browser_and_tap_open_in_mobile">Или откройте ссылку в браузере и нажмите <b>Open in mobile</b>.</string>
|
||||
<string name="scan_contact_s_qr_code">Сосканировать QR код контакта</string>
|
||||
<string name="in_person_or_via_a_video_call__the_most_secure_way_to_connect">При встрече или в видеозвонке – самый безопасный способ установить соединение</string>
|
||||
<string name="or">или</string>
|
||||
<string name="connect_with_the_developers">Соединиться с разработчиками</string>
|
||||
<string name="to_ask_any_questions_and_to_receive_simplex_chat_updates">Чтобы задать вопросы и получать уведомления о <xliff:g id="appNameFull">SimpleX Chat</xliff:g>.</string>
|
||||
<string name="paste_the_link_you_received">Вставить полученную ссылку</string>
|
||||
|
||||
<!-- Call -->
|
||||
<string name="incoming_video_call">Входящий видеозвонок</string>
|
||||
@@ -464,6 +483,8 @@
|
||||
<string name="accept_call_on_lock_screen">Принимать</string>
|
||||
<string name="show_call_on_lock_screen">Показывать</string>
|
||||
<string name="no_call_on_lock_screen">Выключить</string>
|
||||
<string name="your_ice_servers">Ваши ICE серверы</string>
|
||||
<string name="webrtc_ice_servers">WebRTC ICE серверы</string>
|
||||
|
||||
<!-- Call Lock Screen -->
|
||||
<string name="open_simplex_chat_to_accept_call">Откройте <xliff:g id="appNameFull">SimpleX Chat</xliff:g>\nчтобы принять звонок</string>
|
||||
@@ -521,6 +542,8 @@
|
||||
<string name="settings_section_title_socks">SOCKS ПРОКСИ</string>
|
||||
<string name="settings_section_title_icon">ИКОНКА</string>
|
||||
<string name="settings_section_title_themes">ТЕМЫ</string>
|
||||
<string name="settings_section_title_messages">СООБЩЕНИЯ</string>
|
||||
<string name="settings_section_title_calls">ЗВОНКИ</string>
|
||||
<string name="settings_section_title_incognito">Режим Инкогнито</string>
|
||||
|
||||
<!-- DatabaseView.kt -->
|
||||
@@ -645,7 +668,6 @@
|
||||
<string name="alert_message_group_invitation_expired">Приглашение в группу больше не действительно, оно было удалено отправителем.</string>
|
||||
<string name="alert_title_no_group">Группа не найдена!</string>
|
||||
<string name="alert_message_no_group">Эта группа больше не существует.</string>
|
||||
<string name="alert_title_join_group_error">Ошибка приглашения</string>
|
||||
<string name="alert_title_cant_invite_contacts">Нельзя пригласить контакты!</string>
|
||||
<string name="alert_title_cant_invite_contacts_descr">Вы используете инкогнито профиль для этой группы - чтобы предотвратить раскрытие вашего основного профиля, приглашать контакты не разрешено</string>
|
||||
|
||||
|
||||
@@ -43,12 +43,24 @@
|
||||
<string name="error_setting_network_config">Error updating network configuration</string>
|
||||
|
||||
<!-- API Error Responses - SimpleXAPI.kt -->
|
||||
<string name="connection_timeout">Connection timeout</string>
|
||||
<string name="connection_error">Connection error</string>
|
||||
<string name="network_error_desc">Please check your network connection and try again.</string>
|
||||
<string name="error_sending_message">Error sending message</string>
|
||||
<string name="error_adding_members">Error adding member(s)</string>
|
||||
<string name="error_joining_group">Error joining group</string>
|
||||
<string name="cannot_receive_file">Cannot receive file</string>
|
||||
<string name="sender_cancelled_file_transfer">Sender cancelled file transfer.</string>Error receiving file
|
||||
<string name="error_receiving_file">Error receiving file</string>
|
||||
<string name="error_creating_address">Error creating address</string>
|
||||
<string name="contact_already_exists">Contact already exists</string>
|
||||
<string name="you_are_already_connected_to_vName_via_this_link">You are already connected to <xliff:g id="contactName" example="Alice">%1$s!</xliff:g> via this link.</string>
|
||||
<string name="invalid_connection_link">Invalid connection link</string>
|
||||
<string name="please_check_correct_link_and_maybe_ask_for_a_new_one">Please check that you used the correct link or ask your contact to send you another one.</string>
|
||||
<string name="connection_error_auth">Connection error (AUTH)</string>
|
||||
<string name="connection_error_auth_desc">Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection.</string>
|
||||
<string name="error_accepting_contact_request">Error accepting contact request</string>
|
||||
<string name="sender_may_have_deleted_the_connection_request">Sender may have deleted the connection request.</string>
|
||||
<string name="cannot_delete_contact">Can\'t delete contact!</string>
|
||||
<string name="contact_cannot_be_deleted_as_they_are_in_groups">Contact <xliff:g id="contactName" example="Jane Doe">%1$s!</xliff:g> cannot be deleted, they are a member of the group(s) <xliff:g id="groups" example="[team, chess club]">%2$s</xliff:g>.</string>
|
||||
<string name="error_deleting_contact">Error deleting contact</string>
|
||||
@@ -152,6 +164,9 @@
|
||||
<string name="group_preview_you_are_invited">you are invited to group</string>
|
||||
<string name="group_preview_join_as">join as %s</string>
|
||||
<string name="group_connection_pending">connecting…</string>
|
||||
<string name="tap_to_start_new_chat">Tap to start a new chat</string>
|
||||
<string name="chat_with_developers">Chat with the developers</string>
|
||||
<string name="you_have_no_chats">You have no chats</string>
|
||||
|
||||
<!-- ComposeView.kt, helpers -->
|
||||
<string name="attach">Attach</string>
|
||||
@@ -201,18 +216,17 @@
|
||||
<string name="confirm_verb">Confirm</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="no_details">no details</string>
|
||||
<string name="add_contact">Add contact</string>
|
||||
<string name="add_contact">One-time invitation link</string>
|
||||
<string name="copied">Copied to clipboard</string>
|
||||
|
||||
<!-- NewChatSheet -->
|
||||
<string name="add_contact_or_create_group">Start new chat</string>
|
||||
<string name="create_one_time_link">Create link / QR code</string>
|
||||
<string name="paste_received_link">Connect via received link</string>
|
||||
<string name="share_one_time_link">Create one-time invitation link</string>
|
||||
<string name="connect_via_link_or_qr">Connect via link / QR code</string>
|
||||
<string name="scan_QR_code">Scan QR code</string>
|
||||
<string name="create_group">Create secret group</string>
|
||||
<string name="to_share_with_your_contact">(to share with your contact)</string>
|
||||
<string name="in_person_or_in_video_call__bracketed">(in person or in video call)</string>
|
||||
<string name="paste_received_link_from_clipboard">(paste link from clipboard)</string>
|
||||
<string name="connect_via_link_or_qr_from_clipboard_or_in_person">(scan or paste from clipboard)</string>
|
||||
<string name="only_stored_on_members_devices">(only stored by group members)</string>
|
||||
|
||||
<!-- GetImageView -->
|
||||
@@ -291,8 +305,8 @@
|
||||
<string name="connection_request_sent">Connection request sent!</string>
|
||||
<string name="you_will_be_connected_when_your_connection_request_is_accepted">You will be connected when your connection request is accepted, please wait or check later!</string>
|
||||
<string name="you_will_be_connected_when_your_contacts_device_is_online">You will be connected when your contact\'s device is online, please wait or check later!</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Show QR code for your contact to scan from the app.</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">If you can\'t meet in person, you can <b>show QR code in the video call</b>, or you can share the invitation link via any other channel.</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Your contact can scan it from the app.</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">If you can\'t meet in person, <b>show QR code in the video call</b>, or share the link.</string>
|
||||
<string name="your_chat_profile_will_be_sent_to_your_contact">Your chat profile will be sent\nto your contact</string>
|
||||
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">If you cannot meet in person, you can <b>scan QR code in the video call</b>, or your contact can share an invitation link.</string>
|
||||
<string name="share_invitation_link">Share invitation link</string>
|
||||
@@ -301,9 +315,16 @@
|
||||
|
||||
<!-- PasteToConnect.kt -->
|
||||
<string name="connect_via_link">Connect via link</string>
|
||||
<string name="connect_button">Connect</string>
|
||||
<string name="paste_button">Paste</string>
|
||||
<string name="this_string_is_not_a_connection_link">This string is not a connection link!</string>
|
||||
<string name="you_can_also_connect_by_clicking_the_link">You can also connect by clicking the link. If it opens in the browser, click <b>Open in mobile app</b> button.</string>
|
||||
|
||||
<!-- CreateLinkView.kt -->
|
||||
<string name="create_one_time_link">Create one-time invitation link</string>
|
||||
<string name="one_time_link">One-time invitation link</string>
|
||||
<string name="your_contact_address">Your contact address</string>
|
||||
|
||||
<!-- settings - SettingsView.kt -->
|
||||
<string name="your_settings">Your settings</string>
|
||||
<string name="your_simplex_contact_address">Your <xliff:g id="appName">SimpleX</xliff:g> contact address</string>
|
||||
@@ -319,12 +340,18 @@
|
||||
<string name="smp_servers">SMP servers</string>
|
||||
<string name="install_simplex_chat_for_terminal">Install <xliff:g id="appNameFull">SimpleX Chat</xliff:g> for terminal</string>
|
||||
<string name="use_simplex_chat_servers__question">Use <xliff:g id="appNameFull">SimpleX Chat</xliff:g> servers?</string>
|
||||
<string name="saved_SMP_servers_will_br_removed">Saved SMP servers will be removed.</string>
|
||||
<string name="saved_SMP_servers_will_be_removed">Saved SMP servers will be removed.</string>
|
||||
<string name="your_SMP_servers">Your SMP servers</string>
|
||||
<string name="configure_SMP_servers">Configure SMP servers</string>
|
||||
<string name="using_simplex_chat_servers">Using <xliff:g id="appNameFull">SimpleX Chat</xliff:g> servers.</string>
|
||||
<string name="enter_one_SMP_server_per_line">Enter one SMP server per line:</string>
|
||||
<string name="how_to">How to</string>
|
||||
<string name="saved_ICE_servers_will_be_removed">Saved WebRTC ICE servers will be removed.</string>
|
||||
<string name="your_ICE_servers">Your ICE servers</string>
|
||||
<string name="configure_ICE_servers">Configure ICE servers</string>
|
||||
<string name="enter_one_ICE_server_per_line">ICE servers (one per line)</string>
|
||||
<string name="error_saving_ICE_servers">Error saving ICE servers</string>
|
||||
<string name="ensure_ICE_server_address_are_correct_format_and_unique">Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.</string>
|
||||
<string name="save_servers_button">Save</string>
|
||||
<string name="network_and_servers">Network & servers</string>
|
||||
<string name="network_settings">Advanced network settings</string>
|
||||
@@ -351,9 +378,9 @@
|
||||
<string name="create_address">Create address</string>
|
||||
<string name="delete_address__question">Delete address?</string>
|
||||
<string name="all_your_contacts_will_remain_connected">All your contacts will remain connected.</string>
|
||||
<string name="your_chat_address">Your chat address</string>
|
||||
<string name="you_can_share_your_address_anybody_will_be_able_to_connect">You can share your address as a link or as a QR code - anybody will be able to connect to you.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">If you later delete it - you won\'t lose your contacts.</string>
|
||||
<string name="if_you_later_delete_address_you_wont_lose_contacts">If you later delete it - you won\'t lose your contacts made via the address.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">If you delete it - you won\'t lose your contacts made via this address.</string>
|
||||
<string name="share_link">Share link</string>
|
||||
<string name="delete_address">Delete address</string>
|
||||
|
||||
@@ -431,16 +458,7 @@
|
||||
<string name="read_more_in_github_with_link">Read more in our <font color="#0088ff">GitHub repository</font>.</string>
|
||||
|
||||
<!-- MakeConnection -->
|
||||
<string name="to_make_your_first_private_connection_choose">To make your first private connection, choose <b>one of the following</b>:</string>
|
||||
<string name="create_1_time_link_qr_code">Create 1-time link / QR code</string>
|
||||
<string name="it_s_secure_to_share__only_one_contact_can_use_it">It\'s secure to share - only one contact can use it.</string>
|
||||
<string name="paste_the_link_you_received">Paste the link you received</string>
|
||||
<string name="or_open_the_link_in_the_browser_and_tap_open_in_mobile">Or open the link in the browser and tap <b>Open in mobile</b>.</string>
|
||||
<string name="scan_contact_s_qr_code">Scan contact\'s QR code</string>
|
||||
<string name="in_person_or_via_a_video_call__the_most_secure_way_to_connect">In person or via a video call – the most secure way to connect.</string>
|
||||
<string name="or">or</string>
|
||||
<string name="connect_with_the_developers">Connect with the developers</string>
|
||||
<string name="to_ask_any_questions_and_to_receive_simplex_chat_updates">To ask any questions and to receive <xliff:g id="appNameFull">SimpleX Chat</xliff:g> updates.</string>
|
||||
<string name="paste_the_link_you_received">Paste received link</string>
|
||||
|
||||
<!-- Call -->
|
||||
<string name="incoming_video_call">Incoming video call</string>
|
||||
@@ -465,6 +483,8 @@
|
||||
<string name="accept_call_on_lock_screen">Accept</string>
|
||||
<string name="show_call_on_lock_screen">Show</string>
|
||||
<string name="no_call_on_lock_screen">Disable</string>
|
||||
<string name="your_ice_servers">Your ICE servers</string>
|
||||
<string name="webrtc_ice_servers">WebRTC ICE servers</string>
|
||||
|
||||
<!-- Call Lock Screen -->
|
||||
<string name="open_simplex_chat_to_accept_call">Open <xliff:g id="appNameFull">SimpleX Chat</xliff:g> to accept call</string>
|
||||
@@ -522,6 +542,8 @@
|
||||
<string name="settings_section_title_socks">SOCKS PROXY</string>
|
||||
<string name="settings_section_title_icon">APP ICON</string>
|
||||
<string name="settings_section_title_themes">THEMES</string>
|
||||
<string name="settings_section_title_messages">MESSAGES</string>
|
||||
<string name="settings_section_title_calls">CALLS</string>
|
||||
<string name="settings_section_title_incognito">Incognito mode</string>
|
||||
|
||||
<!-- DatabaseView.kt -->
|
||||
@@ -646,7 +668,6 @@
|
||||
<string name="alert_message_group_invitation_expired">Group invitation is no longer valid, it was removed by sender.</string>
|
||||
<string name="alert_title_no_group">Group not found!</string>
|
||||
<string name="alert_message_no_group">This group no longer exists.</string>
|
||||
<string name="alert_title_join_group_error">Error joining group</string>
|
||||
<string name="alert_title_cant_invite_contacts">Can\'t invite contacts!</string>
|
||||
<string name="alert_title_cant_invite_contacts_descr">You\'re using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed</string>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 788 B After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 6.3 KiB |
23
apps/ios/Shared/Assets.xcassets/decentralized.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "decentralized.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "decentralized@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "decentralized@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
apps/ios/Shared/Assets.xcassets/decentralized.imageset/decentralized.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/ios/Shared/Assets.xcassets/decentralized.imageset/decentralized@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
apps/ios/Shared/Assets.xcassets/decentralized.imageset/decentralized@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "60.png",
|
||||
"filename" : "icon-light.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "120.png",
|
||||
"filename" : "icon-light@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "180.png",
|
||||
"filename" : "icon-light@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
|
||||
BIN
apps/ios/Shared/Assets.xcassets/icon-light.imageset/icon-light.png
vendored
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
apps/ios/Shared/Assets.xcassets/icon-light.imageset/icon-light@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
apps/ios/Shared/Assets.xcassets/icon-light.imageset/icon-light@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |