@@ -92,7 +92,6 @@ import org.osmdroid.views.overlay.gestures.RotationGestureOverlay
9292import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
9393import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
9494import java.io.File
95- import kotlin.math.min
9695
9796class MapFragment : Fragment (), LocationListener, PopupMenu.OnMenuItemClickListener,
9897 GestureCallback , ClickableCompassOverlay .OnCompassClickListener {
@@ -171,6 +170,9 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
171170 private var gpxViewModel: GpxViewModel ? = null
172171 private var gestureOverlay: GestureOverlay ? = null
173172
173+ private var firstFixZoom = DEFAULT_ZOOM
174+ private var awaitingFirstFix = false
175+ private var explicitViewport = false
174176 private var mapViewInitialized = false
175177
176178 private val markerViewModel: MarkerViewModel by viewModels()
@@ -388,7 +390,12 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
388390
389391 rotationGestureOverlay = RotationGestureOverlay (mapView)
390392 rotationGestureOverlay?.isEnabled = true
391- gestureOverlay = GestureOverlay (this )
393+ gestureOverlay = GestureOverlay (object : GestureCallback {
394+ override fun onUserMapInteraction () {
395+ awaitingFirstFix = false
396+ this @MapFragment.onUserMapInteraction()
397+ }
398+ })
392399 mapView.overlays.add(gestureOverlay)
393400 mapView.maxZoomLevel = maxZoomLevel
394401 mapView.isTilesScaledToDpi = true
@@ -398,6 +405,13 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
398405 mapView.overlays.add(locationOverlay)
399406 mapView.overlays.add(compassOverlay)
400407 mapView.overlays.add(scaleBarOverlay)
408+ mapView.addMapListener(object : MapListener {
409+ override fun onScroll (event : ScrollEvent ) = false
410+ override fun onZoom (event : ZoomEvent ): Boolean {
411+ if (event.zoomLevel != firstFixZoom) awaitingFirstFix = false
412+ return false
413+ }
414+ })
401415 mapView.addMapListener(DelayedMapListener (dragListener))
402416 copyrightView = view?.findViewById(R .id.copyrightView)
403417 setBaseMap()
@@ -449,21 +463,6 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
449463 }
450464
451465 private fun initializeMapState (savedInstanceState : Bundle ? ) {
452- listener?.setGpx()
453- listener?.setKml()
454-
455- val arguments = arguments
456- var mapCenterSet = false
457- // Move to received geo intent coordinates
458- arguments?.let {
459- val lat = it.getDouble(PARAM_LATITUDE , Double .MIN_VALUE )
460- val lon = it.getDouble(PARAM_LONGITUDE , Double .MIN_VALUE )
461- if (lat != Double .MIN_VALUE && lon != Double .MIN_VALUE ) {
462- mapCenterSet = true
463- animateToLatLon(lat, lon)
464- }
465- }
466-
467466 val hostActivity = activity
468467 val hasFineLocationPermission =
469468 hostActivity?.checkSelfPermission(Manifest .permission.ACCESS_FINE_LOCATION ) == PackageManager .PERMISSION_GRANTED
@@ -480,53 +479,41 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
480479 }
481480 }
482481 }
483- savedInstanceState?.let {
484- if (it.containsKey(STATE_LATITUDE ) && savedInstanceState.containsKey(STATE_LONGITUDE )
485- ) {
486- mapCenterState = GeoPoint (
487- it.getDouble(STATE_LATITUDE , 0.0 ),
488- it.getDouble(STATE_LONGITUDE , 0.0 )
489- )
490- Log .d(
491- TAG ,
492- String .format(
493- " Restoring center state: %f, %f" ,
494- mapCenterState?.latitude,
495- mapCenterState?.longitude
496- )
497- )
498- zoomState = it.getDouble(STATE_ZOOM , DEFAULT_ZOOM )
499- } else {
500- Log .d(TAG , " No center state delivered" )
501- }
502- }
503- if (mapCenterState == null ) {
504- Log .d(TAG , " No saved center state" )
505- val prefLat = sharedPreferences.getFloat(PREF_LATITUDE , 0f )
506- val prefLon = sharedPreferences.getFloat(PREF_LONGITUDE , 0f )
507- if (prefLat > 0 && prefLon > 0 ) {
508- mapCenterState = GeoPoint (prefLat.toDouble(), prefLon.toDouble())
509- Log .d(
510- TAG ,
511- String .format(" Restoring center state from prefs: %f, %f" , prefLat, prefLon)
512- )
513- }
514- }
515- mapCenterState?.let {
516- mapView.controller.setCenter(it)
517- mapCenterSet = true
518- } ? : run {
519- locationViewModel?.currentLocation?.value?.let { location ->
520- mapView.controller.setCenter(GeoPoint (location.latitude, location.longitude))
521- mapCenterSet = true
482+ val restored = savedInstanceState?.let {
483+ MapViewport .restore(
484+ if (it.containsKey(STATE_LATITUDE )) it.getDouble(STATE_LATITUDE ) else null ,
485+ if (it.containsKey(STATE_LONGITUDE )) it.getDouble(STATE_LONGITUDE ) else null ,
486+ it.getDouble(STATE_ZOOM , DEFAULT_ZOOM )
487+ )
488+ } ? : mapCenterState?.let { MapViewport (it.latitude, it.longitude, zoomState) }
489+ val persisted = MapViewport .fromPreferences(sharedPreferences.all)
490+ val known = locationViewModel?.currentLocation?.value?.let {
491+ MapViewport .restore(it.latitude, it.longitude, DEFAULT_ZOOM )
492+ }
493+ val viewport = MapViewport .select(restored, persisted, known)
494+ firstFixZoom = viewport.clampedZoom(mapView.minZoomLevel, mapView.maxZoomLevel)
495+ mapView.controller.setZoom(firstFixZoom)
496+ mapView.controller.setCenter(GeoPoint (viewport.latitude, viewport.longitude))
497+ awaitingFirstFix = restored == null && persisted == null && known == null
498+ explicitViewport = false
499+
500+ // Consume navigation arguments once; recreation should restore the viewed viewport.
501+ arguments?.let {
502+ val destination = MapViewport .restore(
503+ if (it.containsKey(PARAM_LATITUDE )) it.getDouble(PARAM_LATITUDE ) else null ,
504+ if (it.containsKey(PARAM_LONGITUDE )) it.getDouble(PARAM_LONGITUDE ) else null ,
505+ DEFAULT_ZOOM
506+ )
507+ if (destination != null && savedInstanceState == null && mapCenterState == null ) {
508+ animateToLatLon(destination.latitude, destination.longitude)
522509 }
510+ it.remove(PARAM_LATITUDE )
511+ it.remove(PARAM_LONGITUDE )
523512 }
524- if (! mapCenterSet) {
525- centerOnFirstFix()
526- }
527- zoomState = min(zoomState, maxZoomLevel)
528- mapView.controller.setZoom(zoomState)
529- if (sharedPreferences.getBoolean(PREF_FOLLOW , false )) enableFollow()
513+ listener?.setGpx()
514+ listener?.setKml()
515+ if (awaitingFirstFix) centerOnFirstFix()
516+ if (! explicitViewport && sharedPreferences.getBoolean(PREF_FOLLOW , false )) enableFollow()
530517
531518 setKeepScreenOn(sharedPreferences.getBoolean(PREF_KEEP_SCREEN_ON , false ))
532519
@@ -659,21 +646,22 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
659646 }
660647
661648 private fun centerOnFirstFix () {
662- locationOverlay?.runOnFirstFix {
663- val location = locationOverlay?.myLocation
664- location?.let {
665- try {
666- activity?.runOnUiThread {
667- mapView.controller.animateTo(it)
668- }
669- } catch (e: IllegalStateException ) {
670- e.printStackTrace()
649+ val startupMap = mapView
650+ val startupOverlay = locationOverlay
651+ startupOverlay?.runOnFirstFix {
652+ activity?.runOnUiThread {
653+ if (view == null || mapView != = startupMap) return @runOnUiThread
654+ if (awaitingFirstFix) {
655+ startupOverlay.myLocation?.let { startupMap.controller.animateTo(it) }
671656 }
657+ awaitingFirstFix = false
672658 }
673659 }
674660 }
675661
676662 private fun animateToLatLon (lat : Double , lon : Double ) {
663+ awaitingFirstFix = false
664+ explicitViewport = true
677665 mapHandler.postDelayed({
678666 disableFollow()
679667 mapView.controller.animateTo(GeoPoint (lat, lon))
@@ -792,6 +780,7 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
792780 }
793781
794782 private fun disableFollow () {
783+ awaitingFirstFix = false
795784 followEnabled = false
796785 activity?.let { (it as AppCompatActivity ).supportInvalidateOptionsMenu() }
797786 locationOverlay?.disableFollowLocation()
@@ -800,17 +789,14 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
800789 }
801790
802791 private fun saveMapCenterPrefs () {
803- mapCenterState?.let {
804- sharedPreferences.edit { putFloat(PREF_LATITUDE , it.latitude.toFloat()) }
805- sharedPreferences.edit { putFloat(PREF_LONGITUDE , it.longitude.toFloat()) }
806- Log .d(
807- TAG ,
808- String .format(
809- " Saving center prefs: %f, %f" ,
810- it.latitude,
811- it.longitude
812- )
813- )
792+ if (! mapViewInitialized) return
793+ val center = mapView.mapCenter
794+ mapCenterState = GeoPoint (center.latitude, center.longitude)
795+ zoomState = mapView.zoomLevelDouble
796+ sharedPreferences.edit {
797+ putFloat(PREF_LATITUDE , center.latitude.toFloat())
798+ putFloat(PREF_LONGITUDE , center.longitude.toFloat())
799+ putFloat(PREF_ZOOM , zoomState.toFloat())
814800 }
815801 }
816802
@@ -853,15 +839,6 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
853839 compassOverlay?.enableCompass()
854840 scaleBarOverlay?.enableScaleBar()
855841 mapView.invalidate()
856- mapCenterState?.let {
857- mapView.controller.setCenter(mapCenterState)
858- mapCenterState = null // We're done with the old state
859- } ? : run {
860- locationViewModel?.currentLocation?.value?.let { location ->
861- val geoPoint = GeoPoint (location.latitude, location.longitude)
862- mapView.controller.setCenter(geoPoint)
863- }
864- }
865842 locationManager?.let {
866843 try {
867844 it.requestLocationUpdates(
@@ -887,8 +864,9 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
887864 }
888865
889866 override fun onPause () {
867+ awaitingFirstFix = false
890868 if (::mapView.isInitialized) {
891- mapCenterState = mapView.mapCenter as GeoPoint
869+ saveMapCenterPrefs()
892870 }
893871 try {
894872 locationManager?.removeUpdates(this )
@@ -922,7 +900,8 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
922900 )
923901 )
924902 }
925- outState.putDouble(STATE_ZOOM , mapView.zoomLevelDouble)
903+ zoomState = mapView.zoomLevelDouble
904+ outState.putDouble(STATE_ZOOM , zoomState)
926905 }
927906 super .onSaveInstanceState(outState)
928907 }
@@ -989,6 +968,8 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
989968 }
990969
991970 private fun zoomToBounds (box : BoundingBox ) {
971+ awaitingFirstFix = false
972+ explicitViewport = true
992973 mapView.post {
993974 if (mapView.height > 0 ) {
994975 mapView.zoomToBoundingBox(box, true , 64 )
@@ -1107,6 +1088,7 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
11071088 return true
11081089 }
11091090 R .id.action_location -> {
1091+ awaitingFirstFix = false
11101092 locationViewModel?.currentLocation?.value?.let { location ->
11111093 mapView.controller.animateTo(GeoPoint (location))
11121094 }
@@ -1206,7 +1188,6 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
12061188 return false
12071189 }
12081190 R .id.action_settings -> {
1209- mapCenterState = mapView.mapCenter as GeoPoint
12101191 saveMapCenterPrefs()
12111192 listener?.openSettings()
12121193 return true
@@ -1360,6 +1341,7 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
13601341 }
13611342
13621343 override fun onDestroyView () {
1344+ awaitingFirstFix = false
13631345 cacheRevisionJob?.cancel()
13641346 mapView.tileProvider.detach()
13651347 super .onDestroyView()
@@ -1558,6 +1540,7 @@ class MapFragment : Fragment(), LocationListener, PopupMenu.OnMenuItemClickListe
15581540 private const val PREF_OVERLAY = " overlay"
15591541 private const val PREF_LATITUDE = " latitude"
15601542 private const val PREF_LONGITUDE = " longitude"
1543+ private const val PREF_ZOOM = " zoom"
15611544 private const val PREF_FOLLOW = " follow"
15621545 private const val BASE_MAP_OTM = 1
15631546 private const val BASE_MAP_OSM = 2
0 commit comments