Skip to content

Commit 9f8a577

Browse files
vadzim-vysgithub-actions[bot]
authored andcommitted
NAVAND-5657 support immediate bearing updates for location puck animation
GitOrigin-RevId: a4e27cccc56f428cacf6ce906369305dbbbc5bbc
1 parent 736082e commit 9f8a577

6 files changed

Lines changed: 49 additions & 55 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Added support of immediate update of location puck bearing in [NavigationLocationProvider] in case of overlapping key points.

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/location/ConstantVelocityInterpolator.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ class ConstantVelocityInterpolator(
2121
var total = 0.0
2222
keyPoints.fold(startPoint) { prevPoint, point ->
2323
val d = distance(prevPoint, point)
24-
if (0.0 < d) {
25-
distances.add(d)
26-
total += d
27-
}
24+
distances.add(d)
25+
total += d
2826
point
2927
}
3028

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/location/PuckAnimationEvaluator.kt renamed to ui-maps/src/main/java/com/mapbox/navigation/ui/maps/internal/location/PuckAnimationEvaluatorInterpolator.kt

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,13 @@ package com.mapbox.navigation.ui.maps.internal.location
22

33
import android.animation.TimeInterpolator
44
import android.animation.TypeEvaluator
5-
import android.animation.ValueAnimator
65
import com.mapbox.geojson.Point
76

87
/**
98
* A time and value interpolator for puck's animator. It uses [ConstantVelocityInterpolator] to
109
* animate at constant velocity between key points.
11-
*
12-
* Use [PuckAnimationEvaluator.installIn] to install it in [ValueAnimator].
13-
* Use [PuckAnimationEvaluator.reset] to reset TimeInterpolator values for re-use with
14-
* next ValueAnimator.
1510
*/
16-
internal class PuckAnimationEvaluator(
11+
internal class PuckAnimationEvaluatorInterpolator(
1712
private val keyPoints: Array<Point>,
1813
) : TimeInterpolator, TypeEvaluator<Point> {
1914

@@ -30,22 +25,6 @@ internal class PuckAnimationEvaluator(
3025
return POINT.evaluate(fraction, startValue, endValue)
3126
}
3227

33-
/**
34-
* Reset TimeInterpolator values for re-use with next ValueAnimator.
35-
*/
36-
fun reset() {
37-
interpolator = null
38-
}
39-
40-
/**
41-
* Set this evaluator as [animator] TimeInterpolator and TypeEvaluator.
42-
*/
43-
fun installIn(animator: ValueAnimator) {
44-
reset()
45-
animator.interpolator = this
46-
animator.setEvaluator(this)
47-
}
48-
4928
companion object {
5029
private val POINT = TypeEvaluator<Point> { fraction, startValue, endValue ->
5130
Point.fromLngLat(

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/location/NavigationLocationProvider.kt

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import com.mapbox.maps.plugin.locationcomponent.LocationProvider
1111
import com.mapbox.maps.plugin.locationcomponent.OnIndicatorPositionChangedListener
1212
import com.mapbox.navigation.core.MapboxNavigation
1313
import com.mapbox.navigation.core.trip.session.LocationObserver
14-
import com.mapbox.navigation.ui.maps.internal.location.PuckAnimationEvaluator
14+
import com.mapbox.navigation.ui.maps.internal.location.PuckAnimationEvaluatorInterpolator
1515
import com.mapbox.navigation.utils.internal.ifNonNull
1616
import java.util.concurrent.CopyOnWriteArraySet
1717

@@ -158,25 +158,26 @@ class NavigationLocationProvider : LocationProvider {
158158
listOf(location.bearing)
159159
}.filterNotNull().toDoubleArray()
160160

161+
val puckAnimationEvaluatorInterpolator = PuckAnimationEvaluatorInterpolator(latLngUpdates)
161162
this.onLocationUpdated(
162163
location = latLngUpdates,
163-
options = locationAnimatorOptions(latLngUpdates, latLngTransitionOptions),
164+
options = {
165+
this.duration = LocationComponentConstants.DEFAULT_INTERVAL_MILLIS
166+
this.interpolator = puckAnimationEvaluatorInterpolator
167+
this.setEvaluator(puckAnimationEvaluatorInterpolator)
168+
latLngTransitionOptions?.also { this.apply(it) }
169+
},
164170
)
165171
if (bearingUpdates.isNotEmpty()) {
166-
this.onBearingUpdated(bearing = bearingUpdates, options = bearingTransitionOptions)
167-
}
168-
}
169-
170-
private fun locationAnimatorOptions(
171-
keyPoints: Array<Point>,
172-
clientOptions: (ValueAnimator.() -> Unit)?,
173-
): (ValueAnimator.() -> Unit) {
174-
val evaluator = PuckAnimationEvaluator(keyPoints)
175-
return {
176-
// TODO: Remove setDuration once patched in MapsSDK https://github.com/mapbox/mapbox-maps-android/issues/1446
177-
duration = LocationComponentConstants.DEFAULT_INTERVAL_MILLIS
178-
evaluator.installIn(this)
179-
clientOptions?.also { apply(it) }
172+
this.onBearingUpdated(
173+
bearing = bearingUpdates,
174+
options = {
175+
// keeping animation frames in sync between bearing and position animation
176+
this.interpolator = puckAnimationEvaluatorInterpolator
177+
this.duration = LocationComponentConstants.DEFAULT_INTERVAL_MILLIS
178+
bearingTransitionOptions?.also { this.apply(it) }
179+
},
180+
)
180181
}
181182
}
182183
}

ui-maps/src/test/java/com/mapbox/navigation/ui/maps/internal/location/ConstantVelocityInterpolatorTest.kt

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,38 @@ class ConstantVelocityInterpolatorTest {
2929
}
3030
}
3131

32+
/**
33+
* [android.animation.ValueAnimator] allocates equal time for each value transition, consider
34+
* default animation timeline:
35+
* |----------------|----------------|----------------| // animation progress from 0.0 to 1.0
36+
* 0.0, 0.1 0.5, 0.5 0.5, 0.5 1.0, 1.0 // location value
37+
* 1/3 of the animation time, location will be 0.5 in that case.
38+
* Desired timeline for constant velocity in that case is:
39+
* |------------------------|-------------------------|
40+
* 0.0, 0.1 0.5, 0.5 1.0, 1.0
41+
* I.e. when animation completed on 25%, location should be (0.25, 0.25) as on the
42+
* timeline bellow instead of (0.5, 0.5) as on the timeline above.
43+
*
44+
* To achieve desired animation timeline, we need to interpolate(remap) animation progress from:
45+
* 0.25 to 1/(3*2)
46+
* 0.499 (i.e. ~ 1/2) to 0.32 (i.e. ~1/3)
47+
* etc
48+
* so that it pointed to the right location on the top timeline.
49+
*/
3250
@Test
33-
fun `should not use keypoints that are at ZERO distance from previous point`() {
51+
fun `0 distance key points interpolated to happen immediate`() {
3452
val p0 = Point.fromLngLat(0.0, 0.0)
3553
val keyPoints = arrayOf(
3654
Point.fromLngLat(0.5, 0.5),
3755
Point.fromLngLat(0.5, 0.5),
3856
Point.fromLngLat(1.0, 1.0),
3957
)
40-
4158
val sut = ConstantVelocityInterpolator(p0, keyPoints)
4259

43-
val path = calculatePathValues(p0, *keyPoints)
44-
assertEquals(keyPoints.size - 1, path.size)
45-
path.forEach { (outTime, inTime) ->
46-
assertEquals(outTime, sut.getInterpolation(inTime))
47-
}
60+
assertEquals(0.166f, sut.getInterpolation(0.25f), 0.001f)
61+
assertEquals(0.329f, sut.getInterpolation(0.499f), 0.01f)
62+
assertEquals(0.667f, sut.getInterpolation(0.501f), 0.01f)
63+
assertEquals(0.833f, sut.getInterpolation(0.75f), 0.01f)
4864
}
4965

5066
@Test
@@ -100,10 +116,8 @@ class ConstantVelocityInterpolatorTest {
100116
var totalDistance = 0.0
101117
points.fold(p0) { a, b ->
102118
val d = distance(a, b)
103-
if (0 < d) {
104-
distances.add(d)
105-
totalDistance += d
106-
}
119+
distances.add(d)
120+
totalDistance += d
107121
b
108122
}
109123

ui-maps/src/test/java/com/mapbox/navigation/ui/maps/location/NavigationLocationProviderTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class NavigationLocationProviderTest {
7777
verify(exactly = 1) { consumer.onLocationUpdated(*expectedPoints, options = any()) }
7878
val expectedBearings = doubleArrayOf(location.bearing!!)
7979
verify(exactly = 1) {
80-
consumer.onBearingUpdated(*expectedBearings, options = bearingOptions)
80+
consumer.onBearingUpdated(*expectedBearings, options = any())
8181
}
8282
}
8383

@@ -141,7 +141,7 @@ class NavigationLocationProviderTest {
141141
location.bearing!!,
142142
)
143143
verify(exactly = 1) {
144-
consumer.onBearingUpdated(*expectedBearings, options = bearingOptions)
144+
consumer.onBearingUpdated(*expectedBearings, options = any())
145145
}
146146
}
147147

@@ -169,6 +169,7 @@ class NavigationLocationProviderTest {
169169
}
170170
val bearingAnimator: ValueAnimator = mockk {
171171
every { setDuration(any()) } returns this
172+
every { setInterpolator(any()) } returns Unit
172173
}
173174
every { consumer.onBearingUpdated(*anyDoubleVararg(), options = captureLambda()) } answers {
174175
lambda<(ValueAnimator.() -> Unit)>().captured.invoke(bearingAnimator)

0 commit comments

Comments
 (0)