Update conversation subject when leaving info fragment + removed todos, replaced by toasts

This commit is contained in:
Sylvain Berfini 2023-11-22 10:42:29 +01:00
parent a64e13a021
commit f35df5e418
10 changed files with 238 additions and 30 deletions

View file

@ -61,6 +61,7 @@ import org.linphone.core.ChatMessage
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatBubbleLongPressMenuBinding
import org.linphone.databinding.ChatConversationFragmentBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.chat.adapter.ChatMessageBottomSheetAdapter
import org.linphone.ui.main.chat.adapter.ConversationEventAdapter
import org.linphone.ui.main.chat.model.ChatMessageDeliveryModel
@ -231,7 +232,8 @@ class ConversationFragment : GenericFragment() {
(view.parent as? ViewGroup)?.doOnPreDraw {
Log.e("$TAG Failed to find chat room, going back")
goBack()
// TODO: show toast
val message = getString(R.string.toast_cant_find_conversation_to_display)
(requireActivity() as MainActivity).showRedToast(message, R.drawable.x)
}
} else {
sendMessageViewModel.configureChatRoom(viewModel.chatRoom)
@ -343,6 +345,14 @@ class ConversationFragment : GenericFragment() {
}
}
sendMessageViewModel.showRedToastEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val message = pair.first
val icon = pair.second
(requireActivity() as MainActivity).showRedToast(message, icon)
}
}
viewModel.searchFilter.observe(viewLifecycleOwner) { filter ->
viewModel.applyFilter(filter.trim())
}
@ -432,6 +442,13 @@ class ConversationFragment : GenericFragment() {
}
}
sharedViewModel.forceRefreshConversationInfo.observe(viewLifecycleOwner) {
it.consume {
Log.i("$TAG Force refreshing conversation info")
viewModel.refresh()
}
}
sharedViewModel.forceRefreshConversationEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i("$TAG Force refreshing chat messages list")

View file

@ -36,6 +36,7 @@ import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatInfoFragmentBinding
import org.linphone.databinding.ChatParticipantAdminPopupMenuBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.chat.adapter.ConversationParticipantsAdapter
import org.linphone.ui.main.chat.model.ConversationEditSubjectDialogModel
import org.linphone.ui.main.chat.model.ParticipantModel
@ -113,7 +114,8 @@ class ConversationInfoFragment : GenericFragment() {
(view.parent as? ViewGroup)?.doOnPreDraw {
Log.e("$TAG Failed to find chat room, going back")
goBack()
// TODO: show toast ?
val message = getString(R.string.toast_cant_find_conversation_to_display)
(requireActivity() as MainActivity).showRedToast(message, R.drawable.x)
}
}
}
@ -129,18 +131,26 @@ class ConversationInfoFragment : GenericFragment() {
viewModel.groupLeftEvent.observe(viewLifecycleOwner) {
it.consume {
// TODO: show toast ?
Log.i("$TAG Group has been left, leaving conversation info...")
goBack()
val message = getString(R.string.toast_group_conversation_left)
(requireActivity() as MainActivity).showGreenToast(
message,
R.drawable.chat_teardrop_text
)
}
}
viewModel.historyDeletedEvent.observe(viewLifecycleOwner) {
it.consume {
// TODO: show toast ?
Log.i("$TAG History has been deleted, leaving conversation info...")
sharedViewModel.forceRefreshConversationEvent.value = Event(true)
goBack()
val message = getString(R.string.toast_conversation_history_deleted)
(requireActivity() as MainActivity).showGreenToast(
message,
R.drawable.chat_teardrop_text
)
}
}
@ -160,6 +170,28 @@ class ConversationInfoFragment : GenericFragment() {
}
}
viewModel.infoChangedEvent.observe(viewLifecycleOwner) {
it.consume {
sharedViewModel.forceRefreshConversationInfo.postValue(Event(true))
}
}
viewModel.showGreenToastEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val message = pair.first
val icon = pair.second
(requireActivity() as MainActivity).showGreenToast(message, icon)
}
}
viewModel.showRedToastEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val message = pair.first
val icon = pair.second
(requireActivity() as MainActivity).showRedToast(message, icon)
}
}
sharedViewModel.listOfSelectedSipUrisEvent.observe(viewLifecycleOwner) {
it.consume { list ->
Log.i("$TAG Found [${list.size}] new participants to add to the group, let's do it")
@ -223,7 +255,8 @@ class ConversationInfoFragment : GenericFragment() {
sharedViewModel.showContactEvent.value = Event(refKey)
} else {
Log.e("$TAG Can't go to contact page, friend ref key is null or empty!")
// TODO: show toast
val message = getString(R.string.toast_cant_find_contact_to_display)
(requireActivity() as MainActivity).showRedToast(message, R.drawable.x)
}
}
@ -236,7 +269,8 @@ class ConversationInfoFragment : GenericFragment() {
sharedViewModel.showNewContactEvent.value = Event(true)
} else {
Log.e("$TAG Can't add empty/null SIP URI to contacts!")
// TODO: show toast
val message = getString(R.string.toast_no_address_to_add_to_contact)
(requireActivity() as MainActivity).showRedToast(message, R.drawable.x)
}
}
@ -300,16 +334,24 @@ class ConversationInfoFragment : GenericFragment() {
sharedViewModel.showContactEvent.value = Event(friendRefKey)
} else {
Log.e("$TAG Can't go to contact page, friend ref key is null or empty!")
// TODO: show toast
val message = getString(R.string.toast_cant_find_contact_to_display)
(requireActivity() as MainActivity).showRedToast(message, R.drawable.x)
}
popupWindow.dismiss()
}
popupView.setAddToContactsClickListener {
Log.i("$TAG Trying to add participant [${participantModel.sipUri}] to contacts")
sharedViewModel.sipAddressToAddToNewContact = participantModel.sipUri
sharedViewModel.navigateToContactsEvent.value = Event(true)
sharedViewModel.showNewContactEvent.value = Event(true)
val sipUri = participantModel.sipUri
if (sipUri.isNotEmpty()) {
Log.i("$TAG Trying to add participant [${participantModel.sipUri}] to contacts")
sharedViewModel.sipAddressToAddToNewContact = sipUri
sharedViewModel.navigateToContactsEvent.value = Event(true)
sharedViewModel.showNewContactEvent.value = Event(true)
} else {
Log.e("$TAG Can't add empty/null SIP URI to contacts!")
val message = getString(R.string.toast_no_address_to_add_to_contact)
(requireActivity() as MainActivity).showRedToast(message, R.drawable.x)
}
popupWindow.dismiss()
}

View file

@ -25,6 +25,7 @@ import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Address
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
@ -34,6 +35,7 @@ import org.linphone.core.Friend
import org.linphone.core.tools.Log
import org.linphone.ui.main.chat.model.ParticipantModel
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
@ -74,6 +76,10 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
MutableLiveData<Event<Boolean>>()
}
val infoChangedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val showParticipantAdminPopupMenuEvent: MutableLiveData<Event<Pair<View, ParticipantModel>>> by lazy {
MutableLiveData<Event<Pair<View, ParticipantModel>>>()
}
@ -82,21 +88,39 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
MutableLiveData<Event<ArrayList<String>>>()
}
val showGreenToastEvent: MutableLiveData<Event<Pair<String, Int>>> by lazy {
MutableLiveData<Event<Pair<String, Int>>>()
}
val showRedToastEvent: MutableLiveData<Event<Pair<String, Int>>> by lazy {
MutableLiveData<Event<Pair<String, Int>>>()
}
private lateinit var chatRoom: ChatRoom
private val chatRoomListener = object : ChatRoomListenerStub() {
@WorkerThread
override fun onParticipantAdded(chatRoom: ChatRoom, eventLog: EventLog) {
Log.i("$TAG A participant has been added to the group [${chatRoom.subject}]")
// TODO: show toast
val message = AppUtils.getString(
R.string.toast_participant_added_to_conversation
)
showGreenToastEvent.postValue(Event(Pair(message, R.drawable.user_circle)))
computeParticipantsList()
infoChangedEvent.postValue(Event(true))
}
@WorkerThread
override fun onParticipantRemoved(chatRoom: ChatRoom, eventLog: EventLog) {
Log.i("$TAG A participant has been removed from the group [${chatRoom.subject}]")
// TODO: show toast
val message = AppUtils.getString(
R.string.toast_participant_removed_from_conversation
)
showGreenToastEvent.postValue(Event(Pair(message, R.drawable.user_circle)))
computeParticipantsList()
infoChangedEvent.postValue(Event(true))
}
@WorkerThread
@ -104,7 +128,17 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
Log.i(
"$TAG A participant has been given/removed administration rights for group [${chatRoom.subject}]"
)
// TODO: show toast
val message = if (eventLog.type == EventLog.Type.ConferenceParticipantSetAdmin) {
AppUtils.getString(
R.string.toast_participant_has_been_granted_admin_rights
)
} else {
AppUtils.getString(
R.string.toast_participant_no_longer_has_admin_rights
)
}
showGreenToastEvent.postValue(Event(Pair(message, R.drawable.user_circle)))
// TODO FIXME: list doesn't have the changes...
computeParticipantsList()
}
@ -114,14 +148,36 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
Log.i(
"$TAG Chat room [${LinphoneUtils.getChatRoomId(chatRoom)}] has a new subject [${chatRoom.subject}]"
)
// TODO: show toast
val message = AppUtils.getString(
R.string.toast_conversation_subject_changed
)
showGreenToastEvent.postValue(Event(Pair(message, R.drawable.check)))
subject.postValue(chatRoom.subject)
infoChangedEvent.postValue(Event(true))
}
@WorkerThread
override fun onEphemeralEvent(chatRoom: ChatRoom, eventLog: EventLog) {
Log.i("$TAG Ephemeral event [${eventLog.type}]")
// TODO: show toast
val message = when (eventLog.type) {
EventLog.Type.ConferenceEphemeralMessageEnabled -> {
AppUtils.getString(
R.string.toast_conversation_ephemeral_messages_enabled
)
}
EventLog.Type.ConferenceEphemeralMessageDisabled -> {
AppUtils.getString(
R.string.toast_conversation_ephemeral_messages_disabled
)
}
else -> {
AppUtils.getString(
R.string.toast_conversation_ephemeral_messages_lifetime_changed
)
}
}
showGreenToastEvent.postValue(Event(Pair(message, R.drawable.clock_countdown)))
}
}
@ -360,7 +416,10 @@ class ConversationInfoViewModel @UiThread constructor() : ViewModel() {
val ok = chatRoom.addParticipants(participantsToAdd)
if (!ok) {
Log.w("$TAG Failed to add some/all participants to the group!")
// TODO: show toast
val message = AppUtils.getString(
R.string.toast_failed_to_add_participant_to_group_conversation
)
showRedToastEvent.postValue(Event(Pair(message, R.drawable.x)))
}
}
}

View file

@ -302,6 +302,14 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
}
}
@UiThread
fun refresh() {
coreContext.postOnCoreThread {
Log.i("$TAG Refreshing conversation info (subject, participants, etc...)")
computeConversationInfo()
}
}
@UiThread
fun applyFilter(filter: String) {
coreContext.postOnCoreThread {
@ -340,6 +348,14 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
Log.w("$TAG Chat room with subject [${chatRoom.subject}] is read only!")
}
computeConversationInfo()
computeEvents()
chatRoom.markAsRead()
}
@WorkerThread
private fun computeConversationInfo() {
val group = LinphoneUtils.isChatRoomAGroup(chatRoom)
isGroup.postValue(group)
@ -369,9 +385,6 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
coreContext.contactsManager.getContactAvatarModelForAddress(address)
}
avatarModel.postValue(avatar)
computeEvents()
chatRoom.markAsRead()
}
@WorkerThread

View file

@ -23,6 +23,7 @@ import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.contacts.ContactsManager
import org.linphone.core.ChatMessage
import org.linphone.core.ChatRoom
@ -33,6 +34,8 @@ import org.linphone.core.tools.Log
import org.linphone.ui.main.chat.model.ConversationModel
import org.linphone.ui.main.model.isInSecureMode
import org.linphone.ui.main.viewmodel.AbstractTopBarViewModel
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.LinphoneUtils
class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewModel() {
@ -44,6 +47,10 @@ class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewMod
val fetchInProgress = MutableLiveData<Boolean>()
val showGreenToastEvent: MutableLiveData<Event<Pair<String, Int>>> by lazy {
MutableLiveData<Event<Pair<String, Int>>>()
}
private val coreListener = object : CoreListenerStub() {
@WorkerThread
override fun onChatRoomStateChanged(
@ -56,9 +63,16 @@ class ConversationsListViewModel @UiThread constructor() : AbstractTopBarViewMod
)
when (state) {
ChatRoom.State.Created, ChatRoom.State.Instantiated, ChatRoom.State.Deleted -> {
ChatRoom.State.Created, ChatRoom.State.Instantiated -> {
computeChatRoomsList(currentFilter)
// TODO: show toast
}
ChatRoom.State.Deleted -> {
computeChatRoomsList(currentFilter)
val message = AppUtils.getString(R.string.toast_conversation_deleted)
showGreenToastEvent.postValue(
Event(Pair(message, R.drawable.chat_teardrop_text))
)
}
else -> {}
}

View file

@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.core.ChatMessage
import org.linphone.core.ChatRoom
import org.linphone.core.ChatRoomListenerStub
@ -49,6 +50,7 @@ import org.linphone.core.tools.Log
import org.linphone.ui.main.chat.model.ChatMessageModel
import org.linphone.ui.main.chat.model.FileModel
import org.linphone.ui.main.chat.model.ParticipantModel
import org.linphone.utils.AppUtils
import org.linphone.utils.AudioRouteUtils
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
@ -112,6 +114,10 @@ class SendMessageInConversationViewModel @UiThread constructor() : ViewModel() {
MutableLiveData<Event<Boolean>>()
}
val showRedToastEvent: MutableLiveData<Event<Pair<String, Int>>> by lazy {
MutableLiveData<Event<Pair<String, Int>>>()
}
lateinit var chatRoom: ChatRoom
private var chatMessageToReplyTo: ChatMessage? = null
@ -483,7 +489,10 @@ class SendMessageInConversationViewModel @UiThread constructor() : ViewModel() {
"$TAG Max duration for voice recording exceeded (${maxVoiceRecordDuration}ms), stopping."
)
stopVoiceRecorder()
// TOOD: show toast
val message = AppUtils.getString(
R.string.toast_voice_recording_max_duration_reached
)
showRedToastEvent.postValue(Event(Pair(message, R.drawable.x)))
}
}
}.launchIn(viewModelScope)

View file

@ -12,6 +12,7 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.linphone.core.tools.Log
import org.linphone.databinding.FileViewerFragmentBinding
import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.viewer.adapter.PdfPagesListAdapter
import org.linphone.ui.main.viewer.viewmodel.FileViewModel
@ -96,6 +97,22 @@ class FileViewerFragment : GenericFragment() {
}
}
}
viewModel.showGreenToastEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val message = pair.first
val icon = pair.second
(requireActivity() as MainActivity).showGreenToast(message, icon)
}
}
viewModel.showRedToastEvent.observe(viewLifecycleOwner) {
it.consume { pair ->
val message = pair.first
val icon = pair.second
(requireActivity() as MainActivity).showRedToast(message, icon)
}
}
}
override fun onPause() {

View file

@ -51,6 +51,14 @@ class FileViewModel @UiThread constructor() : ViewModel() {
MutableLiveData<Event<Boolean>>()
}
val showGreenToastEvent: MutableLiveData<Event<Pair<String, Int>>> by lazy {
MutableLiveData<Event<Pair<String, Int>>>()
}
val showRedToastEvent: MutableLiveData<Event<Pair<String, Int>>> by lazy {
MutableLiveData<Event<Pair<String, Int>>>()
}
// Below are required for PDF viewer
private lateinit var pdfRenderer: PdfRenderer
@ -157,12 +165,19 @@ class FileViewModel @UiThread constructor() : ViewModel() {
viewModelScope.launch {
withContext(Dispatchers.IO) {
Log.i("$TAG Export file [$filePath] to Android's MediaStore")
if (addContentToMediaStore(filePath)) {
val mediaStorePath = addContentToMediaStore(filePath)
if (mediaStorePath.isNotEmpty()) {
Log.i("$TAG File [$filePath] has been successfully exported to MediaStore")
// TODO: show toast
val message = AppUtils.getString(
R.string.toast_file_successfully_exported_to_media_store
)
showGreenToastEvent.postValue(Event(Pair(message, R.drawable.check)))
} else {
Log.e("$TAG Failed to export file [$filePath] to MediaStore!")
// TODO: show toast
val message = AppUtils.getString(
R.string.toast_export_file_to_media_store_error
)
showRedToastEvent.postValue(Event(Pair(message, R.drawable.x)))
}
}
}
@ -174,10 +189,10 @@ class FileViewModel @UiThread constructor() : ViewModel() {
@UiThread
private suspend fun addContentToMediaStore(
path: String
): Boolean {
): String {
if (path.isEmpty()) {
Log.e("$TAG No file path to export to MediaStore!")
return false
return ""
}
val isImage = FileUtils.isExtensionImage(path)
@ -254,10 +269,10 @@ class FileViewModel @UiThread constructor() : ViewModel() {
if (mediaStoreFilePath.isNotEmpty()) {
Log.i("$TAG Exported file path to MediaStore is: $mediaStoreFilePath")
return true
return mediaStoreFilePath
}
return false
return ""
}
@UiThread

View file

@ -110,6 +110,10 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
// When using keyboard to share gif or other, see RichContentReceiver & RichEditText classes
val richContentUri = MutableLiveData<Event<Uri>>()
val forceRefreshConversationInfo: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val forceRefreshConversationEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}

View file

@ -133,6 +133,24 @@
<string name="toast_alert_low_cellular_signal_cleared">Cellular signal is no longer low</string>
<string name="toast_call_can_be_trusted">This call can be trusted</string>
<string name="toast_default_account_connection_state_error">Connection error!</string>
<string name="toast_file_successfully_exported_to_media_store">File has been exported to native gallery</string>
<string name="toast_export_file_to_media_store_error">Error trying to export file to native gallery</string>
<string name="toast_participant_added_to_conversation">Someone joined the conversation</string>
<string name="toast_participant_removed_from_conversation">Someone left the conversation</string>
<string name="toast_participant_has_been_granted_admin_rights">Someone is now admin</string>
<string name="toast_participant_no_longer_has_admin_rights">Someone is no longer admin</string>
<string name="toast_conversation_subject_changed">Conversation subject has changed</string>
<string name="toast_conversation_ephemeral_messages_enabled">Ephemeral messages have been enabled</string>
<string name="toast_conversation_ephemeral_messages_disabled">Ephemeral messages have been disabled</string>
<string name="toast_conversation_ephemeral_messages_lifetime_changed">Ephemeral messages lifetime changed</string>
<string name="toast_cant_find_contact_to_display">Contact was not found</string>
<string name="toast_no_address_to_add_to_contact">No address to add to contact</string>
<string name="toast_cant_find_conversation_to_display">Conversation was not found</string>
<string name="toast_voice_recording_max_duration_reached">Max duration reached</string>
<string name="toast_failed_to_add_participant_to_group_conversation">Failed to add participant(s) to conversation</string>
<string name="toast_conversation_deleted">Conversation was successfully deleted</string>
<string name="toast_conversation_history_deleted">History has been successfully deleted</string>
<string name="toast_group_conversation_left">You have left the group</string>
<string name="assistant_account_login">Login</string>
<string name="assistant_scan_qr_code">Scan QR code</string>