Skip to content

Commit ab77dea

Browse files
author
Melanie Russlies
committed
Merge branch '220-fetch-gas-station-first-if-location-is-not-in-db-when-observing-pois-by-ids' into 'master'
Resolve "Fetch gas station first if location is not in DB when observing POIs by IDs" Closes #220 See merge request pace/mobile/android/pace-cloud-sdk!237
2 parents 3a9371b + 0e2d9e2 commit ab77dea

File tree

2 files changed

+90
-40
lines changed

2 files changed

+90
-40
lines changed

library/src/main/java/cloud/pace/sdk/poikit/poi/LocationPoint.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ fun LocationPoint.toTileQueryRequest(zoomLevel: Int): TileQueryRequestOuterClass
122122
}
123123

124124
fun Collection<LocationPoint>.toTileQueryRequest(zoomLevel: Int): TileQueryRequestOuterClass.TileQueryRequest {
125-
// Build request from bounding box
126125
val tiles = this
127126
.map { it.tileInfo(zoomLevel) }
128127
.distinct()

library/src/main/java/cloud/pace/sdk/poikit/poi/PoiKitObserverToken.kt

Lines changed: 90 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cloud.pace.sdk.poikit.poi
22

3+
import TileQueryRequestOuterClass
34
import androidx.lifecycle.LiveData
45
import androidx.lifecycle.MutableLiveData
56
import androidx.lifecycle.Observer
@@ -8,7 +9,6 @@ import cloud.pace.sdk.api.poi.POIAPI.gasStations
89
import cloud.pace.sdk.api.poi.generated.request.gasStations.GetGasStationAPI.getGasStation
910
import cloud.pace.sdk.poikit.database.GasStationDAO
1011
import cloud.pace.sdk.poikit.poi.download.TileDownloader
11-
import cloud.pace.sdk.poikit.utils.ApiException
1212
import cloud.pace.sdk.poikit.utils.POIKitConfig
1313
import cloud.pace.sdk.poikit.utils.addPadding
1414
import cloud.pace.sdk.poikit.utils.toTileQueryRequest
@@ -70,7 +70,7 @@ class VisibleRegionNotificationToken(
7070

7171
downloadTask = tileDownloader.load(tileRequest) {
7272
it.onSuccess { stations ->
73-
CoroutineScope(Dispatchers.IO).launch {
73+
onIOBackgroundThread {
7474
gasStationDao.insertGasStations(stations)
7575

7676
withContext(Dispatchers.Main) { loading.value = false }
@@ -89,7 +89,7 @@ class VisibleRegionNotificationToken(
8989

9090
it.onFailure { error ->
9191
completion(Failure(error))
92-
MainScope().launch { loading.value = false }
92+
onMainThread { loading.value = false }
9393
}
9494
}
9595

@@ -119,21 +119,45 @@ class IDsNotificationToken(
119119
override fun refresh(zoomLevel: Int) {
120120
loading.value = true
121121

122-
CoroutineScope(Dispatchers.IO).launch {
123-
val tileRequest = gasStationDao.getByIds(ids).mapNotNull { it.center }.toTileQueryRequest(zoomLevel)
124-
125-
downloadTask = tileDownloader.load(tileRequest) {
126-
it.onSuccess { stations ->
127-
CoroutineScope(Dispatchers.IO).launch {
128-
gasStationDao.insertGasStations(stations)
129-
withContext(Dispatchers.Main) { loading.value = false }
130-
}
131-
}
122+
CoroutineScope(Dispatchers.Default).launch {
123+
val dbStations = gasStationDao.getByIds(ids)
124+
// Gas station we don't have in the database
125+
val missingStationIds = ids - dbStations.map { it.id }
126+
// Database stations without location
127+
val stationsWithoutLocations = dbStations.filter { it.center == null }.map { it.id }
128+
// List of unknown gas station locations
129+
val stationsToFetch = (missingStationIds + stationsWithoutLocations).toSet()
130+
131+
if (stationsToFetch.isNotEmpty()) {
132+
// First fetch gas stations to get locations for tile request
133+
try {
134+
val tileRequest = stationsToFetch
135+
.map {
136+
async {
137+
getGasStation(it)
138+
}
139+
}
140+
.awaitAll()
141+
.mapNotNull {
142+
val latitude = it?.latitude?.toDouble()
143+
val longitude = it?.longitude?.toDouble()
144+
if (latitude != null && longitude != null) {
145+
LocationPoint(latitude, longitude)
146+
} else {
147+
null
148+
}
149+
}
150+
.plus(dbStations.mapNotNull { it.center }) // List of gas stations we already have in the database
151+
.toTileQueryRequest(zoomLevel)
132152

133-
it.onFailure { error ->
134-
completion(Failure(error))
135-
MainScope().launch { loading.value = false }
153+
download(tileRequest)
154+
} catch (e: Exception) {
155+
completion(Failure(e))
136156
}
157+
} else {
158+
// We already have all gas station locations, download the data from the tiles right now
159+
val tileRequest = dbStations.mapNotNull { it.center }.toTileQueryRequest(zoomLevel)
160+
download(tileRequest)
137161
}
138162
}
139163

@@ -144,6 +168,22 @@ class IDsNotificationToken(
144168
gasStations.removeObserver(gasStationsObserver)
145169
downloadTask?.cancel()
146170
}
171+
172+
private suspend fun download(tileRequest: TileQueryRequestOuterClass.TileQueryRequest) = withContext(Dispatchers.IO) {
173+
downloadTask = tileDownloader.load(tileRequest) {
174+
it.onSuccess { stations ->
175+
onIOBackgroundThread {
176+
gasStationDao.insertGasStations(stations)
177+
withContext(Dispatchers.Main) { loading.value = false }
178+
}
179+
}
180+
181+
it.onFailure { error ->
182+
completion(Failure(error))
183+
onMainThread { loading.value = false }
184+
}
185+
}
186+
}
147187
}
148188

149189
class IDNotificationToken(
@@ -163,30 +203,22 @@ class IDNotificationToken(
163203
override fun refresh(zoomLevel: Int) {
164204
loading.value = true
165205

166-
GlobalScope.launch {
206+
onIOBackgroundThread {
167207
val location = gasStationDao.getByIds(listOf(id)).mapNotNull { it.center }.firstOrNull()
168208
if (location != null) {
169209
download(location, zoomLevel)
170210
} else {
171-
API.gasStations.getGasStation(id, false).enqueue {
172-
onResponse = {
173-
val body = it.body()
174-
if (it.isSuccessful && body != null) {
175-
val latitude = body.latitude?.toDouble()
176-
val longitude = body.longitude?.toDouble()
177-
if (latitude != null && longitude != null) {
178-
download(LocationPoint(latitude, longitude), zoomLevel)
179-
} else {
180-
completion(Failure(Exception("Latitude or longitude is null")))
181-
}
182-
} else {
183-
completion(Failure(ApiException(it.code(), it.message(), it.requestId)))
184-
}
185-
}
186-
187-
onFailure = {
188-
completion(Failure(it ?: Exception("Unknown exception")))
211+
try {
212+
val gasStation = getGasStation(id)
213+
val latitude = gasStation?.latitude?.toDouble()
214+
val longitude = gasStation?.longitude?.toDouble()
215+
if (latitude != null && longitude != null) {
216+
download(LocationPoint(latitude, longitude), zoomLevel)
217+
} else {
218+
completion(Failure(Exception("Latitude, longitude or gas station itself is null. Gas station ID: $id")))
189219
}
220+
} catch (e: Exception) {
221+
completion(Failure(e))
190222
}
191223
}
192224
}
@@ -202,15 +234,15 @@ class IDNotificationToken(
202234
private fun download(location: LocationPoint, zoomLevel: Int) {
203235
downloadTask = tileDownloader.load(location.toTileQueryRequest(zoomLevel)) {
204236
it.onSuccess { stations ->
205-
CoroutineScope(Dispatchers.IO).launch {
237+
onIOBackgroundThread {
206238
gasStationDao.insertGasStations(stations)
207239
withContext(Dispatchers.Main) { loading.value = false }
208240
}
209241
}
210242

211243
it.onFailure { error ->
212244
completion(Failure(error))
213-
MainScope().launch { loading.value = false }
245+
onMainThread { loading.value = false }
214246
}
215247
}
216248
}
@@ -236,15 +268,15 @@ class LocationsNotificationToken(
236268
val tileRequest = locations.values.toTileQueryRequest(zoomLevel)
237269
downloadTask = tileDownloader.load(tileRequest) {
238270
it.onSuccess { stations ->
239-
CoroutineScope(Dispatchers.IO).launch {
271+
onIOBackgroundThread {
240272
gasStationDao.insertGasStations(stations)
241273
withContext(Dispatchers.Main) { loading.value = false }
242274
}
243275
}
244276

245277
it.onFailure { error ->
246278
completion(Failure(error))
247-
MainScope().launch { loading.value = false }
279+
onMainThread { loading.value = false }
248280
}
249281
}
250282

@@ -256,3 +288,22 @@ class LocationsNotificationToken(
256288
downloadTask?.cancel()
257289
}
258290
}
291+
292+
suspend fun getGasStation(id: String) = withContext(Dispatchers.IO) {
293+
suspendCancellableCoroutine<cloud.pace.sdk.api.poi.generated.model.GasStation?> { continuation ->
294+
API.gasStations.getGasStation(id, false).enqueue {
295+
onResponse = {
296+
val body = it.body()
297+
if (it.isSuccessful && body != null) {
298+
continuation.resumeIfActive(body)
299+
} else {
300+
continuation.resumeIfActive(null)
301+
}
302+
}
303+
304+
onFailure = {
305+
continuation.resumeIfActive(null)
306+
}
307+
}
308+
}
309+
}

0 commit comments

Comments
 (0)