Inject StringProvider instead of context.

This commit is contained in:
Benoit Marty 2023-05-16 12:21:06 +02:00 committed by Benoit Marty
parent 70df081840
commit ac636d9737
5 changed files with 73 additions and 69 deletions

View file

@ -40,8 +40,10 @@ dependencies {
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.uiStrings) implementation(projects.libraries.uiStrings)
implementation(projects.services.toolbox.api)
api(projects.libraries.eventformatter.api) api(projects.libraries.eventformatter.api)
testImplementation(projects.services.toolbox.impl)
testImplementation(libs.test.junit) testImplementation(libs.test.junit)
testImplementation(libs.test.robolectric) testImplementation(libs.test.robolectric)
testImplementation(libs.test.truth) testImplementation(libs.test.truth)

View file

@ -16,15 +16,14 @@
package io.element.android.libraries.eventformatter.impl package io.element.android.libraries.eventformatter.impl
import android.content.Context
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle import androidx.compose.ui.text.withStyle
import com.squareup.anvil.annotations.ContributesBinding import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
@ -49,15 +48,14 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
import io.element.android.libraries.eventformatter.api.RoomLastMessageFormatter import io.element.android.services.toolbox.api.strings.StringProvider
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import io.element.android.libraries.ui.strings.R as StringR import io.element.android.libraries.ui.strings.R as StringR
@ContributesBinding(SessionScope::class) @ContributesBinding(SessionScope::class)
class DefaultRoomLastMessageFormatter @Inject constructor( class DefaultRoomLastMessageFormatter @Inject constructor(
// TODO replace with StringProvider private val sp: StringProvider,
@ApplicationContext private val context: Context,
private val matrixClient: MatrixClient, private val matrixClient: MatrixClient,
) : RoomLastMessageFormatter { ) : RoomLastMessageFormatter {
@ -67,7 +65,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
return when (val content = event.content) { return when (val content = event.content) {
is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom) is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom)
RedactedContent -> { RedactedContent -> {
val message = context.getString(StringR.string.common_message_removed) val message = sp.getString(StringR.string.common_message_removed)
if (!isDmRoom) { if (!isDmRoom) {
prefix(message, senderDisplayName) prefix(message, senderDisplayName)
} else { } else {
@ -78,7 +76,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
content.body content.body
} }
is UnableToDecryptContent -> { is UnableToDecryptContent -> {
val message = context.getString(StringR.string.common_decryption_error) val message = sp.getString(StringR.string.common_decryption_error)
if (!isDmRoom) { if (!isDmRoom) {
prefix(message, senderDisplayName) prefix(message, senderDisplayName)
} else { } else {
@ -95,7 +93,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
processRoomStateChange(content, senderDisplayName, isOutgoing) processRoomStateChange(content, senderDisplayName, isOutgoing)
} }
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> { is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
prefixIfNeeded(context.getString(StringR.string.common_unsupported_event), senderDisplayName, isDmRoom) prefixIfNeeded(sp.getString(StringR.string.common_unsupported_event), senderDisplayName, isDmRoom)
} }
} }
} }
@ -112,19 +110,19 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
messageType.body messageType.body
} }
is VideoMessageType -> { is VideoMessageType -> {
context.getString(StringR.string.common_video) sp.getString(StringR.string.common_video)
} }
is ImageMessageType -> { is ImageMessageType -> {
context.getString(StringR.string.common_image) sp.getString(StringR.string.common_image)
} }
is FileMessageType -> { is FileMessageType -> {
context.getString(StringR.string.common_file) sp.getString(StringR.string.common_file)
} }
is AudioMessageType -> { is AudioMessageType -> {
context.getString(StringR.string.common_audio) sp.getString(StringR.string.common_audio)
} }
UnknownMessageType -> { UnknownMessageType -> {
context.getString(StringR.string.common_unsupported_event) sp.getString(StringR.string.common_unsupported_event)
} }
is NoticeMessageType -> { is NoticeMessageType -> {
messageType.body messageType.body
@ -138,73 +136,73 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
val memberIsYou = userId == matrixClient.sessionId val memberIsYou = userId == matrixClient.sessionId
return when (val change = membershipContent.change) { return when (val change = membershipContent.change) {
MembershipChange.JOINED -> if (memberIsYou) { MembershipChange.JOINED -> if (memberIsYou) {
context.getString(R.string.state_event_room_join_by_you) sp.getString(R.string.state_event_room_join_by_you)
} else { } else {
context.getString(R.string.state_event_room_join, userId.value) sp.getString(R.string.state_event_room_join, userId.value)
} }
MembershipChange.LEFT -> if (memberIsYou) { MembershipChange.LEFT -> if (memberIsYou) {
context.getString(R.string.state_event_room_leave_by_you) sp.getString(R.string.state_event_room_leave_by_you)
} else { } else {
context.getString(R.string.state_event_room_leave, userId.value) sp.getString(R.string.state_event_room_leave, userId.value)
} }
MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) { MembershipChange.BANNED, MembershipChange.KICKED_AND_BANNED -> if (senderIsYou) {
context.getString(R.string.state_event_room_ban_by_you, userId.value) sp.getString(R.string.state_event_room_ban_by_you, userId.value)
} else { } else {
context.getString(R.string.state_event_room_ban, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_ban, senderDisplayName, userId.value)
} }
MembershipChange.UNBANNED -> if (senderIsYou) { MembershipChange.UNBANNED -> if (senderIsYou) {
context.getString(R.string.state_event_room_unban_by_you, userId.value) sp.getString(R.string.state_event_room_unban_by_you, userId.value)
} else { } else {
context.getString(R.string.state_event_room_unban, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_unban, senderDisplayName, userId.value)
} }
MembershipChange.KICKED -> if (senderIsYou) { MembershipChange.KICKED -> if (senderIsYou) {
context.getString(R.string.state_event_room_remove_by_you, userId.value) sp.getString(R.string.state_event_room_remove_by_you, userId.value)
} else { } else {
context.getString(R.string.state_event_room_remove, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_remove, senderDisplayName, userId.value)
} }
MembershipChange.INVITED -> if (senderIsYou) { MembershipChange.INVITED -> if (senderIsYou) {
context.getString(R.string.state_event_room_invite_by_you, userId.value) sp.getString(R.string.state_event_room_invite_by_you, userId.value)
} else if (memberIsYou) { } else if (memberIsYou) {
context.getString(R.string.state_event_room_invite_you, senderDisplayName) sp.getString(R.string.state_event_room_invite_you, senderDisplayName)
} else { } else {
context.getString(R.string.state_event_room_invite, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_invite, senderDisplayName, userId.value)
} }
MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) { MembershipChange.INVITATION_ACCEPTED -> if (memberIsYou) {
context.getString(R.string.state_event_room_invite_accepted_by_you) sp.getString(R.string.state_event_room_invite_accepted_by_you)
} else { } else {
context.getString(R.string.state_event_room_invite_accepted, userId.value) sp.getString(R.string.state_event_room_invite_accepted, userId.value)
} }
MembershipChange.INVITATION_REJECTED -> if (memberIsYou) { MembershipChange.INVITATION_REJECTED -> if (memberIsYou) {
context.getString(R.string.state_event_room_reject_by_you) sp.getString(R.string.state_event_room_reject_by_you)
} else { } else {
context.getString(R.string.state_event_room_reject, userId.value) sp.getString(R.string.state_event_room_reject, userId.value)
} }
MembershipChange.INVITATION_REVOKED -> if (senderIsYou) { MembershipChange.INVITATION_REVOKED -> if (senderIsYou) {
context.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value) sp.getString(R.string.state_event_room_third_party_revoked_invite_by_you, userId.value)
} else { } else {
context.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_third_party_revoked_invite, senderDisplayName, userId.value)
} }
MembershipChange.KNOCKED -> if (memberIsYou) { MembershipChange.KNOCKED -> if (memberIsYou) {
context.getString(R.string.state_event_room_knock_by_you) sp.getString(R.string.state_event_room_knock_by_you)
} else { } else {
context.getString(R.string.state_event_room_knock, userId.value) sp.getString(R.string.state_event_room_knock, userId.value)
} }
MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) { MembershipChange.KNOCK_ACCEPTED -> if (senderIsYou) {
context.getString(R.string.state_event_room_knock_accepted_by_you, userId.value) sp.getString(R.string.state_event_room_knock_accepted_by_you, userId.value)
} else { } else {
context.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_knock_accepted, senderDisplayName, userId.value)
} }
MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) { MembershipChange.KNOCK_RETRACTED -> if (memberIsYou) {
context.getString(R.string.state_event_room_knock_retracted_by_you) sp.getString(R.string.state_event_room_knock_retracted_by_you)
} else { } else {
context.getString(R.string.state_event_room_knock_retracted, userId.value) sp.getString(R.string.state_event_room_knock_retracted, userId.value)
} }
MembershipChange.KNOCK_DENIED -> if (senderIsYou) { MembershipChange.KNOCK_DENIED -> if (senderIsYou) {
context.getString(R.string.state_event_room_knock_denied_by_you, userId.value) sp.getString(R.string.state_event_room_knock_denied_by_you, userId.value)
} else if (memberIsYou) { } else if (memberIsYou) {
context.getString(R.string.state_event_room_knock_denied_you, senderDisplayName) sp.getString(R.string.state_event_room_knock_denied_you, senderDisplayName)
} else { } else {
context.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value) sp.getString(R.string.state_event_room_knock_denied, senderDisplayName, userId.value)
} }
else -> { else -> {
Timber.v("Filtering timeline item for room membership: $membershipContent") Timber.v("Filtering timeline item for room membership: $membershipContent")
@ -218,27 +216,27 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
is OtherState.RoomAvatar -> { is OtherState.RoomAvatar -> {
val hasAvatarUrl = content.url != null val hasAvatarUrl = content.url != null
when { when {
senderIsYou && hasAvatarUrl -> context.getString(R.string.state_event_room_avatar_changed_by_you) senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed_by_you)
senderIsYou && !hasAvatarUrl -> context.getString(R.string.state_event_room_avatar_removed_by_you) senderIsYou && !hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_removed_by_you)
!senderIsYou && hasAvatarUrl -> context.getString(R.string.state_event_room_avatar_changed, senderDisplayName) !senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisplayName)
else -> context.getString(R.string.state_event_room_avatar_removed, senderDisplayName) else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisplayName)
} }
} }
is OtherState.RoomCreate -> { is OtherState.RoomCreate -> {
if (senderIsYou) { if (senderIsYou) {
context.getString(R.string.state_event_room_created_by_you) sp.getString(R.string.state_event_room_created_by_you)
} else { } else {
context.getString(R.string.state_event_room_created, senderDisplayName) sp.getString(R.string.state_event_room_created, senderDisplayName)
} }
} }
is OtherState.RoomEncryption -> context.getString(StringR.string.common_encryption_enabled) is OtherState.RoomEncryption -> sp.getString(StringR.string.common_encryption_enabled)
is OtherState.RoomName -> { is OtherState.RoomName -> {
val hasRoomName = content.name != null val hasRoomName = content.name != null
when { when {
senderIsYou && hasRoomName -> context.getString(R.string.state_event_room_name_changed_by_you, content.name) senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed_by_you, content.name)
senderIsYou && !hasRoomName -> context.getString(R.string.state_event_room_name_removed_by_you) senderIsYou && !hasRoomName -> sp.getString(R.string.state_event_room_name_removed_by_you)
!senderIsYou && hasRoomName -> context.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name) !senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name)
else -> context.getString(R.string.state_event_room_name_removed, senderDisplayName) else -> sp.getString(R.string.state_event_room_name_removed, senderDisplayName)
} }
} }
is OtherState.RoomThirdPartyInvite -> { is OtherState.RoomThirdPartyInvite -> {
@ -247,18 +245,18 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
return null return null
} }
if (senderIsYou) { if (senderIsYou) {
context.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName) sp.getString(R.string.state_event_room_third_party_invite_by_you, content.displayName)
} else { } else {
context.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName) sp.getString(R.string.state_event_room_third_party_invite, senderDisplayName, content.displayName)
} }
} }
is OtherState.RoomTopic -> { is OtherState.RoomTopic -> {
val hasRoomTopic = content.topic != null val hasRoomTopic = content.topic != null
when { when {
senderIsYou && hasRoomTopic -> context.getString(R.string.state_event_room_topic_changed_by_you, content.topic) senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed_by_you, content.topic)
senderIsYou && !hasRoomTopic -> context.getString(R.string.state_event_room_topic_removed_by_you) senderIsYou && !hasRoomTopic -> sp.getString(R.string.state_event_room_topic_removed_by_you)
!senderIsYou && hasRoomTopic -> context.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic) !senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic)
else -> context.getString(R.string.state_event_room_topic_removed, senderDisplayName) else -> sp.getString(R.string.state_event_room_topic_removed, senderDisplayName)
} }
} }
else -> { else -> {
@ -278,35 +276,35 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
return when { return when {
avatarChanged && displayNameChanged -> { avatarChanged && displayNameChanged -> {
val message = processProfileChangeContent(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou) val message = processProfileChangeContent(profileChangeContent.copy(avatarUrl = null, prevAvatarUrl = null), senderDisplayName, senderIsYou)
val avatarChangedToo = context.getString(R.string.state_event_avatar_changed_too) val avatarChangedToo = sp.getString(R.string.state_event_avatar_changed_too)
"$message\n$avatarChangedToo" "$message\n$avatarChangedToo"
} }
displayNameChanged -> { displayNameChanged -> {
if (displayName != null && prevDisplayName != null) { if (displayName != null && prevDisplayName != null) {
if (senderIsYou) { if (senderIsYou) {
context.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName) sp.getString(R.string.state_event_display_name_changed_from_by_you, prevDisplayName, displayName)
} else { } else {
context.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName) sp.getString(R.string.state_event_display_name_changed_from, senderDisplayName, prevDisplayName, displayName)
} }
} else if (displayName != null) { } else if (displayName != null) {
if (senderIsYou) { if (senderIsYou) {
context.getString(R.string.state_event_display_name_set_by_you, displayName) sp.getString(R.string.state_event_display_name_set_by_you, displayName)
} else { } else {
context.getString(R.string.state_event_display_name_set, senderDisplayName, displayName) sp.getString(R.string.state_event_display_name_set, senderDisplayName, displayName)
} }
} else { } else {
if (senderIsYou) { if (senderIsYou) {
context.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName) sp.getString(R.string.state_event_display_name_removed_by_you, prevDisplayName)
} else { } else {
context.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName) sp.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName)
} }
} }
} }
avatarChanged -> { avatarChanged -> {
if (senderIsYou) { if (senderIsYou) {
context.getString(R.string.state_event_avatar_url_changed_by_you) sp.getString(R.string.state_event_avatar_url_changed_by_you)
} else { } else {
context.getString(R.string.state_event_avatar_url_changed, senderDisplayName) sp.getString(R.string.state_event_avatar_url_changed, senderDisplayName)
} }
} }
else -> null else -> null

View file

@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent import io.element.android.libraries.matrix.test.room.aProfileChangeMessageContent
import io.element.android.libraries.matrix.test.room.anEventTimelineItem import io.element.android.libraries.matrix.test.room.anEventTimelineItem
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -66,7 +67,7 @@ class DefaultRoomLastMessageFormatterTests {
fun setup() { fun setup() {
context = RuntimeEnvironment.getApplication() as Context context = RuntimeEnvironment.getApplication() as Context
fakeMatrixClient = FakeMatrixClient() fakeMatrixClient = FakeMatrixClient()
formatter = DefaultRoomLastMessageFormatter(context, fakeMatrixClient) formatter = DefaultRoomLastMessageFormatter(AndroidStringProvider(context.resources), fakeMatrixClient)
} }
@Test @Test
@ -145,6 +146,7 @@ class DefaultRoomLastMessageFormatterTests {
fun createMessageContent(type: MessageType): MessageContent { fun createMessageContent(type: MessageType): MessageContent {
return MessageContent(body, null, false, type) return MessageContent(body, null, false, type)
} }
val sharedContentMessagesTypes = arrayOf( val sharedContentMessagesTypes = arrayOf(
TextMessageType(body, null), TextMessageType(body, null),
VideoMessageType(body, "url", null), VideoMessageType(body, "url", null),

View file

@ -61,6 +61,7 @@ dependencies {
implementation(projects.features.leaveroom.impl) implementation(projects.features.leaveroom.impl)
implementation(projects.features.login.impl) implementation(projects.features.login.impl)
implementation(projects.features.networkmonitor.impl) implementation(projects.features.networkmonitor.impl)
implementation(projects.services.toolbox.impl)
implementation(libs.coroutines.core) implementation(libs.coroutines.core)
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
} }

View file

@ -35,6 +35,7 @@ import io.element.android.libraries.eventformatter.impl.DefaultRoomLastMessageFo
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.services.toolbox.impl.strings.AndroidStringProvider
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
@ -55,7 +56,7 @@ class RoomListScreen(
private val presenter = RoomListPresenter( private val presenter = RoomListPresenter(
client = matrixClient, client = matrixClient,
lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters), lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters),
roomLastMessageFormatter = DefaultRoomLastMessageFormatter(context, matrixClient), roomLastMessageFormatter = DefaultRoomLastMessageFormatter(AndroidStringProvider(context.resources), matrixClient),
sessionVerificationService = sessionVerificationService, sessionVerificationService = sessionVerificationService,
networkMonitor = NetworkMonitorImpl(context), networkMonitor = NetworkMonitorImpl(context),
snackbarDispatcher = SnackbarDispatcher(), snackbarDispatcher = SnackbarDispatcher(),