Skip to content

Commit

Permalink
feat(Android): draw crosshair at selected location (#573)
Browse files Browse the repository at this point in the history
* feat(Android): draw crosshair at selected location

* fix tests
  • Loading branch information
boringcactus authored Dec 11, 2024
1 parent 97218ed commit 4adebca
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.location.Location
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.rememberBottomSheetScaffoldState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.ExperimentalTestApi
Expand Down Expand Up @@ -261,7 +263,10 @@ class NearbyTransitPageTest : KoinTest {
NearbyTransit(
alertData = AlertsStreamDataResponse(builder.alerts),
globalResponse = globalResponse,
lastNearbyTransitLocation = Position(0.0, 0.0),
lastNearbyTransitLocationState =
remember { mutableStateOf(Position(0.0, 0.0)) },
nearbyTransitSelectingLocationState =
remember { mutableStateOf(false) },
scaffoldState = rememberBottomSheetScaffoldState(),
locationDataManager = MockLocationDataManager(Location("mock")),
viewportProvider = ViewportProvider(rememberMapViewportState()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ class NearbyTransitViewTest : KoinTest {
globalResponse = globalResponse,
targetLocation = Position(0.0, 0.0),
setLastLocation = {},
setSelectingLocation = {},
onOpenStopDetails = { _, _ -> }
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class GetNearbyTest {
globalResponse = globalResponse,
location = coordinate1,
setLastLocation = { /* null-op */},
setSelectingLocation = {},
nearbyRepository = nearbyRepository
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ fun ContentView(
}
}
val viewportProvider = remember { ViewportProvider(mapViewportState) }
val lastNearbyTransitLocation by remember { mutableStateOf<Position?>(null) }
val lastNearbyTransitLocationState = remember { mutableStateOf<Position?>(null) }
val nearbyTransitSelectingLocationState = remember { mutableStateOf(false) }
val scaffoldState =
rememberBottomSheetScaffoldState(bottomSheetState = rememberStandardBottomSheetState())
var navBarVisible by remember { mutableStateOf(true) }
Expand All @@ -69,7 +70,8 @@ fun ContentView(
NearbyTransit(
alertData = alertData,
globalResponse = globalResponse,
lastNearbyTransitLocation = lastNearbyTransitLocation,
lastNearbyTransitLocationState = lastNearbyTransitLocationState,
nearbyTransitSelectingLocationState = nearbyTransitSelectingLocationState,
scaffoldState = scaffoldState,
locationDataManager = locationDataManager,
viewportProvider = viewportProvider,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mbta.tid.mbta_app.android.map

import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import com.mbta.tid.mbta_app.android.R

@Composable
fun Crosshairs() {
Column {
Icon(
painterResource(R.drawable.map_nearby_location_cursor),
contentDescription = null,
tint = Color.Unspecified
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -28,12 +29,12 @@ import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.RenderedQueryGeometry
import com.mapbox.maps.RenderedQueryOptions
import com.mapbox.maps.ViewAnnotationAnchor
import com.mapbox.maps.ViewAnnotationOptions
import com.mapbox.maps.extension.compose.DisposableMapEffect
import com.mapbox.maps.extension.compose.MapEffect
import com.mapbox.maps.extension.compose.MapEvents
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.annotation.ViewAnnotation
import com.mapbox.maps.extension.compose.annotation.generated.CircleAnnotation
import com.mapbox.maps.extension.compose.style.MapStyle
import com.mapbox.maps.plugin.gestures.addOnMapClickListener
import com.mapbox.maps.plugin.gestures.generated.GesturesSettings
Expand Down Expand Up @@ -79,6 +80,7 @@ fun HomeMapView(
globalResponse: GlobalResponse?,
alertsData: AlertsStreamDataResponse?,
lastNearbyTransitLocation: Position?,
nearbyTransitSelectingLocationState: MutableState<Boolean>,
locationDataManager: LocationDataManager,
viewportProvider: ViewportProvider,
currentNavEntry: NavBackStackEntry?,
Expand All @@ -87,6 +89,7 @@ fun HomeMapView(
stopDetailsDepartures: StopDetailsDepartures?,
stopDetailsFilter: StopDetailsFilter?
) {
var nearbyTransitSelectingLocation by nearbyTransitSelectingLocationState
val previousNavEntry: NavBackStackEntry? = rememberPrevious(current = currentNavEntry)

val layerManager = remember { LazyObjectQueue<MapLayerManager>() }
Expand All @@ -112,6 +115,10 @@ fun HomeMapView(
val isDarkMode = isSystemInDarkTheme()
val stopMapData: StopMapResponse? = selectedStop?.let { getStopMapData(stopId = it.id) }

val isNearbyNotFollowing =
!viewportProvider.isFollowingPuck &&
currentNavEntry?.destination?.route?.contains("NearbyTransit") == true

fun handleStopClick(map: MapView, point: Point): Boolean {
val pixel = map.mapboxMap.pixelForCoordinate(point)
map.mapboxMap.queryRenderedFeatures(
Expand Down Expand Up @@ -238,7 +245,7 @@ fun HomeMapView(
val zoomLevel by
cameraZoomFlow.collectAsState(initial = ViewportProvider.Companion.Defaults.zoom)

Box(modifier) {
Box(modifier, contentAlignment = Alignment.Center) {
MapboxMap(
Modifier.fillMaxSize(),
mapEvents =
Expand Down Expand Up @@ -336,12 +343,20 @@ fun HomeMapView(
layerManager.`object` = MapLayerManager(map.mapboxMap, context)
}

if (!viewportProvider.isFollowingPuck && lastNearbyTransitLocation != null) {
CircleAnnotation(
point = lastNearbyTransitLocation.toPoint(),
circleColorString = "#ba75c7",
circleRadius = 10.0
)
if (
isNearbyNotFollowing &&
lastNearbyTransitLocation != null &&
!nearbyTransitSelectingLocation
) {
ViewAnnotation(
options =
ViewAnnotationOptions.Builder()
.geometry(lastNearbyTransitLocation.toPoint())
.annotationAnchor { anchor(ViewAnnotationAnchor.CENTER) }
.build()
) {
Crosshairs()
}
}

for (vehicle in vehiclesData) {
Expand Down Expand Up @@ -373,5 +388,18 @@ fun HomeMapView(
Modifier.align(Alignment.TopEnd).padding(16.dp)
)
}

LaunchedEffect(viewportProvider.isManuallyCentering) {
if (
viewportProvider.isManuallyCentering &&
currentNavEntry?.destination?.route?.contains("NearbyTransit") == true
) {
nearbyTransitSelectingLocation = true
}
}

if (nearbyTransitSelectingLocation) {
Crosshairs()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ fun NearbyTransitView(
globalResponse: GlobalResponse?,
targetLocation: Position?,
setLastLocation: (Position) -> Unit,
setSelectingLocation: (Boolean) -> Unit,
onOpenStopDetails: (String, StopDetailsFilter?) -> Unit,
) {
var nearby: NearbyStaticData? =
getNearby(
globalResponse,
targetLocation?.let { Coordinate(latitude = it.latitude, longitude = it.longitude) },
setLastLocation
setLastLocation,
setSelectingLocation,
)
val now = timer(updateInterval = 5.seconds)
val stopIds = remember(nearby) { nearby?.stopIds()?.toList() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -56,11 +57,15 @@ import org.koin.compose.koinInject
data class NearbyTransit(
val alertData: AlertsStreamDataResponse?,
val globalResponse: GlobalResponse?,
var lastNearbyTransitLocation: Position?,
val lastNearbyTransitLocationState: MutableState<Position?>,
val nearbyTransitSelectingLocationState: MutableState<Boolean>,
val scaffoldState: BottomSheetScaffoldState,
val locationDataManager: LocationDataManager,
val viewportProvider: ViewportProvider,
)
) {
var lastNearbyTransitLocation by lastNearbyTransitLocationState
var nearbyTransitSelectingLocation by nearbyTransitSelectingLocationState
}

@OptIn(ExperimentalMaterial3Api::class, MapboxExperimental::class, FlowPreview::class)
@Composable
Expand Down Expand Up @@ -223,6 +228,9 @@ fun NearbyTransitPage(
globalResponse = nearbyTransit.globalResponse,
targetLocation = targetLocation,
setLastLocation = { nearbyTransit.lastNearbyTransitLocation = it },
setSelectingLocation = {
nearbyTransit.nearbyTransitSelectingLocation = it
},
onOpenStopDetails = { stopId, filter ->
navController.navigate(
SheetRoutes.StopDetails(
Expand All @@ -246,6 +254,8 @@ fun NearbyTransitPage(
globalResponse = nearbyTransit.globalResponse,
alertsData = nearbyTransit.alertData,
lastNearbyTransitLocation = nearbyTransit.lastNearbyTransitLocation,
nearbyTransitSelectingLocationState =
nearbyTransit.nearbyTransitSelectingLocationState,
locationDataManager = nearbyTransit.locationDataManager,
viewportProvider = nearbyTransit.viewportProvider,
currentNavEntry = currentNavEntry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class NearbyViewModel(
private val nearbyRepository: INearbyRepository,
private val globalResponse: GlobalResponse?,
private val location: Coordinate?,
setLastLocation: (Position) -> Unit
setLastLocation: (Position) -> Unit,
setSelectingLocation: (Boolean) -> Unit,
) : ViewModel() {
private val _nearbyResponse = MutableStateFlow<NearbyStaticData?>(null)
val nearbyResponse: StateFlow<NearbyStaticData?> = _nearbyResponse
Expand All @@ -36,6 +37,7 @@ class NearbyViewModel(
setLastLocation(
Position(latitude = location.latitude, longitude = location.longitude)
)
setSelectingLocation(false)
}
}
}
Expand All @@ -59,11 +61,18 @@ fun getNearby(
globalResponse: GlobalResponse?,
location: Coordinate?,
setLastLocation: (Position) -> Unit,
setSelectingLocation: (Boolean) -> Unit,
nearbyRepository: INearbyRepository = koinInject()
): NearbyStaticData? {
val viewModel =
remember(globalResponse, location) {
NearbyViewModel(nearbyRepository, globalResponse, location, setLastLocation)
NearbyViewModel(
nearbyRepository,
globalResponse,
location,
setLastLocation,
setSelectingLocation
)
}
return viewModel.nearbyResponse.collectAsState(initial = null).value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="38dp"
android:height="38dp"
android:viewportWidth="38"
android:viewportHeight="38">
<path
android:pathData="M19,19m-13,0a13,13 0,1 1,26 0a13,13 0,1 1,-26 0"
android:strokeAlpha="0.5"
android:strokeWidth="12"
android:fillColor="#00000000"
android:strokeColor="#006CD9"/>
<path
android:pathData="M18,9C18,8.448 18.448,8 19,8C19.552,8 20,8.448 20,9V11.062C23.619,11.513 26.487,14.381 26.938,18H29C29.552,18 30,18.448 30,19C30,19.552 29.552,20 29,20H26.938C26.487,23.619 23.619,26.487 20,26.938V29C20,29.552 19.552,30 19,30C18.448,30 18,29.552 18,29V26.938C14.381,26.487 11.513,23.619 11.062,20H9C8.448,20 8,19.552 8,19C8,18.448 8.448,18 9,18H11.062C11.513,14.381 14.381,11.513 18,11.062V9ZM13,19C13,22.314 15.686,25 19,25C22.314,25 25,22.314 25,19C25,15.686 22.314,13 19,13C15.686,13 13,15.686 13,19Z"
android:fillColor="#66B2FF"
android:fillType="evenOdd"/>
</vector>
16 changes: 16 additions & 0 deletions androidApp/src/main/res/drawable/map_nearby_location_cursor.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="38dp"
android:height="38dp"
android:viewportWidth="38"
android:viewportHeight="38">
<path
android:pathData="M19,19m-13,0a13,13 0,1 1,26 0a13,13 0,1 1,-26 0"
android:strokeAlpha="0.33"
android:strokeWidth="12"
android:fillColor="#00000000"
android:strokeColor="#66B2FF"/>
<path
android:pathData="M18,9C18,8.448 18.448,8 19,8C19.552,8 20,8.448 20,9V11.062C23.619,11.513 26.487,14.381 26.938,18H29C29.552,18 30,18.448 30,19C30,19.552 29.552,20 29,20H26.938C26.487,23.619 23.619,26.487 20,26.938V29C20,29.552 19.552,30 19,30C18.448,30 18,29.552 18,29V26.938C14.381,26.487 11.513,23.619 11.062,20H9C8.448,20 8,19.552 8,19C8,18.448 8.448,18 9,18H11.062C11.513,14.381 14.381,11.513 18,11.062V9ZM13,19C13,22.314 15.686,25 19,25C22.314,25 25,22.314 25,19C25,15.686 22.314,13 19,13C15.686,13 13,15.686 13,19Z"
android:fillColor="#006CD9"
android:fillType="evenOdd"/>
</vector>

0 comments on commit 4adebca

Please sign in to comment.