Code cleanup, remove duplicate code, and add some comments

This commit is contained in:
Benoit Marty 2019-06-04 17:36:49 +02:00
parent ab6220a4cb
commit 2ba7ec48f6
11 changed files with 131 additions and 118 deletions

View File

@ -16,7 +16,6 @@

package im.vector.matrix.android.api

import java.util.*
import java.util.regex.Pattern

/**
@ -25,53 +24,53 @@ import java.util.regex.Pattern
object MatrixPatterns {

// Note: TLD is not mandatory (localhost, IP address...)
private val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"
private const val DOMAIN_REGEX = ":[A-Z0-9.-]+(:[0-9]{2,5})?"

// regex pattern to find matrix user ids in a string.
// See https://matrix.org/speculator/spec/HEAD/appendices.html#historical-user-ids
private val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = Pattern.compile(MATRIX_USER_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_USER_IDENTIFIER_REGEX = "@[A-Z0-9\\x21-\\x39\\x3B-\\x7F]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = Pattern.compile(MATRIX_USER_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find room ids in a string.
private val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = Pattern.compile(MATRIX_ROOM_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = Pattern.compile(MATRIX_ROOM_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find room aliases in a string.
private val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_ALIAS = Pattern.compile(MATRIX_ROOM_ALIAS_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_ROOM_ALIAS_REGEX = "#[A-Z0-9._%#@=+-]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_ALIAS = Pattern.compile(MATRIX_ROOM_ALIAS_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find message ids in a string.
private val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = Pattern.compile(MATRIX_EVENT_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_EVENT_IDENTIFIER_REGEX = "\\$[A-Z0-9]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER = Pattern.compile(MATRIX_EVENT_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find message ids in a string.
private val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+"
val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = Pattern.compile(MATRIX_EVENT_IDENTIFIER_V3_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_EVENT_IDENTIFIER_V3_REGEX = "\\$[A-Z0-9/+]+"
private val PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 = Pattern.compile(MATRIX_EVENT_IDENTIFIER_V3_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find group ids in a string.
private val MATRIX_GROUP_IDENTIFIER_REGEX = "\\+[A-Z0-9=_\\-./]+$DOMAIN_REGEX"
val PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER = Pattern.compile(MATRIX_GROUP_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)
private const val MATRIX_GROUP_IDENTIFIER_REGEX = "\\+[A-Z0-9=_\\-./]+$DOMAIN_REGEX"
private val PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER = Pattern.compile(MATRIX_GROUP_IDENTIFIER_REGEX, Pattern.CASE_INSENSITIVE)

// regex pattern to find permalink with message id.
// Android does not support in URL so extract it.
private val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/"
private val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/[A-Z]{3,}/#/room/"
val SEP_REGEX = "/"
private const val PERMALINK_BASE_REGEX = "https://matrix\\.to/#/"
private const val APP_BASE_REGEX = "https://[A-Z0-9.-]+\\.[A-Z]{2,}/[A-Z]{3,}/#/room/"
const val SEP_REGEX = "/"

private val LINK_TO_ROOM_ID_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_ROOM_ID_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)

private val LINK_TO_ROOM_ALIAS_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_ROOM_ALIAS_REGEXP = PERMALINK_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)

private val LINK_TO_APP_ROOM_ID_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_APP_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_APP_ROOM_ID_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_IDENTIFIER_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID = Pattern.compile(LINK_TO_APP_ROOM_ID_REGEXP, Pattern.CASE_INSENSITIVE)

private val LINK_TO_APP_ROOM_ALIAS_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_APP_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)
private const val LINK_TO_APP_ROOM_ALIAS_REGEXP = APP_BASE_REGEX + MATRIX_ROOM_ALIAS_REGEX + SEP_REGEX + MATRIX_EVENT_IDENTIFIER_REGEX
private val PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ALIAS = Pattern.compile(LINK_TO_APP_ROOM_ALIAS_REGEXP, Pattern.CASE_INSENSITIVE)

// list of patterns to find some matrix item.
val MATRIX_PATTERNS = Arrays.asList(
val MATRIX_PATTERNS = listOf(
PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ID,
PATTERN_CONTAIN_MATRIX_TO_PERMALINK_ROOM_ALIAS,
PATTERN_CONTAIN_APP_LINK_PERMALINK_ROOM_ID,
@ -133,4 +132,4 @@ object MatrixPatterns {
fun isGroupId(str: String?): Boolean {
return str != null && PATTERN_CONTAIN_MATRIX_GROUP_IDENTIFIER.matcher(str).matches()
}
}// Cannot be instantiated
}

View File

@ -18,16 +18,16 @@ package im.vector.matrix.android.api.session.room.model.message

object MessageType {

val MSGTYPE_TEXT = "m.text"
val MSGTYPE_EMOTE = "m.emote"
val MSGTYPE_NOTICE = "m.notice"
val MSGTYPE_IMAGE = "m.image"
val MSGTYPE_AUDIO = "m.audio"
val MSGTYPE_VIDEO = "m.video"
val MSGTYPE_LOCATION = "m.location"
val MSGTYPE_FILE = "m.file"
val FORMAT_MATRIX_HTML = "org.matrix.custom.html"
const val MSGTYPE_TEXT = "m.text"
const val MSGTYPE_EMOTE = "m.emote"
const val MSGTYPE_NOTICE = "m.notice"
const val MSGTYPE_IMAGE = "m.image"
const val MSGTYPE_AUDIO = "m.audio"
const val MSGTYPE_VIDEO = "m.video"
const val MSGTYPE_LOCATION = "m.location"
const val MSGTYPE_FILE = "m.file"
const val FORMAT_MATRIX_HTML = "org.matrix.custom.html"
// Add, in local, a fake message type in order to StickerMessage can inherit Message class
// Because sticker isn't a message type but a event type without msgtype field
val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
const val MSGTYPE_STICKER_LOCAL = "org.matrix.android.sdk.sticker"
}

View File

@ -118,33 +118,4 @@ object AvatarRenderer {
.load(resolvedUrl)
.apply(RequestOptions.circleCropTransform())
}


//Based on riot-web implementation
@ColorRes
fun getColorFromUserId(sender: String): Int {
var hash = 0
var i = 0
var chr: Char
if (sender.isEmpty()) {
return R.color.username_1
}
while (i < sender.length) {
chr = sender[i]
hash = (hash shl 5) - hash + chr.toInt()
hash = hash or 0
i++
}
val cI = Math.abs(hash) % 8 + 1
return when (cI) {
1 -> R.color.username_1
2 -> R.color.username_2
3 -> R.color.username_3
4 -> R.color.username_4
5 -> R.color.username_5
6 -> R.color.username_6
7 -> R.color.username_7
else -> R.color.username_8
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.riotredesign.features.home

import androidx.annotation.ColorRes
import im.vector.riotredesign.R


@ColorRes
fun getColorFromUserId(userId: String?): Int {
if (userId.isNullOrBlank()) {
return R.color.username_1
}

var hash = 0
var i = 0
var chr: Char

while (i < userId.length) {
chr = userId[i]
hash = (hash shl 5) - hash + chr.toInt()
i++
}

return when (Math.abs(hash) % 8 + 1) {
1 -> R.color.username_1
2 -> R.color.username_2
3 -> R.color.username_3
4 -> R.color.username_4
5 -> R.color.username_5
6 -> R.color.username_6
7 -> R.color.username_7
else -> R.color.username_8
}
}

View File

@ -75,6 +75,7 @@ import im.vector.riotredesign.features.command.Command
import im.vector.riotredesign.features.home.AvatarRenderer
import im.vector.riotredesign.features.home.HomeModule
import im.vector.riotredesign.features.home.HomePermalinkHandler
import im.vector.riotredesign.features.home.getColorFromUserId
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerActions
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerView
import im.vector.riotredesign.features.home.room.detail.composer.TextComposerViewModel
@ -223,8 +224,7 @@ class RoomDetailFragment :
//switch to expanded bar
composerLayout.composerRelatedMessageTitle.apply {
text = event.senderName
setTextColor(ContextCompat.getColor(requireContext(), AvatarRenderer.getColorFromUserId(event.root.sender
?: "")))
setTextColor(ContextCompat.getColor(requireContext(), getColorFromUserId(event.root.sender)))
}

//TODO this is used at several places, find way to refactor?

View File

@ -16,6 +16,8 @@
package im.vector.riotredesign.features.home.room.detail.timeline.action

import android.os.Bundle
import android.os.Parcelable
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.MvRxView
import com.airbnb.mvrx.MvRxViewModelStore
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
@ -51,6 +53,10 @@ abstract class BaseMvRxBottomSheetDialog : BottomSheetDialogFragment(), MvRxView
// subscribe to a ViewModel.
postInvalidate()
}

protected fun setArguments(args: Parcelable? = null) {
arguments = args?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
}
}

private const val PERSISTED_VIEW_ID_KEY = "mvrx:bottomsheet_persisted_view_id"

View File

@ -144,15 +144,15 @@ class MessageActionsBottomSheet : BaseMvRxBottomSheetDialog() {

companion object {
fun newInstance(roomId: String, informationData: MessageInformationData): MessageActionsBottomSheet {
val args = Bundle()
val parcelableArgs = ParcelableArgs(
informationData.eventId,
roomId,
informationData
)
args.putParcelable(MvRx.KEY_ARG, parcelableArgs)
return MessageActionsBottomSheet().apply { arguments = args }

return MessageActionsBottomSheet().apply {
setArguments(
ParcelableArgs(
informationData.eventId,
roomId,
informationData
)
)
}
}
}
}

View File

@ -41,6 +41,7 @@ import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.core.utils.DebouncedClickListener
import im.vector.riotredesign.features.home.AvatarRenderer
import im.vector.riotredesign.features.home.getColorFromUserId
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
@ -79,7 +80,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
val avatarUrl = event.senderAvatar
val memberName = event.senderName ?: event.root.sender ?: ""
val formattedMemberName = span(memberName) {
textColor = colorProvider.getColor(AvatarRenderer.getColorFromUserId(event.root.sender
textColor = colorProvider.getColor(getColorFromUserId(event.root.sender
?: ""))
}
val hasBeenEdited = event.annotations?.editSummary != null
@ -135,7 +136,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
}

private fun buildAudioMessageItem(messageContent: MessageAudioContent, informationData: MessageInformationData,
private fun buildAudioMessageItem(messageContent: MessageAudioContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageFileItem? {
return MessageFileItem_()
.informationData(informationData)
@ -164,7 +166,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
}

private fun buildFileMessageItem(messageContent: MessageFileContent, informationData: MessageInformationData,
private fun buildFileMessageItem(messageContent: MessageFileContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageFileItem? {
return MessageFileItem_()
.informationData(informationData)
@ -198,7 +201,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return DefaultItem_().text(text)
}

private fun buildImageMessageItem(messageContent: MessageImageContent, informationData: MessageInformationData,
private fun buildImageMessageItem(messageContent: MessageImageContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageImageVideoItem? {

val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
@ -233,14 +237,14 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(informationData, messageContent, view)
}))

.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(informationData, messageContent, view)
?: false
}
}

private fun buildVideoMessageItem(messageContent: MessageVideoContent, informationData: MessageInformationData,
private fun buildVideoMessageItem(messageContent: MessageVideoContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageImageVideoItem? {

val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
@ -283,7 +287,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
}

private fun buildTextMessageItem(sendState: SendState, messageContent: MessageTextContent,
private fun buildTextMessageItem(sendState: SendState,
messageContent: MessageTextContent,
informationData: MessageInformationData,
hasBeenEdited: Boolean,
editSummary: EditAggregatedSummary?,
@ -335,6 +340,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
editSummary: EditAggregatedSummary?): SpannableStringBuilder {
val spannable = SpannableStringBuilder()
spannable.append(linkifiedBody)
// TODO i18n
val editedSuffix = "(edited)"
spannable.append(" ").append(editedSuffix)
val color = colorProvider.getColorFromAttribute(R.attr.vctr_list_header_secondary_text_color)
@ -362,7 +368,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return spannable
}

private fun buildNoticeMessageItem(messageContent: MessageNoticeContent, informationData: MessageInformationData,
private fun buildNoticeMessageItem(messageContent: MessageNoticeContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageTextItem? {

val message = messageContent.body.let {
@ -395,7 +402,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
}

private fun buildEmoteMessageItem(messageContent: MessageEmoteContent, informationData: MessageInformationData,
private fun buildEmoteMessageItem(messageContent: MessageEmoteContent,
informationData: MessageInformationData,
hasBeenEdited: Boolean,
editSummary: EditAggregatedSummary?,
callback: TimelineEventController.Callback?): MessageTextItem? {
@ -433,7 +441,8 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
}
}

private fun buildRedactedItem(informationData: MessageInformationData, callback: TimelineEventController.Callback?): RedactedMessageItem? {
private fun buildRedactedItem(informationData: MessageInformationData,
callback: TimelineEventController.Callback?): RedactedMessageItem? {
return RedactedMessageItem_()
.informationData(informationData)
.avatarClickListener(
@ -456,32 +465,4 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
VectorLinkify.addLinks(spannable, true)
return spannable
}

//Based on riot-web implementation
@ColorRes
private fun getColorFor(sender: String): Int {
var hash = 0
var i = 0
var chr: Char
if (sender.isEmpty()) {
return R.color.username_1
}
while (i < sender.length) {
chr = sender[i]
hash = (hash shl 5) - hash + chr.toInt()
hash = hash or 0
i++
}
val cI = Math.abs(hash) % 8 + 1
return when (cI) {
1 -> R.color.username_1
2 -> R.color.username_2
3 -> R.color.username_3
4 -> R.color.username_4
5 -> R.color.username_5
6 -> R.color.username_6
7 -> R.color.username_7
else -> R.color.username_8
}
}
}

View File

@ -21,6 +21,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.EmptyItem_
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import timber.log.Timber

class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
private val noticeItemFactory: NoticeItemFactory,
@ -32,8 +33,10 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,

val computedModel = try {
when (event.root.type) {
// Message
EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, callback)

// State and call
EventType.STATE_ROOM_NAME,
EventType.STATE_ROOM_TOPIC,
EventType.STATE_ROOM_MEMBER,
@ -42,12 +45,16 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
EventType.CALL_HANGUP,
EventType.CALL_ANSWER -> noticeItemFactory.create(event)

// Unhandled event types (yet)
EventType.ENCRYPTED,
EventType.ENCRYPTION,
EventType.STATE_ROOM_THIRD_PARTY_INVITE,
EventType.STICKER,
EventType.STATE_ROOM_CREATE -> defaultItemFactory.create(event)
else -> null
else -> {
Timber.w("Ignored event (type: ${event.root.type}")
null
}
}
} catch (e: Exception) {
defaultItemFactory.create(event, e)

View File

@ -40,6 +40,6 @@ abstract class DefaultItem : BaseEventItem<DefaultItem.Holder>() {
}

companion object {
private val STUB_ID = R.id.messageContentDefaultStub
private const val STUB_ID = R.id.messageContentDefaultStub
}
}

View File

@ -57,6 +57,6 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
}

companion object {
private val STUB_ID = R.id.messageContentNoticeStub
private const val STUB_ID = R.id.messageContentNoticeStub
}
}