Merge pull request #774 from vector-im/feature/breadcrumbs_fixes

Fix various UI issues
This commit is contained in:
Benoit Marty 2019-12-16 15:00:21 +01:00 committed by GitHub
commit f14f1db0e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 409 additions and 259 deletions

View file

@ -8,10 +8,12 @@ Improvements 🙌:
- -
Other changes: Other changes:
- - Use same default room colors than Riot-Web
Bugfix 🐛: Bugfix 🐛:
- - Scroll breadcrumbs to top when opened
- Render default room name when it starts with an emoji (#477)
- Do not display " (IRC)") in display names https://github.com/vector-im/riot-android/issues/444
Translations 🗣: Translations 🗣:
- -

View file

@ -10,7 +10,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.3.2' classpath 'com.google.gms:google-services:4.3.2'
classpath "com.airbnb.okreplay:gradle-plugin:1.5.0" classpath "com.airbnb.okreplay:gradle-plugin:1.5.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

View file

@ -16,11 +16,12 @@
package im.vector.matrix.android.api.session.room.send package im.vector.matrix.android.api.session.room.send
import im.vector.matrix.android.api.util.MatrixItem
/** /**
* Tag class for spans that should mention a user. * Tag class for spans that should mention a user.
* These Spans will be transformed into pills when detected in message to send * These Spans will be transformed into pills when detected in message to send
*/ */
interface UserMentionSpan { interface UserMentionSpan {
val displayName: String val matrixItem: MatrixItem
val userId: String
} }

View file

@ -0,0 +1,141 @@
/*
* Copyright 2019 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.matrix.android.api.util
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.session.user.model.User
import java.util.*
sealed class MatrixItem(
open val id: String,
open val displayName: String?,
open val avatarUrl: String?
) {
data class UserItem(override val id: String,
override val displayName: String? = null,
override val avatarUrl: String? = null)
: MatrixItem(id, displayName?.removeSuffix(ircPattern), avatarUrl) {
init {
if (BuildConfig.DEBUG) checkId()
}
}
data class EventItem(override val id: String,
override val displayName: String? = null,
override val avatarUrl: String? = null)
: MatrixItem(id, displayName, avatarUrl) {
init {
if (BuildConfig.DEBUG) checkId()
}
}
data class RoomItem(override val id: String,
override val displayName: String? = null,
override val avatarUrl: String? = null)
: MatrixItem(id, displayName, avatarUrl) {
init {
if (BuildConfig.DEBUG) checkId()
}
}
data class RoomAliasItem(override val id: String,
override val displayName: String? = null,
override val avatarUrl: String? = null)
: MatrixItem(id, displayName, avatarUrl) {
init {
if (BuildConfig.DEBUG) checkId()
}
}
data class GroupItem(override val id: String,
override val displayName: String? = null,
override val avatarUrl: String? = null)
: MatrixItem(id, displayName, avatarUrl) {
init {
if (BuildConfig.DEBUG) checkId()
}
}
fun getBestName(): String {
return displayName?.takeIf { it.isNotBlank() } ?: id
}
protected fun checkId() {
if (!id.startsWith(getIdPrefix())) {
error("Wrong usage of MatrixItem: check the id $id should start with ${getIdPrefix()}")
}
}
/**
* Return the prefix as defined in the matrix spec (and not extracted from the id)
*/
fun getIdPrefix() = when (this) {
is UserItem -> '@'
is EventItem -> '$'
is RoomItem -> '!'
is RoomAliasItem -> '#'
is GroupItem -> '+'
}
fun firstLetterOfDisplayName(): String {
return getBestName()
.let { dn ->
var startIndex = 0
val initial = dn[startIndex]
if (initial in listOf('@', '#', '+') && dn.length > 1) {
startIndex++
}
var length = 1
var first = dn[startIndex]
// LEFT-TO-RIGHT MARK
if (dn.length >= 2 && 0x200e == first.toInt()) {
startIndex++
first = dn[startIndex]
}
// check if its the start of a surrogate pair
if (first.toInt() in 0xD800..0xDBFF && dn.length > startIndex + 1) {
val second = dn[startIndex + 1]
if (second.toInt() in 0xDC00..0xDFFF) {
length++
}
}
dn.substring(startIndex, startIndex + length)
}
.toUpperCase(Locale.ROOT)
}
companion object {
private const val ircPattern = " (IRC)"
}
}
/* ==========================================================================================
* Extensions to create MatrixItem
* ========================================================================================== */
fun User.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)
fun GroupSummary.toMatrixItem() = MatrixItem.GroupItem(groupId, displayName, avatarUrl)
fun RoomSummary.toMatrixItem() = MatrixItem.RoomItem(roomId, displayName, avatarUrl)
fun PublicRoom.toMatrixItem() = MatrixItem.RoomItem(roomId, name, avatarUrl)

View file

@ -65,7 +65,7 @@ internal class TextPillsUtils @Inject constructor(
// append text before pill // append text before pill
append(text, currIndex, start) append(text, currIndex, start)
// append the pill // append the pill
append(String.format(template, urlSpan.userId, urlSpan.displayName)) append(String.format(template, urlSpan.matrixItem.id, urlSpan.matrixItem.displayName))
currIndex = end currIndex = end
} }
// append text after the last pill // append text after the last pill

View file

@ -16,9 +16,7 @@
package im.vector.matrix.android.internal.util package im.vector.matrix.android.internal.util
import im.vector.matrix.android.api.MatrixPatterns
import timber.log.Timber import timber.log.Timber
import java.util.Locale
/** /**
* Convert a string to an UTF8 String * Convert a string to an UTF8 String
@ -51,10 +49,3 @@ fun convertFromUTF8(s: String): String {
s s
} }
} }
fun String?.firstLetterOfDisplayName(): String {
if (this.isNullOrEmpty()) return ""
val isUserId = MatrixPatterns.isUserId(this)
val firstLetterIndex = if (isUserId) 1 else 0
return this[firstLetterIndex].toString().toUpperCase(Locale.ROOT)
}

View file

@ -21,6 +21,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -37,11 +38,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
@EpoxyAttribute @EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute @EpoxyAttribute
lateinit var avatarUrl: String lateinit var matrixItem: MatrixItem
@EpoxyAttribute
lateinit var senderId: String
@EpoxyAttribute
var senderName: String? = null
@EpoxyAttribute @EpoxyAttribute
lateinit var body: CharSequence lateinit var body: CharSequence
@EpoxyAttribute @EpoxyAttribute
@ -50,8 +47,8 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
var movementMethod: MovementMethod? = null var movementMethod: MovementMethod? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
avatarRenderer.render(avatarUrl, senderId, senderName, holder.avatar) avatarRenderer.render(matrixItem, holder.avatar)
holder.sender.setTextOrHide(senderName) holder.sender.setTextOrHide(matrixItem.displayName)
holder.body.movementMethod = movementMethod holder.body.movementMethod = movementMethod
holder.body.text = body holder.body.text = body
body.findPillsAndProcess { it.bind(holder.body) } body.findPillsAndProcess { it.bind(holder.body) }

View file

@ -21,6 +21,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -36,16 +37,12 @@ abstract class BottomSheetRoomPreviewItem : VectorEpoxyModel<BottomSheetRoomPrev
@EpoxyAttribute @EpoxyAttribute
lateinit var avatarRenderer: AvatarRenderer lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute @EpoxyAttribute
lateinit var avatarUrl: String lateinit var matrixItem: MatrixItem
@EpoxyAttribute
lateinit var roomId: String
@EpoxyAttribute
var roomName: String? = null
@EpoxyAttribute var settingsClickListener: View.OnClickListener? = null @EpoxyAttribute var settingsClickListener: View.OnClickListener? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
avatarRenderer.render(avatarUrl, roomId, roomName, holder.avatar) avatarRenderer.render(matrixItem, holder.avatar)
holder.roomName.setTextOrHide(roomName) holder.roomName.setTextOrHide(matrixItem.displayName)
holder.roomSettings.setOnClickListener(settingsClickListener) holder.roomSettings.setOnClickListener(settingsClickListener)
} }

View file

@ -23,6 +23,8 @@ import android.widget.ProgressBar
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.vectorComponent import im.vector.riotx.core.extensions.vectorComponent
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -59,9 +61,9 @@ open class UserAvatarPreference : Preference {
val session = mSession ?: return val session = mSession ?: return
val view = mAvatarView ?: return val view = mAvatarView ?: return
session.getUser(session.myUserId)?.let { session.getUser(session.myUserId)?.let {
avatarRenderer.render(it, view) avatarRenderer.render(it.toMatrixItem(), view)
} ?: run { } ?: run {
avatarRenderer.render(null, session.myUserId, null, view) avatarRenderer.render(MatrixItem.UserItem(session.myUserId), view)
} }
} }

View file

@ -26,6 +26,7 @@ import im.vector.riotx.R
import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideApp
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
import im.vector.riotx.features.home.room.detail.timeline.item.toMatrixItem
import kotlinx.android.synthetic.main.view_read_receipts.view.* import kotlinx.android.synthetic.main.view_read_receipts.view.*
private const val MAX_RECEIPT_DISPLAYED = 5 private const val MAX_RECEIPT_DISPLAYED = 5
@ -59,7 +60,7 @@ class ReadReceiptsView @JvmOverloads constructor(
receiptAvatars[index].visibility = View.INVISIBLE receiptAvatars[index].visibility = View.INVISIBLE
} else { } else {
receiptAvatars[index].visibility = View.VISIBLE receiptAvatars[index].visibility = View.VISIBLE
avatarRenderer.render(receiptData.avatarUrl, receiptData.userId, receiptData.displayName, receiptAvatars[index]) avatarRenderer.render(receiptData.toMatrixItem(), receiptAvatars[index])
} }
} }

View file

@ -18,11 +18,12 @@ package im.vector.riotx.features.autocomplete.user
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.features.autocomplete.AutocompleteClickListener import im.vector.riotx.features.autocomplete.AutocompleteClickListener
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject import javax.inject.Inject
class AutocompleteUserController @Inject constructor(): TypedEpoxyController<List<User>>() { class AutocompleteUserController @Inject constructor() : TypedEpoxyController<List<User>>() {
var listener: AutocompleteClickListener<User>? = null var listener: AutocompleteClickListener<User>? = null
@ -35,9 +36,7 @@ class AutocompleteUserController @Inject constructor(): TypedEpoxyController<Lis
data.forEach { user -> data.forEach { user ->
autocompleteUserItem { autocompleteUserItem {
id(user.userId) id(user.userId)
userId(user.userId) matrixItem(user.toMatrixItem())
name(user.displayName)
avatarUrl(user.avatarUrl)
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
clickListener { _ -> clickListener { _ ->
listener?.onItemClick(user) listener?.onItemClick(user)

View file

@ -21,6 +21,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -30,15 +31,13 @@ import im.vector.riotx.features.home.AvatarRenderer
abstract class AutocompleteUserItem : VectorEpoxyModel<AutocompleteUserItem.Holder>() { abstract class AutocompleteUserItem : VectorEpoxyModel<AutocompleteUserItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var name: String? = null @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var userId: String = ""
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null @EpoxyAttribute var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.view.setOnClickListener(clickListener) holder.view.setOnClickListener(clickListener)
holder.nameView.text = name holder.nameView.text = matrixItem.getBestName()
avatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -22,6 +22,8 @@ import androidx.lifecycle.Observer
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction import im.vector.matrix.android.api.session.crypto.sas.IncomingSasVerificationTransaction
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseFragment import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -57,10 +59,10 @@ class SASVerificationIncomingFragment @Inject constructor(
otherDeviceTextView.text = viewModel.otherDeviceId otherDeviceTextView.text = viewModel.otherDeviceId
viewModel.otherUser?.let { viewModel.otherUser?.let {
avatarRenderer.render(it, avatarImageView) avatarRenderer.render(it.toMatrixItem(), avatarImageView)
} ?: run { } ?: run {
// Fallback to what we know // Fallback to what we know
avatarRenderer.render(null, viewModel.otherUserId ?: "", viewModel.otherUserId, avatarImageView) avatarRenderer.render(MatrixItem.UserItem(viewModel.otherUserId ?: "", viewModel.otherUserId), avatarImageView)
} }
viewModel.transactionState.observe(viewLifecycleOwner, Observer { viewModel.transactionState.observe(viewLifecycleOwner, Observer {

View file

@ -27,10 +27,7 @@ import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.DrawableImageViewTarget import com.bumptech.glide.request.target.DrawableImageViewTarget
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.util.MatrixItem
import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.internal.util.firstLetterOfDisplayName
import im.vector.riotx.R
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideApp
import im.vector.riotx.core.glide.GlideRequest import im.vector.riotx.core.glide.GlideRequest
@ -45,76 +42,42 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
companion object { companion object {
private const val THUMBNAIL_SIZE = 250 private const val THUMBNAIL_SIZE = 250
private val AVATAR_COLOR_LIST = listOf(
R.color.riotx_avatar_fill_1,
R.color.riotx_avatar_fill_2,
R.color.riotx_avatar_fill_3
)
} }
@UiThread @UiThread
fun render(roomSummary: RoomSummary, imageView: ImageView) { fun render(matrixItem: MatrixItem, imageView: ImageView) {
render(roomSummary.avatarUrl, roomSummary.roomId, roomSummary.displayName, imageView) render(imageView.context,
} GlideApp.with(imageView),
matrixItem,
@UiThread DrawableImageViewTarget(imageView))
fun render(user: User, imageView: ImageView) {
render(imageView.context, GlideApp.with(imageView), user.avatarUrl, user.userId, user.displayName, DrawableImageViewTarget(imageView))
}
@UiThread
fun render(avatarUrl: String?, identifier: String, name: String?, imageView: ImageView) {
render(imageView.context, GlideApp.with(imageView), avatarUrl, identifier, name, DrawableImageViewTarget(imageView))
} }
@UiThread @UiThread
fun render(context: Context, fun render(context: Context,
glideRequest: GlideRequests, glideRequest: GlideRequests,
avatarUrl: String?, matrixItem: MatrixItem,
identifier: String,
name: String?,
target: Target<Drawable>) { target: Target<Drawable>) {
val displayName = if (name.isNullOrBlank()) { val placeholder = getPlaceholderDrawable(context, matrixItem)
identifier buildGlideRequest(glideRequest, matrixItem.avatarUrl)
} else {
name
}
val placeholder = getPlaceholderDrawable(context, identifier, displayName)
buildGlideRequest(glideRequest, avatarUrl)
.placeholder(placeholder) .placeholder(placeholder)
.into(target) .into(target)
} }
@AnyThread @AnyThread
fun getPlaceholderDrawable(context: Context, identifier: String, text: String): Drawable { fun getPlaceholderDrawable(context: Context, matrixItem: MatrixItem): Drawable {
val avatarColor = ContextCompat.getColor(context, getColorFromUserId(identifier)) val avatarColor = when (matrixItem) {
return if (text.isEmpty()) { is MatrixItem.UserItem -> ContextCompat.getColor(context, getColorFromUserId(matrixItem.id))
TextDrawable.builder().buildRound("", avatarColor) else -> ContextCompat.getColor(context, getColorFromRoomId(matrixItem.id))
} else {
val firstLetter = text.firstLetterOfDisplayName()
TextDrawable.builder()
.beginConfig()
.bold()
.endConfig()
.buildRound(firstLetter, avatarColor)
} }
return TextDrawable.builder()
.beginConfig()
.bold()
.endConfig()
.buildRound(matrixItem.firstLetterOfDisplayName(), avatarColor)
} }
// PRIVATE API ********************************************************************************* // PRIVATE API *********************************************************************************
// private fun getAvatarColor(text: String? = null): Int {
// var colorIndex: Long = 0
// if (!text.isNullOrEmpty()) {
// var sum: Long = 0
// for (i in 0 until text.length) {
// sum += text[i].toLong()
// }
// colorIndex = sum % AVATAR_COLOR_LIST.size
// }
// return AVATAR_COLOR_LIST[colorIndex.toInt()]
// }
private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> { private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> {
val resolvedUrl = activeSessionHolder.getActiveSession().contentUrlResolver() val resolvedUrl = activeSessionHolder.getActiveSession().contentUrlResolver()
.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE) .resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)

View file

@ -27,6 +27,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationItemView
import com.google.android.material.bottomnavigation.BottomNavigationMenuView import com.google.android.material.bottomnavigation.BottomNavigationMenuView
import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState import im.vector.matrix.android.api.session.crypto.keysbackup.KeysBackupState
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.commitTransactionNow import im.vector.riotx.core.extensions.commitTransactionNow
import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.ToolbarConfigurable
@ -74,12 +75,7 @@ class HomeDetailFragment @Inject constructor(
private fun onGroupChange(groupSummary: GroupSummary?) { private fun onGroupChange(groupSummary: GroupSummary?) {
groupSummary?.let { groupSummary?.let {
avatarRenderer.render( avatarRenderer.render(it.toMatrixItem(), groupToolbarAvatarImageView)
it.avatarUrl,
it.groupId,
it.displayName,
groupToolbarAvatarImageView
)
} }
} }
@ -155,7 +151,7 @@ class HomeDetailFragment @Inject constructor(
bottomNavigationView.selectedItemId = when (displayMode) { bottomNavigationView.selectedItemId = when (displayMode) {
RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people RoomListDisplayMode.PEOPLE -> R.id.bottom_action_people
RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms RoomListDisplayMode.ROOMS -> R.id.bottom_action_rooms
else -> R.id.bottom_action_home else -> R.id.bottom_action_home
} }
} }

View file

@ -19,6 +19,7 @@ package im.vector.riotx.features.home
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.observeK import im.vector.riotx.core.extensions.observeK
import im.vector.riotx.core.extensions.replaceChildFragment import im.vector.riotx.core.extensions.replaceChildFragment
@ -42,7 +43,7 @@ class HomeDrawerFragment @Inject constructor(
session.liveUser(session.myUserId).observeK(this) { optionalUser -> session.liveUser(session.myUserId).observeK(this) { optionalUser ->
val user = optionalUser?.getOrNull() val user = optionalUser?.getOrNull()
if (user != null) { if (user != null) {
avatarRenderer.render(user.avatarUrl, user.userId, user.displayName, homeDrawerHeaderAvatarView) avatarRenderer.render(user.toMatrixItem(), homeDrawerHeaderAvatarView)
homeDrawerUsernameView.text = user.displayName homeDrawerUsernameView.text = user.displayName
homeDrawerUserIdView.text = user.userId homeDrawerUserIdView.text = user.userId
} }

View file

@ -0,0 +1,29 @@
/*
* Copyright 2019 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.riotx.features.home
import androidx.annotation.ColorRes
import im.vector.riotx.R
@ColorRes
fun getColorFromRoomId(roomId: String?): Int {
return when ((roomId?.toList()?.sumBy { it.toInt() } ?: 0) % 3) {
1 -> R.color.riotx_avatar_fill_2
2 -> R.color.riotx_avatar_fill_3
else -> R.color.riotx_avatar_fill_1
}
}

View file

@ -22,28 +22,18 @@ import kotlin.math.abs
@ColorRes @ColorRes
fun getColorFromUserId(userId: String?): Int { fun getColorFromUserId(userId: String?): Int {
if (userId.isNullOrBlank()) {
return R.color.riotx_username_1
}
var hash = 0 var hash = 0
var i = 0
var chr: Char
while (i < userId.length) { userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.toInt() }
chr = userId[i]
hash = (hash shl 5) - hash + chr.toInt()
i++
}
return when (abs(hash) % 8 + 1) { return when (abs(hash) % 8) {
1 -> R.color.riotx_username_1 1 -> R.color.riotx_username_2
2 -> R.color.riotx_username_2 2 -> R.color.riotx_username_3
3 -> R.color.riotx_username_3 3 -> R.color.riotx_username_4
4 -> R.color.riotx_username_4 4 -> R.color.riotx_username_5
5 -> R.color.riotx_username_5 5 -> R.color.riotx_username_6
6 -> R.color.riotx_username_6 6 -> R.color.riotx_username_7
7 -> R.color.riotx_username_7 7 -> R.color.riotx_username_8
else -> R.color.riotx_username_8 else -> R.color.riotx_username_1
} }
} }

View file

@ -25,6 +25,7 @@ import androidx.core.content.ContextCompat
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -34,22 +35,20 @@ import im.vector.riotx.features.home.AvatarRenderer
abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() { abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute var name: String? = null @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var userId: String = ""
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null @EpoxyAttribute var clickListener: View.OnClickListener? = null
@EpoxyAttribute var selected: Boolean = false @EpoxyAttribute var selected: Boolean = false
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.view.setOnClickListener(clickListener) holder.view.setOnClickListener(clickListener)
// If name is empty, use userId as name and force it being centered // If name is empty, use userId as name and force it being centered
if (name.isNullOrEmpty()) { if (matrixItem.displayName.isNullOrEmpty()) {
holder.userIdView.visibility = View.GONE holder.userIdView.visibility = View.GONE
holder.nameView.text = userId holder.nameView.text = matrixItem.id
} else { } else {
holder.userIdView.visibility = View.VISIBLE holder.userIdView.visibility = View.VISIBLE
holder.nameView.text = name holder.nameView.text = matrixItem.displayName
holder.userIdView.text = userId holder.userIdView.text = matrixItem.id
} }
renderSelection(holder, selected) renderSelection(holder, selected)
} }
@ -62,7 +61,7 @@ abstract class CreateDirectRoomUserItem : VectorEpoxyModel<CreateDirectRoomUserI
holder.avatarImageView.setImageDrawable(backgroundDrawable) holder.avatarImageView.setImageDrawable(backgroundDrawable)
} else { } else {
holder.avatarCheckedImageView.visibility = View.GONE holder.avatarCheckedImageView.visibility = View.GONE
avatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
} }
} }

View file

@ -30,7 +30,7 @@ import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.internal.util.firstLetterOfDisplayName import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.extensions.postLiveEvent
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
@ -142,7 +142,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
session.rx() session.rx()
.searchUsersDirectory(search, 50, emptySet()) .searchUsersDirectory(search, 50, emptySet())
.map { users -> .map { users ->
users.sortedBy { it.displayName.firstLetterOfDisplayName() } users.sortedBy { it.toMatrixItem().firstLetterOfDisplayName() }
} }
} }
stream.toAsync { stream.toAsync {

View file

@ -19,9 +19,13 @@
package im.vector.riotx.features.home.createdirect package im.vector.riotx.features.home.createdirect
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.* import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.errorWithRetryItem import im.vector.riotx.core.epoxy.errorWithRetryItem
import im.vector.riotx.core.epoxy.loadingItem import im.vector.riotx.core.epoxy.loadingItem
@ -94,9 +98,7 @@ class DirectoryUsersController @Inject constructor(private val session: Session,
createDirectRoomUserItem { createDirectRoomUserItem {
id(user.userId) id(user.userId)
selected(isSelected) selected(isSelected)
userId(user.userId) matrixItem(user.toMatrixItem())
name(user.displayName)
avatarUrl(user.avatarUrl)
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
clickListener { _ -> clickListener { _ ->
callback?.onItemClick(user) callback?.onItemClick(user)

View file

@ -23,7 +23,7 @@ import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.internal.util.firstLetterOfDisplayName import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.EmptyItem_ import im.vector.riotx.core.epoxy.EmptyItem_
import im.vector.riotx.core.epoxy.loadingItem import im.vector.riotx.core.epoxy.loadingItem
@ -68,9 +68,7 @@ class KnownUsersController @Inject constructor(private val session: Session,
CreateDirectRoomUserItem_() CreateDirectRoomUserItem_()
.id(item.userId) .id(item.userId)
.selected(isSelected) .selected(isSelected)
.userId(item.userId) .matrixItem(item.toMatrixItem())
.name(item.displayName)
.avatarUrl(item.avatarUrl)
.avatarRenderer(avatarRenderer) .avatarRenderer(avatarRenderer)
.clickListener { _ -> .clickListener { _ ->
callback?.onItemClick(item) callback?.onItemClick(item)
@ -87,8 +85,8 @@ class KnownUsersController @Inject constructor(private val session: Session,
var lastFirstLetter: String? = null var lastFirstLetter: String? = null
for (model in models) { for (model in models) {
if (model is CreateDirectRoomUserItem) { if (model is CreateDirectRoomUserItem) {
if (model.userId == session.myUserId) continue if (model.matrixItem.id == session.myUserId) continue
val currentFirstLetter = model.name.firstLetterOfDisplayName() val currentFirstLetter = model.matrixItem.firstLetterOfDisplayName()
val showLetter = !isFiltering && currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter val showLetter = !isFiltering && currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter
lastFirstLetter = currentFirstLetter lastFirstLetter = currentFirstLetter

View file

@ -36,7 +36,7 @@ import im.vector.riotx.core.utils.LiveEvent
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.functions.BiFunction import io.reactivex.functions.BiFunction
const val ALL_COMMUNITIES_GROUP_ID = "ALL_COMMUNITIES_GROUP_ID" const val ALL_COMMUNITIES_GROUP_ID = "+ALL_COMMUNITIES_GROUP_ID"
class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState, class GroupListViewModel @AssistedInject constructor(@Assisted initialState: GroupListViewState,
private val selectedGroupStore: SelectedGroupDataSource, private val selectedGroupStore: SelectedGroupDataSource,

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.group
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.group.model.GroupSummary
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject import javax.inject.Inject
@ -49,10 +50,8 @@ class GroupSummaryController @Inject constructor(private val avatarRenderer: Ava
groupSummaryItem { groupSummaryItem {
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
id(groupSummary.groupId) id(groupSummary.groupId)
groupId(groupSummary.groupId) matrixItem(groupSummary.toMatrixItem())
groupName(groupSummary.displayName)
selected(isSelected) selected(isSelected)
avatarUrl(groupSummary.avatarUrl)
listener { callback?.onGroupSelected(groupSummary) } listener { callback?.onGroupSelected(groupSummary) }
} }
} }

View file

@ -20,6 +20,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -30,18 +31,16 @@ import im.vector.riotx.features.home.AvatarRenderer
abstract class GroupSummaryItem : VectorEpoxyModel<GroupSummaryItem.Holder>() { abstract class GroupSummaryItem : VectorEpoxyModel<GroupSummaryItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var groupName: CharSequence @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute lateinit var groupId: String
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var selected: Boolean = false @EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute var listener: (() -> Unit)? = null @EpoxyAttribute var listener: (() -> Unit)? = null
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.rootView.setOnClickListener { listener?.invoke() } holder.rootView.setOnClickListener { listener?.invoke() }
holder.groupNameView.text = groupName holder.groupNameView.text = matrixItem.displayName
holder.rootView.isChecked = selected holder.rootView.isChecked = selected
avatarRenderer.render(avatarUrl, groupId, groupName.toString(), holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.breadcrumbs
import android.view.View import android.view.View
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.core.utils.DebouncedClickListener import im.vector.riotx.core.utils.DebouncedClickListener
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import javax.inject.Inject import javax.inject.Inject
@ -52,9 +53,7 @@ class BreadcrumbsController @Inject constructor(
breadcrumbsItem { breadcrumbsItem {
id(it.roomId) id(it.roomId)
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
roomId(it.roomId) matrixItem(it.toMatrixItem())
roomName(it.displayName)
avatarUrl(it.avatarUrl)
unreadNotificationCount(it.notificationCount) unreadNotificationCount(it.notificationCount)
showHighlighted(it.highlightCount > 0) showHighlighted(it.highlightCount > 0)
hasUnreadMessage(it.hasUnreadMessages) hasUnreadMessage(it.hasUnreadMessages)

View file

@ -67,4 +67,8 @@ class BreadcrumbsFragment @Inject constructor(
override fun onBreadcrumbClicked(roomId: String) { override fun onBreadcrumbClicked(roomId: String) {
sharedActionViewModel.post(RoomDetailSharedAction.SwitchToRoom(roomId)) sharedActionViewModel.post(RoomDetailSharedAction.SwitchToRoom(roomId))
} }
fun scrollToTop() {
breadcrumbsRecyclerView.scrollToPosition(0)
}
} }

View file

@ -22,6 +22,7 @@ import android.widget.ImageView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -32,9 +33,7 @@ import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView
abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() { abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var roomId: String @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute lateinit var roomName: CharSequence
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var unreadNotificationCount: Int = 0 @EpoxyAttribute var unreadNotificationCount: Int = 0
@EpoxyAttribute var showHighlighted: Boolean = false @EpoxyAttribute var showHighlighted: Boolean = false
@EpoxyAttribute var hasUnreadMessage: Boolean = false @EpoxyAttribute var hasUnreadMessage: Boolean = false
@ -45,7 +44,7 @@ abstract class BreadcrumbsItem : VectorEpoxyModel<BreadcrumbsItem.Holder>() {
super.bind(holder) super.bind(holder)
holder.rootView.setOnClickListener(itemClickListener) holder.rootView.setOnClickListener(itemClickListener)
holder.unreadIndentIndicator.isVisible = hasUnreadMessage holder.unreadIndentIndicator.isVisible = hasUnreadMessage
avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted)) holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.draftIndentIndicator.isVisible = hasDraft holder.draftIndentIndicator.isVisible = hasDraft
} }

View file

@ -86,9 +86,19 @@ class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable {
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
override fun onDrawerStateChanged(newState: Int) { override fun onDrawerStateChanged(newState: Int) {
hideKeyboard() hideKeyboard()
if (!drawerLayout.isDrawerOpen(GravityCompat.START) && newState == DrawerLayout.STATE_DRAGGING) {
// User is starting to open the drawer, scroll the list to op
scrollBreadcrumbsToTop()
}
} }
} }
private fun scrollBreadcrumbsToTop() {
supportFragmentManager.fragments.filterIsInstance<BreadcrumbsFragment>()
.forEach { it.scrollToTop() }
}
override fun onBackPressed() { override fun onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) { if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START) drawerLayout.closeDrawer(GravityCompat.START)

View file

@ -66,6 +66,8 @@ import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.dialogs.withColoredButton import im.vector.riotx.core.dialogs.withColoredButton
import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer import im.vector.riotx.core.epoxy.LayoutManagerStateRestorer
@ -408,9 +410,7 @@ class RoomDetailFragment @Inject constructor(
composerLayout.sendButton.setContentDescription(getString(descriptionRes)) composerLayout.sendButton.setContentDescription(getString(descriptionRes))
avatarRenderer.render( avatarRenderer.render(
event.senderAvatar, MatrixItem.UserItem(event.root.senderId ?: "", event.getDisambiguatedDisplayName(), event.senderAvatar),
event.root.senderId ?: "",
event.getDisambiguatedDisplayName(),
composerLayout.composerRelatedMessageAvatar composerLayout.composerRelatedMessageAvatar
) )
composerLayout.expand { composerLayout.expand {
@ -599,20 +599,19 @@ class RoomDetailFragment @Inject constructor(
} }
// Replace the word by its completion // Replace the word by its completion
val displayName = item.displayName ?: item.userId val matrixItem = item.toMatrixItem()
val displayName = matrixItem.getBestName()
// with a trailing space // with a trailing space
editable.replace(startIndex, endIndex, "$displayName ") editable.replace(startIndex, endIndex, "$displayName ")
// Add the span // Add the span
val user = session.getUser(item.userId)
val span = PillImageSpan( val span = PillImageSpan(
glideRequests, glideRequests,
avatarRenderer, avatarRenderer,
requireContext(), requireContext(),
item.userId, matrixItem
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)
@ -684,7 +683,7 @@ class RoomDetailFragment @Inject constructor(
inviteView.visibility = View.GONE inviteView.visibility = View.GONE
val uid = session.myUserId val uid = session.myUserId
val meMember = session.getRoom(state.roomId)?.getRoomMember(uid) val meMember = session.getRoom(state.roomId)?.getRoomMember(uid)
avatarRenderer.render(meMember?.avatarUrl, uid, meMember?.displayName, composerLayout.composerAvatarImageView) avatarRenderer.render(MatrixItem.UserItem(uid, meMember?.displayName, meMember?.avatarUrl), composerLayout.composerAvatarImageView)
} else if (summary?.membership == Membership.INVITE && inviter != null) { } else if (summary?.membership == Membership.INVITE && inviter != null) {
inviteView.visibility = View.VISIBLE inviteView.visibility = View.VISIBLE
inviteView.render(inviter, VectorInviteView.Mode.LARGE) inviteView.render(inviter, VectorInviteView.Mode.LARGE)
@ -711,7 +710,7 @@ class RoomDetailFragment @Inject constructor(
activity?.finish() activity?.finish()
} else { } else {
roomToolbarTitleView.text = it.displayName roomToolbarTitleView.text = it.displayName
avatarRenderer.render(it, roomToolbarAvatarImageView) avatarRenderer.render(it.toMatrixItem(), roomToolbarAvatarImageView)
roomToolbarSubtitleView.setTextOrHide(it.topic) roomToolbarSubtitleView.setTextOrHide(it.topic)
} }
jumpToBottomView.count = it.notificationCount jumpToBottomView.count = it.notificationCount
@ -1195,9 +1194,8 @@ class RoomDetailFragment @Inject constructor(
glideRequests, glideRequests,
avatarRenderer, avatarRenderer,
requireContext(), requireContext(),
userId, MatrixItem.UserItem(userId, displayName, roomMember?.avatarUrl)
displayName, )
roomMember?.avatarUrl)
.also { it.bind(composerLayout.composerEditText) }, .also { it.bind(composerLayout.composerEditText) },
0, 0,
displayName.length, displayName.length,

View file

@ -22,6 +22,7 @@ import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder import com.airbnb.epoxy.EpoxyModelWithHolder
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -29,15 +30,13 @@ import im.vector.riotx.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_display_read_receipt) @EpoxyModelClass(layout = R.layout.item_display_read_receipt)
abstract class DisplayReadReceiptItem : EpoxyModelWithHolder<DisplayReadReceiptItem.Holder>() { abstract class DisplayReadReceiptItem : EpoxyModelWithHolder<DisplayReadReceiptItem.Holder>() {
@EpoxyAttribute var name: String? = null @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute var userId: String = ""
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var timestamp: CharSequence? = null @EpoxyAttribute var timestamp: CharSequence? = null
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
avatarRenderer.render(avatarUrl, userId, name, holder.avatarView) avatarRenderer.render(matrixItem, holder.avatarView)
holder.displayNameView.text = name ?: userId holder.displayNameView.text = matrixItem.getBestName()
timestamp?.let { timestamp?.let {
holder.timestampView.text = it holder.timestampView.text = it
holder.timestampView.isVisible = true holder.timestampView.isVisible = true

View file

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.Session
import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
import im.vector.riotx.features.home.room.detail.timeline.item.toMatrixItem
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -36,9 +37,7 @@ class DisplayReadReceiptsController @Inject constructor(private val dateFormatte
val timestamp = dateFormatter.formatRelativeDateTime(it.timestamp) val timestamp = dateFormatter.formatRelativeDateTime(it.timestamp)
DisplayReadReceiptItem_() DisplayReadReceiptItem_()
.id(it.userId) .id(it.userId)
.userId(it.userId) .matrixItem(it.toMatrixItem())
.avatarUrl(it.avatarUrl)
.name(it.displayName)
.avatarRenderer(avatarRender) .avatarRenderer(avatarRender)
.timestamp(timestamp) .timestamp(timestamp)
.addIf(session.myUserId != it.userId, this) .addIf(session.myUserId != it.userId, this)

View file

@ -44,9 +44,7 @@ class MessageActionsEpoxyController @Inject constructor(private val stringProvid
bottomSheetMessagePreviewItem { bottomSheetMessagePreviewItem {
id("preview") id("preview")
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
avatarUrl(state.informationData.avatarUrl ?: "") matrixItem(state.informationData.matrixItem)
senderId(state.informationData.senderId)
senderName(state.senderName())
movementMethod(createLinkMovementMethod(listener)) movementMethod(createLinkMovementMethod(listener))
body(body.linkify(listener)) body(body.linkify(listener))
time(state.time()) time(state.time())

View file

@ -60,7 +60,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
val avatarUrl = event.senderAvatar val avatarUrl = event.senderAvatar
val memberName = event.getDisambiguatedDisplayName() val memberName = event.getDisambiguatedDisplayName()
val formattedMemberName = span(memberName) { val formattedMemberName = span(memberName) {
textColor = colorProvider.getColor(getColorFromUserId(event.root.senderId ?: "")) textColor = colorProvider.getColor(getColorFromUserId(event.root.senderId))
} }
return MessageInformationData( return MessageInformationData(

View file

@ -77,12 +77,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : BaseEventItem<H>() {
holder.timeView.visibility = View.VISIBLE holder.timeView.visibility = View.VISIBLE
holder.timeView.text = attributes.informationData.time holder.timeView.text = attributes.informationData.time
holder.memberNameView.text = attributes.informationData.memberName holder.memberNameView.text = attributes.informationData.memberName
attributes.avatarRenderer.render( attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
attributes.informationData.avatarUrl,
attributes.informationData.senderId,
attributes.informationData.memberName?.toString(),
holder.avatarImageView
)
holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener) holder.avatarImageView.setOnLongClickListener(attributes.itemLongClickListener)
holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener) holder.memberNameView.setOnLongClickListener(attributes.itemLongClickListener)
} else { } else {

View file

@ -24,6 +24,7 @@ import androidx.core.view.children
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController
@ -54,7 +55,7 @@ abstract class MergedHeaderItem : BaseEventItem<MergedHeaderItem.Holder>() {
val data = distinctMergeData.getOrNull(index) val data = distinctMergeData.getOrNull(index)
if (data != null && view is ImageView) { if (data != null && view is ImageView) {
view.visibility = View.VISIBLE view.visibility = View.VISIBLE
attributes.avatarRenderer.render(data.avatarUrl, data.userId, data.memberName, view) attributes.avatarRenderer.render(data.toMatrixItem(), view)
} else { } else {
view.visibility = View.GONE view.visibility = View.GONE
} }
@ -87,6 +88,8 @@ abstract class MergedHeaderItem : BaseEventItem<MergedHeaderItem.Holder>() {
val avatarUrl: String? val avatarUrl: String?
) )
fun Data.toMatrixItem() = MatrixItem.UserItem(userId, memberName, avatarUrl)
data class Attributes( data class Attributes(
val isCollapsed: Boolean, val isCollapsed: Boolean,
val mergeData: List<Data>, val mergeData: List<Data>,

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.item
import android.os.Parcelable import android.os.Parcelable
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.util.MatrixItem
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
@ -34,7 +35,11 @@ data class MessageInformationData(
val hasBeenEdited: Boolean = false, val hasBeenEdited: Boolean = false,
val hasPendingEdits: Boolean = false, val hasPendingEdits: Boolean = false,
val readReceipts: List<ReadReceiptData> = emptyList() val readReceipts: List<ReadReceiptData> = emptyList()
) : Parcelable ) : Parcelable {
val matrixItem: MatrixItem
get() = MatrixItem.UserItem(senderId, memberName?.toString(), avatarUrl)
}
@Parcelize @Parcelize
data class ReactionInfoData( data class ReactionInfoData(
@ -51,3 +56,5 @@ data class ReadReceiptData(
val displayName: String?, val displayName: String?,
val timestamp: Long val timestamp: Long
) : Parcelable ) : Parcelable
fun ReadReceiptData.toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl)

View file

@ -39,13 +39,7 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.noticeTextView.text = attributes.noticeText holder.noticeTextView.text = attributes.noticeText
attributes.avatarRenderer.render( attributes.avatarRenderer.render(attributes.informationData.matrixItem, holder.avatarImageView)
attributes.informationData.avatarUrl,
attributes.informationData.senderId,
attributes.informationData.memberName?.toString()
?: attributes.informationData.senderId,
holder.avatarImageView
)
holder.view.setOnLongClickListener(attributes.itemLongClickListener) holder.view.setOnLongClickListener(attributes.itemLongClickListener)
holder.readReceiptsView.render(attributes.informationData.readReceipts, attributes.avatarRenderer, _readReceiptsClickListener) holder.readReceiptsView.render(attributes.informationData.readReceipts, attributes.avatarRenderer, _readReceiptsClickListener)
} }

View file

@ -22,6 +22,7 @@ import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -33,10 +34,8 @@ import im.vector.riotx.features.home.AvatarRenderer
abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>() { abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var roomName: CharSequence @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute lateinit var roomId: String
@EpoxyAttribute var secondLine: CharSequence? = null @EpoxyAttribute var secondLine: CharSequence? = null
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var listener: (() -> Unit)? = null @EpoxyAttribute var listener: (() -> Unit)? = null
@EpoxyAttribute var invitationAcceptInProgress: Boolean = false @EpoxyAttribute var invitationAcceptInProgress: Boolean = false
@EpoxyAttribute var invitationAcceptInError: Boolean = false @EpoxyAttribute var invitationAcceptInError: Boolean = false
@ -85,9 +84,9 @@ abstract class RoomInvitationItem : VectorEpoxyModel<RoomInvitationItem.Holder>(
rejectListener?.invoke() rejectListener?.invoke()
} }
} }
holder.titleView.text = roomName holder.titleView.text = matrixItem.getBestName()
holder.subtitleView.setTextOrHide(secondLine) holder.subtitleView.setTextOrHide(secondLine)
avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -23,6 +23,7 @@ import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -32,11 +33,9 @@ import im.vector.riotx.features.home.AvatarRenderer
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() { abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute lateinit var roomName: CharSequence @EpoxyAttribute lateinit var matrixItem: MatrixItem
@EpoxyAttribute lateinit var roomId: String
@EpoxyAttribute lateinit var lastFormattedEvent: CharSequence @EpoxyAttribute lateinit var lastFormattedEvent: CharSequence
@EpoxyAttribute lateinit var lastEventTime: CharSequence @EpoxyAttribute lateinit var lastEventTime: CharSequence
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var unreadNotificationCount: Int = 0 @EpoxyAttribute var unreadNotificationCount: Int = 0
@EpoxyAttribute var hasUnreadMessage: Boolean = false @EpoxyAttribute var hasUnreadMessage: Boolean = false
@EpoxyAttribute var hasDraft: Boolean = false @EpoxyAttribute var hasDraft: Boolean = false
@ -48,13 +47,13 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
super.bind(holder) super.bind(holder)
holder.rootView.setOnClickListener(itemClickListener) holder.rootView.setOnClickListener(itemClickListener)
holder.rootView.setOnLongClickListener(itemLongClickListener) holder.rootView.setOnLongClickListener(itemLongClickListener)
holder.titleView.text = roomName holder.titleView.text = matrixItem.getBestName()
holder.lastEventTimeView.text = lastEventTime holder.lastEventTimeView.text = lastEventTime
holder.lastEventView.text = lastFormattedEvent holder.lastEventView.text = lastFormattedEvent
holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted)) holder.unreadCounterBadgeView.render(UnreadCounterBadgeView.State(unreadNotificationCount, showHighlighted))
holder.unreadIndentIndicator.isVisible = hasUnreadMessage holder.unreadIndentIndicator.isVisible = hasUnreadMessage
holder.draftView.isVisible = hasDraft holder.draftView.isVisible = hasDraft
avatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
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.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent import im.vector.matrix.android.api.session.room.timeline.getLastMessageContent
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.date.VectorDateFormatter import im.vector.riotx.core.date.VectorDateFormatter
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -69,7 +70,7 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte
return RoomInvitationItem_() return RoomInvitationItem_()
.id(roomSummary.roomId) .id(roomSummary.roomId)
.avatarRenderer(avatarRenderer) .avatarRenderer(avatarRenderer)
.roomId(roomSummary.roomId) .matrixItem(roomSummary.toMatrixItem())
.secondLine(secondLine) .secondLine(secondLine)
.invitationAcceptInProgress(joiningRoomsIds.contains(roomSummary.roomId)) .invitationAcceptInProgress(joiningRoomsIds.contains(roomSummary.roomId))
.invitationAcceptInError(joiningErrorRoomsIds.contains(roomSummary.roomId)) .invitationAcceptInError(joiningErrorRoomsIds.contains(roomSummary.roomId))
@ -77,8 +78,6 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte
.invitationRejectInError(rejectingErrorRoomsIds.contains(roomSummary.roomId)) .invitationRejectInError(rejectingErrorRoomsIds.contains(roomSummary.roomId))
.acceptListener { listener?.onAcceptRoomInvitation(roomSummary) } .acceptListener { listener?.onAcceptRoomInvitation(roomSummary) }
.rejectListener { listener?.onRejectRoomInvitation(roomSummary) } .rejectListener { listener?.onRejectRoomInvitation(roomSummary) }
.roomName(roomSummary.displayName)
.avatarUrl(roomSummary.avatarUrl)
.listener { listener?.onRoomClicked(roomSummary) } .listener { listener?.onRoomClicked(roomSummary) }
} }
@ -125,11 +124,9 @@ class RoomSummaryItemFactory @Inject constructor(private val noticeEventFormatte
return RoomSummaryItem_() return RoomSummaryItem_()
.id(roomSummary.roomId) .id(roomSummary.roomId)
.avatarRenderer(avatarRenderer) .avatarRenderer(avatarRenderer)
.roomId(roomSummary.roomId) .matrixItem(roomSummary.toMatrixItem())
.lastEventTime(latestEventTime) .lastEventTime(latestEventTime)
.lastFormattedEvent(latestFormattedEvent) .lastFormattedEvent(latestFormattedEvent)
.roomName(roomSummary.displayName)
.avatarUrl(roomSummary.avatarUrl)
.showHighlighted(showHighlighted) .showHighlighted(showHighlighted)
.unreadNotificationCount(unreadCount) .unreadNotificationCount(unreadCount)
.hasUnreadMessage(roomSummary.hasUnreadMessages) .hasUnreadMessage(roomSummary.hasUnreadMessages)

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.list.actions
import android.view.View import android.view.View
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.room.notification.RoomNotificationState import im.vector.matrix.android.api.session.room.notification.RoomNotificationState
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetActionItem import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetActionItem
import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetRoomPreviewItem
import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetSeparatorItem import im.vector.riotx.core.epoxy.bottomsheet.bottomSheetSeparatorItem
@ -39,9 +40,7 @@ class RoomListQuickActionsEpoxyController @Inject constructor(private val avatar
bottomSheetRoomPreviewItem { bottomSheetRoomPreviewItem {
id("preview") id("preview")
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
roomName(roomSummary.displayName) matrixItem(roomSummary.toMatrixItem())
avatarUrl(roomSummary.avatarUrl)
roomId(roomSummary.roomId)
settingsClickListener(View.OnClickListener { listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) }) settingsClickListener(View.OnClickListener { listener?.didSelectMenuAction(RoomListQuickActionsSharedAction.Settings(roomSummary.roomId)) })
} }

View file

@ -20,6 +20,7 @@ import android.content.Context
import android.text.style.URLSpan import android.text.style.URLSpan
import im.vector.matrix.android.api.permalinks.PermalinkData import im.vector.matrix.android.api.permalinks.PermalinkData
import im.vector.matrix.android.api.permalinks.PermalinkParser import im.vector.matrix.android.api.permalinks.PermalinkParser
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.glide.GlideRequests import im.vector.riotx.core.glide.GlideRequests
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -41,8 +42,8 @@ class MxLinkTagHandler(private val glideRequests: GlideRequests,
when (permalinkData) { when (permalinkData) {
is PermalinkData.UserLink -> { is PermalinkData.UserLink -> {
val user = sessionHolder.getSafeActiveSession()?.getUser(permalinkData.userId) val user = sessionHolder.getSafeActiveSession()?.getUser(permalinkData.userId)
val span = PillImageSpan(glideRequests, avatarRenderer, context, permalinkData.userId, user?.displayName val span = PillImageSpan(glideRequests, avatarRenderer, context, MatrixItem.UserItem(permalinkData.userId, user?.displayName
?: permalinkData.userId, user?.avatarUrl) ?: permalinkData.userId, user?.avatarUrl))
SpannableBuilder.setSpans( SpannableBuilder.setSpans(
visitor.builder(), visitor.builder(),
span, span,

View file

@ -29,6 +29,7 @@ import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.google.android.material.chip.ChipDrawable import com.google.android.material.chip.ChipDrawable
import im.vector.matrix.android.api.session.room.send.UserMentionSpan import im.vector.matrix.android.api.session.room.send.UserMentionSpan
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.glide.GlideRequests import im.vector.riotx.core.glide.GlideRequests
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -42,9 +43,8 @@ import java.lang.ref.WeakReference
class PillImageSpan(private val glideRequests: GlideRequests, class PillImageSpan(private val glideRequests: GlideRequests,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val context: Context, private val context: Context,
override val userId: String, override val matrixItem: MatrixItem
override val displayName: String, ) : ReplacementSpan(), UserMentionSpan {
private val avatarUrl: String?) : ReplacementSpan(), UserMentionSpan {
private val pillDrawable = createChipDrawable() private val pillDrawable = createChipDrawable()
private val target = PillImageSpanTarget(this) private val target = PillImageSpanTarget(this)
@ -53,7 +53,7 @@ class PillImageSpan(private val glideRequests: GlideRequests,
@UiThread @UiThread
fun bind(textView: TextView) { fun bind(textView: TextView) {
tv = WeakReference(textView) tv = WeakReference(textView)
avatarRenderer.render(context, glideRequests, avatarUrl, userId, displayName, target) avatarRenderer.render(context, glideRequests, matrixItem, target)
} }
// ReplacementSpan ***************************************************************************** // ReplacementSpan *****************************************************************************
@ -101,12 +101,12 @@ class PillImageSpan(private val glideRequests: GlideRequests,
private fun createChipDrawable(): ChipDrawable { private fun createChipDrawable(): ChipDrawable {
val textPadding = context.resources.getDimension(R.dimen.pill_text_padding) val textPadding = context.resources.getDimension(R.dimen.pill_text_padding)
return ChipDrawable.createFromResource(context, R.xml.pill_view).apply { return ChipDrawable.createFromResource(context, R.xml.pill_view).apply {
text = displayName text = matrixItem.getBestName()
textEndPadding = textPadding textEndPadding = textPadding
textStartPadding = textPadding textStartPadding = textPadding
setChipMinHeightResource(R.dimen.pill_min_height) setChipMinHeightResource(R.dimen.pill_min_height)
setChipIconSizeResource(R.dimen.pill_avatar_size) setChipIconSizeResource(R.dimen.pill_avatar_size)
chipIcon = avatarRenderer.getPlaceholderDrawable(context, userId, displayName) chipIcon = avatarRenderer.getPlaceholderDrawable(context, matrixItem)
setBounds(0, 0, intrinsicWidth, intrinsicHeight) setBounds(0, 0, intrinsicWidth, intrinsicHeight)
} }
} }

View file

@ -22,6 +22,7 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.di.HasScreenInjector
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -56,7 +57,7 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
fun render(sender: User, mode: Mode = Mode.LARGE) { fun render(sender: User, mode: Mode = Mode.LARGE) {
if (mode == Mode.LARGE) { if (mode == Mode.LARGE) {
updateLayoutParams { height = LayoutParams.MATCH_CONSTRAINT } updateLayoutParams { height = LayoutParams.MATCH_CONSTRAINT }
avatarRenderer.render(sender.avatarUrl, sender.userId, sender.displayName, inviteAvatarView) avatarRenderer.render(sender.toMatrixItem(), inviteAvatarView)
inviteIdentifierView.text = sender.userId inviteIdentifierView.text = sender.userId
inviteNameView.text = sender.displayName inviteNameView.text = sender.displayName
inviteLabelView.text = context.getString(R.string.send_you_invite) inviteLabelView.text = context.getString(R.string.send_you_invite)

View file

@ -21,6 +21,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -35,13 +36,7 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
lateinit var avatarRenderer: AvatarRenderer lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute @EpoxyAttribute
var avatarUrl: String? = null lateinit var matrixItem: MatrixItem
@EpoxyAttribute
var roomId: String? = null
@EpoxyAttribute
var roomName: String? = null
@EpoxyAttribute @EpoxyAttribute
var roomAlias: String? = null var roomAlias: String? = null
@ -64,8 +59,8 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.rootView.setOnClickListener { globalListener?.invoke() } holder.rootView.setOnClickListener { globalListener?.invoke() }
avatarRenderer.render(avatarUrl, roomId!!, roomName, holder.avatarView) avatarRenderer.render(matrixItem, holder.avatarView)
holder.nameView.text = roomName holder.nameView.text = matrixItem.displayName
holder.aliasView.setTextOrHide(roomAlias) holder.aliasView.setTextOrHide(roomAlias)
holder.topicView.setTextOrHide(roomTopic) holder.topicView.setTextOrHide(roomTopic)
// TODO Use formatter for big numbers? // TODO Use formatter for big numbers?

View file

@ -22,6 +22,7 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.errorWithRetryItem import im.vector.riotx.core.epoxy.errorWithRetryItem
import im.vector.riotx.core.epoxy.loadingItem import im.vector.riotx.core.epoxy.loadingItem
@ -83,9 +84,7 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri
publicRoomItem { publicRoomItem {
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
id(publicRoom.roomId) id(publicRoom.roomId)
roomId(publicRoom.roomId) matrixItem(publicRoom.toMatrixItem())
avatarUrl(publicRoom.avatarUrl)
roomName(publicRoom.name)
roomAlias(publicRoom.canonicalAlias) roomAlias(publicRoom.canonicalAlias)
roomTopic(publicRoom.topic) roomTopic(publicRoom.topic)
nbOfMembers(publicRoom.numJoinedMembers) nbOfMembers(publicRoom.numJoinedMembers)

View file

@ -21,6 +21,7 @@ import android.content.Intent
import android.os.Parcelable import android.os.Parcelable
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.extensions.addFragment import im.vector.riotx.core.extensions.addFragment
import im.vector.riotx.core.platform.ToolbarConfigurable import im.vector.riotx.core.platform.ToolbarConfigurable
@ -34,7 +35,10 @@ data class RoomPreviewData(
val topic: String?, val topic: String?,
val worldReadable: Boolean, val worldReadable: Boolean,
val avatarUrl: String? val avatarUrl: String?
) : Parcelable ) : Parcelable {
val matrixItem: MatrixItem
get() = MatrixItem.RoomItem(roomId, roomName, avatarUrl)
}
class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { class RoomPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {

View file

@ -49,11 +49,11 @@ class RoomPreviewNoPreviewFragment @Inject constructor(
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupToolbar(roomPreviewNoPreviewToolbar) setupToolbar(roomPreviewNoPreviewToolbar)
// Toolbar // Toolbar
avatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewToolbarAvatar) avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewToolbarAvatar)
roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName roomPreviewNoPreviewToolbarTitle.text = roomPreviewData.roomName
// Screen // Screen
avatarRenderer.render(roomPreviewData.avatarUrl, roomPreviewData.roomId, roomPreviewData.roomName, roomPreviewNoPreviewAvatar) avatarRenderer.render(roomPreviewData.matrixItem, roomPreviewNoPreviewAvatar)
roomPreviewNoPreviewName.text = roomPreviewData.roomName roomPreviewNoPreviewName.text = roomPreviewData.roomName
roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic) roomPreviewNoPreviewTopic.setTextOrHide(roomPreviewData.topic)

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.settings.ignored
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.session.user.model.User
import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.noResultItem import im.vector.riotx.core.epoxy.noResultItem
import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.resources.StringProvider
@ -44,19 +45,19 @@ class IgnoredUsersController @Inject constructor(private val stringProvider: Str
buildIgnoredUserModels(nonNullViewState.ignoredUsers) buildIgnoredUserModels(nonNullViewState.ignoredUsers)
} }
private fun buildIgnoredUserModels(userIds: List<User>) { private fun buildIgnoredUserModels(users: List<User>) {
if (userIds.isEmpty()) { if (users.isEmpty()) {
noResultItem { noResultItem {
id("empty") id("empty")
text(stringProvider.getString(R.string.no_ignored_users)) text(stringProvider.getString(R.string.no_ignored_users))
} }
} else { } else {
userIds.forEach { userId -> users.forEach { user ->
userItem { userItem {
id(userId.userId) id(user.userId)
avatarRenderer(avatarRenderer) avatarRenderer(avatarRenderer)
user(userId) matrixItem(user.toMatrixItem())
itemClickAction { callback?.onUserIdClicked(userId.userId) } itemClickAction { callback?.onUserIdClicked(user.userId) }
} }
} }
} }

View file

@ -20,7 +20,7 @@ import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.MatrixItem
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.VectorEpoxyHolder import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
@ -37,7 +37,7 @@ abstract class UserItem : VectorEpoxyModel<UserItem.Holder>() {
lateinit var avatarRenderer: AvatarRenderer lateinit var avatarRenderer: AvatarRenderer
@EpoxyAttribute @EpoxyAttribute
lateinit var user: User lateinit var matrixItem: MatrixItem
@EpoxyAttribute @EpoxyAttribute
var itemClickAction: (() -> Unit)? = null var itemClickAction: (() -> Unit)? = null
@ -45,9 +45,9 @@ abstract class UserItem : VectorEpoxyModel<UserItem.Holder>() {
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
holder.root.setOnClickListener { itemClickAction?.invoke() } holder.root.setOnClickListener { itemClickAction?.invoke() }
avatarRenderer.render(user, holder.avatarImage) avatarRenderer.render(matrixItem, holder.avatarImage)
holder.userIdText.setTextOrHide(user.userId) holder.userIdText.setTextOrHide(matrixItem.id)
holder.displayNameText.setTextOrHide(user.displayName) holder.displayNameText.setTextOrHide(matrixItem.displayName)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {

View file

@ -40,10 +40,10 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:minWidth="16dp" android:minWidth="18dp"
android:minHeight="16dp" android:minHeight="18dp"
android:textColor="@android:color/white" android:textColor="@android:color/white"
android:textSize="10sp" android:textSize="11sp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintCircle="@+id/breadcrumbsImageView" app:layout_constraintCircle="@+id/breadcrumbsImageView"
app:layout_constraintCircleAngle="45" app:layout_constraintCircleAngle="45"

View file

@ -0,0 +1,42 @@
/*
* Copyright 2019 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.riotx.features.home
import im.vector.riotx.R
import org.junit.Assert.assertEquals
import org.junit.Test
class UserColorTest {
@Test
fun testNull() {
assertEquals(R.color.riotx_username_1, getColorFromUserId(null))
}
@Test
fun testEmpty() {
assertEquals(R.color.riotx_username_1, getColorFromUserId(""))
}
@Test
fun testName() {
assertEquals(R.color.riotx_username_1, getColorFromUserId("@ganfra:matrix.org"))
assertEquals(R.color.riotx_username_4, getColorFromUserId("@benoit0816:matrix.org"))
assertEquals(R.color.riotx_username_5, getColorFromUserId("@hubert:uhoreg.ca"))
assertEquals(R.color.riotx_username_7, getColorFromUserId("@nadonomy:matrix.org"))
}
}