Make sure we can display both Live and Static locations in ShowLocation
This commit is contained in:
parent
222b9f0c9e
commit
b4cf8c274e
16 changed files with 168 additions and 67 deletions
|
|
@ -15,8 +15,7 @@ import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
||||||
interface ShowLocationEntryPoint : FeatureEntryPoint {
|
interface ShowLocationEntryPoint : FeatureEntryPoint {
|
||||||
data class Inputs(
|
data class Inputs(
|
||||||
val location: Location,
|
val mode: ShowLocationMode,
|
||||||
val description: String?,
|
|
||||||
) : NodeInputs
|
) : NodeInputs
|
||||||
|
|
||||||
fun createNode(
|
fun createNode(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Element Creations Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.location.api
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
sealed interface ShowLocationMode : Parcelable {
|
||||||
|
@Parcelize
|
||||||
|
data class Static(
|
||||||
|
val location: Location,
|
||||||
|
val senderName: String,
|
||||||
|
val senderId: UserId,
|
||||||
|
val senderAvatarUrl: String?,
|
||||||
|
val timestamp: Long,
|
||||||
|
val assetType: AssetType?,
|
||||||
|
) : ShowLocationMode
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data object Live : ShowLocationMode
|
||||||
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ class ShowLocationNode(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inputs: ShowLocationEntryPoint.Inputs = inputs()
|
private val inputs: ShowLocationEntryPoint.Inputs = inputs()
|
||||||
private val presenter = presenterFactory.create(inputs.location, inputs.description)
|
private val presenter = presenterFactory.create(inputs.mode)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import androidx.compose.runtime.setValue
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
import dev.zacsweers.metro.AssistedFactory
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
import io.element.android.features.location.impl.common.actions.LocationActions
|
import io.element.android.features.location.impl.common.actions.LocationActions
|
||||||
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
|
import io.element.android.features.location.impl.common.permissions.PermissionsEvents
|
||||||
|
|
@ -29,15 +29,14 @@ import io.element.android.libraries.core.meta.BuildMeta
|
||||||
|
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class ShowLocationPresenter(
|
class ShowLocationPresenter(
|
||||||
@Assisted private val location: Location,
|
@Assisted private val mode: ShowLocationMode,
|
||||||
@Assisted private val description: String?,
|
|
||||||
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
permissionsPresenterFactory: PermissionsPresenter.Factory,
|
||||||
private val locationActions: LocationActions,
|
private val locationActions: LocationActions,
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) : Presenter<ShowLocationState> {
|
) : Presenter<ShowLocationState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
fun interface Factory {
|
fun interface Factory {
|
||||||
fun create(location: Location, description: String?): ShowLocationPresenter
|
fun create(mode: ShowLocationMode): ShowLocationPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
private val permissionsPresenter = permissionsPresenterFactory.create(MapDefaults.permissions)
|
private val permissionsPresenter = permissionsPresenterFactory.create(MapDefaults.permissions)
|
||||||
|
|
@ -59,7 +58,16 @@ class ShowLocationPresenter(
|
||||||
|
|
||||||
fun handleEvent(event: ShowLocationEvents) {
|
fun handleEvent(event: ShowLocationEvents) {
|
||||||
when (event) {
|
when (event) {
|
||||||
ShowLocationEvents.Share -> locationActions.share(location, description)
|
ShowLocationEvents.Share -> {
|
||||||
|
when (mode) {
|
||||||
|
is ShowLocationMode.Static -> {
|
||||||
|
locationActions.share(mode.location, null)
|
||||||
|
}
|
||||||
|
ShowLocationMode.Live -> {
|
||||||
|
// TODO: Handle sharing for live locations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
is ShowLocationEvents.TrackMyLocation -> {
|
is ShowLocationEvents.TrackMyLocation -> {
|
||||||
if (event.enabled) {
|
if (event.enabled) {
|
||||||
when {
|
when {
|
||||||
|
|
@ -82,8 +90,7 @@ class ShowLocationPresenter(
|
||||||
|
|
||||||
return ShowLocationState(
|
return ShowLocationState(
|
||||||
permissionDialog = permissionDialog,
|
permissionDialog = permissionDialog,
|
||||||
location = location,
|
mode = mode,
|
||||||
description = description,
|
|
||||||
hasLocationPermission = permissionsState.isAnyGranted,
|
hasLocationPermission = permissionsState.isAnyGranted,
|
||||||
isTrackMyLocation = isTrackMyLocation,
|
isTrackMyLocation = isTrackMyLocation,
|
||||||
appName = appName,
|
appName = appName,
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,11 @@
|
||||||
|
|
||||||
package io.element.android.features.location.impl.show
|
package io.element.android.features.location.impl.show
|
||||||
|
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
|
|
||||||
data class ShowLocationState(
|
data class ShowLocationState(
|
||||||
val permissionDialog: Dialog,
|
val permissionDialog: Dialog,
|
||||||
val location: Location,
|
val mode: ShowLocationMode,
|
||||||
val description: String?,
|
|
||||||
val hasLocationPermission: Boolean,
|
val hasLocationPermission: Boolean,
|
||||||
val isTrackMyLocation: Boolean,
|
val isTrackMyLocation: Boolean,
|
||||||
val appName: String,
|
val appName: String,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ package io.element.android.features.location.impl.show
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
|
||||||
private const val APP_NAME = "ApplicationName"
|
private const val APP_NAME = "ApplicationName"
|
||||||
|
|
||||||
|
|
@ -31,32 +34,47 @@ class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
|
||||||
isTrackMyLocation = true,
|
isTrackMyLocation = true,
|
||||||
),
|
),
|
||||||
aShowLocationState(
|
aShowLocationState(
|
||||||
description = "My favourite place!",
|
mode = aStaticLocationMode(senderName = "My favourite place!"),
|
||||||
),
|
),
|
||||||
aShowLocationState(
|
aShowLocationState(
|
||||||
description = "For some reason I decided to to write a small essay that wraps at just two lines!",
|
mode = aStaticLocationMode(
|
||||||
|
senderName = "For some reason I decided to write a small essay that wraps at just two lines!"
|
||||||
|
),
|
||||||
),
|
),
|
||||||
aShowLocationState(
|
aShowLocationState(
|
||||||
description = "For some reason I decided to write a small essay in the location description. " +
|
mode = ShowLocationMode.Live,
|
||||||
"It is so long that it will wrap onto more than two lines!",
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aShowLocationState(
|
fun aShowLocationState(
|
||||||
permissionDialog: ShowLocationState.Dialog = ShowLocationState.Dialog.None,
|
permissionDialog: ShowLocationState.Dialog = ShowLocationState.Dialog.None,
|
||||||
location: Location = Location(1.23, 2.34, 4f),
|
mode: ShowLocationMode = aStaticLocationMode(),
|
||||||
description: String? = null,
|
|
||||||
hasLocationPermission: Boolean = false,
|
hasLocationPermission: Boolean = false,
|
||||||
isTrackMyLocation: Boolean = false,
|
isTrackMyLocation: Boolean = false,
|
||||||
appName: String = APP_NAME,
|
appName: String = APP_NAME,
|
||||||
eventSink: (ShowLocationEvents) -> Unit = {},
|
eventSink: (ShowLocationEvents) -> Unit = {},
|
||||||
) = ShowLocationState(
|
) = ShowLocationState(
|
||||||
permissionDialog = permissionDialog,
|
permissionDialog = permissionDialog,
|
||||||
location = location,
|
mode = mode,
|
||||||
description = description,
|
|
||||||
hasLocationPermission = hasLocationPermission,
|
hasLocationPermission = hasLocationPermission,
|
||||||
isTrackMyLocation = isTrackMyLocation,
|
isTrackMyLocation = isTrackMyLocation,
|
||||||
appName = appName,
|
appName = appName,
|
||||||
eventSink = eventSink,
|
eventSink = eventSink,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fun aStaticLocationMode(
|
||||||
|
location: Location = Location(1.23, 2.34, 4f),
|
||||||
|
senderName: String = "Alice",
|
||||||
|
senderId: UserId = UserId("@alice:matrix.org"),
|
||||||
|
senderAvatarUrl: String? = null,
|
||||||
|
timestamp: Long = System.currentTimeMillis(),
|
||||||
|
assetType: AssetType? = null,
|
||||||
|
) = ShowLocationMode.Static(
|
||||||
|
location = location,
|
||||||
|
senderName = senderName,
|
||||||
|
senderId = senderId,
|
||||||
|
senderAvatarUrl = senderAvatarUrl,
|
||||||
|
timestamp = timestamp,
|
||||||
|
assetType = assetType,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ package io.element.android.features.location.impl.show
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.SheetValue
|
||||||
|
import androidx.compose.material3.rememberBottomSheetScaffoldState
|
||||||
|
import androidx.compose.material3.rememberStandardBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
|
@ -23,6 +26,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||||
import io.element.android.compound.tokens.generated.TypographyTokens
|
import io.element.android.compound.tokens.generated.TypographyTokens
|
||||||
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.location.impl.R
|
import io.element.android.features.location.impl.R
|
||||||
import io.element.android.features.location.impl.common.MapDefaults
|
import io.element.android.features.location.impl.common.MapDefaults
|
||||||
import io.element.android.features.location.impl.common.PermissionDeniedDialog
|
import io.element.android.features.location.impl.common.PermissionDeniedDialog
|
||||||
|
|
@ -74,12 +78,16 @@ fun ShowLocationView(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val cameraState = rememberCameraState(
|
val initialPosition = when (val mode = state.mode) {
|
||||||
firstPosition = CameraPosition(
|
is ShowLocationMode.Static -> CameraPosition(
|
||||||
target = Position(latitude = state.location.lat, longitude = state.location.lon),
|
target = Position(latitude = mode.location.lat, longitude = mode.location.lon),
|
||||||
zoom = MapDefaults.DEFAULT_ZOOM
|
zoom = MapDefaults.DEFAULT_ZOOM
|
||||||
)
|
)
|
||||||
)
|
ShowLocationMode.Live -> CameraPosition(
|
||||||
|
zoom = MapDefaults.DEFAULT_ZOOM
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val cameraState = rememberCameraState(firstPosition = initialPosition)
|
||||||
val locationProvider = if (state.hasLocationPermission) {
|
val locationProvider = if (state.hasLocationPermission) {
|
||||||
rememberDefaultLocationProvider(
|
rememberDefaultLocationProvider(
|
||||||
updateInterval = 1.minutes,
|
updateInterval = 1.minutes,
|
||||||
|
|
@ -96,10 +104,13 @@ fun ShowLocationView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val scaffoldState = rememberBottomSheetScaffoldState(
|
||||||
|
bottomSheetState = rememberStandardBottomSheetState()
|
||||||
|
)
|
||||||
MapBottomSheetScaffold(
|
MapBottomSheetScaffold(
|
||||||
|
scaffoldState = scaffoldState,
|
||||||
cameraState = cameraState,
|
cameraState = cameraState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
sheetPeekHeight = 80.dp,
|
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
titleStr = stringResource(CommonStrings.screen_view_location_title),
|
titleStr = stringResource(CommonStrings.screen_view_location_title),
|
||||||
|
|
@ -121,17 +132,22 @@ fun ShowLocationView(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
sheetContent = {
|
sheetContent = {
|
||||||
state.description?.let {
|
when (val mode = state.mode) {
|
||||||
Text(
|
is ShowLocationMode.Static -> {
|
||||||
text = it,
|
Text(
|
||||||
textAlign = TextAlign.Center,
|
text = mode.senderName,
|
||||||
maxLines = 2,
|
textAlign = TextAlign.Center,
|
||||||
overflow = TextOverflow.Ellipsis,
|
maxLines = 2,
|
||||||
style = TypographyTokens.fontBodyMdRegular,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier
|
style = TypographyTokens.fontBodyMdRegular,
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(8.dp),
|
.fillMaxWidth()
|
||||||
)
|
.padding(8.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ShowLocationMode.Live -> {
|
||||||
|
// TODO: Show list of active live location sharers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mapContent = {
|
mapContent = {
|
||||||
|
|
@ -140,22 +156,29 @@ fun ShowLocationView(
|
||||||
locationState = userLocationState,
|
locationState = userLocationState,
|
||||||
trackUserLocation = state.isTrackMyLocation
|
trackUserLocation = state.isTrackMyLocation
|
||||||
)
|
)
|
||||||
val senderLocation = rememberGeoJsonSource(
|
when (val mode = state.mode) {
|
||||||
data = GeoJsonData.Features(
|
is ShowLocationMode.Static -> {
|
||||||
Point(
|
val senderLocation = rememberGeoJsonSource(
|
||||||
Position(
|
data = GeoJsonData.Features(
|
||||||
latitude = state.location.lat,
|
Point(
|
||||||
longitude = state.location.lon
|
Position(
|
||||||
|
latitude = mode.location.lat,
|
||||||
|
longitude = mode.location.lon
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
val marker = painterResource(R.drawable.pin_small)
|
||||||
)
|
SymbolLayer(
|
||||||
val marker = painterResource(R.drawable.pin_small)
|
id = "sender_location",
|
||||||
SymbolLayer(
|
source = senderLocation,
|
||||||
id = "sender_location",
|
iconImage = image(marker)
|
||||||
source = senderLocation,
|
)
|
||||||
iconImage = image(marker)
|
}
|
||||||
)
|
ShowLocationMode.Live -> {
|
||||||
|
// TODO: Show pins for all active live location sharers
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
overlayContent = {
|
overlayContent = {
|
||||||
LocationFloatingActionButton(
|
LocationFloatingActionButton(
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,10 @@ import io.element.android.features.call.api.CallType
|
||||||
import io.element.android.features.call.api.ElementCallEntryPoint
|
import io.element.android.features.call.api.ElementCallEntryPoint
|
||||||
import io.element.android.features.forward.api.ForwardEntryPoint
|
import io.element.android.features.forward.api.ForwardEntryPoint
|
||||||
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
|
import io.element.android.features.knockrequests.api.list.KnockRequestsListEntryPoint
|
||||||
import io.element.android.features.location.api.Location
|
|
||||||
import io.element.android.features.location.api.LocationService
|
import io.element.android.features.location.api.LocationService
|
||||||
import io.element.android.features.location.api.ShareLocationEntryPoint
|
import io.element.android.features.location.api.ShareLocationEntryPoint
|
||||||
import io.element.android.features.location.api.ShowLocationEntryPoint
|
import io.element.android.features.location.api.ShowLocationEntryPoint
|
||||||
|
import io.element.android.features.location.api.ShowLocationMode
|
||||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
import io.element.android.features.messages.impl.attachments.Attachment
|
import io.element.android.features.messages.impl.attachments.Attachment
|
||||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode
|
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewNode
|
||||||
|
|
@ -75,6 +75,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.matrix.api.room.alias.matches
|
import io.element.android.libraries.matrix.api.room.alias.matches
|
||||||
import io.element.android.libraries.matrix.api.room.joinedRoomMembers
|
import io.element.android.libraries.matrix.api.room.joinedRoomMembers
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||||
|
|
@ -148,7 +149,7 @@ class MessagesFlowNode(
|
||||||
data class AttachmentPreview(val timelineMode: Timeline.Mode, val attachment: Attachment, val inReplyToEventId: EventId?) : NavTarget
|
data class AttachmentPreview(val timelineMode: Timeline.Mode, val attachment: Attachment, val inReplyToEventId: EventId?) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class LocationViewer(val location: Location, val description: String?) : NavTarget
|
data class LocationViewer(val mode: ShowLocationMode) : NavTarget
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class EventDebugInfo(val eventId: EventId?, val debugInfo: TimelineItemDebugInfo) : NavTarget
|
data class EventDebugInfo(val eventId: EventId?, val debugInfo: TimelineItemDebugInfo) : NavTarget
|
||||||
|
|
@ -336,7 +337,7 @@ class MessagesFlowNode(
|
||||||
createNode<AttachmentsPreviewNode>(buildContext, listOf(inputs))
|
createNode<AttachmentsPreviewNode>(buildContext, listOf(inputs))
|
||||||
}
|
}
|
||||||
is NavTarget.LocationViewer -> {
|
is NavTarget.LocationViewer -> {
|
||||||
val inputs = ShowLocationEntryPoint.Inputs(navTarget.location, navTarget.description)
|
val inputs = ShowLocationEntryPoint.Inputs(navTarget.mode)
|
||||||
showLocationEntryPoint.createNode(
|
showLocationEntryPoint.createNode(
|
||||||
parentNode = this,
|
parentNode = this,
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
|
|
@ -558,9 +559,16 @@ class MessagesFlowNode(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is TimelineItemLocationContent -> {
|
is TimelineItemLocationContent -> {
|
||||||
NavTarget.LocationViewer(
|
val mode = ShowLocationMode.Static(
|
||||||
location = event.content.location,
|
location = event.content.location,
|
||||||
description = event.content.description,
|
senderName = event.safeSenderName,
|
||||||
|
senderId = event.senderId,
|
||||||
|
senderAvatarUrl = event.senderAvatar.url,
|
||||||
|
timestamp = event.sentTimeMillis,
|
||||||
|
assetType = event.content.assetType,
|
||||||
|
)
|
||||||
|
NavTarget.LocationViewer(
|
||||||
|
mode = mode
|
||||||
).takeIf { locationService.isServiceAvailable() }
|
).takeIf { locationService.isServiceAvailable() }
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,13 @@
|
||||||
package io.element.android.features.messages.impl.timeline.model.event
|
package io.element.android.features.messages.impl.timeline.model.event
|
||||||
|
|
||||||
import io.element.android.features.location.api.Location
|
import io.element.android.features.location.api.Location
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
|
||||||
data class TimelineItemLocationContent(
|
data class TimelineItemLocationContent(
|
||||||
val body: String,
|
val body: String,
|
||||||
val location: Location,
|
val location: Location,
|
||||||
val description: String? = null,
|
val description: String? = null,
|
||||||
|
val assetType: AssetType? = null,
|
||||||
) : TimelineItemEventContent {
|
) : TimelineItemEventContent {
|
||||||
override val type: String = "TimelineItemLocationContent"
|
override val type: String = "TimelineItemLocationContent"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
import io.element.android.libraries.matrix.api.media.ThumbnailInfo
|
import io.element.android.libraries.matrix.api.media.ThumbnailInfo
|
||||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
import io.element.android.libraries.matrix.api.timeline.item.EventThreadInfo
|
import io.element.android.libraries.matrix.api.timeline.item.EventThreadInfo
|
||||||
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
|
||||||
|
|
@ -98,8 +99,9 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
@Test
|
@Test
|
||||||
fun `test create LocationMessageType not null`() = runTest {
|
fun `test create LocationMessageType not null`() = runTest {
|
||||||
val sut = createTimelineItemContentMessageFactory()
|
val sut = createTimelineItemContentMessageFactory()
|
||||||
|
val assetType = AssetType.SENDER
|
||||||
val result = sut.create(
|
val result = sut.create(
|
||||||
content = createMessageContent(type = LocationMessageType("body", "geo:1,2", "description")),
|
content = createMessageContent(type = LocationMessageType("body", "geo:1,2", "description", assetType)),
|
||||||
senderDisambiguatedDisplayName = "Bob",
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
)
|
)
|
||||||
|
|
@ -107,6 +109,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
body = "body",
|
body = "body",
|
||||||
location = Location(lat = 1.0, lon = 2.0, accuracy = 0.0F),
|
location = Location(lat = 1.0, lon = 2.0, accuracy = 0.0F),
|
||||||
description = "description",
|
description = "description",
|
||||||
|
assetType = assetType,
|
||||||
)
|
)
|
||||||
assertThat(result).isEqualTo(expected)
|
assertThat(result).isEqualTo(expected)
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +118,7 @@ class TimelineItemContentMessageFactoryTest {
|
||||||
fun `test create LocationMessageType null`() = runTest {
|
fun `test create LocationMessageType null`() = runTest {
|
||||||
val sut = createTimelineItemContentMessageFactory()
|
val sut = createTimelineItemContentMessageFactory()
|
||||||
val result = sut.create(
|
val result = sut.create(
|
||||||
content = createMessageContent(type = LocationMessageType("body", "", null)),
|
content = createMessageContent(type = LocationMessageType("body", "", null, null)),
|
||||||
senderDisambiguatedDisplayName = "Bob",
|
senderDisambiguatedDisplayName = "Bob",
|
||||||
eventId = AN_EVENT_ID,
|
eventId = AN_EVENT_ID,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import io.element.android.libraries.matrix.api.media.FileInfo
|
||||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||||
import io.element.android.libraries.matrix.api.media.MediaSource
|
import io.element.android.libraries.matrix.api.media.MediaSource
|
||||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||||
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed interface MessageType
|
sealed interface MessageType
|
||||||
|
|
@ -55,6 +56,7 @@ data class LocationMessageType(
|
||||||
val body: String,
|
val body: String,
|
||||||
val geoUri: String,
|
val geoUri: String,
|
||||||
val description: String?,
|
val description: String?,
|
||||||
|
val assetType: AssetType?,
|
||||||
) : MessageType
|
) : MessageType
|
||||||
|
|
||||||
data class AudioMessageType(
|
data class AudioMessageType(
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,14 @@
|
||||||
package io.element.android.libraries.matrix.impl.room.location
|
package io.element.android.libraries.matrix.impl.room.location
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
import org.matrix.rustcomponents.sdk.AssetType as RustAssetType
|
||||||
|
|
||||||
fun AssetType.toInner(): org.matrix.rustcomponents.sdk.AssetType = when (this) {
|
fun AssetType.into(): RustAssetType = when (this) {
|
||||||
AssetType.SENDER -> org.matrix.rustcomponents.sdk.AssetType.SENDER
|
AssetType.SENDER -> RustAssetType.SENDER
|
||||||
AssetType.PIN -> org.matrix.rustcomponents.sdk.AssetType.PIN
|
AssetType.PIN -> RustAssetType.PIN
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RustAssetType.into(): AssetType = when(this){
|
||||||
|
RustAssetType.SENDER -> AssetType.SENDER
|
||||||
|
RustAssetType.PIN -> AssetType.PIN
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import io.element.android.libraries.matrix.impl.media.MediaUploadHandlerImpl
|
||||||
import io.element.android.libraries.matrix.impl.media.map
|
import io.element.android.libraries.matrix.impl.media.map
|
||||||
import io.element.android.libraries.matrix.impl.poll.toInner
|
import io.element.android.libraries.matrix.impl.poll.toInner
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||||
import io.element.android.libraries.matrix.impl.room.location.toInner
|
import io.element.android.libraries.matrix.impl.room.location.into
|
||||||
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper
|
||||||
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper
|
||||||
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper
|
||||||
|
|
@ -478,7 +478,7 @@ class RustTimeline(
|
||||||
geoUri = geoUri,
|
geoUri = geoUri,
|
||||||
description = description,
|
description = description,
|
||||||
zoomLevel = zoomLevel?.toUByte(),
|
zoomLevel = zoomLevel?.toUByte(),
|
||||||
assetType = assetType?.toInner(),
|
assetType = assetType?.into(),
|
||||||
repliedToEventId = inReplyToEventId?.value,
|
repliedToEventId = inReplyToEventId?.value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageTy
|
||||||
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.matrix.api.timeline.item.event.VoiceMessageType
|
import io.element.android.libraries.matrix.api.timeline.item.event.VoiceMessageType
|
||||||
import io.element.android.libraries.matrix.impl.media.map
|
import io.element.android.libraries.matrix.impl.media.map
|
||||||
|
import io.element.android.libraries.matrix.impl.room.location.into
|
||||||
import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper
|
import io.element.android.libraries.matrix.impl.timeline.reply.InReplyToMapper
|
||||||
import org.matrix.rustcomponents.sdk.InReplyToDetails
|
import org.matrix.rustcomponents.sdk.InReplyToDetails
|
||||||
import org.matrix.rustcomponents.sdk.MessageType
|
import org.matrix.rustcomponents.sdk.MessageType
|
||||||
|
|
@ -112,7 +113,12 @@ class EventMessageMapper {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is RustMessageType.Location -> {
|
is RustMessageType.Location -> {
|
||||||
LocationMessageType(type.content.body, type.content.geoUri, type.content.description)
|
LocationMessageType(
|
||||||
|
body = type.content.body,
|
||||||
|
geoUri = type.content.geoUri,
|
||||||
|
description = type.content.description,
|
||||||
|
assetType = type.content.asset?.into()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
is MessageType.Other -> {
|
is MessageType.Other -> {
|
||||||
OtherMessageType(type.msgtype, type.body)
|
OtherMessageType(type.msgtype, type.body)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import org.junit.Test
|
||||||
class AssetTypeKtTest {
|
class AssetTypeKtTest {
|
||||||
@Test
|
@Test
|
||||||
fun toInner() {
|
fun toInner() {
|
||||||
assertThat(AssetType.SENDER.toInner()).isEqualTo(org.matrix.rustcomponents.sdk.AssetType.SENDER)
|
assertThat(AssetType.SENDER.into()).isEqualTo(org.matrix.rustcomponents.sdk.AssetType.SENDER)
|
||||||
assertThat(AssetType.PIN.toInner()).isEqualTo(org.matrix.rustcomponents.sdk.AssetType.PIN)
|
assertThat(AssetType.PIN.into()).isEqualTo(org.matrix.rustcomponents.sdk.AssetType.PIN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ open class InReplyToDetailsProvider : PreviewParameterProvider<InReplyToDetails>
|
||||||
),
|
),
|
||||||
aMessageContent(
|
aMessageContent(
|
||||||
body = "Location",
|
body = "Location",
|
||||||
type = LocationMessageType("Location", "geo:1,2", null),
|
type = LocationMessageType("Location", "geo:1,2", null, assetType = null),
|
||||||
),
|
),
|
||||||
aMessageContent(
|
aMessageContent(
|
||||||
body = "Notice",
|
body = "Notice",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue