diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt index 2cb1048e9..99c4625f8 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt @@ -20,6 +20,9 @@ package org.linphone.ui.main.chat.fragment import android.app.Dialog +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context import android.graphics.Rect import android.graphics.RenderEffect import android.graphics.Shader @@ -185,16 +188,33 @@ class ConversationFragment : GenericFragment() { null, false ) + layout.root.setOnClickListener { dialog.dismiss() - binding.root.setRenderEffect(null) + } + + layout.setDeleteClickListener { + viewModel.deleteChatMessage(chatMessageModel) + dialog.dismiss() + } + + layout.setCopyClickListener { + val text = chatMessageModel.text + val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val label = "Message" + clipboard.setPrimaryClip(ClipData.newPlainText(label, text)) + + dialog.dismiss() } layout.model = chatMessageModel + chatMessageModel.dismissLongPressMenuEvent.observe(viewLifecycleOwner) { + dialog.dismiss() + } + val screenY = yPosition - AppUtils.getDimension( R.dimen.chat_bubble_long_press_menu_bubble_offset ) - val rect = Rect() binding.root.getGlobalVisibleRect(rect) val height = rect.height() @@ -209,6 +229,9 @@ class ConversationFragment : GenericFragment() { set.applyTo(constraintLayout) dialog.setContentView(layout.root) + dialog.setOnDismissListener { + binding.root.setRenderEffect(null) + } dialog.window ?.setLayout( diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageModel.kt index 25fda053b..1e7291969 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageModel.kt @@ -22,15 +22,25 @@ package org.linphone.ui.main.chat.model 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.ChatMessage +import org.linphone.core.ChatMessageListenerStub +import org.linphone.core.ChatMessageReaction +import org.linphone.core.tools.Log import org.linphone.ui.main.contacts.model.ContactAvatarModel +import org.linphone.utils.Event import org.linphone.utils.LinphoneUtils import org.linphone.utils.TimestampUtils class ChatMessageModel @WorkerThread constructor( - chatMessage: ChatMessage, + val chatMessage: ChatMessage, val avatarModel: ContactAvatarModel ) { + companion object { + private const val TAG = "[Chat Message Model]" + } + val id = chatMessage.messageId val isOutgoing = chatMessage.isOutgoing @@ -45,13 +55,47 @@ class ChatMessageModel @WorkerThread constructor( val time = TimestampUtils.toString(timestamp) + val dismissLongPressMenuEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + + private val chatMessageListener = object : ChatMessageListenerStub() { + @WorkerThread + override fun onMsgStateChanged(message: ChatMessage, messageState: ChatMessage.State?) { + state.postValue(chatMessage.state) + } + + @WorkerThread + override fun onNewMessageReaction(message: ChatMessage, reaction: ChatMessageReaction) { + Log.i( + "$TAG New reaction [${reaction.body}] from [${reaction.fromAddress.asStringUriOnly()}] for chat message with ID [$id]" + ) + } + + @WorkerThread + override fun onReactionRemoved(message: ChatMessage, address: Address) { + Log.i("$TAG A reaction was removed for chat message with ID [$id]") + } + } + init { + chatMessage.addListener(chatMessageListener) state.postValue(chatMessage.state) } + @WorkerThread + fun destroy() { + chatMessage.removeListener(chatMessageListener) + } + @UiThread - fun onLongClick(): Boolean { - return true + fun sendReaction(emoji: String) { + coreContext.postOnCoreThread { + Log.i("$TAG Sending reaction [$emoji] to chat message with ID [$id]") + val reaction = chatMessage.createReaction(emoji) + reaction.send() + dismissLongPressMenuEvent.postValue(Event(true)) + } } @UiThread diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt index 9e0e198af..659f6a97a 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt @@ -35,4 +35,11 @@ class EventLogModel @WorkerThread constructor(eventLog: EventLog, avatarModel: C } val notifyId = eventLog.notifyId + + @WorkerThread + fun destroy() { + if (model is ChatMessageModel) { + model.destroy() + } + } } diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt index c297d9634..61e6abacc 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt @@ -32,6 +32,7 @@ import org.linphone.core.EventLog import org.linphone.core.Factory import org.linphone.core.Friend import org.linphone.core.tools.Log +import org.linphone.ui.main.chat.model.ChatMessageModel import org.linphone.ui.main.chat.model.EventLogModel import org.linphone.ui.main.contacts.model.ContactAvatarModel import org.linphone.ui.main.contacts.model.GroupAvatarModel @@ -129,6 +130,7 @@ class ConversationViewModel @UiThread constructor() : ViewModel() { coreContext.postOnCoreThread { chatRoom.removeListener(chatRoomListener) + events.value.orEmpty().forEach(EventLogModel::destroy) avatarsMap.values.forEach(ContactAvatarModel::destroy) } } @@ -186,6 +188,25 @@ class ConversationViewModel @UiThread constructor() : ViewModel() { } } + @UiThread + fun deleteChatMessage(chatMessageModel: ChatMessageModel) { + coreContext.postOnCoreThread { + val eventsLogs = events.value.orEmpty() + val found = eventsLogs.find { + it.model == chatMessageModel + } + if (found != null) { + val list = arrayListOf() + list.addAll(eventsLogs) + list.remove(found) + events.postValue(list) + } + + Log.i("$TAG Deleting message id [${chatMessageModel.id}]") + chatRoom.deleteMessage(chatMessageModel.chatMessage) + } + } + @WorkerThread private fun configureChatRoom() { computeComposingLabel() diff --git a/app/src/main/res/layout/chat_conversation_long_press_menu.xml b/app/src/main/res/layout/chat_conversation_long_press_menu.xml index fe0ef19c6..5e81b27c9 100644 --- a/app/src/main/res/layout/chat_conversation_long_press_menu.xml +++ b/app/src/main/res/layout/chat_conversation_long_press_menu.xml @@ -4,6 +4,12 @@ + + @@ -35,6 +41,7 @@