Merge branch 'master' into master-ghc8107

This commit is contained in:
spaced4ndy
2023-11-30 20:56:51 +04:00
32 changed files with 449 additions and 271 deletions
@@ -370,7 +370,6 @@ fun CallInfoView(call: Call, alignment: Alignment.Horizontal) {
InfoText(call.callState.text)
val connInfo = call.connectionInfo
// val connInfoText = if (connInfo == null) "" else " (${connInfo.text}, ${connInfo.protocolText})"
val connInfoText = if (connInfo == null) "" else " (${connInfo.text})"
InfoText(call.encryptionStatus + connInfoText)
}
@@ -585,8 +584,8 @@ fun PreviewActiveCallOverlayVideo() {
localMedia = CallMediaType.Video,
peerMedia = CallMediaType.Video,
connectionInfo = ConnectionInfo(
RTCIceCandidate(RTCIceCandidateType.Host, "tcp", null),
RTCIceCandidate(RTCIceCandidateType.Host, "tcp", null)
RTCIceCandidate(RTCIceCandidateType.Host, "tcp"),
RTCIceCandidate(RTCIceCandidateType.Host, "tcp")
)
),
speakerCanBeEnabled = true,
@@ -611,8 +610,8 @@ fun PreviewActiveCallOverlayAudio() {
localMedia = CallMediaType.Audio,
peerMedia = CallMediaType.Audio,
connectionInfo = ConnectionInfo(
RTCIceCandidate(RTCIceCandidateType.Host, "udp", null),
RTCIceCandidate(RTCIceCandidateType.Host, "udp", null)
RTCIceCandidate(RTCIceCandidateType.Host, "udp"),
RTCIceCandidate(RTCIceCandidateType.Host, "udp")
)
),
speakerCanBeEnabled = true,
@@ -173,6 +173,8 @@ class AppPreferences {
val connectRemoteViaMulticastAuto = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO, true)
val offerRemoteMulticast = mkBoolPreference(SHARED_PREFS_OFFER_REMOTE_MULTICAST, true)
val desktopWindowState = mkStrPreference(SHARED_PREFS_DESKTOP_WINDOW_STATE, null)
private fun mkIntPreference(prefName: String, default: Int) =
SharedPreference(
get = fun() = settings.getInt(prefName, default),
@@ -317,6 +319,7 @@ class AppPreferences {
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST = "ConnectRemoteViaMulticast"
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "ConnectRemoteViaMulticastAuto"
private const val SHARED_PREFS_OFFER_REMOTE_MULTICAST = "OfferRemoteMulticast"
private const val SHARED_PREFS_DESKTOP_WINDOW_STATE = "DesktopWindowState"
}
}
@@ -891,20 +894,21 @@ object ChatController {
return null
}
suspend fun apiConnect(rh: Long?, incognito: Boolean, connReq: String): Boolean {
suspend fun apiConnect(rh: Long?, incognito: Boolean, connReq: String): PendingContactConnection? {
val userId = chatModel.currentUser.value?.userId ?: run {
Log.e(TAG, "apiConnect: no current user")
return false
return null
}
val r = sendCmd(rh, CC.APIConnect(userId, incognito, connReq))
when {
r is CR.SentConfirmation || r is CR.SentInvitation -> return true
r is CR.SentConfirmation -> return r.connection
r is CR.SentInvitation -> return r.connection
r is CR.ContactAlreadyExists -> {
AlertManager.shared.showAlertMsg(
generalGetString(MR.strings.contact_already_exists),
String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), r.contact.displayName)
)
return false
return null
}
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat
&& r.chatError.errorType is ChatErrorType.InvalidConnReq -> {
@@ -912,7 +916,7 @@ object ChatController {
generalGetString(MR.strings.invalid_connection_link),
generalGetString(MR.strings.please_check_correct_link_and_maybe_ask_for_a_new_one)
)
return false
return null
}
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
&& r.chatError.agentError is AgentErrorType.SMP
@@ -921,13 +925,13 @@ object ChatController {
generalGetString(MR.strings.connection_error_auth),
generalGetString(MR.strings.connection_error_auth_desc)
)
return false
return null
}
else -> {
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiConnect", generalGetString(MR.strings.connection_error), r)
}
return false
return null
}
}
}
@@ -1526,16 +1530,6 @@ object ChatController {
fun active(user: UserLike): Boolean = activeUser(rhId, user)
chatModel.addTerminalItem(TerminalItem.resp(rhId, r))
when (r) {
is CR.NewContactConnection -> {
if (active(r.user)) {
chatModel.updateContactConnection(rhId, r.connection)
}
}
is CR.ContactConnectionDeleted -> {
if (active(r.user)) {
chatModel.removeChat(rhId, r.connection.id)
}
}
is CR.ContactDeletedByContact -> {
if (active(r.user) && r.contact.directOrUsed) {
chatModel.updateContact(rhId, r.contact)
@@ -3707,8 +3701,8 @@ sealed class CR {
@Serializable @SerialName("invitation") class Invitation(val user: UserRef, val connReqInvitation: String, val connection: PendingContactConnection): CR()
@Serializable @SerialName("connectionIncognitoUpdated") class ConnectionIncognitoUpdated(val user: UserRef, val toConnection: PendingContactConnection): CR()
@Serializable @SerialName("connectionPlan") class CRConnectionPlan(val user: UserRef, val connectionPlan: ConnectionPlan): CR()
@Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: UserRef): CR()
@Serializable @SerialName("sentInvitation") class SentInvitation(val user: UserRef): CR()
@Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: UserRef, val connection: PendingContactConnection): CR()
@Serializable @SerialName("sentInvitation") class SentInvitation(val user: UserRef, val connection: PendingContactConnection): CR()
@Serializable @SerialName("sentInvitationToContact") class SentInvitationToContact(val user: UserRef, val contact: Contact, val customUserProfile: Profile?): CR()
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("contactRequestAlreadyAccepted") class ContactRequestAlreadyAccepted(val user: UserRef, val contact: Contact): CR()
@@ -3802,7 +3796,6 @@ sealed class CR {
@Serializable @SerialName("callAnswer") class CallAnswer(val user: UserRef, val contact: Contact, val answer: WebRTCSession): CR()
@Serializable @SerialName("callExtraInfo") class CallExtraInfo(val user: UserRef, val contact: Contact, val extraInfo: WebRTCExtraInfo): CR()
@Serializable @SerialName("callEnded") class CallEnded(val user: UserRef, val contact: Contact): CR()
@Serializable @SerialName("newContactConnection") class NewContactConnection(val user: UserRef, val connection: PendingContactConnection): CR()
@Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val user: UserRef, val connection: PendingContactConnection): CR()
// remote events (desktop)
@Serializable @SerialName("remoteHostList") class RemoteHostList(val remoteHosts: List<RemoteHostInfo>): CR()
@@ -3951,7 +3944,6 @@ sealed class CR {
is CallAnswer -> "callAnswer"
is CallExtraInfo -> "callExtraInfo"
is CallEnded -> "callEnded"
is NewContactConnection -> "newContactConnection"
is ContactConnectionDeleted -> "contactConnectionDeleted"
is RemoteHostList -> "remoteHostList"
is CurrentRemoteHost -> "currentRemoteHost"
@@ -4006,11 +3998,11 @@ sealed class CR {
is ContactCode -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionCode: $connectionCode")
is GroupMemberCode -> withUser(user, "groupInfo: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionCode: $connectionCode")
is ConnectionVerified -> withUser(user, "verified: $verified\nconnectionCode: $expectedCode")
is Invitation -> withUser(user, connReqInvitation)
is Invitation -> withUser(user, "connReqInvitation: $connReqInvitation\nconnection: $connection")
is ConnectionIncognitoUpdated -> withUser(user, json.encodeToString(toConnection))
is CRConnectionPlan -> withUser(user, json.encodeToString(connectionPlan))
is SentConfirmation -> withUser(user, noDetails())
is SentInvitation -> withUser(user, noDetails())
is SentConfirmation -> withUser(user, json.encodeToString(connection))
is SentInvitation -> withUser(user, json.encodeToString(connection))
is SentInvitationToContact -> withUser(user, json.encodeToString(contact))
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
is ContactRequestAlreadyAccepted -> withUser(user, json.encodeToString(contact))
@@ -4098,7 +4090,6 @@ sealed class CR {
is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}")
is CallExtraInfo -> withUser(user, "contact: ${contact.id}\nextraInfo: ${json.encodeToString(extraInfo)}")
is CallEnded -> withUser(user, "contact: ${contact.id}")
is NewContactConnection -> withUser(user, json.encodeToString(connection))
is ContactConnectionDeleted -> withUser(user, json.encodeToString(connection))
// remote events (mobile)
is RemoteHostList -> json.encodeToString(remoteHosts)
@@ -127,18 +127,10 @@ sealed class WCallResponse {
"${local?.value ?: "unknown"} / ${remote?.value ?: "unknown"}"
}
}
val protocolText: String get() {
val local = localCandidate?.protocol?.uppercase(Locale.ROOT) ?: "unknown"
val localRelay = localCandidate?.relayProtocol?.uppercase(Locale.ROOT) ?: "unknown"
val remote = remoteCandidate?.protocol?.uppercase(Locale.ROOT) ?: "unknown"
val localText = if (localRelay == local || localCandidate?.relayProtocol == null) local else "$local ($localRelay)"
return if (local == remote) localText else "$localText / $remote"
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate
@Serializable data class RTCIceCandidate(val candidateType: RTCIceCandidateType?, val protocol: String?, val relayProtocol: String?)
@Serializable data class RTCIceCandidate(val candidateType: RTCIceCandidateType?, val protocol: String?)
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer
@Serializable data class RTCIceServer(val urls: List<String>, val username: String? = null, val credential: String? = null)
@@ -108,6 +108,7 @@ private fun createInvitation(
withApi {
val r = m.controller.apiAddContact(rhId, incognito = m.controller.appPrefs.incognito.get())
if (r != null) {
m.updateContactConnection(rhId, r.second)
connReqInvitation.value = r.first
contactConnection.value = r.second
} else {
@@ -283,10 +283,11 @@ suspend fun connectViaUri(
incognito: Boolean,
connectionPlan: ConnectionPlan?,
close: (() -> Unit)?
): Boolean {
val r = chatModel.controller.apiConnect(rhId, incognito, uri.toString())
) {
val pcc = chatModel.controller.apiConnect(rhId, incognito, uri.toString())
val connLinkType = if (connectionPlan != null) planToConnectionLinkType(connectionPlan) else ConnectionLinkType.INVITATION
if (r) {
if (pcc != null) {
chatModel.updateContactConnection(rhId, pcc)
close?.invoke()
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.connection_request_sent),
@@ -299,7 +300,6 @@ suspend fun connectViaUri(
hostDevice = hostDevice(rhId),
)
}
return r
}
fun planToConnectionLinkType(connectionPlan: ConnectionPlan): ConnectionLinkType {
@@ -15,7 +15,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import chat.simplex.common.model.ChatController
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.desktopPlatform
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.common.views.TerminalView
@@ -31,10 +30,30 @@ import java.io.File
val simplexWindowState = SimplexWindowState()
fun showApp() = application {
// For some reason on Linux actual width will be 10.dp less after specifying it here. If we specify 1366,
// it will show 1356. But after that we can still update it to 1366 by changing window state. Just making it +10 now here
val width = if (desktopPlatform.isLinux()) 1376.dp else 1366.dp
val windowState = rememberWindowState(placement = WindowPlacement.Floating, width = width, height = 768.dp)
// Creates file if not exists; comes with proper defaults
val state = getStoredWindowState()
val windowState: WindowState = rememberWindowState(
placement = WindowPlacement.Floating,
width = state.width.dp,
height = state.height.dp,
position = WindowPosition(state.x.dp, state.y.dp)
)
LaunchedEffect(
windowState.position.x.value,
windowState.position.y.value,
windowState.size.width.value,
windowState.size.height.value
) {
storeWindowState(WindowPositionSize(
x = windowState.position.x.value.toInt(),
y = windowState.position.y.value.toInt(),
width = windowState.size.width.value.toInt(),
height = windowState.size.height.value.toInt()
))
}
simplexWindowState.windowState = windowState
// Reload all strings in all @Composable's after language change at runtime
if (remember { ChatController.appPrefs.appLanguage.state }.value != "") {
@@ -0,0 +1,36 @@
package chat.simplex.common
import chat.simplex.common.model.json
import chat.simplex.common.platform.appPreferences
import chat.simplex.common.platform.desktopPlatform
import kotlinx.serialization.*
@Serializable
data class WindowPositionSize(
val width: Int = 1366,
val height: Int = 768,
val x: Int = 0,
val y: Int = 0,
)
fun getStoredWindowState(): WindowPositionSize =
try {
val str = appPreferences.desktopWindowState.get()
var state = if (str == null) {
WindowPositionSize()
} else {
json.decodeFromString(str)
}
// For some reason on Linux actual width will be 10.dp less after specifying it here. If we specify 1366,
// it will show 1356. But after that we can still update it to 1366 by changing window state. Just making it +10 now here
if (desktopPlatform.isLinux() && state.width == 1366) {
state = state.copy(width = 1376)
}
state
} catch (e: Throwable) {
WindowPositionSize()
}
fun storeWindowState(state: WindowPositionSize) =
appPreferences.desktopWindowState.set(json.encodeToString(state))
@@ -136,7 +136,6 @@ private fun SendStateUpdates() {
.collect { call ->
val state = call.callState.text
val connInfo = call.connectionInfo
// val connInfoText = if (connInfo == null) "" else " (${connInfo.text}, ${connInfo.protocolText})"
val connInfoText = if (connInfo == null) "" else " (${connInfo.text})"
val description = call.encryptionStatus + connInfoText
chatModel.callCommand.add(WCallCommand.Description(state, description))