diff --git a/libraries/eventformatter/impl/build.gradle.kts b/libraries/eventformatter/impl/build.gradle.kts index d93043efd1..e1a1fdb70a 100644 --- a/libraries/eventformatter/impl/build.gradle.kts +++ b/libraries/eventformatter/impl/build.gradle.kts @@ -40,8 +40,10 @@ dependencies { implementation(projects.libraries.architecture) implementation(projects.libraries.matrix.api) implementation(projects.libraries.uiStrings) + implementation(projects.services.toolbox.api) api(projects.libraries.eventformatter.api) + testImplementation(projects.services.toolbox.impl) testImplementation(libs.test.junit) testImplementation(libs.test.robolectric) testImplementation(libs.test.truth) diff --git a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt index 4eeef1c62f..3c84166ddf 100644 --- a/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt +++ b/libraries/eventformatter/impl/src/main/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatter.kt @@ -16,15 +16,14 @@ package io.element.android.libraries.eventformatter.impl -import android.content.Context import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle 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.eventformatter.api.RoomLastMessageFormatter 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.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.UnknownMessageType 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 javax.inject.Inject import io.element.android.libraries.ui.strings.R as StringR @ContributesBinding(SessionScope::class) class DefaultRoomLastMessageFormatter @Inject constructor( - // TODO replace with StringProvider - @ApplicationContext private val context: Context, + private val sp: StringProvider, private val matrixClient: MatrixClient, ) : RoomLastMessageFormatter { @@ -67,7 +65,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( return when (val content = event.content) { is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom) RedactedContent -> { - val message = context.getString(StringR.string.common_message_removed) + val message = sp.getString(StringR.string.common_message_removed) if (!isDmRoom) { prefix(message, senderDisplayName) } else { @@ -78,7 +76,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( content.body } is UnableToDecryptContent -> { - val message = context.getString(StringR.string.common_decryption_error) + val message = sp.getString(StringR.string.common_decryption_error) if (!isDmRoom) { prefix(message, senderDisplayName) } else { @@ -95,7 +93,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor( processRoomStateChange(content, senderDisplayName, isOutgoing) } 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 } is VideoMessageType -> { - context.getString(StringR.string.common_video) + sp.getString(StringR.string.common_video) } is ImageMessageType -> { - context.getString(StringR.string.common_image) + sp.getString(StringR.string.common_image) } is FileMessageType -> { - context.getString(StringR.string.common_file) + sp.getString(StringR.string.common_file) } is AudioMessageType -> { - context.getString(StringR.string.common_audio) + sp.getString(StringR.string.common_audio) } UnknownMessageType -> { - context.getString(StringR.string.common_unsupported_event) + sp.getString(StringR.string.common_unsupported_event) } is NoticeMessageType -> { messageType.body @@ -138,73 +136,73 @@ class DefaultRoomLastMessageFormatter @Inject constructor( val memberIsYou = userId == matrixClient.sessionId return when (val change = membershipContent.change) { 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 { - context.getString(R.string.state_event_room_join, userId.value) + sp.getString(R.string.state_event_room_join, userId.value) } 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 { - 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) { - 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 { - 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) { - 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 { - 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) { - 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 { - 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) { - 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) { - context.getString(R.string.state_event_room_invite_you, senderDisplayName) + sp.getString(R.string.state_event_room_invite_you, senderDisplayName) } 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) { - context.getString(R.string.state_event_room_invite_accepted_by_you) + sp.getString(R.string.state_event_room_invite_accepted_by_you) } 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) { - context.getString(R.string.state_event_room_reject_by_you) + sp.getString(R.string.state_event_room_reject_by_you) } 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) { - 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 { - 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) { - context.getString(R.string.state_event_room_knock_by_you) + sp.getString(R.string.state_event_room_knock_by_you) } 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) { - 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 { - 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) { - context.getString(R.string.state_event_room_knock_retracted_by_you) + sp.getString(R.string.state_event_room_knock_retracted_by_you) } 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) { - 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) { - context.getString(R.string.state_event_room_knock_denied_you, senderDisplayName) + sp.getString(R.string.state_event_room_knock_denied_you, senderDisplayName) } 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 -> { Timber.v("Filtering timeline item for room membership: $membershipContent") @@ -218,27 +216,27 @@ class DefaultRoomLastMessageFormatter @Inject constructor( is OtherState.RoomAvatar -> { val hasAvatarUrl = content.url != null when { - senderIsYou && hasAvatarUrl -> context.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 -> context.getString(R.string.state_event_room_avatar_changed, senderDisplayName) - else -> context.getString(R.string.state_event_room_avatar_removed, senderDisplayName) + senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed_by_you) + senderIsYou && !hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_removed_by_you) + !senderIsYou && hasAvatarUrl -> sp.getString(R.string.state_event_room_avatar_changed, senderDisplayName) + else -> sp.getString(R.string.state_event_room_avatar_removed, senderDisplayName) } } is OtherState.RoomCreate -> { if (senderIsYou) { - context.getString(R.string.state_event_room_created_by_you) + sp.getString(R.string.state_event_room_created_by_you) } 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 -> { val hasRoomName = content.name != null when { - senderIsYou && hasRoomName -> context.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 -> context.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name) - else -> context.getString(R.string.state_event_room_name_removed, senderDisplayName) + senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed_by_you, content.name) + senderIsYou && !hasRoomName -> sp.getString(R.string.state_event_room_name_removed_by_you) + !senderIsYou && hasRoomName -> sp.getString(R.string.state_event_room_name_changed, senderDisplayName, content.name) + else -> sp.getString(R.string.state_event_room_name_removed, senderDisplayName) } } is OtherState.RoomThirdPartyInvite -> { @@ -247,18 +245,18 @@ class DefaultRoomLastMessageFormatter @Inject constructor( return null } 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 { - 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 -> { val hasRoomTopic = content.topic != null when { - senderIsYou && hasRoomTopic -> context.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 -> context.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic) - else -> context.getString(R.string.state_event_room_topic_removed, senderDisplayName) + senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed_by_you, content.topic) + senderIsYou && !hasRoomTopic -> sp.getString(R.string.state_event_room_topic_removed_by_you) + !senderIsYou && hasRoomTopic -> sp.getString(R.string.state_event_room_topic_changed, senderDisplayName, content.topic) + else -> sp.getString(R.string.state_event_room_topic_removed, senderDisplayName) } } else -> { @@ -278,35 +276,35 @@ class DefaultRoomLastMessageFormatter @Inject constructor( return when { avatarChanged && displayNameChanged -> { 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" } displayNameChanged -> { if (displayName != null && prevDisplayName != null) { 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 { - 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) { 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 { - context.getString(R.string.state_event_display_name_set, senderDisplayName, displayName) + sp.getString(R.string.state_event_display_name_set, senderDisplayName, displayName) } } else { 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 { - context.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName) + sp.getString(R.string.state_event_display_name_removed, senderDisplayName, prevDisplayName) } } } avatarChanged -> { 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 { - context.getString(R.string.state_event_avatar_url_changed, senderDisplayName) + sp.getString(R.string.state_event_avatar_url_changed, senderDisplayName) } } else -> null diff --git a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt index 8888b2c3eb..4fb7fc3e9c 100644 --- a/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt +++ b/libraries/eventformatter/impl/src/test/kotlin/io/element/android/libraries/eventformatter/impl/DefaultRoomLastMessageFormatterTests.kt @@ -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.room.aProfileChangeMessageContent 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.Test import org.junit.runner.RunWith @@ -66,7 +67,7 @@ class DefaultRoomLastMessageFormatterTests { fun setup() { context = RuntimeEnvironment.getApplication() as Context fakeMatrixClient = FakeMatrixClient() - formatter = DefaultRoomLastMessageFormatter(context, fakeMatrixClient) + formatter = DefaultRoomLastMessageFormatter(AndroidStringProvider(context.resources), fakeMatrixClient) } @Test @@ -145,6 +146,7 @@ class DefaultRoomLastMessageFormatterTests { fun createMessageContent(type: MessageType): MessageContent { return MessageContent(body, null, false, type) } + val sharedContentMessagesTypes = arrayOf( TextMessageType(body, null), VideoMessageType(body, "url", null), diff --git a/samples/minimal/build.gradle.kts b/samples/minimal/build.gradle.kts index 41390999f0..7d4b222713 100644 --- a/samples/minimal/build.gradle.kts +++ b/samples/minimal/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { implementation(projects.features.leaveroom.impl) implementation(projects.features.login.impl) implementation(projects.features.networkmonitor.impl) + implementation(projects.services.toolbox.impl) implementation(libs.coroutines.core) coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3") } diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt index b9703aa98a..ace7572858 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/RoomListScreen.kt @@ -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.core.RoomId 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.withContext import kotlinx.datetime.Clock @@ -55,7 +56,7 @@ class RoomListScreen( private val presenter = RoomListPresenter( client = matrixClient, lastMessageTimestampFormatter = DefaultLastMessageTimestampFormatter(dateTimeProvider, dateFormatters), - roomLastMessageFormatter = DefaultRoomLastMessageFormatter(context, matrixClient), + roomLastMessageFormatter = DefaultRoomLastMessageFormatter(AndroidStringProvider(context.resources), matrixClient), sessionVerificationService = sessionVerificationService, networkMonitor = NetworkMonitorImpl(context), snackbarDispatcher = SnackbarDispatcher(),