diff --git a/changelog.d/689.feature b/changelog.d/689.feature new file mode 100644 index 0000000000..36f2084715 --- /dev/null +++ b/changelog.d/689.feature @@ -0,0 +1 @@ +Show location events in the timeline diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/GeoUris.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/GeoUris.kt new file mode 100644 index 0000000000..2986d50591 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/GeoUris.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.location.api + +private const val GEO_URI_REGEX = """geo:(?-?\d+(?:\.\d+)?),(?-?\d+(?:\.\d+)?)(?:;u=(?\d+(?:\.\d+)?))?""" + +data class Location( + val lat: Double, + val lon: Double, + val accuracy: Float, +) + +fun parseGeoUri(geoUri: String): Location? { + val result = Regex(GEO_URI_REGEX).matchEntire(geoUri) ?: return null + return Location ( + lat = result.groups["latitude"]?.value?.toDoubleOrNull() ?: return null, + lon = result.groups["longitude"]?.value?.toDoubleOrNull() ?: return null, + accuracy = result.groups["uncertainty"]?.value?.toFloatOrNull() ?: 0f, + ) +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index e01ae49801..8a3541c22a 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -34,10 +34,12 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImagePainter import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest +import io.element.android.features.location.api.internal.AttributionPlacement import io.element.android.features.location.api.internal.StaticMapPlaceholder import io.element.android.features.location.api.internal.buildStaticMapsApiUrl import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.theme.ElementTheme import timber.log.Timber @@ -73,9 +75,13 @@ fun StaticMapView( lat = lat, lon = lon, desiredZoom = zoom, - desiredWidth = constraints.maxWidth, - desiredHeight = constraints.maxHeight, darkMode = darkMode, + attributionPlacement = AttributionPlacement.BottomLeft, + // Size the map based on DP rather than pixels, as otherwise the features and attribution + // end up being illegibly tiny on high density displays. + desiredWidth = constraints.maxWidth.toDp().value.toInt(), + desiredHeight = constraints.maxHeight.toDp().value.toInt(), + doubleScale = true, ) ) .size(width = constraints.maxWidth, height = constraints.maxHeight) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapsUtils.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapsUtils.kt index d3b9130045..66bbb906fc 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapsUtils.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapsUtils.kt @@ -23,7 +23,7 @@ private const val BASE_URL = "https://api.maptiler.com" private const val LIGHT_MAP_ID = "9bc819c8-e627-474a-a348-ec144fe3d810" private const val DARK_MAP_ID = "dea61faf-292b-4774-9660-58fcef89a7f3" private const val STATIC_MAP_FORMAT = "webp" -private const val STATIC_MAP_SCALE = "" // Either "" (empty string) for normal image or "@2x" for retina images. +private const val STATIC_MAP_SCALE_2X = "@2x" private const val STATIC_MAP_MAX_WIDTH_HEIGHT = 2048 private const val STATIC_MAP_MAX_ZOOM = 22.0 @@ -35,6 +35,14 @@ fun buildTileServerUrl( "$BASE_URL/maps/$DARK_MAP_ID/style.json?key=$API_KEY" } +internal enum class AttributionPlacement(val value: String) { + BottomRight("bottomright"), + BottomLeft("bottomleft"), + TopLeft("topleft"), + TopRight("topright"), + Hidden("false"), +} + /** * Builds a valid URL for maptiler.com static map api based on the given params. * @@ -50,7 +58,9 @@ internal fun buildStaticMapsApiUrl( desiredZoom: Double, desiredWidth: Int, desiredHeight: Int, - darkMode: Boolean + darkMode: Boolean, + doubleScale: Boolean, + attributionPlacement: AttributionPlacement, ): String { require(desiredWidth > 0 && desiredHeight > 0) { "Width ($desiredHeight) and height ($desiredHeight) must be > 0" @@ -72,9 +82,10 @@ internal fun buildStaticMapsApiUrl( width = (height * aspectRatio).roundToInt() } } - return if (!darkMode) { - "$BASE_URL/maps/$LIGHT_MAP_ID/static/${lon},${lat},${zoom}/${width}x${height}$STATIC_MAP_SCALE.$STATIC_MAP_FORMAT?key=$API_KEY" - } else { - "$BASE_URL/maps/$DARK_MAP_ID/static/${lon},${lat},${zoom}/${width}x${height}$STATIC_MAP_SCALE.$STATIC_MAP_FORMAT?key=$API_KEY" - } + + val mapId = if (darkMode) DARK_MAP_ID else LIGHT_MAP_ID + val scaleSuffix = if (doubleScale) STATIC_MAP_SCALE_2X else "" + + return "$BASE_URL/maps/$mapId/static/${lon},${lat},${zoom}/${width}x${height}${scaleSuffix}.$STATIC_MAP_FORMAT" + + "?key=$API_KEY&attribution=${attributionPlacement.value}" } diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/GeoUrisKtTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/GeoUrisKtTest.kt new file mode 100644 index 0000000000..1c591bb2bb --- /dev/null +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/GeoUrisKtTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.location.api + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +internal class GeoUrisKtTest { + + @Test + fun `parseGeoUri - returns null for invalid urls`() { + assertThat(parseGeoUri("")).isNull() + assertThat(parseGeoUri("http://example.com/")).isNull() + assertThat(parseGeoUri("geo:")).isNull() + assertThat(parseGeoUri("geo:1.234")).isNull() + assertThat(parseGeoUri("geo:1.234,")).isNull() + assertThat(parseGeoUri("geo:,1.234")).isNull() + assertThat(parseGeoUri("notgeo:1.234,5.678")).isNull() + assertThat(parseGeoUri("geo:+1.234,5.678")).isNull() + assertThat(parseGeoUri("geo:+1.234,*5.678")).isNull() + assertThat(parseGeoUri("geo:not,good")).isNull() + assertThat(parseGeoUri("geo:1.234,5.678;u=wrong")).isNull() + assertThat(parseGeoUri("geo:1.234,5.678trailing")).isNull() + } + + @Test + fun `parseGeoUri - returns location for valid urls`() { + assertThat(parseGeoUri("geo:1.234,5.678")).isEqualTo(Location( + lat = 1.234, + lon = 5.678, + accuracy = 0f, + )) + + assertThat(parseGeoUri("geo:1,5")).isEqualTo(Location( + lat = 1.0, + lon = 5.0, + accuracy = 0f, + )) + + assertThat(parseGeoUri("geo:1.234,5.678;u=3000")).isEqualTo(Location( + lat = 1.234, + lon = 5.678, + accuracy = 3000f, + )) + + assertThat(parseGeoUri("geo:1,5;u=3000")).isEqualTo(Location( + lat = 1.0, + lon = 5.0, + accuracy = 3000f, + )) + + assertThat(parseGeoUri("geo:-1.234,-5.678;u=9.10")).isEqualTo(Location( + lat = -1.234, + lon = -5.678, + accuracy = 9.10f, + )) + + assertThat(parseGeoUri("geo:-1,-5;u=9.10")).isEqualTo(Location( + lat = -1.0, + lon = -5.0, + accuracy = 9.10f, + )) + } + +} diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/BuildStaticMapsApiUrlTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/BuildStaticMapsApiUrlTest.kt index 023c7be365..71e5988185 100644 --- a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/BuildStaticMapsApiUrlTest.kt +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/BuildStaticMapsApiUrlTest.kt @@ -17,7 +17,6 @@ package io.element.android.features.location.api.internal import com.google.common.truth.Truth.assertThat -import io.element.android.features.location.api.internal.buildStaticMapsApiUrl import org.junit.Test class BuildStaticMapsApiUrlTest { @@ -30,10 +29,13 @@ class BuildStaticMapsApiUrlTest { desiredZoom = 1.2, desiredWidth = 100, desiredHeight = 200, - darkMode = false + darkMode = false, + doubleScale = false, + attributionPlacement = AttributionPlacement.BottomLeft, ) ).isEqualTo( - "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/static/5.678,1.234,1.2/100x200.webp?key=fU3vlMsMn4Jb6dnEIFsx" + "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/static/5.678,1.234,1.2/100x200.webp" + + "?key=fU3vlMsMn4Jb6dnEIFsx&attribution=bottomleft" ) } @@ -46,10 +48,51 @@ class BuildStaticMapsApiUrlTest { desiredZoom = 1.2, desiredWidth = 100, desiredHeight = 200, - darkMode = true + darkMode = true, + doubleScale = false, + attributionPlacement = AttributionPlacement.BottomLeft, ) ).isEqualTo( - "https://api.maptiler.com/maps/dea61faf-292b-4774-9660-58fcef89a7f3/static/5.678,1.234,1.2/100x200.webp?key=fU3vlMsMn4Jb6dnEIFsx" + "https://api.maptiler.com/maps/dea61faf-292b-4774-9660-58fcef89a7f3/static/5.678,1.234,1.2/100x200.webp" + + "?key=fU3vlMsMn4Jb6dnEIFsx&attribution=bottomleft" + ) + } + + @Test + fun `buildStaticMapsApiUrl builds double scale mode url`() { + assertThat( + buildStaticMapsApiUrl( + lat = 1.234, + lon = 5.678, + desiredZoom = 1.2, + desiredWidth = 100, + desiredHeight = 200, + darkMode = false, + doubleScale = true, + attributionPlacement = AttributionPlacement.BottomLeft, + ) + ).isEqualTo( + "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/static/5.678,1.234,1.2/100x200@2x.webp" + + "?key=fU3vlMsMn4Jb6dnEIFsx&attribution=bottomleft" + ) + } + + @Test + fun `buildStaticMapsApiUrl builds no attribution url`() { + assertThat( + buildStaticMapsApiUrl( + lat = 1.234, + lon = 5.678, + desiredZoom = 1.2, + desiredWidth = 100, + desiredHeight = 200, + darkMode = false, + doubleScale = false, + attributionPlacement = AttributionPlacement.Hidden, + ) + ).isEqualTo( + "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/static/5.678,1.234,1.2/100x200.webp" + + "?key=fU3vlMsMn4Jb6dnEIFsx&attribution=false" ) } @@ -62,10 +105,13 @@ class BuildStaticMapsApiUrlTest { desiredZoom = 100.0, desiredWidth = 8192, desiredHeight = 4096, - darkMode = false + darkMode = false, + doubleScale = false, + attributionPlacement = AttributionPlacement.BottomLeft, ) ).isEqualTo( - "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/static/5.678,1.234,22.0/2048x1024.webp?key=fU3vlMsMn4Jb6dnEIFsx" + "https://api.maptiler.com/maps/9bc819c8-e627-474a-a348-ec144fe3d810/static/5.678,1.234,22.0/2048x1024.webp" + + "?key=fU3vlMsMn4Jb6dnEIFsx&attribution=bottomleft" ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 29659d4daa..ac64715215 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -45,6 +45,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -267,6 +268,7 @@ class MessagesPresenter @AssistedInject constructor( is TimelineItemRedactedContent, is TimelineItemStateContent, is TimelineItemEncryptedContent, + is TimelineItemLocationContent, is TimelineItemUnknownContent -> null } val composerMode = MessageComposerMode.Reply( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt index 6208301a36..430238ce37 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt @@ -58,6 +58,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -231,6 +232,7 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif is TimelineItemStateContent, is TimelineItemEncryptedContent, is TimelineItemRedactedContent, + is TimelineItemLocationContent, is TimelineItemUnknownContent -> content = { ContentForBody(textContent) } is TimelineItemImageContent -> { icon = { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt index 59d824ce86..026d6d4557 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventRow.kt @@ -55,6 +55,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent @@ -224,7 +225,9 @@ private fun MessageEventBubbleContent( onTimestampClicked: () -> Unit, modifier: Modifier = Modifier ) { - val isMediaItem = event.content is TimelineItemImageContent || event.content is TimelineItemVideoContent + val isMediaItem = event.content is TimelineItemImageContent + || event.content is TimelineItemVideoContent + || event.content is TimelineItemLocationContent val replyToDetails = event.inReplyTo as? InReplyTo.Ready // Long clicks are not not automatically propagated from a `clickable` diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt index 086592a983..7d3f8835ca 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemContentView.kt @@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -62,6 +63,10 @@ fun TimelineItemEventContentView( extraPadding = extraPadding, modifier = modifier ) + is TimelineItemLocationContent -> TimelineItemLocationView( + content = content, + modifier = modifier + ) is TimelineItemImageContent -> TimelineItemImageView( content = content, modifier = modifier, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt new file mode 100644 index 0000000000..28c9e4fd16 --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/event/TimelineItemLocationView.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023 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 io.element.android.features.messages.impl.timeline.components.event + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.features.location.api.StaticMapView +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContentProvider +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight + +@Composable +fun TimelineItemLocationView( + content: TimelineItemLocationContent, + modifier: Modifier = Modifier, +) { + StaticMapView( + modifier = modifier + .fillMaxWidth() + .heightIn(max = 188.dp), + lat = content.location.lat, + lon = content.location.lon, + zoom = 15.0, + contentDescription = content.body + ) +} + +@Preview +@Composable +internal fun TimelineItemLocationViewLightPreview(@PreviewParameter(TimelineItemLocationContentProvider::class) content: TimelineItemLocationContent) = + ElementPreviewLight { ContentToPreview(content) } + +@Preview +@Composable +internal fun TimelineItemLocationViewDarkPreview(@PreviewParameter(TimelineItemLocationContentProvider::class) content: TimelineItemLocationContent) = + ElementPreviewDark { ContentToPreview(content) } + +@Composable +private fun ContentToPreview(content: TimelineItemLocationContent) { + TimelineItemLocationView(content) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt index d9a12cf615..6d70d8318b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemContentMessageFactory.kt @@ -16,10 +16,12 @@ package io.element.android.features.messages.impl.timeline.factories.event +import io.element.android.features.location.api.parseGeoUri import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent @@ -31,6 +33,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType @@ -64,6 +67,21 @@ class TimelineItemContentMessageFactory @Inject constructor( fileExtension = fileExtensionExtractor.extractFromName(messageType.body) ) } + is LocationMessageType -> { + val location = parseGeoUri(messageType.geoUri) + if (location == null) { + TimelineItemTextContent( + body = messageType.body, + htmlDocument = null, + isEdited = content.isEdited, + ) + } else { + TimelineItemLocationContent( + body = messageType.body, + location = location, + ) + } + } is VideoMessageType -> { val aspectRatio = aspectRatioOf(messageType.info?.width, messageType.info?.height) TimelineItemVideoContent( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt index 879f33e328..bf827a2b3d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt @@ -20,6 +20,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent @@ -51,6 +52,7 @@ internal fun TimelineItem.Event.canBeGrouped(): Boolean { is TimelineItemImageContent, is TimelineItemFileContent, is TimelineItemVideoContent, + is TimelineItemLocationContent, TimelineItemRedactedContent, TimelineItemUnknownContent -> false is TimelineItemProfileChangeContent, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt index 49e82fc383..52c84d31db 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/event/TimelineItemEventContentProvider.kt @@ -28,6 +28,7 @@ class TimelineItemEventContentProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aTimelineItemLocationContent(), + ) +} + +fun aTimelineItemLocationContent() = TimelineItemLocationContent( + body = "User location geo:52.2445,0.7186;u=5000", + location = Location( + lat = 52.2445, + lon = 0.7186, + accuracy = 5000f, + ) +) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt index c7bae0c995..852c3f1620 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/utils/messagesummary/MessageSummaryFormatterImpl.kt @@ -22,6 +22,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLocationContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent @@ -40,8 +41,9 @@ class MessageSummaryFormatterImpl @Inject constructor( override fun format(event: TimelineItem.Event): String { return when (event.content) { is TimelineItemTextBasedContent -> event.content.body - is TimelineItemStateContent -> event.content.body is TimelineItemProfileChangeContent -> event.content.body + is TimelineItemStateContent -> event.content.body + is TimelineItemLocationContent -> event.content.body is TimelineItemEncryptedContent -> context.getString(CommonStrings.common_unable_to_decrypt) is TimelineItemRedactedContent -> context.getString(CommonStrings.common_message_removed) is TimelineItemUnknownContent -> context.getString(CommonStrings.common_unsupported_event) 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 1c76d46998..2e828f8793 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 @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageType import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType @@ -116,6 +117,9 @@ class DefaultRoomLastMessageFormatter @Inject constructor( is ImageMessageType -> { sp.getString(CommonStrings.common_image) } + is LocationMessageType -> { + sp.getString(CommonStrings.common_shared_location) + } is FileMessageType -> { sp.getString(CommonStrings.common_file) } 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 ad1ef259b7..b737031302 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 @@ -30,6 +30,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParse import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageType @@ -161,6 +162,7 @@ class DefaultRoomLastMessageFormatterTests { AudioMessageType(body, MediaSource("url"), null), ImageMessageType(body, MediaSource("url"), null), FileMessageType(body, MediaSource("url"), null), + LocationMessageType(body, "geo:1,2"), NoticeMessageType(body, null), EmoteMessageType(body, null), ) @@ -196,6 +198,7 @@ class DefaultRoomLastMessageFormatterTests { is AudioMessageType -> "Audio" is ImageMessageType -> "Image" is FileMessageType -> "File" + is LocationMessageType -> "Shared location" is EmoteMessageType -> "- $senderName ${type.body}" is TextMessageType, is NoticeMessageType -> body UnknownMessageType -> "Unsupported event" @@ -211,6 +214,7 @@ class DefaultRoomLastMessageFormatterTests { is AudioMessageType -> "$senderName: Audio" is ImageMessageType -> "$senderName: Image" is FileMessageType -> "$senderName: File" + is LocationMessageType -> "$senderName: Shared location" is EmoteMessageType -> "- $senderName ${type.body}" is TextMessageType, is NoticeMessageType -> "$senderName: $body" UnknownMessageType -> "$senderName: Unsupported event" @@ -220,6 +224,7 @@ class DefaultRoomLastMessageFormatterTests { is AudioMessageType -> true is ImageMessageType -> true is FileMessageType -> true + is LocationMessageType -> false is EmoteMessageType -> false is TextMessageType, is NoticeMessageType -> true UnknownMessageType -> true diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt index 57e3993926..f37f7ceba2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventContent.kt @@ -125,6 +125,11 @@ data class ImageMessageType( val info: ImageInfo? ) : MessageType +data class LocationMessageType( + val body: String, + val geoUri: String, +) : MessageType + data class AudioMessageType( val body: String, val source: MediaSource, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt index 9b70582308..18d6d389e2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt @@ -100,9 +100,9 @@ private fun MessageType.toContent(): String { is MessageType.Emote -> content.body is MessageType.File -> content.use { it.body } is MessageType.Image -> content.use { it.body } + is MessageType.Location -> content.body is MessageType.Notice -> content.body is MessageType.Text -> content.body is MessageType.Video -> content.use { it.body } - is MessageType.Location -> content.body } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt index d45124bf40..0a76d2c4bb 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/item/event/EventMessageMapper.kt @@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageTy import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo +import io.element.android.libraries.matrix.api.timeline.item.event.LocationMessageType import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType @@ -53,6 +54,9 @@ class EventMessageMapper { is MessageType.Image -> { ImageMessageType(type.content.body, type.content.source.map(), type.content.info?.map()) } + is MessageType.Location -> { + LocationMessageType(type.content.body, type.content.geoUri) + } is MessageType.Notice -> { NoticeMessageType(type.content.body, type.content.formatted?.map()) } @@ -65,7 +69,6 @@ class EventMessageMapper { is MessageType.Video -> { VideoMessageType(type.content.body, type.content.source.map(), type.content.info?.map()) } - is MessageType.Location, null -> { UnknownMessageType } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f1b15b6d73 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b9dd2f15bb1a4d1ff8a1614f0a4c29f61b9277dcd04e921b8c164e7f18946e2 +size 76727 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..6bdd02b8f4 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components.event_null_DefaultGroup_TimelineItemLocationViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cf8ad6c8f8da98fdfc7a9d5c47e959aaa612dfc5192dd93b589c783d4e94fd6 +size 155690 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png index 2c10ad60bb..23116a4ff2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a2d25abb7d0d15d53994b2b7af2aa08819b2b68e5e13931e53b4d7bbdc27f18 -size 47543 +oid sha256:d9e4fc3870b5dfb628f6bcdcb69ea1fccbb085b4a538bb7ba85d946d7d37ffd6 +size 56908 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_11,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2c10ad60bb --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_11,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a2d25abb7d0d15d53994b2b7af2aa08819b2b68e5e13931e53b4d7bbdc27f18 +size 47543 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index ae909c8f1f..3d7c197e06 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1da5db0c72cee6075e73ec958efad2a4e7d84b4d45d0e93187135bbe2a50451f -size 44299 +oid sha256:fcda324bbc41e69e5d88d35e09e527f3ad918f9c37131284e2da3b9daecb9c38 +size 171506 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index febd4230d8..ae909c8f1f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:081081bf7e41eccb9a8812c95100fcc0a2a4ead33c82ef4e314fe02fb7a0551d -size 55601 +oid sha256:1da5db0c72cee6075e73ec958efad2a4e7d84b4d45d0e93187135bbe2a50451f +size 44299 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png index 7b3d4e5553..febd4230d8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21d1b7469df1b1ba6f3c4b053702b045506fa5b510f16d07661bb8842a59047f -size 40750 +oid sha256:081081bf7e41eccb9a8812c95100fcc0a2a4ead33c82ef4e314fe02fb7a0551d +size 55601 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png index 23116a4ff2..7b3d4e5553 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9e4fc3870b5dfb628f6bcdcb69ea1fccbb085b4a538bb7ba85d946d7d37ffd6 -size 56908 +oid sha256:21d1b7469df1b1ba6f3c4b053702b045506fa5b510f16d07661bb8842a59047f +size 40750 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png index 17418fa6f0..0b8e363b46 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e88cdaaf2c98cd872c9645662b97f7157b3c862ab70616d29c4e8d53424612fc -size 49040 +oid sha256:42f04d6a93187cc26c9c0fb5c9d60c418d0e01fc42a27eb14d13f6267a7aabd5 +size 59076 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_11,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..17418fa6f0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_11,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e88cdaaf2c98cd872c9645662b97f7157b3c862ab70616d29c4e8d53424612fc +size 49040 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index c699c16bc1..55d887e971 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd951c7ffcd7673b2224986e5fdf168543e763b7697e1389bbbeea071a7a3c83 -size 45317 +oid sha256:d9434e4e7cd01ea5c241a631800348e79c1538344ee1525aba3bdd5b3dd5f300 +size 359637 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index 9a5aeb4984..c699c16bc1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6db451aec7d5d3b19c3378ff15218aacb0d838714b2d196465d0cc9190b45ae1 -size 57725 +oid sha256:fd951c7ffcd7673b2224986e5fdf168543e763b7697e1389bbbeea071a7a3c83 +size 45317 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png index 7107fb5d93..9a5aeb4984 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f547ad07db87cbc08c31922e1f21fdbe6fdbdea8667159c8e100a440a2c05c7f -size 41624 +oid sha256:6db451aec7d5d3b19c3378ff15218aacb0d838714b2d196465d0cc9190b45ae1 +size 57725 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png index 0b8e363b46..7107fb5d93 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:42f04d6a93187cc26c9c0fb5c9d60c418d0e01fc42a27eb14d13f6267a7aabd5 -size 59076 +oid sha256:f547ad07db87cbc08c31922e1f21fdbe6fdbdea8667159c8e100a440a2c05c7f +size 41624