mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-22 19:42:13 +00:00
Merge branch 'master' into master-android
This commit is contained in:
102 files changed
+2000
-965
No files matched your search
@@ -58,8 +58,8 @@ typedef long* chat_ctrl;
|
||||
|
||||
extern char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl);
|
||||
extern char *chat_close_store(chat_ctrl ctrl);
|
||||
extern char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
|
||||
extern char *chat_send_remote_cmd(chat_ctrl ctrl, const int rhId, const char *cmd);
|
||||
extern char *chat_send_cmd_retry(chat_ctrl ctrl, const char *cmd, const int retryNum);
|
||||
extern char *chat_send_remote_cmd_retry(chat_ctrl ctrl, const int rhId, const char *cmd, const int retryNum);
|
||||
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);
|
||||
@@ -102,20 +102,20 @@ Java_chat_simplex_common_platform_CoreKt_chatCloseStore(JNIEnv *env, __unused jc
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendCmd(JNIEnv *env, __unused jclass clazz, jlong controller, jstring msg) {
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendCmdRetry(JNIEnv *env, __unused jclass clazz, jlong controller, jstring msg, jint retryNum) {
|
||||
const char *_msg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE);
|
||||
//jint length = (jint) (*env)->GetStringUTFLength(env, msg);
|
||||
//for (int i = 0; i < length; ++i)
|
||||
// __android_log_print(ANDROID_LOG_ERROR, "simplex", "%d: %02x\n", i, _msg[i]);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_send_cmd((void*)controller, _msg));
|
||||
jstring res = (*env)->NewStringUTF(env, chat_send_cmd_retry((void*)controller, _msg, retryNum));
|
||||
(*env)->ReleaseStringUTFChars(env, msg, _msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendRemoteCmd(JNIEnv *env, __unused jclass clazz, jlong controller, jint rhId, jstring msg) {
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendRemoteCmdRetry(JNIEnv *env, __unused jclass clazz, jlong controller, jint rhId, jstring msg, jint retryNum) {
|
||||
const char *_msg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_send_remote_cmd((void*)controller, rhId, _msg));
|
||||
jstring res = (*env)->NewStringUTF(env, chat_send_remote_cmd_retry((void*)controller, rhId, _msg, retryNum));
|
||||
(*env)->ReleaseStringUTFChars(env, msg, _msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ typedef long* chat_ctrl;
|
||||
|
||||
extern char *chat_migrate_init(const char *path, const char *key, const char *confirm, chat_ctrl *ctrl);
|
||||
extern char *chat_close_store(chat_ctrl ctrl);
|
||||
extern char *chat_send_cmd(chat_ctrl ctrl, const char *cmd);
|
||||
extern char *chat_send_remote_cmd(chat_ctrl ctrl, const int rhId, const char *cmd);
|
||||
extern char *chat_send_cmd_retry(chat_ctrl ctrl, const char *cmd, const int retryNum);
|
||||
extern char *chat_send_remote_cmd_retry(chat_ctrl ctrl, const int rhId, const char *cmd, const int retryNum);
|
||||
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);
|
||||
@@ -115,17 +115,17 @@ Java_chat_simplex_common_platform_CoreKt_chatCloseStore(JNIEnv *env, jclass claz
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendCmd(JNIEnv *env, jclass clazz, jlong controller, jstring msg) {
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendCmdRetry(JNIEnv *env, __unused jclass clazz, jlong controller, jstring msg, jint retryNum) {
|
||||
const char *_msg = encode_to_utf8_chars(env, msg);
|
||||
jstring res = decode_to_utf8_string(env, chat_send_cmd((void*)controller, _msg));
|
||||
jstring res = decode_to_utf8_string(env, chat_send_cmd_retry((void*)controller, _msg, retryNum));
|
||||
(*env)->ReleaseStringUTFChars(env, msg, _msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendRemoteCmd(JNIEnv *env, jclass clazz, jlong controller, jint rhId, jstring msg) {
|
||||
Java_chat_simplex_common_platform_CoreKt_chatSendRemoteCmdRetry(JNIEnv *env, __unused jclass clazz, jlong controller, jint rhId, jstring msg, jint retryNum) {
|
||||
const char *_msg = encode_to_utf8_chars(env, msg);
|
||||
jstring res = decode_to_utf8_string(env, chat_send_remote_cmd((void*)controller, rhId, _msg));
|
||||
jstring res = decode_to_utf8_string(env, chat_send_remote_cmd_retry((void*)controller, rhId, _msg, retryNum));
|
||||
(*env)->ReleaseStringUTFChars(env, msg, _msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
+25
-2
@@ -1288,6 +1288,8 @@ interface SomeChat {
|
||||
val ready: Boolean
|
||||
val chatDeleted: Boolean
|
||||
val nextConnect: Boolean
|
||||
val nextConnectPrepared: Boolean
|
||||
val profileChangeProhibited: Boolean
|
||||
val incognito: Boolean
|
||||
fun featureEnabled(feature: ChatFeature): Boolean
|
||||
val timedMessagesTTL: Int?
|
||||
@@ -1368,6 +1370,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
override val ready get() = contact.ready
|
||||
override val chatDeleted get() = contact.chatDeleted
|
||||
override val nextConnect get() = contact.nextConnect
|
||||
override val nextConnectPrepared get() = contact.nextConnectPrepared
|
||||
override val profileChangeProhibited get() = contact.profileChangeProhibited
|
||||
override val incognito get() = contact.incognito
|
||||
override fun featureEnabled(feature: ChatFeature) = contact.featureEnabled(feature)
|
||||
override val timedMessagesTTL: Int? get() = contact.timedMessagesTTL
|
||||
@@ -1393,6 +1397,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
override val ready get() = groupInfo.ready
|
||||
override val chatDeleted get() = groupInfo.chatDeleted
|
||||
override val nextConnect get() = groupInfo.nextConnect
|
||||
override val nextConnectPrepared get() = groupInfo.nextConnectPrepared
|
||||
override val profileChangeProhibited get() = groupInfo.profileChangeProhibited
|
||||
override val incognito get() = groupInfo.incognito
|
||||
override fun featureEnabled(feature: ChatFeature) = groupInfo.featureEnabled(feature)
|
||||
override val timedMessagesTTL: Int? get() = groupInfo.timedMessagesTTL
|
||||
@@ -1417,6 +1423,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
override val ready get() = noteFolder.ready
|
||||
override val chatDeleted get() = noteFolder.chatDeleted
|
||||
override val nextConnect get() = noteFolder.nextConnect
|
||||
override val nextConnectPrepared get() = noteFolder.nextConnectPrepared
|
||||
override val profileChangeProhibited get() = noteFolder.profileChangeProhibited
|
||||
override val incognito get() = noteFolder.incognito
|
||||
override fun featureEnabled(feature: ChatFeature) = noteFolder.featureEnabled(feature)
|
||||
override val timedMessagesTTL: Int? get() = noteFolder.timedMessagesTTL
|
||||
@@ -1441,6 +1449,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
override val ready get() = contactRequest.ready
|
||||
override val chatDeleted get() = contactRequest.chatDeleted
|
||||
override val nextConnect get() = contactRequest.nextConnect
|
||||
override val nextConnectPrepared get() = contactRequest.nextConnectPrepared
|
||||
override val profileChangeProhibited get() = contactRequest.profileChangeProhibited
|
||||
override val incognito get() = contactRequest.incognito
|
||||
override fun featureEnabled(feature: ChatFeature) = contactRequest.featureEnabled(feature)
|
||||
override val timedMessagesTTL: Int? get() = contactRequest.timedMessagesTTL
|
||||
@@ -1465,6 +1475,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
override val ready get() = contactConnection.ready
|
||||
override val chatDeleted get() = contactConnection.chatDeleted
|
||||
override val nextConnect get() = contactConnection.nextConnect
|
||||
override val nextConnectPrepared get() = contactConnection.nextConnectPrepared
|
||||
override val profileChangeProhibited get() = contactConnection.profileChangeProhibited
|
||||
override val incognito get() = contactConnection.incognito
|
||||
override fun featureEnabled(feature: ChatFeature) = contactConnection.featureEnabled(feature)
|
||||
override val timedMessagesTTL: Int? get() = contactConnection.timedMessagesTTL
|
||||
@@ -1494,6 +1506,8 @@ sealed class ChatInfo: SomeChat, NamedChat {
|
||||
override val ready get() = false
|
||||
override val chatDeleted get() = false
|
||||
override val nextConnect get() = false
|
||||
override val nextConnectPrepared get() = false
|
||||
override val profileChangeProhibited get() = false
|
||||
override val incognito get() = false
|
||||
override fun featureEnabled(feature: ChatFeature) = false
|
||||
override val timedMessagesTTL: Int? get() = null
|
||||
@@ -1688,7 +1702,8 @@ data class Contact(
|
||||
val active get() = contactStatus == ContactStatus.Active
|
||||
override val nextConnect get() = sendMsgToConnect
|
||||
val nextSendGrpInv get() = contactGroupMemberId != null && !contactGrpInvSent
|
||||
val nextConnectPrepared get() = preparedContact != null && (activeConn == null || activeConn.connStatus == ConnStatus.Prepared)
|
||||
override val nextConnectPrepared get() = preparedContact != null && (activeConn == null || activeConn.connStatus == ConnStatus.Prepared)
|
||||
override val profileChangeProhibited get() = activeConn != null
|
||||
val nextAcceptContactRequest get() = contactRequestId != null && (activeConn == null || activeConn.connStatus == ConnStatus.New)
|
||||
val sendMsgToConnect get() = nextSendGrpInv || nextConnectPrepared
|
||||
override val incognito get() = contactConnIncognito
|
||||
@@ -1942,7 +1957,8 @@ data class GroupInfo (
|
||||
override val apiId get() = groupId
|
||||
override val ready get() = membership.memberActive
|
||||
override val nextConnect get() = nextConnectPrepared
|
||||
val nextConnectPrepared = if (preparedGroup != null) !preparedGroup.connLinkStartedConnection else false
|
||||
override val nextConnectPrepared = if (preparedGroup != null) !preparedGroup.connLinkStartedConnection else false
|
||||
override val profileChangeProhibited get() = preparedGroup?.connLinkPreparedConnection ?: false
|
||||
override val chatDeleted get() = false
|
||||
override val incognito get() = membership.memberIncognito
|
||||
override fun featureEnabled(feature: ChatFeature) = when (feature) {
|
||||
@@ -2015,6 +2031,7 @@ data class GroupInfo (
|
||||
@Serializable
|
||||
data class PreparedGroup (
|
||||
val connLinkToConnect: CreatedConnLink,
|
||||
val connLinkPreparedConnection: Boolean,
|
||||
val connLinkStartedConnection: Boolean
|
||||
)
|
||||
|
||||
@@ -2396,6 +2413,8 @@ class NoteFolder(
|
||||
override val chatDeleted get() = false
|
||||
override val ready get() = true
|
||||
override val nextConnect get() = false
|
||||
override val nextConnectPrepared get() = false
|
||||
override val profileChangeProhibited get() = false
|
||||
override val incognito get() = false
|
||||
override fun featureEnabled(feature: ChatFeature) = feature == ChatFeature.Voice
|
||||
override val timedMessagesTTL: Int? get() = null
|
||||
@@ -2432,6 +2451,8 @@ class UserContactRequest (
|
||||
override val chatDeleted get() = false
|
||||
override val ready get() = true
|
||||
override val nextConnect get() = false
|
||||
override val nextConnectPrepared get() = false
|
||||
override val profileChangeProhibited get() = false
|
||||
override val incognito get() = false
|
||||
override fun featureEnabled(feature: ChatFeature) = false
|
||||
override val timedMessagesTTL: Int? get() = null
|
||||
@@ -2473,6 +2494,8 @@ class PendingContactConnection(
|
||||
override val chatDeleted get() = false
|
||||
override val ready get() = false
|
||||
override val nextConnect get() = false
|
||||
override val nextConnectPrepared get() = false
|
||||
override val profileChangeProhibited get() = false
|
||||
override val incognito get() = customUserProfileId != null
|
||||
override fun featureEnabled(feature: ChatFeature) = false
|
||||
override val timedMessagesTTL: Int? get() = null
|
||||
|
||||
+207
-57
@@ -149,8 +149,10 @@ class AppPreferences {
|
||||
val networkHostMode: SharedPreference<HostMode> = mkSafeEnumPreference(SHARED_PREFS_NETWORK_HOST_MODE, HostMode.default)
|
||||
val networkRequiredHostMode = mkBoolPreference(SHARED_PREFS_NETWORK_REQUIRED_HOST_MODE, false)
|
||||
val networkSMPWebPortServers: SharedPreference<SMPWebPortServers> = mkSafeEnumPreference(SHARED_PREFS_NETWORK_SMP_WEB_PORT_SERVERS, SMPWebPortServers.default)
|
||||
val networkTCPConnectTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT, NetCfg.defaults.tcpConnectTimeout, NetCfg.proxyDefaults.tcpConnectTimeout)
|
||||
val networkTCPTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT, NetCfg.defaults.tcpTimeout, NetCfg.proxyDefaults.tcpTimeout)
|
||||
val networkTCPConnectTimeoutBackground = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT_BACKGROUND, NetCfg.defaults.tcpConnectTimeout.backgroundTimeout, NetCfg.proxyDefaults.tcpConnectTimeout.backgroundTimeout)
|
||||
val networkTCPConnectTimeoutInteractive = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT_INTERACTIVE, NetCfg.defaults.tcpConnectTimeout.interactiveTimeout, NetCfg.proxyDefaults.tcpConnectTimeout.interactiveTimeout)
|
||||
val networkTCPTimeoutBackground = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_BACKGROUND, NetCfg.defaults.tcpTimeout.backgroundTimeout, NetCfg.proxyDefaults.tcpTimeout.backgroundTimeout)
|
||||
val networkTCPTimeoutInteractive = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_INTERACTIVE, NetCfg.defaults.tcpTimeout.interactiveTimeout, NetCfg.proxyDefaults.tcpTimeout.interactiveTimeout)
|
||||
val networkTCPTimeoutPerKb = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB, NetCfg.defaults.tcpTimeoutPerKb, NetCfg.proxyDefaults.tcpTimeoutPerKb)
|
||||
val networkRcvConcurrency = mkIntPreference(SHARED_PREFS_NETWORK_RCV_CONCURRENCY, NetCfg.defaults.rcvConcurrency)
|
||||
val networkSMPPingInterval = mkLongPreference(SHARED_PREFS_NETWORK_SMP_PING_INTERVAL, NetCfg.defaults.smpPingInterval)
|
||||
@@ -271,13 +273,14 @@ class AppPreferences {
|
||||
set = fun(value) = settings.putFloat(prefName, value)
|
||||
)
|
||||
|
||||
private fun mkTimeoutPreference(prefName: String, default: Long, proxyDefault: Long): SharedPreference<Long> {
|
||||
val d = if (networkUseSocksProxy.get()) proxyDefault else default
|
||||
return SharedPreference(
|
||||
get = fun() = settings.getLong(prefName, d),
|
||||
private fun mkTimeoutPreference(prefName: String, default: Long, proxyDefault: Long): SharedPreference<Long> =
|
||||
SharedPreference(
|
||||
get = {
|
||||
val d = if (networkUseSocksProxy.get()) proxyDefault else default
|
||||
settings.getLong(prefName, d)
|
||||
},
|
||||
set = fun(value) = settings.putLong(prefName, value)
|
||||
)
|
||||
}
|
||||
|
||||
private fun mkBoolPreference(prefName: String, default: Boolean) =
|
||||
SharedPreference(
|
||||
@@ -402,8 +405,12 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_NETWORK_HOST_MODE = "NetworkHostMode"
|
||||
private const val SHARED_PREFS_NETWORK_REQUIRED_HOST_MODE = "NetworkRequiredHostMode"
|
||||
private const val SHARED_PREFS_NETWORK_SMP_WEB_PORT_SERVERS = "NetworkSMPWebPortServers"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT = "NetworkTCPConnectTimeout"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT = "NetworkTCPTimeout"
|
||||
// private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT = "NetworkTCPConnectTimeout"
|
||||
// private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT = "NetworkTCPTimeout"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT_BACKGROUND = "NetworkTCPConnectTimeoutBackground"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT_INTERACTIVE = "NetworkTCPConnectTimeoutInteractive"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_BACKGROUND = "NetworkTCPTimeoutBackground"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_INTERACTIVE = "NetworkTCPTimeoutInteractive"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
|
||||
private const val SHARED_PREFS_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
|
||||
private const val SHARED_PREFS_NETWORK_SMP_PING_INTERVAL = "NetworkSMPPingInterval"
|
||||
@@ -666,7 +673,87 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun sendCmd(rhId: Long?, cmd: CC, otherCtrl: ChatCtrl? = null, log: Boolean = true): API {
|
||||
private suspend fun sendCmdWithRetry(rhId: Long?, cmd: CC, retryNum: Int = 0): API? {
|
||||
val r = sendCmd(rhId, cmd, retryNum = retryNum)
|
||||
val alert = if (r is API.Error) retryableNetworkErrorAlert(r.err) else null
|
||||
if (alert == null) return r
|
||||
else return suspendCancellableCoroutine { cont ->
|
||||
showRetryAlert(
|
||||
alert,
|
||||
onCancel = {
|
||||
cont.resumeWith(Result.success(null))
|
||||
},
|
||||
onRetry = {
|
||||
withLongRunningApi {
|
||||
cont.resumeWith(
|
||||
runCatching {
|
||||
coroutineScope {
|
||||
sendCmdWithRetry(rhId, cmd, retryNum = retryNum + 1)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
cont.invokeOnCancellation {
|
||||
cont.resumeWith(Result.success(null))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showRetryAlert(alert: Pair<StringResource, String>, onCancel: () -> Unit, onRetry: () -> Unit) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(alert.first),
|
||||
text = alert.second,
|
||||
confirmText = generalGetString(MR.strings.retry_verb),
|
||||
onConfirm = onRetry,
|
||||
onDismiss = onCancel,
|
||||
onDismissRequest = onCancel
|
||||
)
|
||||
}
|
||||
|
||||
private fun retryableNetworkErrorAlert(err: ChatError): Pair<StringResource, String>? {
|
||||
if (err !is ChatError.ChatErrorAgent) return null
|
||||
val e = err.agentError
|
||||
when (e) {
|
||||
is AgentErrorType.BROKER -> {
|
||||
val message = { String.format(generalGetString(MR.strings.network_error_desc), serverHostname(e.brokerAddress)) }
|
||||
return when (e.brokerErr) {
|
||||
is BrokerErrorType.TIMEOUT -> MR.strings.connection_timeout to message()
|
||||
is BrokerErrorType.NETWORK -> MR.strings.connection_error to message()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
is AgentErrorType.SMP ->
|
||||
if (e.smpErr is SMPErrorType.PROXY && e.smpErr.proxyErr is ProxyError.BROKER) {
|
||||
val message = { String.format(generalGetString(MR.strings.smp_proxy_error_connecting), serverHostname(e.serverAddress)) }
|
||||
return when (e.smpErr.proxyErr.brokerErr) {
|
||||
is BrokerErrorType.TIMEOUT -> MR.strings.private_routing_timeout to message()
|
||||
is BrokerErrorType.NETWORK -> MR.strings.private_routing_error to message()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
is AgentErrorType.PROXY ->
|
||||
if (e.proxyErr is ProxyClientError.ProxyProtocolError && e.proxyErr.protocolErr is SMPErrorType.PROXY) {
|
||||
val message = { String.format(generalGetString(MR.strings.proxy_destination_error_failed_to_connect), serverHostname(e.proxyServer), serverHostname(e.relayServer)) }
|
||||
return when (e.proxyErr.protocolErr.proxyErr) {
|
||||
is ProxyError.BROKER ->
|
||||
when (e.proxyErr.protocolErr.proxyErr.brokerErr) {
|
||||
is BrokerErrorType.TIMEOUT -> MR.strings.private_routing_timeout to message()
|
||||
is BrokerErrorType.NETWORK -> MR.strings.private_routing_error to message()
|
||||
else -> null
|
||||
}
|
||||
is ProxyError.NO_SESSION -> MR.strings.private_routing_no_session to message()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun sendCmd(rhId: Long?, cmd: CC, otherCtrl: ChatCtrl? = null, retryNum: Int = 0, log: Boolean = true): API {
|
||||
val ctrl = otherCtrl ?: ctrl ?: throw Exception("Controller is not initialized")
|
||||
|
||||
return withContext(Dispatchers.IO) {
|
||||
@@ -675,7 +762,7 @@ object ChatController {
|
||||
chatModel.addTerminalItem(TerminalItem.cmd(rhId, cmd.obfuscated))
|
||||
Log.d(TAG, "sendCmd: ${cmd.cmdType}")
|
||||
}
|
||||
val rStr = if (rhId == null) chatSendCmd(ctrl, c) else chatSendRemoteCmd(ctrl, rhId.toInt(), c)
|
||||
val rStr = if (rhId == null) chatSendCmdRetry(ctrl, c, retryNum) else chatSendRemoteCmdRetry(ctrl, rhId.toInt(), c, retryNum)
|
||||
// coroutine was cancelled already, no need to process response (helps with apiListMembers - very heavy query in large groups)
|
||||
interruptIfCancelled()
|
||||
val r = json.decodeFromString<API>(rStr)
|
||||
@@ -1208,16 +1295,16 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiContactQueueInfo(rh: Long?, contactId: Long): Pair<RcvMsgInfo?, ServerQueueInfo>? {
|
||||
val r = sendCmd(rh, CC.APIContactQueueInfo(contactId))
|
||||
val r = sendCmdWithRetry(rh, CC.APIContactQueueInfo(contactId))
|
||||
if (r is API.Result && r.res is CR.QueueInfoR) return r.res.rcvMsgInfo to r.res.queueInfo
|
||||
apiErrorAlert("apiContactQueueInfo", generalGetString(MR.strings.error), r)
|
||||
if (r != null) apiErrorAlert("apiContactQueueInfo", generalGetString(MR.strings.error), r)
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiGroupMemberQueueInfo(rh: Long?, groupId: Long, groupMemberId: Long): Pair<RcvMsgInfo?, ServerQueueInfo>? {
|
||||
val r = sendCmd(rh, CC.APIGroupMemberQueueInfo(groupId, groupMemberId))
|
||||
val r = sendCmdWithRetry(rh, CC.APIGroupMemberQueueInfo(groupId, groupMemberId))
|
||||
if (r is API.Result && r.res is CR.QueueInfoR) return r.res.rcvMsgInfo to r.res.queueInfo
|
||||
apiErrorAlert("apiGroupMemberQueueInfo", generalGetString(MR.strings.error), r)
|
||||
if (r != null) apiErrorAlert("apiGroupMemberQueueInfo", generalGetString(MR.strings.error), r)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1293,9 +1380,10 @@ object ChatController {
|
||||
|
||||
suspend fun apiAddContact(rh: Long?, incognito: Boolean): Pair<Pair<CreatedConnLink, PendingContactConnection>?, (() -> Unit)?> {
|
||||
val userId = try { currentUserId("apiAddContact") } catch (e: Exception) { return null to null }
|
||||
val r = sendCmd(rh, CC.APIAddContact(userId, incognito = incognito))
|
||||
val r = sendCmdWithRetry(rh, CC.APIAddContact(userId, incognito = incognito))
|
||||
return when {
|
||||
r is API.Result && r.res is CR.Invitation -> (r.res.connLinkInvitation to r.res.connection) to null
|
||||
r == null -> null to null
|
||||
!(networkErrorAlert(r)) -> null to { apiErrorAlert("apiAddContact", generalGetString(MR.strings.connection_error), r) }
|
||||
else -> null to null
|
||||
}
|
||||
@@ -1311,8 +1399,9 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiChangeConnectionUser(rh: Long?, connId: Long, userId: Long): PendingContactConnection? {
|
||||
val r = sendCmd(rh, CC.ApiChangeConnectionUser(connId, userId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiChangeConnectionUser(connId, userId))
|
||||
if (r is API.Result && r.res is CR.ConnectionUserChanged) return r.res.toConnection
|
||||
if (r == null) return null
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiChangeConnectionUser", generalGetString(MR.strings.error_sending_message), r)
|
||||
}
|
||||
@@ -1321,15 +1410,15 @@ object ChatController {
|
||||
|
||||
suspend fun apiConnectPlan(rh: Long?, connLink: String): Pair<CreatedConnLink, ConnectionPlan>? {
|
||||
val userId = kotlin.runCatching { currentUserId("apiConnectPlan") }.getOrElse { return null }
|
||||
val r = sendCmd(rh, CC.APIConnectPlan(userId, connLink))
|
||||
val r = sendCmdWithRetry(rh, CC.APIConnectPlan(userId, connLink))
|
||||
if (r is API.Result && r.res is CR.CRConnectionPlan) return r.res.connLink to r.res.connectionPlan
|
||||
apiConnectResponseAlert(r)
|
||||
if (r != null) apiConnectResponseAlert(r)
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiConnect(rh: Long?, incognito: Boolean, connLink: CreatedConnLink): PendingContactConnection? {
|
||||
val userId = try { currentUserId("apiConnect") } catch (e: Exception) { return null }
|
||||
val r = sendCmd(rh, CC.APIConnect(userId, incognito, connLink))
|
||||
val r = sendCmdWithRetry(rh, CC.APIConnect(userId, incognito, connLink))
|
||||
when {
|
||||
r is API.Result && r.res is CR.SentConfirmation -> return r.res.connection
|
||||
r is API.Result && r.res is CR.SentInvitation -> return r.res.connection
|
||||
@@ -1338,7 +1427,7 @@ object ChatController {
|
||||
generalGetString(MR.strings.contact_already_exists),
|
||||
String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), r.res.contact.displayName)
|
||||
)
|
||||
else -> apiConnectResponseAlert(r)
|
||||
r != null -> apiConnectResponseAlert(r)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -1426,8 +1515,9 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiConnectPreparedContact(rh: Long?, contactId: Long, incognito: Boolean, msg: MsgContent?): Contact? {
|
||||
val r = sendCmd(rh, CC.APIConnectPreparedContact(contactId, incognito, msg))
|
||||
val r = sendCmdWithRetry(rh, CC.APIConnectPreparedContact(contactId, incognito, msg))
|
||||
if (r is API.Result && r.res is CR.StartedConnectionToContact) return r.res.contact
|
||||
if (r == null) return null
|
||||
Log.e(TAG, "apiConnectPreparedContact bad response: ${r.responseType} ${r.details}")
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiConnectPreparedContact", generalGetString(MR.strings.connection_error), r)
|
||||
@@ -1436,8 +1526,9 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiConnectPreparedGroup(rh: Long?, groupId: Long, incognito: Boolean, msg: MsgContent?): GroupInfo? {
|
||||
val r = sendCmd(rh, CC.APIConnectPreparedGroup(groupId, incognito, msg))
|
||||
val r = sendCmdWithRetry(rh, CC.APIConnectPreparedGroup(groupId, incognito, msg))
|
||||
if (r is API.Result && r.res is CR.StartedConnectionToGroup) return r.res.groupInfo
|
||||
if (r == null) return null
|
||||
Log.e(TAG, "apiConnectPreparedGroup bad response: ${r.responseType} ${r.details}")
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiConnectPreparedGroup", generalGetString(MR.strings.connection_error), r)
|
||||
@@ -1447,8 +1538,9 @@ object ChatController {
|
||||
|
||||
suspend fun apiConnectContactViaAddress(rh: Long?, incognito: Boolean, contactId: Long): Contact? {
|
||||
val userId = try { currentUserId("apiConnectContactViaAddress") } catch (e: Exception) { return null }
|
||||
val r = sendCmd(rh, CC.ApiConnectContactViaAddress(userId, incognito, contactId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiConnectContactViaAddress(userId, incognito, contactId))
|
||||
if (r is API.Result && r.res is CR.SentInvitationToContact) return r.res.contact
|
||||
if (r == null) return null
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiConnectContactViaAddress", generalGetString(MR.strings.connection_error), r)
|
||||
}
|
||||
@@ -1592,8 +1684,9 @@ object ChatController {
|
||||
|
||||
suspend fun apiCreateUserAddress(rh: Long?): CreatedConnLink? {
|
||||
val userId = kotlin.runCatching { currentUserId("apiCreateUserAddress") }.getOrElse { return null }
|
||||
val r = sendCmd(rh, CC.ApiCreateMyAddress(userId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiCreateMyAddress(userId))
|
||||
if (r is API.Result && r.res is CR.UserContactLinkCreated) return r.res.connLinkContact
|
||||
if (r == null) return null
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiCreateUserAddress", generalGetString(MR.strings.error_creating_address), r)
|
||||
}
|
||||
@@ -1602,8 +1695,9 @@ object ChatController {
|
||||
|
||||
suspend fun apiDeleteUserAddress(rh: Long?): User? {
|
||||
val userId = try { currentUserId("apiDeleteUserAddress") } catch (e: Exception) { return null }
|
||||
val r = sendCmd(rh, CC.ApiDeleteMyAddress(userId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiDeleteMyAddress(userId))
|
||||
if (r is API.Result && r.res is CR.UserContactLinkDeleted) return r.res.user.updateRemoteHostId(rh)
|
||||
if (r == null) return null
|
||||
Log.e(TAG, "apiDeleteUserAddress bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
@@ -1623,8 +1717,9 @@ object ChatController {
|
||||
|
||||
suspend fun apiAddMyAddressShortLink(rh: Long?): UserContactLinkRec? {
|
||||
val userId = kotlin.runCatching { currentUserId("apiAddMyAddressShortLink") }.getOrElse { return null }
|
||||
val r = sendCmd(rh, CC.ApiAddMyAddressShortLink(userId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiAddMyAddressShortLink(userId))
|
||||
if (r is API.Result && r.res is CR.UserContactLink) return r.res.contactLink
|
||||
if (r == null) return null
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAddMyAddressShortLink", generalGetString(MR.strings.error_creating_address), r)
|
||||
}
|
||||
@@ -1633,19 +1728,20 @@ object ChatController {
|
||||
|
||||
suspend fun apiSetUserAddressSettings(rh: Long?, settings: AddressSettings): UserContactLinkRec? {
|
||||
val userId = kotlin.runCatching { currentUserId("apiSetUserAddressSettings") }.getOrElse { return null }
|
||||
val r = sendCmd(rh, CC.ApiSetAddressSettings(userId, settings))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiSetAddressSettings(userId, settings))
|
||||
if (r is API.Result && r.res is CR.UserContactLinkUpdated) return r.res.contactLink
|
||||
if (r is API.Error && r.err is ChatError.ChatErrorStore
|
||||
&& r.err.storeError is StoreError.UserContactLinkNotFound
|
||||
) {
|
||||
return null
|
||||
}
|
||||
if (r == null) return null
|
||||
Log.e(TAG, "userAddressAutoAccept bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiAcceptContactRequest(rh: Long?, incognito: Boolean, contactReqId: Long): Contact? {
|
||||
val r = sendCmd(rh, CC.ApiAcceptContact(incognito, contactReqId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiAcceptContact(incognito, contactReqId))
|
||||
return when {
|
||||
r is API.Result && r.res is CR.AcceptingContactRequest -> r.res.contact
|
||||
r is API.Error && r.err is ChatError.ChatErrorAgent
|
||||
@@ -1657,12 +1753,13 @@ object ChatController {
|
||||
)
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
r != null -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAcceptContactRequest", generalGetString(MR.strings.error_accepting_contact_request), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1944,7 +2041,7 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiJoinGroup(rh: Long?, groupId: Long) {
|
||||
val r = sendCmd(rh, CC.ApiJoinGroup(groupId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiJoinGroup(groupId))
|
||||
when {
|
||||
r is API.Result && r.res is CR.UserAcceptedGroupSent ->
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -1965,7 +2062,7 @@ object ChatController {
|
||||
apiErrorAlert("apiJoinGroup", generalGetString(MR.strings.error_joining_group), r)
|
||||
}
|
||||
}
|
||||
else -> apiErrorAlert("apiJoinGroup", generalGetString(MR.strings.error_joining_group), r)
|
||||
r != null -> apiErrorAlert("apiJoinGroup", generalGetString(MR.strings.error_joining_group), r)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2046,8 +2143,9 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiCreateGroupLink(rh: Long?, groupId: Long, memberRole: GroupMemberRole = GroupMemberRole.Member): GroupLink? {
|
||||
val r = sendCmd(rh, CC.APICreateGroupLink(groupId, memberRole))
|
||||
val r = sendCmdWithRetry(rh, CC.APICreateGroupLink(groupId, memberRole))
|
||||
if (r is API.Result && r.res is CR.GroupLinkCreated) return r.res.groupLink
|
||||
if (r == null) return null
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiCreateGroupLink", generalGetString(MR.strings.error_creating_link_for_group), r)
|
||||
}
|
||||
@@ -2064,8 +2162,9 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiDeleteGroupLink(rh: Long?, groupId: Long): Boolean {
|
||||
val r = sendCmd(rh, CC.APIDeleteGroupLink(groupId))
|
||||
val r = sendCmdWithRetry(rh, CC.APIDeleteGroupLink(groupId))
|
||||
if (r is API.Result && r.res is CR.GroupLinkDeleted) return true
|
||||
if (r == null) return false
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiDeleteGroupLink", generalGetString(MR.strings.error_deleting_link_for_group), r)
|
||||
}
|
||||
@@ -2080,8 +2179,9 @@ object ChatController {
|
||||
}
|
||||
|
||||
suspend fun apiAddGroupShortLink(rh: Long?, groupId: Long): GroupLink? {
|
||||
val r = sendCmd(rh, CC.ApiAddGroupShortLink(groupId))
|
||||
val r = sendCmdWithRetry(rh, CC.ApiAddGroupShortLink(groupId))
|
||||
if (r is API.Result && r.res is CR.CRGroupLink) return r.res.groupLink
|
||||
if (r == null) return null
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiAddGroupShortLink", generalGetString(MR.strings.error_creating_link_for_group), r)
|
||||
}
|
||||
@@ -2510,6 +2610,12 @@ object ChatController {
|
||||
chatModel.networkStatuses[s.agentConnId] = s.networkStatus
|
||||
}
|
||||
}
|
||||
is CR.ChatInfoUpdated ->
|
||||
if (active(r.user)) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateChatInfo(rhId, r.chatInfo)
|
||||
}
|
||||
}
|
||||
is CR.NewChatItems -> withBGApi {
|
||||
r.chatItems.forEach { chatItem ->
|
||||
val cInfo = chatItem.chatInfo
|
||||
@@ -3306,8 +3412,14 @@ object ChatController {
|
||||
val smpProxyMode = appPrefs.networkSMPProxyMode.get()
|
||||
val smpProxyFallback = appPrefs.networkSMPProxyFallback.get()
|
||||
val smpWebPortServers = appPrefs.networkSMPWebPortServers.get()
|
||||
val tcpConnectTimeout = appPrefs.networkTCPConnectTimeout.get()
|
||||
val tcpTimeout = appPrefs.networkTCPTimeout.get()
|
||||
val tcpConnectTimeout = NetworkTimeout(
|
||||
backgroundTimeout = appPrefs.networkTCPConnectTimeoutBackground.get(),
|
||||
interactiveTimeout = appPrefs.networkTCPConnectTimeoutInteractive.get()
|
||||
)
|
||||
val tcpTimeout = NetworkTimeout(
|
||||
backgroundTimeout = appPrefs.networkTCPTimeoutBackground.get(),
|
||||
interactiveTimeout = appPrefs.networkTCPTimeoutInteractive.get()
|
||||
)
|
||||
val tcpTimeoutPerKb = appPrefs.networkTCPTimeoutPerKb.get()
|
||||
val rcvConcurrency = appPrefs.networkRcvConcurrency.get()
|
||||
val smpPingInterval = appPrefs.networkSMPPingInterval.get()
|
||||
@@ -3350,8 +3462,10 @@ object ChatController {
|
||||
appPrefs.networkSMPProxyMode.set(cfg.smpProxyMode)
|
||||
appPrefs.networkSMPProxyFallback.set(cfg.smpProxyFallback)
|
||||
appPrefs.networkSMPWebPortServers.set(cfg.smpWebPortServers)
|
||||
appPrefs.networkTCPConnectTimeout.set(cfg.tcpConnectTimeout)
|
||||
appPrefs.networkTCPTimeout.set(cfg.tcpTimeout)
|
||||
appPrefs.networkTCPConnectTimeoutBackground.set(cfg.tcpConnectTimeout.backgroundTimeout)
|
||||
appPrefs.networkTCPConnectTimeoutInteractive.set(cfg.tcpConnectTimeout.interactiveTimeout)
|
||||
appPrefs.networkTCPTimeoutBackground.set(cfg.tcpTimeout.backgroundTimeout)
|
||||
appPrefs.networkTCPTimeoutInteractive.set(cfg.tcpTimeout.interactiveTimeout)
|
||||
appPrefs.networkTCPTimeoutPerKb.set(cfg.tcpTimeoutPerKb)
|
||||
appPrefs.networkRcvConcurrency.set(cfg.rcvConcurrency)
|
||||
appPrefs.networkSMPPingInterval.set(cfg.smpPingInterval)
|
||||
@@ -4502,8 +4616,8 @@ data class NetCfg(
|
||||
val smpProxyMode: SMPProxyMode = SMPProxyMode.default,
|
||||
val smpProxyFallback: SMPProxyFallback = SMPProxyFallback.default,
|
||||
val smpWebPortServers: SMPWebPortServers = SMPWebPortServers.default,
|
||||
val tcpConnectTimeout: Long, // microseconds
|
||||
val tcpTimeout: Long, // microseconds
|
||||
val tcpConnectTimeout: NetworkTimeout,
|
||||
val tcpTimeout: NetworkTimeout,
|
||||
val tcpTimeoutPerKb: Long, // microseconds
|
||||
val rcvConcurrency: Int, // pool size
|
||||
val tcpKeepAlive: KeepAliveOpts? = KeepAliveOpts.defaults,
|
||||
@@ -4522,8 +4636,8 @@ data class NetCfg(
|
||||
val defaults: NetCfg =
|
||||
NetCfg(
|
||||
socksProxy = null,
|
||||
tcpConnectTimeout = 25_000_000,
|
||||
tcpTimeout = 15_000_000,
|
||||
tcpConnectTimeout = NetworkTimeout(backgroundTimeout = 45_000_000, interactiveTimeout = 15_000_000),
|
||||
tcpTimeout = NetworkTimeout(backgroundTimeout = 30_000_000, interactiveTimeout = 10_000_000),
|
||||
tcpTimeoutPerKb = 10_000,
|
||||
rcvConcurrency = 12,
|
||||
smpPingInterval = 1200_000_000
|
||||
@@ -4532,8 +4646,8 @@ data class NetCfg(
|
||||
val proxyDefaults: NetCfg =
|
||||
NetCfg(
|
||||
socksProxy = ":9050",
|
||||
tcpConnectTimeout = 35_000_000,
|
||||
tcpTimeout = 20_000_000,
|
||||
tcpConnectTimeout = NetworkTimeout(backgroundTimeout = 60_000_000, interactiveTimeout = 30_000_000),
|
||||
tcpTimeout = NetworkTimeout(backgroundTimeout = 40_000_000, interactiveTimeout = 20_000_000),
|
||||
tcpTimeoutPerKb = 15_000,
|
||||
rcvConcurrency = 8,
|
||||
smpPingInterval = 1200_000_000
|
||||
@@ -4557,6 +4671,12 @@ data class NetCfg(
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class NetworkTimeout(
|
||||
val backgroundTimeout: Long, // microseconds
|
||||
val interactiveTimeout: Long // microseconds
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class NetworkProxy(
|
||||
val username: String = "",
|
||||
@@ -5959,6 +6079,7 @@ sealed class CR {
|
||||
// TODO remove above
|
||||
@Serializable @SerialName("networkStatus") class NetworkStatusResp(val networkStatus: NetworkStatus, val connections: List<String>): CR()
|
||||
@Serializable @SerialName("networkStatuses") class NetworkStatuses(val user_: UserRef?, val networkStatuses: List<ConnNetworkStatus>): CR()
|
||||
@Serializable @SerialName("chatInfoUpdated") class ChatInfoUpdated(val user: UserRef, val chatInfo: ChatInfo): CR()
|
||||
@Serializable @SerialName("newChatItems") class NewChatItems(val user: UserRef, val chatItems: List<AChatItem>): CR()
|
||||
@Serializable @SerialName("chatItemsStatusesUpdated") class ChatItemsStatusesUpdated(val user: UserRef, val chatItems: List<AChatItem>): CR()
|
||||
@Serializable @SerialName("chatItemUpdated") class ChatItemUpdated(val user: UserRef, val chatItem: AChatItem): CR()
|
||||
@@ -6144,6 +6265,7 @@ sealed class CR {
|
||||
is ContactSubSummary -> "contactSubSummary"
|
||||
is NetworkStatusResp -> "networkStatus"
|
||||
is NetworkStatuses -> "networkStatuses"
|
||||
is ChatInfoUpdated -> "chatInfoUpdated"
|
||||
is NewChatItems -> "newChatItems"
|
||||
is ChatItemsStatusesUpdated -> "chatItemsStatusesUpdated"
|
||||
is ChatItemUpdated -> "chatItemUpdated"
|
||||
@@ -6321,6 +6443,7 @@ sealed class CR {
|
||||
is ContactSubSummary -> withUser(user, json.encodeToString(contactSubscriptions))
|
||||
is NetworkStatusResp -> "networkStatus $networkStatus\nconnections: $connections"
|
||||
is NetworkStatuses -> withUser(user_, json.encodeToString(networkStatuses))
|
||||
is ChatInfoUpdated -> withUser(user, json.encodeToString(chatInfo))
|
||||
is NewChatItems -> withUser(user, chatItems.joinToString("\n") { json.encodeToString(it) })
|
||||
is ChatItemsStatusesUpdated -> withUser(user, chatItems.joinToString("\n") { json.encodeToString(it) })
|
||||
is ChatItemUpdated -> withUser(user, json.encodeToString(chatItem))
|
||||
@@ -6467,17 +6590,6 @@ data class CreatedConnLink(val connFullLink: String, val connShortLink: String?)
|
||||
fun simplexChatUri(short: Boolean): String =
|
||||
if (short) connShortLink ?: simplexChatLink(connFullLink)
|
||||
else simplexChatLink(connFullLink)
|
||||
|
||||
companion object {
|
||||
val nullableStateSaver: Saver<CreatedConnLink?, Pair<String?, String?>> = Saver(
|
||||
save = { link -> link?.connFullLink to link?.connShortLink },
|
||||
restore = { saved ->
|
||||
val connFullLink = saved.first
|
||||
if (connFullLink == null) null
|
||||
else CreatedConnLink(connFullLink = connFullLink, connShortLink = saved.second)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun simplexChatLink(uri: String): String =
|
||||
@@ -6645,7 +6757,45 @@ data class GroupLink(
|
||||
val shortLinkDataSet: Boolean,
|
||||
val groupLinkId: String,
|
||||
val acceptMemberRole: GroupMemberRole
|
||||
)
|
||||
) {
|
||||
companion object {
|
||||
val nullableStateSaver: Saver<GroupLink?, List<Any?>> = Saver(
|
||||
save = { groupLink ->
|
||||
if (groupLink == null) return@Saver null
|
||||
|
||||
val conn = groupLink.connLinkContact
|
||||
val connData = conn.connFullLink to conn.connShortLink
|
||||
|
||||
listOf(
|
||||
groupLink.userContactLinkId,
|
||||
connData,
|
||||
groupLink.shortLinkDataSet,
|
||||
groupLink.groupLinkId,
|
||||
groupLink.acceptMemberRole.name
|
||||
)
|
||||
},
|
||||
restore = { saved ->
|
||||
val list = saved as? List<*> ?: return@Saver null
|
||||
|
||||
val userContactLinkId = list.getOrNull(0) as? Long ?: return@Saver null
|
||||
val connPair = list.getOrNull(1) as? Pair<*, *> ?: return@Saver null
|
||||
val connFullLink = connPair.first as? String ?: return@Saver null
|
||||
val connShortLink = connPair.second as? String
|
||||
val shortLinkDataSet = list.getOrNull(2) as? Boolean ?: return@Saver null
|
||||
val groupLinkId = list.getOrNull(3) as? String ?: return@Saver null
|
||||
val roleName = list.getOrNull(4) as? String ?: return@Saver null
|
||||
|
||||
GroupLink(
|
||||
userContactLinkId = userContactLinkId,
|
||||
connLinkContact = CreatedConnLink(connFullLink, connShortLink),
|
||||
shortLinkDataSet = shortLinkDataSet,
|
||||
groupLinkId = groupLinkId,
|
||||
acceptMemberRole = GroupMemberRole.valueOf(roleName)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class CoreVersionInfo(
|
||||
|
||||
+2
-2
@@ -22,8 +22,8 @@ external fun pipeStdOutToSocket(socketName: String) : Int
|
||||
typealias ChatCtrl = Long
|
||||
external fun chatMigrateInit(dbPath: String, dbKey: String, confirm: String): Array<Any>
|
||||
external fun chatCloseStore(ctrl: ChatCtrl): String
|
||||
external fun chatSendCmd(ctrl: ChatCtrl, msg: String): String
|
||||
external fun chatSendRemoteCmd(ctrl: ChatCtrl, rhId: Int, msg: String): String
|
||||
external fun chatSendCmdRetry(ctrl: ChatCtrl, msg: String, retryNum: Int): String
|
||||
external fun chatSendRemoteCmdRetry(ctrl: ChatCtrl, rhId: Int, msg: String, retryNum: Int): String
|
||||
external fun chatRecvMsg(ctrl: ChatCtrl): String
|
||||
external fun chatRecvMsgWait(ctrl: ChatCtrl, timeout: Int): String
|
||||
external fun chatParseMarkdown(str: String): String
|
||||
|
||||
+2
-1
@@ -729,7 +729,8 @@ private fun connectingText(chatInfo: ChatInfo): String? {
|
||||
|
||||
is ChatInfo.Group ->
|
||||
when (chatInfo.groupInfo.membership.memberStatus) {
|
||||
GroupMemberStatus.MemAccepted -> generalGetString(MR.strings.group_connection_pending) // TODO [short links] add member status to show transition from prepared group to started connection earlier?
|
||||
GroupMemberStatus.MemUnknown -> if (chatInfo.groupInfo.preparedGroup?.connLinkStartedConnection == true) generalGetString(MR.strings.group_connection_pending) else null
|
||||
GroupMemberStatus.MemAccepted -> generalGetString(MR.strings.group_connection_pending)
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
+38
-17
@@ -65,7 +65,7 @@ fun ComposeContextProfilePickerView(
|
||||
Modifier.size(20.dp),
|
||||
tint = MaterialTheme.colors.secondary,
|
||||
)
|
||||
} else {
|
||||
} else if (!chat.chatInfo.profileChangeProhibited) {
|
||||
Icon(
|
||||
painterResource(
|
||||
MR.images.ic_chevron_up
|
||||
@@ -103,14 +103,21 @@ fun ComposeContextProfilePickerView(
|
||||
keepingChatId = chat.id
|
||||
)
|
||||
if (chatModel.currentUser.value?.userId != newUser.userId) {
|
||||
AlertManager.shared.showAlertMsg(generalGetString(
|
||||
MR.strings.switching_profile_error_title),
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.switching_profile_error_title),
|
||||
String.format(generalGetString(MR.strings.switching_profile_error_message), newUser.chatViewName)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showCantChangeProfileAlert() {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.context_user_picker_cant_change_profile_alert_title),
|
||||
generalGetString(MR.strings.context_user_picker_cant_change_profile_alert_message)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ProfilePickerUserOption(user: User) {
|
||||
Row(
|
||||
@@ -118,15 +125,19 @@ fun ComposeContextProfilePickerView(
|
||||
.fillMaxWidth()
|
||||
.sizeIn(minHeight = DEFAULT_MIN_SECTION_ITEM_HEIGHT + 8.dp)
|
||||
.clickable(onClick = {
|
||||
if (selectedUser.value.userId == user.userId) {
|
||||
if (!incognitoDefault) {
|
||||
listExpanded.value = !listExpanded.value
|
||||
if (!chat.chatInfo.profileChangeProhibited) {
|
||||
if (selectedUser.value.userId == user.userId) {
|
||||
if (!incognitoDefault) {
|
||||
listExpanded.value = !listExpanded.value
|
||||
} else {
|
||||
chatModel.controller.appPrefs.incognito.set(false)
|
||||
listExpanded.value = false
|
||||
}
|
||||
} else {
|
||||
chatModel.controller.appPrefs.incognito.set(false)
|
||||
listExpanded.value = false
|
||||
changeProfile(user)
|
||||
}
|
||||
} else {
|
||||
changeProfile(user)
|
||||
showCantChangeProfileAlert()
|
||||
}
|
||||
})
|
||||
.padding(horizontal = DEFAULT_PADDING_HALF, vertical = 4.dp),
|
||||
@@ -156,11 +167,15 @@ fun ComposeContextProfilePickerView(
|
||||
.fillMaxWidth()
|
||||
.sizeIn(minHeight = DEFAULT_MIN_SECTION_ITEM_HEIGHT + 8.dp)
|
||||
.clickable(onClick = {
|
||||
if (incognitoDefault) {
|
||||
listExpanded.value = !listExpanded.value
|
||||
if (!chat.chatInfo.profileChangeProhibited) {
|
||||
if (incognitoDefault) {
|
||||
listExpanded.value = !listExpanded.value
|
||||
} else {
|
||||
chatModel.controller.appPrefs.incognito.set(true)
|
||||
listExpanded.value = false
|
||||
}
|
||||
} else {
|
||||
chatModel.controller.appPrefs.incognito.set(true)
|
||||
listExpanded.value = false
|
||||
showCantChangeProfileAlert()
|
||||
}
|
||||
})
|
||||
.padding(horizontal = DEFAULT_PADDING_HALF, vertical = 4.dp),
|
||||
@@ -265,7 +280,13 @@ fun ComposeContextProfilePickerView(
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
|
||||
if (incognitoDefault) {
|
||||
if (chat.chatInfo.profileChangeProhibited) {
|
||||
if (chat.chatInfo.incognito) {
|
||||
IncognitoOption()
|
||||
} else {
|
||||
ProfilePickerUserOption(selectedUser.value)
|
||||
}
|
||||
} else if (incognitoDefault) {
|
||||
IncognitoOption()
|
||||
} else {
|
||||
ProfilePickerUserOption(selectedUser.value)
|
||||
@@ -273,9 +294,9 @@ fun ComposeContextProfilePickerView(
|
||||
}
|
||||
}
|
||||
|
||||
if (listExpanded.value) {
|
||||
ProfilePicker()
|
||||
} else {
|
||||
if (!listExpanded.value || chat.chatInfo.profileChangeProhibited) {
|
||||
CurrentSelection()
|
||||
} else {
|
||||
ProfilePicker()
|
||||
}
|
||||
}
|
||||
+5
-8
@@ -532,10 +532,11 @@ fun ComposeView(
|
||||
suspend fun sendConnectPreparedContact() {
|
||||
val mc = checkLinkPreview()
|
||||
sending()
|
||||
val incognito = if (chat.chatInfo.profileChangeProhibited) chat.chatInfo.incognito else chatModel.controller.appPrefs.incognito.get()
|
||||
val contact = chatModel.controller.apiConnectPreparedContact(
|
||||
rh = chat.remoteHostId,
|
||||
contactId = chat.chatInfo.apiId,
|
||||
incognito = chatModel.controller.appPrefs.incognito.get(),
|
||||
incognito = incognito,
|
||||
msg = mc
|
||||
)
|
||||
if (contact != null) {
|
||||
@@ -573,10 +574,11 @@ fun ComposeView(
|
||||
suspend fun connectPreparedGroup() {
|
||||
val mc = checkLinkPreview()
|
||||
sending()
|
||||
val incognito = if (chat.chatInfo.profileChangeProhibited) chat.chatInfo.incognito else chatModel.controller.appPrefs.incognito.get()
|
||||
val groupInfo = chatModel.controller.apiConnectPreparedGroup(
|
||||
rh = chat.remoteHostId,
|
||||
groupId = chat.chatInfo.apiId,
|
||||
incognito = chatModel.controller.appPrefs.incognito.get(),
|
||||
incognito = incognito,
|
||||
msg = mc
|
||||
)
|
||||
if (groupInfo != null) {
|
||||
@@ -1328,12 +1330,7 @@ fun ComposeView(
|
||||
|
||||
Column {
|
||||
val currentUser = chatModel.currentUser.value
|
||||
if ((
|
||||
(chat.chatInfo is ChatInfo.Direct && chat.chatInfo.contact.nextConnectPrepared)
|
||||
|| (chat.chatInfo is ChatInfo.Group && chat.chatInfo.groupInfo.nextConnectPrepared)
|
||||
)
|
||||
&& currentUser != null
|
||||
) {
|
||||
if (chat.chatInfo.nextConnectPrepared && currentUser != null) {
|
||||
ComposeContextProfilePickerView(
|
||||
rhId = rhId,
|
||||
chat = chat,
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ fun GroupLinkView(
|
||||
creatingGroup: Boolean = false,
|
||||
close: (() -> Unit)? = null
|
||||
) {
|
||||
var groupLinkVar by rememberSaveable { mutableStateOf(groupLink) }
|
||||
var groupLinkVar by rememberSaveable(stateSaver = GroupLink.nullableStateSaver) { mutableStateOf(groupLink) }
|
||||
val groupLinkMemberRole = rememberSaveable { mutableStateOf(groupLink?.acceptMemberRole) }
|
||||
var creatingLink by rememberSaveable { mutableStateOf(false) }
|
||||
fun createLink() {
|
||||
|
||||
+14
-12
@@ -138,19 +138,21 @@ fun ChatPreviewView(
|
||||
val deleting by remember(disabled, chat.id) { mutableStateOf(chatModel.deletedChats.value.contains(chat.remoteHostId to chat.chatInfo.id)) }
|
||||
when (cInfo) {
|
||||
is ChatInfo.Direct -> {
|
||||
if (cInfo.contact.verified) {
|
||||
VerifiedIcon()
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (cInfo.contact.verified) {
|
||||
VerifiedIcon()
|
||||
}
|
||||
val color = if (deleting)
|
||||
MaterialTheme.colors.secondary
|
||||
else if (cInfo.contact.nextAcceptContactRequest || cInfo.contact.sendMsgToConnect) {
|
||||
MaterialTheme.colors.primary
|
||||
} else if (!cInfo.contact.sndReady) {
|
||||
MaterialTheme.colors.secondary
|
||||
} else {
|
||||
Color.Unspecified
|
||||
}
|
||||
chatPreviewTitleText(color = color)
|
||||
}
|
||||
val color = if (deleting)
|
||||
MaterialTheme.colors.secondary
|
||||
else if (cInfo.contact.nextAcceptContactRequest || cInfo.contact.sendMsgToConnect) {
|
||||
MaterialTheme.colors.primary
|
||||
} else if (!cInfo.contact.sndReady) {
|
||||
MaterialTheme.colors.secondary
|
||||
} else {
|
||||
Color.Unspecified
|
||||
}
|
||||
chatPreviewTitleText(color = color)
|
||||
}
|
||||
is ChatInfo.Group -> {
|
||||
val color = if (deleting) {
|
||||
|
||||
+44
-16
@@ -48,8 +48,10 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
|
||||
val smpProxyFallback = remember { mutableStateOf(currentCfgVal.smpProxyFallback) }
|
||||
val smpWebPortServers = remember { mutableStateOf(currentCfgVal.smpWebPortServers) }
|
||||
|
||||
val networkTCPConnectTimeout = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout) }
|
||||
val networkTCPTimeout = remember { mutableStateOf(currentCfgVal.tcpTimeout) }
|
||||
val networkTCPConnectTimeoutInteractive = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout.interactiveTimeout) }
|
||||
val networkTCPConnectTimeoutBackground = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout.backgroundTimeout) }
|
||||
val networkTCPTimeoutInteractive = remember { mutableStateOf(currentCfgVal.tcpTimeout.interactiveTimeout) }
|
||||
val networkTCPTimeoutBackground = remember { mutableStateOf(currentCfgVal.tcpTimeout.backgroundTimeout) }
|
||||
val networkTCPTimeoutPerKb = remember { mutableStateOf(currentCfgVal.tcpTimeoutPerKb) }
|
||||
val networkRcvConcurrency = remember { mutableStateOf(currentCfgVal.rcvConcurrency) }
|
||||
val networkSMPPingInterval = remember { mutableStateOf(currentCfgVal.smpPingInterval) }
|
||||
@@ -86,8 +88,14 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
|
||||
smpProxyMode = smpProxyMode.value,
|
||||
smpProxyFallback = smpProxyFallback.value,
|
||||
smpWebPortServers = smpWebPortServers.value,
|
||||
tcpConnectTimeout = networkTCPConnectTimeout.value,
|
||||
tcpTimeout = networkTCPTimeout.value,
|
||||
tcpConnectTimeout = NetworkTimeout(
|
||||
backgroundTimeout = networkTCPConnectTimeoutBackground.value,
|
||||
interactiveTimeout = networkTCPConnectTimeoutInteractive.value
|
||||
) ,
|
||||
tcpTimeout = NetworkTimeout(
|
||||
backgroundTimeout = networkTCPTimeoutBackground.value,
|
||||
interactiveTimeout = networkTCPTimeoutInteractive.value
|
||||
),
|
||||
tcpTimeoutPerKb = networkTCPTimeoutPerKb.value,
|
||||
rcvConcurrency = networkRcvConcurrency.value,
|
||||
tcpKeepAlive = tcpKeepAlive,
|
||||
@@ -101,8 +109,10 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
|
||||
smpProxyMode.value = cfg.smpProxyMode
|
||||
smpProxyFallback.value = cfg.smpProxyFallback
|
||||
smpWebPortServers.value = cfg.smpWebPortServers
|
||||
networkTCPConnectTimeout.value = cfg.tcpConnectTimeout
|
||||
networkTCPTimeout.value = cfg.tcpTimeout
|
||||
networkTCPConnectTimeoutInteractive.value = cfg.tcpConnectTimeout.interactiveTimeout
|
||||
networkTCPConnectTimeoutBackground.value = cfg.tcpConnectTimeout.backgroundTimeout
|
||||
networkTCPTimeoutInteractive.value = cfg.tcpTimeout.interactiveTimeout
|
||||
networkTCPTimeoutBackground.value = cfg.tcpTimeout.backgroundTimeout
|
||||
networkTCPTimeoutPerKb.value = cfg.tcpTimeoutPerKb
|
||||
networkRcvConcurrency.value = cfg.rcvConcurrency
|
||||
networkSMPPingInterval.value = cfg.smpPingInterval
|
||||
@@ -156,8 +166,10 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
|
||||
smpProxyMode = smpProxyMode,
|
||||
smpProxyFallback = smpProxyFallback,
|
||||
smpWebPortServers,
|
||||
networkTCPConnectTimeout,
|
||||
networkTCPTimeout,
|
||||
networkTCPConnectTimeoutInteractive,
|
||||
networkTCPConnectTimeoutBackground,
|
||||
networkTCPTimeoutInteractive,
|
||||
networkTCPTimeoutBackground,
|
||||
networkTCPTimeoutPerKb,
|
||||
networkRcvConcurrency,
|
||||
networkSMPPingInterval,
|
||||
@@ -189,8 +201,10 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
|
||||
smpProxyMode: MutableState<SMPProxyMode>,
|
||||
smpProxyFallback: MutableState<SMPProxyFallback>,
|
||||
smpWebPortServers: MutableState<SMPWebPortServers>,
|
||||
networkTCPConnectTimeout: MutableState<Long>,
|
||||
networkTCPTimeout: MutableState<Long>,
|
||||
networkTCPConnectTimeoutInteractive: MutableState<Long>,
|
||||
networkTCPConnectTimeoutBackground: MutableState<Long>,
|
||||
networkTCPTimeoutInteractive: MutableState<Long>,
|
||||
networkTCPTimeoutBackground: MutableState<Long>,
|
||||
networkTCPTimeoutPerKb: MutableState<Long>,
|
||||
networkRcvConcurrency: MutableState<Int>,
|
||||
networkSMPPingInterval: MutableState<Long>,
|
||||
@@ -242,14 +256,26 @@ fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> U
|
||||
SectionView(stringResource(MR.strings.network_option_tcp_connection).uppercase()) {
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeout,
|
||||
listOf(10_000000, 15_000000, 20_000000, 30_000000, 45_000000, 60_000000, 90_000000), secondsLabel
|
||||
stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeoutInteractive,
|
||||
listOf(10_000000, 15_000000, 20_000000, 30_000000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_protocol_timeout), networkTCPTimeout,
|
||||
listOf(5_000000, 7_000000, 10_000000, 15_000000, 20_000_000, 30_000_000), secondsLabel
|
||||
stringResource(MR.strings.network_option_tcp_connection_timeout_background), networkTCPConnectTimeoutBackground,
|
||||
listOf(30_000000, 45_000000, 60_000000, 90_000000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_protocol_timeout), networkTCPTimeoutInteractive,
|
||||
listOf(5_000000, 7_000000, 10_000000, 15_000000, 20_000_000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_protocol_timeout_background), networkTCPTimeoutBackground,
|
||||
listOf(15_000000, 20_000000, 30_000000, 45_000000, 60_000_000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
@@ -555,8 +581,10 @@ fun PreviewAdvancedNetworkSettingsLayout() {
|
||||
smpProxyMode = remember { mutableStateOf(SMPProxyMode.Never) },
|
||||
smpProxyFallback = remember { mutableStateOf(SMPProxyFallback.Allow) },
|
||||
smpWebPortServers = remember { mutableStateOf(SMPWebPortServers.Preset) },
|
||||
networkTCPConnectTimeout = remember { mutableStateOf(10_000000) },
|
||||
networkTCPTimeout = remember { mutableStateOf(10_000000) },
|
||||
networkTCPConnectTimeoutInteractive = remember { mutableStateOf(15_000000) },
|
||||
networkTCPConnectTimeoutBackground = remember { mutableStateOf(45_000000) },
|
||||
networkTCPTimeoutInteractive = remember { mutableStateOf(10_000000) },
|
||||
networkTCPTimeoutBackground = remember { mutableStateOf(30_000000) },
|
||||
networkTCPTimeoutPerKb = remember { mutableStateOf(10_000) },
|
||||
networkRcvConcurrency = remember { mutableStateOf(8) },
|
||||
networkSMPPingInterval = remember { mutableStateOf(10_000000) },
|
||||
|
||||
@@ -147,7 +147,9 @@
|
||||
<string name="network_error_desc">Please check your network connection with %1$s and try again.</string>
|
||||
<string name="network_error_broker_host_desc">Server address is incompatible with network settings: %1$s.</string>
|
||||
<string name="network_error_broker_version_desc">Server version is incompatible with your app: %1$s.</string>
|
||||
<string name="private_routing_timeout">Private routing timeout</string>
|
||||
<string name="private_routing_error">Private routing error</string>
|
||||
<string name="private_routing_no_session">No private routing session</string>
|
||||
<string name="smp_proxy_error_connecting">Error connecting to forwarding server %1$s. Please try later.</string>
|
||||
<string name="smp_proxy_error_broker_host">Forwarding server address is incompatible with network settings: %1$s.</string>
|
||||
<string name="smp_proxy_error_broker_version">Forwarding server version is incompatible with network settings: %1$s.</string>
|
||||
@@ -870,6 +872,8 @@
|
||||
|
||||
<!-- ComposeContextProfilePickerView.kt -->
|
||||
<string name="context_user_picker_your_profile">Your profile</string>
|
||||
<string name="context_user_picker_cant_change_profile_alert_title">Can\'t change profile</string>
|
||||
<string name="context_user_picker_cant_change_profile_alert_message">To use another profile after connection attempt, delete the chat and use the link again.</string>
|
||||
|
||||
<!-- ScanCodeView.kt -->
|
||||
<string name="scan_code">Scan code</string>
|
||||
@@ -1974,7 +1978,9 @@
|
||||
<string name="network_options_reset_to_defaults">Reset to defaults</string>
|
||||
<string name="network_option_seconds_label">sec</string>
|
||||
<string name="network_option_tcp_connection_timeout">TCP connection timeout</string>
|
||||
<string name="network_option_tcp_connection_timeout_background">TCP connection bg timeout</string>
|
||||
<string name="network_option_protocol_timeout">Protocol timeout</string>
|
||||
<string name="network_option_protocol_timeout_background">Protocol background timeout</string>
|
||||
<string name="network_option_protocol_timeout_per_kb">Protocol timeout per KB</string>
|
||||
<string name="network_option_rcv_concurrency">Receiving concurrency</string>
|
||||
<string name="network_option_ping_interval">PING interval</string>
|
||||
|
||||
@@ -24,13 +24,13 @@ android.nonTransitiveRClass=true
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
kotlin.jvm.target=11
|
||||
|
||||
android.version_name=6.4-beta.3
|
||||
android.version_code=297
|
||||
android.version_name=6.4-beta.4
|
||||
android.version_code=300
|
||||
|
||||
android.bundle=false
|
||||
|
||||
desktop.version_name=6.4-beta.3
|
||||
desktop.version_code=107
|
||||
desktop.version_name=6.4-beta.4
|
||||
desktop.version_code=109
|
||||
|
||||
kotlin.version=1.9.23
|
||||
gradle.plugin.version=8.2.0
|
||||
|
||||
Reference in new issue
Block a user