@@ -19,8 +19,11 @@ package com.google.maps.flutter.navigation
1919import android.annotation.SuppressLint
2020import android.content.res.Resources
2121import android.graphics.Point
22+ import android.graphics.SurfaceTexture
2223import android.location.Location
24+ import android.view.TextureView
2325import android.view.View
26+ import android.view.ViewGroup
2427import com.google.android.gms.maps.CameraUpdate
2528import com.google.android.gms.maps.CameraUpdateFactory
2629import com.google.android.gms.maps.GoogleMap
@@ -34,19 +37,13 @@ import com.google.android.gms.maps.model.MapStyleOptions
3437import com.google.android.gms.maps.model.Marker
3538import com.google.android.gms.maps.model.Polygon
3639import com.google.android.gms.maps.model.Polyline
37- import com.google.android.libraries.navigation.NavigationView
3840
3941abstract class GoogleMapsBaseMapView (
4042 protected val viewId : Int? ,
4143 mapOptions : MapOptions ,
4244 protected val viewEventApi : ViewEventApi ? ,
4345 private val imageRegistry : ImageRegistry ,
4446) {
45- companion object {
46- const val INVALIDATION_FRAME_SKIP_AMOUNT = 4 // Amount of skip frames before invalidation
47- }
48-
49- private val _frameDelayHandler = FrameDelayHandler (INVALIDATION_FRAME_SKIP_AMOUNT )
5047 private var _map : GoogleMap ? = null
5148 private val _markers = mutableListOf<MarkerController >()
5249 private val _polygons = mutableListOf<PolygonController >()
@@ -107,6 +104,9 @@ abstract class GoogleMapsBaseMapView(
107104 }
108105
109106 protected fun mapReady () {
107+ // Install custom invalidator for the map view.
108+ installInvalidator()
109+
110110 // Call and clear view ready callback if available.
111111 _mapReadyCallback ?.let { callback ->
112112 callback(Result .success(Unit ))
@@ -214,27 +214,55 @@ abstract class GoogleMapsBaseMapView(
214214 )
215215 }
216216
217- /* *
218- * Workaround for map view not showing added or edited map objects immediately after add/edit.
219- * Schedules [NavigationView.invalidate] call after a certain amount of frames are drawn. In
220- * marker updates short delay is not enough, [doubleInvalidate] is set to true.
221- *
222- * @param doubleInvalidate if true, schedules another invalidate event after the first one.
223- */
224- protected fun invalidateViewAfterMapLoad (doubleInvalidate : Boolean = false) {
225- if (_loadedCallbackPending ) {
217+ // Installs a custom invalidator for the map view.
218+ private fun installInvalidator () {
219+ if (getView() == null ) {
220+ // This should only happen in tests.
221+ return
222+ }
223+ val textureView = findTextureView(getView()!! )
224+ if (textureView == null ) {
226225 return
227226 }
228- _loadedCallbackPending = true
229- getMap().setOnMapLoadedCallback {
230- _loadedCallbackPending = false
231- _frameDelayHandler .scheduleActionWithFrameDelay {
232- getView().invalidate()
233- if (doubleInvalidate) {
234- _frameDelayHandler .scheduleActionWithFrameDelay { getView().invalidate() }
227+ val internalListener = textureView.surfaceTextureListener
228+
229+ // Override the Maps internal SurfaceTextureListener with one that invalidates mapview on
230+ // texture update.
231+ textureView.surfaceTextureListener =
232+ object : TextureView .SurfaceTextureListener {
233+ override fun onSurfaceTextureAvailable (surface : SurfaceTexture , width : Int , height : Int ) {
234+ internalListener?.onSurfaceTextureAvailable(surface, width, height)
235+ }
236+
237+ override fun onSurfaceTextureDestroyed (surface : SurfaceTexture ): Boolean {
238+ return internalListener?.onSurfaceTextureDestroyed(surface) ? : true
239+ }
240+
241+ override fun onSurfaceTextureSizeChanged (surface : SurfaceTexture , width : Int , height : Int ) {
242+ internalListener?.onSurfaceTextureSizeChanged(surface, width, height)
243+ }
244+
245+ override fun onSurfaceTextureUpdated (surface : SurfaceTexture ) {
246+ internalListener?.onSurfaceTextureUpdated(surface)
247+ // Invalidate the view to ensure it is redrawn.
248+ getView()?.invalidate()
249+ }
250+ }
251+ }
252+
253+ // Returns the first TextureView found in the view hierarchy.
254+ private fun findTextureView (view : View ): TextureView ? {
255+ if (view is TextureView ) {
256+ return view
257+ }
258+ if (view is ViewGroup ) {
259+ for (i in 0 until view.childCount) {
260+ findTextureView(view.getChildAt(i))?.let {
261+ return it
235262 }
236263 }
237264 }
265+ return null
238266 }
239267
240268 @Throws(FlutterError ::class )
@@ -304,22 +332,18 @@ abstract class GoogleMapsBaseMapView(
304332
305333 @SuppressLint(" MissingPermission" )
306334 fun setMyLocationEnabled (enabled : Boolean ) {
307- invalidateViewAfterMapLoad()
308335 getMap().isMyLocationEnabled = enabled
309336 }
310337
311338 fun setMyLocationButtonEnabled (enabled : Boolean ) {
312- invalidateViewAfterMapLoad()
313339 getMap().uiSettings.isMyLocationButtonEnabled = enabled
314340 }
315341
316342 fun setZoomGesturesEnabled (enabled : Boolean ) {
317- invalidateViewAfterMapLoad()
318343 getMap().uiSettings.isZoomGesturesEnabled = enabled
319344 }
320345
321346 fun setZoomControlsEnabled (enabled : Boolean ) {
322- invalidateViewAfterMapLoad()
323347 getMap().uiSettings.isZoomControlsEnabled = enabled
324348 }
325349
@@ -364,7 +388,6 @@ abstract class GoogleMapsBaseMapView(
364388 }
365389
366390 fun setCompassEnabled (enabled : Boolean ) {
367- invalidateViewAfterMapLoad()
368391 getMap().uiSettings.isCompassEnabled = enabled
369392 }
370393
@@ -576,20 +599,17 @@ abstract class GoogleMapsBaseMapView(
576599 }
577600
578601 fun setMapType (mapType : Int ) {
579- invalidateViewAfterMapLoad()
580602 getMap().mapType = mapType
581603 }
582604
583605 fun setMapStyle (styleJson : String ) {
584- invalidateViewAfterMapLoad()
585606 if (! getMap().setMapStyle(MapStyleOptions (styleJson))) {
586607 throw FlutterError (" mapStyleError" , " Failed to set map style" )
587608 }
588609 }
589610
590611 @SuppressLint(" MissingPermission" )
591612 fun followMyLocation (perspective : Int , zoomLevel : Double? ) {
592- invalidateViewAfterMapLoad()
593613 getMap().followMyLocation(perspective)
594614 if (zoomLevel != null ) {
595615 val options: FollowMyLocationOptions =
@@ -657,9 +677,6 @@ abstract class GoogleMapsBaseMapView(
657677 result.add(it)
658678 }
659679 }
660- // Double invalidate map view. Marker icon updates seem to take extra
661- // time and some times icon did not update properly after single invalidate.
662- invalidateViewAfterMapLoad(true )
663680 return result
664681 }
665682
@@ -677,15 +694,11 @@ abstract class GoogleMapsBaseMapView(
677694 }
678695 }
679696 error?.let { throw error as Throwable }
680- // Double invalidate map view. Marker icon updates seem to take extra
681- // time and some times icon did not update properly after single invalidate.
682- invalidateViewAfterMapLoad(true )
683697 return result
684698 }
685699
686700 @Throws(FlutterError ::class )
687701 fun removeMarkers (markers : List <MarkerDto >) {
688- invalidateViewAfterMapLoad()
689702 var error: Throwable ? = null
690703 markers.forEach {
691704 findMarkerController(it.markerId)?.let { controller ->
@@ -700,7 +713,6 @@ abstract class GoogleMapsBaseMapView(
700713 }
701714
702715 fun clearMarkers () {
703- invalidateViewAfterMapLoad()
704716 _markers .forEach { controller -> controller.remove() }
705717 _markers .clear()
706718 }
@@ -722,7 +734,6 @@ abstract class GoogleMapsBaseMapView(
722734
723735 fun addPolygons (polygons : List <PolygonDto >): List <PolygonDto > {
724736 val density = Resources .getSystem().displayMetrics.density
725- invalidateViewAfterMapLoad()
726737 val result = mutableListOf<PolygonDto >()
727738 polygons.forEach {
728739 val builder = PolygonBuilder ()
@@ -739,7 +750,6 @@ abstract class GoogleMapsBaseMapView(
739750 }
740751
741752 fun updatePolygons (polygons : List <PolygonDto >): List <PolygonDto > {
742- invalidateViewAfterMapLoad()
743753 var error: Throwable ? = null
744754 val result = mutableListOf<PolygonDto >()
745755 val density = Resources .getSystem().displayMetrics.density
@@ -759,7 +769,6 @@ abstract class GoogleMapsBaseMapView(
759769 }
760770
761771 fun removePolygons (polygons : List <PolygonDto >) {
762- invalidateViewAfterMapLoad()
763772 var error: Throwable ? = null
764773 polygons.forEach {
765774 findPolygonController(it.polygonId)?.let { controller ->
@@ -775,7 +784,6 @@ abstract class GoogleMapsBaseMapView(
775784 }
776785
777786 fun clearPolygons () {
778- invalidateViewAfterMapLoad()
779787 _polygons .forEach { controller -> controller.remove() }
780788 _polygons .clear()
781789 }
@@ -789,7 +797,6 @@ abstract class GoogleMapsBaseMapView(
789797
790798 fun addPolylines (polylines : List <PolylineDto >): List <PolylineDto > {
791799 val density = Resources .getSystem().displayMetrics.density
792- invalidateViewAfterMapLoad()
793800 val result = mutableListOf<PolylineDto >()
794801 polylines.forEach {
795802 val builder = PolylineBuilder ()
@@ -808,7 +815,6 @@ abstract class GoogleMapsBaseMapView(
808815 fun updatePolylines (polylines : List <PolylineDto >): List <PolylineDto > {
809816 var error: Throwable ? = null
810817 val density = Resources .getSystem().displayMetrics.density
811- invalidateViewAfterMapLoad()
812818 val result = mutableListOf<PolylineDto >()
813819 polylines.forEach {
814820 findPolylineController(it.polylineId)?.let { controller ->
@@ -825,7 +831,6 @@ abstract class GoogleMapsBaseMapView(
825831 }
826832
827833 fun removePolylines (polylines : List <PolylineDto >) {
828- invalidateViewAfterMapLoad()
829834 var error: Throwable ? = null
830835 polylines.forEach {
831836 findPolylineController(it.polylineId)?.let { controller ->
@@ -841,7 +846,6 @@ abstract class GoogleMapsBaseMapView(
841846 }
842847
843848 fun clearPolylines () {
844- invalidateViewAfterMapLoad()
845849 _polylines .forEach { controller -> controller.remove() }
846850 _polylines .clear()
847851 }
@@ -855,7 +859,6 @@ abstract class GoogleMapsBaseMapView(
855859
856860 fun addCircles (circles : List <CircleDto >): List <CircleDto > {
857861 val density = Resources .getSystem().displayMetrics.density
858- invalidateViewAfterMapLoad()
859862 val result = mutableListOf<CircleDto >()
860863 circles.forEach {
861864 val builder = CircleBuilder ()
@@ -873,7 +876,6 @@ abstract class GoogleMapsBaseMapView(
873876
874877 fun updateCircles (circles : List <CircleDto >): List <CircleDto > {
875878 val density = Resources .getSystem().displayMetrics.density
876- invalidateViewAfterMapLoad()
877879 val result = mutableListOf<CircleDto >()
878880 var error: Throwable ? = null
879881 circles.forEach {
@@ -890,7 +892,6 @@ abstract class GoogleMapsBaseMapView(
890892 }
891893
892894 fun removeCircles (circles : List <CircleDto >) {
893- invalidateViewAfterMapLoad()
894895 var error: Throwable ? = null
895896 circles.forEach {
896897 findCircleController(it.circleId)?.let { controller ->
@@ -905,7 +906,6 @@ abstract class GoogleMapsBaseMapView(
905906 }
906907
907908 fun clearCircles () {
908- invalidateViewAfterMapLoad()
909909 _circles .forEach { controller -> controller.remove() }
910910 _circles .clear()
911911 }
0 commit comments