Improved incoming bubble layout

This commit is contained in:
Sylvain Berfini 2023-10-11 14:44:41 +02:00
parent 9d5474f352
commit 3dc23d906e
8 changed files with 153 additions and 37 deletions

View file

@ -119,10 +119,11 @@ class ConversationEventAdapter(
with(binding) {
model = chatMessageData
isGroupedWithPreviousOne = if (bindingAdapterPosition == 0) {
val position = bindingAdapterPosition
isGroupedWithPreviousOne = if (position == 0) {
false
} else {
val previous = bindingAdapterPosition - 1
val previous = position - 1
if (getItemViewType(previous) == INCOMING_CHAT_MESSAGE) {
val previousItem = getItem(previous).data as ChatMessageModel
if (kotlin.math.abs(chatMessageData.timestamp - previousItem.timestamp) < MAX_TIME_TO_GROUP_MESSAGES) {
@ -135,10 +136,20 @@ class ConversationEventAdapter(
}
}
binding.setOnLongClickListener {
selectedAdapterPosition = bindingAdapterPosition
binding.root.isSelected = true
isLastOneOfGroup = if (position == itemCount - 1) {
true
} else {
val next = position + 1
if (getItemViewType(next) == INCOMING_CHAT_MESSAGE) {
val nextItem = getItem(next).data as ChatMessageModel
if (kotlin.math.abs(chatMessageData.timestamp - nextItem.timestamp) < MAX_TIME_TO_GROUP_MESSAGES) {
nextItem.fromSipUri != chatMessageData.fromSipUri
} else {
true
}
} else {
true
}
}
lifecycleOwner = viewLifecycleOwner
@ -154,10 +165,11 @@ class ConversationEventAdapter(
with(binding) {
model = chatMessageData
isGroupedWithPreviousOne = if (bindingAdapterPosition == 0) {
val position = bindingAdapterPosition
isGroupedWithPreviousOne = if (position == 0) {
false
} else {
val previous = bindingAdapterPosition - 1
val previous = position - 1
if (getItemViewType(previous) == OUTGOING_CHAT_MESSAGE) {
val previousItem = getItem(previous).data as ChatMessageModel
if (kotlin.math.abs(chatMessageData.timestamp - previousItem.timestamp) < MAX_TIME_TO_GROUP_MESSAGES) {
@ -170,10 +182,20 @@ class ConversationEventAdapter(
}
}
binding.setOnLongClickListener {
selectedAdapterPosition = bindingAdapterPosition
binding.root.isSelected = true
isLastOneOfGroup = if (position == itemCount - 1) {
true
} else {
val next = position + 1
if (getItemViewType(next) == INCOMING_CHAT_MESSAGE) {
val nextItem = getItem(next).data as ChatMessageModel
if (kotlin.math.abs(chatMessageData.timestamp - nextItem.timestamp) < MAX_TIME_TO_GROUP_MESSAGES) {
nextItem.fromSipUri != chatMessageData.fromSipUri
} else {
true
}
} else {
true
}
}
lifecycleOwner = viewLifecycleOwner

View file

@ -136,6 +136,7 @@ class ConversationsListFragment : AbstractTopBarFragment() {
Log.i(
"$TAG Default account changed, updating avatar in top bar & re-computing conversations"
)
listViewModel.applyFilter()
}
}

View file

@ -19,6 +19,7 @@
*/
package org.linphone.ui.main.chat.model
import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.lifecycle.MutableLiveData
import org.linphone.core.ChatMessage
@ -47,4 +48,13 @@ class ChatMessageModel @WorkerThread constructor(
init {
state.postValue(chatMessage.state)
}
@UiThread
fun onLongClick(): Boolean {
return true
}
@UiThread
fun showDeliveryInfo() {
}
}

View file

@ -35,11 +35,4 @@ class EventLogModel @WorkerThread constructor(eventLog: EventLog, avatarModel: C
}
val notifyId = eventLog.notifyId
fun destroy() {
/*when (data) {
is EventData -> data.destroy()
is ChatMessageModel -> data.destroy()
}*/
}
}

View file

@ -53,6 +53,17 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
private val avatarsMap = hashMapOf<String, ContactAvatarModel>()
init {
}
override fun onCleared() {
super.onCleared()
coreContext.postOnCoreThread {
avatarsMap.values.forEach(ContactAvatarModel::destroy)
}
}
@UiThread
fun findChatRoom(localSipUri: String, remoteSipUri: String) {
coreContext.postOnCoreThread { core ->
@ -105,12 +116,14 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
avatarModel.postValue(getAvatarModelForAddress(address))
val eventsList = arrayListOf<EventLogModel>()
val history = chatRoom.getHistoryEvents(0)
for (event in history) {
val avatar = getAvatarModelForAddress(event.chatMessage?.fromAddress)
val model = EventLogModel(event, avatar)
eventsList.add(model)
}
events.postValue(eventsList)
chatRoom.markAsRead()
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M224,48H32a8,8 0,0 0,-8 8V192a16,16 0,0 0,16 16H216a16,16 0,0 0,16 -16V56A8,8 0,0 0,224 48ZM203.43,64 L128,133.15 52.57,64ZM216,192H40V74.19l82.59,75.71a8,8 0,0 0,10.82 0L216,74.19V192Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -6,19 +6,20 @@
<data>
<import type="android.view.View" />
<import type="org.linphone.core.ConsolidatedPresence" />
<variable
name="onLongClickListener"
type="View.OnLongClickListener" />
<import type="org.linphone.core.ChatMessage.State" />
<variable
name="model"
type="org.linphone.ui.main.chat.model.ChatMessageModel" />
<variable
name="isGroupedWithPreviousOne"
type="Boolean" />
<variable
name="isLastOneOfGroup"
type="Boolean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:onLongClick="@{onLongClickListener}"
android:onLongClick="@{() -> model.onLongClick()}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@{isGroupedWithPreviousOne ? @dimen/chat_bubble_grouped_top_margin : @dimen/chat_bubble_top_margin, default=@dimen/chat_bubble_top_margin}"
@ -26,8 +27,8 @@
<io.getstream.avatarview.AvatarView
android:id="@+id/avatar"
android:layout_width="@dimen/avatar_list_cell_size"
android:layout_height="@dimen/avatar_list_cell_size"
android:layout_width="@dimen/avatar_bubble_size"
android:layout_height="@dimen/avatar_bubble_size"
android:adjustViewBounds="true"
android:background="@drawable/shape_circle_light_blue_background"
android:visibility="@{isGroupedWithPreviousOne ? View.INVISIBLE : View.VISIBLE}"
@ -44,13 +45,13 @@
<ImageView
android:id="@+id/presence_badge"
android:layout_width="@dimen/avatar_presence_badge_size"
android:layout_height="@dimen/avatar_presence_badge_size"
android:layout_marginEnd="@dimen/avatar_presence_badge_end_margin"
android:layout_width="@dimen/avatar_bubble_presence_badge_size"
android:layout_height="@dimen/avatar_bubble_presence_badge_size"
android:layout_marginEnd="1dp"
android:background="@drawable/led_background"
android:padding="@dimen/avatar_presence_badge_padding"
android:padding="1dp"
app:presenceIcon="@{model.avatarModel.presenceStatus}"
android:visibility="@{model.avatarModel.presenceStatus == ConsolidatedPresence.Offline ? View.GONE : View.VISIBLE}"
android:visibility="@{isGroupedWithPreviousOne || model.avatarModel.presenceStatus == ConsolidatedPresence.Offline ? View.GONE : View.VISIBLE}"
app:layout_constraintEnd_toEndOf="@id/avatar"
app:layout_constraintBottom_toBottomOf="@id/avatar"/>
@ -58,26 +59,90 @@
android:id="@+id/background"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="10dp"
android:src="@{isGroupedWithPreviousOne ? @drawable/shape_chat_bubble_incoming_full : @drawable/shape_chat_bubble_incoming_first, default=@drawable/shape_chat_bubble_incoming_first}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/text_message"
app:layout_constraintEnd_toEndOf="@id/text_message"/>
app:layout_constraintBottom_toBottomOf="@id/bubble_bottom_barrier"
app:layout_constraintStart_toEndOf="@id/avatar"
app:layout_constraintEnd_toEndOf="@id/bubble_end_barrier"/>
<org.linphone.ui.main.chat.view.ChatBubbleTextView
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bubble_end_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:barrierMargin="18dp"
app:constraint_referenced_ids="text_message, delivery_status"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bubble_bottom_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="text_bottom_anchor, delivery_status"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/text_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:padding="16dp"
android:layout_marginStart="18dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="18dp"
android:text="@{model.text, default=`Lorem ipsum dolor sit amet`}"
android:textSize="14sp"
android:textColor="@color/gray_main2_700"
android:gravity="center_vertical|start"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/avatar"/>
app:layout_constrainedWidth="true"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintTop_toTopOf="@id/background"
app:layout_constraintStart_toStartOf="@id/background"
app:layout_constraintEnd_toStartOf="@id/text_end_anchor"/>
<View
android:id="@+id/text_bottom_anchor"
android:layout_width="1dp"
android:layout_height="1dp"
android:layout_marginTop="12dp"
app:layout_constraintTop_toBottomOf="@id/text_message"
app:layout_constraintStart_toStartOf="@id/text_message"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/text_end_anchor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="16dp" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/date_time"
android:onClick="@{() -> model.showDeliveryInfo()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:text="@{model.time, default=`13:40`}"
android:textSize="12sp"
android:visibility="@{isLastOneOfGroup ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toBottomOf="@id/text_message"
app:layout_constraintStart_toStartOf="@id/text_message"
app:layout_constraintBottom_toBottomOf="@id/background"/>
<ImageView
style="@style/default_text_style_300"
android:id="@+id/delivery_status"
android:onClick="@{() -> model.showDeliveryInfo()}"
android:layout_width="@dimen/small_icon_size"
android:layout_height="@dimen/small_icon_size"
android:layout_marginStart="5dp"
android:layout_marginTop="2dp"
android:src="@{model.state == State.Displayed ? @drawable/checks : model.state == State.DeliveredToUser ? @drawable/check : model.state == State.Delivered ? @drawable/envelope_simple : model.state == State.NotDelivered ? @drawable/warning_circle : @drawable/in_progress, default=@drawable/in_progress}"
android:visibility="@{isLastOneOfGroup ? View.VISIBLE : View.GONE}"
app:layout_constraintTop_toTopOf="@id/date_time"
app:layout_constraintStart_toEndOf="@id/date_time"
app:layout_constraintBottom_toBottomOf="@id/date_time"
app:tint="@color/orange_main_500" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -14,6 +14,9 @@
<variable
name="isGroupedWithPreviousOne"
type="Boolean" />
<variable
name="isLastOneOfGroup"
type="Boolean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout