diff --git a/src/it/reyboz/bustorino/ActivityMap.java b/src/it/reyboz/bustorino/ActivityMap.java index 80122d1..ba00de4 100644 --- a/src/it/reyboz/bustorino/ActivityMap.java +++ b/src/it/reyboz/bustorino/ActivityMap.java @@ -1,214 +1,253 @@ package it.reyboz.bustorino; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.PreferenceManager; import org.osmdroid.api.IMapController; import org.osmdroid.config.Configuration; import org.osmdroid.events.DelayedMapListener; import org.osmdroid.events.MapListener; import org.osmdroid.events.ScrollEvent; import org.osmdroid.events.ZoomEvent; import org.osmdroid.tileprovider.tilesource.TileSourceFactory; import org.osmdroid.util.BoundingBox; import org.osmdroid.util.GeoPoint; import org.osmdroid.views.MapView; +import org.osmdroid.views.overlay.FolderOverlay; import org.osmdroid.views.overlay.Marker; +import org.osmdroid.views.overlay.Overlay; import org.osmdroid.views.overlay.infowindow.InfoWindow; +import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider; +import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay; + +import java.util.List; import it.reyboz.bustorino.backend.Stop; import it.reyboz.bustorino.map.CustomInfoWindow; import it.reyboz.bustorino.middleware.StopsDB; public class ActivityMap extends AppCompatActivity { + private static final String TAG = "Busto-MapActivity"; + private static final String MAP_CURRENT_ZOOM_KEY = "map-current-zoom"; + private static final String MAP_CENTER_LAT_KEY = "map-center-lat"; + private static final String MAP_CENTER_LON_KEY = "map-center-lon"; + private static MapView map = null; public Context ctx; + private MyLocationNewOverlay mLocationOverlay = null; + private FolderOverlay stopsFolderOverlay = null; @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //handle permissions first, before map is created. not depicted here //load/initialize the osmdroid configuration, this can be done ctx = getApplicationContext(); Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx)); //setting this before the layout is inflated is a good idea //it 'should' ensure that the map has a writable location for the map cache, even without permissions //if no tiles are displayed, you can try overriding the cache path using Configuration.getInstance().setCachePath //see also StorageUtils //note, the load method also sets the HTTP User Agent to your application's package name, abusing osm's tile servers will get you banned based on this string //inflate and create the map setContentView(R.layout.activity_map); map = (MapView) findViewById(R.id.map); map.setTileSource(TileSourceFactory.MAPNIK); // add ability to zoom with 2 fingers map.setMultiTouchControls(true); + //setup FolderOverlay + stopsFolderOverlay = new FolderOverlay(); + // take the parameters if it's called from other Activities Bundle b = getIntent().getExtras(); GeoPoint marker = null; String name = null; String ID = null; if(b != null) { double lat = b.getDouble("lat"); double lon = b.getDouble("lon"); marker = new GeoPoint(lat, lon); name = b.getString("name"); ID = b.getString("ID"); } startMap(marker, name, ID); + + // on drag and zoom reload the markers map.addMapListener(new DelayedMapListener(new MapListener() { @Override public boolean onScroll(ScrollEvent paramScrollEvent) { loadMarkers(); return true; } @Override public boolean onZoom(ZoomEvent event) { loadMarkers(); return true; } })); } public void startMap(GeoPoint marker, String name, String ID) { // move the map on the marker position or on a default view point: Turin, Piazza Castello // and set the start zoom IMapController mapController = map.getController(); GeoPoint startPoint; if (marker != null) { startPoint = marker; mapController.setZoom(20.0); } else { startPoint = new GeoPoint(45.0708, 7.6858); mapController.setZoom(18.0); } // set the minimum zoom level map.setMinZoomLevel(15.5); mapController.setCenter(startPoint); + // Location Overlay + // from OpenBikeSharing (THANK GOD) + GpsMyLocationProvider imlp = new GpsMyLocationProvider(this.getBaseContext()); + imlp.setLocationUpdateMinDistance(200); + imlp.setLocationUpdateMinTime(30000); + this.mLocationOverlay = new MyLocationNewOverlay(imlp,map); + this.mLocationOverlay.enableMyLocation(); + map.getOverlays().add(this.mLocationOverlay); + + //add stops overlay + map.getOverlays().add(this.stopsFolderOverlay); + + loadMarkers(); if (marker != null) { // make a marker with the info window open for the searched marker makeMarker(startPoint, name , ID, true); } } - public void makeMarker(GeoPoint geoPoint, String stopName, String ID, boolean isStartMarker) { + public Marker makeMarker(GeoPoint geoPoint, String stopName, String ID, boolean isStartMarker) { // add a marker Marker marker = new Marker(map); // set custom info window as info window CustomInfoWindow popup = new CustomInfoWindow(map, ID, stopName); marker.setInfoWindow(popup); // make the marker clickable marker.setOnMarkerClickListener((thisMarker, mapView) -> { if (thisMarker.isInfoWindowOpen()) { // on second click // create an intent with these extras Intent intent = new Intent(ActivityMap.this, ActivityMain.class); Bundle b = new Bundle(); b.putString("bus-stop-ID", ID); b.putString("bus-stop-display-name", stopName); intent.putExtras(b); intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); // start ActivityMain with the previous intent startActivity(intent); } else { // on first click // hide all opened info window InfoWindow.closeAllInfoWindowsOn(map); // show this particular info window thisMarker.showInfoWindow(); // move the map to its position map.getController().animateTo(thisMarker.getPosition()); } return true; }); // set its position marker.setPosition(geoPoint); marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM); - // display the marker - map.getOverlays().add(marker); // add to it an icon marker.setIcon(getResources().getDrawable(R.drawable.bus_marker)); // add to it a title marker.setTitle(stopName); // set the description as the ID marker.setSnippet(ID); // show popup info window of the searched marker if (isStartMarker) { marker.showInfoWindow(); } + return marker; } public void loadMarkers() { // get rid of the previous markers - map.getOverlays().clear(); + //map.getOverlays().clear(); + //stopsFolderOverlay = new FolderOverlay(); + List stopsOverlays = stopsFolderOverlay.getItems(); + if (stopsOverlays != null){ + stopsOverlays.clear(); + } // get the top, bottom, left and right screen's coordinate BoundingBox bb = map.getBoundingBox(); double latFrom = bb.getLatSouth(); double latTo = bb.getLatNorth(); double lngFrom = bb.getLonWest(); double lngTo = bb.getLonEast(); // get the stops located in those coordinates StopsDB stopsDB = new StopsDB(ctx); stopsDB.openIfNeeded(); Stop[] stops = stopsDB.queryAllInsideMapView(latFrom, latTo, lngFrom, lngTo); stopsDB.closeIfNeeded(); // add new markers of those stops for (Stop stop : stops) { GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude()); - makeMarker(marker, stop.getStopDefaultName(), stop.ID, false); + Marker stopMarker = makeMarker(marker, stop.getStopDefaultName(), stop.ID, false); + stopsFolderOverlay.add(stopMarker); } } public void onResume(){ super.onResume(); //this will refresh the osmdroid configuration on resuming. //if you make changes to the configuration, use //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); //Configuration.getInstance().load(this, PreferenceManager.getDefaultSharedPreferences(this)); map.onResume(); //needed for compass, my location overlays, v6.0.0 and up + mLocationOverlay.enableMyLocation(); } public void onPause(){ super.onPause(); //this will refresh the osmdroid configuration on resuming. //if you make changes to the configuration, use //SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); //Configuration.getInstance().save(this, prefs); map.onPause(); //needed for compass, my location overlays, v6.0.0 and up + mLocationOverlay.disableMyLocation(); } + } \ No newline at end of file diff --git a/src/it/reyboz/bustorino/middleware/AppLocationManager.java b/src/it/reyboz/bustorino/middleware/AppLocationManager.java index 7e89155..a9c42d3 100644 --- a/src/it/reyboz/bustorino/middleware/AppLocationManager.java +++ b/src/it/reyboz/bustorino/middleware/AppLocationManager.java @@ -1,219 +1,219 @@ /* BusTO (middleware) Copyright (C) 2019 Fabio Mazza This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package it.reyboz.bustorino.middleware; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import it.reyboz.bustorino.util.LocationCriteria; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.ListIterator; /** * Singleton class used to access location. Possibly extended with other location sources. */ public class AppLocationManager implements LocationListener { public static final int LOCATION_GPS_AVAILABLE = 22; public static final int LOCATION_UNAVAILABLE = -22; private Context con; private LocationManager locMan; public static final String DEBUG_TAG = "BUSTO LocAdapter"; private final String BUNDLE_LOCATION = "location"; private static AppLocationManager instance; private int oldGPSLocStatus = LOCATION_UNAVAILABLE; private int minimum_time_milli = -1; private ArrayList> requestersRef = new ArrayList<>(); private AppLocationManager(Context con) { - this.con = con; + this.con = con.getApplicationContext(); locMan = (LocationManager) con.getSystemService(Context.LOCATION_SERVICE); } public static AppLocationManager getInstance(Context con) { if(instance==null) instance = new AppLocationManager(con.getApplicationContext()); return instance; } private void requestGPSPositionUpdates(){ final int timeinterval = (minimum_time_milli>0 && minimum_time_milli> iter = requestersRef.listIterator(); while(iter.hasNext()){ final LocationRequester cReq = iter.next().get(); if(cReq==null) iter.remove(); else { minimum_time_milli = Math.min(cReq.getLocationCriteria().getTimeInterval(),minimum_time_milli); } } Log.d(DEBUG_TAG,"Updated requesters, got "+requestersRef.size()+" listeners to update every "+minimum_time_milli+" ms at least"); } public void addLocationRequestFor(LocationRequester req){ boolean present = false; minimum_time_milli = Integer.MAX_VALUE; ListIterator> iter = requestersRef.listIterator(); while(iter.hasNext()){ final LocationRequester cReq = iter.next().get(); if(cReq==null) iter.remove(); else if(cReq.equals(req)){ present = true; minimum_time_milli = Math.min(cReq.getLocationCriteria().getTimeInterval(),minimum_time_milli); } } if(!present) { WeakReference newref = new WeakReference<>(req); requestersRef.add(newref); minimum_time_milli = Math.min(req.getLocationCriteria().getTimeInterval(),minimum_time_milli); Log.d(DEBUG_TAG,"Added new stop requester, instance of "+req.getClass().getSimpleName()); } if(requestersRef.size()>0){ requestGPSPositionUpdates(); } } public void removeLocationRequestFor(LocationRequester req){ minimum_time_milli = Integer.MAX_VALUE; ListIterator> iter = requestersRef.listIterator(); while(iter.hasNext()){ final LocationRequester cReq = iter.next().get(); if(cReq==null || cReq.equals(req)) iter.remove(); else { minimum_time_milli = Math.min(cReq.getLocationCriteria().getTimeInterval(),minimum_time_milli); } } if(requestersRef.size()<=0){ locMan.removeUpdates(this); } else { requestGPSPositionUpdates(); } } private void sendLocationStatusToAll(int status){ ListIterator> iter = requestersRef.listIterator(); while(iter.hasNext()){ final LocationRequester cReq = iter.next().get(); if(cReq==null) iter.remove(); else cReq.onLocationStatusChanged(status); } } @Override public void onLocationChanged(Location location) { Log.d("GPSLocationListener","found location:\nlat: "+location.getLatitude()+" lon: "+location.getLongitude()+"\naccuracy: "+location.getAccuracy()); ListIterator> iter = requestersRef.listIterator(); int new_min_interval = Integer.MAX_VALUE; while(iter.hasNext()){ final LocationRequester requester = iter.next().get(); if(requester==null) iter.remove(); else{ final long timeNow = System.currentTimeMillis(); final LocationCriteria criteria = requester.getLocationCriteria(); if(location.getAccuracy()criteria.getTimeInterval()){ requester.onLocationChanged(location); Log.d("AppLocationManager","Updating position for instance of requester "+requester.getClass().getSimpleName()); } //update minimum time interval new_min_interval = Math.min(requester.getLocationCriteria().getTimeInterval(),new_min_interval); } } minimum_time_milli = new_min_interval; if(requestersRef.size()==0){ //stop requesting the position locMan.removeUpdates(this); } } @Override public void onStatusChanged(String provider, int status, Bundle extras) { //IF ANOTHER LOCATION SOURCE IS READY, USE IT //OTHERWISE, SIGNAL THAT WE HAVE NO LOCATION if(oldGPSLocStatus !=status){ if(status == LocationProvider.OUT_OF_SERVICE || status == LocationProvider.TEMPORARILY_UNAVAILABLE) { sendLocationStatusToAll(LOCATION_UNAVAILABLE); }else if(status == LocationProvider.AVAILABLE){ sendLocationStatusToAll(LOCATION_GPS_AVAILABLE); } oldGPSLocStatus = status; } } @Override public void onProviderEnabled(String provider) { requestGPSPositionUpdates(); } @Override public void onProviderDisabled(String provider) { locMan.removeUpdates(this); } /** * Interface to be implemented to get the location request */ public interface LocationRequester{ /** * Do something with the newly obtained location * @param loc the obtained location */ void onLocationChanged(Location loc); /** * Inform the requester that the GPS status has changed * @param status new status */ void onLocationStatusChanged(int status); /** * Give the last time of update the requester has * Set it to -1 in order to receive each new location * @return the time for update in milliseconds since epoch */ long getLastUpdateTimeMillis(); /** * Get the specifications for the location * @return fully parsed LocationCriteria */ LocationCriteria getLocationCriteria(); } }