Skip to content

Commit 19a57b6

Browse files
committed
Fix: Comprehensive map location and recentering improvements
- Auto-recenter map on fresh install and after enabling location services. - Show location-off dialog when recenter FAB is tapped with services disabled. - Auto-reload map when location services are enabled from system settings. - Add blue dot marker at user location on map initialization. - Center map to current location or wait for first GPS fix properly. Fixes issue where map showed London instead of user's actual location on fresh install. Fixes commons-app#6599
1 parent 74c2258 commit 19a57b6

File tree

1 file changed

+154
-30
lines changed

1 file changed

+154
-30
lines changed

app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt

Lines changed: 154 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
8686
LocationUpdateListener, LocationPermissionCallback {
8787
private var bottomSheetDetailsBehavior: BottomSheetBehavior<*>? = null
8888
private var broadcastReceiver: BroadcastReceiver? = null
89+
private var locationProvidersBroadcastReceiver: BroadcastReceiver? = null
8990
private var isNetworkErrorOccurred = false
9091
private var snackbar: Snackbar? = null
9192
private var isDarkTheme = false
9293
private var isPermissionDenied = false
94+
private var locationServicesEnabledOnPause = false
9395
private var lastKnownLocation: LatLng? = null // last location of user
9496
private var recenterToUserLocation = false // true is recenter is needed (ie. when current location is in visible map boundaries)
9597
private var clickedMarker: BaseMarker? = null
@@ -103,6 +105,7 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
103105
private var prevLongitude = 0.0
104106
private var recentlyCameFromNearbyMap = false
105107
private var shouldPerformMapReadyActionsOnResume = false
108+
private var isWaitingForFirstLocation = false
106109
private var presenter: ExploreMapPresenter? = null
107110
private var binding: FragmentExploreMapBinding? = null
108111
var mediaList: MutableList<Media>? = null
@@ -179,6 +182,7 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
179182
Html.fromHtml(getString(R.string.map_attribution))
180183
}
181184
initNetworkBroadCastReceiver()
185+
initLocationProvidersBroadCastReceiver()
182186
locationPermissionsHelper = LocationPermissionsHelper(
183187
requireActivity(), locationManager,
184188
this
@@ -281,7 +285,37 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
281285
if (broadcastReceiver != null) {
282286
requireActivity().registerReceiver(broadcastReceiver, intentFilter)
283287
}
288+
if (locationProvidersBroadcastReceiver != null) {
289+
val locationProvidersFilter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
290+
requireActivity().registerReceiver(locationProvidersBroadcastReceiver, locationProvidersFilter)
291+
}
284292
setSearchThisAreaButtonVisibility(false)
293+
294+
// Check if location services were enabled while app was in Settings
295+
val hasPermission = locationPermissionsHelper?.checkLocationPermission(requireActivity()) == true
296+
val isLocationNowEnabled = locationPermissionsHelper?.isLocationAccessToAppsTurnedOn() == true
297+
298+
if (hasPermission && isLocationNowEnabled && !locationServicesEnabledOnPause) {
299+
Timber.d("Location services enabled while app was paused - refreshing map")
300+
locationManager.registerLocationManager()
301+
drawMyLocationMarker()
302+
303+
val cachedLocation = locationManager.getLastLocation()
304+
if (cachedLocation != null) {
305+
val targetP = GeoPoint(cachedLocation.latitude, cachedLocation.longitude)
306+
mapCenter = targetP
307+
binding?.mapView?.controller?.setCenter(targetP)
308+
recenterMarkerToPosition(targetP)
309+
moveCameraToPosition(targetP)
310+
populatePlaces(cachedLocation)
311+
} else {
312+
isWaitingForFirstLocation = true
313+
setProgressBarVisibility(true)
314+
}
315+
}
316+
// Update tracked state for next pause/resume cycle
317+
locationServicesEnabledOnPause = isLocationNowEnabled
318+
285319
if (shouldPerformMapReadyActionsOnResume) {
286320
shouldPerformMapReadyActionsOnResume = false
287321
performMapReadyActions()
@@ -290,8 +324,12 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
290324

291325
override fun onPause() {
292326
super.onPause()
327+
// Track location services state before pausing
328+
locationServicesEnabledOnPause = locationPermissionsHelper?.isLocationAccessToAppsTurnedOn() == true
329+
293330
// unregistering the broadcastReceiver, as it was causing an exception and a potential crash
294331
unregisterNetworkReceiver()
332+
unregisterLocationProvidersReceiver()
295333
locationManager.unregisterLocationManager()
296334
locationManager.removeLocationListener(this)
297335
}
@@ -311,10 +349,8 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
311349
locationPermissionsHelper!!.showLocationOffDialog(requireActivity(), R.string.location_off_dialog_text)
312350
}
313351
} else {
314-
locationPermissionsHelper!!.requestForLocationAccess(
315-
R.string.location_permission_title,
316-
R.string.location_permission_rationale
317-
)
352+
// Use activityResultLauncher for proper callback handling
353+
askForLocationPermission()
318354
}
319355
}
320356

@@ -339,6 +375,16 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
339375
*/
340376
private fun unregisterNetworkReceiver() =
341377
activity?.unregisterReceiver(broadcastReceiver)
378+
379+
private fun unregisterLocationProvidersReceiver() {
380+
try {
381+
if (locationProvidersBroadcastReceiver != null) {
382+
requireActivity().unregisterReceiver(locationProvidersBroadcastReceiver)
383+
}
384+
} catch (e: Exception) {
385+
Timber.e(e, "Error unregistering location providers receiver")
386+
}
387+
}
342388

343389
private fun startMapWithoutPermission() {
344390
lastKnownLocation = defaultLatLng
@@ -361,23 +407,29 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
361407
isPermissionDenied = true
362408
}
363409

364-
lastKnownLocation = getLastLocation()
365-
366-
if (lastKnownLocation == null) {
367-
lastKnownLocation = defaultLatLng
368-
}
410+
lastKnownLocation = locationManager.getLastLocation()
369411

370412
// if we came from 'Show in Explore' in Nearby, load Nearby map center and zoom
371413
if (isCameFromNearbyMap) {
414+
val targetP = GeoPoint(prevLatitude, prevLongitude)
415+
mapCenter = targetP
372416
moveCameraToPosition(
373-
GeoPoint(prevLatitude, prevLongitude),
417+
targetP,
374418
prevZoom.coerceIn(1.0, 22.0),
375419
1L
376420
)
377-
} else {
378-
moveCameraToPosition(
379-
GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)
380-
)
421+
recenterMarkerToPosition(targetP)
422+
} else if (lastKnownLocation != null) {
423+
val targetP = GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)
424+
mapCenter = targetP
425+
moveCameraToPosition(targetP)
426+
recenterMarkerToPosition(targetP)
427+
} else if (!isWaitingForFirstLocation) {
428+
lastKnownLocation = defaultLatLng
429+
val targetP = GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)
430+
mapCenter = targetP
431+
moveCameraToPosition(targetP)
432+
recenterMarkerToPosition(targetP)
381433
}
382434
presenter!!.onMapReady(exploreMapController)
383435
}
@@ -484,6 +536,16 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
484536
) {
485537
lastKnownLocation = latLng
486538
exploreMapController.currentLocation = lastKnownLocation
539+
if (isWaitingForFirstLocation && latLng != null) {
540+
isWaitingForFirstLocation = false
541+
setProgressBarVisibility(false)
542+
val targetP = GeoPoint(latLng.latitude, latLng.longitude)
543+
mapCenter = targetP
544+
binding!!.mapView.controller.setCenter(targetP)
545+
recenterMarkerToPosition(targetP)
546+
moveCameraToPosition(targetP)
547+
}
548+
487549
presenter!!.updateMap(locationChangeType)
488550
}
489551

@@ -563,28 +625,28 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
563625
private fun locationPermissionGranted() {
564626
isPermissionDenied = false
565627
applicationKvStore.putBoolean("doNotAskForLocationPermission", false)
628+
629+
// Add listener and register location manager
630+
locationManager.addLocationListener(this)
631+
locationManager.registerLocationManager()
632+
drawMyLocationMarker()
633+
566634
lastKnownLocation = locationManager.getLastLocation()
567-
val target = lastKnownLocation
635+
568636
if (lastKnownLocation != null) {
569-
val targetP = GeoPoint(target!!.latitude, target.longitude)
637+
val targetP = GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)
570638
mapCenter = targetP
571639
binding!!.mapView.controller.setCenter(targetP)
572640
recenterMarkerToPosition(targetP)
573641
moveCameraToPosition(targetP)
574-
} else if (locationManager.isGPSProviderEnabled()
575-
|| locationManager.isNetworkProviderEnabled()
576-
) {
577-
locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
578-
locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
579-
setProgressBarVisibility(true)
642+
populatePlaces(lastKnownLocation)
580643
} else {
581-
locationPermissionsHelper!!.showLocationOffDialog(
582-
requireActivity(),
583-
R.string.ask_to_turn_location_on_text
584-
)
644+
// No cached location - set flag to wait for first GPS fix
645+
isWaitingForFirstLocation = true
646+
setProgressBarVisibility(true)
647+
// Still need to populate with default location, will recenter when location arrives
648+
populatePlaces(getMapCenter())
585649
}
586-
presenter!!.onMapReady(exploreMapController)
587-
registerUnregisterLocationListener(false)
588650
}
589651

590652
fun registerUnregisterLocationListener(removeLocationListener: Boolean) {
@@ -605,7 +667,11 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
605667
if (!locationPermissionsHelper!!.checkLocationPermission(requireActivity())) {
606668
askForLocationPermission()
607669
} else {
608-
locationPermissionGranted()
670+
if (locationPermissionsHelper!!.isLocationAccessToAppsTurnedOn()) {
671+
locationPermissionGranted()
672+
} else {
673+
locationPermissionsHelper!!.showLocationOffDialog(requireActivity(), R.string.location_off_dialog_text)
674+
}
609675
}
610676
}
611677
if (curLatLng == null) {
@@ -1113,6 +1179,44 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
11131179
}
11141180
}
11151181
}
1182+
1183+
/**
1184+
* Broadcast receiver for location providers changes (when location services are turned on/off)
1185+
*/
1186+
private fun initLocationProvidersBroadCastReceiver() {
1187+
locationProvidersBroadcastReceiver = object : BroadcastReceiver() {
1188+
override fun onReceive(context: Context?, intent: Intent?) {
1189+
if (intent?.action == LocationManager.PROVIDERS_CHANGED_ACTION) {
1190+
if (activity != null && isResumed) {
1191+
val hasPermission = locationPermissionsHelper?.checkLocationPermission(requireActivity()) == true
1192+
val isLocationEnabled = locationPermissionsHelper?.isLocationAccessToAppsTurnedOn() == true
1193+
1194+
// If location services just got turned on
1195+
if (hasPermission && isLocationEnabled && !locationServicesEnabledOnPause) {
1196+
Timber.d("Location services were turned on")
1197+
locationManager.registerLocationManager()
1198+
locationManager.addLocationListener(this@ExploreMapFragment)
1199+
drawMyLocationMarker()
1200+
1201+
val cachedLocation = locationManager.getLastLocation()
1202+
if (cachedLocation != null) {
1203+
val targetP = GeoPoint(cachedLocation.latitude, cachedLocation.longitude)
1204+
mapCenter = targetP
1205+
binding?.mapView?.controller?.setCenter(targetP)
1206+
recenterMarkerToPosition(targetP)
1207+
moveCameraToPosition(targetP)
1208+
populatePlaces(cachedLocation)
1209+
} else {
1210+
isWaitingForFirstLocation = true
1211+
setProgressBarVisibility(true)
1212+
}
1213+
}
1214+
locationServicesEnabledOnPause = isLocationEnabled
1215+
}
1216+
}
1217+
}
1218+
}
1219+
}
11161220

11171221
val isAttachedToActivity: Boolean
11181222
get() = isVisible && activity != null
@@ -1121,12 +1225,32 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
11211225

11221226
override fun onLocationPermissionGranted() {
11231227
if (locationPermissionsHelper!!.isLocationAccessToAppsTurnedOn()) {
1228+
locationManager.addLocationListener(this)
1229+
11241230
locationManager.registerLocationManager()
11251231
drawMyLocationMarker()
1232+
1233+
// Check if we have a cached location
1234+
val cachedLocation = locationManager.getLastLocation()
1235+
1236+
if (cachedLocation != null) {
1237+
// Center map immediately
1238+
val targetP = GeoPoint(cachedLocation.latitude, cachedLocation.longitude)
1239+
mapCenter = targetP
1240+
binding?.mapView?.controller?.setCenter(targetP)
1241+
recenterMarkerToPosition(targetP)
1242+
moveCameraToPosition(targetP)
1243+
populatePlaces(cachedLocation)
1244+
} else {
1245+
// No cached location - wait for first GPS fix
1246+
isWaitingForFirstLocation = true
1247+
setProgressBarVisibility(true)
1248+
// Still need to populate with default, but will recenter when location arrives
1249+
populatePlaces(getMapCenter())
1250+
}
11261251
} else {
11271252
locationPermissionsHelper!!.showLocationOffDialog(requireActivity(), R.string.location_off_dialog_text)
11281253
}
1129-
onLocationChanged(LocationChangeType.PERMISSION_JUST_GRANTED, null)
11301254
}
11311255

11321256
fun onLocationChanged(locationChangeType: LocationChangeType, location: Location?) {

0 commit comments

Comments
 (0)