diff --git a/changelog.d/5303.misc b/changelog.d/5303.misc new file mode 100644 index 0000000000..dbad0b738d --- /dev/null +++ b/changelog.d/5303.misc @@ -0,0 +1 @@ +Improve Bubble layouts rendering. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt new file mode 100644 index 0000000000..f11b1c6951 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleContentLayout.kt @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.timeline.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.view.ViewStub +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.marginBottom +import androidx.core.view.marginEnd +import androidx.core.view.marginStart +import androidx.core.view.marginTop +import im.vector.app.R +import im.vector.app.core.resources.LocaleProvider +import im.vector.app.core.resources.getLayoutDirectionFromCurrentLocale + +class MessageBubbleContentLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + ConstraintLayout(context, attrs, defStyleAttr) { + + private var messageTextView: TextView? = null + + private lateinit var contentContainerView: View + private lateinit var timeView: View + private lateinit var contentOverlayView: View + private var localeLayoutDirection: Int = View.LAYOUT_DIRECTION_LOCALE + + private val timeViewMeasuredWidthWithMargins: Int + get() = timeView.measuredWidth + timeView.marginStart + timeView.marginEnd + + override fun onFinishInflate() { + super.onFinishInflate() + val textViewStub: ViewStub = findViewById(R.id.messageContentTextStub) + contentContainerView = findViewById(R.id.viewStubContainer) + contentOverlayView = findViewById(R.id.messageOverlayView) + timeView = findViewById(R.id.messageTimeView) + textViewStub.setOnInflateListener { _, inflated -> + textViewStub.setOnInflateListener(null) + messageTextView = inflated.findViewById(R.id.messageTextView) + } + localeLayoutDirection = LocaleProvider(resources).getLayoutDirectionFromCurrentLocale() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + // Let the ConstraintLayouts measure children + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val messageTextView = this.messageTextView + // Then if we have a text message layout, we can resize ourself so we can position the timeView without losing space. + if (messageTextView != null) { + val width: Int + val height: Int + val textLineCount = messageTextView.lineCount + val maxContentWidth = (contentContainerView.layoutParams as LayoutParams).matchConstraintMaxWidth + val lastLineWidth = if (textLineCount != 0) messageTextView.layout.getLineWidth(textLineCount - 1) else 0f + if (textLineCount == 1 && contentContainerView.measuredWidth + timeViewMeasuredWidthWithMargins < maxContentWidth) { + width = contentContainerView.measuredWidth + timeViewMeasuredWidthWithMargins + height = contentContainerView.measuredHeight + } else if (textLineCount > 1 && lastLineWidth + timeViewMeasuredWidthWithMargins + < contentContainerView.measuredWidth - contentContainerView.paddingEnd) { + width = contentContainerView.measuredWidth + height = contentContainerView.measuredHeight + } else { + width = contentContainerView.measuredWidth + height = contentContainerView.measuredHeight + timeView.measuredHeight + } + setMeasuredDimension(width, height) + } + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + // If we have a text message layout, we want to render it manually, so we don't call super.onLayout + if (messageTextView != null) { + val parentLeft: Int = paddingLeft + val parentRight: Int = right - left - paddingRight + val parentTop: Int = paddingTop + val parentBottom: Int = bottom - top - paddingBottom + if (localeLayoutDirection == LAYOUT_DIRECTION_RTL) { + val contentLeft = parentRight - contentContainerView.measuredWidth - contentContainerView.marginEnd + contentContainerView.layout( + contentLeft, + parentTop + contentContainerView.marginTop, + parentRight - contentContainerView.marginEnd, + parentTop + contentContainerView.marginTop + contentContainerView.measuredHeight + ) + timeView.layout( + parentLeft + timeView.marginEnd, + parentBottom - timeView.measuredHeight - timeView.marginBottom, + parentLeft + timeView.measuredWidth + timeView.marginEnd, + parentBottom - timeView.marginBottom + ) + } else { + contentContainerView.layout( + parentLeft, + parentTop, + parentLeft + contentContainerView.measuredWidth, + parentTop + contentContainerView.measuredHeight + ) + timeView.layout( + parentRight - timeView.measuredWidth - timeView.marginEnd, + parentBottom - timeView.measuredHeight - timeView.marginBottom, + parentRight - timeView.marginEnd, + parentBottom - timeView.marginBottom + ) + } + } else { + super.onLayout(changed, left, top, right, bottom) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt index 422dfb0dbd..93eae9a1d3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/view/MessageBubbleView.kt @@ -49,7 +49,7 @@ class MessageBubbleView @JvmOverloads constructor(context: Context, attrs: Attri private var isIncoming: Boolean = false private val horizontalStubPadding = DimensionConverter(resources).dpToPx(12) - private val verticalStubPadding = DimensionConverter(resources).dpToPx(4) + private val verticalStubPadding = DimensionConverter(resources).dpToPx(8) private lateinit var views: ViewMessageBubbleBinding private lateinit var bubbleDrawable: MaterialShapeDrawable diff --git a/vector/src/main/res/layout/item_timeline_event_text_message_stub.xml b/vector/src/main/res/layout/item_timeline_event_text_message_stub.xml index e2a11c7926..5c5280ad4e 100644 --- a/vector/src/main/res/layout/item_timeline_event_text_message_stub.xml +++ b/vector/src/main/res/layout/item_timeline_event_text_message_stub.xml @@ -12,6 +12,7 @@ style="@style/Widget.Vector.TextView.Body" android:layout_width="match_parent" android:layout_height="wrap_content" + android:textAlignment="viewStart" android:textColor="?vctr_content_primary" tools:text="@sample/messages.json/data/message" /> diff --git a/vector/src/main/res/layout/view_message_bubble.xml b/vector/src/main/res/layout/view_message_bubble.xml index e6f24e43c5..5ae5afc329 100644 --- a/vector/src/main/res/layout/view_message_bubble.xml +++ b/vector/src/main/res/layout/view_message_bubble.xml @@ -84,7 +84,7 @@ android:clipChildren="false" android:clipToPadding="false"> - - +