Initial message bubbles
Drawables taken from AOSP Messaging
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* https://stackoverflow.com/questions/7439748/why-is-wrap-content-in-multiple-line-textview-filling-parent
|
||||
*/
|
||||
|
||||
package im.vector.riotx.core.ui.views;
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Layout
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import kotlin.math.ceil
|
||||
|
||||
class WrapWidthTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : AppCompatTextView(context, attrs, defStyleAttr) {
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
val layout = this.layout ?: return
|
||||
val width = ceil(getMaxLineWidth(layout)).toInt() + compoundPaddingLeft + compoundPaddingRight
|
||||
val height = measuredHeight
|
||||
setMeasuredDimension(width, height)
|
||||
}
|
||||
|
||||
private fun getMaxLineWidth(layout: Layout): Float {
|
||||
var maxWidth = 0.0f
|
||||
val lines = layout.lineCount
|
||||
for (i in 0 until lines) {
|
||||
if (layout.getLineWidth(i) > maxWidth) {
|
||||
maxWidth = layout.getLineWidth(i)
|
||||
}
|
||||
}
|
||||
return maxWidth
|
||||
}
|
||||
}
|
@ -397,6 +397,8 @@ class MessageItemFactory @Inject constructor(
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.attributes(attributes)
|
||||
.highlighted(highlight)
|
||||
.outgoingMessage(informationData.sentByMe)
|
||||
.incomingMessage(!informationData.sentByMe)
|
||||
.movementMethod(createLinkMovementMethod(callback))
|
||||
}
|
||||
|
||||
|
@ -16,14 +16,24 @@
|
||||
|
||||
package im.vector.riotx.features.home.room.detail.timeline.item
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.text.method.MovementMethod
|
||||
import android.view.Gravity
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.PrecomputedTextCompat
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.riotx.R
|
||||
import im.vector.riotx.features.home.room.detail.timeline.tools.findPillsAndProcess
|
||||
import im.vector.riotx.features.themes.BubbleThemeUtils
|
||||
import im.vector.riotx.features.themes.BubbleThemeUtils.BUBBLE_STYLE_BOTH
|
||||
import im.vector.riotx.features.themes.BubbleThemeUtils.BUBBLE_STYLE_NONE
|
||||
import im.vector.riotx.features.themes.BubbleThemeUtils.BUBBLE_STYLE_START
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import kotlin.math.round
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
@ -36,6 +46,10 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
var useBigFont: Boolean = false
|
||||
@EpoxyAttribute
|
||||
var movementMethod: MovementMethod? = null
|
||||
@EpoxyAttribute
|
||||
var incomingMessage: Boolean = false
|
||||
@EpoxyAttribute
|
||||
var outgoingMessage: Boolean = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
@ -56,6 +70,35 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
TextViewCompat.getTextMetricsParams(holder.messageView),
|
||||
null)
|
||||
holder.messageView.setTextFuture(textFuture)
|
||||
|
||||
var bubbleStyle = if (incomingMessage || outgoingMessage) BubbleThemeUtils.getBubbleStyle(holder.messageView.context) else BUBBLE_STYLE_NONE
|
||||
when (bubbleStyle) {
|
||||
BUBBLE_STYLE_NONE -> {
|
||||
holder.messageView.background = null
|
||||
holder.messageView.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
BUBBLE_STYLE_START, BUBBLE_STYLE_BOTH -> {
|
||||
holder.messageView.setBackgroundResource(R.drawable.msg_bubble_incoming)
|
||||
var tintColor = ColorStateList(
|
||||
arrayOf(intArrayOf(0)),
|
||||
intArrayOf(ThemeUtils.getColor(holder.messageView.context,
|
||||
if (outgoingMessage) R.attr.sc_message_bg_outgoing else R.attr.sc_message_bg_incoming)
|
||||
)
|
||||
)
|
||||
holder.messageView.backgroundTintList = tintColor
|
||||
val density = holder.messageView.resources.displayMetrics.density
|
||||
holder.messageView.setPaddingRelative(
|
||||
round(20*density).toInt(),
|
||||
round(8*density).toInt(),
|
||||
round(8*density).toInt(),
|
||||
round(8*density).toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
if (holder.messageView.layoutParams is FrameLayout.LayoutParams) {
|
||||
(holder.messageView.layoutParams as FrameLayout.LayoutParams).gravity =
|
||||
if (outgoingMessage && bubbleStyle == BUBBLE_STYLE_BOTH) Gravity.END else Gravity.START
|
||||
}
|
||||
}
|
||||
|
||||
override fun getViewType() = STUB_ID
|
||||
|
@ -28,6 +28,7 @@ import im.vector.riotx.R
|
||||
import im.vector.riotx.core.preference.VectorListPreference
|
||||
import im.vector.riotx.core.preference.VectorPreference
|
||||
import im.vector.riotx.features.configuration.VectorConfiguration
|
||||
import im.vector.riotx.features.themes.BubbleThemeUtils
|
||||
import im.vector.riotx.features.themes.ThemeUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -66,6 +67,11 @@ class VectorSettingsPreferencesFragment @Inject constructor(
|
||||
false
|
||||
}
|
||||
}
|
||||
findPreference<VectorListPreference>(BubbleThemeUtils.BUBBLE_STYLE_KEY)!!
|
||||
.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
|
||||
BubbleThemeUtils.invalidateBubbleStyle()
|
||||
true
|
||||
}
|
||||
|
||||
// Url preview
|
||||
findPreference<SwitchPreference>(VectorPreferences.SETTINGS_SHOW_URL_PREVIEW_KEY)!!.let {
|
||||
|
@ -0,0 +1,27 @@
|
||||
package im.vector.riotx.features.themes
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
|
||||
/**
|
||||
* Util class for managing themes.
|
||||
*/
|
||||
object BubbleThemeUtils {
|
||||
const val BUBBLE_STYLE_KEY = "BUBBLE_STYLE_KEY"
|
||||
|
||||
const val BUBBLE_STYLE_NONE = "none"
|
||||
const val BUBBLE_STYLE_START = "start"
|
||||
const val BUBBLE_STYLE_BOTH = "both"
|
||||
private var mBubbleStyle: String = ""
|
||||
|
||||
fun getBubbleStyle(context: Context): String {
|
||||
if (mBubbleStyle == "") {
|
||||
mBubbleStyle = PreferenceManager.getDefaultSharedPreferences(context).getString(BUBBLE_STYLE_KEY, BUBBLE_STYLE_START)!!
|
||||
}
|
||||
return mBubbleStyle
|
||||
}
|
||||
|
||||
fun invalidateBubbleStyle() {
|
||||
mBubbleStyle = ""
|
||||
}
|
||||
}
|
BIN
vector/src/main/res/drawable-hdpi/msg_bubble_incoming.9.png
Normal file
After Width: | Height: | Size: 469 B |
After Width: | Height: | Size: 472 B |
After Width: | Height: | Size: 344 B |
After Width: | Height: | Size: 598 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
BIN
vector/src/main/res/drawable-mdpi/msg_bubble_incoming.9.png
Normal file
After Width: | Height: | Size: 339 B |
BIN
vector/src/main/res/drawable-xhdpi/msg_bubble_incoming.9.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
vector/src/main/res/drawable-xxhdpi/msg_bubble_incoming.9.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
vector/src/main/res/drawable-xxxhdpi/msg_bubble_incoming.9.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
@ -92,6 +92,7 @@
|
||||
android:id="@+id/messageContentTextStub"
|
||||
style="@style/TimelineContentStubBaseParams"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:inflatedId="@id/messageTextView"
|
||||
android:layout="@layout/item_timeline_event_text_message_stub"
|
||||
tools:visibility="visible" />
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<im.vector.riotx.core.ui.views.WrapWidthTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/messageTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="14sp"
|
||||
|
@ -3,4 +3,9 @@
|
||||
|
||||
<string name="redacted_stub_text">(Gelöschte Nachricht)</string>
|
||||
|
||||
<string name="bubble_style">Nachrichtblasen</string>
|
||||
<string name="bubble_style_none">Keine</string>
|
||||
<string name="bubble_style_start">Selbe Seite</string>
|
||||
<string name="bubble_style_both">Beide Seiten</string>
|
||||
|
||||
</resources>
|
||||
|
15
vector/src/main/res/values/arrays_sc.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string-array name="bubble_style_entries" translatable="false">
|
||||
<item>@string/bubble_style_none</item>
|
||||
<item>@string/bubble_style_start</item>
|
||||
<item>@string/bubble_style_both</item>
|
||||
</string-array>
|
||||
<string-array name="bubble_style_values" translatable="false">
|
||||
<item>none</item>
|
||||
<item>start</item>
|
||||
<item>both</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
11
vector/src/main/res/values/attrs_sc.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="VectorStylesSC">
|
||||
|
||||
<attr name="sc_message_bg_incoming" format="color" />
|
||||
<attr name="sc_message_bg_outgoing" format="color" />
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
@ -3,4 +3,9 @@
|
||||
|
||||
<string name="redacted_stub_text">(Deleted message)</string>
|
||||
|
||||
<string name="bubble_style">Message bubbles</string>
|
||||
<string name="bubble_style_none">None</string>
|
||||
<string name="bubble_style_start">Same side</string>
|
||||
<string name="bubble_style_both">Both sides</string>
|
||||
|
||||
</resources>
|
||||
|
@ -216,6 +216,9 @@
|
||||
<item name="snackbarButtonStyle">@style/VectorSnackBarButton</item>
|
||||
<!-- Style to use for message text within a SnackBar in this theme. -->
|
||||
<item name="snackbarTextViewStyle">@style/VectorSnackBarText</item>
|
||||
|
||||
<item name="sc_message_bg_incoming">#FF465561</item>
|
||||
<item name="sc_message_bg_outgoing">#FF1A2027</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Dark" parent="AppTheme.Base.Dark" />
|
||||
|
@ -216,6 +216,9 @@
|
||||
<item name="snackbarButtonStyle">@style/VectorSnackBarButton</item>
|
||||
<!-- Style to use for message text within a SnackBar in this theme. -->
|
||||
<item name="snackbarTextViewStyle">@style/VectorSnackBarText</item>
|
||||
|
||||
<item name="sc_message_bg_incoming">#FF999999</item>
|
||||
<item name="sc_message_bg_outgoing">#FFC7C7C7</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Light" parent="AppTheme.Base.Light" />
|
||||
|
@ -203,6 +203,9 @@
|
||||
<item name="snackbarButtonStyle">@style/VectorSnackBarButton</item>
|
||||
<!-- Style to use for message text within a SnackBar in this theme. -->
|
||||
<item name="snackbarTextViewStyle">@style/VectorSnackBarText</item>
|
||||
|
||||
<item name="sc_message_bg_incoming">@color/background_floating_sc</item>
|
||||
<item name="sc_message_bg_outgoing">@color/accent_sc_alpha25</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.SC" parent="AppTheme.Base.SC" />
|
||||
|
@ -21,6 +21,15 @@
|
||||
android:title="@string/settings_theme"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorListPreference
|
||||
android:defaultValue="start"
|
||||
android:entries="@array/bubble_style_entries"
|
||||
android:entryValues="@array/bubble_style_values"
|
||||
android:key="BUBBLE_STYLE_KEY"
|
||||
android:summary="%s"
|
||||
android:title="@string/bubble_style"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorPreference
|
||||
android:dialogTitle="@string/font_size"
|
||||
android:key="SETTINGS_INTERFACE_TEXT_SIZE_KEY"
|
||||
|