diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt index 2d940c067c..3487ad9305 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt @@ -4,6 +4,10 @@ import android.app.Application import android.net.LocalServerSocket import android.util.Log import chat.simplex.app.model.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.InputStreamReader import java.lang.ref.WeakReference @@ -28,17 +32,21 @@ class SimplexApp: Application() { override fun onCreate() { super.onCreate() controller = ChatController(chatInit(applicationContext.filesDir.toString())) - var user = controller.apiGetActiveUser() - if (user == null) { + GlobalScope.launch { + withContext(Dispatchers.Main) { + var user = controller.apiGetActiveUser() + if (user == null) { // user = controller.apiCreateActiveUser(Profile("android", "Android test")) - } - Log.d("SIMPLEX (user)", user.toString()) - try { - controller.apiStartChat() - Log.d("SIMPLEX", "started chat") - } catch(e: Error) { - Log.d("SIMPLEX", "failed starting chat $e") + } + Log.d("SIMPLEX (user)", user.toString()) + try { + controller.apiStartChat() + Log.d("SIMPLEX", "started chat") + } catch(e: Error) { + Log.d("SIMPLEX", "failed starting chat $e") // throw e + } + } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt index 1dec8dabb6..82a514221e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SImpleXAPI.kt @@ -3,18 +3,20 @@ package chat.simplex.app.model import android.util.Log import chat.simplex.app.chatRecvMsg import chat.simplex.app.chatSendCmd +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import kotlinx.serialization.* import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive import java.lang.Exception import java.util.* import kotlin.concurrent.thread -typealias Controller = Long +typealias ChatCtrl = Long -open class ChatController(val ctrl: Controller) { +open class ChatController(val ctrl: ChatCtrl) { private var chatModel: ChatModel? = null + private val callbacks = mutableMapOf Unit>() fun setModel(m: ChatModel) { chatModel = m @@ -26,34 +28,46 @@ open class ChatController(val ctrl: Controller) { while(true) { val json = chatRecvMsg(ctrl) Log.d("SIMPLEX chatRecvMsg: ", json) - chatModel?.terminalItems?.add(TerminalItem.Resp(APIResponse.decodeStr(json))) + val r = APIResponse.decodeStr(json) + if (r.corr != null) { + val cb = callbacks[r.corr] + if (cb != null) cb(null, r.resp) + } + chatModel?.terminalItems?.add(TerminalItem.Resp(r.resp)) } } } - fun sendCmd(cmd: CC): CR { - val c = cmd.cmdString - val json = chatSendCmd(ctrl, c) - Log.d("SIMPLEX", "sendCmd: $c") - Log.d("SIMPLEX", "sendCmd response $json") - chatModel?.terminalItems?.add(TerminalItem.Resp(APIResponse.decodeStr(json))) - return APIResponse.decodeStr(json) + suspend fun sendCmd(cmd: CC): CR { + return withContext(Dispatchers.IO) { + val c = cmd.cmdString + chatModel?.terminalItems?.add(TerminalItem.Cmd(cmd)) + val json = chatSendCmd(ctrl, c) + Log.d("SIMPLEX", "sendCmd: $c") + Log.d("SIMPLEX", "sendCmd response $json") + val r = APIResponse.decodeStr(json) + chatModel?.terminalItems?.add(TerminalItem.Resp(r.resp)) + r.resp + } } - fun apiGetActiveUser(): User? { + suspend fun apiGetActiveUser(): User? { val r = sendCmd(CC.ShowActiveUser()) - return if (r is CR.ActiveUser) r.user else null + if (r is CR.ActiveUser) return r.user + Log.d("SIMPLEX", "apiGetActiveUser: ${r.toString()}") + return null } - fun apiCreateActiveUser(p: Profile): User { + suspend fun apiCreateActiveUser(p: Profile): User { val r = sendCmd(CC.CreateActiveUser(p)) if (r is CR.ActiveUser) return r.user - throw Error("failed creating user: ${r.toString()}") + Log.d("SIMPLEX", "apiCreateActiveUser: ${r.toString()}") + throw Error("user not created ${r.toString()}") } - fun apiStartChat() { + suspend fun apiStartChat() { val r = sendCmd(CC.StartChat()) - if (r is CR.ChatStarted) return + if (r is CR.ChatStarted ) return throw Error("failed starting chat: ${r.toString()}") } @@ -101,17 +115,20 @@ val json = Json { } @Serializable -class APIResponse(val resp: CR) { +class APIResponse(val resp: CR, val corr: String? = null) { companion object { - fun decodeStr(str: String): CR { + fun decodeStr(str: String): APIResponse { try { - return json.decodeFromString(str).resp + return json.decodeFromString(str) } catch(e: Exception) { try { - val data = json.parseToJsonElement(str) - return CR.Response(data.jsonObject["resp"]!!.jsonObject["type"]?.toString() ?: "invalid", json.encodeToString(data)) + val data = json.parseToJsonElement(str).jsonObject + return APIResponse( + resp = CR.Response(data["resp"]!!.jsonObject["type"]?.toString() ?: "invalid", json.encodeToString(data)), + corr = data["corr"]?.toString() + ) } catch(e: Exception) { - return CR.Invalid(str) + return APIResponse(CR.Invalid(str)) } } } @@ -145,6 +162,13 @@ sealed class CR { override val details get() = contact.toString() } + @Serializable + @SerialName("cmdAccepted") + class CmdAccepted(val corr: String): CR() { + override val responseType get() = "cmdAccepted" + override val details get() = "corrId: $corr" + } + @Serializable class Response(val type: String, val json: String): CR() { override val responseType get() = "* $type" @@ -168,7 +192,7 @@ abstract class TerminalItem { abstract val details: String class Cmd(val cmd: CC): TerminalItem() { - override val label get() = "> ${cmd.cmdString.substring(0, 30)}" + override val label get() = "> ${cmd.cmdString}" override val details get() = cmd.cmdString } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt index a129d75b96..8f9fcf916e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -8,15 +8,16 @@ import androidx.compose.material.Text import androidx.compose.material.Button import androidx.compose.runtime.Composable import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.* import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import chat.simplex.app.model.* -import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.SendMsgView - +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext @Composable fun TerminalPage(chatModel: ChatModel) { @@ -30,7 +31,7 @@ fun TerminalPage(chatModel: ChatModel) { type = NavType.StringType } ) - ) { entry -> DetailView( entry.arguments?.getString("_details"), navController)} + ) { entry -> DetailView( entry.arguments?.getString("_details"), navController) } } } @@ -39,7 +40,13 @@ fun TerminalView(chatModel: ChatModel, navController: NavController) { Column { TerminalLog(chatModel.terminalItems, navController) SendMsgView(sendMessage = { cmd -> - chatModel.controller.sendCmd(CC.Console(cmd)) + GlobalScope.launch { + withContext(Dispatchers.Main) { + // show "in progress" + chatModel.controller.sendCmd(CC.Console(cmd)) + // hide "in progress" + } + } }) } } @@ -50,7 +57,7 @@ fun TerminalLog(terminalItems: List, navController: NavController) items(terminalItems) { item -> ClickableText( AnnotatedString(item.label), - onClick={navController.navigate("details/${item.details}")} + onClick = { navController.navigate("details/${item.details}") } ) } } @@ -60,11 +67,8 @@ fun TerminalLog(terminalItems: List, navController: NavController) fun DetailView(details: String?, navController: NavController){ Column { Text("$details") - - Button( - onClick={navController.popBackStack()} - ) - {Text("Back")} + Button(onClick = { navController.popBackStack() }) { + Text("Back") + } } } -