Skip to content

Commit b606eb6

Browse files
committed
NAVAND-777: remove expiring data only after timeout
1 parent afda02e commit b606eb6

File tree

12 files changed

+462
-188
lines changed

12 files changed

+462
-188
lines changed

instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/RouteRefreshStateTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ class RouteRefreshStateTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
352352
mapboxNavigation.setNavigationRoutesAndWaitForUpdate(requestedRoutes)
353353
mapboxNavigation.refreshRoutesImmediately()
354354

355-
waitForRefresh()
355+
delay(2500) // execute request
356356

357357
assertEquals(
358358
listOf(
@@ -461,7 +461,7 @@ class RouteRefreshStateTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
461461
mapboxNavigation.refreshRoutesImmediately()
462462
delay(5000)
463463

464-
waitForRefreshes(2) // immediate + planned
464+
waitForRefresh()
465465

466466
assertEquals(
467467
listOf(
@@ -533,7 +533,7 @@ class RouteRefreshStateTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
533533

534534
mapboxNavigation.refreshRoutesImmediately()
535535

536-
waitForRefreshes(2) // one from immediate and the next planned
536+
waitForRefresh()
537537

538538
assertEquals(
539539
listOf(
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.mapbox.navigation.core.routerefresh
2+
3+
import com.mapbox.api.directions.v5.models.DirectionsRoute
4+
import com.mapbox.api.directions.v5.models.RouteLeg
5+
import com.mapbox.navigation.base.internal.route.update
6+
import com.mapbox.navigation.base.internal.time.parseISO8601DateToLocalTimeOrNull
7+
import com.mapbox.navigation.base.route.NavigationRoute
8+
import java.util.Date
9+
10+
internal class ExpiringDataRemover(
11+
private val localDateProvider: () -> Date,
12+
) {
13+
14+
fun removeExpiringDataFromRoutes(
15+
routes: List<NavigationRoute>,
16+
currentLegIndex: Int,
17+
): List<NavigationRoute> {
18+
return routes.map {
19+
removeExpiringDataFromRoute(it, currentLegIndex)
20+
}
21+
}
22+
23+
private fun removeExpiringDataFromRoute(
24+
route: NavigationRoute,
25+
currentLegIndex: Int,
26+
): NavigationRoute {
27+
val routeLegs = route.directionsRoute.legs()
28+
val directionsRouteBlock: DirectionsRoute.() -> DirectionsRoute = {
29+
toBuilder().legs(
30+
routeLegs?.mapIndexed { legIndex, leg ->
31+
val legHasAlreadyBeenPassed = legIndex < currentLegIndex
32+
if (legHasAlreadyBeenPassed) {
33+
leg
34+
} else {
35+
removeExpiredDataFromLeg(leg)
36+
}
37+
}
38+
).build()
39+
}
40+
return route.update(
41+
directionsRouteBlock = directionsRouteBlock,
42+
directionsResponseBlock = { this }
43+
)
44+
}
45+
46+
private fun removeExpiredDataFromLeg(leg: RouteLeg): RouteLeg {
47+
val oldAnnotation = leg.annotation()
48+
return leg.toBuilder()
49+
.annotation(
50+
oldAnnotation?.let { nonNullOldAnnotation ->
51+
nonNullOldAnnotation.toBuilder()
52+
.congestion(nonNullOldAnnotation.congestion()?.map { "unknown" })
53+
.congestionNumeric(nonNullOldAnnotation.congestionNumeric()?.map { null })
54+
.build()
55+
}
56+
)
57+
.incidents(
58+
leg.incidents()?.filter {
59+
val parsed = parseISO8601DateToLocalTimeOrNull(it.endTime())
60+
?: return@filter true
61+
val currentDate = localDateProvider()
62+
parsed > currentDate
63+
}
64+
)
65+
.build()
66+
}
67+
}

libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/PlannedRouteRefreshController.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ internal class PlannedRouteRefreshController @VisibleForTesting constructor(
2929
stateHolder,
3030
listener,
3131
CancellableHandler(scope),
32-
RetryRouteRefreshStrategy(maxRetryCount = 2)
32+
RetryRouteRefreshStrategy(maxRetryCount = MAX_RETRY_COUNT)
3333
)
3434

3535
private var paused = false
@@ -142,4 +142,9 @@ internal class PlannedRouteRefreshController @VisibleForTesting constructor(
142142
}
143143
}
144144
}
145+
146+
companion object {
147+
148+
const val MAX_RETRY_COUNT = 2
149+
}
145150
}

libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RefreshObserversManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ internal fun interface RouteRefreshObserver {
77
fun onRoutesRefreshed(routeInfo: RefreshedRouteInfo)
88
}
99

10-
internal class RefreshObserversManager : RouteRefresherListener {
10+
internal class RefreshObserversManager {
1111

1212
private val refreshObservers = CopyOnWriteArraySet<RouteRefreshObserver>()
1313

@@ -23,7 +23,7 @@ internal class RefreshObserversManager : RouteRefresherListener {
2323
refreshObservers.clear()
2424
}
2525

26-
override fun onRoutesRefreshed(result: RouteRefresherResult) {
26+
fun onRoutesRefreshed(result: RouteRefresherResult) {
2727
refreshObservers.forEach { observer ->
2828
observer.onRoutesRefreshed(result.toRefreshedRoutesInfo())
2929
}

libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefreshController.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal class RouteRefreshController(
99
private val immediateRouteRefreshController: ImmediateRouteRefreshController,
1010
private val stateHolder: RouteRefreshStateHolder,
1111
private val refreshObserversManager: RefreshObserversManager,
12+
private val routeRefresherResultProcessor: RouteRefresherResultProcessor,
1213
) {
1314

1415
fun registerRouteRefreshObserver(observer: RouteRefreshObserver) {
@@ -35,6 +36,7 @@ internal class RouteRefreshController(
3536
}
3637

3738
fun requestPlannedRouteRefresh(routes: List<NavigationRoute>) {
39+
routeRefresherResultProcessor.reset()
3840
plannedRouteRefreshController.startRoutesRefreshing(routes)
3941
}
4042

libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefreshControllerProvider.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.mapbox.navigation.base.route.RouteRefreshOptions
55
import com.mapbox.navigation.core.RouteProgressDataProvider
66
import com.mapbox.navigation.core.directions.session.DirectionsSession
77
import com.mapbox.navigation.utils.internal.ThreadController
8+
import com.mapbox.navigation.utils.internal.Time
89
import java.util.Date
910

1011
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
@@ -23,7 +24,6 @@ internal object RouteRefreshControllerProvider {
2324
evDataHolder,
2425
DirectionsRouteDiffProvider(),
2526
directionsSession,
26-
{ Date() },
2727
)
2828
val routeRefresherExecutor = RouteRefresherExecutor(
2929
routeRefresher,
@@ -32,25 +32,32 @@ internal object RouteRefreshControllerProvider {
3232
)
3333
val stateHolder = RouteRefreshStateHolder()
3434
val refreshObserversManager = RefreshObserversManager()
35+
val routeRefresherResultProcessor = RouteRefresherResultProcessor(
36+
refreshObserversManager,
37+
ExpiringDataRemover { Date() },
38+
Time.SystemImpl,
39+
routeRefreshOptions.intervalMillis * (PlannedRouteRefreshController.MAX_RETRY_COUNT + 1)
40+
)
3541

3642
val plannedRouteRefreshController = PlannedRouteRefreshController(
3743
routeRefresherExecutor,
3844
routeRefreshOptions,
3945
stateHolder,
4046
scope,
41-
refreshObserversManager
47+
routeRefresherResultProcessor
4248
)
4349
val immediateRouteRefreshController = ImmediateRouteRefreshController(
4450
routeRefresherExecutor,
4551
plannedRouteRefreshController,
4652
stateHolder,
47-
refreshObserversManager
53+
routeRefresherResultProcessor
4854
)
4955
return RouteRefreshController(
5056
plannedRouteRefreshController,
5157
immediateRouteRefreshController,
5258
stateHolder,
53-
refreshObserversManager
59+
refreshObserversManager,
60+
routeRefresherResultProcessor
5461
)
5562
}
5663
}

libnavigation-core/src/main/java/com/mapbox/navigation/core/routerefresh/RouteRefresher.kt

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package com.mapbox.navigation.core.routerefresh
22

3-
import com.mapbox.api.directions.v5.models.DirectionsRoute
4-
import com.mapbox.api.directions.v5.models.RouteLeg
53
import com.mapbox.navigation.base.internal.RouteRefreshRequestData
6-
import com.mapbox.navigation.base.internal.route.update
7-
import com.mapbox.navigation.base.internal.time.parseISO8601DateToLocalTimeOrNull
84
import com.mapbox.navigation.base.route.NavigationRoute
95
import com.mapbox.navigation.base.route.NavigationRouterRefreshCallback
106
import com.mapbox.navigation.base.route.NavigationRouterRefreshError
@@ -18,7 +14,6 @@ import kotlinx.coroutines.awaitAll
1814
import kotlinx.coroutines.coroutineScope
1915
import kotlinx.coroutines.suspendCancellableCoroutine
2016
import kotlinx.coroutines.withTimeoutOrNull
21-
import java.util.Date
2217
import kotlin.coroutines.resume
2318

2419
internal data class RouteRefresherResult(
@@ -32,7 +27,6 @@ internal class RouteRefresher(
3227
private val evDataHolder: EVDataHolder,
3328
private val routeDiffProvider: DirectionsRouteDiffProvider,
3429
private val routeRefresh: RouteRefresh,
35-
private val localDateProvider: () -> Date,
3630
) {
3731

3832
suspend fun refresh(
@@ -52,7 +46,7 @@ internal class RouteRefresher(
5246
} else {
5347
RouteRefresherResult(
5448
success = false,
55-
routes.map { removeExpiringDataFromRoute(it, routeProgressData.legIndex) },
49+
routes,
5650
routeProgressData
5751
)
5852
}
@@ -165,51 +159,6 @@ internal class RouteRefresher(
165159
}
166160
}
167161

168-
private fun removeExpiringDataFromRoute(
169-
route: NavigationRoute,
170-
currentLegIndex: Int,
171-
): NavigationRoute {
172-
val routeLegs = route.directionsRoute.legs()
173-
val directionsRouteBlock: DirectionsRoute.() -> DirectionsRoute = {
174-
toBuilder().legs(
175-
routeLegs?.mapIndexed { legIndex, leg ->
176-
val legHasAlreadyBeenPassed = legIndex < currentLegIndex
177-
if (legHasAlreadyBeenPassed) {
178-
leg
179-
} else {
180-
removeExpiredDataFromLeg(leg)
181-
}
182-
}
183-
).build()
184-
}
185-
return route.update(
186-
directionsRouteBlock = directionsRouteBlock,
187-
directionsResponseBlock = { this }
188-
)
189-
}
190-
191-
private fun removeExpiredDataFromLeg(leg: RouteLeg): RouteLeg {
192-
val oldAnnotation = leg.annotation()
193-
return leg.toBuilder()
194-
.annotation(
195-
oldAnnotation?.let { nonNullOldAnnotation ->
196-
nonNullOldAnnotation.toBuilder()
197-
.congestion(nonNullOldAnnotation.congestion()?.map { "unknown" })
198-
.congestionNumeric(nonNullOldAnnotation.congestionNumeric()?.map { null })
199-
.build()
200-
}
201-
)
202-
.incidents(
203-
leg.incidents()?.filter {
204-
val parsed = parseISO8601DateToLocalTimeOrNull(it.endTime())
205-
?: return@filter true
206-
val currentDate = localDateProvider()
207-
parsed > currentDate
208-
}
209-
)
210-
.build()
211-
}
212-
213162
private sealed class RouteRefreshResult {
214163
data class Success(val route: NavigationRoute) : RouteRefreshResult()
215164
data class Fail(val error: NavigationRouterRefreshError) : RouteRefreshResult()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.mapbox.navigation.core.routerefresh
2+
3+
import com.mapbox.navigation.utils.internal.Time
4+
5+
internal class RouteRefresherResultProcessor(
6+
private val observersManager: RefreshObserversManager,
7+
private val expiringDataRemover: ExpiringDataRemover,
8+
private val timeProvider: Time,
9+
private val staleDataTimeoutMillis: Long,
10+
) : RouteRefresherListener {
11+
12+
private var lastRefreshTimeMillis: Long = 0
13+
14+
fun reset() {
15+
lastRefreshTimeMillis = timeProvider.millis()
16+
}
17+
18+
override fun onRoutesRefreshed(result: RouteRefresherResult) {
19+
val currentTime = timeProvider.millis()
20+
if (result.success) {
21+
lastRefreshTimeMillis = currentTime
22+
observersManager.onRoutesRefreshed(result)
23+
} else {
24+
if (currentTime >= lastRefreshTimeMillis + staleDataTimeoutMillis) {
25+
lastRefreshTimeMillis = currentTime
26+
val newRoutes = expiringDataRemover.removeExpiringDataFromRoutes(
27+
result.refreshedRoutes,
28+
result.routeProgressData.legIndex
29+
)
30+
if (result.refreshedRoutes != newRoutes) {
31+
val processedResult = RouteRefresherResult(
32+
result.success,
33+
newRoutes,
34+
result.routeProgressData
35+
)
36+
observersManager.onRoutesRefreshed(processedResult)
37+
}
38+
}
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)