Skip to content

Cancelled animation corrupts CameraPositionState and prevents drag gesture #502

Description

@jeffphp

android-maps-compose 4.3.0

When an animation occurs and is cancelled (and in other circumstances that I did not manage to reproduce), the CameraPositionState gets corrupted and a drag gesture on the map doesn't work anymore as intended. Here is a code to reproduce the bug (click several times on the button and try to move the map, zoom is not affected somehow).

Environment details

  1. android-maps-compose 4.3.0
  2. doesn't seem to occur on the simulator, please use a real device.

Steps to reproduce

  1. Click several times on the button that animates the card, causing cancellation exceptions (caught and displayed)
  2. Try to move the map, it doesn't move as intended (zoom is not affected somehow)

Code example

class RecompositionActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val cameraPositionState = rememberCameraPositionState {
                position = defaultCameraPosition
            }
            Box(Modifier.fillMaxSize()) {
                MapsComposeSampleTheme {
                    GoogleMapView(
                        modifier = Modifier.matchParentSize(),
                        cameraPositionState = cameraPositionState
                    )
                }
            }
        }
    }


    @Composable
    fun GoogleMapView(
        modifier: Modifier = Modifier,
        cameraPositionState: CameraPositionState = rememberCameraPositionState(),
        content: @Composable () -> Unit = {},
    ) {
        var location by remember { mutableStateOf(singapore) }

        var zoom by remember { mutableStateOf(5f) }

        val context = LocalContext.current
        val singaporeState = MarkerState(position = location)


        val mapProperties = remember {
            MapProperties(
                isBuildingEnabled = false,
                isIndoorEnabled = false,
                isMyLocationEnabled = false,
                isTrafficEnabled = false,
                mapType = MapType.SATELLITE,
            )
        }

        val mapOptions = remember {
            GoogleMapOptions()
                .ambientEnabled(false)
                .mapToolbarEnabled(false)
                .compassEnabled(false)
        }

        val uiSettings = remember {
            MapUiSettings(
                compassEnabled = false,
                indoorLevelPickerEnabled = false,
                mapToolbarEnabled = false,
                myLocationButtonEnabled = false,
                rotationGesturesEnabled = false,
                scrollGesturesEnabledDuringRotateOrZoom = false,
                tiltGesturesEnabled = false,
                zoomControlsEnabled = false,
            )
        }


        val mapVisible by remember { mutableStateOf(true) }
        if (mapVisible) {

            LaunchedEffect(zoom) {
                try {
                    cameraPositionState.animate(
                        update = CameraUpdateFactory.newCameraPosition(
                            CameraPosition(
                                cameraPositionState.position.target,
                                zoom,
                                0f,
                                0f
                            ),

                            ), durationMs = 1000
                    )
                } catch(e: CancellationException) {
                    Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
                } catch(e: Exception) {
                    Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
                }
            }


            // Detect when the map starts moving and print the reason
            LaunchedEffect(cameraPositionState.isMoving) {
                if (cameraPositionState.isMoving) {
                    Log.d(TAG, "Map camera started moving due to ${cameraPositionState.cameraMoveStartedReason.name}")
                }
            }

            GoogleMap(
                modifier = modifier.fillMaxSize(),
                cameraPositionState = cameraPositionState,
                googleMapOptionsFactory = {mapOptions},
                onMapLoaded = {},
                properties = mapProperties,
                uiSettings = uiSettings,
                onPOIClick = {
                    Log.d(TAG, "POI clicked: ${it.name}")
                }
            ) {
                val markerClick: (Marker) -> Boolean = {
                    Log.d(TAG, "${it.title} was clicked")
                    cameraPositionState.projection?.let { projection ->
                        Log.d(TAG, "The current projection is: $projection")
                    }
                    false
                }

                Marker(
                    state = singaporeState,
                    title = "Marker in Singapore",
                    onClick = markerClick
                )

                content()
            }
            Column {

                Button(onClick = {
                    val randomValue = Random.nextInt(3)

                    location = when (randomValue) {
                        0 -> singapore
                        1 -> singapore2
                        2 -> singapore3
                        else -> singapore
                    }
                    zoom = when (randomValue) {
                        0 -> 3f
                        1 -> 6f
                        2 -> 15f
                        else -> 16f
                    }
                }) {
                    Text("Change Location & Zoom")
                }
            }
        }
    }
}

Thanks by advance!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: p1Important issue which blocks shipping the next release. Will be fixed prior to next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions