diff --git a/app/build.gradle b/app/build.gradle
index 50a6a4ca0..f1d3aadc9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -77,7 +77,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.12.0"
implementation "androidx.core:core-telecom:1.0.0-alpha02"
implementation "androidx.media:media:1.6.0"
- implementation "androidx.recyclerview:recyclerview:1.3.1"
+ implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.slidingpanelayout:slidingpanelayout:1.2.0"
implementation "androidx.window:window:1.1.0"
diff --git a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt
index 733acd17d..6bb8b0e21 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationEventAdapter.kt
@@ -137,6 +137,10 @@ class ConversationEventAdapter(
with(binding) {
model = message
+ setShowDeliveryInfoClickListener {
+ showDeliveryForChatMessageModelEvent.value = Event(message)
+ }
+
lifecycleOwner = viewLifecycleOwner
executePendingBindings()
}
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 d0ecb681e..49793c5e9 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
@@ -43,13 +43,17 @@ import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.tabs.TabLayout
+import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import kotlinx.coroutines.launch
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
+import org.linphone.core.ChatMessage
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatConversationFragmentBinding
import org.linphone.databinding.ChatConversationLongPressMenuBinding
import org.linphone.ui.main.chat.adapter.ConversationEventAdapter
+import org.linphone.ui.main.chat.model.ChatMessageDeliveryModel
import org.linphone.ui.main.chat.model.ChatMessageModel
import org.linphone.ui.main.chat.viewmodel.ConversationViewModel
import org.linphone.ui.main.fragment.GenericFragment
@@ -177,13 +181,9 @@ class ConversationFragment : GenericFragment() {
emojisBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
emojisBottomSheetBehavior.isDraggable = false // To allow scrolling through the emojis
- val imdnBottomSheetBehavior = BottomSheetBehavior.from(binding.deliveryBottomSheet.root)
- imdnBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
-
adapter.showDeliveryForChatMessageModelEvent.observe(viewLifecycleOwner) {
it.consume { model ->
- binding.deliveryBottomSheet.model = model
- imdnBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+ showDeliveryBottomSheetDialog(model)
}
}
@@ -303,4 +303,62 @@ class ConversationFragment : GenericFragment() {
dialog.window?.setBackgroundDrawable(d)
dialog.show()
}
+
+ @UiThread
+ private fun showDeliveryBottomSheetDialog(chatMessageModel: ChatMessageModel) {
+ val deliveryBottomSheetBehavior = BottomSheetBehavior.from(binding.messageDelivery.root)
+ if (deliveryBottomSheetBehavior.state == BottomSheetBehavior.STATE_HALF_EXPANDED) {
+ deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ }
+
+ binding.messageDelivery.setHandleClickedListener {
+ deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ }
+
+ coreContext.postOnCoreThread {
+ val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage)
+
+ coreContext.postOnMainThread {
+ binding.messageDelivery.tabs.removeAllTabs()
+ binding.messageDelivery.tabs.addTab(
+ binding.messageDelivery.tabs.newTab().setText(model.readLabel.value).setId(
+ ChatMessage.State.Displayed.toInt()
+ )
+ )
+ binding.messageDelivery.tabs.addTab(
+ binding.messageDelivery.tabs.newTab().setText(model.receivedLabel.value).setId(
+ ChatMessage.State.DeliveredToUser.toInt()
+ )
+ )
+ binding.messageDelivery.tabs.addTab(
+ binding.messageDelivery.tabs.newTab().setText(model.sentLabel.value).setId(
+ ChatMessage.State.Delivered.toInt()
+ )
+ )
+ binding.messageDelivery.tabs.addTab(
+ binding.messageDelivery.tabs.newTab().setText(model.errorLabel.value).setId(
+ ChatMessage.State.NotDelivered.toInt()
+ )
+ )
+
+ binding.messageDelivery.tabs.setOnTabSelectedListener(object : OnTabSelectedListener {
+ override fun onTabSelected(tab: TabLayout.Tab?) {
+ val state = tab?.id ?: ChatMessage.State.Displayed.toInt()
+ model.computeListForState(ChatMessage.State.fromInt(state))
+ }
+
+ override fun onTabUnselected(tab: TabLayout.Tab?) {
+ }
+
+ override fun onTabReselected(tab: TabLayout.Tab?) {
+ }
+ })
+
+ binding.messageDelivery.model = model
+
+ binding.messageDelivery.root.visibility = View.VISIBLE
+ deliveryBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+ }
+ }
+ }
}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/MessageDeliveryDialogFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/MessageDeliveryDialogFragment.kt
new file mode 100644
index 000000000..54f2518fc
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/MessageDeliveryDialogFragment.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010-2023 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.chat.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.UiThread
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import org.linphone.databinding.ChatMessageDeliveryBottomSheetBinding
+import org.linphone.ui.main.chat.model.ChatMessageDeliveryModel
+import org.linphone.ui.main.chat.model.ChatMessageModel
+
+@UiThread
+class MessageDeliveryDialogFragment(
+ chatMessageModel: ChatMessageModel
+) : BottomSheetDialogFragment() {
+ companion object {
+ const val TAG = "MessageDeliveryDialogFragment"
+ }
+
+ val model = ChatMessageDeliveryModel(chatMessageModel.chatMessage)
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = ChatMessageDeliveryBottomSheetBinding.inflate(layoutInflater)
+ view.model = model
+ return view.root
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt
index eba841549..1365e17c7 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageDeliveryModel.kt
@@ -1,18 +1,102 @@
package org.linphone.ui.main.chat.model
+import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
-import org.linphone.LinphoneApplication
-import org.linphone.core.ParticipantImdnState
-import org.linphone.utils.TimestampUtils
+import androidx.lifecycle.MutableLiveData
+import org.linphone.R
+import org.linphone.core.ChatMessage
+import org.linphone.core.ChatMessage.State
+import org.linphone.utils.AppUtils
class ChatMessageDeliveryModel @WorkerThread constructor(
- imdnState: ParticipantImdnState
+ private val chatMessage: ChatMessage
) {
- val address = imdnState.participant.address
+ companion object {
+ private const val TAG = "[Chat Message Delivery Model]"
+ }
- val avatarModel = LinphoneApplication.coreContext.contactsManager.getContactAvatarModelForAddress(
- address
- )
+ val readLabel = MutableLiveData()
- val time = TimestampUtils.toString(imdnState.stateChangeTime)
+ val receivedLabel = MutableLiveData()
+
+ val sentLabel = MutableLiveData()
+
+ val errorLabel = MutableLiveData()
+
+ val deliveryModels = MutableLiveData>()
+
+ private val displayedModels = arrayListOf()
+
+ private val deliveredModels = arrayListOf()
+
+ private val sentModels = arrayListOf()
+
+ private val errorModels = arrayListOf()
+
+ init {
+ computeDeliveryStatus()
+ }
+
+ @UiThread
+ fun computeListForState(state: State) {
+ when (state) {
+ State.DeliveredToUser -> {
+ deliveryModels.value = deliveredModels
+ }
+ State.Delivered -> {
+ deliveryModels.value = sentModels
+ }
+ State.NotDelivered -> {
+ deliveryModels.value = errorModels
+ }
+ else -> {
+ deliveryModels.value = displayedModels
+ }
+ }
+ }
+
+ @WorkerThread
+ private fun computeDeliveryStatus() {
+ for (participant in chatMessage.getParticipantsByImdnState(State.Displayed)) {
+ displayedModels.add(ChatMessageParticipantDeliveryModel(participant))
+ }
+ readLabel.postValue(
+ AppUtils.getFormattedString(
+ R.string.message_delivery_info_read_title,
+ displayedModels.size.toString()
+ )
+ )
+
+ for (participant in chatMessage.getParticipantsByImdnState(State.DeliveredToUser)) {
+ deliveredModels.add(ChatMessageParticipantDeliveryModel(participant))
+ }
+ receivedLabel.postValue(
+ AppUtils.getFormattedString(
+ R.string.message_delivery_info_received_title,
+ deliveredModels.size.toString()
+ )
+ )
+
+ for (participant in chatMessage.getParticipantsByImdnState(State.Delivered)) {
+ sentModels.add(ChatMessageParticipantDeliveryModel(participant))
+ }
+ sentLabel.postValue(
+ AppUtils.getFormattedString(
+ R.string.message_delivery_info_sent_title,
+ sentModels.size.toString()
+ )
+ )
+
+ for (participant in chatMessage.getParticipantsByImdnState(State.NotDelivered)) {
+ errorModels.add(ChatMessageParticipantDeliveryModel(participant))
+ }
+ errorLabel.postValue(
+ AppUtils.getFormattedString(
+ R.string.message_delivery_info_error_title,
+ errorModels.size.toString()
+ )
+ )
+
+ deliveryModels.postValue(displayedModels)
+ }
}
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 ae5658920..04a4c8a42 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
@@ -59,8 +59,6 @@ class ChatMessageModel @WorkerThread constructor(
val chatRoomIsReadOnly = chatMessage.chatRoom.isReadOnly
- val deliveryModels = MutableLiveData>()
-
val dismissLongPressMenuEvent: MutableLiveData> by lazy {
MutableLiveData>()
}
@@ -87,8 +85,6 @@ class ChatMessageModel @WorkerThread constructor(
init {
chatMessage.addListener(chatMessageListener)
computeStatusIcon(chatMessage.state)
-
- computeDeliveryStatus()
}
@WorkerThread
@@ -127,15 +123,4 @@ class ChatMessageModel @WorkerThread constructor(
}
statusIcon.postValue(icon)
}
-
- @WorkerThread
- private fun computeDeliveryStatus() {
- val list = arrayListOf()
-
- /*for (participant in chatMessage.getParticipantsByImdnState(ChatMessage.State.Displayed)) {
- list.add(ChatMessageDeliveryModel(participant))
- }*/
-
- deliveryModels.postValue(list)
- }
}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageParticipantDeliveryModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageParticipantDeliveryModel.kt
new file mode 100644
index 000000000..d1ccb4b36
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/chat/model/ChatMessageParticipantDeliveryModel.kt
@@ -0,0 +1,18 @@
+package org.linphone.ui.main.chat.model
+
+import androidx.annotation.WorkerThread
+import org.linphone.LinphoneApplication
+import org.linphone.core.ParticipantImdnState
+import org.linphone.utils.TimestampUtils
+
+class ChatMessageParticipantDeliveryModel @WorkerThread constructor(
+ imdnState: ParticipantImdnState
+) {
+ val address = imdnState.participant.address
+
+ val avatarModel = LinphoneApplication.coreContext.contactsManager.getContactAvatarModelForAddress(
+ address
+ )
+
+ val time = TimestampUtils.toString(imdnState.stateChangeTime)
+}
diff --git a/app/src/main/res/drawable/shape_bottom_sheet_background.xml b/app/src/main/res/drawable/shape_bottom_sheet_gray_100_background.xml
similarity index 100%
rename from app/src/main/res/drawable/shape_bottom_sheet_background.xml
rename to app/src/main/res/drawable/shape_bottom_sheet_gray_100_background.xml
diff --git a/app/src/main/res/drawable/shape_bottom_sheet_white_background.xml b/app/src/main/res/drawable/shape_bottom_sheet_white_background.xml
new file mode 100644
index 000000000..8675a28f4
--- /dev/null
+++ b/app/src/main/res/drawable/shape_bottom_sheet_white_background.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_chat_bubble_incoming_first.xml b/app/src/main/res/drawable/shape_chat_bubble_incoming_first.xml
index 0dd826c00..e82f7df3c 100644
--- a/app/src/main/res/drawable/shape_chat_bubble_incoming_first.xml
+++ b/app/src/main/res/drawable/shape_chat_bubble_incoming_first.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_chat_bubble_incoming_full.xml b/app/src/main/res/drawable/shape_chat_bubble_incoming_full.xml
index 18bb74022..df3a98fea 100644
--- a/app/src/main/res/drawable/shape_chat_bubble_incoming_full.xml
+++ b/app/src/main/res/drawable/shape_chat_bubble_incoming_full.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/chat_bubble_outgoing.xml b/app/src/main/res/layout/chat_bubble_outgoing.xml
index ad21828fe..103bcc85b 100644
--- a/app/src/main/res/layout/chat_bubble_outgoing.xml
+++ b/app/src/main/res/layout/chat_bubble_outgoing.xml
@@ -9,6 +9,9 @@
+
@@ -75,6 +78,7 @@
@@ -203,7 +203,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:paddingBottom="5dp"
- android:background="@color/gray_100"
+ android:background="@color/white"
android:text="@{viewModel.composingLabel, default=`John Doe is composing...`}"
android:textSize="12sp"
android:textColor="@color/gray_main2_400"
@@ -223,7 +223,8 @@
layout="@layout/chat_conversation_send_area"/>
diff --git a/app/src/main/res/layout/chat_delivery_list_cell.xml b/app/src/main/res/layout/chat_delivery_list_cell.xml
index 4e99c4c10..ed2b59ae5 100644
--- a/app/src/main/res/layout/chat_delivery_list_cell.xml
+++ b/app/src/main/res/layout/chat_delivery_list_cell.xml
@@ -9,7 +9,7 @@
+ type="org.linphone.ui.main.chat.model.ChatMessageParticipantDeliveryModel" />
+ type="org.linphone.ui.main.chat.model.ChatMessageDeliveryModel" />
@@ -39,40 +38,25 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent_color"
+ android:layout_marginBottom="16dp"
app:layout_constraintTop_toBottomOf="@id/handle"
+ app:layout_constraintBottom_toTopOf="@id/scrollview"
app:tabMode="fixed"
+ app:tabUnboundedRipple="true"
+ app:tabRippleColor="@color/orange_main_100"
app:tabGravity="fill"
app:tabPadding="0dp"
app:tabInlineLabel="true"
+ app:tabIndicatorColor="@color/orange_main_500"
app:tabTextColor="@color/gray_main2_400"
- app:tabSelectedTextColor="@color/gray_main2_600">
-
-
-
-
-
-
-
-
-
-
+ app:tabSelectedTextColor="@color/gray_main2_600" />
+ android:layout_height="250dp"
+ app:layout_constraintHeight_min="50dp"
+ app:layout_constraintBottom_toBottomOf="parent">
Give admin rights
Remove admin rights
+ Read (%s)
+ Received (%s)
+ Sent (%s)
+ Error (%s)
+
No meeting for the moment…
New meeting
Meeting