Skip to content

Commit

Permalink
detect click listener on speed info view
Browse files Browse the repository at this point in the history
  • Loading branch information
abhishek1508 committed Jan 12, 2023
1 parent fac840d commit 93428c5
Show file tree
Hide file tree
Showing 27 changed files with 197 additions and 114 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ Mapbox welcomes participation and contributions from everyone.
}
}
```
- Added guarantees that route progress with `RouteProgress#currentState == OFF_ROUTE` arrives earlier than `NavigationRerouteController#reroute` is called. [#6764](https://github.com/mapbox/mapbox-navigation-android/pull/6764)
- Fixed a rare `java.lang.NullPointerException: Attempt to read from field 'SpeechAnnouncement PlayCallback.announcement' on a null object reference` crash in `PlayCallback.getAnnouncement`. [#6760](https://github.com/mapbox/mapbox-navigation-android/pull/6760)
- Fixed standalone `MapboxManeuverView` appearance when the app also integrates Drop-In UI. [#6774](https://github.com/mapbox/mapbox-navigation-android/pull/6774)
- Introduced `NavigationViewListener.onSpeedInfoClicked` that would be triggered when `MapboxSpeedInfoView` is clicked upon. [#6770](https://github.com/mapbox/mapbox-navigation-android/pull/6770)

## Mapbox Navigation SDK 2.10.0-rc.1 - 16 December, 2022
### Changelog
Expand Down
1 change: 1 addition & 0 deletions libnavui-dropin/api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ package com.mapbox.navigation.dropin.navigationview {
method public void onRouteFetchSuccessful(java.util.List<com.mapbox.navigation.base.route.NavigationRoute> routes);
method public void onRouteFetching(long requestId);
method public void onRoutePreview();
method public void onSpeedInfoClicked(com.mapbox.navigation.ui.speedlimit.model.SpeedInfoValue? speedInfo);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.mapbox.navigation.dropin

import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

internal class ClickBehavior<T> {

private val _onViewClicked = MutableSharedFlow<T>(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
val onViewClicked = _onViewClicked.asSharedFlow()

fun onClicked(value: T) {
_onViewClicked.tryEmit(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal class InfoPanelComponent(

val navigationView = findNavigationView()
combine(
context.infoPanelBehavior.slideOffset,
context.behavior.infoPanelBehavior.slideOffset,
context.systemBarsInsets
) { slideOffset, insets ->
slideOffset to insets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ internal class InfoPanelCoordinator(

private val updateGuideline = object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
context.infoPanelBehavior.updateBottomSheetState(newState)
context.behavior.infoPanelBehavior.updateBottomSheetState(newState)
updateGuidelinePosition()
}

override fun onSlide(bottomSheet: View, slideOffset: Float) {
context.infoPanelBehavior.updateSlideOffset(slideOffset)
context.behavior.infoPanelBehavior.updateSlideOffset(slideOffset)
updateGuidelinePosition()
}
}
Expand Down Expand Up @@ -184,7 +184,7 @@ internal class InfoPanelCoordinator(
BottomSheetBehavior.STATE_HIDDEN -> -1.0f
else -> null
}?.also { slideOffset ->
context.infoPanelBehavior.updateSlideOffset(slideOffset)
context.behavior.infoPanelBehavior.updateSlideOffset(slideOffset)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import kotlinx.coroutines.flow.asStateFlow

internal class ManeuverBehavior {

private val _maneuverBehavior = MutableStateFlow<MapboxManeuverViewState>(
private val _maneuverViewState = MutableStateFlow<MapboxManeuverViewState>(
MapboxManeuverViewState.COLLAPSED
)
private val _maneuverViewVisibility = MutableStateFlow(false)

val maneuverBehavior = _maneuverBehavior.asStateFlow()
val maneuverViewState = _maneuverViewState.asStateFlow()
val maneuverViewVisibility = _maneuverViewVisibility.asStateFlow()

fun updateBehavior(newState: MapboxManeuverViewState) {
_maneuverBehavior.value = newState
_maneuverViewState.value = newState
}

fun updateViewVisibility(visibility: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ internal class ManeuverComponentContractImpl(
) : ManeuverComponentContract {

override fun onManeuverViewStateChanged(state: MapboxManeuverViewState) {
context.maneuverBehavior.updateBehavior(state)
context.behavior.maneuverBehavior.updateBehavior(state)
}

override fun onManeuverViewVisibilityChanged(isVisible: Boolean) {
context.maneuverBehavior.updateViewVisibility(isVisible)
context.behavior.maneuverBehavior.updateViewVisibility(isVisible)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ abstract class MapViewBinder : UIBinder {

private fun routeLineComponent(lineOptions: MapboxRouteLineOptions, mapView: MapView) =
RouteLineComponent(mapView.getMapboxMap(), mapView, lineOptions, contractProvider = {
RouteLineComponentContractImpl(context.store, context.mapClickBehavior)
RouteLineComponentContractImpl(context.store, context.behavior.mapClickBehavior)
})

private fun longPressMapComponent(navigationState: NavigationState, mapView: MapView) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.mapbox.navigation.dropin.map
import com.mapbox.geojson.Point
import com.mapbox.navigation.base.route.NavigationRoute
import com.mapbox.navigation.core.MapboxNavigation
import com.mapbox.navigation.dropin.ClickBehavior
import com.mapbox.navigation.ui.app.internal.Store
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
import com.mapbox.navigation.ui.app.internal.routefetch.RoutePreviewAction
Expand All @@ -14,7 +15,7 @@ import kotlinx.coroutines.flow.combine

internal class RouteLineComponentContractImpl(
private val store: Store,
private val mapClickBehavior: MapClickBehavior,
private val mapClickBehavior: ClickBehavior<Point>,
) : RouteLineComponentContract {
override fun setRoutes(mapboxNavigation: MapboxNavigation, routes: List<NavigationRoute>) {
when (store.state.value.navigation) {
Expand Down Expand Up @@ -48,6 +49,6 @@ internal class RouteLineComponentContractImpl(
}

override fun onMapClicked(point: Point) {
mapClickBehavior.onMapClicked(point)
mapClickBehavior.onClicked(point)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal class ScalebarPlaceholderBinder(
return ScalebarPlaceholderComponent(
binding.scalebarPlaceholder,
context.options.showMapScalebar,
context.maneuverBehavior.maneuverViewVisibility
context.behavior.maneuverBehavior.maneuverViewVisibility
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.mapbox.navigation.dropin.navigationview

import com.mapbox.geojson.Point
import com.mapbox.navigation.dropin.ClickBehavior
import com.mapbox.navigation.dropin.infopanel.InfoPanelBehavior
import com.mapbox.navigation.dropin.maneuver.ManeuverBehavior
import com.mapbox.navigation.ui.speedlimit.model.SpeedInfoValue

internal class NavigationViewBehavior {
val maneuverBehavior = ManeuverBehavior()
val infoPanelBehavior = InfoPanelBehavior()
val mapClickBehavior = ClickBehavior<Point>()
val speedInfoBehavior = ClickBehavior<SpeedInfoValue?>()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import com.mapbox.navigation.dropin.NavigationView
import com.mapbox.navigation.dropin.ViewBinderCustomization
import com.mapbox.navigation.dropin.ViewOptionsCustomization
import com.mapbox.navigation.dropin.ViewStyleCustomization
import com.mapbox.navigation.dropin.infopanel.InfoPanelBehavior
import com.mapbox.navigation.dropin.maneuver.ManeuverBehavior
import com.mapbox.navigation.dropin.map.MapClickBehavior
import com.mapbox.navigation.dropin.map.MapStyleLoader
import com.mapbox.navigation.dropin.map.MapViewOwner
import com.mapbox.navigation.dropin.map.marker.MapMarkerFactory
Expand Down Expand Up @@ -45,17 +42,15 @@ internal class NavigationViewContext(
val uiBinders = NavigationViewBinder()
val styles = NavigationViewStyles(context)
val options = NavigationViewOptions(context)
val maneuverBehavior = ManeuverBehavior()
val infoPanelBehavior = InfoPanelBehavior()
val mapViewOwner = MapViewOwner()
val mapStyleLoader = MapStyleLoader(context, options)
val mapClickBehavior = MapClickBehavior()
val behavior by lazy {
NavigationViewBehavior()
}
val listenerRegistry by lazy {
NavigationViewListenerRegistry(
store,
maneuverBehavior,
infoPanelBehavior,
mapClickBehavior,
behavior,
lifecycleOwner.lifecycleScope
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import com.mapbox.navigation.base.route.Router
import com.mapbox.navigation.base.route.RouterFailure
import com.mapbox.navigation.base.route.RouterOrigin
import com.mapbox.navigation.dropin.NavigationView
import com.mapbox.navigation.ui.speedlimit.model.SpeedInfoValue
import com.mapbox.navigation.ui.speedlimit.view.MapboxSpeedInfoView

/**
* Interface definition for the NavigationView listener.
Expand Down Expand Up @@ -159,4 +161,11 @@ abstract class NavigationViewListener {
* @param point The projected map coordinate the user clicked on.
*/
open fun onMapClicked(point: Point) = Unit

/**
* Called when [MapboxSpeedInfoView] was clicked
*
* @param speedInfo [SpeedInfoValue] at the time user clicked on the view.
*/
open fun onSpeedInfoClicked(speedInfo: SpeedInfoValue?) = Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package com.mapbox.navigation.dropin.navigationview

import androidx.annotation.VisibleForTesting
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.mapbox.navigation.dropin.infopanel.InfoPanelBehavior
import com.mapbox.navigation.dropin.maneuver.ManeuverBehavior
import com.mapbox.navigation.dropin.map.MapClickBehavior
import com.mapbox.navigation.ui.app.internal.Store
import com.mapbox.navigation.ui.app.internal.camera.TargetCameraMode
import com.mapbox.navigation.ui.app.internal.navigation.NavigationState
Expand All @@ -20,10 +17,8 @@ import kotlinx.coroutines.launch

internal class NavigationViewListenerRegistry(
private val store: Store,
private val maneuverSubscriber: ManeuverBehavior,
private val infoPanelSubscriber: InfoPanelBehavior,
private val mapClickSubscriber: MapClickBehavior,
private val coroutineScope: CoroutineScope
private val behavior: NavigationViewBehavior,
private val coroutineScope: CoroutineScope,
) {
private var listeners = mutableMapOf<NavigationViewListener, Job>()

Expand Down Expand Up @@ -93,7 +88,8 @@ internal class NavigationViewListenerRegistry(
}
}
launch {
infoPanelSubscriber
behavior
.infoPanelBehavior
.bottomSheetState
.filterNotNull()
.collect { behavior ->
Expand All @@ -107,11 +103,12 @@ internal class NavigationViewListenerRegistry(
}
}
launch {
infoPanelSubscriber.slideOffset.collect(listener::onInfoPanelSlide)
behavior.infoPanelBehavior.slideOffset.collect(listener::onInfoPanelSlide)
}
launch {
maneuverSubscriber
behavior
.maneuverBehavior
.maneuverViewState
.collect { behavior ->
when (behavior) {
MapboxManeuverViewState.EXPANDED -> {
Expand All @@ -124,9 +121,13 @@ internal class NavigationViewListenerRegistry(
}
}

mapClickSubscriber.mapClickBehavior
behavior.mapClickBehavior.onViewClicked
.onEach { listener.onMapClicked(it) }
.launchIn(scope = this)

behavior.speedInfoBehavior.onViewClicked
.onEach { listener.onSpeedInfoClicked(it) }
.launchIn(scope = this)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class RoadNameCoordinator(
super.onAttached(mapboxNavigation)

coroutineScope.launch {
context.infoPanelBehavior.bottomSheetState
context.behavior.infoPanelBehavior.bottomSheetState
.filterNotNull()
.map { bottomSheetState ->
val isBottomSheetVisible = bottomSheetState != BottomSheetBehavior.STATE_HIDDEN
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.mapbox.navigation.dropin.speedlimit

import com.mapbox.navigation.dropin.ClickBehavior
import com.mapbox.navigation.ui.speedlimit.internal.SpeedInfoComponentContract
import com.mapbox.navigation.ui.speedlimit.model.SpeedInfoValue

internal class SpeedInfoComponentContractImpl(
private val speedInfoBehavior: ClickBehavior<SpeedInfoValue?>
) : SpeedInfoComponentContract {

override fun onSpeedInfoClicked(speedInfo: SpeedInfoValue?) {
speedInfoBehavior.onClicked(speedInfo)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ internal class SpeedInfoViewBinder(
speedInfoOptions = options,
speedInfoView = binding.speedInfoView,
distanceFormatterOptions = distanceFormatter,
contractProvider = {
SpeedInfoComponentContractImpl(context.behavior.speedInfoBehavior)
}
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.mapbox.navigation.dropin

import com.mapbox.geojson.Point
import com.mapbox.navigation.ui.speedlimit.model.SpeedInfoValue
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Assert.assertEquals
import org.junit.Test

@ExperimentalCoroutinesApi
class ClickBehaviorTest {

@Test
fun `when speed info is clicked, event is received`() = runBlockingTest {
val sut = ClickBehavior<SpeedInfoValue?>()
val events = arrayListOf<SpeedInfoValue?>()
val job = sut.onViewClicked.onEach { events.add(it) }.launchIn(scope = this)

val speedInfo = mockk<SpeedInfoValue>()
sut.onClicked(speedInfo)
job.cancelAndJoin()

assertEquals(listOf(speedInfo), events)
}

@Test
fun `when map is clicked, event is received`() = runBlockingTest {
val sut = ClickBehavior<Point>()
val events = arrayListOf<Point>()
val job = sut.onViewClicked.onEach { events.add(it) }.launchIn(scope = this)

val point = mockk<Point>()
sut.onClicked(point)
job.cancelAndJoin()

assertEquals(listOf(point), events)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ManeuverBehaviorTest {

sut.updateBehavior(MapboxManeuverViewState.EXPANDED)

assertEquals(MapboxManeuverViewState.EXPANDED, sut.maneuverBehavior.value)
assertEquals(MapboxManeuverViewState.EXPANDED, sut.maneuverViewState.value)
}

@Test
Expand Down
Loading

0 comments on commit 93428c5

Please sign in to comment.