Reputation: 309
I have a question regarding usage of Google APIs (in my case NavigationAPI) in clean architecture.
So in order to use google navigation it is necessary to have Navigator object from NavigationApi.getNavigator API. Initially I thought that it’s ok to put all necessary implementation in DATA layer, however this layer is supposed to only hold and provide access to data (local, remote). But then Navigator is highly dependent on SupportNavigationFragment which is in presentation layer. As we know, lower layers shouldn’t depend on higher layers.
So next I thought that all mentioned implementation should be moved to fragment, but I am not 100% sure if this is correct even if it sounds reasonable. Does anyone have any experience with google api in clean arch?
Functionality that I need:
private var navigator: Navigator? = null
override fun initializeNavigator(activity: FragmentActivity) {
NavigationApi.getNavigator(activity, object : NavigationApi.NavigatorListener {
override fun onNavigatorReady(navigator: Navigator?) {
navigator = navigator
addListeners()
}
override fun onError(errorCode: Int) {
handleError(errorCode)
}
})
}
override fun startNavigation() {
navigator?.setAudioGuidance(Navigator.AudioGuidance.VOICE_ALERTS_AND_GUIDANCE)
navigator?.startGuidance()
}
override fun stopNavigation() {
navigator?.stopGuidance()
navigator?.clearDestinations()
}
override fun getRouteSummary(waypoint: Waypoint, options: RoutingOptions) = callbackFlow {
navigator?.let { navigator ->
val pendingRoute = navigator.setDestination(waypoint, options)
pendingRoute.setOnResultListener { code ->
_navigationStateFlow.value = NavigationState.SUMMARY
val result = trySend(
NavigationSummary(
response = code,
meters = navigator.currentTimeAndDistance.meters,
seconds = navigator.currentTimeAndDistance.seconds
)
)
}
awaitClose {
…
}
}
}
private fun addListeners() {
val arrivalListener = Navigator.ArrivalListener {
navigator?.clearDestinations()
}
navigator?.addArrivalListener(arrivalListener)
val routeChangedListener = Navigator.RouteChangedListener {
…
}
navigator?.addRouteChangedListener(routeChangedListener)
}
private fun handleError(errorCode: Int) {
when (errorCode) {
NavigationApi.ErrorCode.NOT_AUTHORIZED -> {
…
}
NavigationApi.ErrorCode.TERMS_NOT_ACCEPTED -> {
...
}
NavigationApi.ErrorCode.NETWORK_ERROR -> {
…
}
NavigationApi.ErrorCode.LOCATION_PERMISSION_MISSING -> {
…
}
else -> {
...
}
}
}
Upvotes: 1
Views: 58
Reputation: 1007369
Quoting the docs:
Note that Navigator is a singleton; if you call this method multiple times, each call will return the same Navigator.
As a result, IMHO, there is no reason to have the Navigator
be part of the UI layer. I would have my own singleton manager that wraps the Navigator
to bridge between the Navigator
API and the internal needs of my app. I would have that manager implement an interface describing its API, so I can create test fakes for it.
In terms of your specific concerns:
initialization of navigator needs activity as parameter (there is also possibility to pass application instead, didn’t check that yet)
Off the cuff, I would use the Application
approach.
setting the destination (via navigator.setDestination(waypoint, options)) has the effect of drawing a path on the map
The result of a Web service call has the effect of updating your UI. This does not mean that you make the Web service call directly from the UI layer. Similarly, the fact that Navigator
triggers UI updates does not mean that the Navigator
should be part of the UI layer.
Upvotes: 0