Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package com.google.maps.android.compose

import android.content.Intent
import android.os.Bundle
import android.os.StrictMode
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
Expand Down Expand Up @@ -48,6 +49,15 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()

StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.penaltyLog()
.penaltyDeath()
.build()
)

setContent {
MapsComposeSampleTheme {
Surface(
Expand Down
189 changes: 105 additions & 84 deletions maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composition
import androidx.compose.runtime.CompositionContext
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -35,6 +36,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
Expand All @@ -53,9 +55,11 @@ import com.google.maps.android.compose.meta.AttributionId
import com.google.maps.android.ktx.awaitMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
* A compose container for a [MapView].
Expand Down Expand Up @@ -109,101 +113,118 @@ public fun GoogleMap(
return
}

// rememberUpdatedState and friends are used here to make these values observable to
// the subcomposition without providing a new content function each recomposition
val mapClickListeners = remember { MapClickListeners() }.also {
it.indoorStateChangeListener = indoorStateChangeListener
it.onMapClick = onMapClick
it.onMapLongClick = onMapLongClick
it.onMapLoaded = onMapLoaded
it.onMyLocationButtonClick = onMyLocationButtonClick
it.onMyLocationClick = onMyLocationClick
it.onPOIClick = onPOIClick
}
var isInitialized by remember { mutableStateOf(false) }
val context = LocalContext.current

val mapUpdaterState = remember {
MapUpdaterState(
mergeDescendants,
contentDescription,
cameraPositionState,
contentPadding,
locationSource,
properties,
uiSettings,
mapColorScheme?.value,
)
}.also {
it.mergeDescendants = mergeDescendants
it.contentDescription = contentDescription
it.cameraPositionState = cameraPositionState
it.contentPadding = contentPadding
it.locationSource = locationSource
it.mapProperties = properties
it.mapUiSettings = uiSettings
it.mapColorScheme = mapColorScheme?.value
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
MapsApiSettings.addInternalUsageAttributionId(context, AttributionId.VALUE)
}
isInitialized = true
}

val parentComposition = rememberCompositionContext()
val currentContent by rememberUpdatedState(content)
var subcompositionJob by remember { mutableStateOf<Job?>(null) }
val parentCompositionScope = rememberCoroutineScope()

AndroidView(
modifier = modifier,
factory = { context ->
MapView(context, googleMapOptionsFactory()) .also { mapView ->
MapsApiSettings.addInternalUsageAttributionId(context, AttributionId.VALUE )
val componentCallbacks = object : ComponentCallbacks2 {
override fun onConfigurationChanged(newConfig: Configuration) {}
@Deprecated("Deprecated in Java", ReplaceWith("onTrimMemory(level)"))
override fun onLowMemory() { mapView.onLowMemory() }
override fun onTrimMemory(level: Int) { mapView.onLowMemory() }
}
context.registerComponentCallbacks(componentCallbacks)

val lifecycleObserver = MapLifecycleEventObserver(mapView)
if (isInitialized) {
// rememberUpdatedState and friends are used here to make these values observable to
// the subcomposition without providing a new content function each recomposition
val mapClickListeners = remember { MapClickListeners() }.also {
it.indoorStateChangeListener = indoorStateChangeListener
it.onMapClick = onMapClick
it.onMapLongClick = onMapLongClick
it.onMapLoaded = onMapLoaded
it.onMyLocationButtonClick = onMyLocationButtonClick
it.onMyLocationClick = onMyLocationClick
it.onPOIClick = onPOIClick
}

mapView.tag = MapTagData(componentCallbacks, lifecycleObserver)
val mapUpdaterState = remember {
MapUpdaterState(
mergeDescendants,
contentDescription,
cameraPositionState,
contentPadding,
locationSource,
properties,
uiSettings,
mapColorScheme?.value,
)
}.also {
it.mergeDescendants = mergeDescendants
it.contentDescription = contentDescription
it.cameraPositionState = cameraPositionState
it.contentPadding = contentPadding
it.locationSource = locationSource
it.mapProperties = properties
it.mapUiSettings = uiSettings
it.mapColorScheme = mapColorScheme?.value
}

// Only register for [lifecycleOwner]'s lifecycle events while MapView is attached
val onAttachStateListener = object : View.OnAttachStateChangeListener {
private var lifecycle: Lifecycle? = null
val parentComposition = rememberCompositionContext()
val currentContent by rememberUpdatedState(content)
var subcompositionJob by remember { mutableStateOf<Job?>(null) }
val parentCompositionScope = rememberCoroutineScope()

AndroidView(
modifier = modifier,
factory = { context ->
MapView(context, googleMapOptionsFactory()).also { mapView ->
MapsApiSettings.addInternalUsageAttributionId(context, AttributionId.VALUE)
val componentCallbacks = object : ComponentCallbacks2 {
override fun onConfigurationChanged(newConfig: Configuration) {}

@Deprecated("Deprecated in Java", ReplaceWith("onTrimMemory(level)"))
override fun onLowMemory() {
mapView.onLowMemory()
}

override fun onViewAttachedToWindow(mapView: View) {
lifecycle = mapView.findViewTreeLifecycleOwner()!!.lifecycle.also {
it.addObserver(lifecycleObserver)
override fun onTrimMemory(level: Int) {
mapView.onLowMemory()
}
}
context.registerComponentCallbacks(componentCallbacks)

val lifecycleObserver = MapLifecycleEventObserver(mapView)

mapView.tag = MapTagData(componentCallbacks, lifecycleObserver)

// Only register for [lifecycleOwner]'s lifecycle events while MapView is attached
val onAttachStateListener = object : View.OnAttachStateChangeListener {
private var lifecycle: Lifecycle? = null

override fun onViewDetachedFromWindow(v: View) {
lifecycle?.removeObserver(lifecycleObserver)
lifecycle = null
lifecycleObserver.moveToBaseState()
override fun onViewAttachedToWindow(mapView: View) {
lifecycle = mapView.findViewTreeLifecycleOwner()!!.lifecycle.also {
it.addObserver(lifecycleObserver)
}
}

override fun onViewDetachedFromWindow(v: View) {
lifecycle?.removeObserver(lifecycleObserver)
lifecycle = null
lifecycleObserver.moveToBaseState()
}
}
}

mapView.addOnAttachStateChangeListener(onAttachStateListener)
}
},
onReset = { /* View is detached. */ },
onRelease = { mapView ->
val (componentCallbacks, lifecycleObserver) = mapView.tagData
mapView.context.unregisterComponentCallbacks(componentCallbacks)
lifecycleObserver.moveToDestroyedState()
mapView.tag = null
},
update = { mapView ->
if (subcompositionJob == null) {
subcompositionJob = parentCompositionScope.launchSubcomposition(
mapUpdaterState,
parentComposition,
mapView,
mapClickListeners,
currentContent,
)
}
}
)
mapView.addOnAttachStateChangeListener(onAttachStateListener)
}
},
onReset = { /* View is detached. */ },
onRelease = { mapView ->
val (componentCallbacks, lifecycleObserver) = mapView.tagData
mapView.context.unregisterComponentCallbacks(componentCallbacks)
lifecycleObserver.moveToDestroyedState()
mapView.tag = null
},
update = { mapView ->
if (subcompositionJob == null) {
subcompositionJob = parentCompositionScope.launchSubcomposition(
mapUpdaterState,
parentComposition,
mapView,
mapClickListeners,
currentContent,
)
}
})
}
}

/**
Expand Down
Loading