Pills: simplify and improve the algorithm

This commit is contained in:
Benoit Marty 2019-11-28 21:54:37 +01:00
parent f11cd47df3
commit 4b273e8746

View file

@ -16,6 +16,7 @@
package im.vector.riotx.features.home.room.detail package im.vector.riotx.features.home.room.detail
import android.annotation.SuppressLint
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
@ -27,7 +28,6 @@ import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.text.Editable import android.text.Editable
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder
import android.view.* import android.view.*
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.TextView import android.widget.TextView
@ -37,6 +37,7 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.text.buildSpannedString
import androidx.core.util.Pair import androidx.core.util.Pair
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.forEach import androidx.core.view.forEach
@ -60,7 +61,6 @@ import im.vector.matrix.android.api.session.content.ContentAttachmentData
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.LocalEcho import im.vector.matrix.android.api.session.events.model.LocalEcho
import im.vector.matrix.android.api.session.room.model.Membership import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
@ -159,7 +159,7 @@ class RoomDetailFragment @Inject constructor(
companion object { companion object {
/**x /**
* Sanitize the display name. * Sanitize the display name.
* *
* @param displayName the display name to sanitize * @param displayName the display name to sanitize
@ -406,8 +406,12 @@ class RoomDetailFragment @Inject constructor(
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes)) composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
composerLayout.sendButton.setContentDescription(getString(descriptionRes)) composerLayout.sendButton.setContentDescription(getString(descriptionRes))
avatarRenderer.render(event.senderAvatar, event.root.senderId avatarRenderer.render(
?: "", event.getDisambiguatedDisplayName(), composerLayout.composerRelatedMessageAvatar) event.senderAvatar,
event.root.senderId ?: "",
event.getDisambiguatedDisplayName(),
composerLayout.composerRelatedMessageAvatar
)
composerLayout.expand { composerLayout.expand {
// need to do it here also when not using quick reply // need to do it here also when not using quick reply
focusComposerAndShowKeyboard() focusComposerAndShowKeyboard()
@ -420,8 +424,7 @@ class RoomDetailFragment @Inject constructor(
if (text != composerLayout.composerEditText.text.toString()) { if (text != composerLayout.composerEditText.text.toString()) {
// Ignore update to avoid saving a draft // Ignore update to avoid saving a draft
composerLayout.composerEditText.setText(text) composerLayout.composerEditText.setText(text)
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length ?: 0)
?: 0)
} }
} }
@ -591,8 +594,13 @@ class RoomDetailFragment @Inject constructor(
// Add the span // Add the span
val user = session.getUser(item.userId) val user = session.getUser(item.userId)
val span = PillImageSpan(glideRequests, avatarRenderer, requireContext(), item.userId, user?.displayName val span = PillImageSpan(
?: item.userId, user?.avatarUrl) glideRequests,
avatarRenderer,
requireContext(),
item.userId,
user?.displayName ?: item.userId,
user?.avatarUrl)
span.bind(composerLayout.composerEditText) span.bind(composerLayout.composerEditText)
editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) editable.setSpan(span, startIndex, startIndex + displayName.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
@ -980,10 +988,7 @@ class RoomDetailFragment @Inject constructor(
} }
override fun onMemberNameClicked(informationData: MessageInformationData) { override fun onMemberNameClicked(informationData: MessageInformationData) {
val userId = informationData.senderId insertUserDisplayNameInTextEditor(informationData.senderId)
roomDetailViewModel.getMember(userId)?.let {
insertUserDisplayNameInTextEditor(userId, it)
}
} }
override fun onClickOnReactionPill(informationData: MessageInformationData, reaction: String, on: Boolean) { override fun onClickOnReactionPill(informationData: MessageInformationData, reaction: String, on: Boolean) {
@ -1165,78 +1170,57 @@ class RoomDetailFragment @Inject constructor(
} }
} }
// utils
/** /**
* Insert an user displayname in the message editor. * Insert a user displayName in the message editor.
* *
* @param text the text to insert. * @param userId the userId.
*/ */
// TODO legacy, refactor @SuppressLint("SetTextI18n")
private fun insertUserDisplayNameInTextEditor(userId: String, memberInfo: RoomMember) { private fun insertUserDisplayNameInTextEditor(userId: String) {
// TODO move logic outside of fragment val startToCompose = composerLayout.composerEditText.text.isNullOrBlank()
val text = memberInfo.displayName
if (null != text) {
// var vibrate = false
val myDisplayName = session.getUser(session.myUserId)?.displayName if (startToCompose
if (myDisplayName == text) { && userId == session.myUserId) {
// current user // Empty composer, current user: start an emote
if (composerLayout.composerEditText.text.isNullOrBlank()) { composerLayout.composerEditText.setText(Command.EMOTE.command + " ")
composerLayout.composerEditText.append(Command.EMOTE.command + " ") composerLayout.composerEditText.setSelection(Command.EMOTE.command.length + 1)
composerLayout.composerEditText.setSelection(composerLayout.composerEditText.text?.length
?: 0)
// vibrate = true
}
} else { } else {
// another user val roomMember = roomDetailViewModel.getMember(userId)
val sanitizeDisplayName = sanitizeDisplayName(text) // TODO move logic outside of fragment
if (composerLayout.composerEditText.text.isNullOrBlank()) { (roomMember?.displayName ?: userId)
.let { sanitizeDisplayName(it) }
.let { displayName ->
buildSpannedString {
append(displayName)
setSpan(
PillImageSpan(
glideRequests,
avatarRenderer,
requireContext(),
userId,
displayName,
roomMember?.avatarUrl),
0,
displayName.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(if (startToCompose) ": " else " ")
}.let { pill ->
if (startToCompose) {
if (displayName.startsWith("/")) {
// Ensure displayName will not be interpreted as a Slash command // Ensure displayName will not be interpreted as a Slash command
if (text.startsWith("/")) {
composerLayout.composerEditText.append("\\") composerLayout.composerEditText.append("\\")
} }
SpannableStringBuilder().apply { composerLayout.composerEditText.append(pill)
append(sanitizeDisplayName)
setSpan(
PillImageSpan(glideRequests, avatarRenderer, requireContext(), userId, memberInfo.displayName
?: userId, memberInfo.avatarUrl),
0,
sanitizeDisplayName.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(": ")
}.let {
composerLayout.composerEditText.append(it)
}
} else { } else {
SpannableStringBuilder().apply { composerLayout.composerEditText.text?.insert(composerLayout.composerEditText.selectionStart, pill)
append(sanitizeDisplayName) }
setSpan( }
PillImageSpan(glideRequests, avatarRenderer, requireContext(), userId, memberInfo.displayName
?: userId, memberInfo.avatarUrl),
0,
sanitizeDisplayName.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
append(" ")
}.let {
composerLayout.composerEditText.text?.insert(composerLayout.composerEditText.selectionStart, it)
} }
// composerLayout.composerEditText.text?.insert(composerLayout.composerEditText.selectionStart, sanitizeDisplayName + " ")
} }
// vibrate = true
}
// if (vibrate && vectorPreferences.vibrateWhenMentioning()) {
// val v= context.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
// if (v?.hasVibrator() == true) {
// v.vibrate(100)
// }
// }
focusComposerAndShowKeyboard() focusComposerAndShowKeyboard()
} }
}
private fun focusComposerAndShowKeyboard() { private fun focusComposerAndShowKeyboard() {
composerLayout.composerEditText.requestFocus() composerLayout.composerEditText.requestFocus()