diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingFragment.kt index 7d5a0473d..a34b631ef 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingFragment.kt @@ -223,14 +223,16 @@ class MeetingFragment : SlidingPaneChildFragment() { ) val isUserOrganizer = viewModel.isEditable.value == true && viewModel.isCancelled.value == false - popupView.cancelInsteadOfDelete = isUserOrganizer + val hasNotStartedYet = viewModel.hasNotStartedYet.value == true + val showCancelActionInsteadOfDelete = isUserOrganizer && hasNotStartedYet + popupView.cancelInsteadOfDelete = showCancelActionInsteadOfDelete popupView.setDeleteClickListener { - if (isUserOrganizer) { - // In case we are organizer of the meeting, ask user confirmation before cancelling it + if (isUserOrganizer && hasNotStartedYet) { + Log.i("$TAG Meeting start hasn't started yet and we are the organizer, asking user if it should be cancelled") showCancelMeetingDialog() } else { - // If we're not organizer, ask user confirmation of removing itself from participants & deleting it locally - showDeleteMeetingDialog() + Log.i("$TAG Deleting meeting [${viewModel.sipUri}]") + viewModel.delete() } popupWindow.dismiss() } @@ -299,28 +301,16 @@ class MeetingFragment : SlidingPaneChildFragment() { model.confirmEvent.observe(viewLifecycleOwner) { it.consume { - viewModel.cancel() + Log.i("$TAG Cancelling meeting [${viewModel.sipUri}] and sending notification to participants") + viewModel.cancel(true) dialog.dismiss() } } - dialog.show() - } - - private fun showDeleteMeetingDialog() { - Log.i("$TAG Meeting is not editable or already cancelled, asking whether to delete it or not") - val model = ConfirmationDialogModel() - val dialog = DialogUtils.getDeleteMeetingDialog(requireContext(), model) - - model.dismissEvent.observe(viewLifecycleOwner) { + model.alternativeChoiceEvent.observe(viewLifecycleOwner) { it.consume { - dialog.dismiss() - } - } - - model.confirmEvent.observe(viewLifecycleOwner) { - it.consume { - viewModel.delete() + Log.i("$TAG Cancelling meeting [${viewModel.sipUri}] without notifying participants") + viewModel.cancel(false) dialog.dismiss() } } diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt index 11f59c3c0..1f93f4a25 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsListFragment.kt @@ -166,7 +166,7 @@ class MeetingsListFragment : AbstractMainFragment() { listViewModel.fetchInProgress.value = false } - listViewModel.conferenceCancelledEvent.observe(viewLifecycleOwner) { + listViewModel.cancelMeetingViewModel.conferenceCancelledEvent.observe(viewLifecycleOwner) { it.consume { Log.i("$TAG Meeting has been cancelled successfully, deleting it") (requireActivity() as GenericActivity).showGreenToast( @@ -188,24 +188,20 @@ class MeetingsListFragment : AbstractMainFragment() { adapter.meetingLongClickedEvent.observe(viewLifecycleOwner) { it.consume { model -> val isUserOrganizer = model.isMyselfOrganizer && !model.isCancelled + val showCancelActionInsteadOfDelete = isUserOrganizer && model.hasNotStartedYet val modalBottomSheet = MeetingsMenuDialogFragment( - isUserOrganizer, + showCancelActionInsteadOfDelete, { // onDismiss adapter.resetSelection() }, { // onDelete - if (isUserOrganizer) { + if (showCancelActionInsteadOfDelete) { + Log.i("$TAG Meeting start hasn't started yet and we are the organizer, asking user if it should be cancelled") showCancelMeetingDialog(model) } else { - showDeleteMeetingDialog(model) - /*Log.i("$TAG Deleting meeting [${model.id}]") + 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 - )*/ } } ) @@ -335,42 +331,22 @@ class MeetingsListFragment : AbstractMainFragment() { model.confirmEvent.observe(viewLifecycleOwner) { it.consume { - Log.i("$TAG Cancelling meeting [${meetingModel.id}]") + Log.i("$TAG Cancelling meeting [${meetingModel.id}] and sending notification to participants") meetingViewModelBeingCancelled = meetingModel - listViewModel.cancelMeeting(meetingModel.conferenceInfo) + listViewModel.cancelMeetingViewModel.cancelMeeting(meetingModel.conferenceInfo, true) dialog.dismiss() } } - dialog.show() - } - - private fun showDeleteMeetingDialog(meetingModel: MeetingModel) { - Log.i("$TAG Meeting is not editable or already cancelled, asking whether to deleting it or not") - - val model = ConfirmationDialogModel() - val dialog = DialogUtils.getDeleteMeetingDialog(requireContext(), model) - - model.dismissEvent.observe(viewLifecycleOwner) { + model.alternativeChoiceEvent.observe(viewLifecycleOwner) { it.consume { + Log.i("$TAG Cancelling meeting [${meetingModel.id}] without notifying participants") + meetingViewModelBeingCancelled = meetingModel + listViewModel.cancelMeetingViewModel.cancelMeeting(meetingModel.conferenceInfo, false) dialog.dismiss() } } - model.confirmEvent.observe(viewLifecycleOwner) { - it.consume { - 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 - ) - } - } - dialog.show() } } diff --git a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsMenuDialogFragment.kt index e5529097b..08ed41870 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/fragment/MeetingsMenuDialogFragment.kt @@ -33,7 +33,7 @@ import org.linphone.databinding.MeetingsListLongPressMenuBinding @UiThread class MeetingsMenuDialogFragment( - private val isUserOrganizer: Boolean, + private val showCancelActionInsteadOfDelete: Boolean, private val onDismiss: (() -> Unit)? = null, private val onDeleteMeeting: (() -> Unit)? = null ) : BottomSheetDialogFragment() { @@ -65,7 +65,7 @@ class MeetingsMenuDialogFragment( savedInstanceState: Bundle? ): View { val view = MeetingsListLongPressMenuBinding.inflate(layoutInflater) - view.cancelInsteadOfDelete = isUserOrganizer + view.cancelInsteadOfDelete = showCancelActionInsteadOfDelete view.setDeleteClickListener { onDeleteMeeting?.invoke() diff --git a/app/src/main/java/org/linphone/ui/main/meetings/model/MeetingModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/model/MeetingModel.kt index a648ae859..dbf627c38 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/model/MeetingModel.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/model/MeetingModel.kt @@ -49,6 +49,8 @@ class MeetingModel val isAfterToday = TimestampUtils.isAfterToday(timestamp) + val hasNotStartedYet = timestamp * 1000 > System.currentTimeMillis() + private val startTime = TimestampUtils.timeToString(timestamp) private val endTime = TimestampUtils.timeToString(timestamp + (conferenceInfo.duration * 60)) diff --git a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/CancelMeetingViewModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/CancelMeetingViewModel.kt new file mode 100644 index 000000000..57594aa11 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/CancelMeetingViewModel.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2010-2025 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.linphone.ui.main.meetings.viewmodel + +import androidx.annotation.UiThread +import androidx.lifecycle.MutableLiveData +import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.LinphoneApplication.Companion.corePreferences +import org.linphone.core.Address +import org.linphone.core.ConferenceInfo +import org.linphone.core.ConferenceScheduler +import org.linphone.core.ConferenceSchedulerListenerStub +import org.linphone.core.tools.Log +import org.linphone.ui.GenericViewModel +import org.linphone.utils.Event +import org.linphone.utils.LinphoneUtils + +open class CancelMeetingViewModel + @UiThread + constructor() : GenericViewModel() { + companion object { + private const val TAG = "[Cancel Meeting ViewModel]" + } + + val operationInProgress = MutableLiveData() + + val conferenceCancelledEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + + private var sendNotificationForCancelledConference: Boolean = false + + 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} has been cancelled" + ) + if (sendNotificationForCancelledConference) { + Log.i("$TAG Sending cancelled meeting ICS to participants") + val params = LinphoneUtils.getChatRoomParamsToCancelMeeting() + if (params != null && !corePreferences.disableChat) { + conferenceScheduler.sendInvitations(params) + } else { + Log.e("$TAG Failed to get chat room params to send cancelled meeting ICS!") + operationInProgress.postValue(false) + } + } else { + operationInProgress.postValue(false) + conferenceCancelledEvent.postValue(Event(true)) + } + } else if (state == ConferenceScheduler.State.Error) { + operationInProgress.postValue(false) + // TODO FIXME: show error to user + } + } + + override fun onInvitationsSent( + conferenceScheduler: ConferenceScheduler, + failedInvitations: Array? + ) { + if (failedInvitations?.isNotEmpty() == true) { + // TODO FIXME: show error to user + 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 + } + + @UiThread + fun cancelMeeting(conferenceInfo: ConferenceInfo, sendNotification: Boolean) { + coreContext.postOnCoreThread { core -> + Log.w("$TAG Cancelling conference info [${conferenceInfo.uri?.asStringUriOnly()}]") + sendNotificationForCancelledConference = sendNotification + operationInProgress.postValue(true) + + val conferenceScheduler = LinphoneUtils.createConferenceScheduler( + LinphoneUtils.getDefaultAccount() + ) + conferenceScheduler.addListener(conferenceSchedulerListener) + conferenceScheduler.cancelConference(conferenceInfo) + } + } +} diff --git a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt index c0490fa0d..799ed3855 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingViewModel.kt @@ -24,24 +24,18 @@ import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import java.util.TimeZone import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.LinphoneApplication.Companion.corePreferences -import org.linphone.core.Address import org.linphone.core.ConferenceInfo -import org.linphone.core.ConferenceScheduler -import org.linphone.core.ConferenceSchedulerListenerStub import org.linphone.core.Factory import org.linphone.core.Participant import org.linphone.core.tools.Log -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() { + constructor() : CancelMeetingViewModel() { companion object { private const val TAG = "[Meeting ViewModel]" } @@ -60,6 +54,8 @@ class MeetingViewModel val timezone = MutableLiveData() + val hasNotStartedYet = MutableLiveData() + val description = MutableLiveData() val speakers = MutableLiveData>() @@ -73,65 +69,12 @@ class MeetingViewModel val startTimeStamp = MutableLiveData() val endTimeStamp = MutableLiveData() - val operationInProgress = MutableLiveData() - - val conferenceCancelledEvent: MutableLiveData> by lazy { - MutableLiveData>() - } - val conferenceInfoDeletedEvent: MutableLiveData> by lazy { MutableLiveData>() } - 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 && !corePreferences.disableChat) { - conferenceScheduler.sendInvitations(params) - } else { - operationInProgress.postValue(false) - } - } else if (state == ConferenceScheduler.State.Error) { - operationInProgress.postValue(false) - } - } - - override fun onInvitationsSent( - conferenceScheduler: ConferenceScheduler, - failedInvitations: Array? - ) { - 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)) - } - } - private lateinit var conferenceInfo: ConferenceInfo - init { - operationInProgress.value = false - } - @UiThread fun findConferenceInfo(meeting: ConferenceInfo?, uri: String) { coreContext.postOnCoreThread { core -> @@ -182,21 +125,6 @@ class MeetingViewModel } } - @UiThread - fun cancel() { - coreContext.postOnCoreThread { core -> - if (::conferenceInfo.isInitialized) { - Log.i("$TAG Cancelling conference information [$conferenceInfo]") - operationInProgress.postValue(true) - val conferenceScheduler = LinphoneUtils.createConferenceScheduler( - LinphoneUtils.getDefaultAccount() - ) - conferenceScheduler.addListener(conferenceSchedulerListener) - conferenceScheduler.cancelConference(conferenceInfo) - } - } - } - @UiThread fun refreshInfo(uri: String) { coreContext.postOnCoreThread { core -> @@ -243,6 +171,7 @@ class MeetingViewModel endTimeStamp.postValue(end * 1000) val displayedTimestamp = "$date | $startTime - $endTime" dateTime.postValue(displayedTimestamp) + hasNotStartedYet.postValue(timestamp * 1000 > System.currentTimeMillis()) Log.i("$TAG Conference is scheduled for [$displayedTimestamp]") timezone.postValue(TimeZoneModel(TimeZone.getDefault()).toString()) @@ -311,4 +240,11 @@ class MeetingViewModel speakers.postValue(speakersList) participants.postValue(participantsList) } + + @UiThread + fun cancel(sendNotification: Boolean) { + if (::conferenceInfo.isInitialized) { + cancelMeeting(conferenceInfo, sendNotification) + } + } } diff --git a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingsListViewModel.kt index 35aa87bf3..d82254146 100644 --- a/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingsListViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/meetings/viewmodel/MeetingsListViewModel.kt @@ -23,21 +23,15 @@ import androidx.annotation.UiThread import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.core.Account import org.linphone.core.AccountListenerStub -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 @@ -51,11 +45,7 @@ class MeetingsListViewModel val fetchInProgress = MutableLiveData() - val operationInProgress = MutableLiveData() - - val conferenceCancelledEvent: MutableLiveData> by lazy { - MutableLiveData>() - } + val cancelMeetingViewModel = CancelMeetingViewModel() private val coreListener = object : CoreListenerStub() { @WorkerThread @@ -78,51 +68,7 @@ class MeetingsListViewModel } } - 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 && !corePreferences.disableChat) { - conferenceScheduler.sendInvitations(params) - } else { - operationInProgress.postValue(false) - } - } else if (state == ConferenceScheduler.State.Error) { - operationInProgress.postValue(false) - } - } - - override fun onInvitationsSent( - conferenceScheduler: ConferenceScheduler, - failedInvitations: Array? - ) { - 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 -> @@ -150,19 +96,6 @@ class MeetingsListViewModel } } - @UiThread - fun cancelMeeting(conferenceInfo: ConferenceInfo) { - coreContext.postOnCoreThread { core -> - Log.w("$TAG Cancelling conference info [${conferenceInfo.uri?.asStringUriOnly()}]") - operationInProgress.postValue(true) - val conferenceScheduler = LinphoneUtils.createConferenceScheduler( - LinphoneUtils.getDefaultAccount() - ) - conferenceScheduler.addListener(conferenceSchedulerListener) - conferenceScheduler.cancelConference(conferenceInfo) - } - } - @WorkerThread private fun computeMeetingsListFromLocallyStoredInfo() { var source = coreContext.core.defaultAccount?.conferenceInformationList diff --git a/app/src/main/res/layout-land/meetings_list_fragment.xml b/app/src/main/res/layout-land/meetings_list_fragment.xml index b657c6436..187dd63fb 100644 --- a/app/src/main/res/layout-land/meetings_list_fragment.xml +++ b/app/src/main/res/layout-land/meetings_list_fragment.xml @@ -112,7 +112,7 @@ + bind:visibility="@{viewModel.cancelMeetingViewModel.operationInProgress}" /> diff --git a/app/src/main/res/layout/dialog_cancel_meeting.xml b/app/src/main/res/layout/dialog_cancel_meeting.xml index 9f78b8cc5..c92455975 100644 --- a/app/src/main/res/layout/dialog_cancel_meeting.xml +++ b/app/src/main/res/layout/dialog_cancel_meeting.xml @@ -37,7 +37,7 @@ android:layout_height="wrap_content" android:layout_marginStart="15dp" android:paddingTop="@dimen/dialog_top_bottom_margin" - android:text="@string/meeting_schedule_cancel_dialog_title" + android:text="@string/meeting_schedule_notify_cancel_dialog_title" app:layout_constraintVertical_chainStyle="packed" app:layout_constraintBottom_toTopOf="@id/message" app:layout_constraintStart_toStartOf="@id/dialog_background" @@ -52,7 +52,7 @@ android:layout_marginStart="15dp" android:layout_marginEnd="15dp" android:layout_marginTop="10dp" - android:text="@string/meeting_schedule_cancel_dialog_message" + android:text="@string/meeting_schedule_notify_cancel_dialog_message" android:textSize="14sp" app:layout_constraintBottom_toTopOf="@id/cancel" app:layout_constraintStart_toStartOf="@id/dialog_background" @@ -62,7 +62,7 @@ + + diff --git a/app/src/main/res/layout/meetings_list_fragment.xml b/app/src/main/res/layout/meetings_list_fragment.xml index c789d793a..6cd498242 100644 --- a/app/src/main/res/layout/meetings_list_fragment.xml +++ b/app/src/main/res/layout/meetings_list_fragment.xml @@ -103,7 +103,7 @@ + bind:visibility="@{viewModel.cancelMeetingViewModel.operationInProgress}" /> diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d0d21864d..323aa7f57 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -42,6 +42,7 @@ Retirer Confirmer J\'ai compris + Ne pas annuler Notifications d\'appels en cours @@ -643,8 +644,8 @@ Réunion introuvable ! Description Modifier la réunion - Annuler la réunion? - Voulez-vous annuler la réunion et envoyer une notification aux participants ? + La réunion va être annulée + Voulez-vous envoyer une notification aux participants ? Annuler la réunion Supprimer la réunion ? Voulez-vous supprimer la réunion ? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f658a6f8f..53f1e3981 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -83,6 +83,7 @@ Remove Confirm Understood + Do not cancel Active calls notifications @@ -686,8 +687,8 @@ Meeting cannot be found! Description Edit meeting - Cancel the meeting? - Do you want to cancel the meeting and send a notification to all participants? + Meeting will be cancelled + Do you want to send a notification to all participants? Cancel meeting Delete the meeting? Do you want to delete the meeting?