From c684008803618eb81dbfa5a6e14fd7c6a3bb55ea Mon Sep 17 00:00:00 2001 From: Jason Whitmore Date: Wed, 25 Jun 2025 18:36:59 -0700 Subject: [PATCH] NearbyParentFragment.kt: fix bug where multiple user location overlays would appear Before this commit, the user could see multiple user location overlays if they paused the app and reopened it when there are no Places/pins on the map. This was caused by a linear search failing to identify the target overlay because it compared Drawables between two Overlays, which was unreliable. This commit contains a better solution for replacing existing user location overlays by adding 2 instance variables to keep track of the overlays. The position of these overlays in the overlay list can then be found by using indexOf() with these instance variables rather than the linear search that was implemented before. Some refactoring was also done. --- .../nearby/fragments/NearbyParentFragment.kt | 98 +++++++++++-------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt index e660d04df..26875927e 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt @@ -123,6 +123,7 @@ import org.osmdroid.views.CustomZoomButtonsController import org.osmdroid.views.MapView import org.osmdroid.views.overlay.MapEventsOverlay import org.osmdroid.views.overlay.Marker +import org.osmdroid.views.overlay.Overlay import org.osmdroid.views.overlay.ScaleBarOverlay import org.osmdroid.views.overlay.ScaleDiskOverlay import org.osmdroid.views.overlay.TilesOverlay @@ -267,6 +268,9 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), private var dataList: MutableList? = null private var bottomSheetAdapter: BottomSheetAdapter? = null + private var userLocationOverlay: Overlay? = null + private var userLocationErrorOverlay: Overlay? = null + private val galleryPickLauncherForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> controller?.handleActivityResultWithCallback( @@ -2690,47 +2694,8 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), */ private fun updateUserLocationOverlays(geoPoint: GeoPoint?, invalidate: Boolean) { geoPoint?.let{ - val overlays = binding?.map?.overlays ?: return@let - - // Multiply accuracy by 2 to get 95% confidence interval - val accuracy = getCurrentLocationAccuracy() * 2 - - // Create disk overlay - val errorOverlay = createCurrentLocationErrorOverlay(this.context, geoPoint, - (accuracy).toInt(), UnitOfMeasure.meter) - - // Create current location overlay - val locationOverlay = createCurrentLocationOverlay(geoPoint) - - var locationOverlayFound = false - var errorOverlayFound = false - - // Find and replace overlays - for (i in overlays.indices) { - val overlay = overlays[i] - if (overlay is ScaleDiskOverlay) { - overlays[i] = errorOverlay - errorOverlayFound = true - } - - if (overlay is Marker && overlay.icon.equals(locationOverlay.icon)) { - overlays[i] = locationOverlay - locationOverlayFound = true - } - - if (errorOverlayFound && locationOverlayFound) { - break - } - } - - // If the user location overlays were not found, add them - if (!errorOverlayFound) { - overlays.add(errorOverlay) - } - - if (!locationOverlayFound) { - overlays.add(locationOverlay) - } + updateUserLocationOverlay(geoPoint) + updateUserLocationErrorOverlay(geoPoint) } if (invalidate) { @@ -2738,6 +2703,57 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), } } + /** + * Updates the user location error overlay by either replacing it with or adding a new one. + * + * If the user location error overlay is null, the new overlay is added. If the + * overlay is not null, it is replaced by the new overlay. + * + * @param geoPoint The GeoPoint representing the user's location + */ + private fun updateUserLocationErrorOverlay(geoPoint: GeoPoint) { + val overlays = binding?.map?.overlays ?: return + + // Multiply accuracy by 2 to get 95% confidence interval + val accuracy = getCurrentLocationAccuracy() * 2 + val overlay = createCurrentLocationErrorOverlay(this.context, geoPoint, + (accuracy).toInt(), UnitOfMeasure.meter) + + val index = overlays.indexOf(userLocationErrorOverlay) + + if (userLocationErrorOverlay == null || index == -1) { + overlays.add(overlay) + } else { + overlays[index] = overlay + } + + userLocationErrorOverlay = overlay + } + + /** + * Updates the user location overlay by either replacing it with or adding a new one. + * + * If the user location overlay is null, the new overlay is added. If the + * overlay is not null, it is replaced by the new overlay. + * + * @param geoPoint The GeoPoint representing the user's location + */ + private fun updateUserLocationOverlay(geoPoint: GeoPoint) { + val overlays = binding?.map?.overlays ?: return + + val overlay = createCurrentLocationOverlay(geoPoint) + + val index = overlays.indexOf(userLocationOverlay) + + if (userLocationOverlay == null || index == -1) { + overlays.add(overlay) + } else { + overlays[index] = overlay + } + + userLocationOverlay = overlay + } + /** * @return The accuracy of the current location with a confidence at the 68th percentile. * Units are in meters. Returning 0 may indicate failure.