diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 18eba71fb9..144f461a97 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2921,7 +2921,7 @@ enum class NotificationPreviewMode { } data class RemoteCtrlSession( - val ctrlAppInfo: CtrlAppInfo, + val ctrlAppInfo: CtrlAppInfo?, val appVersion: String, val sessionState: UIRemoteCtrlSessionState ) { @@ -2939,6 +2939,7 @@ data class RemoteCtrlSession( @Serializable sealed class RemoteCtrlSessionState { @Serializable @SerialName("starting") object Starting: RemoteCtrlSessionState() + @Serializable @SerialName("searching") object Searching: RemoteCtrlSessionState() @Serializable @SerialName("connecting") object Connecting: RemoteCtrlSessionState() @Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val sessionCode: String): RemoteCtrlSessionState() @Serializable @SerialName("connected") data class Connected(val sessionCode: String): RemoteCtrlSessionState() @@ -2946,6 +2947,8 @@ sealed class RemoteCtrlSessionState { sealed class UIRemoteCtrlSessionState { @Serializable @SerialName("starting") object Starting: UIRemoteCtrlSessionState() + @Serializable @SerialName("searching") object Searching: UIRemoteCtrlSessionState() + @Serializable @SerialName("found") data class Found(val remoteCtrl: RemoteCtrlInfo, val compatible: Boolean): UIRemoteCtrlSessionState() @Serializable @SerialName("connecting") data class Connecting(val remoteCtrl_: RemoteCtrlInfo? = null): UIRemoteCtrlSessionState() @Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val remoteCtrl_: RemoteCtrlInfo? = null, val sessionCode: String): UIRemoteCtrlSessionState() @Serializable @SerialName("connected") data class Connected(val remoteCtrl: RemoteCtrlInfo, val sessionCode: String): UIRemoteCtrlSessionState() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 5a706efe69..20fa4abf58 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -170,6 +170,7 @@ class AppPreferences { val confirmRemoteSessions = mkBoolPreference(SHARED_PREFS_CONFIRM_REMOTE_SESSIONS, false) val connectRemoteViaMulticast = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST, false) + val connectRemoteViaMulticastAuto = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO, true) val offerRemoteMulticast = mkBoolPreference(SHARED_PREFS_OFFER_REMOTE_MULTICAST, true) private fun mkIntPreference(prefName: String, default: Int) = @@ -314,6 +315,7 @@ class AppPreferences { private const val SHARED_PREFS_DEVICE_NAME_FOR_REMOTE_ACCESS = "DeviceNameForRemoteAccess" private const val SHARED_PREFS_CONFIRM_REMOTE_SESSIONS = "ConfirmRemoteSessions" 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" } } @@ -1432,14 +1434,25 @@ object ChatController { suspend fun connectRemoteCtrl(desktopAddress: String): Pair { val r = sendCmd(null, CC.ConnectRemoteCtrl(desktopAddress)) - if (r is CR.RemoteCtrlConnecting) return SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null - else if (r is CR.ChatCmdError) return null to r - else throw Exception("connectRemoteCtrl error: ${r.responseType} ${r.details}") + return if (r is CR.RemoteCtrlConnecting) SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null + else if (r is CR.ChatCmdError) null to r + else { + apiErrorAlert("connectRemoteCtrl", generalGetString(MR.strings.error_alert_title), r) + null to null + } } suspend fun findKnownRemoteCtrl(): Boolean = sendCommandOkResp(null, CC.FindKnownRemoteCtrl()) - suspend fun confirmRemoteCtrl(rcId: Long): Boolean = sendCommandOkResp(null, CC.ConfirmRemoteCtrl(rcId)) + suspend fun confirmRemoteCtrl(rcId: Long): Pair { + val r = sendCmd(null, CC.ConfirmRemoteCtrl(remoteCtrlId = rcId)) + return if (r is CR.RemoteCtrlConnecting) SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null + else if (r is CR.ChatCmdError) null to r + else { + apiErrorAlert("confirmRemoteCtrl", generalGetString(MR.strings.error_alert_title), r) + null to null + } + } suspend fun verifyRemoteCtrlSession(sessionCode: String): RemoteCtrlInfo? { val r = sendCmd(null, CC.VerifyRemoteCtrlSession(sessionCode)) @@ -1857,8 +1870,15 @@ object ChatController { } } is CR.RemoteCtrlFound -> { - // TODO multicast - Log.d(TAG, "RemoteCtrlFound: ${r.remoteCtrl}") + val sess = chatModel.remoteCtrlSession.value + if (sess != null && sess.sessionState is UIRemoteCtrlSessionState.Searching) { + val state = UIRemoteCtrlSessionState.Found(remoteCtrl = r.remoteCtrl, compatible = r.compatible) + chatModel.remoteCtrlSession.value = RemoteCtrlSession( + ctrlAppInfo = r.ctrlAppInfo_, + appVersion = r.appVersion, + sessionState = state + ) + } } is CR.RemoteCtrlSessionCode -> { val state = UIRemoteCtrlSessionState.PendingConfirmation(remoteCtrl_ = r.remoteCtrl_, sessionCode = r.sessionCode) @@ -1870,7 +1890,13 @@ object ChatController { chatModel.remoteCtrlSession.value = chatModel.remoteCtrlSession.value?.copy(sessionState = state) } is CR.RemoteCtrlStopped -> { - switchToLocalSession() + val sess = chatModel.remoteCtrlSession.value + if (sess != null) { + chatModel.remoteCtrlSession.value = null + if (sess.sessionState is UIRemoteCtrlSessionState.Connected) { + switchToLocalSession() + } + } } else -> Log.d(TAG , "unsupported event: ${r.responseType}") @@ -3782,7 +3808,7 @@ sealed class CR { @Serializable @SerialName("remoteFileStored") class RemoteFileStored(val remoteHostId: Long, val remoteFileSource: CryptoFile): CR() // remote events (mobile) @Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List): CR() - @Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo): CR() + @Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo, val ctrlAppInfo_: CtrlAppInfo?, val appVersion: String, val compatible: Boolean): CR() @Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrl_: RemoteCtrlInfo?, val ctrlAppInfo: CtrlAppInfo, val appVersion: String): CR() @Serializable @SerialName("remoteCtrlSessionCode") class RemoteCtrlSessionCode(val remoteCtrl_: RemoteCtrlInfo?, val sessionCode: String): CR() @Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrl: RemoteCtrlInfo): CR() @@ -4080,7 +4106,11 @@ sealed class CR { is RemoteHostStopped -> "remote host ID: $remoteHostId_" is RemoteFileStored -> "remote host ID: $remoteHostId\nremoteFileSource:\n" + json.encodeToString(remoteFileSource) is RemoteCtrlList -> json.encodeToString(remoteCtrls) - is RemoteCtrlFound -> json.encodeToString(remoteCtrl) + is RemoteCtrlFound -> "remote ctrl: " + json.encodeToString(remoteCtrl) + + "\nctrlAppInfo: " + + (if (ctrlAppInfo_ == null) "null" else json.encodeToString(ctrlAppInfo_)) + + "\nappVersion: $appVersion" + + "\ncompatible: $compatible" is RemoteCtrlConnecting -> "remote ctrl: " + (if (remoteCtrl_ == null) "null" else json.encodeToString(remoteCtrl_)) + diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt index 71801e7a5f..08dfaa0dfb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultBasicTextField.kt @@ -125,7 +125,7 @@ fun DefaultConfigurableTextField( keyboardType: KeyboardType = KeyboardType.Text, dependsOn: State? = null, ) { - var valid by remember { mutableStateOf(validKey(state.value.text)) } + var valid by remember { mutableStateOf(isValid(state.value.text)) } var showKey by remember { mutableStateOf(false) } val icon = if (valid) { if (showKey) painterResource(MR.images.ic_visibility_off_filled) else painterResource(MR.images.ic_visibility_filled) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt index 8d3cfddcec..44e6969b8d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectDesktopView.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -37,6 +38,7 @@ import chat.simplex.common.views.newchat.QRCodeScanner import chat.simplex.common.views.usersettings.PreferenceToggle import chat.simplex.common.views.usersettings.SettingsActionItem import chat.simplex.res.MR +import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource @@ -69,15 +71,21 @@ fun ConnectDesktopView(close: () -> Unit) { @Composable private fun ConnectDesktopLayout(deviceName: String, close: () -> Unit) { + val showConnectScreen = remember { mutableStateOf(true) } val sessionAddress = remember { mutableStateOf("") } val remoteCtrls = remember { mutableStateListOf() } val session = remember { chatModel.remoteCtrlSession }.value Column( Modifier.fillMaxWidth().verticalScroll(rememberScrollState()), ) { - if (session != null) { + val discovery = if (session == null) null else session.sessionState is UIRemoteCtrlSessionState.Searching + if (discovery == true || (discovery == null && !showConnectScreen.value)) { + SearchingDesktop(deviceName, remoteCtrls) + } else if (session != null) { when (session.sessionState) { is UIRemoteCtrlSessionState.Starting -> ConnectingDesktop(session, null) + is UIRemoteCtrlSessionState.Searching -> SearchingDesktop(deviceName, remoteCtrls) + is UIRemoteCtrlSessionState.Found -> FoundDesktop(session, session.sessionState.remoteCtrl, session.sessionState.compatible, remember { controller.appPrefs.connectRemoteViaMulticastAuto.state }, deviceName, remoteCtrls, sessionAddress) is UIRemoteCtrlSessionState.Connecting -> ConnectingDesktop(session, session.sessionState.remoteCtrl_) is UIRemoteCtrlSessionState.PendingConfirmation -> { if (controller.appPrefs.confirmRemoteSessions.get() || session.sessionState.remoteCtrl_ == null) { @@ -97,11 +105,21 @@ private fun ConnectDesktopLayout(deviceName: String, close: () -> Unit) { } SectionBottomSpacer() } - DisposableEffect(Unit) { + LaunchedEffect(Unit) { setDeviceName(deviceName) updateRemoteCtrls(remoteCtrls) + val useMulticast = useMulticast(remoteCtrls) + showConnectScreen.value = !useMulticast + if (chatModel.remoteCtrlSession.value != null) { + disconnectDesktop() + } else if (useMulticast) { + findKnownDesktop(showConnectScreen) + } + } + DisposableEffect(Unit) { onDispose { if (chatModel.remoteCtrlSession.value != null) { + showConnectScreen.value = false disconnectDesktop() } } @@ -146,7 +164,75 @@ private fun ConnectingDesktop(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) { SectionSpacer() SectionView { - DisconnectButton(::disconnectDesktop) + DisconnectButton(onClick = ::disconnectDesktop) + } +} + +@Composable +private fun SearchingDesktop(deviceName: String, remoteCtrls: SnapshotStateList) { + AppBarTitle(stringResource(MR.strings.connecting_to_desktop)) + SectionView(stringResource(MR.strings.this_device_name).uppercase()) { + DevicesView(deviceName, remoteCtrls) { + if (it != "") { + setDeviceName(it) + controller.appPrefs.deviceNameForRemoteAccess.set(it) + } + } + } + SectionDividerSpaced() + SectionView(stringResource(MR.strings.found_desktop).uppercase(), padding = PaddingValues(horizontal = DEFAULT_PADDING)) { + Text(stringResource(MR.strings.waiting_for_desktop), fontStyle = FontStyle.Italic) + } + SectionSpacer() + DisconnectButton(stringResource(MR.strings.scan_QR_code).replace('\n', ' '), MR.images.ic_qr_code, ::disconnectDesktop) +} + +@Composable +private fun FoundDesktop( + session: RemoteCtrlSession, + rc: RemoteCtrlInfo, + compatible: Boolean, + connectRemoteViaMulticastAuto: State, + deviceName: String, + remoteCtrls: SnapshotStateList, + sessionAddress: MutableState, +) { + AppBarTitle(stringResource(MR.strings.found_desktop)) + SectionView(stringResource(MR.strings.this_device_name).uppercase()) { + DevicesView(deviceName, remoteCtrls) { + if (it != "") { + setDeviceName(it) + controller.appPrefs.deviceNameForRemoteAccess.set(it) + } + } + } + SectionDividerSpaced() + SectionView(stringResource(MR.strings.found_desktop).uppercase(), padding = PaddingValues(horizontal = DEFAULT_PADDING)) { + CtrlDeviceNameText(session, rc) + CtrlDeviceVersionText(session) + if (!compatible) { + Text(stringResource(MR.strings.not_compatible), color = MaterialTheme.colors.error) + } + } + + SectionSpacer() + + if (compatible) { + SectionItemView({ confirmKnownDesktop(sessionAddress, rc) }) { + Icon(painterResource(MR.images.ic_check), generalGetString(MR.strings.connect_button), tint = MaterialTheme.colors.secondary) + TextIconSpaced(false) + Text(generalGetString(MR.strings.connect_button)) + } + } + + if (!compatible || !connectRemoteViaMulticastAuto.value) { + DisconnectButton(stringResource(MR.strings.cancel_verb), onClick = ::disconnectDesktop) + } + + if (compatible && connectRemoteViaMulticastAuto.value) { + LaunchedEffect(Unit) { + confirmKnownDesktop(sessionAddress, rc) + } } } @@ -174,7 +260,7 @@ private fun VerifySession(session: RemoteCtrlSession, rc: RemoteCtrlInfo?, sessC } SectionView { - DisconnectButton(::disconnectDesktop) + DisconnectButton(onClick = ::disconnectDesktop) } } @@ -182,7 +268,7 @@ private fun VerifySession(session: RemoteCtrlSession, rc: RemoteCtrlInfo?, sessC private fun CtrlDeviceNameText(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) { val newDesktop = annotatedStringResource(MR.strings.new_desktop) val text = remember(rc) { - var t = AnnotatedString(rc?.deviceViewName ?: session.ctrlAppInfo.deviceName) + var t = AnnotatedString(rc?.deviceViewName ?: session.ctrlAppInfo?.deviceName ?: "") if (rc == null) { t = t + AnnotatedString(" ") + newDesktop } @@ -195,7 +281,7 @@ private fun CtrlDeviceNameText(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) private fun CtrlDeviceVersionText(session: RemoteCtrlSession) { val thisDeviceVersion = annotatedStringResource(MR.strings.this_device_version, session.appVersion) val text = remember(session) { - val v = AnnotatedString(session.ctrlAppInfo.appVersionRange.maxVersion) + val v = AnnotatedString(session.ctrlAppInfo?.appVersionRange?.maxVersion ?: "") var t = AnnotatedString("v$v") if (v.text != session.appVersion) { t = t + AnnotatedString(" ") + thisDeviceVersion @@ -243,7 +329,8 @@ private fun SessionCodeText(code: String) { private fun DevicesView(deviceName: String, remoteCtrls: SnapshotStateList, updateDeviceName: (String) -> Unit) { DeviceNameField(deviceName) { updateDeviceName(it) } if (remoteCtrls.isNotEmpty()) { - SectionItemView({ ModalManager.start.showModal { LinkedDesktopsView(remoteCtrls) } }) { + SectionItemView({ ModalManager.start.showModal { LinkedDesktopsView(remoteCtrls) } + }) { Text(generalGetString(MR.strings.linked_desktops)) } } @@ -336,8 +423,13 @@ private fun LinkedDesktopsView(remoteCtrls: SnapshotStateList) { PreferenceToggle(stringResource(MR.strings.verify_connections), remember { controller.appPrefs.confirmRemoteSessions.state }.value) { controller.appPrefs.confirmRemoteSessions.set(it) } - PreferenceToggle(stringResource(MR.strings.discover_on_network), remember { controller.appPrefs.connectRemoteViaMulticast.state }.value && false) { - controller.appPrefs.confirmRemoteSessions.set(it) + PreferenceToggle(stringResource(MR.strings.discover_on_network), remember { controller.appPrefs.connectRemoteViaMulticast.state }.value) { + controller.appPrefs.connectRemoteViaMulticast.set(it) + } + if (remember { controller.appPrefs.connectRemoteViaMulticast.state }.value) { + PreferenceToggle(stringResource(MR.strings.multicast_connect_automatically), remember { controller.appPrefs.connectRemoteViaMulticastAuto.state }.value) { + controller.appPrefs.connectRemoteViaMulticastAuto.set(it) + } } } SectionBottomSpacer() @@ -355,13 +447,11 @@ private fun setDeviceName(name: String) { } } -private fun updateRemoteCtrls(remoteCtrls: SnapshotStateList) { - withBGApi { - val res = controller.listRemoteCtrls() - if (res != null) { - remoteCtrls.clear() - remoteCtrls.addAll(res) - } +private suspend fun updateRemoteCtrls(remoteCtrls: SnapshotStateList) { + val res = controller.listRemoteCtrls() + if (res != null) { + remoteCtrls.clear() + remoteCtrls.addAll(res) } } @@ -369,9 +459,34 @@ private fun processDesktopQRCode(sessionAddress: MutableState, resp: Str connectDesktopAddress(sessionAddress, resp) } -private fun connectDesktopAddress(sessionAddress: MutableState, addr: String) { +private fun findKnownDesktop(showConnectScreen: MutableState) { withBGApi { - val res = controller.connectRemoteCtrl(desktopAddress = addr) + if (controller.findKnownRemoteCtrl()) { + chatModel.remoteCtrlSession.value = RemoteCtrlSession( + ctrlAppInfo = null, + appVersion = "", + sessionState = UIRemoteCtrlSessionState.Searching + ) + showConnectScreen.value = true + } + } +} + +private fun confirmKnownDesktop(sessionAddress: MutableState, rc: RemoteCtrlInfo) { + connectDesktop(sessionAddress) { + controller.confirmRemoteCtrl(rc.remoteCtrlId) + } +} + +private fun connectDesktopAddress(sessionAddress: MutableState, addr: String) { + connectDesktop(sessionAddress) { + controller.connectRemoteCtrl(addr) + } +} + +private fun connectDesktop(sessionAddress: MutableState, connect: suspend () -> Pair) { + withBGApi { + val res = connect() if (res.first != null) { val (rc_, ctrlAppInfo, v) = res.first!! sessionAddress.value = "" @@ -409,18 +524,25 @@ private fun verifyDesktopSessionCode(remoteCtrls: SnapshotStateList Unit) { +private fun DisconnectButton(label: String = generalGetString(MR.strings.disconnect_remote_host), icon: ImageResource = MR.images.ic_close, onClick: () -> Unit) { SectionItemView(onClick) { - Icon(painterResource(MR.images.ic_close), generalGetString(MR.strings.disconnect_remote_host), tint = MaterialTheme.colors.secondary) + Icon(painterResource(icon), label, tint = MaterialTheme.colors.secondary) TextIconSpaced(false) - Text(generalGetString(MR.strings.disconnect_remote_host)) + Text(label) } } +private fun useMulticast(remoteCtrls: List): Boolean = + controller.appPrefs.connectRemoteViaMulticast.get() && remoteCtrls.isNotEmpty() + private fun disconnectDesktop(close: (() -> Unit)? = null) { withBGApi { controller.stopRemoteCtrl() - switchToLocalSession() + if (chatModel.remoteCtrlSession.value?.sessionState is UIRemoteCtrlSessionState.Connected) { + switchToLocalSession() + } else { + chatModel.remoteCtrlSession.value = null + } close?.invoke() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt index a22cbfd9bb..53f0339bee 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/remote/ConnectMobileView.kt @@ -30,6 +30,7 @@ import chat.simplex.common.views.chat.item.ItemAction import chat.simplex.common.views.chatlist.* import chat.simplex.common.views.helpers.* import chat.simplex.common.views.newchat.QRCode +import chat.simplex.common.views.usersettings.PreferenceToggle import chat.simplex.common.views.usersettings.SettingsActionItemWithContent import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource @@ -90,6 +91,9 @@ fun ConnectMobileLayout( SectionView(generalGetString(MR.strings.this_device_name).uppercase()) { DeviceNameField(deviceName.value ?: "") { updateDeviceName(it) } SectionTextFooter(generalGetString(MR.strings.this_device_name_shared_with_mobile)) + PreferenceToggle(stringResource(MR.strings.multicast_discoverable_via_local_network), remember { controller.appPrefs.offerRemoteMulticast.state }.value) { + controller.appPrefs.offerRemoteMulticast.set(it) + } SectionDividerSpaced(maxBottomPadding = false) } SectionView(stringResource(MR.strings.devices).uppercase()) { @@ -266,7 +270,7 @@ private fun showAddingMobileDevice(connecting: MutableState) { } DisposableEffect(Unit) { withBGApi { - val r = chatModel.controller.startRemoteHost(null) + val r = chatModel.controller.startRemoteHost(null, controller.appPrefs.offerRemoteMulticast.get()) if (r != null) { connecting.value = true invitation.value = r.second @@ -309,7 +313,7 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState ) var remoteHostId by rememberSaveable { mutableStateOf(null) } LaunchedEffect(Unit) { - val r = chatModel.controller.startRemoteHost(rh.remoteHostId) + val r = chatModel.controller.startRemoteHost(rh.remoteHostId, controller.appPrefs.offerRemoteMulticast.get()) if (r != null) { val (rh_, inv) = r connecting.value = true diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 44f950c325..22f5d2fbec 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1670,6 +1670,8 @@ Connection terminated Session code Connecting to desktop + Waiting for desktop… + Found desktop Connect to desktop Connected to desktop Connected desktop @@ -1681,9 +1683,12 @@ Scan QR code from desktop Desktop address Verify connections - Discover on network + Discover via local network + Discoverable via local network + Connect automatically Paste desktop address Desktop + Not compatible! Coming soon!