Page MenuHomeGitPull.it

D221.1782225596.diff
No OneTemporary

Authored By
Unknown
Size
80 KB
Referenced Files
None
Subscribers
None

D221.1782225596.diff

diff --git a/app/build.gradle b/app/build.gradle
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -134,7 +134,7 @@
implementation "androidx.coordinatorlayout:coordinatorlayout:1.3.0"
- implementation 'org.jsoup:jsoup:1.21.2'
+ implementation 'org.jsoup:jsoup:1.22.1'
implementation 'com.readystatesoftware.sqliteasset:sqliteassethelper:2.0.1'
implementation 'com.android.volley:volley:1.2.1'
//maplibre
@@ -149,14 +149,13 @@
implementation "ch.acra:acra-mail:$acra_version"
implementation "ch.acra:acra-dialog:$acra_version"
// google transit realtime
- implementation 'com.google.protobuf:protoc:4.33.0'
-
- implementation 'com.google.protobuf:protobuf-javalite:4.33.0'
+ implementation 'com.google.protobuf:protoc:4.34.1'
+ implementation 'com.google.protobuf:protobuf-javalite:4.34.1'
// mqtt library
//implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
//implementation 'com.github.hannesa2:paho.mqtt.android:4.4'
- implementation("com.hivemq:hivemq-mqtt-client:1.3.10")
- implementation(platform("com.hivemq:hivemq-mqtt-client-websocket:1.3.10"))
+ implementation("com.hivemq:hivemq-mqtt-client:1.3.13")
+ implementation(platform("com.hivemq:hivemq-mqtt-client-websocket:1.3.13"))
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
@@ -173,15 +172,9 @@
//multidex - we need this to build the app
implementation "androidx.multidex:multidex:$multidex_version"
- implementation 'de.siegmar:fastcsv:2.2.2'
+ implementation 'de.siegmar:fastcsv:4.2.0'
testImplementation 'junit:junit:4.13.2'
- implementation 'junit:junit:4.13.2'
-
- implementation "androidx.test.ext:junit:1.3.0"
- implementation "androidx.test:core:$androidXTestVersion"
- implementation "androidx.test:runner:$androidXTestVersion"
- implementation "androidx.room:room-testing:$room_version"
androidTestImplementation "androidx.test.ext:junit:1.3.0"
androidTestImplementation "androidx.test:core:$androidXTestVersion"
diff --git a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java
--- a/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java
+++ b/app/src/main/java/it/reyboz/bustorino/ActivityExperiments.java
@@ -80,7 +80,7 @@
}
@Override
- public void showLineOnMap(String routeGtfsId, @Nullable String stopIDFrom){
+ public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){
readyGUIfor(FragmentKind.LINES);
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
@@ -89,6 +89,16 @@
tr.addToBackStack("LineonMap-"+routeGtfsId);
tr.commit();
+ }
+ @Override
+ public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
+ readyGUIfor(FragmentKind.LINES);
+ FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
+ tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class,
+ LinesDetailFragment.Companion.makeArgsPattern(routeGtfsId, optionalPatternId, args));
+ tr.addToBackStack("Line-"+routeGtfsId);
+ tr.commit();
}
+
}
\ No newline at end of file
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
@@ -24,18 +24,15 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.*;
-import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.widget.Toolbar;
-import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.graphics.Insets;
import androidx.core.view.*;
import androidx.drawerlayout.widget.DrawerLayout;
@@ -729,17 +726,26 @@
mNavView.setCheckedItem(R.id.nav_arrivals);
}
@Override
- public void showLineOnMap(String routeGtfsId, @Nullable String stopIDFrom){
+ public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom){
readyGUIfor(FragmentKind.LINES);
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class,
LinesDetailFragment.Companion.makeArgs(routeGtfsId, stopIDFrom));
- tr.addToBackStack("LineonMap-"+routeGtfsId);
+ tr.addToBackStack("LineFromStop-"+routeGtfsId);
tr.commit();
+ }
+ @Override
+ public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
+ readyGUIfor(FragmentKind.LINES);
+ FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
+ tr.replace(R.id.mainActContentFrame, LinesDetailFragment.class,
+ LinesDetailFragment.Companion.makeArgsPattern(routeGtfsId, optionalPatternId, args));
+ tr.addToBackStack("LineFromOther-"+routeGtfsId);
+ tr.commit();
}
@Override
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
--- a/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/ArrivalsStopAdapter.java
@@ -286,7 +286,7 @@
final String name = r.getName();
final String destination = r.destinazione;
if (name!= null && destination!=null)
- myMap.put(new Pair<>(name.toLowerCase(Locale.ROOT).trim(),destination.toLowerCase(Locale.ROOT).trim()), i);
+ myMap.put(new Pair<>(name.toLowerCase(Locale.ROOT).trim(),destination.toLowerCase(Locale.ROOT).trim()), i);
}
return myMap;
}
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java b/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java
--- a/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/PalinaAdapter.java
@@ -264,7 +264,7 @@
void showRouteFullDirection(Route route);
/**
- * Show the line with all the stops in the app
+ * Show the line with all the stops in the line screen
* @param route partial line info
*/
void requestShowingRoute(Route route);
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java b/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/FiveTNormalizer.java
@@ -344,6 +344,11 @@
return name.replace(" ","");
}
+ /**
+ * Create the line name in GTFS format (e.g., "gtt:10U") from a more human readable name ("10")
+ * @param route the route object
+ * @return the code for the line in GTFS format
+ */
public static String getGtfsRouteID(Route route){
String routeName = route.getName();
String cutName = routeName.replace("\\s", "");
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsDataParser.java
@@ -22,8 +22,8 @@
import androidx.annotation.NonNull;
import de.siegmar.fastcsv.reader.CloseableIterator;
-import de.siegmar.fastcsv.reader.NamedCsvReader;
-import de.siegmar.fastcsv.reader.NamedCsvRow;
+import de.siegmar.fastcsv.reader.CsvReader;
+import de.siegmar.fastcsv.reader.NamedCsvRecord;
import it.reyboz.bustorino.backend.Fetcher;
import it.reyboz.bustorino.backend.networkTools;
import it.reyboz.bustorino.data.gtfs.CsvTableInserter;
@@ -195,8 +195,8 @@
//System.out.println(Arrays.toString(elements));
//lineElements = readCsvLine(header);
- NamedCsvReader csvReader = NamedCsvReader.builder().build(reader);
- CloseableIterator<NamedCsvRow> iterator = csvReader.iterator();
+ CsvReader<NamedCsvRecord> csvReader = CsvReader.builder().ofNamedCsvRecord(reader);
+ CloseableIterator<NamedCsvRecord> iterator = csvReader.iterator();
final CsvTableInserter inserter = new CsvTableInserter(tableName,con);
@@ -225,7 +225,12 @@
int c = 0;
while (iterator.hasNext()){
- final Map<String,String> rowsMap = iterator.next().getFields();
+ //final Map<String,String> rowsMap = iterator.next().getFields();
+ final NamedCsvRecord record = iterator.next();
+ final Map<String,String> rowsMap = new HashMap<>();
+ for (String col: record.getHeader()){
+ rowsMap.put(col, record.getField(col));
+ }
if (c < 1){
Log.d(DEBUG_TAG, " in map:"+rowsMap);
c++;
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/LivePositionUpdate.kt b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/LivePositionUpdate.kt
--- a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/LivePositionUpdate.kt
+++ b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/LivePositionUpdate.kt
@@ -19,11 +19,15 @@
import com.google.transit.realtime.GtfsRealtime.VehiclePosition
+/**
+ * General data class for the live position update
+ * Used in both the GTFS and MaTO services
+ */
data class LivePositionUpdate(
val tripID: String, //tripID WITHOUT THE "gtt:" prefix
val startTime: String?,
val startDate: String?,
- val routeID: String,
+ val routeID: String, // routeID DOES NOT HAVE THE "gtt:" PREFIX
val vehicle: String,
var latitude: Double,
@@ -53,23 +57,10 @@
position.timestamp,
null
)
- /*data class VehicleInfo(
- val id: String,
- val label:String
- )
-
- */
- /*fun withNewPositionAndBearing(latitude: Double, longitude: Double, bearing: Float) =
- LivePositionUpdate(this.tripID, this.startTime, this.startTime,
- this.routeID, this.vehicle, latitude, longitude, bearing,
- this.timestamp,this.nextStop)
- fun withNewPosition(latitude: Double, longitude: Double) =
- LivePositionUpdate(this.tripID, this.startTime, this.startTime,
- this.routeID, this.vehicle, latitude, longitude, this.bearing,
- this.timestamp,this.nextStop)
-
- */
+ fun getLineGTFSFormat(): String{
+ return "gtt:$routeID"
+ }
}
diff --git a/app/src/main/java/it/reyboz/bustorino/data/UserDB.java b/app/src/main/java/it/reyboz/bustorino/data/UserDB.java
--- a/app/src/main/java/it/reyboz/bustorino/data/UserDB.java
+++ b/app/src/main/java/it/reyboz/bustorino/data/UserDB.java
@@ -33,7 +33,7 @@
import androidx.annotation.Nullable;
import de.siegmar.fastcsv.reader.CloseableIterator;
import de.siegmar.fastcsv.reader.CsvReader;
-import de.siegmar.fastcsv.reader.CsvRow;
+import de.siegmar.fastcsv.reader.CsvRecord;
import de.siegmar.fastcsv.writer.CsvWriter;
import it.reyboz.bustorino.backend.Stop;
import it.reyboz.bustorino.backend.StopsDBInterface;
@@ -342,27 +342,27 @@
Cursor cursor = db.query(TABLE_NAME, getFavoritesColumnNamesAsArray,null,null,null,null, sortOrder);
final int nCols = 2;//cursor.getColumnCount();
- writer.writeRow(cursor.getColumnNames());
+ writer.writeRecord(cursor.getColumnNames());
while (cursor.moveToNext()){
String[] arr = {cursor.getString(0), cursor.getString(1)};
- writer.writeRow(arr);
+ writer.writeRecord(arr);
}
cursor.close();
return true;
}
- public int insertRowsFromCSV(CsvReader reader){
+ public int insertRowsFromCSV(CsvReader<CsvRecord> reader){
SQLiteDatabase db = this.getWritableDatabase();
boolean firstrow = true;
final HashMap<String,Integer> colIndexByRows = new HashMap<>();
- final CloseableIterator<CsvRow> rowsIter = reader.iterator();
+ final CloseableIterator<CsvRecord> rowsIter = reader.iterator();
if (!rowsIter.hasNext()){
//nothing to do, it's an empty file
return -1;
}
- final CsvRow firstRow = rowsIter.next();
+ final CsvRecord firstRow = rowsIter.next();
// close if there isn't another rows
if(!rowsIter.hasNext()) return -2;
for (int i =0; i<firstRow.getFieldCount(); i++){
@@ -378,7 +378,7 @@
final int col_id = colIndexByRows.get(COL_ID);
final int col_username = colIndexByRows.get(COL_USERNAME);
while (rowsIter.hasNext()){
- final CsvRow row = rowsIter.next();
+ final CsvRecord row = rowsIter.next();
final ContentValues cv = new ContentValues();
cv.put(COL_ID, row.getField(col_id));
cv.put(COL_USERNAME, row.getField(col_username));
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt
@@ -122,11 +122,11 @@
DEBUG_TAG, """Need to show line for route: gtfsID ${route.gtfsId} name ${route.name}"""
)
if (route.gtfsId != null) {
- mListener.showLineOnMap(route.gtfsId, stopID)
+ mListener.openLineFromStop(route.gtfsId, stopID)
} else {
val gtfsID = FiveTNormalizer.getGtfsRouteID(route)
Log.d(DEBUG_TAG, "GtfsID for route is: $gtfsID")
- mListener.showLineOnMap(gtfsID, stopID)
+ mListener.openLineFromStop(gtfsID, stopID)
}
}
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/BackupImportFragment.kt
@@ -50,9 +50,7 @@
Toast.makeText(context, R.string.message_check_at_least_one, Toast.LENGTH_SHORT).show()
}
else if (result.resultCode == Activity.RESULT_OK) {
-
result.data?.data?.also { uri ->
-
loadZipData(uri,loadFavorites, loadPreferences)
}
}
@@ -198,7 +196,7 @@
FAVORITES_NAME -> if (loadFavorites) {
val reader = InputStreamReader(zipstream)
- val csvReader = CsvReader.builder().build(reader)
+ val csvReader = CsvReader.builder().ofCsvRecord(reader)
val userDB = UserDB(context)
val updated = userDB.insertRowsFromCSV(csvReader)
@@ -258,7 +256,7 @@
val contentResolver = context.contentResolver
contentResolver.openInputStream(uri)?.use {
InputStreamReader(it).use { stream ->
- val csvReader = CsvReader.builder().build(stream)
+ val csvReader = CsvReader.builder().ofCsvRecord(stream)
val userDB = UserDB(context)
val updated = userDB.insertRowsFromCSV(csvReader)
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java b/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/CommonFragmentListener.java
@@ -1,5 +1,6 @@
package it.reyboz.bustorino.fragments;
+import android.os.Bundle;
import androidx.annotation.Nullable;
import it.reyboz.bustorino.backend.Stop;
@@ -37,8 +38,16 @@
void showMapCenteredOnStop(Stop stop);
/**
- * We want to show the line in detail for route
+ * We want to show the line in detail for route coming from a stop
* @param routeGtfsId the route gtfsID (eg, "gtt:10U")
*/
- void showLineOnMap(String routeGtfsId,@Nullable String fromStopID);
+ void openLineFromStop(String routeGtfsId, @Nullable String fromStopID);
+
+ /**
+ * Open the line screen on the line, from a live vehicle (optional pattern)
+ * @param routeGtfsId the route gtfsID (eg, "gtt:10U")
+ * @param optionalPatternId the pattern name (can be null)
+ * @param args extra arguments given as Bundle
+ */
+ void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args);
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/GeneralMapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/GeneralMapLibreFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/GeneralMapLibreFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/GeneralMapLibreFragment.kt
@@ -15,27 +15,33 @@
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
+import android.widget.ImageButton
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.cardview.widget.CardView
+import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
+import androidx.fragment.app.activityViewModels
+import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.gson.JsonObject
import it.reyboz.bustorino.R
import it.reyboz.bustorino.backend.LivePositionTripPattern
+import it.reyboz.bustorino.backend.LivePositionsServiceStatus
import it.reyboz.bustorino.backend.Stop
import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
import it.reyboz.bustorino.data.PreferencesHolder
import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
import it.reyboz.bustorino.map.MapLibreUtils
import it.reyboz.bustorino.util.ViewUtils
+import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
+import it.reyboz.bustorino.viewmodels.MapStateViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.maplibre.android.MapLibre
-import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.location.LocationComponent
import org.maplibre.android.location.LocationComponentOptions
@@ -61,7 +67,7 @@
abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback {
protected var map: MapLibreMap? = null
protected var shownStopInBottomSheet : Stop? = null
- protected var savedMapStateOnPause : Bundle? = null
+ //protected var savedMapStateOnPause : Bundle? = null
protected var fragmentListener: CommonFragmentListener? = null
@@ -98,8 +104,9 @@
protected lateinit var arrivalsCard: CardView
protected lateinit var directionsCard: CardView
protected lateinit var bottomrightImage: ImageView
-
protected lateinit var locationComponent: LocationComponent
+ protected lateinit var busPositionsIconButton: ImageButton
+
protected var lastLocation : Location? = null
@@ -115,9 +122,13 @@
//extra items to use the LibreMap
- protected lateinit var symbolManager : SymbolManager
+ protected var symbolManager : SymbolManager? = null
protected var stopActiveSymbol: Symbol? = null
protected var stopsLayerStarted = false
+ protected val livePositionsViewModel : LivePositionsViewModel by activityViewModels()
+
+ //private lateinit var symbolManager: SymbolManager
+ protected val mapStateViewModel: MapStateViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
@@ -211,6 +222,7 @@
} else throw RuntimeException("$context must implement CommonFragmentListener")
}
+ /*
protected fun restoreMapStateFromBundle(bundle: Bundle): Boolean{
val nullDouble = -10_000.0
var boundsRestored =false
@@ -266,6 +278,8 @@
return b
}
+ */
+
protected fun stopToGeoJsonFeature(s: Stop): Feature{
return Feature.fromGeometry(
Point.fromLngLat(s.longitude!!, s.latitude!!),
@@ -299,15 +313,15 @@
}
}
if (vehShowing==v){
- hideStopBottomSheet()
+ hideStopOrBusBottomSheet()
}
}
}
// Hide the bottom sheet and remove extra symbol
- protected fun hideStopBottomSheet(){
+ protected fun hideStopOrBusBottomSheet(){
if (stopActiveSymbol!=null){
- symbolManager.delete(stopActiveSymbol)
+ symbolManager?.delete(stopActiveSymbol)
stopActiveSymbol = null
}
if(!showOpenStopWithSymbolLayer()){
@@ -327,18 +341,30 @@
}
protected fun initSymbolManager(mapReady: MapLibreMap , style: Style){
- symbolManager = SymbolManager(mapView,mapReady,style)
- symbolManager.iconAllowOverlap = true
- symbolManager.textAllowOverlap = false
-
- symbolManager.addClickListener{ _ ->
- if (stopActiveSymbol!=null){
- hideStopBottomSheet()
-
+ val sm = SymbolManager(mapView, mapReady, style)
+ sm.iconAllowOverlap = true
+ sm.textAllowOverlap = false
+ sm.addClickListener { _ ->
+ if (stopActiveSymbol != null) {
+ hideStopOrBusBottomSheet()
return@addClickListener true
} else
return@addClickListener false
}
+ symbolManager = sm
+ }
+
+ /**
+ * Change the icon indicating the status of the live Positions
+ */
+ protected fun setBusPositionsIcon(enabled: Boolean, error: Boolean){
+ val ctx = requireContext()
+ if(!enabled)
+ busPositionsIconButton.setImageDrawable(ContextCompat.getDrawable(ctx, R.drawable.bus_pos_circle_inactive))
+ else if(error)
+ busPositionsIconButton.setImageDrawable(ContextCompat.getDrawable(ctx, R.drawable.bus_pos_circle_notworking))
+ else
+ busPositionsIconButton.setImageDrawable(ContextCompat.getDrawable(ctx, R.drawable.bus_pos_circle_active))
}
@@ -644,14 +670,13 @@
Log.d(DEBUG_TAG, "Showing stop: ${stop.ID}")
if (showOpenStopWithSymbolLayer()) {
- stopActiveSymbol = symbolManager.create(
+ stopActiveSymbol = symbolManager?.create(
SymbolOptions()
.withLatLng(LatLng(stop.latitude!!, stop.longitude!!))
.withIconImage(STOP_ACTIVE_IMG)
.withIconAnchor(ICON_ANCHOR_CENTER)
-
)
- } else{
+ } else {
val list = ArrayList<Feature>()
list.add(stopToGeoJsonFeature(stop))
selectedStopSource.setGeoJson(
@@ -769,15 +794,26 @@
}
style.addLayerAbove(busesLayer, STOPS_LAYER_ID)
- val selectedBusLayer = SymbolLayer(SEL_BUS_LAYER, SEL_BUS_SOURCE).withProperties(
+ val selectedBusLayer = SymbolLayer(SEL_BUS_LAYER, SEL_BUS_SOURCE).apply {
+ withProperties(
PropertyFactory.iconImage(BUS_SEL_IMAGE_ID),
PropertyFactory.iconSize(busIconsScale),
PropertyFactory.iconAllowOverlap(true),
PropertyFactory.iconIgnorePlacement(true),
PropertyFactory.iconRotate(Expression.get("bearing")),
PropertyFactory.iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP)
+ )
+ if (withLabels){
+ withProperties(PropertyFactory.textAnchor(TEXT_ANCHOR_CENTER),
+ PropertyFactory.textAllowOverlap(true),
+ PropertyFactory.textField(Expression.get("line")),
+ PropertyFactory.textColor(Color.WHITE),
+ PropertyFactory.textRotationAlignment(TEXT_ROTATION_ALIGNMENT_VIEWPORT),
+ PropertyFactory.textSize(12f),
+ PropertyFactory.textFont(arrayOf("noto_sans_regular")))
+ }
+ }
- )
style.addLayerAbove(selectedBusLayer, BUSES_LAYER_ID)
}
@@ -791,6 +827,35 @@
return locManager.allProviders.contains(LocationManager.GPS_PROVIDER)
}
+ /**
+ * Update automatically the icon when the live position service changes status
+ */
+ protected fun observeStatusLivePositions(){
+ livePositionsViewModel.serviceStatus.observe(viewLifecycleOwner){ status ->
+ //if service is active, update the bus positions icon
+ when(status) {
+ LivePositionsServiceStatus.OK ->
+ setBusPositionsIcon(true, error = false)
+
+ LivePositionsServiceStatus.NO_POSITIONS -> setBusPositionsIcon(true, error = true)
+
+ else -> setBusPositionsIcon( true, error = true)
+ }
+ }
+ }
+
+ /**
+ * Clear all buses from the map
+ */
+ protected fun clearAllBusPositionsInMap(){
+ for ((k, anim) in animatorsByVeh){
+ anim.cancel()
+ }
+ animatorsByVeh.clear()
+ updatesByVehDict.clear()
+ updatePositionsIcons(forced = false)
+ }
+
companion object{
private const val DEBUG_TAG="GeneralMapLibreFragment"
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
@@ -36,7 +36,6 @@
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
-import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
@@ -59,7 +58,7 @@
import it.reyboz.bustorino.middleware.LocationUtils
import it.reyboz.bustorino.util.Permissions
import it.reyboz.bustorino.viewmodels.LinesViewModel
-import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
+import it.reyboz.bustorino.viewmodels.MapStateViewModel
import kotlinx.coroutines.Runnable
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
@@ -102,7 +101,6 @@
private var patternShown: MatoPatternWithStops? = null
private val viewModel: LinesViewModel by viewModels()
- private val mapViewModel: MapViewModel by viewModels()
private var firstInit = true
private var pausedFragment = false
private lateinit var switchButton: ImageButton
@@ -136,7 +134,8 @@
private lateinit var stopsRecyclerView: RecyclerView
private lateinit var descripTextView: TextView
- private var stopIDFromToShow: String? = null
+ private var stopIDFromToShow = ""
+ private var patternIdToShow = ""
//adapter for recyclerView
private val stopAdapterListener= object : StopAdapterListener {
override fun onTappedStop(stop: Stop?) {
@@ -205,7 +204,8 @@
private var showOnTopOfLine = false
private var recyclerInitDone = false
- private var useMQTTPositions = true
+ private var usingMQTTPositions = true
+ private var restoredCameraInMap = false
@@ -213,15 +213,14 @@
private val tripMarkersAnimators = HashMap<String, ObjectAnimator>()
- private val liveBusViewModel: LivePositionsViewModel by activityViewModels()
-
//extra items to use the LibreMap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = requireArguments()
lineID = args.getString(LINEID_KEY,"")
- stopIDFromToShow = args.getString(STOPID_FROM_KEY)
+ stopIDFromToShow = args.getString(STOPID_FROM_KEY, "") //can be null
+ patternIdToShow = args.getString(PATTERN_SHOW_KEY, "")
}
@SuppressLint("SetTextI18n")
@@ -239,9 +238,15 @@
//lineID = requireArguments().getString(LINEID_KEY, "")
arguments?.let {
lineID = it.getString(LINEID_KEY, "")
+ stopIDFromToShow = it.getString(STOPID_FROM_KEY, "") //can be null
+ patternIdToShow = it.getString(PATTERN_SHOW_KEY, "")
+ Log.d(DEBUG_TAG, "LineID selected: $lineID, stopIDFromToShow: $stopIDFromToShow, patternIdToShow: $patternIdToShow")
}
+
switchButton = rootView.findViewById(R.id.switchImageButton)
locationIcon = rootView.findViewById(R.id.locationEnableIcon)
+ busPositionsIconButton = rootView.findViewById(R.id.busPositionsImageButton)
+
favoritesButton = rootView.findViewById(R.id.favoritesButton)
stopsRecyclerView = rootView.findViewById(R.id.patternStopsRecyclerView)
descripTextView = rootView.findViewById(R.id.lineDescripTextView)
@@ -254,7 +259,7 @@
// Setup close button
rootView.findViewById<View>(R.id.btnClose).setOnClickListener {
- hideStopBottomSheet()
+ hideStopOrBusBottomSheet()
}
val titleTextView = rootView.findViewById<TextView>(R.id.titleTextView)
@@ -295,13 +300,46 @@
//set click Listener
view.setOnClickListener(this::onPositionIconButtonClick)
}
+ busPositionsIconButton.setOnClickListener {
+ LivePositionsDialogFragment().show(parentFragmentManager, "LivePositionsDialog")
+ }
//set
//INITIALIZE VIEW MODELS
viewModel.setRouteIDQuery(lineID)
- liveBusViewModel.setGtfsLineToFilterPos(lineID, null)
+ livePositionsViewModel.setGtfsLineToFilterPos(lineID, null)
+ //observe the change, clear buses when switching position
+ livePositionsViewModel.useMQTTPositionsLiveData.observe(viewLifecycleOwner){ useMQTT->
+ //Log.d(DEBUG_TAG, "Changed MQTT positions, now have to use MQTT: $useMQTT")
+ if (isResumed) {
+ //Log.d(DEBUG_TAG, "Deciding to switch, the current source is using MQTT: $usingMQTTPositions")
+ if(useMQTT!=usingMQTTPositions){
+ // we have to switch
+ val clearPos = PreferenceManager.getDefaultSharedPreferences(requireContext()).getBoolean("positions_clear_on_switch_pref", true)
+ livePositionsViewModel.clearOldPositionsUpdates()
+ if(useMQTT){
+ //switching to MQTT, the GTFS positions are disabled automatically
+ livePositionsViewModel.requestMatoPosUpdates(GtfsUtils.getLineNameFromGtfsID(lineID))
+ } else{
+ //switching to GTFS RT: stop Mato, launch first request
+ livePositionsViewModel.stopMatoUpdates()
+ livePositionsViewModel.requestGTFSUpdates()
+ }
+ Log.d(DEBUG_TAG, "Should clear positions: $clearPos")
+ if (clearPos) {
+ livePositionsViewModel.clearAllPositions()
+ //force clear of the viewed data
+ if(vehShowing.isNotEmpty()) hideStopOrBusBottomSheet()
+ clearAllBusPositionsInMap()
+ }
+
+ }
+ }
+ usingMQTTPositions = useMQTT
+
+ }
val keySourcePositions = getString(R.string.pref_positions_source)
- useMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ usingMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getString(keySourcePositions, "mqtt").contentEquals("mqtt")
viewModel.patternsWithStopsByRouteLiveData.observe(viewLifecycleOwner){
@@ -313,6 +351,8 @@
if(mapView.visibility ==View.VISIBLE)
patternShown?.let{
// We have the pattern and the stops here, time to display them
+ //TODO: Decide if we should follow the camera view given by the previous screen (probably the map fragment)
+ // use !restoredCameraInMap to do so
displayPatternWithStopsOnMap(it,stops, true)
} ?:{
Log.w(DEBUG_TAG, "The viewingPattern is null!")
@@ -353,11 +393,11 @@
stopAnimations()
updatesByVehDict.clear()
updatePositionsIcons(true)
- liveBusViewModel.retriggerPositionUpdate()
+ livePositionsViewModel.retriggerPositionUpdate()
}
}
}
- liveBusViewModel.setGtfsLineToFilterPos(lineID, patternWithStops.pattern)
+ livePositionsViewModel.setGtfsLineToFilterPos(lineID, patternWithStops.pattern)
}
@@ -366,6 +406,8 @@
}
Log.d(DEBUG_TAG, "Views created!")
+ observeStatusLivePositions()
+
return rootView
}
@@ -375,14 +417,15 @@
mapView.visibility = View.GONE
stopsRecyclerView.visibility = View.VISIBLE
locationIcon?.visibility = View.GONE
+ busPositionsIconButton?.visibility = View.GONE
viewModel.setMapShowing(false)
- if(useMQTTPositions) liveBusViewModel.stopMatoUpdates()
+ if(usingMQTTPositions) livePositionsViewModel.stopMatoUpdates()
//map.overlayManager.remove(busPositionsOverlay)
switchButton.setImageDrawable(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_map_white_30))
- hideStopBottomSheet()
+ hideStopOrBusBottomSheet()
if(locationComponent.isLocationComponentEnabled){
locationComponent.isLocationComponentEnabled = false
@@ -395,14 +438,16 @@
stopsRecyclerView.visibility = View.GONE
mapView.visibility = View.VISIBLE
locationIcon?.visibility = View.VISIBLE
+ busPositionsIconButton.visibility = View.VISIBLE
+
viewModel.setMapShowing(true)
//map.overlayManager.add(busPositionsOverlay)
//map.
- if(useMQTTPositions)
- liveBusViewModel.requestMatoPosUpdates(GtfsUtils.getLineNameFromGtfsID(lineID))
+ if(usingMQTTPositions)
+ livePositionsViewModel.requestMatoPosUpdates(GtfsUtils.getLineNameFromGtfsID(lineID))
else
- liveBusViewModel.requestGTFSUpdates()
+ livePositionsViewModel.requestGTFSUpdates()
switchButton.setImageDrawable(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_list_30))
@@ -469,6 +514,7 @@
*/
override fun onMapReady(mapReady: MapLibreMap) {
this.map = mapReady
+ var setViewAlready = false
val context = requireContext()
val mjson = MapLibreStyles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context)) //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
@@ -491,11 +537,6 @@
setupBusLayer(style)
initSymbolManager(mapReady, style)
-
- mapViewModel.stopShowing?.let {
- openStopInBottomSheet(it)
- }
- mapViewModel.stopShowing = null
toRunWhenMapReady?.run()
toRunWhenMapReady = null
mapInitialized.set(true)
@@ -504,9 +545,33 @@
viewModel.stopsForPatternLiveData.value?.let {
Log.d(DEBUG_TAG, "Show stops from the cache")
displayPatternWithStopsOnMap(patternShown!!, it, true)
+ //Show stop from cache
+ mapStateViewModel.lastOpenStopID.value?.let{ sID->
+ val s= it.filter { stop -> stop.ID==sID }
+ if (s.isEmpty()) {
+ if(sID.isNotEmpty())
+ Log.w(DEBUG_TAG,"Wanted to open stop $sID in map but it was not loaded!")
+ }
+ else openStopInBottomSheet(s[0])
+
+ }
}
+
+
}
+ var restoredMapState = mapStateViewModel.restoreMapState(mapReady)
+ arguments?.let { args ->
+ // if there is a Camera State in the arguments, set it for the new camera (doesn't work yet!)
+ if (!restoredMapState && MapCameraState.checkInBundle(args)) {
+ val initCamState = MapCameraState.fromBundle(args)
+ //map?.let{
+ MapStateViewModel.restoreMapState(mapReady, initCamState)
+ setViewAlready = true
+ restoredMapState = true
+ }
+ }
+ restoredCameraInMap = restoredMapState
}
mapReady.addOnMapClickListener { point ->
@@ -521,7 +586,7 @@
val stop = viewModel.getStopByID(id)
stop?.let {
if (isBottomSheetShowing() || vehShowing.isNotEmpty()){
- hideStopBottomSheet()
+ hideStopOrBusBottomSheet()
}
openStopInBottomSheet(it)
@@ -535,7 +600,7 @@
val vehid = feature.getStringProperty("veh")
val route = feature.getStringProperty("line")
if(isBottomSheetShowing())
- hideStopBottomSheet()
+ hideStopOrBusBottomSheet()
//if(context!=null){
// Toast.makeText(context, "Veh $vehid on route ${route.slice(0..route.length-2)}", Toast.LENGTH_SHORT).show()
//}
@@ -557,17 +622,10 @@
observeBusPositionUpdates()
}
- /*savedMapStateOnPause?.let{
- restoreMapStateFromBundle(it)
- pendingLocationActivation = false
- Log.d(DEBUG_TAG, "Restored map state from the saved bundle")
- }
-
- */
val zoom = 12.0
val latlngTarget = LatLng(MapLibreFragment.DEFAULT_CENTER_LAT, MapLibreFragment.DEFAULT_CENTER_LON)
-
+ if(!setViewAlready)
mapReady.cameraPosition = savedCameraPosition ?:CameraPosition.Builder().target(latlngTarget).zoom(zoom).build()
savedCameraPosition = null
@@ -580,8 +638,10 @@
}
private fun observeBusPositionUpdates(){
+
+
//live bus positions
- liveBusViewModel.filteredLocationUpdates.observe(viewLifecycleOwner){ pair ->
+ livePositionsViewModel.filteredLocationUpdates.observe(viewLifecycleOwner){ pair ->
//Log.d(DEBUG_TAG, "Received ${updates.size} updates for the positions")
val updates = pair.first
val vehiclesNotOnCorrectDir = pair.second
@@ -596,13 +656,13 @@
showVehicleTripInBottomSheet(veh)
}
//if not using MQTT positions
- if(!useMQTTPositions){
- liveBusViewModel.requestDelayedGTFSUpdates(2000)
+ if(!usingMQTTPositions){
+ livePositionsViewModel.requestDelayedGTFSUpdates(2000)
}
}
//download missing tripIDs
- liveBusViewModel.tripsGtfsIDsToQuery.observe(viewLifecycleOwner){
+ livePositionsViewModel.tripsGtfsIDsToQuery.observe(viewLifecycleOwner){
//gtfsPosViewModel.downloadTripsFromMato(dat);
MatoTripsDownloadWorker.requestMatoTripsDownload(
it, requireContext().applicationContext,
@@ -730,29 +790,29 @@
initStopsLayer(style, null, POLY_ARROWS_LAYER)
}
+ private fun filterPatternFromArgs(patterns: List<MatoPatternWithStops>): MatoPatternWithStops?{
+ var p: MatoPatternWithStops? = null
- /**
- * Save the loaded pattern data, without the stops!
- */
- private fun savePatternsToShow(patterns: List<MatoPatternWithStops>){
-
- currentPatterns = patterns.sortedWith(patternsSorter)
-
- patternsAdapter?.let {
- it.clear()
- it.addAll(currentPatterns.map { p->"${p.pattern.directionId} - ${p.pattern.headsign}" })
- it.notifyDataSetChanged()
+ if (patternIdToShow.isNotEmpty()){
+ for (patt in currentPatterns) {
+ if (patt.pattern.code == patternIdToShow){
+ p = patt
+ }
+ }
+ if(p==null)
+ Log.w(DEBUG_TAG, "We had to show the pattern with code $patternIdToShow, but we didn't find it")
+ else
+ Log.d(DEBUG_TAG, "Requesting to show pattern with code $patternIdToShow, found pattern ${p.pattern.code}")
}
// if we are loading from a stop, find it
- val patternToShow = stopIDFromToShow?.let { sID ->
- val stopGtfsID = "gtt:$sID"
- var p: MatoPatternWithStops? = null
+ else if(stopIDFromToShow.isNotEmpty()) {
+ val stopGtfsID = "gtt:$stopIDFromToShow"
var pLength = 0
- for(patt in currentPatterns){
- for(pstop in patt.stopsIndices){
- if(pstop.stopGtfsId == stopGtfsID){
+ for (patt in currentPatterns) {
+ for (pstop in patt.stopsIndices) {
+ if (pstop.stopGtfsId == stopGtfsID) {
//found
- if (patt.stopsIndices.size>pLength){
+ if (patt.stopsIndices.size > pLength) {
p = patt
pLength = patt.stopsIndices.size
}
@@ -761,20 +821,32 @@
}
}
}
- p
- }
- if(stopIDFromToShow!=null){
- if(patternToShow==null)
+ if(p==null)
Log.w(DEBUG_TAG, "We had to show the pattern from stop $stopIDFromToShow, but we didn't find it")
else
- Log.d(DEBUG_TAG, "Requesting to show pattern from stop $stopIDFromToShow, found pattern ${patternToShow.pattern.code}")
+ Log.d(DEBUG_TAG, "Requesting to show pattern from stop $stopIDFromToShow, found pattern ${p.pattern.code}")
}
- //unset the stopID to show
- if(patternToShow!=null) {
+ stopIDFromToShow = ""
+ patternIdToShow = ""
+ return p
+ }
+ /**
+ * Save the loaded pattern data, without the stops!
+ */
+ private fun savePatternsToShow(patterns: List<MatoPatternWithStops>){
+
+ currentPatterns = patterns.sortedWith(patternsSorter)
+
+ patternsAdapter?.let {
+ it.clear()
+ it.addAll(currentPatterns.map { p->"${p.pattern.directionId} - ${p.pattern.headsign}" })
+ it.notifyDataSetChanged()
+ }
+ val patternToShow = filterPatternFromArgs(patterns)
+ if(patternToShow!=null) {
//showPattern(patternToShow)
patternShown = patternToShow
- stopIDFromToShow = null
}
patternShown?.let {
showPattern(it)
@@ -812,6 +884,9 @@
//setPatternAndReqStops(patternWs)
}
+ /**
+ * Zoom on the map to get the pattern
+ */
private fun zoomToCurrentPattern(){
if(polyline==null) return
val NULL_VALUE = -4000.0
@@ -927,27 +1002,12 @@
Log.e(DEBUG_TAG, "Stops layer is not started!!")
}
- /* OLD CODE
- for(s in stops){
- val gp =
- val marker = MarkerUtils.makeMarker(
- gp, s.ID, s.stopDefaultName,
- s.routesThatStopHereToString(),
- map,stopTouchResponder, stopIcon,
- R.layout.linedetail_stop_infowindow,
- R.color.line_drawn_poly
- )
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- stopsOverlay.add(marker)
- }
- */
//POINTS LIST IS NOT IN ORDER ANY MORE
//if(!map.overlayManager.contains(stopsOverlay)){
// map.overlayManager.add(stopsOverlay)
//}
if(zoomToPattern) zoomToCurrentPattern()
- //map.invalidate()
}
private fun initializeRecyclerView(){
@@ -999,31 +1059,16 @@
pausedFragment = false
val keySourcePositions = getString(R.string.pref_positions_source)
- useMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ usingMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getString(keySourcePositions, "mqtt").contentEquals("mqtt")
//separate paths
- if(useMQTTPositions)
- liveBusViewModel.requestMatoPosUpdates(GtfsUtils.getLineNameFromGtfsID(lineID))
+ if(usingMQTTPositions)
+ livePositionsViewModel.requestMatoPosUpdates(GtfsUtils.getLineNameFromGtfsID(lineID))
else
- liveBusViewModel.requestGTFSUpdates()
-
-
- if(mapViewModel.currentLat.value!=MapViewModel.INVALID) {
- Log.d(DEBUG_TAG, "mapViewModel posi: ${mapViewModel.currentLat.value}, ${mapViewModel.currentLong.value}"+
- " zoom ${mapViewModel.currentZoom.value}")
- //THIS WAS A FIX FOR THE OLD OSMDROID MAP
- /*val controller = map.controller
- viewLifecycleOwner.lifecycleScope.launch {
- delay(100)
- Log.d(DEBUG_TAG, "zooming back to point")
- controller.animateTo(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!),
- mapViewModel.currentZoom.value!!,null,null)
- //controller.setCenter(GeoPoint(mapViewModel.currentLat.value!!, mapViewModel.currentLong.value!!))
- //controller.setZoom(mapViewModel.currentZoom.value!!)
- }
- */
- }
+ livePositionsViewModel.requestGTFSUpdates()
+
+
//initialize GUI here
fragmentListener?.readyGUIfor(FragmentKind.LINES)
@@ -1032,24 +1077,19 @@
override fun onPause() {
super.onPause()
mapView.onPause()
- if(useMQTTPositions) liveBusViewModel.stopMatoUpdates()
+ if(usingMQTTPositions) livePositionsViewModel.stopMatoUpdates()
pausedFragment = true
//save map
- val camera = map?.cameraPosition
- camera?.let {cam->
- mapViewModel.currentLat.value = cam.target?.latitude ?: -400.0
- mapViewModel.currentLong.value = cam.target?.longitude ?: -400.0
- mapViewModel.currentZoom.value = cam.zoom
+ map?.let{
+ //if map is initialized
+ mapStateViewModel.saveMapState(it)
}
-
+ mapStateViewModel.lastOpenStopID.postValue(shownStopInBottomSheet?.ID)
}
override fun onStop() {
super.onStop()
mapView.onStop()
- shownStopInBottomSheet?.let {
- mapViewModel.stopShowing = it
- }
shouldMapLocationBeReactivated = locationComponent.isLocationComponentEnabled
}
@@ -1093,6 +1133,7 @@
companion object {
private const val LINEID_KEY="lineID"
private const val STOPID_FROM_KEY="stopID"
+ private const val PATTERN_SHOW_KEY ="patternIDShow"
private const val DEBUG_TAG="BusTO-LineDetalFragment"
@@ -1103,6 +1144,14 @@
b.putString(STOPID_FROM_KEY, stopIDFrom)
return b
}
+
+ fun makeArgsPattern(lineID: String, patternShow: String?, extraArgs: Bundle?): Bundle {
+
+ val b= extraArgs ?: Bundle()
+ b.putString(LINEID_KEY, lineID)
+ b.putString(PATTERN_SHOW_KEY, patternShow)
+ return b
+ }
fun newInstance(lineID: String?, stopIDFrom: String?) = LinesDetailFragment().apply {
lineID?.let { arguments = makeArgs(it, stopIDFrom) }
}
@@ -1139,8 +1188,4 @@
private const val DEFAULT_CENTER_LAT = 45.12
private const val DEFAULT_CENTER_LON = 7.6858
}
-
- enum class BottomShowing{
- STOP, VEHICLE
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
--- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesGridShowingFragment.kt
@@ -65,7 +65,7 @@
}
private val routeClickListener = RouteAdapter.ItemClicker {
- fragmentListener.showLineOnMap(it.gtfsId, null)
+ fragmentListener.openLineFromStop(it.gtfsId, null)
}
private val arrows = HashMap<String, ImageView>()
private val durations = HashMap<String, Long>()
@@ -158,7 +158,7 @@
//create new item click listener every time
val adapter = RouteOnlyLineAdapter(routesNames){ pos, _ ->
val r = routes[pos]
- fragmentListener.showLineOnMap(r.gtfsId, null)
+ fragmentListener.openLineFromStop(r.gtfsId, null)
}
favoritesRecyclerView.adapter = adapter
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/MainScreenFragment.java
@@ -725,9 +725,14 @@
}
@Override
- public void showLineOnMap(String routeGtfsId, @Nullable String stopIDFrom) {
+ public void openLineFromStop(String routeGtfsId, @Nullable String stopIDFrom) {
//pass to activity
- mListener.showLineOnMap(routeGtfsId, stopIDFrom);
+ mListener.openLineFromStop(routeGtfsId, stopIDFrom);
+ }
+
+ @Override
+ public void openLineFromVehicle(String routeGtfsId, @Nullable String optionalPatternId, @Nullable Bundle args) {
+ mListener.openLineFromVehicle(routeGtfsId, optionalPatternId, args);
}
@Override
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
@@ -4,6 +4,7 @@
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
+import android.content.res.ColorStateList
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
@@ -15,16 +16,19 @@
import android.widget.ImageButton
import android.widget.RelativeLayout
import android.widget.Toast
+import it.reyboz.bustorino.backend.FiveTNormalizer
+import it.reyboz.bustorino.backend.gtfs.GtfsUtils
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
+import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment
-import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
+import androidx.room.concurrent.AtomicBoolean
import com.google.android.material.bottomsheet.BottomSheetBehavior
import it.reyboz.bustorino.R
-import it.reyboz.bustorino.backend.LivePositionsServiceStatus
import it.reyboz.bustorino.backend.Stop
import it.reyboz.bustorino.backend.gtfs.LivePositionUpdate
import it.reyboz.bustorino.backend.mato.MQTTMatoClient
@@ -32,7 +36,6 @@
import it.reyboz.bustorino.data.gtfs.TripAndPatternWithStops
import it.reyboz.bustorino.map.MapLibreStyles
import it.reyboz.bustorino.util.Permissions
-import it.reyboz.bustorino.viewmodels.LivePositionsViewModel
import it.reyboz.bustorino.viewmodels.StopsMapViewModel
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
@@ -60,8 +63,6 @@
private val stopsViewModel: StopsMapViewModel by viewModels()
private var stopsShowing = ArrayList<Stop>(0)
- //private lateinit var symbolManager: SymbolManager
-
// Sources for stops and buses are in GeneralMapLibreFragment
private var isUserMovingCamera = false
@@ -78,10 +79,15 @@
private lateinit var showUserPositionButton: ImageButton
private lateinit var centerUserButton: ImageButton
private lateinit var followUserButton: ImageButton
+
private var followingUserLocation = false
private var pendingLocationActivation = false
private var ignoreCameraMovementForFollowing = true
private var enablingPositionFromClick = false
+ private var restoredMapCamera = AtomicBoolean()
+ private var permissionsGranted = false
+
+ //TODO: Rewrite this mess using LocationEngineProvider in MapLibre
private val positionRequestLauncher = registerForActivityResult<Array<String>, Map<String, Boolean>>(
ActivityResultContracts.RequestMultiplePermissions(), ActivityResultCallback { result ->
if (result == null) {
@@ -92,9 +98,9 @@
} 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
- Log.d(DEBUG_TAG, "HAVE THE PERMISSIONS")
+ permissionsGranted = true
if (context == null || requireContext().getSystemService(Context.LOCATION_SERVICE) == null)
- return@ActivityResultCallback ///@registerForActivityResult
+ return@ActivityResultCallback
val locationManager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
var lastLoc = stopsViewModel.lastUserLocation
@SuppressLint("MissingPermission")
@@ -107,6 +113,7 @@
}
else if (lastLoc != null) {
+
if(LatLng(lastLoc.latitude, lastLoc.longitude).distanceTo(DEFAULT_LATLNG) <= MAX_DIST_KM*1000){
Log.d(DEBUG_TAG, "Showing the user position")
setMapLocationEnabled(true, true, false)
@@ -138,10 +145,7 @@
//BUS POSITIONS
private var usingMQTTPositions = true // THIS IS INSIDE VIEW MODEL NOW
- private val livePositionsViewModel : LivePositionsViewModel by activityViewModels()
- private lateinit var busPositionsIconButton: ImageButton
- //private var busLabelSymbolsByVeh = HashMap<String,Symbol>()
private val symbolsToUpdate = ArrayList<Symbol>()
private var initialStopToShow : Stop? = null
@@ -178,13 +182,8 @@
// Init the MapView
mapView = rootView.findViewById(R.id.libreMapView)
- val restoreBundle = stopsViewModel.savedState
- if(restoreBundle!=null){
- mapView.onCreate(restoreBundle)
- } else mapView.onCreate(savedInstanceState)
- mapView.getMapAsync(this) //{ //map ->
- //map.setStyle("https://demotiles.maplibre.org/style.json") }
-
+ mapView.onCreate(savedInstanceState)
+ mapView.getMapAsync(this)
//init bottom sheet
val bottomSheet = rootView.findViewById<RelativeLayout>(R.id.bottom_sheet)
@@ -243,24 +242,16 @@
Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT)
.show()
}
+ // PERMISSIONS REQUESTED AFTER MAP SETUP
}
// Setup close button
rootView.findViewById<View>(R.id.btnClose).setOnClickListener {
- hideStopBottomSheet()
- }
- livePositionsViewModel.serviceStatus.observe(viewLifecycleOwner){ status ->
- //if service is active, update the bus positions icon
- when(status) {
- LivePositionsServiceStatus.OK ->
- setBusPositionsIcon(true, error = false)
-
- LivePositionsServiceStatus.NO_POSITIONS -> setBusPositionsIcon(true, error = true)
-
- else -> setBusPositionsIcon(true, error = true)
- }
+ hideStopOrBusBottomSheet()
}
+ observeStatusLivePositions()
+ //observe change in source of the live positions
livePositionsViewModel.useMQTTPositionsLiveData.observe(viewLifecycleOwner){ useMQTT->
//Log.d(DEBUG_TAG, "Changed MQTT positions, now have to use MQTT: $useMQTT")
if (showBusLayer && isResumed) {
@@ -281,6 +272,7 @@
if (clearPos) {
livePositionsViewModel.clearAllPositions()
//force clear of the viewed data
+ if(vehShowing.isNotEmpty()) hideStopOrBusBottomSheet()
clearAllBusPositionsInMap()
}
@@ -303,8 +295,6 @@
this.map = mapReady
val context = requireContext()
val mjson = MapLibreStyles.getJsonStyleFromAsset(context, PreferencesHolder.getMapLibreStyleFile(context))
- //ViewUtils.loadJsonFromAsset(requireContext(),"map_style_good.json")
-
val builder = Style.Builder().fromJson(mjson!!)
@@ -323,8 +313,6 @@
displayStops(stopsInCache)
if(showBusLayer) setupBusLayer(style, withLabels = true, busIconsScale = 1.2f)
- initSymbolManager(mapReady, style)
-
// Start observing data now that everything is set up
observeStops()
}
@@ -350,9 +338,6 @@
//the user is moving the map
isUserMovingCamera = true
}
- map?.let { setFollowingUser(it.locationComponent.cameraMode == CameraMode.TRACKING) }
- //setFollowingUser()
-
}
mapReady.addOnMapClickListener { point ->
@@ -364,28 +349,39 @@
observeBusPositionUpdates()
//Restoring data
- var boundsRestored = false
- pendingLocationActivation = true
- stopsViewModel.savedState?.let{
- boundsRestored = restoreMapStateFromBundle(it)
- //why are we disabling it?
- pendingLocationActivation = it.getBoolean(KEY_LOCATION_ENABLED,true)
- Log.d(DEBUG_TAG, "Restored map state from the saved bundle: ")
- }
- if(pendingLocationActivation)
- positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS)
-
- //reset saved State at the end
- if((!boundsRestored)) {
- //set initial position
- //center position
- val latlngTarget = initialStopToShow?.let {
- LatLng(it.latitude!!, it.longitude!!)
- } ?: LatLng(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON)
- mapReady.cameraPosition = CameraPosition.Builder().target(latlngTarget).zoom(DEFAULT_ZOOM).build()
+
+ if (initialStopToShow!=null){
+ val s = initialStopToShow!!
+ mapReady.cameraPosition = CameraPosition.Builder().target(
+ LatLng(s.latitude!!, s.longitude!!)
+ ).zoom(DEFAULT_ZOOM).build()
+ restoredMapCamera.set(true)
+ } else{
+ var boundsRestored = false
+ //restore the map state here
+ map?.let{
+ boundsRestored = mapStateViewModel.restoreMapState(it)
+ mapStateViewModel.lastOpenStopID.value?.let{ sID->
+ val s= stopsViewModel.getStopByID(sID)
+ if (s==null) {
+ if(sID.isNotEmpty())
+ Log.w(DEBUG_TAG,"Wanted to open stop $sID in map but it was not loaded!")
+ }
+ else{
+ openStopInBottomSheet(s) }
+ }
+
+ }
+ if(!boundsRestored){
+ mapReady.cameraPosition = CameraPosition.Builder().target(
+ LatLng(DEFAULT_CENTER_LAT, DEFAULT_CENTER_LON)
+ ).zoom(DEFAULT_ZOOM).build()
+ }
+ restoredMapCamera.set(boundsRestored)
}
- //reset saved state
- stopsViewModel.savedState = null
+
+ pendingLocationActivation = true
+ positionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS)
}
private fun onMapClickReact(point: LatLng): Boolean{
@@ -405,7 +401,7 @@
val sameStopClicked = shownStopInBottomSheet?.let { newstop.ID==it.ID } ?: false
Log.d(DEBUG_TAG, "Hiding clicked stop: $sameStopClicked")
if (isBottomSheetShowing()) {
- hideStopBottomSheet()
+ hideStopOrBusBottomSheet()
}
if(!sameStopClicked){
openStopInBottomSheet(newstop)
@@ -424,9 +420,14 @@
} else if (busNearby.isNotEmpty()) {
val feature = busNearby[0]
val vehid = feature.getStringProperty("veh")
- val route = feature.getStringProperty("line")
-
- Toast.makeText(context, "Veh $vehid on route $route", Toast.LENGTH_SHORT).show()
+ if (isBottomSheetShowing()) hideStopOrBusBottomSheet()
+ showVehicleTripInBottomSheet(vehid)
+ //move camera to center on vehicle
+ updatesByVehDict[vehid]?.let { dat ->
+ mapReady.animateCamera(
+ CameraUpdateFactory.newLatLng(LatLng(dat.posUpdate.latitude, dat.posUpdate.longitude)), 750
+ )
+ }
return true
}
}
@@ -455,19 +456,12 @@
override fun onStart() {
super.onStart()
- //restore state from viewModel
- stopsViewModel.savedState?.let {
- restoreMapStateFromBundle(it)
- //reset state
- stopsViewModel.savedState = null
- }
}
override fun onResume() {
super.onResume()
//mapView.onResume() handled in GeneralMapLibreFragment
- //val keySourcePositions = getString(R.string.pref_positions_source)
if(showBusLayer) {
//first, clean up all the old positions
livePositionsViewModel.clearOldPositionsUpdates()
@@ -480,7 +474,7 @@
livePositionsViewModel.requestGTFSUpdates()
usingMQTTPositions = false
}
- //mapViewModel.testCascade();
+
livePositionsViewModel.isLastWorkResultGood.observe(this) { d: Boolean ->
Log.d(
DEBUG_TAG, "Last trip download result is $d"
@@ -493,8 +487,6 @@
}
fragmentListener?.readyGUIfor(FragmentKind.MAP)
- //restore saved state
- savedMapStateOnPause?.let { restoreMapStateFromBundle(it) }
}
override fun onPause() {
@@ -502,7 +494,11 @@
mapView.onPause()
Log.d(DEBUG_TAG, "Fragment paused")
- savedMapStateOnPause = saveMapStateInBundle()
+ map?.let{
+ //if map is initialized
+ mapStateViewModel.saveMapState(it)
+ }
+ mapStateViewModel.lastOpenStopID.postValue(shownStopInBottomSheet?.ID)
if (livePositionsViewModel.useMQTTPositionsLiveData.value!!) livePositionsViewModel.stopMatoUpdates()
}
@@ -511,10 +507,11 @@
super.onStop()
mapView.onStop()
Log.d(DEBUG_TAG, "Fragment stopped!")
- stopsViewModel.savedState = Bundle().let {
+ /* stopsViewModel.savedState = Bundle().let {
mapView.onSaveInstanceState(it)
it
}
+ */
//save last location
map?.locationComponent?.lastKnownLocation?.let{
stopsViewModel.lastUserLocation = it
@@ -537,6 +534,51 @@
return mapView
}
+ private fun showVehicleTripInBottomSheet(veh: String) {
+ val data = updatesByVehDict[veh] ?: run {
+ Log.w(DEBUG_TAG, "Asked to show vehicle $veh but not in updates")
+ return
+ }
+ bottomLayout?.let {
+ val lineName = FiveTNormalizer.fixShortNameForDisplay(
+ GtfsUtils.getLineNameFromGtfsID(data.posUpdate.routeID), true)
+ val pat = data.pattern
+ if (pat != null) {
+ stopTitleTextView.text = pat.headsign
+ stopTitleTextView.visibility = View.VISIBLE
+ stopNumberTextView.text = getString(R.string.line_fill_towards, lineName)
+
+ directionsCard.setOnClickListener {
+ map?.let{
+ mapStateViewModel.saveMapState(it)
+ }
+ fragmentListener?.openLineFromVehicle(data.posUpdate.getLineGTFSFormat(), pat.code, mapStateViewModel.savedCameraState?.toBundle())
+ }
+ } else {
+ stopTitleTextView.visibility = View.GONE
+ stopNumberTextView.text = getString(R.string.line_fill, lineName)
+ directionsCard.setOnClickListener {
+ map?.let{
+ mapStateViewModel.saveMapState(it)
+ }
+ fragmentListener?.openLineFromVehicle(data.posUpdate.getLineGTFSFormat(), "",mapStateViewModel.savedCameraState?.toBundle())
+ }
+ }
+ directionsCard.visibility = View.VISIBLE
+ bottomrightImage.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_magnifying_glass, activity?.theme))
+ val colorBlue = ResourcesCompat.getColor(resources,R.color.blue_500,activity?.theme)
+ ViewCompat.setBackgroundTintList(directionsCard, ColorStateList.valueOf(colorBlue))
+ linesPassingTextView.text = getString(R.string.vehicle_fill, data.posUpdate.vehicle)
+ arrivalsCard.visibility = View.GONE
+
+ }
+ //directions card
+ vehShowing = veh
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+ updatePositionsIcons(true)
+ Log.d(DEBUG_TAG, "Shown vehicle $veh in bottom sheet")
+ }
+
private fun observeStops() {
// Observe stops
stopsViewModel.stopsToShow.observe(viewLifecycleOwner) { stops ->
@@ -606,7 +648,9 @@
DEBUG_TAG,
"Have " + data.size + " trip updates, has Map start finished: " + mapInitCompleted
)
- if (mapInitCompleted) updateBusPositionsInMap(data)
+ if (mapInitCompleted) updateBusPositionsInMap(data, hasVehicleTracking = true) { veh ->
+ showVehicleTripInBottomSheet(veh)
+ }
if (!isDetached && !livePositionsViewModel.useMQTTPositionsLiveData.value!!) livePositionsViewModel.requestDelayedGTFSUpdates(
3000
)
@@ -614,30 +658,6 @@
}
- /*private fun createLabelForVehicle(positionUpdate: LivePositionUpdate){
- val symOpt = SymbolOptions()
- .withLatLng(LatLng(positionUpdate.latitude, positionUpdate.longitude))
- .withTextColor("#ffffff")
- .withTextField(positionUpdate.routeID.substringBeforeLast('U'))
- .withTextSize(13f)
- .withTextAnchor(TEXT_ANCHOR_CENTER)
- .withTextFont(arrayOf( "noto_sans_regular"))//"noto_sans_regular", "sans-serif")) //"noto_sans_regular"))
-
- val newSymbol = symbolManager.create(symOpt
- )
- Log.d(DEBUG_TAG, "Symbol for veh ${positionUpdate.vehicle}: $newSymbol")
- busLabelSymbolsByVeh[positionUpdate.vehicle] = newSymbol
- }
- private fun removeVehicleLabel(vehicle: String){
- busLabelSymbolsByVeh[vehicle]?.let {
- symbolManager.delete(it)
- busLabelSymbolsByVeh.remove(vehicle)
- }
- }
-
- */
-
-
// ------ LOCATION STUFF -----
@SuppressLint("MissingPermission")
private fun requestInitialUserLocation() {
@@ -672,19 +692,6 @@
}
- /**
- * Clear all buses from the map
- */
- private fun clearAllBusPositionsInMap(){
- for ((k, anim) in animatorsByVeh){
- anim.cancel()
- }
- animatorsByVeh.clear()
- updatesByVehDict.clear()
- updatePositionsIcons(forced = false)
- }
-
-
/**
* Handles logic of enabling the user location on the map
@@ -697,7 +704,7 @@
if (permissionOk) {
Log.d(DEBUG_TAG, "Permission OK, starting location component, assumed: $assumePermissions, fromClick: $fromClick")
locationComponent.isLocationComponentEnabled = true
- if (initialStopToShow==null) {
+ if (!restoredMapCamera.get()) {
locationComponent.cameraMode = CameraMode.TRACKING //CameraMode.TRACKING
setFollowingUser(true)
}
@@ -711,7 +718,6 @@
Toast.makeText(activity, R.string.enable_position_message_map, Toast.LENGTH_SHORT).show()
}
Log.d(DEBUG_TAG, "Requesting permission to show user location")
- enablingPositionFromClick = fromClick
showUserPositionRequestLauncher.launch(Permissions.LOCATION_PERMISSIONS)
}
} else{
@@ -727,6 +733,7 @@
}
+
private fun setLocationIconEnabled(enabled: Boolean){
if (enabled)
showUserPositionButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.location_circlew_red))
@@ -735,20 +742,6 @@
}
- /**
- * Helper method for GUI
- */
- private fun setBusPositionsIcon(enabled: Boolean, error: Boolean){
- val ctx = requireContext()
- if(!enabled)
- busPositionsIconButton.setImageDrawable(ContextCompat.getDrawable(ctx, R.drawable.bus_pos_circle_inactive))
- else if(error)
- busPositionsIconButton.setImageDrawable(ContextCompat.getDrawable(ctx, R.drawable.bus_pos_circle_notworking))
- else
- busPositionsIconButton.setImageDrawable(ContextCompat.getDrawable(ctx, R.drawable.bus_pos_circle_active))
-
- }
-
private fun updateFollowingIcon(enabled: Boolean){
if(enabled)
followUserButton.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.walk_circle_active))
diff --git a/app/src/main/java/it/reyboz/bustorino/map/MapCameraState.kt b/app/src/main/java/it/reyboz/bustorino/map/MapCameraState.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/map/MapCameraState.kt
@@ -0,0 +1,41 @@
+package it.reyboz.bustorino.map
+
+import android.os.Bundle
+
+
+data class MapCameraState(
+ val latitude: Double,
+ val longitude: Double,
+ val zoom: Double,
+ val bearing: Double,
+ val tilt: Double
+){
+ fun toBundle(): Bundle = Bundle().apply {
+ putDouble(KEY_LATITUDE, latitude)
+ putDouble(KEY_LONGITUDE, longitude)
+ putDouble(KEY_ZOOM, zoom)
+ putDouble(KEY_BEARING, bearing)
+ putDouble(KEY_TILT, tilt)
+ }
+
+ companion object {
+ private const val KEY_LATITUDE = "cam-latitude"
+ private const val KEY_LONGITUDE = "cam-longitude"
+ private const val KEY_ZOOM = "cam-zoom"
+ private const val KEY_BEARING = "cam-bearing"
+ private const val KEY_TILT = "cam-tilt"
+
+ fun fromBundle(bundle: Bundle): MapCameraState = MapCameraState(
+ latitude = bundle.getDouble(KEY_LATITUDE),
+ longitude = bundle.getDouble(KEY_LONGITUDE),
+ zoom = bundle.getDouble(KEY_ZOOM),
+ bearing = bundle.getDouble(KEY_BEARING),
+ tilt = bundle.getDouble(KEY_TILT)
+ )
+
+ fun checkInBundle(bundle: Bundle): Boolean {
+ val chck = bundle.containsKey(KEY_LATITUDE) && bundle.containsKey(KEY_LONGITUDE) && bundle.containsKey(KEY_ZOOM)
+ return chck
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/map/MapViewModel.kt b/app/src/main/java/it/reyboz/bustorino/map/MapViewModel.kt
deleted file mode 100644
--- a/app/src/main/java/it/reyboz/bustorino/map/MapViewModel.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package it.reyboz.bustorino.map
-
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import it.reyboz.bustorino.backend.Stop
-
-class MapViewModel : ViewModel() {
-
- val currentLat = MutableLiveData(INVALID)
- val currentLong = MutableLiveData(INVALID)
- val currentZoom = MutableLiveData(-10.0)
-
- var stopShowing: Stop? = null
-
- companion object{
- const val INVALID = -1000.0
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LivePositionsViewModel.kt
@@ -120,9 +120,6 @@
Log.d(DEBUG_TI, "Switched positions source in ViewModel, now using MQTT: ${!usingMQTT}")
serviceStatus.value = LivePositionsServiceStatus.CONNECTING
}
- fun setGtfsLineToFilterPos(line: String, pattern: MatoPattern?){
- gtfsLineToFilterPos.value = Pair(line, pattern)
- }
var isLastWorkResultGood = workManager
.getWorkInfosForUniqueWorkLiveData(MatoTripsDownloadWorker.TAG_TRIPS).map { it ->
@@ -274,7 +271,7 @@
}
filteredLocationUpdates.addSource(gtfsLineToFilterPos){
- //Log.d(DEBUG_TI, "line to filter change to: ${gtfsLineToFilterPos.value}")
+ Log.d(DEBUG_TI, "line to filter change to: ${gtfsLineToFilterPos.value}")
updatesWithTripAndPatterns.value?.let{
ups-> filteredLocationUpdates.postValue(filterUpdatesForGtfsLine(ups, it))
//Log.d(DEBUG_TI, "Set ${ups.size} updates as new value for filteredLocation")
@@ -282,6 +279,13 @@
}
}
+ private fun clearFilteredPositions(){
+ filteredLocationUpdates.postValue(Pair(HashMap(), ArrayList<String>()))
+ }
+ fun setGtfsLineToFilterPos(line: String, pattern: MatoPattern?){
+ clearFilteredPositions()
+ gtfsLineToFilterPos.value = Pair(line, pattern)
+ }
private fun filterUpdatesForGtfsLine(updates: FullPositionUpdatesMap,
linePatt: Pair<String, MatoPattern?>):
@@ -322,6 +326,7 @@
if (dir == directionId) {
//add the trip
updsForTripId[tripId] = pair
+ Log.d(DEBUG_TI, "Add vehicle ${pair.first.vehicle}, route ${pair.first.routeID}")
} else {
vehicleOnWrongDirection.add(vehicle)
}
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/MapStateViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/MapStateViewModel.kt
new file mode 100644
--- /dev/null
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/MapStateViewModel.kt
@@ -0,0 +1,48 @@
+package it.reyboz.bustorino.viewmodels
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import it.reyboz.bustorino.map.MapCameraState
+import org.maplibre.android.camera.CameraPosition
+import org.maplibre.android.geometry.LatLng
+import org.maplibre.android.geometry.LatLngBounds
+import org.maplibre.android.maps.MapLibreMap
+
+class MapStateViewModel : ViewModel() {
+
+ var savedCameraState: MapCameraState? = null
+ private set
+
+ val lastOpenStopID = MutableLiveData<String>()
+
+ fun saveMapState(map: MapLibreMap){
+ val cp = map.cameraPosition
+ val newBbox = map.projection.visibleRegion.latLngBounds
+
+ val cameraState = MapCameraState(
+ latitude = newBbox.center.latitude,
+ longitude = newBbox.center.longitude,
+ zoom = cp.zoom,
+ bearing = cp.bearing,
+ tilt = cp.tilt
+ )
+
+ savedCameraState = cameraState
+ }
+ fun restoreMapState(map: MapLibreMap): Boolean {
+ return restoreMapState(map, this.savedCameraState)
+ }
+
+ companion object{
+ fun restoreMapState(map: MapLibreMap, savedCameraState: MapCameraState?): Boolean {
+ val state = savedCameraState ?: return false
+ map.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(state.latitude, state.longitude))
+ .zoom(state.zoom)
+ .bearing(state.bearing)
+ .tilt(state.tilt)
+ .build()
+ return true
+ }
+ }
+}
\ No newline at end of file
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
@@ -81,11 +81,9 @@
addStopsCallback)
}
}
-
- var savedState: Bundle? = null
var lastUserLocation: Location? = null
companion object{
private const val DEBUG_TAG = "BusTOStopMapViewModel"
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_lines_detail.xml b/app/src/main/res/layout/fragment_lines_detail.xml
--- a/app/src/main/res/layout/fragment_lines_detail.xml
+++ b/app/src/main/res/layout/fragment_lines_detail.xml
@@ -98,27 +98,24 @@
android:src="@drawable/location_circlew_red"
android:layout_marginTop="54dp"
- android:layout_marginEnd="8dp"
+ android:layout_marginEnd="5dp"
android:background="#00ffffff"
android:contentDescription="@string/enable_position"
app:layout_constraintTop_toTopOf="@id/lineMap"
app:layout_constraintEnd_toEndOf="@id/lineMap"
android:cropToPadding="true" />
- <!--
- <ImageButton
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/icon_follow"
- android:src="@drawable/ic_follow_me"
- android:background="#00ffffff"
- android:contentDescription="@string/bt_follow_me_description"
- android:cropToPadding="true"
- app:layout_constraintEnd_toEndOf="@id/lineMap"
- app:layout_constraintTop_toBottomOf="@id/icon_center_map"
- android:layout_marginTop="10dp"
- android:layout_marginRight="10dp"
- android:layout_marginEnd="10dp"
- />-->
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/busPositionsImageButton"
+ android:src="@drawable/bus_pos_circle_inactive"
+ android:background="#00ffffff"
+ android:contentDescription="@string/bt_follow_me_description"
+ android:layout_marginEnd="5dp"
+
+ app:layout_constraintTop_toBottomOf="@id/locationEnableIcon"
+ app:layout_constraintEnd_toEndOf="@id/lineMap"
+ android:layout_marginTop="7dp" />
<View
diff --git a/app/src/main/res/layout/fragment_map_libre.xml b/app/src/main/res/layout/fragment_map_libre.xml
--- a/app/src/main/res/layout/fragment_map_libre.xml
+++ b/app/src/main/res/layout/fragment_map_libre.xml
@@ -15,7 +15,7 @@
/>
<!-- Bottom Sheet for details -->
- <include layout="@layout/map_include_bottom_sheet"></include>
+ <include layout="@layout/map_include_bottom_sheet"/>
<FrameLayout
android:layout_width="match_parent"
diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@
ext.coroutines_version = "1.10.2"
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.5' // or latest
- classpath 'com.android.tools.build:gradle:8.12.3'
+ classpath 'com.android.tools.build:gradle:8.13.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:$kotlin_version-2.0.4"
@@ -28,15 +28,15 @@
multidex_version = "2.0.1"
//libraries versions
fragment_version = "1.8.9"
- activity_version = "1.11.0"
+ activity_version = "1.13.0"
appcompat_version = "1.7.1"
preference_version = "1.2.1"
- work_version = "2.11.0"
+ work_version = "2.11.2"
acra_version = "5.13.1"
- lifecycle_version = "2.9.4"
+ lifecycle_version = "2.10.0"
arch_version = "2.1.0"
- room_version = "2.8.3"
+ room_version = "2.8.4"
}

File Metadata

Mime Type
text/plain
Expires
Tue, Jun 23, 16:39 (16 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1975345
Default Alt Text
D221.1782225596.diff (80 KB)

Event Timeline