From a3ee7170e47833be952a4bce42672b8aeb860f00 Mon Sep 17 00:00:00 2001
From: Philip <psegerfast@gmail.com>
Date: Wed, 21 Feb 2024 10:48:25 +0100
Subject: [PATCH] Use ReusableComposition + ReusableComposeNode to allow reuse
 of underlying MapView

This would reuse the ReusableComposeNodes and ReusableComposition if placed in a container which supports reuse.
Will prevent the entire node tree from being re-created upon reuse which may or may not bring a noticeable performance improvement.
---
 .../java/com/google/maps/android/compose/Circle.kt    |  4 ++--
 .../java/com/google/maps/android/compose/GoogleMap.kt | 10 +++++-----
 .../com/google/maps/android/compose/GroundOverlay.kt  |  6 +++---
 .../com/google/maps/android/compose/InputHandler.kt   |  4 ++--
 .../google/maps/android/compose/MapClickListeners.kt  | 11 ++++++++---
 .../com/google/maps/android/compose/MapUpdater.kt     |  4 ++--
 .../java/com/google/maps/android/compose/Marker.kt    |  6 +++---
 .../java/com/google/maps/android/compose/Polygon.kt   |  4 ++--
 .../java/com/google/maps/android/compose/Polyline.kt  |  4 ++--
 .../com/google/maps/android/compose/TileOverlay.kt    |  4 ++--
 .../maps/android/compose/streetview/StreetView.kt     |  5 +++--
 .../compose/streetview/StreetViewPanoramaUpdater.kt   |  4 ++--
 12 files changed, 36 insertions(+), 30 deletions(-)

diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Circle.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Circle.kt
index 237af045..dedd5878 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/Circle.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/Circle.kt
@@ -15,7 +15,7 @@
 package com.google.maps.android.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
@@ -65,7 +65,7 @@ public fun Circle(
     onClick: (Circle) -> Unit = {},
 ) {
     val mapApplier = currentComposer.applier as? MapApplier
-    ComposeNode<CircleNode, MapApplier>(
+    ReusableComposeNode<CircleNode, MapApplier>(
         factory = {
             val circle = mapApplier?.map?.addCircle {
                 center(center)
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt
index 77d2c3f2..5314bcca 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/GoogleMap.kt
@@ -21,12 +21,12 @@ import android.os.Bundle
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.Composition
 import androidx.compose.runtime.CompositionContext
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.ReusableComposition
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -148,7 +148,7 @@ public fun GoogleMap(
     }
 }
 
-internal suspend inline fun disposingComposition(factory: () -> Composition) {
+internal suspend inline fun disposingComposition(factory: () -> ReusableComposition) {
     val composition = factory()
     try {
         awaitCancellation()
@@ -161,12 +161,12 @@ private suspend inline fun MapView.newComposition(
     parent: CompositionContext,
     mapClickListeners: MapClickListeners,
     noinline content: @Composable () -> Unit
-): Composition {
+): ReusableComposition {
     val map = awaitMap()
-    return Composition(
+    return ReusableComposition(
         MapApplier(map, this, mapClickListeners), parent
     ).apply {
-        setContent(content)
+        setContentWithReuse(content)
     }
 }
 
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/GroundOverlay.kt b/maps-compose/src/main/java/com/google/maps/android/compose/GroundOverlay.kt
index 89828218..6bd83b3a 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/GroundOverlay.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/GroundOverlay.kt
@@ -15,7 +15,7 @@
 package com.google.maps.android.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.ui.geometry.Offset
 import com.google.android.gms.maps.model.BitmapDescriptor
@@ -23,8 +23,8 @@ import com.google.android.gms.maps.model.GroundOverlay
 import com.google.android.gms.maps.model.GroundOverlayOptions
 import com.google.android.gms.maps.model.LatLng
 import com.google.android.gms.maps.model.LatLngBounds
+import com.google.maps.android.compose.GroundOverlayPosition.Companion.create
 import com.google.maps.android.ktx.addGroundOverlay
-import kotlin.IllegalStateException
 
 internal class GroundOverlayNode(
     val groundOverlay: GroundOverlay,
@@ -90,7 +90,7 @@ public fun GroundOverlay(
     onClick: (GroundOverlay) -> Unit = {},
 ) {
     val mapApplier = currentComposer.applier as? MapApplier
-    ComposeNode<GroundOverlayNode, MapApplier>(
+    ReusableComposeNode<GroundOverlayNode, MapApplier>(
         factory = {
             val groundOverlay = mapApplier?.map?.addGroundOverlay {
                 anchor(anchor.x, anchor.y)
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/InputHandler.kt b/maps-compose/src/main/java/com/google/maps/android/compose/InputHandler.kt
index a6bbda2b..779042f5 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/InputHandler.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/InputHandler.kt
@@ -2,7 +2,7 @@ package com.google.maps.android.compose
 
 import androidx.annotation.RestrictTo
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
@@ -34,7 +34,7 @@ public fun InputHandler(
     onMarkerDragEnd: ((Marker) -> Unit)? = null,
     onMarkerDragStart: ((Marker) -> Unit)? = null,
 ) {
-    ComposeNode<InputHandlerNode, MapApplier>(
+    ReusableComposeNode<InputHandlerNode, MapApplier>(
         factory = {
             InputHandlerNode(
                 onCircleClick,
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt
index 42b3ee6e..b255dc8f 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapClickListeners.kt
@@ -16,8 +16,9 @@ package com.google.maps.android.compose
 
 import android.location.Location
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ComposeNodeLifecycleCallback
 import androidx.compose.runtime.NonRestartableComposable
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -76,11 +77,15 @@ internal class MapClickListenerNode<L : Any>(
     private val map: GoogleMap,
     private val setter: GoogleMap.(L?) -> Unit,
     private val listener: L
-) : MapNode {
+) : MapNode, ComposeNodeLifecycleCallback {
     override fun onAttached() = setListener(listener)
     override fun onRemoved() = setListener(null)
     override fun onCleared() = setListener(null)
 
+    override fun onReuse() = setListener(listener)
+    override fun onDeactivate() = setListener(null)
+    override fun onRelease() = setListener(null)
+
     private fun setListener(listenerOrNull: L?) = map.setter(listenerOrNull)
 }
 
@@ -192,5 +197,5 @@ private fun MapClickListenerComposeNode(
     // when callbacks recompose rapidly; setting GoogleMap listeners could potentially be
     // expensive due to synchronization, etc. GoogleMap listeners are not designed with a
     // use case of rapid recomposition in mind.
-    if (callback() != null) ComposeNode<MapClickListenerNode<*>, MapApplier>(factory) {}
+    if (callback() != null) ReusableComposeNode<MapClickListenerNode<*>, MapApplier>(factory) {}
 }
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt b/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt
index f27a000d..2cb69f86 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/MapUpdater.kt
@@ -18,7 +18,7 @@ import android.annotation.SuppressLint
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLayoutDirection
@@ -109,7 +109,7 @@ internal inline fun MapUpdater(
     }
     val density = LocalDensity.current
     val layoutDirection = LocalLayoutDirection.current
-    ComposeNode<MapPropertiesNode, MapApplier>(
+    ReusableComposeNode<MapPropertiesNode, MapApplier>(
         factory = {
             MapPropertiesNode(
                 map = map,
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt
index 1904abc6..67166581 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/Marker.kt
@@ -16,10 +16,10 @@ package com.google.maps.android.compose
 
 import android.view.View
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
 import androidx.compose.runtime.CompositionContext
 import androidx.compose.runtime.Immutable
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -459,7 +459,7 @@ private fun MarkerImpl(
 ) {
     val mapApplier = currentComposer.applier as? MapApplier
     val compositionContext = rememberCompositionContext()
-    ComposeNode<MarkerNode, MapApplier>(
+    ReusableComposeNode<MarkerNode, MapApplier>(
         factory = {
             val marker = mapApplier?.map?.addMarker {
                 contentDescription(contentDescription)
@@ -664,7 +664,7 @@ private fun AdvancedMarkerImpl(
         advancedMarkerOptions.icon(BitmapDescriptorFactory.fromPinConfig(pinConfig))
     }
 
-    ComposeNode<MarkerNode, MapApplier>(
+    ReusableComposeNode<MarkerNode, MapApplier>(
         factory = {
             val marker = mapApplier?.map?.addMarker(advancedMarkerOptions)
                 ?: error("Error adding marker")
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Polygon.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Polygon.kt
index 9848f1dc..36774b74 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/Polygon.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/Polygon.kt
@@ -15,7 +15,7 @@
 package com.google.maps.android.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
@@ -69,7 +69,7 @@ public fun Polygon(
     onClick: (Polygon) -> Unit = {}
 ) {
     val mapApplier = currentComposer.applier as MapApplier?
-    ComposeNode<PolygonNode, MapApplier>(
+    ReusableComposeNode<PolygonNode, MapApplier>(
         factory = {
             val polygon = mapApplier?.map?.addPolygon {
                 addAll(points)
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/Polyline.kt b/maps-compose/src/main/java/com/google/maps/android/compose/Polyline.kt
index 242f0862..cc2b233d 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/Polyline.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/Polyline.kt
@@ -15,7 +15,7 @@
 package com.google.maps.android.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
@@ -71,7 +71,7 @@ public fun Polyline(
     onClick: (Polyline) -> Unit = {}
 ) {
     val mapApplier = currentComposer.applier as MapApplier?
-    ComposeNode<PolylineNode, MapApplier>(
+    ReusableComposeNode<PolylineNode, MapApplier>(
         factory = {
             val polyline = mapApplier?.map?.addPolyline {
                 addAll(points)
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/TileOverlay.kt b/maps-compose/src/main/java/com/google/maps/android/compose/TileOverlay.kt
index 18679b9b..1f53e34d 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/TileOverlay.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/TileOverlay.kt
@@ -15,7 +15,7 @@
 package com.google.maps.android.compose
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -84,7 +84,7 @@ public fun TileOverlay(
     onClick: (TileOverlay) -> Unit = {},
 ) {
     val mapApplier = currentComposer.applier as MapApplier?
-    ComposeNode<TileOverlayNode, MapApplier>(
+    ReusableComposeNode<TileOverlayNode, MapApplier>(
         factory = {
             val tileOverlay = mapApplier?.map?.addTileOverlay {
                 tileProvider(tileProvider)
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt b/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt
index e73c7d53..7816b646 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetView.kt
@@ -24,6 +24,7 @@ import androidx.compose.runtime.CompositionContext
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.ReusableComposition
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -134,10 +135,10 @@ private fun StreetViewLifecycle(streetView: StreetViewPanoramaView) {
 private suspend inline fun StreetViewPanoramaView.newComposition(
     parent: CompositionContext,
     noinline content: @Composable () -> Unit
-): Composition {
+): ReusableComposition {
     val panorama = awaitStreetViewPanorama()
     Log.d("StreetView", "Location is ${panorama.location}")
-    return Composition(
+    return ReusableComposition(
         StreetViewPanoramaApplier(panorama), parent
     ).apply {
         setContent(content)
diff --git a/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetViewPanoramaUpdater.kt b/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetViewPanoramaUpdater.kt
index 2898ec8f..a52ba730 100644
--- a/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetViewPanoramaUpdater.kt
+++ b/maps-compose/src/main/java/com/google/maps/android/compose/streetview/StreetViewPanoramaUpdater.kt
@@ -1,7 +1,7 @@
 package com.google.maps.android.compose.streetview
 
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ComposeNode
+import androidx.compose.runtime.ReusableComposeNode
 import androidx.compose.runtime.currentComposer
 import com.google.android.gms.maps.StreetViewPanorama
 import com.google.maps.android.compose.MapNode
@@ -55,7 +55,7 @@ internal inline fun StreetViewUpdater(
 ) {
     val streetViewPanorama =
         (currentComposer.applier as StreetViewPanoramaApplier).streetViewPanorama
-    ComposeNode<StreetViewPanoramaPropertiesNode, StreetViewPanoramaApplier>(
+    ReusableComposeNode<StreetViewPanoramaPropertiesNode, StreetViewPanoramaApplier>(
         factory = {
             StreetViewPanoramaPropertiesNode(
                 cameraPositionState = cameraPositionState,