Added dialog to confirm removing a participant from conference

This commit is contained in:
Sylvain Berfini 2024-04-24 14:33:40 +02:00
parent 79961739e0
commit fdafcfd7a4
7 changed files with 209 additions and 8 deletions

View file

@ -29,11 +29,14 @@ import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import org.linphone.R
import org.linphone.core.Participant
import org.linphone.core.tools.Log
import org.linphone.databinding.CallConferenceParticipantsListFragmentBinding
import org.linphone.ui.call.adapter.ConferenceParticipantsListAdapter
import org.linphone.ui.call.fragment.GenericCallFragment
import org.linphone.ui.call.viewmodel.CurrentCallViewModel
import org.linphone.ui.main.history.model.ConfirmationDialogModel
import org.linphone.utils.DialogUtils
class ConferenceParticipantsListFragment : GenericCallFragment() {
companion object {
@ -102,5 +105,38 @@ class ConferenceParticipantsListFragment : GenericCallFragment() {
Log.i("$TAG participants list updated with [${it.size}] items")
adapter.submitList(it)
}
viewModel.conferenceModel.removeParticipantEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val displayName = pair.first
val participant = pair.second
showKickParticipantDialog(displayName, participant)
}
}
}
private fun showKickParticipantDialog(displayName: String, participant: Participant) {
val model = ConfirmationDialogModel()
val dialog = DialogUtils.getKickConferenceParticipantConfirmationDialog(
requireActivity(),
model,
displayName
)
model.dismissEvent.observe(viewLifecycleOwner) {
it.consume {
dialog.dismiss()
}
}
model.confirmEvent.observe(viewLifecycleOwner) {
it.consume {
viewModel.conferenceModel.kickParticipant(participant)
// TODO: notify participant was kicked out
dialog.dismiss()
}
}
dialog.show()
}
}

View file

@ -25,10 +25,12 @@ import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.Participant
import org.linphone.core.tools.Log
import org.linphone.ui.main.contacts.model.ContactAvatarModel
class ConferenceParticipantModel @WorkerThread constructor(
val participant: Participant,
val isMyselfAdmin: Boolean,
val avatarModel: ContactAvatarModel,
isMyselfAdmin: Boolean,
val isMyself: Boolean,
private val removeFromConference: ((participant: Participant) -> Unit)?,
private val changeAdminStatus: ((participant: Participant, setAdmin: Boolean) -> Unit)?
@ -39,10 +41,6 @@ class ConferenceParticipantModel @WorkerThread constructor(
val sipUri = participant.address.asStringUriOnly()
val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(
participant.address
)
val isAdmin = MutableLiveData<Boolean>()
val isMeAdmin = MutableLiveData<Boolean>()

View file

@ -76,6 +76,10 @@ class ConferenceViewModel {
MutableLiveData<Event<Boolean>>()
}
val removeParticipantEvent: MutableLiveData<Event<Pair<String, Participant>>> by lazy {
MutableLiveData<Event<Pair<String, Participant>>>()
}
private lateinit var conference: Conference
private val conferenceListener = object : ConferenceListenerStub() {
@ -311,6 +315,16 @@ class ConferenceViewModel {
}
}
@WorkerThread
fun kickParticipant(participant: Participant) {
coreContext.postOnCoreThread {
Log.i(
"$TAG Kicking participant [${participant.address.asStringUriOnly()}] out of conference"
)
conference.removeParticipant(participant)
}
}
@WorkerThread
fun setNewLayout(newLayout: Int) {
val call = conference.call
@ -408,12 +422,18 @@ class ConferenceViewModel {
Log.i(
"$TAG Participant [${participant.address.asStringUriOnly()}] has [${devices.size}] devices and role [${role.name}]"
)
val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(
participant.address
)
val participantModel = ConferenceParticipantModel(
participant,
avatarModel,
admin,
false,
{ participant -> // Remove from conference
conference.removeParticipant(participant)
removeParticipantEvent.postValue(
Event(Pair(avatarModel.name.value.orEmpty(), participant))
)
},
{ participant, setAdmin -> // Change admin status
conference.setParticipantAdminStatus(participant, setAdmin)
@ -443,7 +463,17 @@ class ConferenceViewModel {
"$TAG [${devicesList.size}] participant devices for [${participantsList.size}] participants will be displayed (not counting ourselves)"
)
val meParticipantModel = ConferenceParticipantModel(meParticipant, admin, true, null, null)
val meAvatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(
meParticipant.address
)
val meParticipantModel = ConferenceParticipantModel(
meParticipant,
meAvatarModel,
admin,
true,
null,
null
)
participantsList.add(meParticipantModel)
val ourDevices = conference.me.devices
@ -558,12 +588,18 @@ class ConferenceViewModel {
val list = arrayListOf<ConferenceParticipantModel>()
list.addAll(participants.value.orEmpty())
val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(
participant.address
)
val newModel = ConferenceParticipantModel(
participant,
avatarModel,
isMeAdmin.value == true,
false,
{ participant -> // Remove from conference
conference.removeParticipant(participant)
removeParticipantEvent.postValue(
Event(Pair(avatarModel.name.value.orEmpty(), participant))
)
},
{ participant, setAdmin -> // Change admin status
conference.setParticipantAdminStatus(participant, setAdmin)

View file

@ -41,6 +41,7 @@ import org.linphone.databinding.DialogConfirmZrtpSasBinding
import org.linphone.databinding.DialogContactConfirmTrustCallBinding
import org.linphone.databinding.DialogContactTrustProcessBinding
import org.linphone.databinding.DialogDeleteContactBinding
import org.linphone.databinding.DialogKickFromConferenceBinding
import org.linphone.databinding.DialogManageAccountInternationalPrefixHelpBinding
import org.linphone.databinding.DialogMergeCallsIntoConferenceBinding
import org.linphone.databinding.DialogPickNumberOrAddressBinding
@ -355,6 +356,27 @@ class DialogUtils {
return getDialog(context, binding)
}
@UiThread
fun getKickConferenceParticipantConfirmationDialog(
context: Context,
viewModel: ConfirmationDialogModel,
displayName: String
): Dialog {
val binding: DialogKickFromConferenceBinding = DataBindingUtil.inflate(
LayoutInflater.from(context),
R.layout.dialog_kick_from_conference,
null,
false
)
binding.viewModel = viewModel
binding.title.text = context.getString(
R.string.conference_confirm_removing_participant_dialog_title,
displayName
)
return getDialog(context, binding)
}
@UiThread
fun getCancelMeetingDialog(
context: Context,

View file

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="android.view.View" />
<import type="android.graphics.Typeface" />
<variable
name="viewModel"
type="org.linphone.ui.main.history.model.ConfirmationDialogModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:onClick="@{() -> viewModel.dismiss()}"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/dialog_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="2dp"
android:src="@drawable/shape_dialog_background"
app:layout_constraintWidth_max="@dimen/dialog_max_width"
app:layout_constraintBottom_toBottomOf="@id/anchor"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/section_header_style"
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:paddingTop="@dimen/dialog_top_bottom_margin"
android:text="@string/conference_confirm_removing_participant_dialog_title"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintBottom_toTopOf="@id/message"
app:layout_constraintStart_toStartOf="@id/dialog_background"
app:layout_constraintEnd_toEndOf="@id/dialog_background"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:layout_marginTop="10dp"
android:text="@string/conference_confirm_removing_participant_dialog_message"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@id/cancel"
app:layout_constraintStart_toStartOf="@id/dialog_background"
app:layout_constraintEnd_toEndOf="@id/dialog_background"
app:layout_constraintTop_toBottomOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView
android:onClick="@{() -> viewModel.dismiss()}"
style="@style/secondary_button_label_style"
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:text="@string/dialog_cancel"
app:layout_constraintStart_toStartOf="@id/dialog_background"
app:layout_constraintEnd_toEndOf="@id/dialog_background"
app:layout_constraintTop_toBottomOf="@id/message"
app:layout_constraintBottom_toTopOf="@id/confirm"/>
<androidx.appcompat.widget.AppCompatTextView
android:onClick="@{() -> viewModel.confirm()}"
style="@style/primary_button_label_style"
android:id="@+id/confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:text="@string/dialog_remove"
app:layout_constraintStart_toStartOf="@id/dialog_background"
app:layout_constraintEnd_toEndOf="@id/dialog_background"
app:layout_constraintTop_toBottomOf="@id/cancel"
app:layout_constraintBottom_toTopOf="@id/anchor"/>
<View
android:id="@+id/anchor"
android:layout_width="wrap_content"
android:layout_height="@dimen/dialog_top_bottom_margin"
app:layout_constraintTop_toBottomOf="@id/confirm"
app:layout_constraintStart_toStartOf="@id/dialog_background"
app:layout_constraintEnd_toEndOf="@id/dialog_background"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -38,6 +38,7 @@
<string name="dialog_do_not_show_anymore">Ne plus me montrer ce message</string>
<string name="dialog_no">Non</string>
<string name="dialog_yes">Oui</string>
<string name="dialog_remove">Retirer</string>
<!-- Related to Android notifications -->
<string name="notification_channel_call_name">&appName; notifications d\'appels en cours</string>
@ -585,6 +586,8 @@
<item quantity="one">%s participant</item>
<item quantity="other">%s participants</item>
</plurals>
<string name="conference_confirm_removing_participant_dialog_title">Retirer %s de la conférence ?</string>
<string name="conference_confirm_removing_participant_dialog_message">Voulez-vous vraiment retirer ce participant de la conférence ?</string>
<string name="conference_participant_joining_text">En train de rejoindre…</string>
<string name="conference_participant_paused_text">En pause</string>
<string name="conference_active_speaker_is_screen_sharing">partage son écran</string>

View file

@ -73,6 +73,7 @@
<string name="dialog_do_not_show_anymore">Do not show this dialog anymore</string>
<string name="dialog_no">No</string>
<string name="dialog_yes">Yes</string>
<string name="dialog_remove">Remove</string>
<!-- Related to Android notifications -->
<string name="notification_channel_call_name">&appName; active calls notifications</string>
@ -621,6 +622,8 @@
<item quantity="one">%s participant</item>
<item quantity="other">%s participants</item>
</plurals>
<string name="conference_confirm_removing_participant_dialog_title">Remove %s from conference?</string>
<string name="conference_confirm_removing_participant_dialog_message">Are you sure you want to remove this participant from the conference?</string>
<string name="conference_participant_joining_text">Joining…</string>
<string name="conference_participant_paused_text">Paused</string>
<string name="conference_active_speaker_is_screen_sharing">is sharing it\'s screen</string>