Started 1-1 chat room creation

This commit is contained in:
Sylvain Berfini 2023-10-17 10:58:09 +02:00
parent db72bffcbd
commit 8526c24c3e
4 changed files with 234 additions and 5 deletions

View file

@ -30,13 +30,20 @@ import androidx.navigation.navGraphViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.contacts.getListOfSipAddressesAndPhoneNumbers
import org.linphone.core.tools.Log
import org.linphone.databinding.StartChatFragmentBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.chat.viewmodel.StartConversationViewModel
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel
import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.history.adapter.ContactsAndSuggestionsListAdapter
import org.linphone.ui.main.history.model.ContactOrSuggestionModel
import org.linphone.ui.main.model.isInSecureMode
import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
@UiThread
class StartConversationFragment : GenericFragment() {
@ -56,9 +63,12 @@ class StartConversationFragment : GenericFragment() {
@UiThread
override fun onClicked(model: ContactNumberOrAddressModel) {
val address = model.address
if (address != null) {
coreContext.postOnCoreThread {
// TODO
coreContext.postOnCoreThread {
if (address != null) {
Log.i(
"$TAG Creating a 1-1 conversation with [${model.address.asStringUriOnly()}]"
)
viewModel.createOneToOneChatRoomWith(model.address)
}
}
}
@ -96,7 +106,7 @@ class StartConversationFragment : GenericFragment() {
adapter.contactClickedEvent.observe(viewLifecycleOwner) {
it.consume { model ->
// TODO
createChatRoom(model)
}
}
@ -116,6 +126,23 @@ class StartConversationFragment : GenericFragment() {
}
}
viewModel.chatRoomCreatedEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
Log.i(
"$TAG Chat room [${pair.second}] for local address [${pair.first}] has been created, navigating to it"
)
sharedViewModel.showConversationEvent.value = Event(pair)
goBack()
}
}
viewModel.chatRoomCreationErrorEvent.observe(viewLifecycleOwner) {
it.consume { error ->
Log.i("$TAG Chat room creation error, showing red toast")
(requireActivity() as MainActivity).showRedToast(error, R.drawable.warning_circle)
}
}
viewModel.searchFilter.observe(viewLifecycleOwner) { filter ->
val trimmed = filter.trim()
viewModel.applyFilter(trimmed)
@ -133,4 +160,63 @@ class StartConversationFragment : GenericFragment() {
numberOrAddressPickerDialog?.dismiss()
numberOrAddressPickerDialog = null
}
private fun createChatRoom(model: ContactOrSuggestionModel) {
coreContext.postOnCoreThread { core ->
val friend = model.friend
if (friend == null) {
Log.i("$TAG Friend is null, creating conversation with [${model.address}]")
viewModel.createOneToOneChatRoomWith(model.address)
return@postOnCoreThread
}
val addressesCount = friend.addresses.size
val numbersCount = friend.phoneNumbers.size
// Do not consider phone numbers if default account is in secure mode
val enablePhoneNumbers = core.defaultAccount?.isInSecureMode() != true
if (addressesCount == 1 && (numbersCount == 0 || !enablePhoneNumbers)) {
Log.i(
"$TAG Only 1 SIP address found for contact [${friend.name}], creating conversation directly"
)
val address = friend.addresses.first()
viewModel.createOneToOneChatRoomWith(address)
} else if (addressesCount == 0 && numbersCount == 1 && enablePhoneNumbers) {
val number = friend.phoneNumbers.first()
val address = core.interpretUrl(number, true)
if (address != null) {
Log.i(
"$TAG Only 1 phone number found for contact [${friend.name}], creating conversation directly"
)
viewModel.createOneToOneChatRoomWith(address)
} else {
Log.e("$TAG Failed to interpret phone number [$number] as SIP address")
}
} else {
val list = friend.getListOfSipAddressesAndPhoneNumbers(listener)
Log.i(
"$TAG [${list.size}] numbers or addresses found for contact [${friend.name}], showing selection dialog"
)
coreContext.postOnMainThread {
val numberOrAddressModel = NumberOrAddressPickerDialogModel(list)
val dialog =
DialogUtils.getNumberOrAddressPickerDialog(
requireActivity(),
numberOrAddressModel
)
numberOrAddressPickerDialog = dialog
numberOrAddressModel.dismissEvent.observe(viewLifecycleOwner) { event ->
event.consume {
dialog.dismiss()
}
}
dialog.show()
}
}
}
}
}

View file

@ -26,7 +26,12 @@ import androidx.lifecycle.ViewModel
import java.util.ArrayList
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.contacts.ContactsManager.ContactsListener
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.ChatRoomParams
import org.linphone.core.MagicSearch
import org.linphone.core.MagicSearchListenerStub
import org.linphone.core.SearchResult
@ -34,6 +39,8 @@ import org.linphone.core.tools.Log
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.ui.main.history.model.ContactOrSuggestionModel
import org.linphone.ui.main.model.isInSecureMode
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
class StartConversationViewModel @UiThread constructor() : ViewModel() {
@ -47,7 +54,44 @@ class StartConversationViewModel @UiThread constructor() : ViewModel() {
val hideGroupChatButton = MutableLiveData<Boolean>()
val isGroupChatAvailable = MutableLiveData<Boolean>()
val operationInProgress = MutableLiveData<Boolean>()
val chatRoomCreationErrorEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val chatRoomCreatedEvent: MutableLiveData<Event<Pair<String, String>>> by lazy {
MutableLiveData<Event<Pair<String, String>>>()
}
private val chatRoomListener = object : ChatRoomListenerStub() {
@WorkerThread
override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State?) {
val state = chatRoom.state
Log.i("$TAG Chat room state changed: [$state]")
if (state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG Chat room [$id] successfully created")
chatRoom.removeListener(this)
operationInProgress.postValue(false)
chatRoomCreatedEvent.postValue(
Event(
Pair(
chatRoom.localAddress.asStringUriOnly(),
chatRoom.peerAddress.asStringUriOnly()
)
)
)
} else if (state == ChatRoom.State.CreationFailed) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.e("$TAG Chat room [$id] creation has failed!")
chatRoom.removeListener(this)
operationInProgress.postValue(false)
chatRoomCreationErrorEvent.postValue(Event("Error!")) // TODO FIXME: use translated string
}
}
}
private var currentFilter = ""
private var previousFilter = "NotSet"
@ -106,6 +150,102 @@ class StartConversationViewModel @UiThread constructor() : ViewModel() {
searchFilter.value = ""
}
@WorkerThread
fun createOneToOneChatRoomWith(remote: Address) {
val core = coreContext.core
val account = core.defaultAccount
if (account == null) {
Log.e(
"$TAG No default account found, can't create conversation with [${remote.asStringUriOnly()}]!"
)
return
}
operationInProgress.postValue(true)
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
params.isGroupEnabled = false
params.subject = AppUtils.getString(R.string.conversation_one_to_one_hidden_subject)
val sameDomain = remote.domain == corePreferences.defaultDomain && remote.domain == account.params.domain
if (account.isInSecureMode() && sameDomain) {
Log.i("$TAG Account is in secure mode & domain matches, creating a E2E chat room")
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
} else if (!account.isInSecureMode()) {
Log.i("$TAG Account is in interop mode, creating a SIP simple chat room")
params.backend = ChatRoom.Backend.Basic
params.isEncryptionEnabled = false
} else {
Log.e(
"$TAG Account is in secure mode, can't chat with SIP address of different domain [${remote.asStringUriOnly()}]"
)
operationInProgress.postValue(false)
chatRoomCreationErrorEvent.postValue(Event("Error!")) // TODO FIXME: use translated string
return
}
val participants = arrayOf(remote)
val localAddress = account.params.identityAddress
val existingChatRoom = core.searchChatRoom(params, localAddress, null, participants)
if (existingChatRoom == null) {
Log.i(
"$TAG No existing 1-1 chat room between local account [${localAddress?.asStringUriOnly()}] and remote [${remote.asStringUriOnly()}] was found for given parameters, let's create it"
)
val chatRoom = core.createChatRoom(params, localAddress, participants)
if (chatRoom != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (chatRoom.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG 1-1 chat room [$id] has been created")
operationInProgress.postValue(false)
chatRoomCreatedEvent.postValue(
Event(
Pair(
chatRoom.localAddress.asStringUriOnly(),
chatRoom.peerAddress.asStringUriOnly()
)
)
)
} else {
Log.i("$TAG Chat room isn't in Created state yet, wait for it")
chatRoom.addListener(chatRoomListener)
}
} else {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG Chat room successfully created [$id]")
operationInProgress.postValue(false)
chatRoomCreatedEvent.postValue(
Event(
Pair(
chatRoom.localAddress.asStringUriOnly(),
chatRoom.peerAddress.asStringUriOnly()
)
)
)
}
} else {
Log.e("$TAG Failed to create 1-1 chat room with [${remote.asStringUriOnly()}]!")
operationInProgress.postValue(false)
chatRoomCreationErrorEvent.postValue(Event("Error!")) // TODO FIXME: use translated string
}
} else {
Log.w(
"$TAG A 1-1 chat room between local account [${localAddress?.asStringUriOnly()}] and remote [${remote.asStringUriOnly()}] for given parameters already exists!"
)
operationInProgress.postValue(false)
chatRoomCreatedEvent.postValue(
Event(
Pair(
existingChatRoom.localAddress.asStringUriOnly(),
existingChatRoom.peerAddress.asStringUriOnly()
)
)
)
}
}
@UiThread
fun updateGroupChatButtonVisibility() {
coreContext.postOnCoreThread { core ->

View file

@ -200,6 +200,7 @@ class StartCallFragment : GenericFragment() {
coreContext.postOnCoreThread { core ->
val friend = model.friend
if (friend == null) {
Log.i("$TAG Friend is null, starting call with [${model.address}]")
coreContext.startCall(model.address)
return@postOnCoreThread
}

View file

@ -34,6 +34,8 @@
<string name="website_translate_weblate_url" translatable="false">https://weblate.linphone.org/</string>
<string name="website_open_source_licences_usage_url" translatable="false">https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/Third%20party%20components%20/#Hlinphone-android</string>
<string name="conversation_one_to_one_hidden_subject" translatable="false">Dummy subject</string>
<string name="sip_address">SIP address</string>
<string name="sip_address_display_name">Display name</string>
<string name="sip_address_domain">Domain</string>