Skip to content

Commit b05f7d1

Browse files
yuryybkgithub-actions[bot]
authored andcommitted
NAVAND-5469: Support refresh interval for notifications with geometry-aware refresh logic (#9593)
* NAVAND-5469 Support refresh interval for notifications; support refresh_type (dynamic/static) GitOrigin-RevId: 0fd0309df975043cc4d3cd590935c6380e2759d6
1 parent 097290f commit b05f7d1

File tree

11 files changed

+804
-134
lines changed

11 files changed

+804
-134
lines changed

base/src/main/java/com/mapbox/navigation/base/internal/route/NavigationRouteEx.kt

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ fun NavigationRoute.internalRefreshRoute(
6565
?.get(KEY_REFRESH_TTL)?.asInt,
6666
IncidentsRefresher(),
6767
ClosuresRefresher(),
68+
NotificationsRefresher(),
6869
)
6970
}
7071

@@ -97,6 +98,7 @@ fun NavigationRoute.refreshRoute(
9798
refreshTtl,
9899
IncidentsRefresher(),
99100
ClosuresRefresher(),
101+
NotificationsRefresher(),
100102
)
101103
}
102104

@@ -113,6 +115,7 @@ internal fun NavigationRoute.refreshRoute(
113115
refreshTtl: Int?,
114116
incidentsRefresher: IncidentsRefresher,
115117
closuresRefresher: ClosuresRefresher,
118+
notificationsRefresher: NotificationsRefresher,
116119
): NavigationRoute {
117120
val updateLegs = directionsRoute.legs()?.mapIndexed { index, routeLeg ->
118121
if (index < initialLegIndex) {
@@ -153,18 +156,20 @@ internal fun NavigationRoute.refreshRoute(
153156
startingLegGeometryIndex,
154157
lastLegRefreshIndex,
155158
)
159+
val mergedNotifications = notificationsRefresher.getRefreshedNotifications(
160+
routeLeg.unrecognizedJsonProperties?.get(KEY_NOTIFICATIONS) as? JsonArray,
161+
unrecognizedLegNotifications?.getOrNull(index) as? JsonArray,
162+
startingLegGeometryIndex,
163+
lastLegRefreshIndex,
164+
)
156165

157166
routeLeg.toBuilder()
158167
.duration(mergedAnnotation?.duration()?.sumOf { it } ?: routeLeg.duration())
159168
.annotation(mergedAnnotation)
160169
.incidents(mergedIncidents)
161170
.closures(mergedClosures)
162171
.steps(routeLeg.steps()?.updateSteps(directionsRoute, mergedAnnotation))
163-
.updateNotifications(
164-
routeLeg.unrecognizedJsonProperties,
165-
unrecognizedLegNotifications?.getOrNull(index) as? JsonArray,
166-
routeOptions.isEVRoute(),
167-
)
172+
.applyMergedNotifications(routeLeg.unrecognizedJsonProperties, mergedNotifications)
168173
.build()
169174
}
170175
}
@@ -327,3 +332,18 @@ private fun DirectionsRoute.Builder.updateRouteDurationBasedOnLegsDurationAndCha
327332
duration(result)
328333
return this
329334
}
335+
336+
private fun RouteLeg.Builder.applyMergedNotifications(
337+
oldUnrecognizedProperties: Map<String, JsonElement>?,
338+
mergedNotifications: JsonArray?,
339+
): RouteLeg.Builder {
340+
return unrecognizedJsonProperties(
341+
oldUnrecognizedProperties.orEmpty().toMutableMap().also {
342+
if (mergedNotifications == null || mergedNotifications.isEmpty) {
343+
it.remove(KEY_NOTIFICATIONS)
344+
} else {
345+
it[KEY_NOTIFICATIONS] = mergedNotifications
346+
}
347+
},
348+
)
349+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.mapbox.navigation.base.internal.route
2+
3+
import com.google.gson.JsonArray
4+
import com.google.gson.JsonElement
5+
import com.mapbox.navigation.base.internal.utils.Constants.NotificationRefreshType.DYNAMIC
6+
7+
internal class NotificationsRefresher {
8+
9+
fun getRefreshedNotifications(
10+
oldNotifications: JsonArray?,
11+
newNotifications: JsonArray?,
12+
startingLegGeometryIndex: Int,
13+
lastRefreshLegGeometryIndex: Int,
14+
): JsonArray? {
15+
return when {
16+
oldNotifications == null && newNotifications == null -> null
17+
oldNotifications == null -> adjustNotificationIndices(
18+
newNotifications,
19+
startingLegGeometryIndex,
20+
)
21+
newNotifications == null -> filterNotificationsByGeometryRange(
22+
oldNotifications,
23+
startingLegGeometryIndex,
24+
lastRefreshLegGeometryIndex,
25+
)
26+
else -> mergeNotifications(
27+
oldNotifications,
28+
newNotifications,
29+
startingLegGeometryIndex,
30+
lastRefreshLegGeometryIndex,
31+
)
32+
}
33+
}
34+
35+
private fun mergeNotifications(
36+
oldNotifications: JsonArray,
37+
newNotifications: JsonArray,
38+
startingLegGeometryIndex: Int,
39+
lastRefreshLegGeometryIndex: Int,
40+
): JsonArray {
41+
// Filter old notifications to keep static and dynamic notifications outside refresh range
42+
val result = filterNotificationsByGeometryRange(
43+
oldNotifications,
44+
startingLegGeometryIndex,
45+
lastRefreshLegGeometryIndex,
46+
)
47+
48+
// Add new dynamic notifications with adjusted indices
49+
result.addAll(
50+
adjustNotificationIndices(
51+
newNotifications,
52+
startingLegGeometryIndex,
53+
),
54+
)
55+
56+
return result
57+
}
58+
59+
private fun filterNotificationsByGeometryRange(
60+
notifications: JsonArray,
61+
startingLegGeometryIndex: Int,
62+
lastRefreshLegGeometryIndex: Int,
63+
): JsonArray {
64+
val result = JsonArray()
65+
66+
notifications.forEach { notification ->
67+
if (notification.isDynamicNotification()) {
68+
// Dynamic notifications: only keep those outside the refresh range
69+
val geometryIndex = notification.getGeometryIndex()
70+
if (geometryIndex != null) {
71+
val isOutsideRange = geometryIndex < startingLegGeometryIndex ||
72+
geometryIndex > lastRefreshLegGeometryIndex
73+
if (isOutsideRange) {
74+
result.add(notification)
75+
}
76+
}
77+
// Dynamic notifications without geometry_index are filtered out
78+
} else {
79+
// Static notifications: always keep them
80+
result.add(notification)
81+
}
82+
}
83+
84+
return result
85+
}
86+
87+
/**
88+
* Adjusts geometry indices for new notifications using the provided starting leg geometry index.
89+
*/
90+
private fun adjustNotificationIndices(
91+
notifications: JsonArray?,
92+
startingLegGeometryIndex: Int,
93+
): JsonArray? {
94+
if (notifications == null || notifications.isEmpty) {
95+
return notifications
96+
}
97+
98+
val result = JsonArray()
99+
notifications.forEach { notification ->
100+
val adjustedNotification = adjustNotificationIndex(
101+
notification,
102+
startingLegGeometryIndex,
103+
)
104+
result.add(adjustedNotification)
105+
}
106+
return result
107+
}
108+
109+
private fun adjustNotificationIndex(
110+
notification: JsonElement,
111+
startingLegGeometryIndex: Int,
112+
): JsonElement {
113+
if (!notification.isJsonObject) {
114+
return notification
115+
}
116+
117+
val notificationObject = notification.asJsonObject
118+
val geometryIndex = notificationObject.get("geometry_index")?.asInt
119+
120+
if (geometryIndex != null) {
121+
val adjustedNotification = notificationObject.deepCopy()
122+
adjustedNotification.addProperty(
123+
"geometry_index",
124+
startingLegGeometryIndex + geometryIndex,
125+
)
126+
return adjustedNotification
127+
}
128+
129+
return notification
130+
}
131+
132+
private fun JsonElement.getGeometryIndex(): Int? {
133+
return if (isJsonObject) {
134+
asJsonObject.get("geometry_index")?.asInt
135+
} else {
136+
null
137+
}
138+
}
139+
140+
/**
141+
* Determines if a notification should be treated as dynamic (refreshable)
142+
* based on the refresh_type field
143+
*/
144+
private fun JsonElement.isDynamicNotification(): Boolean {
145+
if (!isJsonObject) return false
146+
147+
val refreshType = asJsonObject.get("refresh_type")?.asJsonPrimitive?.asString
148+
return refreshType == DYNAMIC
149+
}
150+
}

base/src/main/java/com/mapbox/navigation/base/internal/route/RouteLegNotificationMergeEx.kt

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

base/src/main/java/com/mapbox/navigation/base/internal/utils/Constants.kt

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,8 @@ class Constants {
1414
val SEVERE_CONGESTION_RANGE = 80..100
1515
}
1616

17-
internal object NotificationSubtype {
18-
private const val EV_MIN_CHARGE_AT_CHARGING_STATION = "evMinChargeAtChargingStation"
19-
private const val EV_MIN_CHARGE_AT_DESTINATION = "evMinChargeAtDestination"
20-
private const val EV_INSUFFICIENT_CHARGE = "evInsufficientCharge"
21-
private const val EV_STATION_UNAVAILABLE = "stationUnavailable"
22-
23-
val EV_NOTIFICATIONS_SUB_TYPES = setOf(
24-
EV_MIN_CHARGE_AT_CHARGING_STATION,
25-
EV_MIN_CHARGE_AT_DESTINATION,
26-
EV_INSUFFICIENT_CHARGE,
27-
EV_STATION_UNAVAILABLE,
28-
)
17+
internal object NotificationRefreshType {
18+
const val STATIC = "static"
19+
const val DYNAMIC = "dynamic"
2920
}
3021
}

0 commit comments

Comments
 (0)