desktop: fix linking mobile and connecting after onboarding. Also fixes other cases when core controller changes in android/desktop. (#6489)

This commit is contained in:
Evgeny
2025-12-10 09:51:03 +00:00
committed by GitHub
parent 2ed76fd386
commit 8089a8c7ef
8 changed files with 40 additions and 20 deletions
@@ -21,7 +21,7 @@ sealed class WriteFileResult {
* */
fun writeCryptoFile(path: String, data: ByteArray): CryptoFileArgs {
val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized")
val ctrl = ChatController.getChatCtrl() ?: throw Exception("Controller is not initialized")
val buffer = ByteBuffer.allocateDirect(data.size)
buffer.put(data)
buffer.rewind()
@@ -44,7 +44,7 @@ fun readCryptoFile(path: String, cryptoArgs: CryptoFileArgs): ByteArray {
}
fun encryptCryptoFile(fromPath: String, toPath: String): CryptoFileArgs {
val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized")
val ctrl = ChatController.getChatCtrl() ?: throw Exception("Controller is not initialized")
val str = chatEncryptFile(ctrl, fromPath, toPath)
val d = json.decodeFromString(WriteFileResult.serializer(), str)
return when (d) {
@@ -482,17 +482,26 @@ class AppPreferences {
private const val MESSAGE_TIMEOUT: Int = 300_000_000
object ChatController {
var ctrl: ChatCtrl? = -1
private var chatCtrl: ChatCtrl? = -1
val appPrefs: AppPreferences by lazy { AppPreferences() }
val messagesChannel: Channel<API> = Channel()
val chatModel = ChatModel
private var receiverStarted = false
private var receiverJob: Job? = null
var lastMsgReceivedTimestamp: Long = System.currentTimeMillis()
private set
fun hasChatCtrl() = ctrl != -1L && ctrl != null
fun hasChatCtrl() = chatCtrl != -1L && chatCtrl != null
fun getChatCtrl(): ChatCtrl? = chatCtrl
fun setChatCtrl(ctrl: ChatCtrl?) {
val wasRunning = receiverJob != null
stopReceiver()
chatCtrl = ctrl
if (wasRunning && ctrl != null) startReceiver()
}
suspend fun getAgentSubsTotal(rh: Long?): Pair<SMPServerSubs, Boolean>? {
val userId = currentUserId("getAgentSubsTotal")
@@ -639,17 +648,16 @@ object ChatController {
private fun startReceiver() {
Log.d(TAG, "ChatController startReceiver")
if (receiverStarted) return
receiverStarted = true
CoroutineScope(Dispatchers.IO).launch {
if (receiverJob != null || chatCtrl == null) return
receiverJob = CoroutineScope(Dispatchers.IO).launch {
var releaseLock: (() -> Unit) = {}
while (true) {
while (isActive) {
/** Global [ctrl] can be null. It's needed for having the same [ChatModel] that already made in [ChatController] without the need
* to change it everywhere in code after changing a database.
* Since it can be changed in background thread, making this check to prevent NullPointerException */
val ctrl = ctrl
val ctrl = chatCtrl
if (ctrl == null) {
receiverStarted = false
stopReceiver()
break
}
try {
@@ -689,6 +697,15 @@ object ChatController {
}
}
private fun stopReceiver() {
Log.d(TAG, "ChatController stopReceiver")
val job = receiverJob
if (job != null) {
receiverJob = null
job.cancel()
}
}
private suspend fun sendCmdWithRetry(rhId: Long?, cmd: CC, inProgress: MutableState<Boolean>? = null, retryNum: Int = 0): API? {
val r = sendCmd(rhId, cmd, retryNum = retryNum)
val alert = if (r is API.Error) retryableNetworkErrorAlert(r.err) else null
@@ -773,7 +790,7 @@ object ChatController {
}
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")
val ctrl = otherCtrl ?: chatCtrl ?: throw Exception("Controller is not initialized")
return withContext(Dispatchers.IO) {
val c = cmd.cmdString
@@ -56,6 +56,7 @@ fun initChatControllerOnStart() {
}
suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred<Boolean> = { CompletableDeferred(true) }) {
Log.d(TAG, "initChatController")
try {
if (chatModel.ctrlInitInProgress.value) return
chatModel.ctrlInitInProgress.value = true
@@ -92,7 +93,7 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
val ctrl = if (res is DBMigrationResult.OK) {
migrated[1] as Long
} else null
chatController.ctrl = ctrl
chatController.setChatCtrl(ctrl)
chatModel.chatDbEncrypted.value = dbKey != ""
chatModel.chatDbStatus.value = res
if (res != DBMigrationResult.OK) {
@@ -206,7 +207,7 @@ fun chatInitControllerRemovingDatabases() {
}.getOrElse { DBMigrationResult.Unknown(migrated[0] as String) }
val ctrl = migrated[1] as Long
chatController.ctrl = ctrl
chatController.setChatCtrl(ctrl)
// We need only controller, not databases
File(dbPath + "_chat.db").delete()
File(dbPath + "_agent.db").delete()
@@ -804,7 +804,8 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) {
LocalAppColors provides rememberedAppColors,
LocalAppWallpaper provides rememberedWallpaper,
LocalDensity provides density,
content = content)
content = content
)
}
)
}
@@ -131,7 +131,7 @@ fun UserPicker(
}
LaunchedEffect(Unit) {
// Controller.ctrl can be null when self-destructing activates
if (controller.ctrl != null && controller.ctrl != -1L) {
if (controller.hasChatCtrl()) {
withBGApi {
controller.reloadRemoteHosts()
}
@@ -366,6 +366,7 @@ fun startChat(
chatDbChanged: MutableState<Boolean>,
progressIndicator: MutableState<Boolean>? = null
) {
Log.d(TAG, "startChat")
withLongRunningApi {
try {
progressIndicator?.value = true
@@ -532,7 +533,7 @@ fun deleteChatDatabaseFilesAndState() {
appPrefs.newDatabaseInitialized.set(false)
chatModel.desktopOnboardingRandomPassword.value = false
controller.appPrefs.storeDBPassphrase.set(true)
controller.ctrl = null
controller.setChatCtrl(null)
// Clear sensitive data on screen just in case ModalManager will fail to prevent hiding its modals while database encrypts itself
chatModel.chatId.value = null
@@ -33,7 +33,7 @@ fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) {
}
} else {
val r: LAResult = if (passcode.value == authRequest.password) {
if (authRequest.selfDestruct && sdPassword != null && controller.ctrl == -1L) {
if (authRequest.selfDestruct && sdPassword != null && controller.getChatCtrl() == -1L) {
initChatControllerOnStart()
}
LAResult.Success
@@ -58,7 +58,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (
if (m.chatRunning.value == true) {
stopChatAsync(m)
}
val ctrl = m.controller.ctrl
val ctrl = m.controller.getChatCtrl()
if (ctrl != null && ctrl != -1L) {
/**
* The following sequence can bring a user here:
@@ -633,7 +633,7 @@ private fun MutableState<MigrationToState?>.startDownloading(
private fun MutableState<MigrationToState?>.importArchive(archivePath: String, netCfg: NetCfg, networkProxy: NetworkProxy?) {
withLongRunningApi {
try {
if (ChatController.ctrl == null || ChatController.ctrl == -1L) {
if (!ChatController.hasChatCtrl()) {
chatInitControllerRemovingDatabases()
}
controller.apiDeleteStorage()