Use cache for user color

This commit is contained in:
Benoit Marty 2020-09-22 17:05:25 +02:00
parent bd72c0ca8d
commit 7c063972ac
11 changed files with 102 additions and 101 deletions

View File

@ -7,6 +7,7 @@ Features ✨:
Improvements 🙌: Improvements 🙌:
- Add "show password" in import Megolm keys dialog - Add "show password" in import Megolm keys dialog
- Visually disable call buttons in menu and prohibit calling when permissions are insufficient (#2112) - Visually disable call buttons in menu and prohibit calling when permissions are insufficient (#2112)
- Use cache for user color
Bugfix 🐛: Bugfix 🐛:
- Long message cannot be sent/takes infinite time & blocks other messages #1397 - Long message cannot be sent/takes infinite time & blocks other messages #1397

View File

@ -36,6 +36,7 @@ import im.vector.app.features.crypto.verification.IncomingVerificationRequestHan
import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.grouplist.SelectedGroupDataSource
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.HomeRoomListDataSource import im.vector.app.features.home.HomeRoomListDataSource
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.EventHtmlRenderer
import im.vector.app.features.html.VectorHtmlCompressor import im.vector.app.features.html.VectorHtmlCompressor
import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ReAuthHelper
@ -71,6 +72,8 @@ interface VectorComponent {
fun matrix(): Matrix fun matrix(): Matrix
fun matrixItemColorProvider(): MatrixItemColorProvider
fun sessionListener(): SessionListener fun sessionListener(): SessionListener
fun currentSession(): Session fun currentSession(): Session

View File

@ -1,39 +0,0 @@
/*
* 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.app.core.utils
import androidx.annotation.ColorRes
import im.vector.app.R
import kotlin.math.abs
@ColorRes
fun getColorFromUserId(userId: String?): Int {
var hash = 0
userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.toInt() }
return when (abs(hash) % 8) {
1 -> R.color.riotx_username_2
2 -> R.color.riotx_username_3
3 -> R.color.riotx_username_4
4 -> R.color.riotx_username_5
5 -> R.color.riotx_username_6
6 -> R.color.riotx_username_7
7 -> R.color.riotx_username_8
else -> R.color.riotx_username_1
}
}

View File

@ -16,13 +16,11 @@
package im.vector.app.features.home package im.vector.app.features.home
import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.AnyThread import androidx.annotation.AnyThread
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
@ -33,7 +31,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.glide.GlideApp import im.vector.app.core.glide.GlideApp
import im.vector.app.core.glide.GlideRequest import im.vector.app.core.glide.GlideRequest
import im.vector.app.core.glide.GlideRequests import im.vector.app.core.glide.GlideRequests
import im.vector.app.core.utils.getColorFromUserId import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.content.ContentUrlResolver
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
@ -43,7 +41,8 @@ import javax.inject.Inject
* This helper centralise ways to retrieve avatar into ImageView or even generic Target<Drawable> * This helper centralise ways to retrieve avatar into ImageView or even generic Target<Drawable>
*/ */
class AvatarRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) { class AvatarRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder,
private val matrixItemColorProvider: MatrixItemColorProvider) {
companion object { companion object {
private const val THUMBNAIL_SIZE = 250 private const val THUMBNAIL_SIZE = 250
@ -51,8 +50,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
@UiThread @UiThread
fun render(matrixItem: MatrixItem, imageView: ImageView) { fun render(matrixItem: MatrixItem, imageView: ImageView) {
render(imageView.context, render(GlideApp.with(imageView),
GlideApp.with(imageView),
matrixItem, matrixItem,
DrawableImageViewTarget(imageView)) DrawableImageViewTarget(imageView))
} }
@ -64,8 +62,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
@UiThread @UiThread
fun render(matrixItem: MatrixItem, imageView: ImageView, glideRequests: GlideRequests) { fun render(matrixItem: MatrixItem, imageView: ImageView, glideRequests: GlideRequests) {
render(imageView.context, render(glideRequests,
glideRequests,
matrixItem, matrixItem,
DrawableImageViewTarget(imageView)) DrawableImageViewTarget(imageView))
} }
@ -79,7 +76,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
displayName = mappedContact.displayName displayName = mappedContact.displayName
) )
val placeholder = getPlaceholderDrawable(imageView.context, matrixItem) val placeholder = getPlaceholderDrawable(matrixItem)
GlideApp.with(imageView) GlideApp.with(imageView)
.load(mappedContact.photoURI) .load(mappedContact.photoURI)
.apply(RequestOptions.circleCropTransform()) .apply(RequestOptions.circleCropTransform())
@ -88,11 +85,10 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
} }
@UiThread @UiThread
fun render(context: Context, fun render(glideRequests: GlideRequests,
glideRequests: GlideRequests,
matrixItem: MatrixItem, matrixItem: MatrixItem,
target: Target<Drawable>) { target: Target<Drawable>) {
val placeholder = getPlaceholderDrawable(context, matrixItem) val placeholder = getPlaceholderDrawable(matrixItem)
buildGlideRequest(glideRequests, matrixItem.avatarUrl) buildGlideRequest(glideRequests, matrixItem.avatarUrl)
.placeholder(placeholder) .placeholder(placeholder)
.into(target) .into(target)
@ -100,7 +96,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
@AnyThread @AnyThread
@Throws @Throws
fun shortcutDrawable(context: Context, glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap { fun shortcutDrawable(glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap {
return glideRequests return glideRequests
.asBitmap() .asBitmap()
.apply { .apply {
@ -108,7 +104,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
if (resolvedUrl != null) { if (resolvedUrl != null) {
load(resolvedUrl) load(resolvedUrl)
} else { } else {
val avatarColor = avatarColor(matrixItem, context) val avatarColor = matrixItemColorProvider.getColor(matrixItem)
load(TextDrawable.builder() load(TextDrawable.builder()
.beginConfig() .beginConfig()
.bold() .bold()
@ -130,8 +126,8 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
} }
@AnyThread @AnyThread
fun getPlaceholderDrawable(context: Context, matrixItem: MatrixItem): Drawable { fun getPlaceholderDrawable(matrixItem: MatrixItem): Drawable {
val avatarColor = avatarColor(matrixItem, context) val avatarColor = matrixItemColorProvider.getColor(matrixItem)
return TextDrawable.builder() return TextDrawable.builder()
.beginConfig() .beginConfig()
.bold() .bold()
@ -152,11 +148,4 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active
return activeSessionHolder.getSafeActiveSession()?.contentUrlResolver() return activeSessionHolder.getSafeActiveSession()?.contentUrlResolver()
?.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE) ?.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)
} }
private fun avatarColor(matrixItem: MatrixItem, context: Context): Int {
return when (matrixItem) {
is MatrixItem.UserItem -> ContextCompat.getColor(context, getColorFromUserId(matrixItem.id))
else -> ContextCompat.getColor(context, getColorFromRoomId(matrixItem.id))
}
}
} }

View File

@ -1,29 +0,0 @@
/*
* 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.app.features.home
import androidx.annotation.ColorRes
import im.vector.app.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

@ -70,7 +70,7 @@ class ShortcutsHandler @Inject constructor(
.map { room -> .map { room ->
val intent = RoomDetailActivity.shortcutIntent(context, room.roomId) val intent = RoomDetailActivity.shortcutIntent(context, room.roomId)
val bitmap = try { val bitmap = try {
avatarRenderer.shortcutDrawable(context, GlideApp.with(context), room.toMatrixItem(), iconSize) avatarRenderer.shortcutDrawable(GlideApp.with(context), room.toMatrixItem(), iconSize)
} catch (failure: Throwable) { } catch (failure: Throwable) {
null null
} }

View File

@ -97,7 +97,6 @@ import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.core.utils.createJSonViewerStyleProvider
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import im.vector.app.core.utils.getColorFromUserId
import im.vector.app.core.utils.isValidUrl import im.vector.app.core.utils.isValidUrl
import im.vector.app.core.utils.onPermissionResultAudioIpCall import im.vector.app.core.utils.onPermissionResultAudioIpCall
import im.vector.app.core.utils.onPermissionResultVideoIpCall import im.vector.app.core.utils.onPermissionResultVideoIpCall
@ -127,6 +126,7 @@ import im.vector.app.features.home.room.detail.timeline.action.EventSharedAction
import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet import im.vector.app.features.home.room.detail.timeline.action.MessageActionsBottomSheet
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet import im.vector.app.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem import im.vector.app.features.home.room.detail.timeline.item.MessageFileItem
import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem import im.vector.app.features.home.room.detail.timeline.item.MessageImageVideoItem
@ -217,7 +217,9 @@ class RoomDetailFragment @Inject constructor(
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val notificationUtils: NotificationUtils, private val notificationUtils: NotificationUtils,
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager) : private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
private val matrixItemColorProvider: MatrixItemColorProvider
) :
VectorBaseFragment(), VectorBaseFragment(),
TimelineEventController.Callback, TimelineEventController.Callback,
VectorInviteView.Callback, VectorInviteView.Callback,
@ -790,7 +792,7 @@ class RoomDetailFragment @Inject constructor(
// switch to expanded bar // switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply { composerLayout.composerRelatedMessageTitle.apply {
text = event.senderInfo.disambiguatedDisplayName text = event.senderInfo.disambiguatedDisplayName
setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.senderId))) setTextColor(matrixItemColorProvider.getColor(MatrixItem.UserItem(event.root.senderId ?: "@")))
} }
val messageContent: MessageContent? = event.getLastMessageContent() val messageContent: MessageContent? = event.getLastMessageContent()

View File

@ -19,18 +19,20 @@ package im.vector.app.features.home.room.detail.timeline
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.getColorFromUserId import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject import javax.inject.Inject
class MessageColorProvider @Inject constructor( class MessageColorProvider @Inject constructor(
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val matrixItemColorProvider: MatrixItemColorProvider,
private val vectorPreferences: VectorPreferences) { private val vectorPreferences: VectorPreferences) {
@ColorInt @ColorInt
fun getMemberNameTextColor(userId: String): Int { fun getMemberNameTextColor(matrixItem: MatrixItem): Int {
return colorProvider.getColor(getColorFromUserId(userId)) return matrixItemColorProvider.getColor(matrixItem)
} }
@ColorInt @ColorInt

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.home.room.detail.timeline.helper
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import im.vector.app.R
import im.vector.app.core.resources.ColorProvider
import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
@Singleton
class MatrixItemColorProvider @Inject constructor(
private val colorProvider: ColorProvider
) {
private val cache = mutableMapOf<String, Int>()
@ColorInt
fun getColor(matrixItem: MatrixItem): Int {
return cache.getOrPut(matrixItem.id) {
colorProvider.getColor(
when (matrixItem) {
is MatrixItem.UserItem -> getColorFromUserId(matrixItem.id)
else -> getColorFromRoomId(matrixItem.id)
}
)
}
}
@ColorRes
private fun getColorFromUserId(userId: String?): Int {
var hash = 0
userId?.toList()?.map { chr -> hash = (hash shl 5) - hash + chr.toInt() }
return when (abs(hash) % 8) {
1 -> R.color.riotx_username_2
2 -> R.color.riotx_username_3
3 -> R.color.riotx_username_4
4 -> R.color.riotx_username_5
5 -> R.color.riotx_username_6
6 -> R.color.riotx_username_7
7 -> R.color.riotx_username_8
else -> R.color.riotx_username_1
}
}
@ColorRes
private 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

@ -85,7 +85,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
super.unbind(holder) super.unbind(holder)
} }
private fun Attributes.getMemberNameColor() = messageColorProvider.getMemberNameTextColor(informationData.senderId) private fun Attributes.getMemberNameColor() = messageColorProvider.getMemberNameTextColor(informationData.matrixItem)
abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) { abstract class Holder(@IdRes stubId: Int) : AbsBaseMessageItem.Holder(stubId) {
val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView) val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)

View File

@ -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, matrixItem, target) avatarRenderer.render(glideRequests, matrixItem, target)
} }
// ReplacementSpan ***************************************************************************** // ReplacementSpan *****************************************************************************
@ -99,7 +99,7 @@ class PillImageSpan(private val glideRequests: GlideRequests,
val icon = try { val icon = try {
avatarRenderer.getCachedDrawable(glideRequests, matrixItem) avatarRenderer.getCachedDrawable(glideRequests, matrixItem)
} catch (exception: Exception) { } catch (exception: Exception) {
avatarRenderer.getPlaceholderDrawable(context, matrixItem) avatarRenderer.getPlaceholderDrawable(matrixItem)
} }
return ChipDrawable.createFromResource(context, R.xml.pill_view).apply { return ChipDrawable.createFromResource(context, R.xml.pill_view).apply {