Skip to content

Commit 76685a7

Browse files
authored
feat!: switch to TLHC mode to fix rendering issues on Android (#340)
* feat: switch from PlatformViewLink to AndroidView (TLHC) * fix: update mapview invalidation to work better with new map renderer
1 parent 8585982 commit 76685a7

File tree

7 files changed

+62
-179
lines changed

7 files changed

+62
-179
lines changed

android/src/main/kotlin/com/google/maps/flutter/navigation/FrameDelayHandler.kt

Lines changed: 0 additions & 83 deletions
This file was deleted.

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapView.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ internal constructor(
5151
imageRegistry.mapViewInitializationComplete()
5252
mapReady()
5353
mapOptions.padding?.let { setPadding(it) }
54-
invalidateViewAfterMapLoad()
5554
}
5655
}
5756

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsAutoMapView.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ internal constructor(
3838
imageRegistry.mapViewInitializationComplete()
3939
viewRegistry.registerAndroidAutoView(this)
4040
mapReady()
41-
invalidateViewAfterMapLoad()
4241
}
4342

4443
// Handled by AndroidAutoBaseScreen.

android/src/main/kotlin/com/google/maps/flutter/navigation/GoogleMapsBaseMapView.kt

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ package com.google.maps.flutter.navigation
1919
import android.annotation.SuppressLint
2020
import android.content.res.Resources
2121
import android.graphics.Point
22+
import android.graphics.SurfaceTexture
2223
import android.location.Location
24+
import android.view.TextureView
2325
import android.view.View
26+
import android.view.ViewGroup
2427
import com.google.android.gms.maps.CameraUpdate
2528
import com.google.android.gms.maps.CameraUpdateFactory
2629
import com.google.android.gms.maps.GoogleMap
@@ -34,19 +37,13 @@ import com.google.android.gms.maps.model.MapStyleOptions
3437
import com.google.android.gms.maps.model.Marker
3538
import com.google.android.gms.maps.model.Polygon
3639
import com.google.android.gms.maps.model.Polyline
37-
import com.google.android.libraries.navigation.NavigationView
3840

3941
abstract 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

Comments
 (0)