Skip to content

Commit 14b8460

Browse files
tomaszrybakiewiczabhishek1508
authored andcommitted
Moved LocationComponent to libnavui-map module.
Split LocationComponent into two separate components. One responsible for observing MapboxNavigation location and updating NavigationLocationProvider, and second for configuring and updating Location Puck.
1 parent 568d1e9 commit 14b8460

15 files changed

Lines changed: 415 additions & 279 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Mapbox welcomes participation and contributions from everyone.
66
#### Features
77
- Moved `MapboxAudioGuidance` and `MapboxAudioGuidanceState` into public api. [#6336](https://github.com/mapbox/mapbox-navigation-android/pull/6336)
88
- Introduced `ViewOptionsCustomization.showCameraDebugInfo` to allow end users to enable camera debug info. [#6356](https://github.com/mapbox/mapbox-navigation-android/pull/6356)
9+
- Added `ComponentInstaller` for the `LocationComponent` that offers simplified integration of map LocationPuck. [#6206](https://github.com/mapbox/mapbox-navigation-android/pull/6206)
910
#### Bug fixes and improvements
1011
- Marked `PredictiveCacheController`, `MapboxBuildingView`, `ViewportDataSourceUpdateObserver`, `NavigationScaleGestureHandler`, `NavigationCameraStateChangedObserver`, `NavigationCameraStateTransition`, `NavigationCameraTransition`, `TransitionEndListener`, `MapboxRecenterButton`, `MapboxRouteOverviewButton`, `MapboxJunctionView`, `MapboxSignboardView`, `MapboxRoadNameLabelView`, `MapboxRoadNameView`, `MapboxRouteArrowView`, `MapboxRouteLineView`, `MapboxCameraModeButton` methods and `View.capture` extension with `@UiThread` annotation. [#6235](https://github.com/mapbox/mapbox-navigation-android/pull/6235)
1112
- Marked `Binder`, `MapboxExtendableButton` methods and `MapboxNavigation#installComponents` methods with `@UiThread` annotation. [#6268](https://github.com/mapbox/mapbox-navigation-android/pull/6268)
Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

3-
import android.location.Location
4-
import com.mapbox.geojson.Point
53
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
64
import com.mapbox.navigation.core.MapboxNavigation
75
import com.mapbox.navigation.core.internal.extensions.flowLocationMatcherResult
@@ -10,8 +8,6 @@ import com.mapbox.navigation.ui.app.internal.Action
108
import com.mapbox.navigation.ui.app.internal.State
119
import com.mapbox.navigation.ui.app.internal.Store
1210
import com.mapbox.navigation.ui.app.internal.location.LocationAction
13-
import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
14-
import kotlinx.coroutines.delay
1511

1612
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
1713
class LocationStateController(
@@ -21,26 +17,12 @@ class LocationStateController(
2117
store.register(this)
2218
}
2319

24-
val navigationLocationProvider = NavigationLocationProvider()
25-
26-
/**
27-
* Accessor function to get the last [Point]
28-
*/
29-
val lastPoint: Point?
30-
get() = navigationLocationProvider.lastLocation?.run {
31-
Point.fromLngLat(longitude, latitude)
32-
}
20+
override fun onAttached(mapboxNavigation: MapboxNavigation) {
21+
super.onAttached(mapboxNavigation)
3322

34-
/**
35-
* Suspend until a non-null location is available.
36-
*/
37-
suspend fun firstLocation(): Location {
38-
var nonNullLocation: Location? = navigationLocationProvider.lastLocation
39-
while (nonNullLocation == null) {
40-
delay(DELAY_FIRST_LOCATION_MS)
41-
nonNullLocation = navigationLocationProvider.lastLocation
23+
mapboxNavigation.flowLocationMatcherResult().observe {
24+
store.dispatch(LocationAction.Update(it))
4225
}
43-
return nonNullLocation
4426
}
4527

4628
override fun process(state: State, action: Action): State {
@@ -53,25 +35,9 @@ class LocationStateController(
5335
private fun processLocationAction(
5436
state: LocationMatcherResult?,
5537
action: LocationAction
56-
): LocationMatcherResult? {
38+
): LocationMatcherResult {
5739
return when (action) {
5840
is LocationAction.Update -> action.result
5941
}
6042
}
61-
62-
override fun onAttached(mapboxNavigation: MapboxNavigation) {
63-
super.onAttached(mapboxNavigation)
64-
65-
mapboxNavigation.flowLocationMatcherResult().observe {
66-
navigationLocationProvider.changePosition(
67-
location = it.enhancedLocation,
68-
keyPoints = it.keyPoints,
69-
)
70-
store.dispatch(LocationAction.Update(it))
71-
}
72-
}
73-
74-
private companion object {
75-
const val DELAY_FIRST_LOCATION_MS = 100L
76-
}
7743
}
Lines changed: 40 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,83 @@
11
package com.mapbox.navigation.ui.app.internal.controller
22

33
import android.location.Location
4-
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
4+
import android.location.LocationManager
55
import com.mapbox.navigation.core.MapboxNavigation
6-
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
6+
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
77
import com.mapbox.navigation.core.trip.session.LocationObserver
88
import com.mapbox.navigation.testing.MainCoroutineRule
99
import com.mapbox.navigation.ui.app.internal.location.LocationAction
1010
import com.mapbox.navigation.ui.app.testing.TestStore
11-
import com.mapbox.navigation.ui.app.testing.TestingUtil.makeLocationMatcherResult
12-
import io.mockk.Runs
1311
import io.mockk.every
14-
import io.mockk.just
1512
import io.mockk.mockk
16-
import io.mockk.mockkObject
1713
import io.mockk.slot
1814
import io.mockk.spyk
19-
import io.mockk.unmockkAll
2015
import io.mockk.verify
21-
import io.mockk.verifyOrder
2216
import kotlinx.coroutines.ExperimentalCoroutinesApi
2317
import kotlinx.coroutines.test.runBlockingTest
24-
import org.junit.After
2518
import org.junit.Assert.assertEquals
2619
import org.junit.Before
2720
import org.junit.Rule
2821
import org.junit.Test
2922

30-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class, ExperimentalCoroutinesApi::class)
23+
@OptIn(ExperimentalCoroutinesApi::class)
3124
class LocationStateControllerTest {
3225

3326
@get:Rule
3427
var coroutineRule = MainCoroutineRule()
3528

36-
private val locationObserverSlot = slot<LocationObserver>()
37-
private val store = spyk(TestStore())
38-
private val sut = LocationStateController(store)
39-
40-
private fun mockMapboxNavigation(): MapboxNavigation {
41-
val mapboxNavigation = mockk<MapboxNavigation>(relaxed = true) {
42-
every { registerLocationObserver(capture(locationObserverSlot)) } just Runs
43-
}
44-
every { MapboxNavigationApp.current() } returns mapboxNavigation
45-
return mapboxNavigation
46-
}
29+
private lateinit var store: TestStore
30+
private lateinit var sut: LocationStateController
31+
private lateinit var mapboxNavigation: MapboxNavigation
4732

4833
@Before
49-
fun setup() {
50-
mockkObject(MapboxNavigationApp)
51-
}
52-
53-
@After
54-
fun teardown() {
55-
unmockkAll()
34+
fun setUp() {
35+
mapboxNavigation = mockk(relaxed = true)
36+
store = spyk(TestStore())
37+
sut = LocationStateController(store)
5638
}
5739

5840
@Test
59-
fun `onAttached will registerLocationObserver onNewLocationMatcherResult updates state`() =
41+
fun `onAttached should subscribe as LocationObserver and dispatch location Update action`() =
6042
runBlockingTest {
61-
sut.onAttached(mockMapboxNavigation())
62-
val newLocationUpdate = makeLocationMatcherResult(1.0, 2.0, 0f)
63-
locationObserverSlot.captured.onNewLocationMatcherResult(newLocationUpdate)
43+
val locationMatcherResult = locationMatcherResult(location(1.0, 2.0))
44+
givenLocationUpdate(locationMatcherResult)
6445

65-
verify {
66-
store.dispatch(LocationAction.Update(newLocationUpdate))
67-
}
68-
}
69-
70-
@Test
71-
fun `onAttached registerLocationObserver onNewLocationMatcherResult updates navigationLocationProvider`() =
72-
runBlockingTest {
73-
val mockLocation: Location = mockk {
74-
every { longitude } returns -109.587335
75-
every { latitude } returns 38.731370
76-
}
46+
sut.onAttached(mapboxNavigation)
7747

78-
sut.onAttached(mockMapboxNavigation())
79-
locationObserverSlot.captured.onNewLocationMatcherResult(
80-
mockk(relaxed = true) {
81-
every { enhancedLocation } returns mockLocation
82-
every { keyPoints } returns listOf(mockLocation)
83-
}
84-
)
85-
86-
with(sut.navigationLocationProvider.lastLocation!!) {
87-
assertEquals(-109.587335, longitude, 0.00001)
88-
assertEquals(38.731370, latitude, 0.00001)
89-
}
48+
verify { store.dispatch(LocationAction.Update(locationMatcherResult)) }
9049
}
9150

9251
@Test
93-
fun `onAttached registerLocationObserver onNewLocationMatcherResult updates lastPoint`() =
94-
runBlockingTest {
95-
val mockLocation: Location = mockk {
96-
every { longitude } returns -109.587335
97-
every { latitude } returns 38.731370
98-
}
52+
fun `process should process LocationAction and update store state`() {
53+
val action = LocationAction.Update(
54+
locationMatcherResult(location(2.0, 3.0))
55+
)
9956

100-
sut.onAttached(mockMapboxNavigation())
101-
locationObserverSlot.captured.onNewLocationMatcherResult(
102-
mockk(relaxed = true) {
103-
every { enhancedLocation } returns mockLocation
104-
every { keyPoints } returns listOf(mockLocation)
105-
}
106-
)
57+
val state = sut.process(store.state.value, action)
58+
59+
assertEquals(action.result, state.location)
60+
}
10761

108-
with(sut.lastPoint!!) {
109-
assertEquals(-109.587335, longitude(), 0.00001)
110-
assertEquals(38.731370, latitude(), 0.00001)
111-
}
62+
private fun givenLocationUpdate(result: LocationMatcherResult) {
63+
val locObserver = slot<LocationObserver>()
64+
every {
65+
mapboxNavigation.registerLocationObserver(capture(locObserver))
66+
} answers {
67+
locObserver.captured.onNewRawLocation(result.enhancedLocation)
68+
locObserver.captured.onNewLocationMatcherResult(result)
11269
}
70+
}
11371

114-
@Test
115-
fun `onDetached will unregisterLocationObserver`() = runBlockingTest {
116-
val mockMapboxNavigation = mockMapboxNavigation()
117-
sut.onAttached(mockMapboxNavigation)
118-
sut.onDetached(mockMapboxNavigation)
72+
private fun locationMatcherResult(location: Location) = mockk<LocationMatcherResult> {
73+
every { enhancedLocation } returns location
74+
every { keyPoints } returns emptyList()
75+
}
11976

120-
verifyOrder {
121-
mockMapboxNavigation.registerLocationObserver(any())
122-
mockMapboxNavigation.unregisterLocationObserver(any())
123-
}
77+
private fun location(latitude: Double, longitude: Double) = Location(
78+
LocationManager.PASSIVE_PROVIDER
79+
).apply {
80+
this.latitude = latitude
81+
this.longitude = longitude
12482
}
12583
}

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/NavigationViewContext.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.mapbox.navigation.dropin.util.BitmapMemoryCache
1313
import com.mapbox.navigation.dropin.util.BitmapMemoryCache.Companion.MB_IN_BYTES
1414
import com.mapbox.navigation.ui.app.internal.SharedApp
1515
import com.mapbox.navigation.ui.app.internal.Store
16+
import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
1617
import com.mapbox.navigation.ui.utils.internal.Provider
1718
import com.mapbox.navigation.ui.utils.internal.getValue
1819
import kotlinx.coroutines.flow.MutableStateFlow
@@ -53,6 +54,7 @@ internal class NavigationViewContext(
5354
lifecycleOwner.lifecycleScope
5455
)
5556
}
57+
val locationProvider = NavigationLocationProvider()
5658

5759
fun mapAnnotationFactory() = MapMarkerFactory(
5860
context,

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/binder/map/MapBinder.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.mapbox.navigation.dropin.binder.map
22

33
import android.view.ViewGroup
4+
import androidx.core.content.ContextCompat
45
import com.mapbox.maps.MapView
6+
import com.mapbox.maps.plugin.LocationPuck2D
57
import com.mapbox.maps.plugin.compass.compass
8+
import com.mapbox.maps.plugin.locationcomponent.location
69
import com.mapbox.maps.plugin.scalebar.scalebar
710
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
811
import com.mapbox.navigation.base.route.NavigationRoute
@@ -12,24 +15,26 @@ import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver
1215
import com.mapbox.navigation.dropin.NavigationViewContext
1316
import com.mapbox.navigation.dropin.component.camera.CameraComponent
1417
import com.mapbox.navigation.dropin.component.camera.CameraLayoutObserver
15-
import com.mapbox.navigation.dropin.component.location.LocationComponent
1618
import com.mapbox.navigation.dropin.component.logo.LogoAttributionComponent
1719
import com.mapbox.navigation.dropin.component.marker.FreeDriveLongPressMapComponent
1820
import com.mapbox.navigation.dropin.component.marker.GeocodingComponent
1921
import com.mapbox.navigation.dropin.component.marker.MapMarkersComponent
2022
import com.mapbox.navigation.dropin.component.marker.RoutePreviewLongPressMapComponent
2123
import com.mapbox.navigation.dropin.databinding.MapboxNavigationViewLayoutBinding
2224
import com.mapbox.navigation.dropin.internal.extensions.reloadOnChange
23-
import com.mapbox.navigation.ui.app.internal.SharedApp
25+
import com.mapbox.navigation.ui.app.R
2426
import com.mapbox.navigation.ui.app.internal.Store
2527
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
2628
import com.mapbox.navigation.ui.app.internal.routefetch.RoutePreviewAction
2729
import com.mapbox.navigation.ui.app.internal.routefetch.RoutePreviewState
2830
import com.mapbox.navigation.ui.app.internal.routefetch.RoutesAction
2931
import com.mapbox.navigation.ui.base.lifecycle.UIBinder
32+
import com.mapbox.navigation.ui.maps.internal.ui.LocationComponent
33+
import com.mapbox.navigation.ui.maps.internal.ui.LocationPuckComponent
3034
import com.mapbox.navigation.ui.maps.internal.ui.RouteArrowComponent
3135
import com.mapbox.navigation.ui.maps.internal.ui.RouteLineComponent
3236
import com.mapbox.navigation.ui.maps.internal.ui.RouteLineComponentContract
37+
import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
3338
import com.mapbox.navigation.ui.maps.route.arrow.model.RouteArrowOptions
3439
import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions
3540
import kotlinx.coroutines.FlowPreview
@@ -55,7 +60,8 @@ internal class MapBinder(
5560
val navigationState = store.select { it.navigation }
5661
return navigationListOf(
5762
CameraLayoutObserver(store, mapView, binding),
58-
LocationComponent(mapView, SharedApp.locationStateController),
63+
LocationComponent(context.locationProvider),
64+
locationPuckComponent(context.locationProvider),
5965
LogoAttributionComponent(mapView, context.systemBarsInsets),
6066
reloadOnChange(
6167
context.mapStyleLoader.loadedMapStyle,
@@ -85,6 +91,23 @@ internal class MapBinder(
8591
)
8692
}
8793

94+
private fun locationPuckComponent(
95+
locationProvider: NavigationLocationProvider
96+
): LocationPuckComponent {
97+
val locationPuck = LocationPuck2D(
98+
bearingImage = ContextCompat.getDrawable(
99+
mapView.context,
100+
R.drawable.mapbox_navigation_puck_icon
101+
)
102+
)
103+
return LocationPuckComponent(
104+
mapView.getMapboxMap(),
105+
mapView.location,
106+
locationPuck,
107+
locationProvider
108+
)
109+
}
110+
88111
private fun routeLineComponent(lineOptions: MapboxRouteLineOptions) =
89112
RouteLineComponent(mapView.getMapboxMap(), mapView, lineOptions, contractProvider = {
90113
RouteLineComponentContractImpl(store)

libnavui-dropin/src/main/java/com/mapbox/navigation/dropin/component/location/LocationComponent.kt

Lines changed: 0 additions & 39 deletions
This file was deleted.

0 commit comments

Comments
 (0)