diff --git a/CHANGES.md b/CHANGES.md index 081877c313..1aea79961a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ Features ✨: Improvements 🙌: - Add "show password" in import Megolm keys dialog - Visually disable call buttons in menu and prohibit calling when permissions are insufficient (#2112) + - Use cache for user color Bugfix 🐛: - Long message cannot be sent/takes infinite time & blocks other messages #1397 diff --git a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt index c750384485..4ba3d6ba13 100644 --- a/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/app/core/di/VectorComponent.kt @@ -36,6 +36,7 @@ import im.vector.app.features.crypto.verification.IncomingVerificationRequestHan import im.vector.app.features.grouplist.SelectedGroupDataSource import im.vector.app.features.home.AvatarRenderer 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.VectorHtmlCompressor import im.vector.app.features.login.ReAuthHelper @@ -71,6 +72,8 @@ interface VectorComponent { fun matrix(): Matrix + fun matrixItemColorProvider(): MatrixItemColorProvider + fun sessionListener(): SessionListener fun currentSession(): Session diff --git a/vector/src/main/java/im/vector/app/core/utils/UserColor.kt b/vector/src/main/java/im/vector/app/core/utils/UserColor.kt deleted file mode 100644 index af006a35bb..0000000000 --- a/vector/src/main/java/im/vector/app/core/utils/UserColor.kt +++ /dev/null @@ -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 - } -} diff --git a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt index 7787054240..08f18a00ba 100644 --- a/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/home/AvatarRenderer.kt @@ -16,13 +16,11 @@ package im.vector.app.features.home -import android.content.Context import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.widget.ImageView import androidx.annotation.AnyThread import androidx.annotation.UiThread -import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import com.amulyakhare.textdrawable.TextDrawable 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.GlideRequest 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.session.content.ContentUrlResolver 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 */ -class AvatarRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder) { +class AvatarRenderer @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, + private val matrixItemColorProvider: MatrixItemColorProvider) { companion object { private const val THUMBNAIL_SIZE = 250 @@ -51,8 +50,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active @UiThread fun render(matrixItem: MatrixItem, imageView: ImageView) { - render(imageView.context, - GlideApp.with(imageView), + render(GlideApp.with(imageView), matrixItem, DrawableImageViewTarget(imageView)) } @@ -64,8 +62,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active @UiThread fun render(matrixItem: MatrixItem, imageView: ImageView, glideRequests: GlideRequests) { - render(imageView.context, - glideRequests, + render(glideRequests, matrixItem, DrawableImageViewTarget(imageView)) } @@ -79,7 +76,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active displayName = mappedContact.displayName ) - val placeholder = getPlaceholderDrawable(imageView.context, matrixItem) + val placeholder = getPlaceholderDrawable(matrixItem) GlideApp.with(imageView) .load(mappedContact.photoURI) .apply(RequestOptions.circleCropTransform()) @@ -88,11 +85,10 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active } @UiThread - fun render(context: Context, - glideRequests: GlideRequests, + fun render(glideRequests: GlideRequests, matrixItem: MatrixItem, target: Target) { - val placeholder = getPlaceholderDrawable(context, matrixItem) + val placeholder = getPlaceholderDrawable(matrixItem) buildGlideRequest(glideRequests, matrixItem.avatarUrl) .placeholder(placeholder) .into(target) @@ -100,7 +96,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active @AnyThread @Throws - fun shortcutDrawable(context: Context, glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap { + fun shortcutDrawable(glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap { return glideRequests .asBitmap() .apply { @@ -108,7 +104,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active if (resolvedUrl != null) { load(resolvedUrl) } else { - val avatarColor = avatarColor(matrixItem, context) + val avatarColor = matrixItemColorProvider.getColor(matrixItem) load(TextDrawable.builder() .beginConfig() .bold() @@ -130,8 +126,8 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active } @AnyThread - fun getPlaceholderDrawable(context: Context, matrixItem: MatrixItem): Drawable { - val avatarColor = avatarColor(matrixItem, context) + fun getPlaceholderDrawable(matrixItem: MatrixItem): Drawable { + val avatarColor = matrixItemColorProvider.getColor(matrixItem) return TextDrawable.builder() .beginConfig() .bold() @@ -152,11 +148,4 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active return activeSessionHolder.getSafeActiveSession()?.contentUrlResolver() ?.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)) - } - } } diff --git a/vector/src/main/java/im/vector/app/features/home/RoomColor.kt b/vector/src/main/java/im/vector/app/features/home/RoomColor.kt deleted file mode 100644 index 88ec582c92..0000000000 --- a/vector/src/main/java/im/vector/app/features/home/RoomColor.kt +++ /dev/null @@ -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 - } -} diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt index ad0209845f..1a476913f3 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutsHandler.kt @@ -70,7 +70,7 @@ class ShortcutsHandler @Inject constructor( .map { room -> val intent = RoomDetailActivity.shortcutIntent(context, room.roomId) val bitmap = try { - avatarRenderer.shortcutDrawable(context, GlideApp.with(context), room.toMatrixItem(), iconSize) + avatarRenderer.shortcutDrawable(GlideApp.with(context), room.toMatrixItem(), iconSize) } catch (failure: Throwable) { null } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 6acdcc5aa6..7c3ac6011e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -97,7 +97,6 @@ import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.createJSonViewerStyleProvider 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.onPermissionResultAudioIpCall 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.MessageSharedActionViewModel 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.MessageFileItem 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 colorProvider: ColorProvider, private val notificationUtils: NotificationUtils, - private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager) : + private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager, + private val matrixItemColorProvider: MatrixItemColorProvider +) : VectorBaseFragment(), TimelineEventController.Callback, VectorInviteView.Callback, @@ -790,7 +792,7 @@ class RoomDetailFragment @Inject constructor( // switch to expanded bar composerLayout.composerRelatedMessageTitle.apply { 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() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/MessageColorProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/MessageColorProvider.kt index 66e2c745a4..d1bef4c8c7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/MessageColorProvider.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/MessageColorProvider.kt @@ -19,18 +19,20 @@ package im.vector.app.features.home.room.detail.timeline import androidx.annotation.ColorInt import im.vector.app.R 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 org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject class MessageColorProvider @Inject constructor( private val colorProvider: ColorProvider, + private val matrixItemColorProvider: MatrixItemColorProvider, private val vectorPreferences: VectorPreferences) { @ColorInt - fun getMemberNameTextColor(userId: String): Int { - return colorProvider.getColor(getColorFromUserId(userId)) + fun getMemberNameTextColor(matrixItem: MatrixItem): Int { + return matrixItemColorProvider.getColor(matrixItem) } @ColorInt diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt new file mode 100644 index 0000000000..adefb33801 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt @@ -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() + + @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 + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt index 7c2a6286b9..cb7cb1eb31 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt @@ -85,7 +85,7 @@ abstract class AbsMessageItem : AbsBaseMessageItem 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) { val avatarImageView by bind(R.id.messageAvatarImageView) diff --git a/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt b/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt index 2602dfd6c7..76947e8d3d 100644 --- a/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt +++ b/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt @@ -53,7 +53,7 @@ class PillImageSpan(private val glideRequests: GlideRequests, @UiThread fun bind(textView: TextView) { tv = WeakReference(textView) - avatarRenderer.render(context, glideRequests, matrixItem, target) + avatarRenderer.render(glideRequests, matrixItem, target) } // ReplacementSpan ***************************************************************************** @@ -99,7 +99,7 @@ class PillImageSpan(private val glideRequests: GlideRequests, val icon = try { avatarRenderer.getCachedDrawable(glideRequests, matrixItem) } catch (exception: Exception) { - avatarRenderer.getPlaceholderDrawable(context, matrixItem) + avatarRenderer.getPlaceholderDrawable(matrixItem) } return ChipDrawable.createFromResource(context, R.xml.pill_view).apply {