Show conference edit button in schedule list for organizer + allow to edit conference + sort conferences by date when notified of a new conference schedule by callback while in conferences list view

This commit is contained in:
Sylvain Berfini 2022-03-28 14:41:47 +02:00
parent aff768548a
commit f4c89fbb2c
8 changed files with 106 additions and 31 deletions

View file

@ -50,6 +50,10 @@ class ScheduledConferencesAdapter(
MutableLiveData<Event<Pair<String, String?>>>() MutableLiveData<Event<Pair<String, String?>>>()
} }
val editConferenceEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val deleteConferenceInfoEvent: MutableLiveData<Event<ScheduledConferenceData>> by lazy { val deleteConferenceInfoEvent: MutableLiveData<Event<ScheduledConferenceData>> by lazy {
MutableLiveData<Event<ScheduledConferenceData>>() MutableLiveData<Event<ScheduledConferenceData>>()
} }
@ -117,6 +121,13 @@ class ScheduledConferencesAdapter(
} }
} }
setEditConferenceClickListener {
val address = conferenceData.conferenceInfo.uri
if (address != null) {
editConferenceEvent.value = Event(address.asStringUriOnly())
}
}
setDeleteConferenceClickListener { setDeleteConferenceClickListener {
deleteConferenceInfoEvent.value = Event(conferenceData) deleteConferenceInfoEvent.value = Event(conferenceData)
} }

View file

@ -37,6 +37,7 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo) {
val date = MutableLiveData<String>() val date = MutableLiveData<String>()
val duration = MutableLiveData<String>() val duration = MutableLiveData<String>()
val organizer = MutableLiveData<String>() val organizer = MutableLiveData<String>()
val canEdit = MutableLiveData<Boolean>()
val participantsShort = MutableLiveData<String>() val participantsShort = MutableLiveData<String>()
val participantsExpanded = MutableLiveData<String>() val participantsExpanded = MutableLiveData<String>()
val showDuration = MutableLiveData<Boolean>() val showDuration = MutableLiveData<Boolean>()
@ -59,12 +60,19 @@ class ScheduledConferenceData(val conferenceInfo: ConferenceInfo) {
val organizerAddress = conferenceInfo.organizer val organizerAddress = conferenceInfo.organizer
if (organizerAddress != null) { if (organizerAddress != null) {
val localAccount = coreContext.core.accountList.find { account ->
val address = account.params.identityAddress
address != null && organizerAddress.weakEqual(address)
}
canEdit.value = localAccount != null
val contact = coreContext.contactsManager.findContactByAddress(organizerAddress) val contact = coreContext.contactsManager.findContactByAddress(organizerAddress)
organizer.value = if (contact != null) organizer.value = if (contact != null)
contact.fullName contact.fullName
else else
LinphoneUtils.getDisplayName(conferenceInfo.organizer) LinphoneUtils.getDisplayName(conferenceInfo.organizer)
} else { } else {
canEdit.value = false
Log.e("[Scheduled Conference] No organizer SIP URI found for: ${conferenceInfo.uri?.asStringUriOnly()}") Log.e("[Scheduled Conference] No organizer SIP URI found for: ${conferenceInfo.uri?.asStringUriOnly()}")
} }

View file

@ -22,20 +22,26 @@ package org.linphone.activities.main.conference.fragments
import android.os.Bundle import android.os.Bundle
import android.text.format.DateFormat.is24HourFormat import android.text.format.DateFormat.is24HourFormat
import android.view.View import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.navGraphViewModels import androidx.navigation.navGraphViewModels
import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointForward import com.google.android.material.datepicker.DateValidatorPointForward
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat import com.google.android.material.timepicker.TimeFormat
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R import org.linphone.R
import org.linphone.activities.GenericFragment import org.linphone.activities.GenericFragment
import org.linphone.activities.main.conference.viewmodels.ConferenceSchedulingViewModel import org.linphone.activities.main.conference.viewmodels.ConferenceSchedulingViewModel
import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.activities.navigateToParticipantsList import org.linphone.activities.navigateToParticipantsList
import org.linphone.core.Factory
import org.linphone.core.tools.Log
import org.linphone.databinding.ConferenceSchedulingFragmentBinding import org.linphone.databinding.ConferenceSchedulingFragmentBinding
class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmentBinding>() { class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmentBinding>() {
private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph) private val viewModel: ConferenceSchedulingViewModel by navGraphViewModels(R.id.conference_scheduling_nav_graph)
private lateinit var sharedViewModel: SharedMainViewModel
override fun getLayoutId(): Int = R.layout.conference_scheduling_fragment override fun getLayoutId(): Int = R.layout.conference_scheduling_fragment
@ -46,6 +52,29 @@ class ConferenceSchedulingFragment : GenericFragment<ConferenceSchedulingFragmen
binding.viewModel = viewModel binding.viewModel = viewModel
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedMainViewModel::class.java]
}
sharedViewModel.conferenceInfoToEdit.observe(
viewLifecycleOwner
) {
it.consume { address ->
val conferenceAddress = Factory.instance().createAddress(address)
if (conferenceAddress != null) {
Log.i("[Conference Scheduling] Trying to edit conference info using address: $address")
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(conferenceAddress)
if (conferenceInfo != null) {
viewModel.populateFromConferenceInfo(conferenceInfo)
} else {
Log.e("[Conference Scheduling] Failed to find ConferenceInfo matching address: $address")
}
} else {
Log.e("[Conference Scheduling] Failed to parse conference address: $address")
}
}
}
binding.setBackClickListener { binding.setBackClickListener {
goBack() goBack()
} }

View file

@ -33,16 +33,19 @@ import org.linphone.activities.main.MainActivity
import org.linphone.activities.main.conference.adapters.ScheduledConferencesAdapter import org.linphone.activities.main.conference.adapters.ScheduledConferencesAdapter
import org.linphone.activities.main.conference.viewmodels.ScheduledConferencesViewModel import org.linphone.activities.main.conference.viewmodels.ScheduledConferencesViewModel
import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.main.viewmodels.DialogViewModel
import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.activities.navigateToConferenceScheduling import org.linphone.activities.navigateToConferenceScheduling
import org.linphone.activities.navigateToConferenceWaitingRoom import org.linphone.activities.navigateToConferenceWaitingRoom
import org.linphone.databinding.ConferencesScheduledFragmentBinding import org.linphone.databinding.ConferencesScheduledFragmentBinding
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
import org.linphone.utils.RecyclerViewHeaderDecoration import org.linphone.utils.RecyclerViewHeaderDecoration
class ScheduledConferencesFragment : GenericFragment<ConferencesScheduledFragmentBinding>() { class ScheduledConferencesFragment : GenericFragment<ConferencesScheduledFragmentBinding>() {
private lateinit var viewModel: ScheduledConferencesViewModel private lateinit var viewModel: ScheduledConferencesViewModel
private lateinit var adapter: ScheduledConferencesAdapter private lateinit var adapter: ScheduledConferencesAdapter
private lateinit var sharedViewModel: SharedMainViewModel
override fun getLayoutId(): Int = R.layout.conferences_scheduled_fragment override fun getLayoutId(): Int = R.layout.conferences_scheduled_fragment
@ -63,6 +66,10 @@ class ScheduledConferencesFragment : GenericFragment<ConferencesScheduledFragmen
) )
binding.conferenceInfoList.adapter = adapter binding.conferenceInfoList.adapter = adapter
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedMainViewModel::class.java]
}
val layoutManager = LinearLayoutManager(activity) val layoutManager = LinearLayoutManager(activity)
binding.conferenceInfoList.layoutManager = layoutManager binding.conferenceInfoList.layoutManager = layoutManager
@ -97,6 +104,15 @@ class ScheduledConferencesFragment : GenericFragment<ConferencesScheduledFragmen
} }
} }
adapter.editConferenceEvent.observe(
viewLifecycleOwner
) {
it.consume { address ->
sharedViewModel.conferenceInfoToEdit.value = Event(address)
navigateToConferenceScheduling()
}
}
adapter.deleteConferenceInfoEvent.observe( adapter.deleteConferenceInfoEvent.observe(
viewLifecycleOwner viewLifecycleOwner
) { ) {

View file

@ -69,20 +69,9 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
var hour: Int = 0 var hour: Int = 0
var minutes: Int = 0 var minutes: Int = 0
private var confInfo: ConferenceInfo? = null
private val conferenceScheduler = coreContext.core.createConferenceScheduler() private val conferenceScheduler = coreContext.core.createConferenceScheduler()
private val chatRoomListener = object : ChatRoomListenerStub() {
override fun onStateChanged(room: ChatRoom, state: ChatRoom.State) {
if (state == ChatRoom.State.Created) {
Log.i("[Conference Creation] Chat room created")
room.removeListener(this)
} else if (state == ChatRoom.State.CreationFailed) {
Log.e("[Conference Creation] Group chat room creation has failed !")
room.removeListener(this)
}
}
}
private val listener = object : ConferenceSchedulerListenerStub() { private val listener = object : ConferenceSchedulerListenerStub() {
override fun onStateChanged( override fun onStateChanged(
conferenceScheduler: ConferenceScheduler, conferenceScheduler: ConferenceScheduler,
@ -179,6 +168,30 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
super.onCleared() super.onCleared()
} }
fun populateFromConferenceInfo(conferenceInfo: ConferenceInfo) {
confInfo = conferenceInfo
address.value = conferenceInfo.uri
subject.value = conferenceInfo.subject
description.value = conferenceInfo.description
val dateTime = conferenceInfo.dateTime
val calendar = Calendar.getInstance()
calendar.timeInMillis = dateTime * 1000
setDate(calendar.timeInMillis)
setTime(calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE))
val conferenceDuration = conferenceInfo.duration
duration.value = durationList.find { it.value == conferenceDuration }
scheduleForLater.value = conferenceDuration > 0
val participantsList = arrayListOf<Address>()
for (participant in conferenceInfo.participants) {
participantsList.add(participant)
}
selectedAddresses.value = participantsList
computeParticipantsData()
}
fun toggleSchedule() { fun toggleSchedule() {
scheduleForLater.value = scheduleForLater.value == false scheduleForLater.value = scheduleForLater.value == false
} }
@ -221,23 +234,10 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
val core = coreContext.core val core = coreContext.core
val participants = arrayOfNulls<Address>(selectedAddresses.value.orEmpty().size) val participants = arrayOfNulls<Address>(selectedAddresses.value.orEmpty().size)
selectedAddresses.value?.toArray(participants) selectedAddresses.value?.toArray(participants)
val localAddress = core.defaultAccount?.params?.identityAddress val localAccount = core.defaultAccount
val localAddress = localAccount?.params?.identityAddress
// TODO: Temporary workaround for chat room, to be removed once we can get matching chat room from conference val conferenceInfo = confInfo ?: Factory.instance().createConferenceInfo()
/*val chatRoomParams = core.createDefaultChatRoomParams()
chatRoomParams.backend = ChatRoomBackend.FlexisipChat
chatRoomParams.enableGroup(true)
chatRoomParams.subject = subject.value
val chatRoom = core.createChatRoom(chatRoomParams, localAddress, participants)
if (chatRoom == null) {
Log.e("[Conference Creation] Failed to create a chat room with same subject & participants as for conference")
} else {
Log.i("[Conference Creation] Creating chat room with same subject [${subject.value}] & participants as for conference")
chatRoom.addListener(chatRoomListener)
}*/
// END OF TODO
val conferenceInfo = Factory.instance().createConferenceInfo()
conferenceInfo.organizer = localAddress conferenceInfo.organizer = localAddress
conferenceInfo.subject = subject.value conferenceInfo.subject = subject.value
conferenceInfo.description = description.value conferenceInfo.description = description.value
@ -248,7 +248,10 @@ class ConferenceSchedulingViewModel : ContactsSelectionViewModel() {
val duration = duration.value?.value ?: 0 val duration = duration.value?.value ?: 0
conferenceInfo.duration = duration conferenceInfo.duration = duration
} }
conferenceScheduler.info = conferenceInfo // Will trigger the conference creation automatically confInfo = conferenceInfo
conferenceScheduler.account = localAccount
// Will trigger the conference creation/update automatically
conferenceScheduler.info = conferenceInfo
} }
private fun computeTimeZonesList(): List<TimeZoneData> { private fun computeTimeZonesList(): List<TimeZoneData> {

View file

@ -38,6 +38,7 @@ class ScheduledConferencesViewModel : ViewModel() {
conferencesList.addAll(conferences.value.orEmpty()) conferencesList.addAll(conferences.value.orEmpty())
val data = ScheduledConferenceData(conferenceInfo) val data = ScheduledConferenceData(conferenceInfo)
conferencesList.add(data) conferencesList.add(data)
conferencesList.sortBy { it.conferenceInfo.dateTime }
conferences.value = conferencesList conferences.value = conferencesList
} }
} }

View file

@ -95,6 +95,12 @@ class SharedMainViewModel : ViewModel() {
var pendingCallTransfer: Boolean = false var pendingCallTransfer: Boolean = false
/* Conference */
val conferenceInfoToEdit: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
/* Dialer */ /* Dialer */
var dialerUri: String = "" var dialerUri: String = ""

View file

@ -95,7 +95,7 @@
android:visibility="@{data.expanded ? View.GONE : View.VISIBLE}" android:visibility="@{data.expanded ? View.GONE : View.VISIBLE}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="5dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:layout_marginEnd="5dp" android:layout_marginEnd="5dp"
@ -125,6 +125,7 @@
android:visibility="@{data.expanded ? View.VISIBLE : View.GONE, default=gone}" android:visibility="@{data.expanded ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"> android:layout_marginEnd="5dp">
@ -255,7 +256,7 @@
<ImageView <ImageView
android:onClick="@{editConferenceClickListener}" android:onClick="@{editConferenceClickListener}"
android:visibility="gone" android:visibility="@{data.canEdit ? View.VISIBLE : View.GONE, default=gone}"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_gravity="center" android:layout_gravity="center"