Skip to content

Commit 17ecdc6

Browse files
dzinadgithub-actions[bot]
authored andcommitted
add maxUpdateFrameAnimationDuration option
GitOrigin-RevId: dc7a86965eb246cb82175a5f9bf4b39608c2334f
1 parent 36272f8 commit 17ecdc6

File tree

3 files changed

+283
-23
lines changed

3 files changed

+283
-23
lines changed

ui-maps/src/main/java/com/mapbox/navigation/ui/maps/camera/NavigationCamera.kt

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ internal constructor(
244244
private val navigationCameraStateChangedObservers =
245245
CopyOnWriteArraySet<NavigationCameraStateChangedObserver>()
246246

247+
private var currentStateTransitionListener: NavigationCameraTransitionListener? = null
248+
247249
/**
248250
* Returns current [NavigationCameraState].
249251
* @see registerNavigationCameraStateChangeObserver
@@ -354,7 +356,9 @@ internal constructor(
354356
TRANSITION_TO_FOLLOWING,
355357
FOLLOWING,
356358
frameTransitionOptions,
357-
),
359+
).also {
360+
this@NavigationCamera.currentStateTransitionListener = it
361+
},
358362
)
359363
},
360364
instant = false,
@@ -430,7 +434,9 @@ internal constructor(
430434
TRANSITION_TO_OVERVIEW,
431435
OVERVIEW,
432436
frameTransitionOptions,
433-
),
437+
).also {
438+
this@NavigationCamera.currentStateTransitionListener = it
439+
},
434440
)
435441
},
436442
instant = false,
@@ -591,8 +597,77 @@ internal constructor(
591597
progressState: NavigationCameraState,
592598
finalState: NavigationCameraState,
593599
frameTransitionOptions: NavigationCameraTransitionOptions,
594-
) = object : MapboxAnimatorSetListener {
600+
) = NavigationCameraTransitionListener(
601+
progressState,
602+
finalState,
603+
frameTransitionOptions,
604+
)
605+
606+
private fun createFrameListener() = object : MapboxAnimatorSetListener {
607+
608+
override fun onAnimationStart(animation: MapboxAnimatorSet) {
609+
// no impl
610+
}
611+
612+
override fun onAnimationEnd(animation: MapboxAnimatorSet) {
613+
finishAnimation(animation)
614+
}
615+
616+
override fun onAnimationCancel(animation: MapboxAnimatorSet) {
617+
// no impl
618+
}
619+
}
620+
621+
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
622+
private fun updateDebugger() {
623+
debugger?.cameraState = state
624+
}
625+
626+
/**
627+
* Updates following frame transition options on the fly.
628+
* Note that these options will be reset on the next requestToFollowing invocation.
629+
*/
630+
internal fun updateFollowingFrameTransitionOptions(
631+
frameTransitionOptions: NavigationCameraTransitionOptions,
632+
) {
633+
when (state) {
634+
FOLLOWING -> {
635+
this.frameTransitionOptions = frameTransitionOptions
636+
}
637+
TRANSITION_TO_FOLLOWING -> {
638+
currentStateTransitionListener?.frameTransitionOptions = frameTransitionOptions
639+
}
640+
else -> {
641+
// no-op
642+
}
643+
}
644+
}
595645

646+
/**
647+
* Updates overview frame transition options on the fly.
648+
* Note that these options will be reset on the next requestToOverview invocation.
649+
*/
650+
internal fun updateOverviewFrameTransitionOptions(
651+
frameTransitionOptions: NavigationCameraTransitionOptions,
652+
) {
653+
when (state) {
654+
OVERVIEW -> {
655+
this.frameTransitionOptions = frameTransitionOptions
656+
}
657+
TRANSITION_TO_OVERVIEW -> {
658+
currentStateTransitionListener?.frameTransitionOptions = frameTransitionOptions
659+
}
660+
else -> {
661+
// no-op
662+
}
663+
}
664+
}
665+
666+
private inner class NavigationCameraTransitionListener(
667+
private val progressState: NavigationCameraState,
668+
private val finalState: NavigationCameraState,
669+
var frameTransitionOptions: NavigationCameraTransitionOptions,
670+
) : MapboxAnimatorSetListener {
596671
private var isCanceled = false
597672

598673
override fun onAnimationStart(animation: MapboxAnimatorSet) {
@@ -606,6 +681,8 @@ internal constructor(
606681
state = finalState
607682
}
608683

684+
this@NavigationCamera.currentStateTransitionListener = null
685+
609686
finishAnimation(animation)
610687
// Custom transitionEndListener might synchronously start another transition.
611688
// In this case we risk running into a race condition where the new transitionEndListeners
@@ -621,24 +698,4 @@ internal constructor(
621698
isCanceled = true
622699
}
623700
}
624-
625-
private fun createFrameListener() = object : MapboxAnimatorSetListener {
626-
627-
override fun onAnimationStart(animation: MapboxAnimatorSet) {
628-
// no impl
629-
}
630-
631-
override fun onAnimationEnd(animation: MapboxAnimatorSet) {
632-
finishAnimation(animation)
633-
}
634-
635-
override fun onAnimationCancel(animation: MapboxAnimatorSet) {
636-
// no impl
637-
}
638-
}
639-
640-
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
641-
private fun updateDebugger() {
642-
debugger?.cameraState = state
643-
}
644701
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.mapbox.navigation.ui.maps.internal.camera
2+
3+
import androidx.annotation.RestrictTo
4+
import com.mapbox.navigation.ui.maps.camera.NavigationCamera
5+
import com.mapbox.navigation.ui.maps.camera.transition.NavigationCameraTransitionOptions
6+
7+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
8+
fun NavigationCamera.updateFollowingFrameTransitionOptions(
9+
frameTransitionOptions: NavigationCameraTransitionOptions,
10+
) {
11+
updateFollowingFrameTransitionOptions(frameTransitionOptions)
12+
}
13+
14+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
15+
fun NavigationCamera.updateOverviewFrameTransitionOptions(
16+
frameTransitionOptions: NavigationCameraTransitionOptions,
17+
) {
18+
updateOverviewFrameTransitionOptions(frameTransitionOptions)
19+
}

ui-maps/src/test/java/com/mapbox/navigation/ui/maps/camera/NavigationCameraTest.kt

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,4 +876,188 @@ class NavigationCameraTest {
876876
val frameCamera: CameraOptions,
877877
val frameAnimatorSet: MapboxAnimatorSet,
878878
)
879+
880+
@Test
881+
fun `updateFollowingFrameTransitionOptions when FOLLOWING updates frame options`() {
882+
val initialFrameOpt = NavigationCameraTransitionOptions.Builder()
883+
.maxDuration(250L)
884+
.build()
885+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
886+
.maxDuration(100L)
887+
.build()
888+
889+
navigationCamera.requestNavigationCameraToFollowing(
890+
frameTransitionOptions = initialFrameOpt,
891+
)
892+
internalTransitionListenerSlot.captured.onAnimationStart(followingAnimatorSet)
893+
internalTransitionListenerSlot.captured.onAnimationEnd(followingAnimatorSet)
894+
895+
navigationCamera.updateFollowingFrameTransitionOptions(updatedFrameOpt)
896+
897+
val frameTransition = mockFrameTransitionForFollowing(updatedFrameOpt)
898+
internalDataSourceObserverSlot.captured.viewportDataSourceUpdated(viewportData)
899+
900+
verifyTransitionExecuted(
901+
AnimatorsCreator::updateFrameForFollowing,
902+
frameTransition.frameCamera,
903+
updatedFrameOpt,
904+
frameTransition.frameAnimatorSet,
905+
)
906+
}
907+
908+
@Test
909+
fun `updateFollowingFrameTransitionOptions when TRANSITION_TO_FOLLOWING updates transition listener options`() {
910+
val initialFrameOpt = NavigationCameraTransitionOptions.Builder()
911+
.maxDuration(250L)
912+
.build()
913+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
914+
.maxDuration(100L)
915+
.build()
916+
917+
navigationCamera.requestNavigationCameraToFollowing(
918+
frameTransitionOptions = initialFrameOpt,
919+
)
920+
internalTransitionListenerSlot.captured.onAnimationStart(followingAnimatorSet)
921+
922+
navigationCamera.updateFollowingFrameTransitionOptions(updatedFrameOpt)
923+
internalTransitionListenerSlot.captured.onAnimationEnd(followingAnimatorSet)
924+
925+
val frameTransition = mockFrameTransitionForFollowing(updatedFrameOpt)
926+
internalDataSourceObserverSlot.captured.viewportDataSourceUpdated(viewportData)
927+
928+
verifyTransitionExecuted(
929+
AnimatorsCreator::updateFrameForFollowing,
930+
frameTransition.frameCamera,
931+
updatedFrameOpt,
932+
frameTransition.frameAnimatorSet,
933+
)
934+
}
935+
936+
@Test
937+
fun `updateFollowingFrameTransitionOptions when IDLE does nothing`() {
938+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
939+
.maxDuration(100L)
940+
.build()
941+
942+
navigationCamera.updateFollowingFrameTransitionOptions(updatedFrameOpt)
943+
944+
// Verify no side effects - state remains IDLE
945+
assertEquals(NavigationCameraState.IDLE, navigationCamera.state)
946+
}
947+
948+
@Test
949+
fun `updateFollowingFrameTransitionOptions when OVERVIEW does nothing`() {
950+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
951+
.maxDuration(100L)
952+
.build()
953+
954+
navigationCamera.requestNavigationCameraToOverview()
955+
internalTransitionListenerSlot.captured.onAnimationStart(overviewAnimatorSet)
956+
internalTransitionListenerSlot.captured.onAnimationEnd(overviewAnimatorSet)
957+
958+
navigationCamera.updateFollowingFrameTransitionOptions(updatedFrameOpt)
959+
960+
val frameTransition = mockFrameTransitionForOverview(DEFAULT_FRAME_TRANSITION_OPT)
961+
internalDataSourceObserverSlot.captured.viewportDataSourceUpdated(viewportData)
962+
963+
// Verify it still uses default options, not the updated ones
964+
verifyTransitionExecuted(
965+
AnimatorsCreator::updateFrameForOverview,
966+
frameTransition.frameCamera,
967+
DEFAULT_FRAME_TRANSITION_OPT,
968+
frameTransition.frameAnimatorSet,
969+
)
970+
}
971+
972+
@Test
973+
fun `updateOverviewFrameTransitionOptions when OVERVIEW updates frame options`() {
974+
val initialFrameOpt = NavigationCameraTransitionOptions.Builder()
975+
.maxDuration(250L)
976+
.build()
977+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
978+
.maxDuration(100L)
979+
.build()
980+
981+
navigationCamera.requestNavigationCameraToOverview(
982+
frameTransitionOptions = initialFrameOpt,
983+
)
984+
internalTransitionListenerSlot.captured.onAnimationStart(overviewAnimatorSet)
985+
internalTransitionListenerSlot.captured.onAnimationEnd(overviewAnimatorSet)
986+
987+
navigationCamera.updateOverviewFrameTransitionOptions(updatedFrameOpt)
988+
989+
val frameTransition = mockFrameTransitionForOverview(updatedFrameOpt)
990+
internalDataSourceObserverSlot.captured.viewportDataSourceUpdated(viewportData)
991+
992+
verifyTransitionExecuted(
993+
AnimatorsCreator::updateFrameForOverview,
994+
frameTransition.frameCamera,
995+
updatedFrameOpt,
996+
frameTransition.frameAnimatorSet,
997+
)
998+
}
999+
1000+
@Test
1001+
fun `updateOverviewFrameTransitionOptions when TRANSITION_TO_OVERVIEW updates transition listener options`() {
1002+
val initialFrameOpt = NavigationCameraTransitionOptions.Builder()
1003+
.maxDuration(250L)
1004+
.build()
1005+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
1006+
.maxDuration(100L)
1007+
.build()
1008+
1009+
navigationCamera.requestNavigationCameraToOverview(
1010+
frameTransitionOptions = initialFrameOpt,
1011+
)
1012+
internalTransitionListenerSlot.captured.onAnimationStart(overviewAnimatorSet)
1013+
1014+
navigationCamera.updateOverviewFrameTransitionOptions(updatedFrameOpt)
1015+
internalTransitionListenerSlot.captured.onAnimationEnd(overviewAnimatorSet)
1016+
1017+
val frameTransition = mockFrameTransitionForOverview(updatedFrameOpt)
1018+
internalDataSourceObserverSlot.captured.viewportDataSourceUpdated(viewportData)
1019+
1020+
verifyTransitionExecuted(
1021+
AnimatorsCreator::updateFrameForOverview,
1022+
frameTransition.frameCamera,
1023+
updatedFrameOpt,
1024+
frameTransition.frameAnimatorSet,
1025+
)
1026+
}
1027+
1028+
@Test
1029+
fun `updateOverviewFrameTransitionOptions when IDLE does nothing`() {
1030+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
1031+
.maxDuration(100L)
1032+
.build()
1033+
1034+
navigationCamera.updateOverviewFrameTransitionOptions(updatedFrameOpt)
1035+
1036+
// Verify no side effects - state remains IDLE
1037+
assertEquals(NavigationCameraState.IDLE, navigationCamera.state)
1038+
}
1039+
1040+
@Test
1041+
fun `updateOverviewFrameTransitionOptions when FOLLOWING does nothing`() {
1042+
val updatedFrameOpt = NavigationCameraTransitionOptions.Builder()
1043+
.maxDuration(100L)
1044+
.build()
1045+
1046+
navigationCamera.requestNavigationCameraToFollowing()
1047+
internalTransitionListenerSlot.captured.onAnimationStart(followingAnimatorSet)
1048+
internalTransitionListenerSlot.captured.onAnimationEnd(followingAnimatorSet)
1049+
1050+
navigationCamera.updateOverviewFrameTransitionOptions(updatedFrameOpt)
1051+
1052+
val frameTransition = mockFrameTransitionForFollowing(DEFAULT_FRAME_TRANSITION_OPT)
1053+
internalDataSourceObserverSlot.captured.viewportDataSourceUpdated(viewportData)
1054+
1055+
// Verify it still uses default options, not the updated ones
1056+
verifyTransitionExecuted(
1057+
AnimatorsCreator::updateFrameForFollowing,
1058+
frameTransition.frameCamera,
1059+
DEFAULT_FRAME_TRANSITION_OPT,
1060+
frameTransition.frameAnimatorSet,
1061+
)
1062+
}
8791063
}

0 commit comments

Comments
 (0)