messenger-android

Android graphical user interfaces for GNUnet Messenger
Log | Files | Refs | README | LICENSE

commit bd90c26ce7d9ef6b610738a2ca9d9c17a47ba2cb
parent 50ce6cb10ff4847a2f86bd6ff3664bebf6f2ca09
Author: t3sserakt <t3ss@posteo.de>
Date:   Tue, 31 Mar 2026 19:34:32 +0200

WIP: fixing UI and lobby join

Diffstat:
MGNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/MainActivity.kt | 39++++++++++++++++++++++++++++++++-------
MGNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/GnunetChat.kt | 4+++-
MGNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt | 70+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
MGNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/mock/GnunetChatMock.kt | 20+++++++++++++++++++-
MGNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/account/AccountListFragment.kt | 5+++--
MGNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/contact/LobbyJoinFragment.kt | 13+++++++++++--
6 files changed, 133 insertions(+), 18 deletions(-)

diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/MainActivity.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/MainActivity.kt @@ -41,7 +41,9 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.isActive +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.gnunet.gnunetmessenger.model.ChatAccount import org.gnunet.gnunetmessenger.model.ChatContact import org.gnunet.gnunetmessenger.model.ChatContext @@ -314,11 +316,22 @@ class MainActivity : AppCompatActivity() { return chatMenuViewModel } - private fun loadChats() { + fun loadChats() { + lifecycleScope.launch { + try { + loadChatsSuspend() + } catch (t: Throwable) { + Log.e(TAG, "loadChats failed", t) + } + } + } + + private suspend fun loadChatsSuspend() = withContext(Dispatchers.IO) { val summaries = mutableListOf<ChatSummary>() val contacts = mutableListOf<ChatContact>() - gnunetChat.iterateContacts(handle) { contact -> + val contactList = gnunetChat.listContacts(handle) + for (contact in contactList) { val chatContext = gnunetChat.getContactContext(contact) val uuid = gnunetChat.randomUUID() contacts.add(contact) @@ -333,11 +346,10 @@ class MainActivity : AppCompatActivity() { contact = contact ) ) - 0 } - contactListViewModel.setContacts(contacts) - gnunetChat.iterateGroups(handle) { group -> + val groupList = gnunetChat.listGroups(handle) + for (group in groupList) { val chatContext = gnunetChat.getGroupContext(group) val uuid = gnunetChat.randomUUID() @@ -350,10 +362,23 @@ class MainActivity : AppCompatActivity() { group = group ) ) - 0 } - chatOverviewViewModel.setChats(summaries) + withContext(Dispatchers.Main) { + contactListViewModel.setContacts(contacts) + chatOverviewViewModel.setChats(summaries) + } + Log.d(TAG, "loadChats: loaded ${contacts.size} contacts, ${groupList.size} groups") + } + + fun clearChatState() { + chatOverviewViewModel.clearModel() + contactListViewModel.clearModel() + chatViewModels.values.forEach { it.clearModel() } + chatViewModels.clear() + chatMenuViewModels.clear() + chats.clear() + Log.d(TAG, "clearChatState: all chat state cleared") } override fun onCreateOptionsMenu(menu: Menu?): Boolean { diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/GnunetChat.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/GnunetChat.kt @@ -38,7 +38,7 @@ interface GnunetChat { fun getAttributes(handle: ChatHandle, callback: (String, String) -> Unit) fun lobbyOpen(handle: ChatHandle, callback: (String) -> Unit) - fun lobbyJoin(handle: ChatHandle, uri: String) + suspend fun lobbyJoin(handle: ChatHandle, uri: String) fun setGroupName(group: ChatGroup, name: String) fun createGroup(handle: ChatHandle, topic: String): ChatGroup @@ -60,6 +60,8 @@ interface GnunetChat { fun iterateContacts(handle: ChatHandle, callback: (ChatContact) -> Int) fun iterateGroups(handle: ChatHandle, callback: (ChatGroup) -> Int) + suspend fun listContacts(handle: ChatHandle): List<ChatContact> + suspend fun listGroups(handle: ChatHandle): List<ChatGroup> fun getContactContext(chatContact: ChatContact): ChatContext fun getGroupContext(chatGroup: ChatGroup): ChatContext diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/boundimpl/GnunetChatBoundService.kt @@ -488,11 +488,9 @@ class GnunetChatBoundService( } } - override fun lobbyJoin(handle: ChatHandle, uri: String) { - runBlocking { - withReadyRemote(handle) { remote, h -> - remote.lobbyJoin(h, uri) - } + override suspend fun lobbyJoin(handle: ChatHandle, uri: String) { + withReadyRemote(handle) { remote, h -> + withContext(Dispatchers.IO) { remote.lobbyJoin(h, uri) } } } @@ -619,6 +617,68 @@ class GnunetChatBoundService( } } + override suspend fun listContacts(handle: ChatHandle): List<ChatContact> { + return withReadyRemote(handle) { remote, h -> + val result = mutableListOf<ChatContact>() + val done = CompletableDeferred<Unit>() + + val bridge = object : IContactCallback.Stub() { + override fun onContact(contactDto: ChatContactDto) { + result.add(contactDto.toLocal()) + } + + override fun onDone() { + if (!done.isCompleted) { + done.complete(Unit) + } + } + + override fun onError(code: Int, message: String?) { + if (!done.isCompleted) { + done.completeExceptionally( + IllegalStateException("iterateContacts failed: $code ${message ?: ""}".trim()) + ) + } + } + } + + remote.iterateContacts(h, bridge) + done.await() + result.toList() + } + } + + override suspend fun listGroups(handle: ChatHandle): List<ChatGroup> { + return withReadyRemote(handle) { remote, h -> + val result = mutableListOf<ChatGroup>() + val done = CompletableDeferred<Unit>() + + val bridge = object : IGroupCallback.Stub() { + override fun onGroup(groupDto: ChatGroupDto) { + result.add(groupDto.toLocal()) + } + + override fun onDone() { + if (!done.isCompleted) { + done.complete(Unit) + } + } + + override fun onError(code: Int, message: String?) { + if (!done.isCompleted) { + done.completeExceptionally( + IllegalStateException("iterateGroups failed: $code ${message ?: ""}".trim()) + ) + } + } + } + + remote.iterateGroups(h, bridge) + done.await() + result.toList() + } + } + override fun iterateContacts(handle: ChatHandle, callback: (ChatContact) -> Int) { val bridge = object : IContactCallback.Stub() { override fun onContact(contactDto: ChatContactDto) { diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/mock/GnunetChatMock.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/service/mock/GnunetChatMock.kt @@ -167,7 +167,7 @@ class GnunetChatMock : GnunetChat { callback("000G006K2TJNMD9VTCYRX7BRVV3HAEPS15E6NHDXKPJA1KAJJEG9AFF884") } - override fun lobbyJoin(handle: ChatHandle, uri: String) { + override suspend fun lobbyJoin(handle: ChatHandle, uri: String) { println("join lobby") } @@ -346,6 +346,24 @@ class GnunetChatMock : GnunetChat { ) } + override suspend fun listContacts(handle: ChatHandle): List<ChatContact> { + val contextAlice = ChatContext(ChatContextType.CONTACT, null, false, false) + val contextBob = ChatContext(ChatContextType.CONTACT, null, false, false) + return listOf( + ChatContact(contextAlice, name = "Alice"), + ChatContact(contextBob, name = "Bob") + ) + } + + override suspend fun listGroups(handle: ChatHandle): List<ChatGroup> { + val contextDev = ChatContext(ChatContextType.GROUP, null, true, false) + val contextFriends = ChatContext(ChatContextType.GROUP, null, true, false) + return listOf( + ChatGroup(contextDev, name = "Dev Team"), + ChatGroup(contextFriends, name = "Friends") + ) + } + override fun getContactContext(chatContact: ChatContact): ChatContext { return chatContact.chatContext } diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/account/AccountListFragment.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/account/AccountListFragment.kt @@ -80,12 +80,13 @@ class AccountListFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { try { - /*if (activity.currentAccount != null) { + if (activity.currentAccount != null) { + activity.clearChatState() runCatching { gnunetChat.disconnect(handle) } .onFailure { Log.w(TAG, "disconnect before connect failed", it) } - }*/ + } gnunetChat.connect(handle, selectedAccount) selectedAccount.key = gnunetChat.getProfileKey(handle) diff --git a/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/contact/LobbyJoinFragment.kt b/GNUnetMessenger/app/src/main/java/org/gnunet/gnunetmessenger/ui/contact/LobbyJoinFragment.kt @@ -38,12 +38,14 @@ import androidx.camera.view.PreviewView import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import org.gnunet.gnunetmessenger.R import com.google.mlkit.vision.barcode.BarcodeScanning import com.google.mlkit.vision.common.InputImage import org.gnunet.gnunetmessenger.MainActivity import java.util.concurrent.Executors +import kotlinx.coroutines.launch class LobbyJoinFragment : Fragment() { @@ -71,8 +73,15 @@ class LobbyJoinFragment : Fragment() { val activity = activity as MainActivity val gnunetChat = activity.getGnunetChatInstance() val handle = activity.getChatHandle() - gnunetChat.lobbyJoin(handle, lobbyId) - findNavController().popBackStack() + viewLifecycleOwner.lifecycleScope.launch { + try { + gnunetChat.lobbyJoin(handle, lobbyId) + activity.loadChats() + findNavController().popBackStack() + } catch (t: Throwable) { + android.util.Log.e("LobbyJoinFragment", "lobbyJoin failed", t) + } + } } cancelButton.setOnClickListener {