5

I trying to implement google map with help of google compose sample project calls Crane in here: https://github.com/android/compose-samples/tree/main/Crane

I went with same implementation and using MapViewUtils to implement lifeCycler for map and prevent re-compose stuff and more... I put all android map key and also permissions on manifest, But my code getting crash on start of map:

This the point I wanna show map:

@Composable
fun MapScreen(latitude: String, longitude: String) {
    // The MapView lifecycle is handled by this composable. As the MapView also needs to be updated
    // with input from Compose UI, those updates are encapsulated into the MapViewContainer
    // composable. In this way, when an update to the MapView happens, this composable won't
    // recompose and the MapView won't need to be recreated.
    val mapView = rememberMapViewWithLifecycle()
    MapViewContainer(mapView, latitude, longitude)
}

@Composable
private fun MapViewContainer(
    map: MapView,
    latitude: String,
    longitude: String
) {
//    var zoom by savedInstanceState { InitialZoom }

    AndroidView({ map }) { mapView ->
        // Reading zoom so that AndroidView recomposes when it changes. The getMapAsync lambda
        mapView.getMapAsync {
            val position = LatLng(latitude.toDouble(), longitude.toDouble())
            it.addMarker(
                MarkerOptions().position(position)
            )
            it.moveCamera(CameraUpdateFactory.newLatLng(position))
        }
    }
}

And this is inside Util class:

@Composable
fun rememberMapViewWithLifecycle(): MapView {
    val context = ContextAmbient.current
    val mapView = remember {
        MapView(context).apply {
            id = R.id.map
        }
    }

//     Makes MapView follow the lifecycle of this composable
    val lifecycleObserver = rememberMapLifecycleObserver(mapView)
    val lifecycle = LifecycleOwnerAmbient.current.lifecycle
    onCommit(lifecycle) {
        lifecycle.addObserver(lifecycleObserver)
        onDispose {
            lifecycle.removeObserver(lifecycleObserver)
        }
    }

    return mapView
}

@Composable
private fun rememberMapLifecycleObserver(mapView: MapView): LifecycleEventObserver =
    remember(mapView) {
        LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_CREATE -> mapView.onCreate(Bundle()) //Crashes here
                Lifecycle.Event.ON_START -> mapView.onStart()
                Lifecycle.Event.ON_RESUME -> mapView.onResume()
                Lifecycle.Event.ON_PAUSE -> mapView.onPause()
                Lifecycle.Event.ON_STOP -> mapView.onStop()
                Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
                else -> throw IllegalStateException()
            }
        }
    }

And I'm getting this crash:

2020-11-05 12:16:09.282 2665-3383/com.google.android.gms.persistent E/ModuleIdSetter: exception when setting module id
    java.lang.IllegalStateException: Unable to get current module info in ModuleManager created with non-module Context
        at com.google.android.chimera.config.ModuleManager.getCurrentModule(:com.google.android.gms@202414022@20.24.14 (040700-319035315):2)
        at aewd.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):4)
        at aewg.b(:com.google.android.gms@202414022@20.24.14 (040700-319035315):9)
        at aeso.a(Unknown Source:0)
        at rpm.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):0)
        at rlv.c(:com.google.android.gms@202414022@20.24.14 (040700-319035315):1)
        at rlt.b(:com.google.android.gms@202414022@20.24.14 (040700-319035315):1)
        at rok.b(:com.google.android.gms@202414022@20.24.14 (040700-319035315):6)
        at rok.c(:com.google.android.gms@202414022@20.24.14 (040700-319035315):6)
        at rok.b(:com.google.android.gms@202414022@20.24.14 (040700-319035315):10)
        at rok.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):17)
        at rok.g(:com.google.android.gms@202414022@20.24.14 (040700-319035315):3)
        at sdr.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):2)
        at scr.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):10)
        at sci.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):0)
        at scl.handleMessage(:com.google.android.gms@202414022@20.24.14 (040700-319035315):28)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at aekz.a(:com.google.android.gms@202414022@20.24.14 (040700-319035315):2)
        at aekz.dispatchMessage(:com.google.android.gms@202414022@20.24.14 (040700-319035315):14)
        at android.os.Looper.loop(Looper.java:214)
        at android.os.HandlerThread.run(HandlerThread.java:67)
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Mahdi
  • 6,139
  • 9
  • 57
  • 109

1 Answers1

1

You need to ask permission to access the user's location, and make sure you have it before showing the map. You can use a variable with LiveData and ViewModel that is updated on permission granted, here's a part of a example:

class MainViewModel : ViewModel() {
    private val _permissionGranted = MutableLiveData(false)
    val permissionGranted = _permissionGranted
    
    fun onPermissionGranted() = _permissionGranted.postValue(true)
    
    // ...
}
class MainActivity : AppCompatActivity() {
    private val mainViewModel by viewModels<MainViewModel>
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val permissionGranted = mainViewModel.permissionGranted.observeAsState()
            if (permissionGranted) {
                // logic to show your map
            } else {
                // logic to ask for permission
            }
        }
    }
    
    override fun onRequestPermissionsResult(requestCode: Int,
        permissions: Array<String>, grantResults: IntArray) {
        // check if it's your request
        mainViewModel.onPremissionGranted()
    }
    
    // ...
}

You can have more info on asking for permissions here: https://developer.android.com/training/permissions/requesting

Vitor Ramos
  • 1,037
  • 9
  • 19
  • As I mentioned I handled all permissions. Crash is not about permissions. – Mahdi Nov 05 '20 at 22:11
  • You sad you added the permission in the manifest, but have you asked for the permission? – Vitor Ramos Nov 05 '20 at 23:19
  • location is not dangerous permission, so n need to ask it separately. Putting in manifest will enough. – Mahdi Nov 05 '20 at 23:50
  • You have to ask for location permission, according to the docs: https://developer.android.com/training/location/permissions – Vitor Ramos Nov 05 '20 at 23:57
  • Ok you right, I did not asked and it worked cause I did not get any location from user just wanna show some thing on map. I think that was the reason. By the way I can have Old way map right now on my app without in app permission and that woking well. Mmm Is it may related to in old way I'm using com.google.android.gsm.map but in that example they use lcom.google.android.libraries.maps? So in second one always have to aks in app permission?! – Mahdi Nov 06 '20 at 00:38
  • It does not have to do with that, if you try to get the user’s position, you will need to ask for permission on Android 6 or later – Vitor Ramos Nov 06 '20 at 01:59