Bubbles: continue R&D on UI

This commit is contained in:
ganfra 2022-01-05 11:00:12 +01:00
parent e540b26112
commit bde1df0322
10 changed files with 109 additions and 19 deletions

View file

@ -13,6 +13,9 @@
<color name="button_bot_background_color">#14368BD6</color>
<color name="button_bot_enabled_text_color">@color/palette_azure</color>
<color name="bubble_background_outgoing">#0F0DBD8B</color>
<color name="bubble_background_incoming">@color/element_system_light</color>
<!-- Notification (do not depends on theme) -->
<color name="notification_accent_color">@color/palette_azure</color>
<color name="key_share_req_accent_color">@color/palette_melon</color>
@ -137,4 +140,5 @@
<attr name="vctr_presence_indicator_offline" format="color" />
<color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color>
<color name="vctr_presence_indicator_offline_dark">@color/palette_gray_450</color>
</resources>

View file

@ -47,4 +47,8 @@
<dimen name="composer_min_height">56dp</dimen>
<dimen name="composer_attachment_size">52dp</dimen>
<dimen name="composer_attachment_margin">1dp</dimen>
<dimen name="chat_bubble_margin_start">28dp</dimen>
<dimen name="chat_bubble_margin_end">62dp</dimen>
</resources>

View file

@ -3,6 +3,8 @@
<declare-styleable name="MessageBubble">
<attr name="incoming_style" format="boolean" />
<attr name="is_first" format="boolean" />
<attr name="is_last" format="boolean" />
</declare-styleable>
</resources>

View file

@ -145,6 +145,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
}
else -> {
it.apply(RequestOptions.circleCropTransform())
}
}
}

View file

@ -24,7 +24,7 @@ class AvatarSizeProvider @Inject constructor(private val dimensionConverter: Dim
private val avatarStyle = AvatarStyle.X_SMALL
val leftGuideline: Int by lazy {
dimensionConverter.dpToPx(avatarStyle.avatarSizeDP)
dimensionConverter.dpToPx(avatarStyle.avatarSizeDP + 4)
}
val avatarSize: Int by lazy {
@ -37,7 +37,7 @@ class AvatarSizeProvider @Inject constructor(private val dimensionConverter: Dim
BIG(50),
MEDIUM(40),
SMALL(30),
X_SMALL(24),
X_SMALL(28),
NONE(0)
}
}

View file

@ -17,42 +17,94 @@
package im.vector.app.features.home.room.detail.timeline.view
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes
import androidx.core.view.updateLayoutParams
import com.google.android.material.shape.CornerFamily
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import im.vector.app.R
import im.vector.app.core.utils.DimensionConverter
class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : RelativeLayout(context, attrs, defStyleAttr) {
var incoming: Boolean = false
var isFirst: Boolean = false
var isLast: Boolean = false
var cornerRadius = DimensionConverter(resources).dpToPx(12).toFloat()
init {
inflate(context, R.layout.view_message_bubble, this)
context.withStyledAttributes(attrs, R.styleable.MessageBubble) {
incoming = getBoolean(R.styleable.MessageBubble_incoming_style, false)
isFirst = getBoolean(R.styleable.MessageBubble_is_first, false)
isLast = getBoolean(R.styleable.MessageBubble_is_last, false)
}
}
override fun onFinishInflate() {
super.onFinishInflate()
val currentLayoutDirection = layoutDirection
findViewById<ViewGroup>(R.id.bubbleView).apply {
background = createBackgroundDrawable()
outlineProvider = ViewOutlineProvider.BACKGROUND
clipToOutline = true
}
if (incoming) {
findViewById<View>(R.id.informationBottom).layoutDirection = currentLayoutDirection
findViewById<View>(R.id.bubbleWrapper).layoutDirection = currentLayoutDirection
findViewById<View>(R.id.bubbleView).layoutDirection = currentLayoutDirection
findViewById<RelativeLayout>(R.id.bubbleView).setBackgroundResource(R.drawable.bg_timeline_incoming_message)
findViewById<View>(R.id.messageEndGuideline).updateLayoutParams<LayoutParams> {
marginEnd = resources.getDimensionPixelSize(R.dimen.chat_bubble_margin_end)
}
} else {
val oppositeLayoutDirection = if (currentLayoutDirection == View.LAYOUT_DIRECTION_LTR) {
View.LAYOUT_DIRECTION_RTL
} else {
View.LAYOUT_DIRECTION_LTR
}
findViewById<View>(R.id.informationBottom).layoutDirection = oppositeLayoutDirection
findViewById<View>(R.id.bubbleWrapper).layoutDirection = oppositeLayoutDirection
findViewById<View>(R.id.bubbleView).layoutDirection = currentLayoutDirection
findViewById<RelativeLayout>(R.id.bubbleView).setBackgroundResource(R.drawable.bg_timeline_outgoing_message)
findViewById<View>(R.id.messageEndGuideline).updateLayoutParams<LayoutParams> {
marginEnd = resources.getDimensionPixelSize(R.dimen.chat_bubble_margin_start)
}
}
}
private fun createBackgroundDrawable(): Drawable {
val topCornerFamily = if (isFirst) CornerFamily.ROUNDED else CornerFamily.CUT
val bottomCornerFamily = if (isLast) CornerFamily.ROUNDED else CornerFamily.CUT
val topRadius = if (isFirst) cornerRadius else 0f
val bottomRadius = if (isLast) cornerRadius else 0f
val shapeAppearanceModelBuilder = ShapeAppearanceModel().toBuilder()
val backgroundColor: Int
if (incoming) {
backgroundColor = R.color.bubble_background_incoming
shapeAppearanceModelBuilder
.setTopRightCorner(CornerFamily.ROUNDED, cornerRadius)
.setBottomRightCorner(CornerFamily.ROUNDED, cornerRadius)
.setTopLeftCorner(topCornerFamily, topRadius)
.setBottomLeftCorner(bottomCornerFamily, bottomRadius)
} else {
backgroundColor = R.color.bubble_background_outgoing
shapeAppearanceModelBuilder
.setTopLeftCorner(CornerFamily.ROUNDED, cornerRadius)
.setBottomLeftCorner(CornerFamily.ROUNDED, cornerRadius)
.setTopRightCorner(topCornerFamily, topRadius)
.setBottomRightCorner(bottomCornerFamily, bottomRadius)
}
val shapeAppearanceModel = shapeAppearanceModelBuilder.build()
val shapeDrawable = MaterialShapeDrawable(shapeAppearanceModel)
shapeDrawable.fillColor = ContextCompat.getColorStateList(context, backgroundColor)
return shapeDrawable
}
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/transparent"/>
<stroke
android:width="2dp"
android:color="?android:colorBackground"/>
</shape>

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/vctr_system" />
<corners android:radius="12dp"/>
</shape>

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#0F0DBD8B" />
<corners android:radius="12dp"/>
</shape>

View file

@ -19,9 +19,12 @@
android:id="@+id/messageAvatarImageView"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginStart="8dp"
android:layout_marginStart="12dp"
android:layout_marginTop="4dp"
android:padding="2dp"
android:background="@drawable/bg_avatar_border"
android:contentDescription="@string/avatar"
android:elevation="2dp"
tools:src="@sample/user_round_avatars" />
<TextView
@ -31,7 +34,7 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="8dp"
android:layout_marginStart="12dp"
android:layout_marginEnd="4dp"
android:layout_toEndOf="@id/messageStartGuideline"
android:ellipsize="end"
@ -46,6 +49,13 @@
android:layout_height="0dp"
tools:layout_marginStart="52dp" />
<View
android:id="@+id/messageEndGuideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="64dp" />
<Space
android:id="@+id/decorationSpace"
android:layout_width="4dp"
@ -59,6 +69,7 @@
android:layout_alignTop="@id/bubbleWrapper"
android:layout_alignEnd="@id/decorationSpace"
android:layout_marginTop="7dp"
android:elevation="2dp"
android:visibility="gone"
tools:visibility="visible" />
@ -67,45 +78,49 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/messageMemberNameView"
android:layout_toStartOf="@id/messageSendStateImageView"
android:layout_toStartOf="@id/messageEndGuideline"
android:layout_toEndOf="@id/messageStartGuideline"
android:addStatesFromChildren="true"
android:clipChildren="false"
android:clipToPadding="false">
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bubbleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="4dp"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:addStatesFromChildren="true"
android:background="@drawable/bg_timeline_incoming_message"
android:clipChildren="false"
android:clipToPadding="false"
tools:ignore="UselessParent">
android:paddingStart="4dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<include
android:id="@+id/viewStubContainer"
layout="@layout/item_timeline_event_view_stubs_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:addStatesFromChildren="true" />
android:addStatesFromChildren="true"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@id/messageTimeView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="300dp" />
<TextView
android:id="@+id/messageTimeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/viewStubContainer"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:layout_toEndOf="@id/viewStubContainer"
android:textColor="?vctr_content_tertiary"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@id/viewStubContainer"
app:layout_constraintEnd_toEndOf="parent"
tools:text="@tools:sample/date/hhmm" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
<im.vector.app.core.ui.views.SendStateImageView
@ -143,6 +158,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/bubbleWrapper"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:layout_toStartOf="@id/messageEndGuideline"
android:layout_toEndOf="@id/messageStartGuideline"
android:addStatesFromChildren="true"
android:orientation="vertical">
@ -151,7 +169,6 @@
android:id="@+id/reactionsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="4dp"
app:dividerDrawable="@drawable/reaction_divider"
app:flexWrap="wrap"