Lots of work on bubbles

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-05-10 21:14:02 +02:00
parent 7fb8dacb1e
commit 9386dd881c
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
10 changed files with 168 additions and 165 deletions

View File

@ -6,8 +6,9 @@ import android.graphics.drawable.LayerDrawable
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.RelativeLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.api.load import androidx.recyclerview.widget.RecyclerView
import coil.api.loadAny import coil.api.loadAny
import coil.api.newLoadBuilder import coil.api.newLoadBuilder
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
@ -71,12 +72,19 @@ open class ChatPresenter<T : Any>(context: Context, private val onElementClickPa
if (elementType == ChatElementTypes.CHAT_MESSAGE) { if (elementType == ChatElementTypes.CHAT_MESSAGE) {
var shouldShowNameAndAvatar = true var shouldShowNameAndAvatar = true
val previousElement = getAdapter().elementAt(holder.adapterPosition - 1) val previousElement = getAdapter().elementAt(holder.adapterPosition - 1)
val isOutgoingMessage = it.actorId == it.activeUser?.userId
var isGrouped = false
if (isOutgoingMessage) {
shouldShowNameAndAvatar = false
}
if (previousElement != null && previousElement.element.data != null && previousElement.element.data is ChatElement) { if (previousElement != null && previousElement.element.data != null && previousElement.element.data is ChatElement) {
val previousChatElement = previousElement.element.data as ChatElement val previousChatElement = previousElement.element.data as ChatElement
if (previousChatElement.elementType == ChatElementTypes.CHAT_MESSAGE) { if (previousChatElement.elementType == ChatElementTypes.CHAT_MESSAGE) {
val previousChatMessage = previousChatElement.data as ChatMessage val previousChatMessage = previousChatElement.data as ChatMessage
if (previousChatMessage.actorId == it.actorId) { if (previousChatMessage.actorId == it.actorId) {
shouldShowNameAndAvatar = false shouldShowNameAndAvatar = false
isGrouped = true
} }
} }
} }
@ -93,6 +101,8 @@ open class ChatPresenter<T : Any>(context: Context, private val onElementClickPa
if (shouldShowNameAndAvatar) { if (shouldShowNameAndAvatar) {
holder.itemView.authorLayout.isVisible = true holder.itemView.authorLayout.isVisible = true
holder.itemView.authorName.isVisible = true
holder.itemView.authorName?.text = if (it.user.name.isNotEmpty()) it.user.name else context.resources.getText(R.string.nc_guest) holder.itemView.authorName?.text = if (it.user.name.isNotEmpty()) it.user.name else context.resources.getText(R.string.nc_guest)
if (it.actorType == "bots" && it.actorId == "changelog") { if (it.actorType == "bots" && it.actorId == "changelog") {
val layers = arrayOfNulls<Drawable>(2) val layers = arrayOfNulls<Drawable>(2)
@ -118,6 +128,42 @@ open class ChatPresenter<T : Any>(context: Context, private val onElementClickPa
} }
} else { } else {
holder.itemView.authorLayout.isVisible = false holder.itemView.authorLayout.isVisible = false
holder.itemView.authorName.isVisible = false
}
val messageLayoutParams = holder.itemView.messageLayout.layoutParams as RelativeLayout.LayoutParams
if (isOutgoingMessage) {
messageLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_END, 1)
messageLayoutParams.marginStart = 40.dp
} else {
messageLayoutParams.removeRule(RelativeLayout.ALIGN_PARENT_END)
messageLayoutParams.marginEnd = 40.dp
}
if (isGrouped) {
if (isOutgoingMessage) {
messageLayoutParams.marginEnd = 8.dp
holder.itemView.messageLayout.background = context.resources.getDrawable(R.drawable.outgoing_grouped_message_background)
} else {
messageLayoutParams.marginStart = 40.dp
holder.itemView.messageLayout.background = context.resources.getDrawable(R.drawable.incoming_grouped_message_background)
}
holder.itemView.messageLayout.layoutParams = messageLayoutParams
} else {
if (isOutgoingMessage) {
messageLayoutParams.marginEnd = 8.dp
holder.itemView.messageLayout.background = context.resources.getDrawable(R.drawable.outgoing_message_background)
} else {
messageLayoutParams.marginStart = 0
holder.itemView.messageLayout.background = context.resources.getDrawable(R.drawable.incoming_message_background)
}
holder.itemView.messageLayout.layoutParams = messageLayoutParams
} }
it.parentMessage?.let { parentMessage -> it.parentMessage?.let { parentMessage ->
@ -161,6 +207,11 @@ open class ChatPresenter<T : Any>(context: Context, private val onElementClickPa
holder.itemView.quotedAuthor.text = if (parentMessage.user.name.isNotEmpty()) parentMessage.user.name else context.resources.getText(R.string.nc_guest) holder.itemView.quotedAuthor.text = if (parentMessage.user.name.isNotEmpty()) parentMessage.user.name else context.resources.getText(R.string.nc_guest)
holder.itemView.quotedChatText.text = parentMessage.text holder.itemView.quotedChatText.text = parentMessage.text
holder.itemView.quotedMessageTime?.text = DateFormatter.format(it.createdAt, DateFormatter.Template.TIME) holder.itemView.quotedMessageTime?.text = DateFormatter.format(it.createdAt, DateFormatter.Template.TIME)
if (isOutgoingMessage) {
holder.itemView.quoteColoredView.setBackgroundColor(context.resources.getColor(R.color.bg_message_list_incoming_bubble))
} else {
holder.itemView.quoteColoredView.setBackgroundColor(context.resources.getColor(R.color.bg_message_list_outcoming_bubble))
}
} ?: run { } ?: run {
holder.itemView.quotedMessageLayout.isVisible = false holder.itemView.quotedMessageLayout.isVisible = false
} }

View File

@ -0,0 +1,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="15dp"/>
<solid android:color="@color/bg_message_list_incoming_bubble" />
</shape>

View File

@ -0,0 +1,11 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp"
android:topLeftRadius="0dp"
android:topRightRadius="15dp" />
<solid android:color="@color/bg_message_list_incoming_bubble" />
</shape>

View File

@ -0,0 +1,8 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="15dp"/>
<solid android:color="@color/bg_message_list_outcoming_bubble" />
</shape>

View File

@ -0,0 +1,11 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="15dp"
android:bottomRightRadius="15dp"
android:topLeftRadius="15dp"
android:topRightRadius="0dp" />
<solid android:color="@color/bg_message_list_outcoming_bubble" />
</shape>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ 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 <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="@dimen/message_bubble_corners_radius"
android:bottomRightRadius="@dimen/message_bubble_corners_radius"
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/bg_message_list_incoming_bubble" />
</shape>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ 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 <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="@dimen/message_bubble_corners_radius"
android:topLeftRadius="@dimen/message_bubble_corners_radius"
android:bottomRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/bg_default" />
</shape>

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Nextcloud Talk application
~
~ @author Mario Danic
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ 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 <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="0dp"
android:bottomRightRadius="@dimen/message_bubble_corners_radius"
android:bottomLeftRadius="@dimen/message_bubble_corners_radius"
android:topRightRadius="@dimen/message_bubble_corners_radius" />
<solid android:color="@color/bg_message_list_incoming_bubble" />
</shape>

View File

@ -5,7 +5,8 @@
android:id="@+id/quotedMessageLayout" android:id="@+id/quotedMessageLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="4dp"> android:clipToPadding="true"
android:layout_marginVertical="2dp">
<View <View
android:id="@+id/quoteColoredView" android:id="@+id/quoteColoredView"

View File

@ -1,15 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginVertical="2dp"
android:animateLayoutChanges="true"> android:animateLayoutChanges="true">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:id="@+id/authorLayout"> android:id="@+id/authorLayout">
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
@ -17,74 +20,71 @@
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_marginEnd="8dp"
android:layout_centerVertical="true" android:layout_centerVertical="true"
app:shapeAppearanceOverlay="@style/circleImageView" app:shapeAppearanceOverlay="@style/circleImageView"
tools:srcCompat="@tools:sample/avatars" /> tools:srcCompat="@tools:sample/avatars" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toEndOf="@id/authorAvatar"
android:textSize="14sp"
android:id="@+id/authorName"
android:layout_centerVertical="true"
android:gravity="center_vertical"
tools:text="Regular user"/>
</RelativeLayout> </RelativeLayout>
<include layout="@layout/item_message_quote" <RelativeLayout
android:layout_below="@id/authorLayout"
android:layout_marginStart="40dp"
android:layout_marginEnd="8dp"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<ImageView
android:id="@+id/previewImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_alignParentStart="true"
android:scaleType="fitStart"
android:layout_below="@id/quotedMessageLayout"
android:layout_marginStart="40dp"
android:layout_marginEnd="8dp"
tools:src="@tools:sample/backgrounds/scenic"/>
<androidx.emoji.widget.EmojiTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="8dp"
android:textSize="14sp"
android:id="@+id/chatMessage"
android:autoLink="all"
android:layout_below="@id/previewImage"
tools:text="Just another chat message"/>
<ProgressBar
android:layout_width="12dp"
android:layout_height="12dp"
android:id="@+id/sendingProgressBar"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="8dp"
android:visibility="gone"
android:progressBackgroundTint="@color/colorPrimary"/>
<TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/chatMessage" android:id="@+id/messageLayout"
android:textSize="12sp" android:layout_toEndOf="@id/authorLayout"
android:layout_above="@id/failedToSendNotice" android:background="@drawable/incoming_message_background">
android:layout_toStartOf="@id/sendingProgressBar"
android:layout_alignWithParentIfMissing="true" <TextView
android:id="@+id/messageTime" android:layout_width="wrap_content"
android:layout_marginEnd="8dp" android:layout_height="wrap_content"
tools:text="12:30"/> android:textSize="14sp"
android:layout_marginTop="4dp"
android:id="@+id/authorName"
android:layout_alignParentTop="true"
android:layout_marginHorizontal="8dp"
tools:text="Regular user"/>
<include layout="@layout/item_message_quote"
android:layout_marginHorizontal="8dp"
android:layout_marginTop="4dp"
android:layout_below="@id/authorName"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<ImageView
android:id="@+id/previewImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:layout_marginTop="4dp"
android:layout_alignParentStart="true"
android:scaleType="fitStart"
android:layout_below="@id/quotedMessageLayout"
android:layout_marginHorizontal="8dp"
tools:src="@tools:sample/backgrounds/scenic"/>
<androidx.emoji.widget.EmojiTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_marginTop="4dp"
android:layout_marginHorizontal="8dp"
android:id="@+id/chatMessage"
android:autoLink="all"
android:layout_below="@id/previewImage"
tools:text="Just another chat message"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/chatMessage"
android:textSize="12sp"
android:layout_alignParentEnd="true"
android:layout_alignWithParentIfMissing="true"
android:id="@+id/messageTime"
android:layout_marginBottom="4dp"
android:layout_marginEnd="8dp"
tools:text="12:30"/>
</RelativeLayout>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -92,11 +92,23 @@
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:text="@string/nc_failed_to_send" android:text="@string/nc_failed_to_send"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:textAlignment="viewEnd" android:layout_alignStart="@id/messageLayout"
android:layout_alignEnd="@id/messageLayout"
android:layout_below="@id/messageLayout"
android:textColor="@color/nc_darkRed" android:textColor="@color/nc_darkRed"
android:id="@+id/failedToSendNotice" android:id="@+id/failedToSendNotice"
android:visibility="visible" android:visibility="gone"
android:layout_alignParentEnd="true"/> android:layout_alignParentEnd="true"/>
<ProgressBar
android:layout_width="12dp"
android:layout_height="12dp"
android:id="@+id/sendingProgressBar"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_below="@id/messageLayout"
android:layout_marginEnd="8dp"
android:visibility="gone"
android:progressBackgroundTint="@color/colorPrimary"/>
</RelativeLayout> </RelativeLayout>