diff --git a/app/build.gradle b/app/build.gradle index 8f5382cf1..9810fcd74 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,7 +29,7 @@ if (crashlyticsEnabled()) { def gitBranch = new ByteArrayOutputStream() task getGitVersion() { - def gitVersion = "4.5.0" + def gitVersion = "4.6.0" def gitVersionStream = new ByteArrayOutputStream() def gitCommitsCount = new ByteArrayOutputStream() def gitCommitHash = new ByteArrayOutputStream() @@ -269,7 +269,7 @@ dependencies { implementation 'com.google.firebase:firebase-messaging' } - implementation 'org.linphone:linphone-sdk-android:5.0+' + implementation 'org.linphone:linphone-sdk-android:5.1+' // Only enable leak canary prior to release //debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.4' diff --git a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt index 5808807b9..76c051b9b 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/adapters/ChatMessagesListAdapter.kt @@ -69,6 +69,10 @@ class ChatMessagesListAdapter( MutableLiveData>() } + val replyMessageEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + val showImdnForMessageEvent: MutableLiveData> by lazy { MutableLiveData>() } @@ -81,6 +85,10 @@ class ChatMessagesListAdapter( MutableLiveData>() } + val scrollToChatMessageEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + private val contentClickedListener = object : OnContentClickedListener { override fun onContentClicked(content: Content) { openContentEvent.value = Event(content) @@ -155,6 +163,13 @@ class ChatMessagesListAdapter( } } + setReplyClickListener { + val reply = chatMessageViewModel.replyData.value?.chatMessage + if (reply != null) { + scrollToChatMessageEvent.value = Event(reply) + } + } + // Grouping var hasPrevious = false var hasNext = false @@ -196,7 +211,7 @@ class ChatMessagesListAdapter( ) val itemSize = AppUtils.getDimension(R.dimen.chat_message_popup_item_height).toInt() - var totalSize = itemSize * 6 + var totalSize = itemSize * 7 if (chatMessage.chatRoom.hasCapability(ChatRoomCapabilities.OneToOne.toInt()) || chatMessage.state == ChatMessage.State.NotDelivered) { // No message id popupView.imdnHidden = true @@ -237,6 +252,10 @@ class ChatMessagesListAdapter( forwardMessage() popupWindow.dismiss() } + popupView.setReplyClickListener { + replyMessage() + popupWindow.dismiss() + } popupView.setImdnClickListener { showImdnDeliveryFragment() popupWindow.dismiss() @@ -287,6 +306,13 @@ class ChatMessagesListAdapter( } } + private fun replyMessage() { + val chatMessage = binding.data?.chatMessage + if (chatMessage != null) { + replyMessageEvent.value = Event(chatMessage) + } + } + private fun showImdnDeliveryFragment() { val chatMessage = binding.data?.chatMessage if (chatMessage != null) { diff --git a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt index cfd7c51a9..cd8230096 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/data/ChatMessageData.kt @@ -28,6 +28,7 @@ import org.linphone.R import org.linphone.contact.GenericContactData import org.linphone.core.ChatMessage import org.linphone.core.ChatMessageListenerStub +import org.linphone.core.tools.Log import org.linphone.utils.AppUtils import org.linphone.utils.TimestampUtils @@ -56,6 +57,8 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes val text = MutableLiveData() + val replyData = MutableLiveData() + private var countDownTimer: CountDownTimer? = null private val listener = object : ChatMessageListenerStub() { @@ -74,6 +77,15 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes backgroundRes.value = if (chatMessage.isOutgoing) R.drawable.chat_bubble_outgoing_full else R.drawable.chat_bubble_incoming_full hideAvatar.value = false + + if (chatMessage.isReply) { + val reply = chatMessage.replyMessage + if (reply != null) { + Log.i("[Chat Message Data] Message is a reply of message id [${chatMessage.replyMessageId}] sent by [${chatMessage.replyMessageSenderAddress?.asStringUriOnly()}]") + replyData.value = ChatMessageData(reply) + } + } + time.value = TimestampUtils.toString(chatMessage.time) updateEphemeralTimer() @@ -84,6 +96,10 @@ class ChatMessageData(val chatMessage: ChatMessage) : GenericContactData(chatMes override fun destroy() { super.destroy() + if (chatMessage.isReply) { + replyData.value?.destroy() + } + contents.value.orEmpty().forEach(ChatMessageContentData::destroy) chatMessage.removeListener(listener) contentListener = null diff --git a/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt b/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt index 65461790e..d4c564946 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/fragments/DetailChatRoomFragment.kt @@ -39,6 +39,7 @@ import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import java.io.File +import java.lang.IllegalArgumentException import kotlinx.coroutines.launch import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences @@ -47,6 +48,7 @@ import org.linphone.activities.* import org.linphone.activities.main.MainActivity import org.linphone.activities.main.chat.ChatScrollListener import org.linphone.activities.main.chat.adapters.ChatMessagesListAdapter +import org.linphone.activities.main.chat.data.ChatMessageData import org.linphone.activities.main.chat.data.EventLogData import org.linphone.activities.main.chat.viewmodels.* import org.linphone.activities.main.fragments.MasterFragment @@ -166,6 +168,28 @@ class DetailChatRoomFragment : MasterFragment + chatSendingViewModel.pendingChatMessageToReplyTo.value?.destroy() + chatSendingViewModel.pendingChatMessageToReplyTo.value = ChatMessageData(chatMessage) + chatSendingViewModel.isPendingAnswer.value = true + } + }) + adapter.showImdnForMessageEvent.observe(viewLifecycleOwner, { it.consume { chatMessage -> val args = Bundle() @@ -278,6 +310,23 @@ class DetailChatRoomFragment : MasterFragment + val events = listViewModel.events.value.orEmpty() + val eventLog = events.find { eventLog -> + if (eventLog.eventLog.type == EventLog.Type.ConferenceChatMessage) { + (eventLog.data as ChatMessageData).chatMessage.messageId == chatMessage.messageId + } else false + } + val index = events.indexOf(eventLog) + try { + binding.chatMessagesList.smoothScrollToPosition(index) + } catch (iae: IllegalArgumentException) { + Log.e("[Chat Room] Can't scroll to position $index") + } + } + }) + binding.setBackClickListener { goBack() } diff --git a/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt b/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt index 2a71f99dd..6d26ac077 100644 --- a/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/chat/viewmodels/ChatMessageSendingViewModel.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.ViewModelProvider import java.io.File import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.activities.main.chat.data.ChatMessageAttachmentData +import org.linphone.activities.main.chat.data.ChatMessageData import org.linphone.core.ChatMessage import org.linphone.core.ChatRoom import org.linphone.core.ChatRoomCapabilities @@ -53,6 +54,10 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel() var textToSend = MutableLiveData() + val isPendingAnswer = MutableLiveData() + + var pendingChatMessageToReplyTo = MutableLiveData() + init { attachments.value = arrayListOf() @@ -108,14 +113,18 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel() } fun sendMessage() { - val isBasicChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) - val message: ChatMessage = chatRoom.createEmptyMessage() + val pendingMessageToReplyTo = pendingChatMessageToReplyTo.value + val message: ChatMessage = if (isPendingAnswer.value == true && pendingMessageToReplyTo != null) + chatRoom.createReplyMessage(pendingMessageToReplyTo.chatMessage) + else + chatRoom.createEmptyMessage() val toSend = textToSend.value if (toSend != null && toSend.isNotEmpty()) { message.addUtf8TextContent(toSend.trim()) } + val isBasicChatRoom: Boolean = chatRoom.hasCapability(ChatRoomCapabilities.Basic.toInt()) var fileContent = false for (attachment in attachments.value.orEmpty()) { val content = Factory.instance().createContent() @@ -144,6 +153,7 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel() message.send() } + cancelReply() attachments.value.orEmpty().forEach(ChatMessageAttachmentData::destroy) attachments.value = arrayListOf() } @@ -152,4 +162,9 @@ class ChatMessageSendingViewModel(private val chatRoom: ChatRoom) : ViewModel() val message = chatRoom.createForwardMessage(chatMessage) message.send() } + + fun cancelReply() { + pendingChatMessageToReplyTo.value?.destroy() + isPendingAnswer.value = false + } } diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt index 8743e7d3f..a7d9bd209 100644 --- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt +++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt @@ -41,6 +41,15 @@ class LinphoneUtils { private const val RECORDING_DATE_PATTERN = "dd-MM-yyyy-HH-mm-ss" fun getDisplayName(address: Address): String { + if (address.displayName == null) { + val account = coreContext.core.accountList.find { account -> + account.params.identityAddress?.asStringUriOnly() == address.asStringUriOnly() + } + val localDisplayName = account?.params?.identityAddress?.displayName + if (localDisplayName != null) { + return localDisplayName + } + } return address.displayName ?: address.username ?: "" } diff --git a/app/src/main/res/drawable-xhdpi/menu_reply_default.png b/app/src/main/res/drawable-xhdpi/menu_reply_default.png new file mode 100644 index 000000000..47fc5db73 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/menu_reply_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/replied_message_default.png b/app/src/main/res/drawable-xhdpi/replied_message_default.png new file mode 100644 index 000000000..de2fe7649 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/replied_message_default.png differ diff --git a/app/src/main/res/drawable-xhdpi/reply_message_default.png b/app/src/main/res/drawable-xhdpi/reply_message_default.png new file mode 100644 index 000000000..f0d2ff97f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/reply_message_default.png differ diff --git a/app/src/main/res/drawable/chat_bubble_incoming_full.xml b/app/src/main/res/drawable/chat_bubble_incoming_full.xml index f541f2504..a8d9a3b32 100644 --- a/app/src/main/res/drawable/chat_bubble_incoming_full.xml +++ b/app/src/main/res/drawable/chat_bubble_incoming_full.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml b/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml index 46f1fe172..d531a9917 100644 --- a/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml +++ b/app/src/main/res/drawable/chat_bubble_incoming_split_1.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml b/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml index a3a9cfcc2..277b01a2f 100644 --- a/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml +++ b/app/src/main/res/drawable/chat_bubble_incoming_split_2.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml b/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml index af2af2629..7bc997138 100644 --- a/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml +++ b/app/src/main/res/drawable/chat_bubble_incoming_split_3.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_full.xml b/app/src/main/res/drawable/chat_bubble_outgoing_full.xml index 53c64a052..2117a9a5e 100644 --- a/app/src/main/res/drawable/chat_bubble_outgoing_full.xml +++ b/app/src/main/res/drawable/chat_bubble_outgoing_full.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml b/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml index 74894387e..42f873e03 100644 --- a/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml +++ b/app/src/main/res/drawable/chat_bubble_outgoing_split_1.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml b/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml index 6d58ce71a..97bd2976e 100644 --- a/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml +++ b/app/src/main/res/drawable/chat_bubble_outgoing_split_2.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml b/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml index e0df185f3..e1c6f9d03 100644 --- a/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml +++ b/app/src/main/res/drawable/chat_bubble_outgoing_split_3.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/chat_bubble_reply_background.xml b/app/src/main/res/drawable/chat_bubble_reply_background.xml new file mode 100644 index 000000000..5e5b435c7 --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_reply_background.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_reply_file_background.xml b/app/src/main/res/drawable/chat_bubble_reply_file_background.xml new file mode 100644 index 000000000..c181b358a --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_reply_file_background.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_reply_incoming_indicator.xml b/app/src/main/res/drawable/chat_bubble_reply_incoming_indicator.xml new file mode 100644 index 000000000..3bb03b37b --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_reply_incoming_indicator.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/chat_bubble_reply_outgoing_indicator.xml b/app/src/main/res/drawable/chat_bubble_reply_outgoing_indicator.xml new file mode 100644 index 000000000..3cd2b9b0b --- /dev/null +++ b/app/src/main/res/drawable/chat_bubble_reply_outgoing_indicator.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/menu_reply.xml b/app/src/main/res/drawable/menu_reply.xml new file mode 100644 index 000000000..c3099128f --- /dev/null +++ b/app/src/main/res/drawable/menu_reply.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/layout/chat_message_list_cell.xml b/app/src/main/res/layout/chat_message_list_cell.xml index c76c28acf..0d56b3f67 100644 --- a/app/src/main/res/layout/chat_message_list_cell.xml +++ b/app/src/main/res/layout/chat_message_list_cell.xml @@ -13,6 +13,9 @@ + @@ -90,7 +93,6 @@ android:layout_marginBottom="1dp" android:layout_marginRight="@{data.chatMessage.outgoing ? @dimen/outgoing_chat_message_bubble_right_margin : @dimen/incoming_chat_message_bubble_right_margin}" android:layout_marginLeft="@{selectionListViewModel.isEditionEnabled ? @dimen/edit_chat_message_bubble_left_margin : !data.chatMessage.outgoing ? @dimen/incoming_chat_message_bubble_left_margin : @dimen/outgoing_chat_message_bubble_left_margin}" - android:paddingTop="5dp" android:paddingBottom="5dp"> @@ -123,9 +126,52 @@ + + + + + + + + + + + @@ -70,6 +73,15 @@ style="@style/popup_item_font" android:text="@string/chat_message_context_menu_forward" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/chat_message_reply_bubble.xml b/app/src/main/res/layout/chat_message_reply_bubble.xml new file mode 100644 index 000000000..61a2b5207 --- /dev/null +++ b/app/src/main/res/layout/chat_message_reply_bubble.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/chat_message_reply_content_cell.xml b/app/src/main/res/layout/chat_message_reply_content_cell.xml new file mode 100644 index 000000000..9da6290dd --- /dev/null +++ b/app/src/main/res/layout/chat_message_reply_content_cell.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/chat_message_reply_preview_content_cell.xml b/app/src/main/res/layout/chat_message_reply_preview_content_cell.xml new file mode 100644 index 000000000..ccea18ba3 --- /dev/null +++ b/app/src/main/res/layout/chat_message_reply_preview_content_cell.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/chat_room_detail_fragment.xml b/app/src/main/res/layout/chat_room_detail_fragment.xml index 393f9cc4b..12db0e4be 100644 --- a/app/src/main/res/layout/chat_room_detail_fragment.xml +++ b/app/src/main/res/layout/chat_room_detail_fragment.xml @@ -147,6 +147,12 @@ android:background="?attr/lightToolbarBackgroundColor" android:orientation="vertical"> + + Sélectionnez ou créez une conversation pour partager le(s) fichier(s) Sélectionnez ou créez une conversation pour partager le texte Messages éphémères (bêta) + Répondre + Réponse + Message + Réponse diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index a493e51c1..3bbc1bda6 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -20,13 +20,18 @@ #e1e1e1 #f3f3f3 #c2c2c2 + #a1a1a1 #f3f3f3 + #9b9b9b #ffeee5 + #ff9e67 + #f4f4f4 + #dedede #808080 #99ffffff diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 8739f7fb6..3f8ea7826 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -9,6 +9,7 @@ 100dp 120dp 30dp + 50dp 600dp 200dp 45dp @@ -26,4 +27,5 @@ 60dp 250dp 50dp + 6.7dp \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ef17326af..b4aa86075 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -168,9 +168,11 @@ Undelivered Sent Forwarded + Reply Resend Copy text Forward + Reply Delivery status Delete Add to contacts @@ -214,6 +216,8 @@ Would you like to open it as text or export it (unencrypted) to a third party app if available? Export Open as text + Reply + Message No recordings @@ -622,6 +626,7 @@ Attached file Message delivery status Message has been forwarded + Message is a reply Forward message in this conversation Message is ephemeral Contact is a &appName; user @@ -636,6 +641,7 @@ Show chat room menu Enter edition mode Attach a file to the message + File attached to the message Send message Show or hide the participant devices Ephemeral duration selected diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 347f78728..ab7875f81 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -111,6 +111,20 @@ 13sp + + + + + +