forked from GitHub-Mirror/riotX-android
Use addLinks method on message. Next step is to proceed the url to navigate.
This commit is contained in:
parent
58f60eaab4
commit
fdd4642cbb
@ -23,8 +23,9 @@ import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventCo
|
|||||||
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
import kotlinx.android.synthetic.main.fragment_room_detail.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class RoomDetailFragment : RiotFragment() {
|
class RoomDetailFragment : RiotFragment(), TimelineEventController.Callback {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ class RoomDetailFragment : RiotFragment() {
|
|||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
timelineEventController.addModelBuildListener { it.dispatchTo(scrollOnNewMessageCallback) }
|
timelineEventController.addModelBuildListener { it.dispatchTo(scrollOnNewMessageCallback) }
|
||||||
recyclerView.setController(timelineEventController)
|
recyclerView.setController(timelineEventController)
|
||||||
|
timelineEventController.callback = this
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderRoomSummary(roomSummary: RoomSummary?) {
|
private fun renderRoomSummary(roomSummary: RoomSummary?) {
|
||||||
@ -100,4 +102,10 @@ class RoomDetailFragment : RiotFragment() {
|
|||||||
timelineEventController.timeline = events
|
timelineEventController.timeline = events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TimelineEventController.Callback ************************************************************
|
||||||
|
|
||||||
|
override fun onUrlClicked(url: String) {
|
||||||
|
Timber.v("Url clicked: $url")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package im.vector.riotredesign.features.home.room.detail.timeline
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import im.vector.matrix.android.api.permalinks.MatrixURLSpan
|
||||||
|
import im.vector.matrix.android.api.permalinks.MatrixUrlLinkify
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.epoxy.KotlinModel
|
import im.vector.riotredesign.core.epoxy.KotlinModel
|
||||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||||
@ -12,7 +14,8 @@ data class MessageItem(
|
|||||||
val time: CharSequence? = null,
|
val time: CharSequence? = null,
|
||||||
val avatarUrl: String?,
|
val avatarUrl: String?,
|
||||||
val memberName: CharSequence? = null,
|
val memberName: CharSequence? = null,
|
||||||
val showInformation: Boolean = true
|
val showInformation: Boolean = true,
|
||||||
|
val onUrlClickedListener: ((url: String) -> Unit)? = null
|
||||||
) : KotlinModel(R.layout.item_event_message) {
|
) : KotlinModel(R.layout.item_event_message) {
|
||||||
|
|
||||||
private val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
private val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
|
||||||
@ -22,6 +25,11 @@ data class MessageItem(
|
|||||||
|
|
||||||
override fun bind() {
|
override fun bind() {
|
||||||
messageView.text = message
|
messageView.text = message
|
||||||
|
MatrixUrlLinkify.addLinks(messageView, object : MatrixURLSpan.Callback {
|
||||||
|
override fun onUrlClicked(url: String) {
|
||||||
|
onUrlClickedListener?.invoke(url)
|
||||||
|
}
|
||||||
|
})
|
||||||
if (showInformation) {
|
if (showInformation) {
|
||||||
avatarImageView.visibility = View.VISIBLE
|
avatarImageView.visibility = View.VISIBLE
|
||||||
memberNameView.visibility = View.VISIBLE
|
memberNameView.visibility = View.VISIBLE
|
||||||
|
@ -9,7 +9,13 @@ class MessageItemFactory(private val timelineDateFormatter: TimelineDateFormatte
|
|||||||
|
|
||||||
private val messagesDisplayedWithInformation = HashSet<String?>()
|
private val messagesDisplayedWithInformation = HashSet<String?>()
|
||||||
|
|
||||||
fun create(event: EnrichedEvent, nextEvent: EnrichedEvent?, addDaySeparator: Boolean, date: LocalDateTime): MessageItem? {
|
fun create(event: EnrichedEvent,
|
||||||
|
nextEvent: EnrichedEvent?,
|
||||||
|
addDaySeparator: Boolean,
|
||||||
|
date: LocalDateTime,
|
||||||
|
callback: TimelineEventController.Callback?
|
||||||
|
): MessageItem? {
|
||||||
|
|
||||||
val messageContent: MessageContent? = event.root.content.toModel()
|
val messageContent: MessageContent? = event.root.content.toModel()
|
||||||
val roomMember = event.roomMember
|
val roomMember = event.roomMember
|
||||||
if (messageContent == null || roomMember == null) {
|
if (messageContent == null || roomMember == null) {
|
||||||
@ -20,13 +26,13 @@ class MessageItemFactory(private val timelineDateFormatter: TimelineDateFormatte
|
|||||||
messagesDisplayedWithInformation.add(event.root.eventId)
|
messagesDisplayedWithInformation.add(event.root.eventId)
|
||||||
}
|
}
|
||||||
val showInformation = messagesDisplayedWithInformation.contains(event.root.eventId)
|
val showInformation = messagesDisplayedWithInformation.contains(event.root.eventId)
|
||||||
|
|
||||||
return MessageItem(
|
return MessageItem(
|
||||||
message = messageContent.body,
|
message = messageContent.body,
|
||||||
avatarUrl = roomMember.avatarUrl,
|
avatarUrl = roomMember.avatarUrl,
|
||||||
showInformation = showInformation,
|
showInformation = showInformation,
|
||||||
time = timelineDateFormatter.formatMessageHour(date),
|
time = timelineDateFormatter.formatMessageHour(date),
|
||||||
memberName = roomMember.displayName ?: event.root.sender
|
memberName = roomMember.displayName ?: event.root.sender,
|
||||||
|
onUrlClickedListener = { callback?.onUrlClicked(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,8 @@ class TimelineEventController(private val roomId: String,
|
|||||||
buildSnapshotList()
|
buildSnapshotList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var callback: Callback? = null
|
||||||
|
|
||||||
override fun buildModels() {
|
override fun buildModels() {
|
||||||
buildModels(snapshotList)
|
buildModels(snapshotList)
|
||||||
}
|
}
|
||||||
@ -61,8 +63,8 @@ class TimelineEventController(private val roomId: String,
|
|||||||
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
|
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
|
||||||
|
|
||||||
val item = when (event.root.type) {
|
val item = when (event.root.type) {
|
||||||
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, addDaySeparator, date)
|
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, addDaySeparator, date, callback)
|
||||||
else -> textItemFactory.create(event)
|
else -> textItemFactory.create(event)
|
||||||
}
|
}
|
||||||
item
|
item
|
||||||
?.onBind { timeline?.loadAround(index) }
|
?.onBind { timeline?.loadAround(index) }
|
||||||
@ -87,4 +89,8 @@ class TimelineEventController(private val roomId: String,
|
|||||||
requestModelBuild()
|
requestModelBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun onUrlClicked(url: String)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,34 +1,69 @@
|
|||||||
package im.vector.matrix.android.api.permalinks
|
package im.vector.matrix.android.api.permalinks
|
||||||
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableString
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.widget.TextView
|
||||||
|
|
||||||
object MatrixUrlLinkify {
|
object MatrixUrlLinkify {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the matrix spans i.e matrix id , user id ... to display them as URL.
|
* Find the matrix spans i.e matrix id , user id ... to display them as URL.
|
||||||
*
|
*
|
||||||
* @param spannableStringBuilder the text in which the matrix items has to be clickable.
|
* @param spannable the text in which the matrix items has to be clickable.
|
||||||
*/
|
*/
|
||||||
fun addLinks(spannableStringBuilder: SpannableStringBuilder, callback: MatrixURLSpan.Callback?) {
|
fun addLinks(spannable: Spannable?, callback: MatrixURLSpan.Callback?): Boolean {
|
||||||
// sanity checks
|
// sanity checks
|
||||||
if (spannableStringBuilder.isEmpty()) {
|
if (spannable.isNullOrEmpty()) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
val text = spannableStringBuilder.toString()
|
val text = spannable.toString()
|
||||||
|
var hasMatch = false
|
||||||
for (index in MatrixPatterns.MATRIX_PATTERNS.indices) {
|
for (index in MatrixPatterns.MATRIX_PATTERNS.indices) {
|
||||||
val pattern = MatrixPatterns.MATRIX_PATTERNS[index]
|
val pattern = MatrixPatterns.MATRIX_PATTERNS[index]
|
||||||
val matcher = pattern.matcher(spannableStringBuilder)
|
val matcher = pattern.matcher(spannable)
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
|
hasMatch = true
|
||||||
val startPos = matcher.start(0)
|
val startPos = matcher.start(0)
|
||||||
if (startPos == 0 || text[startPos - 1] != '/') {
|
if (startPos == 0 || text[startPos - 1] != '/') {
|
||||||
val endPos = matcher.end(0)
|
val endPos = matcher.end(0)
|
||||||
val url = text.substring(matcher.start(0), matcher.end(0))
|
val url = text.substring(matcher.start(0), matcher.end(0))
|
||||||
val span = MatrixURLSpan(url, callback)
|
val span = MatrixURLSpan(url, callback)
|
||||||
spannableStringBuilder.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
spannable.setSpan(span, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return hasMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addLinks(textView: TextView, callback: MatrixURLSpan.Callback?): Boolean {
|
||||||
|
val text = textView.text
|
||||||
|
if (text is Spannable) {
|
||||||
|
if (addLinks(text, callback)) {
|
||||||
|
addLinkMovementMethod(textView)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
val spannableString = SpannableString.valueOf(text)
|
||||||
|
if (addLinks(spannableString, callback)) {
|
||||||
|
addLinkMovementMethod(textView)
|
||||||
|
textView.text = spannableString
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun addLinkMovementMethod(textView: TextView) {
|
||||||
|
val movementMethod = textView.movementMethod
|
||||||
|
if (movementMethod == null || movementMethod !is LinkMovementMethod) {
|
||||||
|
if (textView.linksClickable) {
|
||||||
|
textView.movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user