Migrated deprecated chat room & conference scheduler APIs

This commit is contained in:
Sylvain Berfini 2024-10-22 11:21:30 +02:00
parent 50cb162bd3
commit 257352927d
15 changed files with 248 additions and 78 deletions

View file

@ -25,6 +25,8 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Address
import org.linphone.core.ConferenceParams
import org.linphone.core.tools.Log
class NotificationBroadcastReceiver : BroadcastReceiver() {
@ -110,7 +112,15 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
return@postOnCoreThread
}
val room = core.searchChatRoom(null, localAddress, remoteAddress, arrayOfNulls(0))
val params: ConferenceParams? = null
val room = core.searchChatRoom(
params,
localAddress,
remoteAddress,
arrayOfNulls<Address>(
0
)
)
if (room == null) {
Log.e(
"$TAG Couldn't find conversation for remote address [$remoteSipAddress] and local address [$localIdentity]"

View file

@ -42,7 +42,8 @@ import org.linphone.core.CallStats
import org.linphone.core.ChatMessage
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.ChatRoomParams
import org.linphone.core.Conference
import org.linphone.core.ConferenceParams
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.MediaDirection
@ -711,7 +712,7 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() {
else -> device.deviceName
}
val currentDevice = currentCall.outputAudioDevice
val isCurrentlyInUse = device.type == currentDevice?.type && device.deviceName == currentDevice?.deviceName
val isCurrentlyInUse = device.type == currentDevice?.type && device.deviceName == currentDevice.deviceName
val model = AudioDeviceModel(device, name, device.type, isCurrentlyInUse, true) {
// onSelected
coreContext.postOnCoreThread {
@ -1281,7 +1282,7 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() {
val params = getChatRoomParams(call) ?: return // TODO: show error to user
val conversation = core.createChatRoom(params, localAddress, participants)
if (conversation != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (params.chatParams?.backend == ChatRoom.Backend.FlexisipChat) {
if (conversation.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(conversation)
Log.i("$TAG 1-1 conversation [$id] has been created")
@ -1323,37 +1324,39 @@ class CurrentCallViewModel @UiThread constructor() : GenericViewModel() {
}
@WorkerThread
private fun getChatRoomParams(call: Call): ChatRoomParams? {
private fun getChatRoomParams(call: Call): ConferenceParams? {
val localAddress = call.callLog.localAddress
val remoteAddress = call.remoteAddress
val core = call.core
val account = LinphoneUtils.getAccountForAddress(localAddress) ?: LinphoneUtils.getDefaultAccount() ?: return null
val params: ChatRoomParams = core.createDefaultChatRoomParams()
val params = coreContext.core.createConferenceParams(call.conference)
params.isChatEnabled = true
params.isGroupEnabled = false
params.subject = AppUtils.getString(R.string.conversation_one_to_one_hidden_subject)
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val chatParams = params.chatParams ?: return null
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val sameDomain = remoteAddress.domain == corePreferences.defaultDomain && remoteAddress.domain == account.params.domain
if (account.params.instantMessagingEncryptionMandatory && sameDomain) {
Log.i(
"$TAG Account is in secure mode & domain matches, requesting E2E encryption"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else if (!account.params.instantMessagingEncryptionMandatory) {
if (LinphoneUtils.isEndToEndEncryptedChatAvailable(core)) {
Log.i(
"$TAG Account is in interop mode but LIME is available, requesting E2E encryption"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else {
Log.i(
"$TAG Account is in interop mode but LIME isn't available, disabling E2E encryption"
)
params.backend = ChatRoom.Backend.Basic
params.isEncryptionEnabled = false
chatParams.backend = ChatRoom.Backend.Basic
params.securityLevel = Conference.SecurityLevel.None
}
} else {
Log.e(

View file

@ -26,7 +26,9 @@ import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ConferenceParams
import org.linphone.core.ConferenceScheduler
import org.linphone.core.ConferenceSchedulerListenerStub
import org.linphone.core.Factory
@ -149,11 +151,12 @@ abstract class AbstractConversationViewModel : GenericViewModel() {
if (localAddress != null && remoteAddress != null) {
Log.i("$TAG Searching for conversation in Core using local & peer SIP addresses")
val params: ConferenceParams? = null
val found = core.searchChatRoom(
null,
params,
localAddress,
remoteAddress,
arrayOfNulls(
arrayOfNulls<Address>(
0
)
)
@ -229,7 +232,7 @@ abstract class AbstractConversationViewModel : GenericViewModel() {
Log.i(
"$TAG Creating group call with subject ${conferenceInfo.subject} and ${participants.size} participant(s)"
)
val conferenceScheduler = core.createConferenceScheduler()
val conferenceScheduler = LinphoneUtils.createConferenceScheduler(account)
conferenceScheduler.addListener(conferenceSchedulerListener)
conferenceScheduler.account = account
// Will trigger the conference creation/update automatically

View file

@ -29,7 +29,7 @@ import org.linphone.contacts.getListOfSipAddressesAndPhoneNumbers
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.ChatRoomParams
import org.linphone.core.Conference
import org.linphone.core.tools.Log
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel
@ -189,29 +189,31 @@ class ConversationForwardMessageViewModel @UiThread constructor() : AddressSelec
operationInProgress.postValue(true)
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
val params = coreContext.core.createConferenceParams(null)
params.isChatEnabled = true
params.isGroupEnabled = false
params.subject = AppUtils.getString(R.string.conversation_one_to_one_hidden_subject)
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val chatParams = params.chatParams ?: return
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val sameDomain = remote.domain == corePreferences.defaultDomain && remote.domain == account.params.domain
if (account.params.instantMessagingEncryptionMandatory && sameDomain) {
Log.i("$TAG Account is in secure mode & domain matches, creating a E2E conversation")
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else if (!account.params.instantMessagingEncryptionMandatory) {
if (LinphoneUtils.isEndToEndEncryptedChatAvailable(core)) {
Log.i(
"$TAG Account is in interop mode but LIME is available, creating a E2E conversation"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else {
Log.i(
"$TAG Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
)
params.backend = ChatRoom.Backend.Basic
params.isEncryptionEnabled = false
chatParams.backend = ChatRoom.Backend.Basic
params.securityLevel = Conference.SecurityLevel.None
}
} else {
Log.e(
@ -238,7 +240,7 @@ class ConversationForwardMessageViewModel @UiThread constructor() : AddressSelec
)
val chatRoom = core.createChatRoom(params, localAddress, participants)
if (chatRoom != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (chatParams.backend == ChatRoom.Backend.FlexisipChat) {
if (chatRoom.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG 1-1 conversation [$id] has been created")

View file

@ -29,7 +29,7 @@ import org.linphone.R
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.ChatRoomParams
import org.linphone.core.Conference
import org.linphone.core.tools.Log
import org.linphone.ui.main.viewmodel.AddressSelectionViewModel
import org.linphone.utils.AppUtils
@ -112,11 +112,14 @@ class StartConversationViewModel @UiThread constructor() : AddressSelectionViewM
operationInProgress.postValue(true)
val groupChatRoomSubject = subject.value.orEmpty()
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
val params = coreContext.core.createConferenceParams(null)
params.isChatEnabled = true
params.isGroupEnabled = true
params.subject = groupChatRoomSubject
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
params.securityLevel = Conference.SecurityLevel.EndToEnd
val chatParams = params.chatParams ?: return@postOnCoreThread
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
chatParams.backend = ChatRoom.Backend.FlexisipChat
val participants = arrayListOf<Address>()
for (participant in selection.value.orEmpty()) {
@ -131,7 +134,7 @@ class StartConversationViewModel @UiThread constructor() : AddressSelectionViewM
participants.toArray(participantsArray)
)
if (chatRoom != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (chatParams.backend == ChatRoom.Backend.FlexisipChat) {
if (chatRoom.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i(
@ -188,29 +191,31 @@ class StartConversationViewModel @UiThread constructor() : AddressSelectionViewM
operationInProgress.postValue(true)
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
val params = coreContext.core.createConferenceParams(null)
params.isChatEnabled = true
params.isGroupEnabled = false
params.subject = AppUtils.getString(R.string.conversation_one_to_one_hidden_subject)
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val chatParams = params.chatParams ?: return
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val sameDomain = remote.domain == corePreferences.defaultDomain && remote.domain == account.params.domain
if (account.params.instantMessagingEncryptionMandatory && sameDomain) {
Log.i("$TAG Account is in secure mode & domain matches, creating a E2E conversation")
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else if (!account.params.instantMessagingEncryptionMandatory) {
if (LinphoneUtils.isEndToEndEncryptedChatAvailable(core)) {
Log.i(
"$TAG Account is in interop mode but LIME is available, creating a E2E conversation"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else {
Log.i(
"$TAG Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
)
params.backend = ChatRoom.Backend.Basic
params.isEncryptionEnabled = false
chatParams.backend = ChatRoom.Backend.Basic
params.securityLevel = Conference.SecurityLevel.None
}
} else {
Log.e(
@ -232,7 +237,7 @@ class StartConversationViewModel @UiThread constructor() : AddressSelectionViewM
)
val chatRoom = core.createChatRoom(params, localAddress, participants)
if (chatRoom != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (chatParams.backend == ChatRoom.Backend.FlexisipChat) {
if (chatRoom.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG 1-1 conversation [$id] has been created")

View file

@ -36,7 +36,7 @@ import org.linphone.core.Address
import org.linphone.core.Call
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.ChatRoomParams
import org.linphone.core.Conference
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.Friend
@ -501,31 +501,33 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() {
"$TAG Looking for existing conversation between [$localSipUri] and [$remoteSipUri]"
)
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
val params = coreContext.core.createConferenceParams(null)
params.isChatEnabled = true
params.isGroupEnabled = false
params.subject = AppUtils.getString(R.string.conversation_one_to_one_hidden_subject)
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val chatParams = params.chatParams ?: return
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val sameDomain = remote.domain == corePreferences.defaultDomain && remote.domain == account.params.domain
if (account.params.instantMessagingEncryptionMandatory && sameDomain) {
Log.i(
"$TAG Account is in secure mode & domain matches, creating a E2E conversation"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else if (!account.params.instantMessagingEncryptionMandatory) {
if (LinphoneUtils.isEndToEndEncryptedChatAvailable(core)) {
Log.i(
"$TAG Account is in interop mode but LIME is available, creating a E2E conversation"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else {
Log.i(
"$TAG Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
)
params.backend = ChatRoom.Backend.Basic
params.isEncryptionEnabled = false
chatParams.backend = ChatRoom.Backend.Basic
params.securityLevel = Conference.SecurityLevel.None
}
} else {
Log.e(
@ -554,7 +556,7 @@ class ContactViewModel @UiThread constructor() : GenericViewModel() {
operationInProgress.postValue(true)
val chatRoom = core.createChatRoom(params, localAddress, participants)
if (chatRoom != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (chatParams.backend == ChatRoom.Backend.FlexisipChat) {
if (chatRoom.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG 1-1 conversation [$id] has been created")

View file

@ -28,7 +28,7 @@ import org.linphone.R
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
import org.linphone.core.ChatRoomParams
import org.linphone.core.Conference
import org.linphone.core.tools.Log
import org.linphone.ui.GenericViewModel
import org.linphone.ui.main.history.model.CallLogHistoryModel
@ -184,31 +184,33 @@ class HistoryViewModel @UiThread constructor() : GenericViewModel() {
"$TAG Looking for existing conversation between [$localSipUri] and [$remoteSipUri]"
)
val params: ChatRoomParams = coreContext.core.createDefaultChatRoomParams()
val params = coreContext.core.createConferenceParams(null)
params.isChatEnabled = true
params.isGroupEnabled = false
params.subject = AppUtils.getString(R.string.conversation_one_to_one_hidden_subject)
params.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val chatParams = params.chatParams ?: return@postOnCoreThread
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
val sameDomain = remote.domain == corePreferences.defaultDomain && remote.domain == account.params.domain
if (account.params.instantMessagingEncryptionMandatory && sameDomain) {
Log.i(
"$TAG Account is in secure mode & domain matches, creating a E2E conversation"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else if (!account.params.instantMessagingEncryptionMandatory) {
if (LinphoneUtils.isEndToEndEncryptedChatAvailable(core)) {
Log.i(
"$TAG Account is in interop mode but LIME is available, creating a E2E conversation"
)
params.backend = ChatRoom.Backend.FlexisipChat
params.isEncryptionEnabled = true
chatParams.backend = ChatRoom.Backend.FlexisipChat
params.securityLevel = Conference.SecurityLevel.EndToEnd
} else {
Log.i(
"$TAG Account is in interop mode but LIME isn't available, creating a SIP simple conversation"
)
params.backend = ChatRoom.Backend.Basic
params.isEncryptionEnabled = false
chatParams.backend = ChatRoom.Backend.Basic
params.securityLevel = Conference.SecurityLevel.None
}
} else {
Log.e(
@ -237,7 +239,7 @@ class HistoryViewModel @UiThread constructor() : GenericViewModel() {
operationInProgress.postValue(true)
val chatRoom = core.createChatRoom(params, localAddress, participants)
if (chatRoom != null) {
if (params.backend == ChatRoom.Backend.FlexisipChat) {
if (chatParams.backend == ChatRoom.Backend.FlexisipChat) {
if (chatRoom.state == ChatRoom.State.Created) {
val id = LinphoneUtils.getChatRoomId(chatRoom)
Log.i("$TAG 1-1 conversation [$id] has been created")

View file

@ -234,7 +234,7 @@ class StartCallViewModel @UiThread constructor() : AddressSelectionViewModel() {
Log.i(
"$TAG Creating group call with subject ${subject.value} and ${participants.size} participant(s)"
)
val conferenceScheduler = core.createConferenceScheduler()
val conferenceScheduler = LinphoneUtils.createConferenceScheduler(account)
conferenceScheduler.addListener(conferenceSchedulerListener)
conferenceScheduler.account = account
// Will trigger the conference creation/update automatically

View file

@ -35,6 +35,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.MeetingsListFragmentBinding
import org.linphone.ui.GenericActivity
import org.linphone.ui.main.fragment.AbstractMainFragment
import org.linphone.ui.main.history.model.ConfirmationDialogModel
import org.linphone.ui.main.meetings.adapter.MeetingsListAdapter
@ -59,6 +60,8 @@ class MeetingsListFragment : AbstractMainFragment() {
private var bottomSheetDialog: BottomSheetDialogFragment? = null
private var meetingViewModelBeingCancelled: MeetingModel? = null
private val dataObserver = object : AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (positionStart == 0 && adapter.itemCount == itemCount) {
@ -161,6 +164,24 @@ class MeetingsListFragment : AbstractMainFragment() {
listViewModel.fetchInProgress.value = false
}
listViewModel.conferenceCancelledEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i("$TAG Meeting has been cancelled successfully, deleting it")
(requireActivity() as GenericActivity).showGreenToast(
getString(R.string.meeting_info_cancelled_toast),
R.drawable.trash_simple
)
meetingViewModelBeingCancelled?.delete()
meetingViewModelBeingCancelled = null
listViewModel.applyFilter()
(requireActivity() as GenericActivity).showGreenToast(
getString(R.string.meeting_info_deleted_toast),
R.drawable.trash_simple
)
}
}
adapter.meetingLongClickedEvent.observe(viewLifecycleOwner) {
it.consume { model ->
val modalBottomSheet = MeetingsMenuDialogFragment(
@ -168,12 +189,17 @@ class MeetingsListFragment : AbstractMainFragment() {
adapter.resetSelection()
},
{ // onDelete
if (model.isOrganizer()) {
if (model.isOrganizer() && !model.isCancelled) {
showCancelMeetingDialog(model)
} else {
Log.i("$TAG Deleting meeting [${model.id}]")
model.delete()
listViewModel.applyFilter()
(requireActivity() as GenericActivity).showGreenToast(
getString(R.string.meeting_info_deleted_toast),
R.drawable.trash_simple
)
}
}
)
@ -296,16 +322,20 @@ class MeetingsListFragment : AbstractMainFragment() {
Log.i("$TAG Deleting meeting [${meetingModel.id}]")
meetingModel.delete()
listViewModel.applyFilter()
dialog.dismiss()
(requireActivity() as GenericActivity).showGreenToast(
getString(R.string.meeting_info_deleted_toast),
R.drawable.trash_simple
)
}
}
model.confirmEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i("$TAG Cancelling meeting [${meetingModel.id}]")
meetingViewModelBeingCancelled = meetingModel
listViewModel.cancelMeeting(meetingModel.conferenceInfo)
meetingModel.delete()
listViewModel.applyFilter()
dialog.dismiss()
}
}

View file

@ -25,7 +25,6 @@ import androidx.lifecycle.MutableLiveData
import java.util.TimeZone
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ConferenceInfo
import org.linphone.core.ConferenceScheduler
import org.linphone.core.ConferenceSchedulerListenerStub
@ -36,6 +35,7 @@ import org.linphone.ui.GenericViewModel
import org.linphone.ui.main.meetings.model.ParticipantModel
import org.linphone.ui.main.meetings.model.TimeZoneModel
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class MeetingViewModel @UiThread constructor() : GenericViewModel() {
@ -90,12 +90,12 @@ class MeetingViewModel @UiThread constructor() : GenericViewModel() {
Log.i(
"$TAG Conference ${conferenceScheduler.info?.subject} cancelled"
)
val chatRoomParams = coreContext.core.createDefaultChatRoomParams()
chatRoomParams.isGroupEnabled = false
chatRoomParams.backend = ChatRoom.Backend.FlexisipChat
chatRoomParams.isEncryptionEnabled = true
chatRoomParams.subject = "Meeting cancelled" // Won't be used
conferenceScheduler.sendInvitations(chatRoomParams) // Send cancel ICS
val params = LinphoneUtils.getChatRoomParamsToCancelMeeting()
if (params != null) {
conferenceScheduler.sendInvitations(params)
} else {
operationInProgress.postValue(false)
}
} else if (state == ConferenceScheduler.State.Error) {
operationInProgress.postValue(false)
}
@ -185,7 +185,9 @@ class MeetingViewModel @UiThread constructor() : GenericViewModel() {
if (::conferenceInfo.isInitialized) {
Log.i("$TAG Cancelling conference information [$conferenceInfo]")
operationInProgress.postValue(true)
val conferenceScheduler = core.createConferenceScheduler()
val conferenceScheduler = LinphoneUtils.createConferenceScheduler(
LinphoneUtils.getDefaultAccount()
)
conferenceScheduler.addListener(conferenceSchedulerListener)
conferenceScheduler.cancelConference(conferenceInfo)
}

View file

@ -23,13 +23,18 @@ import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Address
import org.linphone.core.ConferenceInfo
import org.linphone.core.ConferenceScheduler
import org.linphone.core.ConferenceSchedulerListenerStub
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
import org.linphone.ui.main.meetings.model.MeetingListItemModel
import org.linphone.ui.main.meetings.model.MeetingModel
import org.linphone.ui.main.viewmodel.AbstractMainViewModel
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class MeetingsListViewModel @UiThread constructor() : AbstractMainViewModel() {
@ -41,6 +46,12 @@ class MeetingsListViewModel @UiThread constructor() : AbstractMainViewModel() {
val fetchInProgress = MutableLiveData<Boolean>()
val operationInProgress = MutableLiveData<Boolean>()
val conferenceCancelledEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
private val coreListener = object : CoreListenerStub() {
@WorkerThread
override fun onConferenceInfoReceived(core: Core, conferenceInfo: ConferenceInfo) {
@ -49,7 +60,51 @@ class MeetingsListViewModel @UiThread constructor() : AbstractMainViewModel() {
}
}
private val conferenceSchedulerListener = object : ConferenceSchedulerListenerStub() {
override fun onStateChanged(
conferenceScheduler: ConferenceScheduler,
state: ConferenceScheduler.State?
) {
Log.i("$TAG Conference scheduler state is $state")
if (state == ConferenceScheduler.State.Ready) {
Log.i(
"$TAG Conference ${conferenceScheduler.info?.subject} cancelled"
)
val params = LinphoneUtils.getChatRoomParamsToCancelMeeting()
if (params != null) {
conferenceScheduler.sendInvitations(params)
} else {
operationInProgress.postValue(false)
}
} else if (state == ConferenceScheduler.State.Error) {
operationInProgress.postValue(false)
}
}
override fun onInvitationsSent(
conferenceScheduler: ConferenceScheduler,
failedInvitations: Array<out Address>?
) {
if (failedInvitations?.isNotEmpty() == true) {
for (address in failedInvitations) {
Log.e(
"$TAG Conference cancelled ICS wasn't sent to participant ${address.asStringUriOnly()}"
)
}
} else {
Log.i(
"$TAG Conference cancelled ICS successfully sent to all participants"
)
}
conferenceScheduler.removeListener(this)
operationInProgress.postValue(false)
conferenceCancelledEvent.postValue(Event(true))
}
}
init {
operationInProgress.value = false
fetchInProgress.value = true
coreContext.postOnCoreThread { core ->
@ -79,7 +134,11 @@ class MeetingsListViewModel @UiThread constructor() : AbstractMainViewModel() {
fun cancelMeeting(conferenceInfo: ConferenceInfo) {
coreContext.postOnCoreThread { core ->
Log.w("$TAG Cancelling conference info [${conferenceInfo.uri?.asStringUriOnly()}]")
val conferenceScheduler = core.createConferenceScheduler()
operationInProgress.postValue(true)
val conferenceScheduler = LinphoneUtils.createConferenceScheduler(
LinphoneUtils.getDefaultAccount()
)
conferenceScheduler.addListener(conferenceSchedulerListener)
conferenceScheduler.cancelConference(conferenceInfo)
}
}

View file

@ -30,6 +30,7 @@ import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.Conference
import org.linphone.core.ConferenceInfo
import org.linphone.core.ConferenceScheduler
import org.linphone.core.ConferenceSchedulerListenerStub
@ -41,6 +42,7 @@ import org.linphone.ui.GenericViewModel
import org.linphone.ui.main.meetings.model.TimeZoneModel
import org.linphone.ui.main.model.SelectedAddressModel
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.TimestampUtils
class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
@ -129,11 +131,15 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
if (sendInvitations.value == true) {
Log.i("$TAG User asked for invitations to be sent, let's do it")
val chatRoomParams = coreContext.core.createDefaultChatRoomParams()
val chatRoomParams = coreContext.core.createConferenceParams(null)
chatRoomParams.isChatEnabled = true
chatRoomParams.isGroupEnabled = false
chatRoomParams.backend = ChatRoom.Backend.FlexisipChat
chatRoomParams.isEncryptionEnabled = true
chatRoomParams.subject = "Meeting invitation" // Won't be used
val chatParams = chatRoomParams.chatParams ?: return
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
chatParams.backend = ChatRoom.Backend.FlexisipChat
chatRoomParams.securityLevel = Conference.SecurityLevel.EndToEnd
conferenceScheduler.sendInvitations(chatRoomParams)
} else {
Log.i("$TAG User didn't asked for invitations to be sent")
@ -459,7 +465,7 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
conferenceInfo.setParticipantInfos(participantsInfoArray)
if (!::conferenceScheduler.isInitialized) {
conferenceScheduler = core.createConferenceScheduler()
conferenceScheduler = LinphoneUtils.createConferenceScheduler(localAccount)
conferenceScheduler.addListener(conferenceSchedulerListener)
}
@ -511,7 +517,9 @@ class ScheduleMeetingViewModel @UiThread constructor() : GenericViewModel() {
conferenceInfo.setParticipantInfos(participantsInfoArray)
if (!::conferenceScheduler.isInitialized) {
conferenceScheduler = core.createConferenceScheduler()
conferenceScheduler = LinphoneUtils.createConferenceScheduler(
LinphoneUtils.getDefaultAccount()
)
conferenceScheduler.addListener(conferenceSchedulerListener)
}

View file

@ -41,7 +41,10 @@ import org.linphone.core.Call.Status
import org.linphone.core.CallLog
import org.linphone.core.ChatMessage
import org.linphone.core.ChatRoom
import org.linphone.core.Conference
import org.linphone.core.ConferenceInfo
import org.linphone.core.ConferenceParams
import org.linphone.core.ConferenceScheduler
import org.linphone.core.Core
import org.linphone.core.Factory
import org.linphone.core.Friend
@ -213,6 +216,39 @@ class LinphoneUtils {
return core.defaultAccount?.params?.audioVideoConferenceFactoryAddress != null
}
@WorkerThread
fun createConferenceScheduler(account: Account?): ConferenceScheduler {
if (!account?.params?.ccmpServerUrl.isNullOrEmpty()) {
Log.i(
"$TAG CCMP server URL has been set in Account's params, using CCMP conference scheduler"
)
return coreContext.core.createConferenceSchedulerWithType(
account,
ConferenceScheduler.Type.CCMP
)
}
Log.i(
"$TAG CCMP server URL hasn't been set in Account's params, using SIP conference scheduler"
)
return coreContext.core.createConferenceSchedulerWithType(
account,
ConferenceScheduler.Type.SIP
)
}
@WorkerThread
fun getChatRoomParamsToCancelMeeting(): ConferenceParams? {
val chatRoomParams = coreContext.core.createConferenceParams(null)
chatRoomParams.isChatEnabled = true
chatRoomParams.isGroupEnabled = false
chatRoomParams.subject = "Meeting invitation" // Won't be used
val chatParams = chatRoomParams.chatParams ?: return null
chatParams.ephemeralLifetime = 0 // Make sure ephemeral is disabled by default
chatParams.backend = ChatRoom.Backend.FlexisipChat
chatRoomParams.securityLevel = Conference.SecurityLevel.EndToEnd
return chatRoomParams
}
@WorkerThread
fun arePushNotificationsAvailable(core: Core): Boolean {
if (!core.isPushNotificationAvailable) {

View file

@ -103,6 +103,10 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<include
layout="@layout/operation_in_progress"
bind:visibility="@{viewModel.operationInProgress}" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<androidx.fragment.app.FragmentContainerView

View file

@ -103,6 +103,10 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<include
layout="@layout/operation_in_progress"
bind:visibility="@{viewModel.operationInProgress}" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<androidx.fragment.app.FragmentContainerView