@@ -56,6 +56,14 @@ class MapController(
5656 private var animationJob: Job ? = null
5757 private var animationListener: OnCameraAnimationListener ? = null
5858
59+ var onCameraMoveStartedListener: OnCameraMoveStartedListener ? = null
60+ var onCameraMoveListener: OnCameraMoveListener ? = null
61+ var onCameraIdleListener: OnCameraIdleListener ? = null
62+ var onCameraMoveCanceledListener: OnCameraMoveCanceledListener ? = null
63+
64+ internal var isCameraMoving = false
65+ internal var currentMoveReason: Int? = null
66+
5967 private val scope = CoroutineScope (Dispatchers .Main + Job ())
6068 private val tileDownloader = TileDownloader ()
6169 private val tileCache = TileCache (context)
@@ -257,7 +265,20 @@ class MapController(
257265 fun moveCamera (cameraUpdate : CameraUpdate ) {
258266 stopAnimation()
259267 commitPan()
268+
269+ // Fire camera move started event
270+ if (! isCameraMoving) {
271+ isCameraMoving = true
272+ currentMoveReason = OnCameraMoveStartedListener .REASON_DEVELOPER_ANIMATION
273+ onCameraMoveStartedListener?.onCameraMoveStarted(OnCameraMoveStartedListener .REASON_DEVELOPER_ANIMATION )
274+ }
275+
260276 applyCameraUpdate(cameraUpdate)
277+
278+ // Fire camera idle event immediately since moveCamera is instant
279+ isCameraMoving = false
280+ currentMoveReason = null
281+ onCameraIdleListener?.onCameraIdle()
261282 }
262283
263284 /* *
@@ -281,6 +302,13 @@ class MapController(
281302 val startPosition = getCameraPosition()
282303 val targetPosition = calculateTargetPosition(cameraUpdate, startPosition)
283304
305+ // Fire camera move started event
306+ if (! isCameraMoving) {
307+ isCameraMoving = true
308+ currentMoveReason = OnCameraMoveStartedListener .REASON_API_ANIMATION
309+ onCameraMoveStartedListener?.onCameraMoveStarted(OnCameraMoveStartedListener .REASON_API_ANIMATION )
310+ }
311+
284312 animationListener = listener
285313 animationJob =
286314 scope.launch {
@@ -314,6 +342,8 @@ class MapController(
314342 center = LatLng (interpolatedLat, interpolatedLng)
315343 zoom = interpolatedZoom
316344
345+ // Fire camera move event during animation
346+ onCameraMoveListener?.onCameraMove()
317347 onTileLoadedCallback?.invoke()
318348
319349 kotlinx.coroutines.delay(16 )
@@ -324,6 +354,11 @@ class MapController(
324354 onTileLoadedCallback?.invoke()
325355
326356 animationListener?.onFinish()
357+
358+ // Fire camera idle event after animation completes
359+ isCameraMoving = false
360+ currentMoveReason = null
361+ onCameraIdleListener?.onCameraIdle()
327362 } catch (e: kotlinx.coroutines.CancellationException ) {
328363 animationListener?.onCancel()
329364 throw e
@@ -341,9 +376,18 @@ class MapController(
341376 * onCancel() callback will be invoked. The camera remains at its current position.
342377 */
343378 fun stopAnimation () {
344- animationJob?.cancel()
345- animationJob = null
346- animationListener = null
379+ if (animationJob != null ) {
380+ animationJob?.cancel()
381+ animationJob = null
382+ animationListener = null
383+
384+ // Fire camera move canceled event
385+ if (isCameraMoving) {
386+ onCameraMoveCanceledListener?.onCameraMoveCanceled()
387+ isCameraMoving = false
388+ currentMoveReason = null
389+ }
390+ }
347391 }
348392
349393 private fun applyCameraUpdate (cameraUpdate : CameraUpdate ) {
@@ -459,6 +503,13 @@ class MapController(
459503 // Reset pan offset
460504 panOffsetX = 0f
461505 panOffsetY = 0f
506+
507+ // Fire camera idle event after pan gesture completes
508+ if (isCameraMoving && currentMoveReason == OnCameraMoveStartedListener .REASON_GESTURE ) {
509+ isCameraMoving = false
510+ currentMoveReason = null
511+ onCameraIdleListener?.onCameraIdle()
512+ }
462513 }
463514
464515 /* *
0 commit comments