Page Menu
Home
GitPull.it
Search
Configure Global Search
Log In
Files
F13286735
D185.1777871841.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
101 KB
Referenced Files
None
Subscribers
None
D185.1777871841.diff
View Options
diff --git a/app/build.gradle b/app/build.gradle
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -97,8 +97,6 @@
implementation 'org.jsoup:jsoup:1.15.3'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
implementation 'com.android.volley:volley:1.2.1'
-
- implementation 'org.osmdroid:osmdroid-android:6.1.18'
//maplibre
implementation 'org.maplibre.gl:android-sdk:11.8.6'
implementation 'org.maplibre.gl:android-sdk-turf:6.0.1'
diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
--- a/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityPrincipal.java
@@ -718,8 +718,8 @@
void createAndShowMapFragment(@Nullable Stop stop, boolean addToBackStack){
final FragmentManager fm = getSupportFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
- final MapLibreFragment fragment = MapLibreFragment.Companion.newInstance(stop);
- ft.replace(R.id.mainActContentFrame, fragment, MapFragmentKt.FRAGMENT_TAG);
+ final MapLibreFragment fragment = MapLibreFragment.newInstance(stop);
+ ft.replace(R.id.mainActContentFrame, fragment, MapLibreFragment.FRAGMENT_TAG);
if (addToBackStack) ft.addToBackStack(null);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java b/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/GPSPoint.java
@@ -1,8 +1,7 @@
package it.reyboz.bustorino.backend;
-import org.osmdroid.api.IGeoPoint;
-public class GPSPoint implements IGeoPoint {
+public class GPSPoint {
public final double latitude;
public final double longitude;
@@ -12,22 +11,18 @@
this.longitude = longitude;
}
- @Override
public int getLatitudeE6() {
return (int) (latitude*1e6d);
}
- @Override
public int getLongitudeE6() {
return (int) (longitude*1e6d);
}
- @Override
public double getLatitude() {
return latitude;
}
- @Override
public double getLongitude() {
return longitude;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java b/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/RealtimeVehicle.java
@@ -1,24 +1,23 @@
package it.reyboz.bustorino.backend;
-import org.osmdroid.util.GeoPoint;
public class RealtimeVehicle {
- private final GeoPoint location;
+ private final GPSPoint location;
private final float bearing;
public final int updateTimestamp;
private final String vehicleLabel;
private String routeID;
- public RealtimeVehicle(GeoPoint location, float bearing, int updateTimestamp, String vehicleLabel) {
+ public RealtimeVehicle(GPSPoint location, float bearing, int updateTimestamp, String vehicleLabel) {
this.location = location;
this.bearing = bearing;
this.updateTimestamp = updateTimestamp;
this.vehicleLabel = vehicleLabel;
}
- public GeoPoint getLocation() {
+ public GPSPoint getLocation() {
return location;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Stop.java b/app/src/main/java/it/reyboz/bustorino/backend/Stop.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/Stop.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/Stop.java
@@ -24,7 +24,7 @@
import androidx.annotation.Nullable;
import it.reyboz.bustorino.util.LinesNameSorter;
-import org.osmdroid.api.IGeoPoint;
+import org.jetbrains.annotations.NotNull;
import java.net.URLEncoder;
import java.util.ArrayList;
@@ -313,7 +313,7 @@
}
- public Double getDistanceFromLocation(IGeoPoint loc){
+ public Double getDistanceFromLocation(@NotNull GPSPoint loc){
return getDistanceFromLocation(loc.getLatitude(), loc.getLongitude());
}
public Double getDistanceFromLocation(double latitude, double longitude){
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/utils.java b/app/src/main/java/it/reyboz/bustorino/backend/utils.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/utils.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/utils.java
@@ -50,6 +50,8 @@
public static Double measuredistanceBetween(double lat1,double long1,double lat2,double long2){
+
+
final double phi1 = Math.toRadians(lat1);
final double phi2 = Math.toRadians(lat2);
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt
@@ -514,7 +514,7 @@
this.map = mapReady
val context = requireContext()
- val mjson = Styles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context)) //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
+ val mjson = MapLibreStyles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context)) //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
activity?.run {
val builder = Style.Builder().fromJson(mjson!!)
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragment.java
+++ /dev/null
@@ -1,837 +0,0 @@
-/*
- BusTO - Fragments components
- Copyright (C) 2020 Andrea Ugo
- Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
- */
-package it.reyboz.bustorino.fragments;
-
-import android.Manifest;
-import android.animation.ObjectAnimator;
-import android.annotation.SuppressLint;
-import android.content.Context;
-
-import android.graphics.drawable.Drawable;
-import android.location.Location;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.Toast;
-
-import androidx.activity.result.ActivityResultLauncher;
-import androidx.activity.result.contract.ActivityResultContracts;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
-import androidx.core.content.res.ResourcesCompat;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.preference.PreferenceManager;
-
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate;
-import it.reyboz.bustorino.backend.mato.MQTTMatoClient;
-import it.reyboz.bustorino.backend.utils;
-import it.reyboz.bustorino.data.gtfs.MatoPattern;
-import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops;
-import it.reyboz.bustorino.map.*;
-import it.reyboz.bustorino.viewmodels.LivePositionsViewModel;
-import it.reyboz.bustorino.viewmodels.StopsMapViewModel;
-import org.osmdroid.api.IGeoPoint;
-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.infowindow.InfoWindow;
-import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
-
-import java.util.*;
-
-import kotlin.Pair;
-import it.reyboz.bustorino.R;
-import it.reyboz.bustorino.backend.Stop;
-import it.reyboz.bustorino.middleware.GeneralActivity;
-import it.reyboz.bustorino.util.Permissions;
-
-import static it.reyboz.bustorino.fragments.SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE;
-
-public class MapFragment extends ScreenBaseFragment {
-
- //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 final String FOLLOWING_LOCAT_KEY ="following";
-
- public static final String BUNDLE_LATIT = "lat";
- public static final String BUNDLE_LONGIT = "lon";
- public static final String BUNDLE_NAME = "name";
- public static final String BUNDLE_ID = "ID";
- public static final String BUNDLE_ROUTES_STOPPING = "routesStopping";
-
- public static final String FRAGMENT_TAG="BusTOMapFragment";
-
-
- private static final double DEFAULT_CENTER_LAT = 45.0708;
- private static final double DEFAULT_CENTER_LON = 7.6858;
- private static final double POSITION_FOUND_ZOOM = 18.3;
- public static final double NO_POSITION_ZOOM = 17.1;
-
- private static final String DEBUG_TAG=FRAGMENT_TAG;
-
- protected FragmentListenerMain listenerMain;
-
- private HashSet<String> shownStops = null;
-
-
- private MapView map = null;
- public Context ctx;
- private LocationOverlay mLocationOverlay = null;
- private FolderOverlay stopsFolderOverlay = null;
- private Bundle savedMapState = null;
- protected ImageButton btCenterMap;
- protected ImageButton btFollowMe;
-
- protected CoordinatorLayout coordLayout;
- private boolean hasMapStartFinished = false;
- private boolean followingLocation = false;
-
- //the ViewModel from which we get the stop to display in the map
- private StopsMapViewModel stopsViewModel;
-
- //private GtfsPositionsViewModel gtfsPosViewModel; //= new ViewModelProvider(this).get(MapViewModel.class);
- private LivePositionsViewModel livePositionsViewModel;
- private Boolean useMQTTViewModel = true;
-
- private final HashMap<String,Marker> busPositionMarkersByTrip = new HashMap<>();
- private FolderOverlay busPositionsOverlay = null;
-
- private final HashMap<String, ObjectAnimator> tripMarkersAnimators = new HashMap<>();
-
- protected final CustomInfoWindow.TouchResponder responder = new CustomInfoWindow.TouchResponder() {
- @Override
- public void onActionUp(@NonNull String stopID, @Nullable String stopName) {
- if (listenerMain!= null){
- Log.d(DEBUG_TAG, "Asked to show arrivals for stop ID: "+stopID);
- listenerMain.requestArrivalsForStopID(stopID);
- }
- }
- };
- protected final LocationOverlay.OverlayCallbacks locationCallbacks = new LocationOverlay.OverlayCallbacks() {
- @Override
- public void onDisableFollowMyLocation() {
- updateGUIForLocationFollowing(false);
- followingLocation=false;
- }
-
- @Override
- public void onEnableFollowMyLocation() {
- updateGUIForLocationFollowing(true);
- followingLocation=true;
- }
- };
-
- private final ActivityResultLauncher<String[]> positionRequestLauncher =
- registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), result -> {
- if (result == null){
- Log.w(DEBUG_TAG, "Got asked permission but request is null, doing nothing?");
- }
- else if(Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_COARSE_LOCATION)) &&
- Boolean.TRUE.equals(result.get(Manifest.permission.ACCESS_FINE_LOCATION))){
-
- map.getOverlays().remove(mLocationOverlay);
- startLocationOverlay(true, map);
- if(getContext()==null || getContext().getSystemService(Context.LOCATION_SERVICE)==null)
- return;
- LocationManager locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
- @SuppressLint("MissingPermission")
- Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
- if (userLocation != null) {
- map.getController().setZoom(POSITION_FOUND_ZOOM);
- GeoPoint startPoint = new GeoPoint(userLocation);
- setLocationFollowing(true);
- map.getController().setCenter(startPoint);
- }
- }
- else Log.w(DEBUG_TAG,"No location permission");
- });
-
- public MapFragment() {
- }
- public static MapFragment getInstance(){
- return new MapFragment();
- }
- public static MapFragment getInstance(@NonNull Stop stop){
- MapFragment fragment= new MapFragment();
- Bundle args = new Bundle();
- args.putDouble(BUNDLE_LATIT, stop.getLatitude());
- args.putDouble(BUNDLE_LONGIT, stop.getLongitude());
- args.putString(BUNDLE_NAME, stop.getStopDisplayName());
- args.putString(BUNDLE_ID, stop.ID);
- args.putString(BUNDLE_ROUTES_STOPPING, stop.routesThatStopHereToString());
- fragment.setArguments(args);
-
- return fragment;
- }
- //public static MapFragment getInstance(@NonNull Stop stop){
- // return getInstance(stop.getLatitude(), stop.getLongitude(), stop.getStopDisplayName(), stop.ID);
- //}
-
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
- //use the same layout as the activity
- View root = inflater.inflate(R.layout.fragment_map, container, false);
- if (getContext() == null){
- throw new IllegalStateException();
- }
- ctx = getContext().getApplicationContext();
- Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
- map = root.findViewById(R.id.map);
- map.setTileSource(TileSourceFactory.MAPNIK);
- //map.setTilesScaledToDpi(true);
- map.setFlingEnabled(true);
-
- // add ability to zoom with 2 fingers
- map.setMultiTouchControls(true);
-
- btCenterMap = root.findViewById(R.id.centerMapImageButton);
- btFollowMe = root.findViewById(R.id.followUserImageButton);
- coordLayout = root.findViewById(R.id.coord_layout);
-
- //setup FolderOverlay
- stopsFolderOverlay = new FolderOverlay();
- //setup Bus Markers Overlay
- busPositionsOverlay = new FolderOverlay();
- //reset shown bus updates
- busPositionMarkersByTrip.clear();
- tripMarkersAnimators.clear();
- //set map not done
- hasMapStartFinished = false;
-
- String keySourcePositions=getString(R.string.pref_positions_source);
- useMQTTViewModel = (
- PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions,LIVE_POSITIONS_PREF_MQTT_VALUE).contentEquals(LIVE_POSITIONS_PREF_MQTT_VALUE));
-
-
- //Start map from bundle
- if (savedInstanceState !=null)
- startMap(getArguments(), savedInstanceState);
- else startMap(getArguments(), savedMapState);
- //set listeners
- map.addMapListener(new DelayedMapListener(new MapListener() {
-
- @Override
- public boolean onScroll(ScrollEvent paramScrollEvent) {
- requestStopsToShow();
- //Log.d(DEBUG_TAG, "Scrolling");
- //if (moveTriggeredByCode) moveTriggeredByCode =false;
- //else setLocationFollowing(false);
- return true;
- }
-
- @Override
- public boolean onZoom(ZoomEvent event) {
- requestStopsToShow();
- return true;
- }
-
- }));
-
-
- btCenterMap.setOnClickListener(v -> {
- //Log.i(TAG, "centerMap clicked ");
- if(Permissions.bothLocationPermissionsGranted(getContext())) {
- final GeoPoint myPosition = mLocationOverlay.getMyLocation();
- map.getController().animateTo(myPosition);
- } else
- Toast.makeText(getContext(), R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show();
- });
-
- btFollowMe.setOnClickListener(v -> {
- //Log.i(TAG, "btFollowMe clicked ");
- if(Permissions.bothLocationPermissionsGranted(getContext()))
- setLocationFollowing(!followingLocation);
- else
- Toast.makeText(getContext(), R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show();
- });
-
-
- return root;
- }
-
- @Override
- public void onAttach(@NonNull Context context) {
- super.onAttach(context);
-
- ViewModelProvider provider = new ViewModelProvider(this);
- //gtfsPosViewModel = provider.get(GtfsPositionsViewModel.class);
- livePositionsViewModel = provider.get(LivePositionsViewModel.class);
- stopsViewModel = provider.get(StopsMapViewModel.class);
-
-
- if (context instanceof FragmentListenerMain) {
- listenerMain = (FragmentListenerMain) context;
- } else {
- throw new RuntimeException(context.toString()
- + " must implement FragmentListenerMain");
- }
- }
- @Override
- public void onDetach() {
- super.onDetach();
- listenerMain = null;
- //stop animations
-
- // setupOnAttached = true;
- Log.w(DEBUG_TAG, "Fragment detached");
- }
-
- @Override
- public void onPause() {
- super.onPause();
- Log.w(DEBUG_TAG, "On pause called mapfrag");
- saveMapState();
- for (ObjectAnimator animator : tripMarkersAnimators.values()) {
- if(animator!=null && animator.isRunning()){
- animator.cancel();
- }
- }
- tripMarkersAnimators.clear();
- if(useMQTTViewModel) livePositionsViewModel.stopMatoUpdates();
-
- }
-
- /**
- * Save the map state inside the fragment
- * (calls saveMapState(bundle))
- */
- private void saveMapState(){
- savedMapState = new Bundle();
- saveMapState(savedMapState);
- }
-
- /**
- * Save the state of the map to restore it to a later time
- * @param bundle the bundle in which to save the data
- */
- private void saveMapState(Bundle bundle){
- Log.d(DEBUG_TAG, "Saving state, location following: "+followingLocation);
- bundle.putBoolean(FOLLOWING_LOCAT_KEY, followingLocation);
- if (map == null){
- //The map is null, it can happen?
- Log.e(DEBUG_TAG, "Cannot save map center, map is null");
- return;
- }
- final IGeoPoint loc = map.getMapCenter();
- bundle.putDouble(MAP_CENTER_LAT_KEY, loc.getLatitude());
- bundle.putDouble(MAP_CENTER_LON_KEY, loc.getLongitude());
- bundle.putDouble(MAP_CURRENT_ZOOM_KEY, map.getZoomLevelDouble());
- }
-
-
- @Override
- public void onResume() {
- super.onResume();
- //TODO: cleanup duplicate code (maybe merging the positions classes?)
- if(listenerMain!=null) listenerMain.readyGUIfor(FragmentKind.MAP);
- /// choose which to use
- String keySourcePositions=getString(R.string.pref_positions_source);
- useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions,LIVE_POSITIONS_PREF_MQTT_VALUE).contentEquals(
- LIVE_POSITIONS_PREF_MQTT_VALUE);
- if(livePositionsViewModel !=null) {
- //gtfsPosViewModel.requestUpdates();
- if(useMQTTViewModel)
- livePositionsViewModel.requestMatoPosUpdates(MQTTMatoClient.LINES_ALL);
- else
- livePositionsViewModel.requestGTFSUpdates();
- //mapViewModel.testCascade();
- livePositionsViewModel.isLastWorkResultGood().observe(this, d ->
- Log.d(DEBUG_TAG, "Last trip download result is "+d));
- livePositionsViewModel.getTripsGtfsIDsToQuery().observe(this, dat -> {
- Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: "+dat);
- livePositionsViewModel.downloadTripsFromMato(dat);
- /*MatoTripsDownloadWorker.Companion.requestMatoTripsDownload(dat,
- requireContext().getApplicationContext(),
- "BusTO-MatoTripDownload");
-
- */
- });
- } /*else if(gtfsPosViewModel!=null){
- gtfsPosViewModel.requestUpdates();
- gtfsPosViewModel.getTripsGtfsIDsToQuery().observe(this, dat -> {
- Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: "+dat);
- //gtfsPosViewModel.downloadTripsFromMato(dat);
- MatoTripsDownloadWorker.Companion.downloadTripsFromMato(dat,getContext().getApplicationContext(),
- "BusTO-MatoTripDownload");
- });
- }
- */
- else Log.e(DEBUG_TAG, "livePositionsViewModel is null at onResume");
-
- //rerequest stop
- stopsViewModel.requestStopsInBoundingBox(map.getBoundingBox());
- }
-
- private void startRequestsPositions(){
- if (livePositionsViewModel != null) {
- //should always be the case
- livePositionsViewModel.getUpdatesWithTripAndPatterns().observe(getViewLifecycleOwner(), data -> {
- Log.d(DEBUG_TAG, "Have " + data.size() + " trip updates, has Map start finished: " + hasMapStartFinished);
- if (hasMapStartFinished) updateBusPositionsInMap(data);
-
- if(!isDetached() && !useMQTTViewModel)
- livePositionsViewModel.requestDelayedGTFSUpdates(3000);
-
- });
-
- } else {
- Log.e(DEBUG_TAG, "PositionsViewModel is null");
- }
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- saveMapState(outState);
-
- super.onSaveInstanceState(outState);
- }
-
- //own methods
-
- /**
- * Switch following the location on and off
- * @param value true if we want to follow location
- */
- public void setLocationFollowing(Boolean value){
- followingLocation = value;
- if(mLocationOverlay==null || getContext() == null || map ==null)
- //nothing else to do
- return;
- if (value){
- mLocationOverlay.enableFollowLocation();
- } else {
- mLocationOverlay.disableFollowLocation();
- }
- }
-
- /**
- * Do all the stuff you need to do on the gui, when parameter is changed to value
- * @param following value
- */
- protected void updateGUIForLocationFollowing(boolean following){
- if (following)
- btFollowMe.setImageResource(R.drawable.ic_follow_me_on);
- else
- btFollowMe.setImageResource(R.drawable.ic_follow_me);
-
- }
-
- /**
- * Build the location overlay. Enable only when
- * a) we know we have the permission
- * b) the location map is set
- */
- private void startLocationOverlay(boolean enableLocation, MapView map){
- if(getActivity()== null) throw new IllegalStateException("Cannot enable LocationOverlay now");
- // Location Overlay
- // from OpenBikeSharing (THANK GOD)
- Log.d(DEBUG_TAG, "Starting position overlay");
- GpsMyLocationProvider imlp = new GpsMyLocationProvider(getActivity().getBaseContext());
- imlp.setLocationUpdateMinDistance(5);
- imlp.setLocationUpdateMinTime(2000);
-
- final LocationOverlay overlay = new LocationOverlay(imlp,map, locationCallbacks);
- if (enableLocation) overlay.enableMyLocation();
- overlay.setOptionsMenuEnabled(true);
-
- //map.getOverlays().add(this.mLocationOverlay);
- this.mLocationOverlay = overlay;
- map.getOverlays().add(mLocationOverlay);
- }
-
- public void startMap(Bundle incoming, Bundle savedInstanceState) {
- //Check that we're attached
- GeneralActivity activity = getActivity() instanceof GeneralActivity ? (GeneralActivity) getActivity() : null;
- if(getContext()==null|| activity==null){
- //we are not attached
- Log.e(DEBUG_TAG, "Calling startMap when not attached");
- return;
- }else{
- Log.d(DEBUG_TAG, "Starting map from scratch");
- }
- //clear previous overlays
- map.getOverlays().clear();
-
-
- //parse incoming bundle
- GeoPoint marker = null;
- String name = null;
- String ID = null;
- String routesStopping = "";
- if (incoming != null) {
- double lat = incoming.getDouble(BUNDLE_LATIT);
- double lon = incoming.getDouble(BUNDLE_LONGIT);
- marker = new GeoPoint(lat, lon);
- name = incoming.getString(BUNDLE_NAME);
- ID = incoming.getString(BUNDLE_ID);
- routesStopping = incoming.getString(BUNDLE_ROUTES_STOPPING, "");
- }
-
-
- //ask for location permission
- if(!Permissions.bothLocationPermissionsGranted(activity)){
- if(shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){
- //TODO: show dialog for permission rationale
- Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT).show();
- }
- positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS);
-
- }
-
- shownStops = new HashSet<>();
- // 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 = null;
- startLocationOverlay(Permissions.bothLocationPermissionsGranted(activity),
- map);
- // set the center point
- if (marker != null) {
- //startPoint = marker;
- mapController.setZoom(POSITION_FOUND_ZOOM);
- setLocationFollowing(false);
- // put the center a little bit off (animate later)
- startPoint = new GeoPoint(marker);
- startPoint.setLatitude(marker.getLatitude()+ utils.angleRawDifferenceFromMeters(20));
- startPoint.setLongitude(marker.getLongitude()-utils.angleRawDifferenceFromMeters(20));
- //don't need to do all the rest since we want to show a point
- } else if (savedInstanceState != null && savedInstanceState.containsKey(MAP_CURRENT_ZOOM_KEY)) {
- mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY));
- mapController.setCenter(new GeoPoint(savedInstanceState.getDouble(MAP_CENTER_LAT_KEY),
- savedInstanceState.getDouble(MAP_CENTER_LON_KEY)));
- Log.d(DEBUG_TAG, "Location following from savedInstanceState: "+savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY));
- setLocationFollowing(savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY));
- } else {
- Log.d(DEBUG_TAG, "No position found from intent or saved state");
- boolean found = false;
- LocationManager locationManager =
- (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
- //check for permission
- if (locationManager != null && Permissions.bothLocationPermissionsGranted(activity)) {
-
- @SuppressLint("MissingPermission")
- Location userLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
-
- if (userLocation != null) {
- double distan = utils.measuredistanceBetween(userLocation.getLatitude(), userLocation.getLongitude(),
- DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON);
- if (distan < 100_000.0) {
- mapController.setZoom(POSITION_FOUND_ZOOM);
- startPoint = new GeoPoint(userLocation);
- found = true;
- setLocationFollowing(true);
- }
- }
- }
- if(!found){
- startPoint = new GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON);
- mapController.setZoom(NO_POSITION_ZOOM);
- setLocationFollowing(false);
- }
- }
-
- // set the minimum zoom level
- map.setMinZoomLevel(15.0);
- //add contingency check (shouldn't happen..., but)
-
- if (startPoint != null) {
- mapController.setCenter(startPoint);
- }
-
-
- //add stops overlay
- //map.getOverlays().add(mLocationOverlay);
- map.getOverlays().add(this.stopsFolderOverlay);
-
- Log.d(DEBUG_TAG, "Requesting stops load");
- // This is not necessary, by setting the center we already move
- // the map and we trigger a stop request
- //requestStopsToShow();
- if (marker != null) {
- // make a marker with the info window open for the searched marker
- //TODO: make Stop Bundle-able
- Marker stopMarker = makeMarker(marker, ID , name, routesStopping,true);
- map.getController().animateTo(marker);
- }
- //add the overlays with the bus stops
- if(busPositionsOverlay == null){
- //Log.i(DEBUG_TAG, "Null bus positions overlay,redo");
- busPositionsOverlay = new FolderOverlay();
- }
- startRequestsPositions();
- if(stopsViewModel !=null){
-
- stopsViewModel.getStopsInBoundingBox().observe(getViewLifecycleOwner(),
- this::showStopsMarkers
- );
- } else Log.d(DEBUG_TAG, "Cannot observe new stops in map, stopsViewModel is null");
- map.getOverlays().add(this.busPositionsOverlay);
- //set map as started
- hasMapStartFinished = true;
- }
-
- /**
- * Start a request to load the stops that are in the current view
- * from the database
- */
- private void requestStopsToShow(){
- // get the top, bottom, left and right screen's coordinate
- BoundingBox bb = map.getBoundingBox();
- Log.d(DEBUG_TAG, "Requesting stops in bounding box, stopViewModel is null "+(stopsViewModel==null));
- if(stopsViewModel!=null){
- stopsViewModel.requestStopsInBoundingBox(bb);
- }
- /*double latFrom = bb.getLatSouth();
- double latTo = bb.getLatNorth();
- double lngFrom = bb.getLonWest();
- double lngTo = bb.getLonEast();
- if (stopFetcher!= null && stopFetcher.getStatus()!= AsyncTask.Status.FINISHED)
- stopFetcher.cancel(true);
- stopFetcher = new AsyncStopFetcher(this);
- stopFetcher.execute(
- new AsyncStopFetcher.BoundingBoxLimit(lngFrom,lngTo,latFrom, latTo));
-
- */
- }
-
- private void updateBusMarker(final Marker marker, final LivePositionUpdate posUpdate, @Nullable boolean justCreated){
- GeoPoint position;
- final String updateID = posUpdate.getTripID();
- if(!justCreated){
- position = marker.getPosition();
- if(posUpdate.getLatitude()!=position.getLatitude() || posUpdate.getLongitude()!=position.getLongitude()){
- GeoPoint newpos = new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude());
- ObjectAnimator valueAnimator = MarkerUtils.makeMarkerAnimator(
- map, marker, newpos, MarkerUtils.LINEAR_ANIMATION, 1200);
- valueAnimator.setAutoCancel(true);
- tripMarkersAnimators.put(updateID,valueAnimator);
- valueAnimator.start();
- }
- //marker.setPosition(new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude()));
- } else {
-
- position = new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude());
- marker.setPosition(position);
- }
-
- if(posUpdate.getBearing()!=null)
- marker.setRotation(posUpdate.getBearing()*(-1.f));
- }
-
- private void updateBusPositionsInMap(HashMap<String, Pair<LivePositionUpdate, TripAndPatternWithStops>> tripsPatterns){
- Log.d(DEBUG_TAG, "Updating positions of the buses");
- //if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- final ArrayList<String> noPatternsTrips = new ArrayList<>();
- for(String tripID: tripsPatterns.keySet()) {
- final Pair<LivePositionUpdate, TripAndPatternWithStops> pair = tripsPatterns.get(tripID);
- if (pair == null) continue;
- final LivePositionUpdate update = pair.getFirst();
- final TripAndPatternWithStops tripWithPatternStops = pair.getSecond();
-
-
- //check if Marker is already created
- if (busPositionMarkersByTrip.containsKey(tripID)){
- //need to change the position of the marker
- final Marker marker = busPositionMarkersByTrip.get(tripID);
- assert marker!=null;
- updateBusMarker(marker, update, false);
- if(marker.getInfoWindow()!=null && marker.getInfoWindow() instanceof BusInfoWindow){
- BusInfoWindow window = (BusInfoWindow) marker.getInfoWindow();
- if(tripWithPatternStops != null) {
- //Log.d(DEBUG_TAG, "Update pattern for trip: "+tripID);
- window.setPatternAndDraw(tripWithPatternStops.getPattern());
- }
-
- }
- } else{
- //marker is not there, need to make it
- if(map==null) Log.e(DEBUG_TAG, "Creating marker with null map, things will explode");
- final Marker marker = new Marker(map);
-
- /*final Drawable mDrawable = DrawableUtils.Companion.getScaledDrawableResources(
- getResources(),
- R.drawable.point_heading_icon,
- R.dimen.map_icons_size, R.dimen.map_icons_size);
-
- */
- //String route = GtfsUtils.getLineNameFromGtfsID(update.getRouteID());
- final Drawable mdraw = ResourcesCompat.getDrawable(getResources(),R.drawable.map_bus_position_icon, null);
- /*final Drawable mdraw = DrawableUtils.Companion.writeOnDrawable(getResources(),
- R.drawable.point_heading_icon,
- R.color.white,
- route,12);
-
- */
- assert mdraw != null;
- //mdraw.setBounds(0,0,28,28);
- marker.setIcon(mdraw);
- if(tripWithPatternStops == null){
- noPatternsTrips.add(tripID);
- }
- MatoPattern markerPattern = null;
- if(tripWithPatternStops != null && tripWithPatternStops.getPattern()!=null)
- markerPattern = tripWithPatternStops.getPattern();
- marker.setInfoWindow(new BusInfoWindow(map, update, markerPattern , false, (pattern) -> { }));
- marker.setInfoWindowAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER);
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER);
-
- updateBusMarker(marker, update, true);
- // the overlay is null when it's not attached yet?5
- // cannot recreate it because it becomes null very soon
- // if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- //save the marker
- if(busPositionsOverlay!=null) {
- busPositionsOverlay.add(marker);
- busPositionMarkersByTrip.put(tripID, marker);
- }
-
- }
- }
- if(noPatternsTrips.size()>0){
- Log.i(DEBUG_TAG, "These trips have no matching pattern: "+noPatternsTrips);
- }
- }
-
- /**
- * Add stops as Markers on the map
- * @param stops the list of stops that must be included
- */
- protected void showStopsMarkers(List<Stop> stops){
- if (getContext() == null || stops == null){
- //we are not attached
- return;
- }
- boolean good = true;
-
- for (Stop stop : stops) {
- if (shownStops.contains(stop.ID)){
- continue;
- }
- if(stop.getLongitude()==null || stop.getLatitude()==null)
- continue;
-
- shownStops.add(stop.ID);
- if(!map.isShown()){
- if(good)
- Log.d(DEBUG_TAG, "Need to show stop but map is not shown, probably detached already");
- good = false;
- continue;
- } else if(map.getRepository() == null){
- Log.e(DEBUG_TAG, "Map view repository is null");
- }
- GeoPoint marker = new GeoPoint(stop.getLatitude(), stop.getLongitude());
-
- Marker stopMarker = makeMarker(marker, stop, false);
- stopsFolderOverlay.add(stopMarker);
- if (!map.getOverlays().contains(stopsFolderOverlay)) {
- Log.w(DEBUG_TAG, "Map doesn't have folder overlay");
- }
- good=true;
- }
- //Log.d(DEBUG_TAG,"We have " +stopsFolderOverlay.getItems().size()+" stops in the folderOverlay");
- //force redraw of markers
- map.invalidate();
- }
-
- public Marker makeMarker(GeoPoint geoPoint, Stop stop, boolean isStartMarker){
- return makeMarker(geoPoint,stop.ID,
- stop.getStopDefaultName(),
- stop.routesThatStopHereToString(), isStartMarker);
- }
-
- public Marker makeMarker(GeoPoint geoPoint, String stopID, String stopName,
- String routesStopping, boolean isStartMarker) {
-
- // add a marker
- final Marker marker = new Marker(map);
-
- // set custom info window as info window
- CustomInfoWindow popup = new CustomInfoWindow(map, stopID, stopName, routesStopping,
- responder, R.layout.linedetail_stop_infowindow, R.color.red_darker);
- marker.setInfoWindow(popup);
-
- // make the marker clickable
- marker.setOnMarkerClickListener((thisMarker, mapView) -> {
- if (thisMarker.isInfoWindowOpen()) {
- // on second click
- Log.w(DEBUG_TAG, "Pressed on the click marker");
- } 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_CENTER);
- // add to it an icon
- //marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
-
- marker.setIcon(ResourcesCompat.getDrawable(getResources(), R.drawable.bus_stop, ctx.getTheme()));
- // add to it a title
- marker.setTitle(stopName);
- // set the description as the ID
- marker.setSnippet(stopID);
-
- // show popup info window of the searched marker
- if (isStartMarker) {
- marker.showInfoWindow();
- //map.getController().animateTo(marker.getPosition());
- }
-
- return marker;
- }
-
- @Nullable
- @Override
- public View getBaseViewForSnackBar() {
- return coordLayout;
- }
-
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragmentKt.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapFragmentKt.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapFragmentKt.kt
+++ /dev/null
@@ -1,750 +0,0 @@
-/*
- BusTO - Fragments components
- Copyright (C) 2020 Andrea Ugo
- Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
- */
-package it.reyboz.bustorino.fragments
-
-import android.Manifest
-import android.animation.ObjectAnimator
-import android.annotation.SuppressLint
-import android.content.Context
-import android.location.LocationManager
-import android.os.Bundle
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageButton
-import android.widget.Toast
-import androidx.activity.result.ActivityResultCallback
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.content.res.ResourcesCompat
-import androidx.fragment.app.viewModels
-import androidx.preference.PreferenceManager
-import it.reyboz.bustorino.R
-import it.reyboz.bustorino.backend.Stop
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
-import it.reyboz.bustorino.backend.mato.MQTTMatoClient
-import it.reyboz.bustorino.backend.utils
-import it.reyboz.bustorino.data.gtfs.MatoPattern
-import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
-import it.reyboz.bustorino.map.BusInfoWindow
-import it.reyboz.bustorino.map.CustomInfoWindow
-import it.reyboz.bustorino.map.CustomInfoWindow.TouchResponder
-import it.reyboz.bustorino.map.LocationOverlay
-import it.reyboz.bustorino.map.LocationOverlay.OverlayCallbacks
-import it.reyboz.bustorino.map.MarkerUtils
-import it.reyboz.bustorino.middleware.GeneralActivity
-import it.reyboz.bustorino.util.Permissions
-import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
-import it.reyboz.bustorino.viewmodels.StopsMapViewModel
-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.GeoPoint
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.FolderOverlay
-import org.osmdroid.views.overlay.Marker
-import org.osmdroid.views.overlay.infowindow.InfoWindow
-import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
-
-open class MapFragmentKt : ScreenBaseFragment() {
- protected var listenerMain: FragmentListenerMain? = null
- private var shownStops: HashSet<String>? = null
- private lateinit var map: MapView
- var ctx: Context? = null
- private lateinit var mLocationOverlay: LocationOverlay
- private lateinit var stopsFolderOverlay: FolderOverlay
- private var savedMapState: Bundle? = null
- protected lateinit var btCenterMap: ImageButton
- protected lateinit var btFollowMe: ImageButton
- protected var coordLayout: CoordinatorLayout? = null
- private var hasMapStartFinished = false
- private var followingLocation = false
-
- //the ViewModel from which we get the stop to display in the map
- private val stopsViewModel: StopsMapViewModel by viewModels()
-
- //private GtfsPositionsViewModel gtfsPosViewModel; //= new ViewModelProvider(this).get(MapViewModel.class);
- private val livePositionsViewModel: LivePositionsViewModel by viewModels()
- private var useMQTTViewModel = true
- private val busPositionMarkersByTrip = HashMap<String, Marker>()
- private var busPositionsOverlay: FolderOverlay? = null
- private val tripMarkersAnimators = HashMap<String, ObjectAnimator>()
- protected val responder = TouchResponder { stopID, stopName ->
- if (listenerMain != null) {
- Log.d(DEBUG_TAG, "Asked to show arrivals for stop ID: $stopID")
- listenerMain!!.requestArrivalsForStopID(stopID)
- }
- }
- protected val locationCallbacks: OverlayCallbacks = object : OverlayCallbacks {
- override fun onDisableFollowMyLocation() {
- updateGUIForLocationFollowing(false)
- followingLocation = false
- }
-
- override fun onEnableFollowMyLocation() {
- updateGUIForLocationFollowing(true)
- followingLocation = true
- }
- }
- private val positionRequestLauncher =
- registerForActivityResult<Array<String>, Map<String, Boolean>>(
- ActivityResultContracts.RequestMultiplePermissions(),
- ActivityResultCallback { result ->
- if (result == null) {
- Log.w(DEBUG_TAG, "Got asked permission but request is null, doing nothing?")
- } else if (java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_COARSE_LOCATION]
- && java.lang.Boolean.TRUE == result[Manifest.permission.ACCESS_FINE_LOCATION]) {
- // We can use the position, restart location overlay
- map.overlays.remove(mLocationOverlay)
- startLocationOverlay(true, map)
- if (context == null || requireContext().getSystemService(Context.LOCATION_SERVICE) == null)
- return@ActivityResultCallback ///@registerForActivityResult
- val locationManager =
- requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
- @SuppressLint("MissingPermission") val userLocation =
- locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
- if (userLocation != null) {
- map!!.controller.setZoom(POSITION_FOUND_ZOOM)
- val startPoint = GeoPoint(userLocation)
- setLocationFollowing(true)
- map!!.controller.setCenter(startPoint)
- }
- } else Log.w(DEBUG_TAG, "No location permission")
- })
-
- //public static MapFragment getInstance(@NonNull Stop stop){
- // return getInstance(stop.getLatitude(), stop.getLongitude(), stop.getStopDisplayName(), stop.ID);
- //}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- //use the same layout as the activity
- val root = inflater.inflate(R.layout.fragment_map, container, false)
- val context = requireContext()
- ctx = context.applicationContext
- Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(context))
- map = root.findViewById(R.id.map)
- map.setTileSource(TileSourceFactory.MAPNIK)
- //map.setTilesScaledToDpi(true);
- map.setFlingEnabled(true)
-
- // add ability to zoom with 2 fingers
- map.setMultiTouchControls(true)
- btCenterMap = root.findViewById(R.id.centerMapImageButton)
- btFollowMe = root.findViewById(R.id.followUserImageButton)
- coordLayout = root.findViewById(R.id.coord_layout)
-
- //setup FolderOverlay
- stopsFolderOverlay = FolderOverlay()
- //setup Bus Markers Overlay
- busPositionsOverlay = FolderOverlay()
- //reset shown bus updates
- busPositionMarkersByTrip.clear()
- tripMarkersAnimators.clear()
- //set map not done
- hasMapStartFinished = false
- val keySourcePositions = getString(R.string.pref_positions_source)
- useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions, SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE)
- .contentEquals(SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE)
-
-
- //Start map from bundle
- if (savedInstanceState != null) startMap(arguments, savedInstanceState) else startMap(
- arguments, savedMapState
- )
- //set listeners
- map.addMapListener(DelayedMapListener(object : MapListener {
- override fun onScroll(paramScrollEvent: ScrollEvent): Boolean {
- requestStopsToShow()
- //Log.d(DEBUG_TAG, "Scrolling");
- //if (moveTriggeredByCode) moveTriggeredByCode =false;
- //else setLocationFollowing(false);
- return true
- }
-
- override fun onZoom(event: ZoomEvent): Boolean {
- requestStopsToShow()
- return true
- }
- }))
- btCenterMap.setOnClickListener(View.OnClickListener { v: View? ->
- //Log.i(TAG, "centerMap clicked ");
- if (Permissions.bothLocationPermissionsGranted(context)) {
- val myPosition = mLocationOverlay!!.myLocation
- map.getController().animateTo(myPosition)
- } else Toast.makeText(context, R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show()
- })
- btFollowMe.setOnClickListener(View.OnClickListener { v: View? ->
- //Log.i(TAG, "btFollowMe clicked ");
- if (Permissions.bothLocationPermissionsGranted(context)) setLocationFollowing(!followingLocation) else Toast.makeText(
- context, R.string.enable_position_message_map, Toast.LENGTH_SHORT
- )
- .show()
- })
- return root
- }
-
- override fun onAttach(context: Context) {
- super.onAttach(context)
- listenerMain = if (context is FragmentListenerMain) {
- context
- } else {
- throw RuntimeException(
- context.toString()
- + " must implement FragmentListenerMain"
- )
- }
- }
-
- override fun onDetach() {
- super.onDetach()
- listenerMain = null
-
- Log.w(DEBUG_TAG, "Fragment detached")
- }
-
- override fun onPause() {
- super.onPause()
- Log.w(DEBUG_TAG, "On pause called mapfrag")
- saveMapState()
- for (animator in tripMarkersAnimators.values) {
- if (animator != null && animator.isRunning) {
- animator.cancel()
- }
- }
- tripMarkersAnimators.clear()
- if (useMQTTViewModel) livePositionsViewModel.stopMatoUpdates()
- }
-
- /**
- * Save the map state inside the fragment
- * (calls saveMapState(bundle))
- */
- private fun saveMapState() {
- savedMapState = Bundle()
- saveMapState(savedMapState!!)
- }
-
- /**
- * Save the state of the map to restore it to a later time
- * @param bundle the bundle in which to save the data
- */
- private fun saveMapState(bundle: Bundle) {
- Log.d(DEBUG_TAG, "Saving state, location following: $followingLocation")
- bundle.putBoolean(FOLLOWING_LOCAT_KEY, followingLocation)
- if (map == null) {
- //The map is null, it can happen?
- Log.e(DEBUG_TAG, "Cannot save map center, map is null")
- return
- }
- val loc = map!!.mapCenter
- bundle.putDouble(MAP_CENTER_LAT_KEY, loc.latitude)
- bundle.putDouble(MAP_CENTER_LON_KEY, loc.longitude)
- bundle.putDouble(MAP_CURRENT_ZOOM_KEY, map!!.zoomLevelDouble)
- }
-
- override fun onResume() {
- super.onResume()
- //TODO: cleanup duplicate code (maybe merging the positions classes?)
- if (listenerMain != null) listenerMain!!.readyGUIfor(FragmentKind.MAP)
- /// choose which to use
- val keySourcePositions = getString(R.string.pref_positions_source)
- useMQTTViewModel = PreferenceManager.getDefaultSharedPreferences(requireContext())
- .getString(keySourcePositions, SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE)
- .contentEquals(
- SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE
- )
- //gtfsPosViewModel.requestUpdates();
- if (useMQTTViewModel) livePositionsViewModel.requestMatoPosUpdates(MQTTMatoClient.LINES_ALL)
- else livePositionsViewModel.requestGTFSUpdates()
- //mapViewModel.testCascade();
- livePositionsViewModel.isLastWorkResultGood.observe(this) { d: Boolean ->
- Log.d(
- DEBUG_TAG, "Last trip download result is $d"
- )
- }
- livePositionsViewModel.tripsGtfsIDsToQuery.observe(this) { dat: List<String> ->
- Log.i(DEBUG_TAG, "Have these trips IDs missing from the DB, to be queried: $dat")
- livePositionsViewModel.downloadTripsFromMato(dat)
- }
-
- //rerequest stop
- stopsViewModel!!.requestStopsInBoundingBox(map!!.boundingBox)
- }
-
- private fun startRequestsPositions() {
- if (livePositionsViewModel != null) {
- //should always be the case
- livePositionsViewModel!!.updatesWithTripAndPatterns.observe(viewLifecycleOwner) { data: HashMap<String, Pair<LivePositionUpdate, TripAndPatternWithStops?>> ->
- Log.d(
- DEBUG_TAG,
- "Have " + data.size + " trip updates, has Map start finished: " + hasMapStartFinished
- )
- if (hasMapStartFinished) updateBusPositionsInMap(data)
- if (!isDetached && !useMQTTViewModel) livePositionsViewModel!!.requestDelayedGTFSUpdates(
- 3000
- )
- }
- } else {
- Log.e(DEBUG_TAG, "PositionsViewModel is null")
- }
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- saveMapState(outState)
- super.onSaveInstanceState(outState)
- }
- //own methods
- /**
- * Switch following the location on and off
- * @param value true if we want to follow location
- */
- fun setLocationFollowing(value: Boolean) {
- followingLocation = value
- if (mLocationOverlay == null || context == null || map == null) //nothing else to do
- return
- if (value) {
- mLocationOverlay!!.enableFollowLocation()
- } else {
- mLocationOverlay!!.disableFollowLocation()
- }
- }
-
- /**
- * Do all the stuff you need to do on the gui, when parameter is changed to value
- * @param following value
- */
- protected fun updateGUIForLocationFollowing(following: Boolean) {
- if (following) btFollowMe!!.setImageResource(R.drawable.ic_follow_me_on) else btFollowMe!!.setImageResource(
- R.drawable.ic_follow_me
- )
- }
-
- /**
- * Build the location overlay. Enable only when
- * a) we know we have the permission
- * b) the location map is set
- */
- private fun startLocationOverlay(enableLocation: Boolean, map: MapView?) {
- checkNotNull(activity) { "Cannot enable LocationOverlay now" }
- // Location Overlay
- // from OpenBikeSharing (THANK GOD)
- Log.d(DEBUG_TAG, "Starting position overlay")
- val imlp = GpsMyLocationProvider(requireActivity().baseContext)
- imlp.locationUpdateMinDistance = 5f
- imlp.locationUpdateMinTime = 2000
- val overlay = LocationOverlay(imlp, map, locationCallbacks)
- if (enableLocation) overlay.enableMyLocation()
- overlay.isOptionsMenuEnabled = true
-
- //map.getOverlays().add(this.mLocationOverlay);
- mLocationOverlay = overlay
- map!!.overlays.add(mLocationOverlay)
- }
-
- fun startMap(incoming: Bundle?, savedInstanceState: Bundle?) {
- //Check that we're attached
- val activity = if (activity is GeneralActivity) activity as GeneralActivity? else null
- if (context == null || activity == null) {
- //we are not attached
- Log.e(DEBUG_TAG, "Calling startMap when not attached")
- return
- } else {
- Log.d(DEBUG_TAG, "Starting map from scratch")
- }
- //clear previous overlays
- map!!.overlays.clear()
-
-
- //parse incoming bundle
- var marker: GeoPoint? = null
- var name: String? = null
- var ID: String? = null
- var routesStopping: String? = ""
- if (incoming != null) {
- val lat = incoming.getDouble(BUNDLE_LATIT)
- val lon = incoming.getDouble(BUNDLE_LONGIT)
- marker = GeoPoint(lat, lon)
- name = incoming.getString(BUNDLE_NAME)
- ID = incoming.getString(BUNDLE_ID)
- routesStopping = incoming.getString(BUNDLE_ROUTES_STOPPING, "")
- }
-
-
- //ask for location permission
- if (!Permissions.bothLocationPermissionsGranted(activity)) {
- if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
- //TODO: show dialog for permission rationale
- Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT)
- .show()
- }
- positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS)
- }
- shownStops = HashSet()
- // move the map on the marker position or on a default view point: Turin, Piazza Castello
- // and set the start zoom
- val mapController = map!!.controller
- var startPoint: GeoPoint? = null
- startLocationOverlay(
- Permissions.bothLocationPermissionsGranted(activity),
- map
- )
- // set the center point
- if (marker != null) {
- //startPoint = marker;
- mapController.setZoom(POSITION_FOUND_ZOOM)
- setLocationFollowing(false)
- // put the center a little bit off (animate later)
- startPoint = GeoPoint(marker)
- startPoint.latitude = marker.latitude + utils.angleRawDifferenceFromMeters(20.0)
- startPoint.longitude = marker.longitude - utils.angleRawDifferenceFromMeters(20.0)
- //don't need to do all the rest since we want to show a point
- } else if (savedInstanceState != null && savedInstanceState.containsKey(MAP_CURRENT_ZOOM_KEY)) {
- mapController.setZoom(savedInstanceState.getDouble(MAP_CURRENT_ZOOM_KEY))
- mapController.setCenter(
- GeoPoint(
- savedInstanceState.getDouble(MAP_CENTER_LAT_KEY),
- savedInstanceState.getDouble(MAP_CENTER_LON_KEY)
- )
- )
- Log.d(
- DEBUG_TAG,
- "Location following from savedInstanceState: " + savedInstanceState.getBoolean(
- FOLLOWING_LOCAT_KEY
- )
- )
- setLocationFollowing(savedInstanceState.getBoolean(FOLLOWING_LOCAT_KEY))
- } else {
- Log.d(DEBUG_TAG, "No position found from intent or saved state")
- var found = false
- val locationManager =
- requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
- //check for permission
- if (Permissions.bothLocationPermissionsGranted(activity)) {
- @SuppressLint("MissingPermission") val userLocation =
- locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)
- if (userLocation != null) {
- val distan = utils.measuredistanceBetween(
- userLocation.latitude, userLocation.longitude,
- DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON
- )
- if (distan < 100000.0) {
- mapController.setZoom(POSITION_FOUND_ZOOM)
- startPoint = GeoPoint(userLocation)
- found = true
- setLocationFollowing(true)
- }
- }
- }
- if (!found) {
- startPoint = GeoPoint(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON)
- mapController.setZoom(NO_POSITION_ZOOM)
- setLocationFollowing(false)
- }
- }
-
- // set the minimum zoom level
- map!!.minZoomLevel = 15.0
- //add contingency check (shouldn't happen..., but)
- if (startPoint != null) {
- mapController.setCenter(startPoint)
- }
-
-
- //add stops overlay
- //map.getOverlays().add(mLocationOverlay);
- map!!.overlays.add(stopsFolderOverlay)
- Log.d(DEBUG_TAG, "Requesting stops load")
- // This is not necessary, by setting the center we already move
- // the map and we trigger a stop request
- //requestStopsToShow();
- if (marker != null) {
- // make a marker with the info window open for the searched marker
- //TODO: make Stop Bundle-able
- val stopMarker = makeMarker(marker, ID, name, routesStopping, true)
- map!!.controller.animateTo(marker)
- }
- //add the overlays with the bus stops
- if (busPositionsOverlay == null) {
- //Log.i(DEBUG_TAG, "Null bus positions overlay,redo");
- busPositionsOverlay = FolderOverlay()
- }
- startRequestsPositions()
- if (stopsViewModel != null) {
- stopsViewModel!!.stopsInBoundingBox.observe(viewLifecycleOwner) { stops: List<Stop>? ->
- showStopsMarkers(
- stops
- )
- }
- } else Log.d(DEBUG_TAG, "Cannot observe new stops in map, stopsViewModel is null")
- map!!.overlays.add(busPositionsOverlay)
- //set map as started
- hasMapStartFinished = true
- }
-
- /**
- * Start a request to load the stops that are in the current view
- * from the database
- */
- private fun requestStopsToShow() {
- // get the top, bottom, left and right screen's coordinate
- val bb = map!!.boundingBox
- Log.d(
- DEBUG_TAG,
- "Requesting stops in bounding box, stopViewModel is null " + (false)
- )
- stopsViewModel.requestStopsInBoundingBox(bb)
-
- }
-
- private fun updateBusMarker(
- marker: Marker?,
- posUpdate: LivePositionUpdate,
- justCreated: Boolean
- ) {
- val position: GeoPoint
- val updateID = posUpdate.tripID
- if (!justCreated) {
- position = marker!!.position
- if (posUpdate.latitude != position.latitude || posUpdate.longitude != position.longitude) {
- val newpos = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- val valueAnimator = MarkerUtils.makeMarkerAnimator(
- map, marker, newpos, MarkerUtils.LINEAR_ANIMATION, 1200
- )
- valueAnimator.setAutoCancel(true)
- tripMarkersAnimators[updateID] = valueAnimator
- valueAnimator.start()
- }
- //marker.setPosition(new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude()));
- } else {
- position = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- marker!!.position = position
- }
- marker.rotation = posUpdate.bearing?.let { it*-1f } ?: 0.0f
- }
-
- private fun updateBusPositionsInMap(tripsPatterns: HashMap<String, Pair<LivePositionUpdate, TripAndPatternWithStops?>>) {
- Log.d(DEBUG_TAG, "Updating positions of the buses")
- //if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- val noPatternsTrips = ArrayList<String>()
- for (tripID in tripsPatterns.keys) {
- val (update, tripWithPatternStops) = tripsPatterns[tripID] ?: continue
-
-
- //check if Marker is already created
- if (busPositionMarkersByTrip.containsKey(tripID)) {
- //need to change the position of the marker
- val marker = busPositionMarkersByTrip[tripID]!!
- updateBusMarker(marker, update, false)
- if (marker.infoWindow != null && marker.infoWindow is BusInfoWindow) {
- val window = marker.infoWindow as BusInfoWindow
- if (tripWithPatternStops != null) {
- //Log.d(DEBUG_TAG, "Update pattern for trip: "+tripID);
- window.setPatternAndDraw(tripWithPatternStops.pattern)
- }
- }
- } else {
- //marker is not there, need to make it
- val marker = Marker(map)
-
- /*final Drawable mDrawable = DrawableUtils.Companion.getScaledDrawableResources(
- getResources(),
- R.drawable.point_heading_icon,
- R.dimen.map_icons_size, R.dimen.map_icons_size);
-
- */
- //String route = GtfsUtils.getLineNameFromGtfsID(update.getRouteID());
- val mdraw =
- ResourcesCompat.getDrawable(resources, R.drawable.map_bus_position_icon, null)!!
- //mdraw.setBounds(0,0,28,28);
- marker.icon = mdraw
- if (tripWithPatternStops == null) {
- noPatternsTrips.add(tripID)
- }
- var markerPattern: MatoPattern? = null
- if (tripWithPatternStops != null && tripWithPatternStops.pattern != null) markerPattern =
- tripWithPatternStops.pattern
- marker.infoWindow =
- BusInfoWindow(map!!, update, markerPattern, false) { pattern: MatoPattern? -> }
- marker.setInfoWindowAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- updateBusMarker(marker, update, true)
- // the overlay is null when it's not attached yet?5
- // cannot recreate it because it becomes null very soon
- // if(busPositionsOverlay == null) busPositionsOverlay = new FolderOverlay();
- //save the marker
- if (busPositionsOverlay != null) {
- busPositionsOverlay!!.add(marker)
- busPositionMarkersByTrip[tripID] = marker
- }
- }
- }
- if (noPatternsTrips.size > 0) {
- Log.i(DEBUG_TAG, "These trips have no matching pattern: $noPatternsTrips")
- }
- }
-
- /**
- * Add stops as Markers on the map
- * @param stops the list of stops that must be included
- */
- protected fun showStopsMarkers(stops: List<Stop>?) {
- if (context == null || stops == null) {
- //we are not attached
- return
- }
- var good = true
- for (stop in stops) {
- if (shownStops!!.contains(stop.ID)) {
- continue
- }
- if (stop.longitude == null || stop.latitude == null) continue
- shownStops!!.add(stop.ID)
- if (!map!!.isShown) {
- if (good) Log.d(
- DEBUG_TAG,
- "Need to show stop but map is not shown, probably detached already"
- )
- good = false
- continue
- } else if (map!!.repository == null) {
- Log.e(DEBUG_TAG, "Map view repository is null")
- }
- val marker = GeoPoint(stop.latitude!!, stop.longitude!!)
- val stopMarker = makeMarker(marker, stop, false)
- stopsFolderOverlay!!.add(stopMarker)
- if (!map!!.overlays.contains(stopsFolderOverlay)) {
- Log.w(DEBUG_TAG, "Map doesn't have folder overlay")
- }
- good = true
- }
- //Log.d(DEBUG_TAG,"We have " +stopsFolderOverlay.getItems().size()+" stops in the folderOverlay");
- //force redraw of markers
- map!!.invalidate()
- }
-
- fun makeMarker(geoPoint: GeoPoint?, stop: Stop, isStartMarker: Boolean): Marker {
- return makeMarker(
- geoPoint, stop.ID,
- stop.stopDefaultName,
- stop.routesThatStopHereToString(), isStartMarker
- )
- }
-
- fun makeMarker(
- geoPoint: GeoPoint?, stopID: String?, stopName: String?,
- routesStopping: String?, isStartMarker: Boolean
- ): Marker {
-
- // add a marker
- val marker = Marker(map)
-
- // set custom info window as info window
- val popup = CustomInfoWindow(
- map, stopID, stopName, routesStopping,
- responder, R.layout.linedetail_stop_infowindow, R.color.red_darker
- )
- marker.infoWindow = popup
-
- // make the marker clickable
- marker.setOnMarkerClickListener { thisMarker: Marker, mapView: MapView? ->
- if (thisMarker.isInfoWindowOpen) {
- // on second click
- Log.w(DEBUG_TAG, "Pressed on the click marker")
- } 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!!.controller.animateTo(thisMarker.position)
- }
- true
- }
-
- // set its position
- marker.position = geoPoint
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- // add to it an icon
- //marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
- marker.icon = ResourcesCompat.getDrawable(resources, R.drawable.bus_stop, ctx!!.theme)
- // add to it a title
- marker.title = stopName
- // set the description as the ID
- marker.snippet = stopID
-
- // show popup info window of the searched marker
- if (isStartMarker) {
- marker.showInfoWindow()
- //map.getController().animateTo(marker.getPosition());
- }
- return marker
- }
-
- override fun getBaseViewForSnackBar(): View? {
- return coordLayout
- }
-
- companion object {
- //private static final String TAG = "Busto-MapActivity";
- private const val MAP_CURRENT_ZOOM_KEY = "map-current-zoom"
- private const val MAP_CENTER_LAT_KEY = "map-center-lat"
- private const val MAP_CENTER_LON_KEY = "map-center-lon"
- private const val FOLLOWING_LOCAT_KEY = "following"
- const val BUNDLE_LATIT = "lat"
- const val BUNDLE_LONGIT = "lon"
- const val BUNDLE_NAME = "name"
- const val BUNDLE_ID = "ID"
- const val BUNDLE_ROUTES_STOPPING = "routesStopping"
- const val FRAGMENT_TAG = "BusTOMapFragment"
- private const val DEFAULT_CENTER_LAT = 45.0708
- private const val DEFAULT_CENTER_LON = 7.6858
- private const val POSITION_FOUND_ZOOM = 18.3
- const val NO_POSITION_ZOOM = 17.1
- private const val DEBUG_TAG = FRAGMENT_TAG
-
- @JvmStatic
- fun getInstance(): MapFragmentKt {
- return MapFragmentKt()
- }
- @JvmStatic
- fun getInstance(stop: Stop): MapFragmentKt {
- val fragment = MapFragmentKt()
- val args = Bundle()
- args.putDouble(MapFragment.BUNDLE_LATIT, stop.latitude!!)
- args.putDouble(MapFragment.BUNDLE_LONGIT, stop.longitude!!)
- args.putString(MapFragment.BUNDLE_NAME, stop.stopDisplayName)
- args.putString(MapFragment.BUNDLE_ID, stop.ID)
- args.putString(MapFragment.BUNDLE_ROUTES_STOPPING, stop.routesThatStopHereToString())
- fragment.arguments = args
-
- return fragment
- }
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt
@@ -7,12 +7,10 @@
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
-import android.content.Intent
import android.graphics.Color
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
-import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@@ -32,7 +30,6 @@
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.gson.Gson
import com.google.gson.JsonObject
import it.reyboz.bustorino.R
import it.reyboz.bustorino.backend.Stop
@@ -42,12 +39,11 @@
import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
import it.reyboz.bustorino.fragments.SettingsFragment.LIVE_POSITIONS_PREF_MQTT_VALUE
import it.reyboz.bustorino.map.MapLibreUtils
-import it.reyboz.bustorino.map.Styles
+import it.reyboz.bustorino.map.MapLibreStyles
import it.reyboz.bustorino.util.Permissions
import it.reyboz.bustorino.util.ViewUtils
import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
import it.reyboz.bustorino.viewmodels.StopsMapViewModel
-import org.maplibre.android.MapLibre
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
@@ -56,14 +52,9 @@
import org.maplibre.android.location.LocationComponentOptions
import org.maplibre.android.location.modes.CameraMode
import org.maplibre.android.maps.MapLibreMap
-import org.maplibre.android.maps.MapView
-import org.maplibre.android.maps.OnMapReadyCallback
import org.maplibre.android.maps.Style
import org.maplibre.android.plugins.annotation.Symbol
-import org.maplibre.android.plugins.annotation.SymbolManager
-import org.maplibre.android.plugins.annotation.SymbolOptions
import org.maplibre.android.style.expressions.Expression
-import org.maplibre.android.style.layers.Property
import org.maplibre.android.style.layers.Property.*
import org.maplibre.android.style.layers.PropertyFactory
import org.maplibre.android.style.layers.SymbolLayer
@@ -289,7 +280,7 @@
override fun onMapReady(mapReady: MapLibreMap) {
this.map = mapReady
val context = requireContext()
- val mjson = Styles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context))
+ val mjson = MapLibreStyles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context))
//ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
activity?.run {
@@ -1125,6 +1116,8 @@
private const val DEBUG_TAG = "BusTO-MapLibreFrag"
private const val STOP_ACTIVE_IMG = "Stop-active"
+ const val FRAGMENT_TAG = "BusTOMapFragment"
+
private const val LOCATION_PERMISSION_REQUEST_CODE = 981202
/**
diff --git a/app/src/main/java/it/reyboz/bustorino/map/BusInfoWindow.kt b/app/src/main/java/it/reyboz/bustorino/map/BusInfoWindow.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/BusInfoWindow.kt
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- BusTO - Map components
- Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
- */
-package it.reyboz.bustorino.map
-
-import android.annotation.SuppressLint
-import android.view.View.*
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.view.marginEnd
-import it.reyboz.bustorino.R
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
-import it.reyboz.bustorino.backend.gtfs.GtfsUtils
-import it.reyboz.bustorino.backend.utils
-import it.reyboz.bustorino.data.gtfs.MatoPattern
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.infowindow.BasicInfoWindow
-
-@SuppressLint("ClickableViewAccessibility")
-class BusInfoWindow(map: MapView,
- private val routeName: String,
- private val vehicleLabel: String,
- var pattern: MatoPattern?,
- val showClose: Boolean,
- private val touchUp: onTouchUp
- ):
- BasicInfoWindow(R.layout.bus_info_window,map) {
-
- init {
- mView.setOnTouchListener { view, motionEvent ->
- touchUp.onActionUp(pattern)
- close()
- //mView.performClick()
- true
-
- }
- }
- constructor(map: MapView, update: LivePositionUpdate, pattern: MatoPattern?, showClose: Boolean, touchUp: onTouchUp, ):
- this(map,
- GtfsUtils.getLineNameFromGtfsID(update.routeID),
- update.vehicle,
- pattern,
- showClose,
- touchUp
- )
-
-
- override fun onOpen(item: Any?) {
- // super.onOpen(item)
- val titleView = mView.findViewById<TextView>(R.id.businfo_title)
- val descrView = mView.findViewById<TextView>(R.id.businfo_description)
- val subdescrView = mView.findViewById<TextView>(R.id.businfo_subdescription)
-
- val iconClose = mView.findViewById<ImageView>(R.id.closeIcon)
-
- //val nameRoute = GtfsUtils.getLineNameFromGtfsID(update.lineGtfsId)
-
- titleView.text = (mView.resources.getString(R.string.line_fill, routeName)
- )
- subdescrView.text = vehicleLabel
- //mView.resources.getString(R.string.vehicle_fill, vehicleLabel)
-
-
- if(pattern!=null){
- descrView.text = pattern!!.headsign
- descrView.visibility = VISIBLE
- } else{
- descrView.visibility = GONE
- }
- if(!showClose){
- iconClose.visibility = GONE
- val ctx = titleView.context
- val layPars = (titleView.layoutParams as ConstraintLayout.LayoutParams).apply {
- marginStart= 0 //utils.convertDipToPixelsInt(ctx, 8.0)//8.dpToPixels()
- topMargin=utils.convertDipToPixelsInt(ctx, 4.0)
- marginEnd=0
- bottomMargin=0
- }
- //titleView.layoutParams = layPars
- }
- }
-
- fun setPatternAndDraw(pattern: MatoPattern?){
- if(pattern==null){
- return
- }
- this.pattern = pattern
- if(isOpen){
- onOpen(pattern)
- }
- }
-
- fun interface onTouchUp{
- fun onActionUp(pattern: MatoPattern?)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/map/BusPositionUtils.kt b/app/src/main/java/it/reyboz/bustorino/map/BusPositionUtils.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/BusPositionUtils.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package it.reyboz.bustorino.map
-
-import android.animation.ObjectAnimator
-import android.util.Log
-import androidx.core.content.res.ResourcesCompat
-import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
-import it.reyboz.bustorino.data.gtfs.MatoPattern
-import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
-import org.osmdroid.util.GeoPoint
-import org.osmdroid.views.MapView
-import org.osmdroid.views.overlay.Marker
-
-class BusPositionUtils {
- companion object{
- @JvmStatic
- public fun updateBusPositionMarker(map: MapView, marker: Marker?, posUpdate: LivePositionUpdate,
- tripMarkersAnimators: HashMap<String, ObjectAnimator>,
- justCreated: Boolean) {
- val position: GeoPoint
- val updateID = posUpdate.tripID
- if (!justCreated) {
- position = marker!!.position
- if (posUpdate.latitude != position.latitude || posUpdate.longitude != position.longitude) {
- val newpos = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- val valueAnimator = MarkerUtils.makeMarkerAnimator(
- map, marker, newpos, MarkerUtils.LINEAR_ANIMATION, 1200
- )
- valueAnimator.setAutoCancel(true)
- tripMarkersAnimators.put(updateID, valueAnimator)
- valueAnimator.start()
- }
- //marker.setPosition(new GeoPoint(posUpdate.getLatitude(), posUpdate.getLongitude()));
- } else {
- position = GeoPoint(posUpdate.latitude, posUpdate.longitude)
- marker!!.position = position
- }
- //if (posUpdate.bearing != null) marker.rotation = posUpdate.bearing * -1f
- marker.rotation = posUpdate.bearing?.let { it*-1f } ?: 0.0f
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/map/CustomInfoWindow.java b/app/src/main/java/it/reyboz/bustorino/map/CustomInfoWindow.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/CustomInfoWindow.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- BusTO - Map components
- Copyright (C) 2020 Andrea Ugo
- Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
- */
-package it.reyboz.bustorino.map;
-
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.view.MotionEvent;
-import android.view.View;
-
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import androidx.core.content.ContextCompat;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.infowindow.BasicInfoWindow;
-
-import it.reyboz.bustorino.R;
-
-public class CustomInfoWindow extends BasicInfoWindow {
- //TODO: Make the action on the Click customizable
- private final TouchResponder touchResponder;
- private final String stopID, name, routesStopping;
-
- private final int colorResID;
- //final DisplayMetrics metrics;
-
- @Override
- public void onOpen(Object item) {
- super.onOpen(item);
- TextView descr_textView = mView.findViewById(R.id.bubble_description);
- CharSequence text = descr_textView.getText();
- TextView titleTV = mView.findViewById(R.id.bubble_title);
- titleTV.setTextColor(ContextCompat.getColor(mView.getContext(),colorResID));
- //Log.d("BusTO-MapInfoWindow", "Descrip: "+text+", title "+(titleTV==null? "null": titleTV.getText()));
-
- if (text==null || !text.toString().isEmpty()){
- descr_textView.setVisibility(View.VISIBLE);
- } else
- descr_textView.setVisibility(View.GONE);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- mView.setElevation(3.2f);
- }
- TextView subDescriptTextView = mView.findViewById(R.id.bubble_subdescription);
- if (routesStopping!=null && !routesStopping.isEmpty()){
- subDescriptTextView.setText(routesStopping);
- subDescriptTextView.setVisibility(View.VISIBLE);
- }
-
- //check if there is a close image
- ImageView image = mView.findViewById(R.id.closeIcon);
- if (image != null) {
- image.setOnClickListener( view -> close());
- }
-
- }
- public CustomInfoWindow(MapView mapView, String stopID, String name, String routesStopping,
- TouchResponder responder){
-
- this(mapView, stopID, name, routesStopping, responder,R.layout.map_popup, R.color.red_darker);
- }
-
- @SuppressLint("ClickableViewAccessibility")
- public CustomInfoWindow(MapView mapView,
- String stopID,
- String name,
- String routesStopping,
- TouchResponder responder,
- int layoutId,
- int colorResId) {
- // get the personalized layout
- super(layoutId, mapView);
- touchResponder =responder;
- this.stopID = stopID;
- this.name = name;
- this.routesStopping = routesStopping;
- colorResID = colorResId;
-
- //metrics = Resources.getSystem().getDisplayMetrics();
-
- // make clickable
- mView.setOnTouchListener((View v, MotionEvent e) -> {
- if (e.getAction() == MotionEvent.ACTION_UP) {
- // on click
- touchResponder.onActionUp(this.stopID, this.name);
- }
- return true;
- });
-
-
- }
-
- public interface TouchResponder{
- /**
- * React to a click on the stop View
- * @param stopID the stop id
- * @param stopName the stop name
- */
- void onActionUp(@NonNull String stopID, @Nullable String stopName);
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/map/GeoPointInterpolator.java b/app/src/main/java/it/reyboz/bustorino/map/LatLngInterpolator.java
rename from app/src/main/java/it/reyboz/bustorino/map/GeoPointInterpolator.java
rename to app/src/main/java/it/reyboz/bustorino/map/LatLngInterpolator.java
--- a/app/src/main/java/it/reyboz/bustorino/map/GeoPointInterpolator.java
+++ b/app/src/main/java/it/reyboz/bustorino/map/LatLngInterpolator.java
@@ -3,7 +3,8 @@
/* Copyright 2013 Google Inc.
Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */
-import org.osmdroid.util.GeoPoint;
+
+import org.maplibre.android.geometry.LatLng;
import static java.lang.Math.asin;
import static java.lang.Math.atan2;
@@ -14,21 +15,21 @@
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;
-public interface GeoPointInterpolator {
- public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b);
+public interface LatLngInterpolator {
+ public LatLng interpolate(float fraction, LatLng a, LatLng b);
- public class Linear implements GeoPointInterpolator {
+ public class Linear implements LatLngInterpolator {
@Override
- public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b) {
+ public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.getLatitude() - a.getLatitude()) * fraction + a.getLatitude();
double lng = (b.getLongitude() - a.getLongitude()) * fraction + a.getLongitude();
- return new GeoPoint(lat, lng);
+ return new LatLng(lat, lng);
}
}
- public class LinearFixed implements GeoPointInterpolator {
+ public class LinearFixed implements LatLngInterpolator {
@Override
- public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b) {
+ public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.getLatitude() - a.getLatitude()) * fraction + a.getLatitude();
double lngDelta = b.getLongitude() - a.getLongitude();
@@ -37,15 +38,15 @@
lngDelta -= Math.signum(lngDelta) * 360;
}
double lng = lngDelta * fraction + a.getLongitude();
- return new GeoPoint(lat, lng);
+ return new LatLng(lat, lng);
}
}
- public class Spherical implements GeoPointInterpolator {
+ public class Spherical implements LatLngInterpolator {
/* From github.com/googlemaps/android-maps-utils */
@Override
- public GeoPoint interpolate(float fraction, GeoPoint from, GeoPoint to) {
+ public LatLng interpolate(float fraction, LatLng from, LatLng to) {
// http://en.wikipedia.org/wiki/Slerp
double fromLat = toRadians(from.getLatitude());
double fromLng = toRadians(from.getLongitude());
@@ -71,7 +72,7 @@
// Converts interpolated vector back to polar.
double lat = atan2(z, sqrt(x * x + y * y));
double lng = atan2(y, x);
- return new GeoPoint(toDegrees(lat), toDegrees(lng));
+ return new LatLng(toDegrees(lat), toDegrees(lng));
}
private double computeAngleBetween(double fromLat, double fromLng, double toLat, double toLng) {
diff --git a/app/src/main/java/it/reyboz/bustorino/map/LocationOverlay.java b/app/src/main/java/it/reyboz/bustorino/map/LocationOverlay.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/LocationOverlay.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- BusTO - Map components
- Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
- */
-package it.reyboz.bustorino.map;
-
-
-import android.content.Context;
-import android.util.Log;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider;
-import org.osmdroid.views.overlay.mylocation.IMyLocationProvider;
-import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay;
-
-public class LocationOverlay extends MyLocationNewOverlay {
-
- private final static String DEBUG_TAG = "BusTOLocationOverlay";
- final OverlayCallbacks callbacks;
-
- public LocationOverlay(MapView mapView, OverlayCallbacks callbacks) {
- super(mapView);
- this.callbacks = callbacks;
- }
-
- public LocationOverlay(IMyLocationProvider myLocationProvider, MapView mapView, OverlayCallbacks callbacks) {
- super(myLocationProvider, mapView);
- this.callbacks = callbacks;
- }
-
- @Override
- public void enableFollowLocation() {
- super.enableFollowLocation();
- callbacks.onEnableFollowMyLocation();
- }
-
- @Override
- public void disableFollowLocation() {
-
- super.disableFollowLocation();
- callbacks.onDisableFollowMyLocation();
- }
-
- public static LocationOverlay createLocationOverlay(boolean enableLocation, MapView map, Context context, OverlayCallbacks locationCallbacks){
- if(context== null) {
- Log.d(DEBUG_TAG, "Cannot start location overlay, context is null");
- return null;
- }
- // Location Overlay
- // from OpenBikeSharing (THANK GOD)
- Log.d(DEBUG_TAG, "Starting position overlay");
- GpsMyLocationProvider imlp = new GpsMyLocationProvider(context.getApplicationContext());
- imlp.setLocationUpdateMinDistance(5);
- imlp.setLocationUpdateMinTime(2000);
-
- final LocationOverlay overlay = new LocationOverlay(imlp,map, locationCallbacks);
- if (enableLocation) overlay.enableMyLocation();
- //overlay.setOptionsMenuEnabled(true);
-
- return overlay;
- }
-
- public interface OverlayCallbacks{
- /**
- * Called right after disableFollowMyLocation
- */
- void onDisableFollowMyLocation();
-
- /**
- * Called right after enableFollowMyLocation
- */
- void onEnableFollowMyLocation();
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/map/Styles.kt b/app/src/main/java/it/reyboz/bustorino/map/MapLibreStyles.kt
rename from app/src/main/java/it/reyboz/bustorino/map/Styles.kt
rename to app/src/main/java/it/reyboz/bustorino/map/MapLibreStyles.kt
--- a/app/src/main/java/it/reyboz/bustorino/map/Styles.kt
+++ b/app/src/main/java/it/reyboz/bustorino/map/MapLibreStyles.kt
@@ -3,7 +3,7 @@
import it.reyboz.bustorino.util.ViewUtils
import org.maplibre.android.maps.Style
-object Styles {
+object MapLibreStyles {
const val DEMOTILES = "https://demotiles.maplibre.org/style.json"
const val VERSATILES = "https://tiles.versatiles.org/assets/styles/colorful.json"
diff --git a/app/src/main/java/it/reyboz/bustorino/map/MarkerUtils.java b/app/src/main/java/it/reyboz/bustorino/map/MarkerUtils.java
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/MarkerUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package it.reyboz.bustorino.map;
-
-import android.animation.ObjectAnimator;
-import android.animation.TypeEvaluator;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.util.Property;
-
-
-import android.view.animation.LinearInterpolator;
-import it.reyboz.bustorino.R;
-import org.osmdroid.util.GeoPoint;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.Marker;
-import org.osmdroid.views.overlay.infowindow.InfoWindow;
-
-public class MarkerUtils {
-
- public static final int LINEAR_ANIMATION = 1;
-
- /* Copyright 2013 Google Inc.
- Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */
- public static ObjectAnimator makeMarkerAnimator(final MapView map, Marker marker, GeoPoint finalPosition, int animationType, int durationMs) {
-
- GeoPointInterpolator interpolator;
- switch (animationType){
- case LINEAR_ANIMATION:
- interpolator = new GeoPointInterpolator.Linear();
- break;
- default:
- throw new IllegalArgumentException("Value "+animationType+ " for animationType is invalid");
- }
- TypeEvaluator<GeoPoint> typeEvaluator = (fraction, startValue, endValue) ->
- interpolator.interpolate(fraction, startValue, endValue);
- Property<Marker, GeoPoint> property = Property.of(Marker.class, GeoPoint.class, "position");
- ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
- switch (animationType){
- case LINEAR_ANIMATION:
-
- animator.setInterpolator(new LinearInterpolator());
- default:
- }
- animator.setDuration(durationMs);
- //animator.start();
- return animator;
- }
-
- public static Marker makeMarker(GeoPoint geoPoint, String stopID, String stopName,
- String routesStopping,
- MapView map,
- CustomInfoWindow.TouchResponder responder,
- Drawable icon,
- int infoWindowLayout,
- int titleColorId) {
-
- // add a marker
- final Marker marker = new Marker(map);
-
- // set custom info window as info window
- CustomInfoWindow popup = new CustomInfoWindow(map, stopID, stopName, routesStopping, responder, infoWindowLayout, titleColorId);
- marker.setInfoWindow(popup);
-
- // make the marker clickable
- marker.setOnMarkerClickListener((thisMarker, mapView) -> {
- if (thisMarker.isInfoWindowOpen()) {
- // on second click
- Log.w("BusTO-OsmMap", "Pressed on the click marker");
- } 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);
- // add to it an icon
- //marker.setIcon(getResources().getDrawable(R.drawable.bus_marker));
-
- marker.setIcon(icon);
- // add to it a title
- marker.setTitle(stopName);
- // set the description as the ID
- marker.setSnippet(stopID);
-
- // show popup info window of the searched marker
- /*if (isStartMarker) {
- marker.showInfoWindow();
- //map.getController().animateTo(marker.getPosition());
- }*/
-
- return marker;
- }
-}
diff --git a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
--- a/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
+++ b/app/src/main/java/it/reyboz/bustorino/util/RoutePositionSorter.java
@@ -21,11 +21,7 @@
import androidx.core.util.Pair;
import android.util.Log;
-import it.reyboz.bustorino.backend.Passaggio;
-import it.reyboz.bustorino.backend.Route;
-import it.reyboz.bustorino.backend.Stop;
-import it.reyboz.bustorino.backend.utils;
-import org.osmdroid.api.IGeoPoint;
+import it.reyboz.bustorino.backend.*;
import java.util.Collections;
import java.util.Comparator;
@@ -39,7 +35,7 @@
latPos = latitude;
longPos = longitude;
}
- public RoutePositionSorter(IGeoPoint position){
+ public RoutePositionSorter(GPSPoint position){
this(position.getLatitude(), position.getLongitude());
}
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/StopsMapViewModel.kt
@@ -9,7 +9,6 @@
import it.reyboz.bustorino.data.NextGenDB
import it.reyboz.bustorino.data.OldDataRepository
import org.maplibre.android.geometry.LatLngBounds
-import org.osmdroid.util.BoundingBox
import java.util.concurrent.Executors
import kotlin.collections.ArrayList
@@ -60,12 +59,14 @@
return ArrayList(allStopsLoaded.values)
}
- fun requestStopsInBoundingBox(bb: BoundingBox) {
+ /*fun requestStopsInBoundingBox(bb: BoundingBox) {
bb.let {
Log.d(DEBUG_TAG, "Launching stop request")
oldRepo.requestStopsInArea(it.latSouth, it.latNorth, it.lonWest, it.lonEast, callback)
}
}
+
+ */
fun requestStopsInLatLng(bb: LatLngBounds) {
bb.let {
Log.d(DEBUG_TAG, "Launching stop request")
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -285,13 +285,13 @@
</array>
<string name="positions_source_pref_title">Source of real time positions for buses and trams</string>
-
+ <string name="positions_source_mato_descr">MaTO (updated more frequently, might be offline)</string>
+ <string name="positions_source_gtfsrt_descr">GTFS RT (more stable, less frequently updated)</string>
<array name="positions_source_sel">
<item>@string/positions_source_mato_descr</item>
<item>@string/positions_source_gtfsrt_descr</item>
</array>
- <string name="positions_source_mato_descr">MaTO (updated more frequently, might be offline)</string>
- <string name="positions_source_gtfsrt_descr">GTFS RT (more stable, less frequently updated)</string>
+
<string name="map_style_pref_title">Style of the map</string>
<string name="map_style_versatiles">Versatiles (vector)</string>
<string name="map_style_legacy_raster">OSM legacy (raster, lighter)</string>
@@ -299,8 +299,6 @@
<item>@string/map_style_versatiles</item>
<item>@string/map_style_legacy_raster</item>
</array>
- <string name="positions_source_mato_descr">MaTO (updated more frequently, might be offline)</string>
- <string name="positions_source_gtfsrt_descr">GTFS RT (more stable, less frequently updated)</string>
<string name="remove_all_trips">Remove trips data (free up space)</string>
<string name="all_trips_removed">All GTFS trips have been removed from the database</string>
@@ -359,4 +357,5 @@
<string name="destination_loading">Loading destination…</string>
<string name="destination_unknown">Destination unknown</string>
+
</resources>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, May 4, 07:17 (12 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1887658
Default Alt Text
D185.1777871841.diff (101 KB)
Attached To
Mode
D185: Remove OsmDroid library and related code
Attached
Detach File
Event Timeline
Log In to Comment