android, desktop: connect remote desktop via multicast (#3442)
* android, desktop: connect remote desktop via multicast * changes * string --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
parent
f7903c5c83
commit
b2dbb558f9
@ -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()
|
||||
|
@ -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<SomeRemoteCtrl?, CR.ChatCmdError?> {
|
||||
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<SomeRemoteCtrl?, CR.ChatCmdError?> {
|
||||
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<RemoteCtrlInfo>): 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_)) +
|
||||
|
@ -125,7 +125,7 @@ fun DefaultConfigurableTextField(
|
||||
keyboardType: KeyboardType = KeyboardType.Text,
|
||||
dependsOn: State<Any?>? = 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)
|
||||
|
@ -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<RemoteCtrlInfo>() }
|
||||
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<RemoteCtrlInfo>) {
|
||||
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<Boolean>,
|
||||
deviceName: String,
|
||||
remoteCtrls: SnapshotStateList<RemoteCtrlInfo>,
|
||||
sessionAddress: MutableState<String>,
|
||||
) {
|
||||
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<RemoteCtrlInfo>, 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<RemoteCtrlInfo>) {
|
||||
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<RemoteCtrlInfo>) {
|
||||
withBGApi {
|
||||
val res = controller.listRemoteCtrls()
|
||||
if (res != null) {
|
||||
remoteCtrls.clear()
|
||||
remoteCtrls.addAll(res)
|
||||
}
|
||||
private suspend fun updateRemoteCtrls(remoteCtrls: SnapshotStateList<RemoteCtrlInfo>) {
|
||||
val res = controller.listRemoteCtrls()
|
||||
if (res != null) {
|
||||
remoteCtrls.clear()
|
||||
remoteCtrls.addAll(res)
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,9 +459,34 @@ private fun processDesktopQRCode(sessionAddress: MutableState<String>, resp: Str
|
||||
connectDesktopAddress(sessionAddress, resp)
|
||||
}
|
||||
|
||||
private fun connectDesktopAddress(sessionAddress: MutableState<String>, addr: String) {
|
||||
private fun findKnownDesktop(showConnectScreen: MutableState<Boolean>) {
|
||||
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<String>, rc: RemoteCtrlInfo) {
|
||||
connectDesktop(sessionAddress) {
|
||||
controller.confirmRemoteCtrl(rc.remoteCtrlId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectDesktopAddress(sessionAddress: MutableState<String>, addr: String) {
|
||||
connectDesktop(sessionAddress) {
|
||||
controller.connectRemoteCtrl(addr)
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectDesktop(sessionAddress: MutableState<String>, connect: suspend () -> Pair<SomeRemoteCtrl?, CR.ChatCmdError?>) {
|
||||
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<RemoteCtrlIn
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisconnectButton(onClick: () -> 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<RemoteCtrlInfo>): 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()
|
||||
}
|
||||
}
|
||||
|
@ -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<Boolean>) {
|
||||
}
|
||||
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<Long?>(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
|
||||
|
@ -1670,6 +1670,8 @@
|
||||
<string name="desktop_connection_terminated">Connection terminated</string>
|
||||
<string name="session_code">Session code</string>
|
||||
<string name="connecting_to_desktop">Connecting to desktop</string>
|
||||
<string name="waiting_for_desktop">Waiting for desktop…</string>
|
||||
<string name="found_desktop">Found desktop</string>
|
||||
<string name="connect_to_desktop">Connect to desktop</string>
|
||||
<string name="connected_to_desktop">Connected to desktop</string>
|
||||
<string name="connected_desktop">Connected desktop</string>
|
||||
@ -1681,9 +1683,12 @@
|
||||
<string name="scan_qr_code_from_desktop">Scan QR code from desktop</string>
|
||||
<string name="desktop_address">Desktop address</string>
|
||||
<string name="verify_connections">Verify connections</string>
|
||||
<string name="discover_on_network">Discover on network</string>
|
||||
<string name="discover_on_network">Discover via local network</string>
|
||||
<string name="multicast_discoverable_via_local_network">Discoverable via local network</string>
|
||||
<string name="multicast_connect_automatically">Connect automatically</string>
|
||||
<string name="paste_desktop_address">Paste desktop address</string>
|
||||
<string name="desktop_device">Desktop</string>
|
||||
<string name="not_compatible">Not compatible!</string>
|
||||
|
||||
<!-- Under development -->
|
||||
<string name="in_developing_title">Coming soon!</string>
|
||||
|
Loading…
Reference in New Issue
Block a user