Start cleaning up location code
This commit is contained in:
parent
e6b18668a6
commit
4a271f339a
5 changed files with 80 additions and 85 deletions
|
|
@ -36,6 +36,8 @@ import org.maplibre.spatialk.geojson.Point
|
|||
import org.maplibre.spatialk.geojson.Position
|
||||
import org.maplibre.spatialk.geojson.toJson
|
||||
|
||||
private const val LOCATION_MARKER_ID = "LOCATION_MARKER_ID"
|
||||
|
||||
/**
|
||||
* Data class representing a marker on the map.
|
||||
*
|
||||
|
|
@ -81,7 +83,7 @@ fun LocationPinMarkers(
|
|||
id = JsonPrimitive(marker.id),
|
||||
geometry = Point(Position(marker.location.lon, marker.location.lat)),
|
||||
properties = mapOf(
|
||||
"id" to JsonPrimitive(marker.id),
|
||||
LOCATION_MARKER_ID to JsonPrimitive(marker.id),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -153,7 +155,7 @@ private fun LocationPinMarkerLayer(
|
|||
SymbolLayer(
|
||||
id = "pin-marker-${marker.id}",
|
||||
source = source,
|
||||
filter = !feature.has("point_count") and (feature["id"].asString() eq const(marker.id)),
|
||||
filter = !feature.has("point_count") and (feature[LOCATION_MARKER_ID].asString() eq const(marker.id)),
|
||||
iconImage = image(imageBitmap),
|
||||
iconAnchor = const(SymbolAnchor.Bottom),
|
||||
iconAllowOverlap = const(true),
|
||||
|
|
|
|||
|
|
@ -24,8 +24,13 @@ 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.PermissionsPresenter
|
||||
import io.element.android.features.location.impl.common.permissions.PermissionsState
|
||||
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.designsystem.components.PinVariant
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
|
||||
@AssistedInject
|
||||
class ShowLocationPresenter(
|
||||
|
|
@ -88,9 +93,38 @@ class ShowLocationPresenter(
|
|||
}
|
||||
}
|
||||
|
||||
val markers = remember(mode) {
|
||||
when (mode) {
|
||||
is ShowLocationMode.Static -> {
|
||||
val pinVariant = if (mode.assetType == AssetType.PIN) {
|
||||
PinVariant.PinnedLocation
|
||||
} else {
|
||||
PinVariant.UserLocation(
|
||||
avatarData = AvatarData(
|
||||
id = mode.senderId.value,
|
||||
name = mode.senderName,
|
||||
url = mode.senderAvatarUrl,
|
||||
size = AvatarSize.UserListItem,
|
||||
),
|
||||
isLive = false,
|
||||
)
|
||||
}
|
||||
listOf(
|
||||
LocationMarkerData(
|
||||
id = mode.senderId.value,
|
||||
location = mode.location,
|
||||
variant = pinVariant,
|
||||
)
|
||||
)
|
||||
}
|
||||
ShowLocationMode.Live -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
return ShowLocationState(
|
||||
permissionDialog = permissionDialog,
|
||||
mode = mode,
|
||||
markers = markers,
|
||||
hasLocationPermission = permissionsState.isAnyGranted,
|
||||
isTrackMyLocation = isTrackMyLocation,
|
||||
appName = appName,
|
||||
|
|
|
|||
|
|
@ -9,10 +9,12 @@
|
|||
package io.element.android.features.location.impl.show
|
||||
|
||||
import io.element.android.features.location.api.ShowLocationMode
|
||||
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
||||
|
||||
data class ShowLocationState(
|
||||
val permissionDialog: Dialog,
|
||||
val mode: ShowLocationMode,
|
||||
val markers: List<LocationMarkerData>,
|
||||
val hasLocationPermission: Boolean,
|
||||
val isTrackMyLocation: Boolean,
|
||||
val appName: String,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ package io.element.android.features.location.impl.show
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.location.api.Location
|
||||
import io.element.android.features.location.api.ShowLocationMode
|
||||
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
||||
import io.element.android.libraries.designsystem.components.PinVariant
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
|
||||
|
|
@ -50,18 +54,44 @@ class ShowLocationStateProvider : PreviewParameterProvider<ShowLocationState> {
|
|||
fun aShowLocationState(
|
||||
permissionDialog: ShowLocationState.Dialog = ShowLocationState.Dialog.None,
|
||||
mode: ShowLocationMode = aStaticLocationMode(),
|
||||
markers: List<LocationMarkerData>? = null,
|
||||
hasLocationPermission: Boolean = false,
|
||||
isTrackMyLocation: Boolean = false,
|
||||
appName: String = APP_NAME,
|
||||
eventSink: (ShowLocationEvents) -> Unit = {},
|
||||
) = ShowLocationState(
|
||||
permissionDialog = permissionDialog,
|
||||
mode = mode,
|
||||
hasLocationPermission = hasLocationPermission,
|
||||
isTrackMyLocation = isTrackMyLocation,
|
||||
appName = appName,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
): ShowLocationState {
|
||||
val effectiveMarkers = markers ?: when (mode) {
|
||||
is ShowLocationMode.Static -> listOf(
|
||||
LocationMarkerData(
|
||||
id = mode.senderId.value,
|
||||
location = mode.location,
|
||||
variant = if (mode.assetType == AssetType.PIN) {
|
||||
PinVariant.PinnedLocation
|
||||
} else {
|
||||
PinVariant.UserLocation(
|
||||
avatarData = AvatarData(
|
||||
id = mode.senderId.value,
|
||||
name = mode.senderName,
|
||||
url = mode.senderAvatarUrl,
|
||||
size = AvatarSize.UserListItem,
|
||||
),
|
||||
isLive = true,
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
ShowLocationMode.Live -> emptyList()
|
||||
}
|
||||
return ShowLocationState(
|
||||
permissionDialog = permissionDialog,
|
||||
mode = mode,
|
||||
markers = effectiveMarkers,
|
||||
hasLocationPermission = hasLocationPermission,
|
||||
isTrackMyLocation = isTrackMyLocation,
|
||||
appName = appName,
|
||||
eventSink = eventSink,
|
||||
)
|
||||
}
|
||||
|
||||
fun aStaticLocationMode(
|
||||
location: Location = Location(1.23, 2.34, 4f),
|
||||
|
|
|
|||
|
|
@ -15,33 +15,26 @@ import androidx.compose.material3.rememberBottomSheetScaffoldState
|
|||
import androidx.compose.material3.rememberStandardBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
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.PermissionDeniedDialog
|
||||
import io.element.android.features.location.impl.common.PermissionRationaleDialog
|
||||
import io.element.android.features.location.impl.common.ui.LocationFloatingActionButton
|
||||
import io.element.android.features.location.impl.common.ui.LocationMarkerData
|
||||
import io.element.android.features.location.impl.common.ui.LocationPinMarkers
|
||||
import io.element.android.features.location.impl.common.ui.MapBottomSheetScaffold
|
||||
import io.element.android.features.location.impl.common.ui.UserLocationPuck
|
||||
import io.element.android.libraries.designsystem.components.PinVariant
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import org.maplibre.compose.camera.CameraMoveReason
|
||||
import org.maplibre.compose.camera.CameraPosition
|
||||
|
|
@ -51,10 +44,6 @@ import org.maplibre.compose.location.rememberDefaultLocationProvider
|
|||
import org.maplibre.compose.location.rememberNullLocationProvider
|
||||
import org.maplibre.compose.location.rememberUserLocationState
|
||||
import org.maplibre.spatialk.geojson.Position
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -105,7 +94,7 @@ fun ShowLocationView(
|
|||
}
|
||||
|
||||
val scaffoldState = rememberBottomSheetScaffoldState(
|
||||
bottomSheetState = rememberStandardBottomSheetState(skipHiddenState = false, initialValue = SheetValue.Hidden)
|
||||
bottomSheetState = rememberStandardBottomSheetState(initialValue = SheetValue.PartiallyExpanded)
|
||||
)
|
||||
MapBottomSheetScaffold(
|
||||
scaffoldState = scaffoldState,
|
||||
|
|
@ -137,69 +126,7 @@ fun ShowLocationView(
|
|||
locationState = userLocationState,
|
||||
trackUserLocation = state.isTrackMyLocation
|
||||
)
|
||||
when (val mode = state.mode) {
|
||||
is ShowLocationMode.Static -> {
|
||||
val pinVariant = if (mode.assetType == AssetType.PIN) {
|
||||
PinVariant.PinnedLocation
|
||||
} else {
|
||||
PinVariant.UserLocation(
|
||||
avatarData = AvatarData(mode.senderId.value, mode.senderName, mode.senderAvatarUrl, AvatarSize.UserListItem),
|
||||
isLive = true
|
||||
)
|
||||
}
|
||||
// Generate test markers around the original location
|
||||
val testMarkers = remember {
|
||||
buildList {
|
||||
// Add the original marker
|
||||
add(
|
||||
LocationMarkerData(
|
||||
id = "original",
|
||||
location = mode.location,
|
||||
variant = pinVariant
|
||||
)
|
||||
)
|
||||
// Generate 10 random points within 50 meters
|
||||
val radiusInMeters = 50.0
|
||||
val metersPerDegreeLat = 111_320.0
|
||||
val metersPerDegreeLon = 111_320.0 * cos(Math.toRadians(mode.location.lat))
|
||||
val variants = listOf(
|
||||
PinVariant.StaleLocation,
|
||||
PinVariant.UserLocation(AvatarData("@alice", "Alice", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@bob", "Bob", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@cassy", "Cassy", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@daisy", "Daisy", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@en", "G", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@f", "H", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@g", "I", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@h", "J", null, AvatarSize.TimelineSender), isLive = true),
|
||||
PinVariant.UserLocation(AvatarData("@i", "K", null, AvatarSize.TimelineSender), isLive = true),
|
||||
)
|
||||
repeat(10) { index ->
|
||||
// Random point in a circle using sqrt for uniform distribution
|
||||
val angle = Random.nextDouble() * 2 * Math.PI
|
||||
val distance = sqrt(Random.nextDouble()) * radiusInMeters
|
||||
val latOffset = (distance * cos(angle)) / metersPerDegreeLat
|
||||
val lonOffset = (distance * sin(angle)) / metersPerDegreeLon
|
||||
add(
|
||||
LocationMarkerData(
|
||||
id = "test_$index",
|
||||
location = Location(
|
||||
lat = mode.location.lat + latOffset,
|
||||
lon = mode.location.lon + lonOffset
|
||||
),
|
||||
variant = variants[index % (variants.size-1)]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
LocationPinMarkers(testMarkers)
|
||||
}
|
||||
ShowLocationMode.Live -> {
|
||||
// TODO: Show pins for all active live location sharers
|
||||
}
|
||||
}
|
||||
|
||||
LocationPinMarkers(state.markers)
|
||||
},
|
||||
overlayContent = {
|
||||
LocationFloatingActionButton(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue