Merge pull request #187 from vector-im/feature/issues_fix

Add a few feature
This commit is contained in:
Benoit Marty 2019-06-18 16:03:36 +02:00 committed by GitHub
commit b1f5b3ad96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 224 additions and 227 deletions

View File

@ -19,7 +19,6 @@ package im.vector.matrix.android.api.session.room.timeline
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary import im.vector.matrix.android.api.session.room.model.EventAnnotationsSummary
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState


/** /**
@ -63,7 +62,8 @@ data class TimelineEvent(
return metadata[key] as T? return metadata[key] as T?
} }


fun isEncrypted() : Boolean { fun isEncrypted(): Boolean {
return EventType.ENCRYPTED == root.getClearType() // warning: Do not use getClearType here
return EventType.ENCRYPTED == root.type
} }
} }

View File

@ -332,7 +332,7 @@ abstract class VectorBaseActivity : BaseMvRxActivity() {
open fun getMenuRes() = -1 open fun getMenuRes() = -1


@AttrRes @AttrRes
open fun getMenuTint() = R.attr.vctr_icon_tint_on_dark_action_bar_color open fun getMenuTint() = R.attr.vctr_icon_tint_on_light_action_bar_color


/** /**
* Return a object containing other themes for this activity * Return a object containing other themes for this activity

View File

@ -29,6 +29,7 @@ import im.vector.riotredesign.features.home.room.detail.timeline.factory.*
import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter import im.vector.riotredesign.features.home.room.detail.timeline.format.NoticeEventFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
import im.vector.riotredesign.features.home.room.list.RoomSummaryController import im.vector.riotredesign.features.home.room.list.RoomSummaryController
import im.vector.riotredesign.features.html.EventHtmlRenderer import im.vector.riotredesign.features.html.EventHtmlRenderer
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
@ -69,15 +70,22 @@ class HomeModule {
val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get()) val eventHtmlRenderer = EventHtmlRenderer(GlideApp.with(fragment), fragment.requireContext(), get())
val noticeEventFormatter = get<NoticeEventFormatter>(parameters = { parametersOf(fragment) }) val noticeEventFormatter = get<NoticeEventFormatter>(parameters = { parametersOf(fragment) })
val timelineMediaSizeProvider = TimelineMediaSizeProvider() val timelineMediaSizeProvider = TimelineMediaSizeProvider()
val messageItemFactory = MessageItemFactory(colorProvider, timelineMediaSizeProvider, val messageInformationDataFactory = MessageInformationDataFactory(timelineDateFormatter, colorProvider)
timelineDateFormatter, eventHtmlRenderer, get(), get()) val messageItemFactory = MessageItemFactory(colorProvider,
timelineMediaSizeProvider,
eventHtmlRenderer,
get(),
messageInformationDataFactory,
get())

val encryptedItemFactory = EncryptedItemFactory(messageInformationDataFactory, colorProvider, get())


val timelineItemFactory = TimelineItemFactory( val timelineItemFactory = TimelineItemFactory(
messageItemFactory = messageItemFactory, messageItemFactory = messageItemFactory,
noticeItemFactory = NoticeItemFactory(noticeEventFormatter), noticeItemFactory = NoticeItemFactory(noticeEventFormatter),
defaultItemFactory = DefaultItemFactory(), defaultItemFactory = DefaultItemFactory(),
encryptionItemFactory = EncryptionItemFactory(get()), encryptionItemFactory = EncryptionItemFactory(get()),
encryptedItemFactory = EncryptedItemFactory(get()) encryptedItemFactory = encryptedItemFactory
) )
TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider) TimelineEventController(timelineDateFormatter, timelineItemFactory, timelineMediaSizeProvider)
} }

View File

@ -542,6 +542,10 @@ class RoomDetailFragment :
roomDetailViewModel.process(RoomDetailActions.EventDisplayed(event)) roomDetailViewModel.process(RoomDetailActions.EventDisplayed(event))
} }


override fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View) {
vectorBaseActivity.notImplemented("encrypted message click")
}

override fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View) { override fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View) {
val intent = ImageMediaViewerActivity.newIntent(vectorBaseActivity, mediaData) val intent = ImageMediaViewerActivity.newIntent(vectorBaseActivity, mediaData)
startActivity(intent) startActivity(intent)
@ -576,7 +580,7 @@ class RoomDetailFragment :
} }


override fun onAvatarClicked(informationData: MessageInformationData) { override fun onAvatarClicked(informationData: MessageInformationData) {
vectorBaseActivity.notImplemented() vectorBaseActivity.notImplemented("Click on user avatar")
} }


@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")

View File

@ -49,6 +49,7 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
interface Callback : ReactionPillCallback, AvatarCallback, BaseCallback { interface Callback : ReactionPillCallback, AvatarCallback, BaseCallback {
fun onEventVisible(event: TimelineEvent) fun onEventVisible(event: TimelineEvent)
fun onUrlClicked(url: String) fun onUrlClicked(url: String)
fun onEncryptedMessageClicked(informationData: MessageInformationData, view: View)
fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View) fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View)
fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View) fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View)
fun onFileMessageClicked(messageFileContent: MessageFileContent) fun onFileMessageClicked(messageFileContent: MessageFileContent)

View File

@ -123,7 +123,11 @@ class MessageMenuViewModel(initialState: MessageMenuState) : VectorViewModel<Mes


this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.toContent()).toString(4))) this.add(SimpleAction(VIEW_SOURCE, R.string.view_source, R.drawable.ic_view_source, JSONObject(event.root.toContent()).toString(4)))
if (event.isEncrypted()) { if (event.isEncrypted()) {
this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, parcel.eventId)) val decryptedContent = event.root.mClearEvent?.toContent()?.let {
JSONObject(it).toString(4)
} ?: viewModelContext.activity.getString(R.string.encryption_information_decryption_error)

this.add(SimpleAction(VIEW_DECRYPTED_SOURCE, R.string.view_decrypted_source, R.drawable.ic_view_source, decryptedContent))
} }
this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, parcel.eventId)) this.add(SimpleAction(ACTION_COPY_PERMALINK, R.string.permalink, R.drawable.ic_permalink, parcel.eventId))



View File

@ -16,28 +16,33 @@


package im.vector.riotredesign.features.home.room.detail.timeline.factory package im.vector.riotredesign.features.home.room.detail.timeline.factory


import android.graphics.Typeface import android.view.View
import android.text.Spannable
import android.text.SpannableString
import android.text.style.StyleSpan
import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar import im.vector.riotredesign.core.utils.DebouncedClickListener
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem_ import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
import me.gujun.android.span.span


// This class handles timeline event who haven't been successfully decrypted // This class handles timeline events who haven't been successfully decrypted
class EncryptedItemFactory(private val stringProvider: StringProvider) { class EncryptedItemFactory(private val messageInformationDataFactory: MessageInformationDataFactory,
private val colorProvider: ColorProvider,
private val stringProvider: StringProvider) {

fun create(event: TimelineEvent,
nextEvent: TimelineEvent?,
callback: TimelineEventController.Callback?): VectorEpoxyModel<*>? {
event.root.eventId ?: return null


fun create(timelineEvent: TimelineEvent): VectorEpoxyModel<*>? {
return when { return when {
EventType.ENCRYPTED == timelineEvent.root.getClearType() -> { EventType.ENCRYPTED == event.root.getClearType() -> {
val cryptoError = timelineEvent.root.mCryptoError val cryptoError = event.root.mCryptoError
val errorDescription = val errorDescription =
if (cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) { if (cryptoError?.code == MXCryptoError.UNKNOWN_INBOUND_SESSION_ID_ERROR_CODE) {
stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id) stringProvider.getString(R.string.notice_crypto_error_unkwown_inbound_session_id)
@ -46,22 +51,28 @@ class EncryptedItemFactory(private val stringProvider: StringProvider) {
} }


val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription) val message = stringProvider.getString(R.string.notice_crypto_unable_to_decrypt, errorDescription)
val spannableStr = SpannableString(message) val spannableStr = span(message) {
spannableStr.setSpan(StyleSpan(Typeface.ITALIC), 0, message.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) textStyle = "italic"
textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
}

// TODO This is not correct format for error, change it // TODO This is not correct format for error, change it
val informationData = MessageInformationData( val informationData = messageInformationDataFactory.create(event, nextEvent)
eventId = timelineEvent.root.eventId ?: "?",
senderId = timelineEvent.root.sender ?: "", return MessageTextItem_()
sendState = timelineEvent.sendState, .message(spannableStr)
avatarUrl = timelineEvent.senderAvatar(),
memberName = timelineEvent.senderName(),
showInformation = false
)
return NoticeItem_()
.noticeText(spannableStr)
.informationData(informationData) .informationData(informationData)
.avatarCallback(callback)
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEncryptedMessageClicked(informationData, view)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(informationData, null, view)
?: false
}
} }
else -> null else -> null
} }
} }
} }

View File

@ -23,6 +23,7 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent import im.vector.matrix.android.internal.crypto.model.event.EncryptionEventContent
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderName
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
@ -31,7 +32,8 @@ import im.vector.riotredesign.features.home.room.detail.timeline.item.NoticeItem


class EncryptionItemFactory(private val stringProvider: StringProvider) { class EncryptionItemFactory(private val stringProvider: StringProvider) {


fun create(event: TimelineEvent): NoticeItem? { fun create(event: TimelineEvent,
callback: TimelineEventController.BaseCallback?): NoticeItem? {
val text = buildNoticeText(event.root, event.senderName) ?: return null val text = buildNoticeText(event.root, event.senderName) ?: return null
val informationData = MessageInformationData( val informationData = MessageInformationData(
eventId = event.root.eventId ?: "?", eventId = event.root.eventId ?: "?",
@ -44,6 +46,7 @@ class EncryptionItemFactory(private val stringProvider: StringProvider) {
return NoticeItem_() return NoticeItem_()
.noticeText(text) .noticeText(text)
.informationData(informationData) .informationData(informationData)
.baseCallback(callback)
} }


private fun buildNoticeText(event: Event, senderName: String?): CharSequence? { private fun buildNoticeText(event: Event, senderName: String?): CharSequence? {
@ -52,7 +55,7 @@ class EncryptionItemFactory(private val stringProvider: StringProvider) {
val content = event.content.toModel<EncryptionEventContent>() ?: return null val content = event.content.toModel<EncryptionEventContent>() ?: return null
stringProvider.getString(R.string.notice_end_to_end, senderName, content.algorithm) stringProvider.getString(R.string.notice_end_to_end, senderName, content.algorithm)
} }
else -> null else -> null
} }


} }

View File

@ -25,7 +25,6 @@ import android.text.style.RelativeSizeSpan
import android.view.View import android.view.View
import im.vector.matrix.android.api.permalinks.MatrixLinkify import im.vector.matrix.android.api.permalinks.MatrixLinkify
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.RelationType import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
@ -35,16 +34,14 @@ import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.EmojiCompatFontProvider import im.vector.riotredesign.EmojiCompatFontProvider
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.linkify.VectorLinkify import im.vector.riotredesign.core.linkify.VectorLinkify
import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.core.resources.StringProvider import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.core.utils.DebouncedClickListener import im.vector.riotredesign.core.utils.DebouncedClickListener
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.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.* import im.vector.riotredesign.features.home.room.detail.timeline.item.*
import im.vector.riotredesign.features.home.room.detail.timeline.util.MessageInformationDataFactory
import im.vector.riotredesign.features.html.EventHtmlRenderer import im.vector.riotredesign.features.html.EventHtmlRenderer
import im.vector.riotredesign.features.media.ImageContentRenderer import im.vector.riotredesign.features.media.ImageContentRenderer
import im.vector.riotredesign.features.media.VideoContentRenderer import im.vector.riotredesign.features.media.VideoContentRenderer
@ -52,50 +49,18 @@ import me.gujun.android.span.span


class MessageItemFactory(private val colorProvider: ColorProvider, class MessageItemFactory(private val colorProvider: ColorProvider,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider, private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val timelineDateFormatter: TimelineDateFormatter,
private val htmlRenderer: EventHtmlRenderer, private val htmlRenderer: EventHtmlRenderer,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val messageInformationDataFactory: MessageInformationDataFactory,
private val emojiCompatFontProvider: EmojiCompatFontProvider) { private val emojiCompatFontProvider: EmojiCompatFontProvider) {


fun create(event: TimelineEvent, fun create(event: TimelineEvent,
nextEvent: TimelineEvent?, nextEvent: TimelineEvent?,
callback: TimelineEventController.Callback? callback: TimelineEventController.Callback?
): VectorEpoxyModel<*>? { ): VectorEpoxyModel<*>? {
event.root.eventId ?: return null


val eventId = event.root.eventId ?: return null val informationData = messageInformationDataFactory.create(event, nextEvent)

val date = event.root.localDateTime()
val nextDate = nextEvent?.root?.localDateTime()
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60))
?: false

val showInformation = addDaySeparator
|| event.senderAvatar != nextEvent?.senderAvatar
|| event.senderName != nextEvent?.senderName
|| nextEvent?.root?.getClearType() != EventType.MESSAGE
|| isNextMessageReceivedMoreThanOneHourAgo

val time = timelineDateFormatter.formatMessageHour(date)
val avatarUrl = event.senderAvatar
val memberName = event.senderName ?: event.root.sender ?: ""
val formattedMemberName = span(memberName) {
textColor = colorProvider.getColor(getColorFromUserId(event.root.sender
?: ""))
}
val hasBeenEdited = event.annotations?.editSummary != null
val informationData = MessageInformationData(eventId = eventId,
senderId = event.root.sender ?: "",
sendState = event.sendState,
time = time,
avatarUrl = avatarUrl,
memberName = formattedMemberName,
showInformation = showInformation,
orderedReactionList = event.annotations?.reactionsSummary?.map {
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
},
hasBeenEdited = hasBeenEdited
)


if (event.root.unsignedData?.redactedEvent != null) { if (event.root.unsignedData?.redactedEvent != null) {
//message is redacted //message is redacted
@ -117,13 +82,11 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return when (messageContent) { return when (messageContent) {
is MessageEmoteContent -> buildEmoteMessageItem(messageContent, is MessageEmoteContent -> buildEmoteMessageItem(messageContent,
informationData, informationData,
hasBeenEdited,
event.annotations?.editSummary, event.annotations?.editSummary,
callback) callback)
is MessageTextContent -> buildTextMessageItem(event.sendState, is MessageTextContent -> buildTextMessageItem(event.sendState,
messageContent, messageContent,
informationData, informationData,
hasBeenEdited,
event.annotations?.editSummary, event.annotations?.editSummary,
callback callback
) )
@ -266,7 +229,6 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
private fun buildTextMessageItem(sendState: SendState, private fun buildTextMessageItem(sendState: SendState,
messageContent: MessageTextContent, messageContent: MessageTextContent,
informationData: MessageInformationData, informationData: MessageInformationData,
hasBeenEdited: Boolean,
editSummary: EditAggregatedSummary?, editSummary: EditAggregatedSummary?,
callback: TimelineEventController.Callback?): MessageTextItem? { callback: TimelineEventController.Callback?): MessageTextItem? {


@ -278,7 +240,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,


return MessageTextItem_() return MessageTextItem_()
.apply { .apply {
if (hasBeenEdited) { if (informationData.hasBeenEdited) {
val spannable = annotateWithEdited(linkifiedBody, callback, informationData, editSummary) val spannable = annotateWithEdited(linkifiedBody, callback, informationData, editSummary)
message(spannable) message(spannable)
} else { } else {
@ -368,7 +330,6 @@ class MessageItemFactory(private val colorProvider: ColorProvider,


private fun buildEmoteMessageItem(messageContent: MessageEmoteContent, private fun buildEmoteMessageItem(messageContent: MessageEmoteContent,
informationData: MessageInformationData, informationData: MessageInformationData,
hasBeenEdited: Boolean,
editSummary: EditAggregatedSummary?, editSummary: EditAggregatedSummary?,
callback: TimelineEventController.Callback?): MessageTextItem? { callback: TimelineEventController.Callback?): MessageTextItem? {


@ -378,7 +339,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
} }
return MessageTextItem_() return MessageTextItem_()
.apply { .apply {
if (hasBeenEdited) { if (informationData.hasBeenEdited) {
val spannable = annotateWithEdited(message, callback, informationData, editSummary) val spannable = annotateWithEdited(message, callback, informationData, editSummary)
message(spannable) message(spannable)
} else { } else {

View File

@ -52,8 +52,8 @@ class TimelineItemFactory(private val messageItemFactory: MessageItemFactory,
EventType.CALL_ANSWER -> noticeItemFactory.create(event, callback) EventType.CALL_ANSWER -> noticeItemFactory.create(event, callback)


// Crypto // Crypto
EventType.ENCRYPTION -> encryptionItemFactory.create(event) EventType.ENCRYPTION -> encryptionItemFactory.create(event, callback)
EventType.ENCRYPTED -> encryptedItemFactory.create(event) EventType.ENCRYPTED -> encryptedItemFactory.create(event, nextEvent, callback)


// Unhandled event types (yet) // Unhandled event types (yet)
EventType.STATE_ROOM_THIRD_PARTY_INVITE, EventType.STATE_ROOM_THIRD_PARTY_INVITE,

View File

@ -38,11 +38,9 @@ abstract class NoticeItem : BaseEventItem<NoticeItem.Holder>() {
var baseCallback: TimelineEventController.BaseCallback? = null var baseCallback: TimelineEventController.BaseCallback? = null


private var longClickListener = View.OnLongClickListener { private var longClickListener = View.OnLongClickListener {
baseCallback?.onEventLongClicked(informationData, null, it) return@OnLongClickListener baseCallback?.onEventLongClicked(informationData, null, it) == true
baseCallback != null
} }



override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.noticeTextView.text = noticeText holder.noticeTextView.text = noticeText

View File

@ -0,0 +1,77 @@
/*
* 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.room.detail.timeline.util

import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.features.home.getColorFromUserId
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.senderAvatar
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotredesign.features.home.room.detail.timeline.item.ReactionInfoData
import me.gujun.android.span.span

/**
* This class compute if data of an event (such has avatar, display name, ...) should be displayed, depending on the previous event in the timeline
*/
class MessageInformationDataFactory(private val timelineDateFormatter: TimelineDateFormatter,
private val colorProvider: ColorProvider) {

fun create(event: TimelineEvent, nextEvent: TimelineEvent?): MessageInformationData {
// Non nullability has been tested before
val eventId = event.root.eventId!!

val date = event.root.localDateTime()

val nextDate = nextEvent?.root?.localDateTime()
val addDaySeparator = date.toLocalDate() != nextDate?.toLocalDate()
val isNextMessageReceivedMoreThanOneHourAgo = nextDate?.isBefore(date.minusMinutes(60))
?: false
val showInformation =
addDaySeparator
|| event.senderAvatar != nextEvent?.senderAvatar
|| event.senderName != nextEvent?.senderName
|| (nextEvent?.root?.getClearType() != EventType.MESSAGE && nextEvent?.root?.getClearType() != EventType.ENCRYPTED)
|| isNextMessageReceivedMoreThanOneHourAgo

val time = timelineDateFormatter.formatMessageHour(date)
val avatarUrl = event.senderAvatar()
val memberName = event.senderName ?: event.root.sender ?: ""
val formattedMemberName = span(memberName) {
textColor = colorProvider.getColor(getColorFromUserId(event.root.sender
?: ""))
}

val hasBeenEdited = event.annotations?.editSummary != null

return MessageInformationData(
eventId = eventId,
senderId = event.root.sender ?: "",
sendState = event.sendState,
time = time,
avatarUrl = avatarUrl,
memberName = formattedMemberName,
showInformation = showInformation,
orderedReactionList = event.annotations?.reactionsSummary?.map {
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
},
hasBeenEdited = hasBeenEdited
)
}
}

View File

@ -19,10 +19,8 @@ package im.vector.riotredesign.features.rageshake
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.widget.Toast
import android.widget.*
import androidx.core.view.isVisible import androidx.core.view.isVisible
import butterknife.BindView
import butterknife.OnCheckedChanged import butterknife.OnCheckedChanged
import butterknife.OnTextChanged import butterknife.OnTextChanged
import im.vector.riotredesign.R import im.vector.riotredesign.R
@ -35,48 +33,17 @@ import timber.log.Timber
*/ */
class BugReportActivity : VectorBaseActivity() { class BugReportActivity : VectorBaseActivity() {


/* ==========================================================================================
* UI
* ========================================================================================== */

@BindView(R.id.bug_report_edit_text)
lateinit var mBugReportText: EditText

@BindView(R.id.bug_report_button_include_logs)
lateinit var mIncludeLogsButton: CheckBox

@BindView(R.id.bug_report_button_include_crash_logs)
lateinit var mIncludeCrashLogsButton: CheckBox

@BindView(R.id.bug_report_button_include_screenshot)
lateinit var mIncludeScreenShotButton: CheckBox

@BindView(R.id.bug_report_screenshot_preview)
lateinit var mScreenShotPreview: ImageView

@BindView(R.id.bug_report_progress_view)
lateinit var mProgressBar: ProgressBar

@BindView(R.id.bug_report_progress_text_view)
lateinit var mProgressTextView: TextView

@BindView(R.id.bug_report_scrollview)
lateinit var mScrollView: View

@BindView(R.id.bug_report_mask_view)
lateinit var mMaskView: View

override fun getLayoutRes() = R.layout.activity_bug_report override fun getLayoutRes() = R.layout.activity_bug_report


override fun initUiAndData() { override fun initUiAndData() {
configureToolbar(bugReportToolbar) configureToolbar(bugReportToolbar)


if (BugReporter.screenshot != null) { if (BugReporter.screenshot != null) {
mScreenShotPreview.setImageBitmap(BugReporter.screenshot) bug_report_screenshot_preview.setImageBitmap(BugReporter.screenshot)
} else { } else {
mScreenShotPreview.isVisible = false bug_report_screenshot_preview.isVisible = false
mIncludeScreenShotButton.isChecked = false bug_report_button_include_screenshot.isChecked = false
mIncludeScreenShotButton.isEnabled = false bug_report_button_include_screenshot.isEnabled = false
} }
} }


@ -84,8 +51,8 @@ class BugReportActivity : VectorBaseActivity() {


override fun onPrepareOptionsMenu(menu: Menu): Boolean { override fun onPrepareOptionsMenu(menu: Menu): Boolean {
menu.findItem(R.id.ic_action_send_bug_report)?.let { menu.findItem(R.id.ic_action_send_bug_report)?.let {
val isValid = mBugReportText.text.toString().trim().length > 10 val isValid = bug_report_edit_text.text.toString().trim().length > 10
&& !mMaskView.isVisible && !bug_report_mask_view.isVisible


it.isEnabled = isValid it.isEnabled = isValid
it.icon.alpha = if (isValid) 255 else 100 it.icon.alpha = if (isValid) 255 else 100
@ -109,22 +76,22 @@ class BugReportActivity : VectorBaseActivity() {
* Send the bug report * Send the bug report
*/ */
private fun sendBugReport() { private fun sendBugReport() {
mScrollView.alpha = 0.3f bug_report_scrollview.alpha = 0.3f
mMaskView.isVisible = true bug_report_mask_view.isVisible = true


invalidateOptionsMenu() invalidateOptionsMenu()


mProgressTextView.isVisible = true bug_report_progress_text_view.isVisible = true
mProgressTextView.text = getString(R.string.send_bug_report_progress, 0.toString() + "") bug_report_progress_text_view.text = getString(R.string.send_bug_report_progress, "0")


mProgressBar.isVisible = true bug_report_progress_view.isVisible = true
mProgressBar.progress = 0 bug_report_progress_view.progress = 0


BugReporter.sendBugReport(this, BugReporter.sendBugReport(this,
mIncludeLogsButton.isChecked, bug_report_button_include_logs.isChecked,
mIncludeCrashLogsButton.isChecked, bug_report_button_include_crash_logs.isChecked,
mIncludeScreenShotButton.isChecked, bug_report_button_include_screenshot.isChecked,
mBugReportText.text.toString(), bug_report_edit_text.text.toString(),
object : BugReporter.IMXBugReportListener { object : BugReporter.IMXBugReportListener {
override fun onUploadFailed(reason: String?) { override fun onUploadFailed(reason: String?) {
try { try {
@ -136,10 +103,10 @@ class BugReportActivity : VectorBaseActivity() {
Timber.e(e, "## onUploadFailed() : failed to display the toast " + e.message) Timber.e(e, "## onUploadFailed() : failed to display the toast " + e.message)
} }


mMaskView.isVisible = false bug_report_mask_view.isVisible = false
mProgressBar.isVisible = false bug_report_progress_view.isVisible = false
mProgressTextView.isVisible = false bug_report_progress_text_view.isVisible = false
mScrollView.alpha = 1.0f bug_report_scrollview.alpha = 1.0f


invalidateOptionsMenu() invalidateOptionsMenu()
} }
@ -149,17 +116,10 @@ class BugReportActivity : VectorBaseActivity() {
} }


override fun onProgress(progress: Int) { override fun onProgress(progress: Int) {
var progress = progress val myProgress = progress.coerceIn(0, 100)
if (progress > 100) {
Timber.e("## onProgress() : progress > 100")
progress = 100
} else if (progress < 0) {
Timber.e("## onProgress() : progress < 0")
progress = 0
}


mProgressBar.progress = progress bug_report_progress_view.progress = myProgress
mProgressTextView.text = getString(R.string.send_bug_report_progress, progress.toString() + "") bug_report_progress_text_view.text = getString(R.string.send_bug_report_progress, "$myProgress")
} }


override fun onUploadSucceed() { override fun onUploadSucceed() {
@ -174,7 +134,6 @@ class BugReportActivity : VectorBaseActivity() {
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## onUploadSucceed() : failed to dismiss the dialog " + e.message) Timber.e(e, "## onUploadSucceed() : failed to dismiss the dialog " + e.message)
} }

} }
}) })
} }
@ -190,7 +149,7 @@ class BugReportActivity : VectorBaseActivity() {


@OnCheckedChanged(R.id.bug_report_button_include_screenshot) @OnCheckedChanged(R.id.bug_report_button_include_screenshot)
internal fun onSendScreenshotChanged() { internal fun onSendScreenshotChanged() {
mScreenShotPreview.isVisible = mIncludeScreenShotButton.isChecked && BugReporter.screenshot != null bug_report_screenshot_preview.isVisible = bug_report_button_include_screenshot.isChecked && BugReporter.screenshot != null
} }


override fun onBackPressed() { override fun onBackPressed() {
@ -199,12 +158,4 @@ class BugReportActivity : VectorBaseActivity() {


super.onBackPressed() super.onBackPressed()
} }

/* ==========================================================================================
* Companion
* ========================================================================================== */

companion object {
private val LOG_TAG = BugReportActivity::class.java.simpleName
}
} }

View File

@ -23,6 +23,7 @@ import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import butterknife.BindView import butterknife.BindView
import butterknife.ButterKnife import butterknife.ButterKnife
@ -67,24 +68,23 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
fun bind(test: TroubleshootTest) { fun bind(test: TroubleshootTest) {


val context = itemView.context val context = itemView.context
titleText.setTextColor(ThemeUtils.getColor(context, android.R.attr.textColorTertiary)) titleText.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_primary))
descriptionText.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_default_text_hint_color)) descriptionText.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))


when (test.status) { when (test.status) {
TroubleshootTest.TestStatus.NOT_STARTED -> { TroubleshootTest.TestStatus.NOT_STARTED -> {
titleText.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_default_text_hint_color)) titleText.setTextColor(ThemeUtils.getColor(context, R.attr.riotx_text_secondary))
descriptionText.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_default_text_hint_color))


progressBar.visibility = View.INVISIBLE progressBar.visibility = View.INVISIBLE
statusIconImage.visibility = View.VISIBLE statusIconImage.visibility = View.VISIBLE
statusIconImage.setImageResource(R.drawable.unit_test) statusIconImage.setImageResource(R.drawable.unit_test)
} }
TroubleshootTest.TestStatus.RUNNING -> { TroubleshootTest.TestStatus.RUNNING -> {
progressBar.visibility = View.VISIBLE progressBar.visibility = View.VISIBLE
statusIconImage.visibility = View.INVISIBLE statusIconImage.visibility = View.INVISIBLE


} }
TroubleshootTest.TestStatus.FAILED -> { TroubleshootTest.TestStatus.FAILED -> {
progressBar.visibility = View.INVISIBLE progressBar.visibility = View.INVISIBLE
statusIconImage.visibility = View.VISIBLE statusIconImage.visibility = View.VISIBLE
statusIconImage.setImageResource(R.drawable.unit_test_ko) statusIconImage.setImageResource(R.drawable.unit_test_ko)
@ -93,9 +93,9 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
statusIconImage.imageTintList = null statusIconImage.imageTintList = null
} }


descriptionText.setTextColor(ThemeUtils.getColor(context, R.attr.vctr_highlighted_message_text_color)) descriptionText.setTextColor(ContextCompat.getColor(context, R.color.riotx_notice))
} }
TroubleshootTest.TestStatus.SUCCESS -> { TroubleshootTest.TestStatus.SUCCESS -> {
progressBar.visibility = View.INVISIBLE progressBar.visibility = View.INVISIBLE
statusIconImage.visibility = View.VISIBLE statusIconImage.visibility = View.VISIBLE
statusIconImage.setImageResource(R.drawable.unit_test_ok) statusIconImage.setImageResource(R.drawable.unit_test_ok)

View File

@ -7,6 +7,7 @@


<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/bugReportToolbar" android:id="@+id/bugReportToolbar"
style="@style/VectorToolbarStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:elevation="4dp" /> android:elevation="4dp" />
@ -28,6 +29,7 @@
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/send_bug_report_progress" android:text="@string/send_bug_report_progress"
android:textColor="?riotx_text_primary"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />


@ -65,7 +67,8 @@
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/send_bug_report_description" /> android:text="@string/send_bug_report_description"
android:textColor="?riotx_text_primary" />


<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
style="@style/VectorTextInputLayout" style="@style/VectorTextInputLayout"
@ -96,6 +99,7 @@
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/send_bug_report_description_in_english" android:text="@string/send_bug_report_description_in_english"
android:textColor="?riotx_text_secondary"
android:textSize="12sp" /> android:textSize="12sp" />


<TextView <TextView
@ -106,67 +110,41 @@
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/send_bug_report_logs_description" /> android:text="@string/send_bug_report_logs_description"
android:textColor="?riotx_text_primary" />


<LinearLayout <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/bug_report_button_include_logs"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:checked="true"
android:text="@string/send_bug_report_include_logs" />


<CheckBox <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/bug_report_button_include_logs" android:id="@+id/bug_report_button_include_crash_logs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:text="@string/send_bug_report_include_logs" />
</LinearLayout>

<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:checked="true"
android:text="@string/send_bug_report_include_crash_logs" />


<CheckBox <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/bug_report_button_include_crash_logs" android:id="@+id/bug_report_button_include_screenshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:text="@string/send_bug_report_include_crash_logs" />
</LinearLayout>

<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:layout_marginStart="10dp"

android:layout_marginLeft="10dp"
<CheckBox android:layout_marginEnd="10dp"
android:id="@+id/bug_report_button_include_screenshot" android:layout_marginRight="10dp"
android:layout_width="wrap_content" android:checked="true"
android:layout_height="wrap_content" android:text="@string/send_bug_report_include_screenshot" />
android:checked="true" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:text="@string/send_bug_report_include_screenshot" />
</LinearLayout>


<ImageView <ImageView
android:id="@+id/bug_report_screenshot_preview" android:id="@+id/bug_report_screenshot_preview"
@ -189,5 +167,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:clickable="true" android:clickable="true"
android:focusable="true"
android:visibility="gone" /> android:visibility="gone" />
</LinearLayout> </LinearLayout>

View File

@ -2,7 +2,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context="org.matrix.vector.activity.RoomActivity"> tools:context=".features.rageshake.BugReportActivity">


<item <item
android:id="@+id/ic_action_send_bug_report" android:id="@+id/ic_action_send_bug_report"

View File

@ -135,7 +135,7 @@


<!-- icon colors --> <!-- icon colors -->
<item name="vctr_settings_icon_tint_color">@android:color/white</item> <item name="vctr_settings_icon_tint_color">@android:color/white</item>
<item name="vctr_icon_tint_on_light_action_bar_color">@android:color/white</item> <item name="vctr_icon_tint_on_light_action_bar_color">@color/riotx_accent</item>
<item name="vctr_icon_tint_on_dark_action_bar_color">@android:color/white</item> <item name="vctr_icon_tint_on_dark_action_bar_color">@android:color/white</item>


<!-- theses colours are requested a background cannot be set by an ?att on android < 5 --> <!-- theses colours are requested a background cannot be set by an ?att on android < 5 -->

View File

@ -136,7 +136,7 @@


<!-- icon colors --> <!-- icon colors -->
<item name="vctr_settings_icon_tint_color">@android:color/black</item> <item name="vctr_settings_icon_tint_color">@android:color/black</item>
<item name="vctr_icon_tint_on_light_action_bar_color">@android:color/white</item> <item name="vctr_icon_tint_on_light_action_bar_color">@color/riotx_accent</item>
<item name="vctr_icon_tint_on_dark_action_bar_color">@android:color/white</item> <item name="vctr_icon_tint_on_dark_action_bar_color">@android:color/white</item>


<!-- theses colours are requested a background cannot be set by an ?att on android < 5 --> <!-- theses colours are requested a background cannot be set by an ?att on android < 5 -->

View File

@ -94,7 +94,7 @@


<!-- icon colors --> <!-- icon colors -->
<item name="vctr_settings_icon_tint_color">@color/accent_color_status</item> <item name="vctr_settings_icon_tint_color">@color/accent_color_status</item>
<item name="vctr_icon_tint_on_light_action_bar_color">@android:color/white</item> <item name="vctr_icon_tint_on_light_action_bar_color">@color/riotx_accent</item>
<item name="vctr_icon_tint_on_dark_action_bar_color">@android:color/white</item> <item name="vctr_icon_tint_on_dark_action_bar_color">@android:color/white</item>


<!-- theses colours are requested a background cannot be set by an ?att on android < 5 --> <!-- theses colours are requested a background cannot be set by an ?att on android < 5 -->