Page Menu
Home
GitPull.it
Search
Configure Global Search
Log In
Files
F13213707
D222.1776864501.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
69 KB
Referenced Files
None
Subscribers
None
D222.1776864501.diff
View Options
diff --git a/app/build.gradle b/app/build.gradle
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -116,6 +116,8 @@
// Guava implementation for DBUpdateWorker
implementation 'com.google.guava:guava:33.5.0-android'
+
+ implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation "androidx.fragment:fragment-ktx:$fragment_version"
implementation "androidx.activity:activity-ktx:$activity_version"
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
@@ -25,11 +25,13 @@
import androidx.preference.PreferenceManager;
import android.content.SharedPreferences;
+import android.view.Gravity;
import android.os.Build;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.PopupMenu;
import android.widget.TextView;
import java.util.*;
@@ -88,6 +90,12 @@
vh.routeIDTextView.setText(route.getDisplayCode());
vh.routeCard.setOnClickListener(view -> mRouteListener.requestShowingRoute(route));
+
+ // Clicking anywhere on the row shows a popup menu
+ vh.itemView.setOnClickListener(view ->
+ openPopupMenuDetails(con,view, route)
+ ); //vh.rowRouteDestination.getVisibility() == View.VISIBLE ? vh.rowRouteDestination : vh.itemView
+
if(route.destinazione==null || route.destinazione.length() == 0) {
vh.rowRouteDestination.setVisibility(View.GONE);
// move around the route timetable
@@ -114,11 +122,6 @@
}
vh.rowRouteDestination.setText(dest);
-
- //set click listener
- vh.itemView.setOnClickListener(view -> {
- mRouteListener.showRouteFullDirection(route);
- });
}
switch (route.type) {
@@ -208,6 +211,26 @@
return Capitalize.DO_NOTHING;
}
+ private void openPopupMenuDetails(Context con, View view, Route route){
+ PopupMenu popup = new PopupMenu(con, view, Gravity.END);
+ popup.inflate(R.menu.menu_arrivals_line_item);
+ if (route.destinazione == null || route.destinazione.isEmpty()) {
+ popup.getMenu().findItem(R.id.action_show_direction).setVisible(false);
+ }
+ popup.setOnMenuItemClickListener(item -> {
+ int id = item.getItemId();
+ if (id == R.id.action_open_line) {
+ mRouteListener.requestShowingRoute(route);
+ return true;
+ } else if (id == R.id.action_show_direction) {
+ mRouteListener.showRouteFullDirection(route);
+ return true;
+ }
+ return false;
+ });
+ popup.show();
+ }
+
public PalinaAdapter(Context context, Palina p, PalinaClickListener listener, boolean hideEmptyRoutes) {
Comparator<Passaggio> sorter = null;
if (p.getPassaggiSourceIfAny()== Passaggio.Source.GTTJSON){
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt b/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt
--- a/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/RouteAdapter.kt
@@ -12,7 +12,7 @@
class RouteAdapter(val routes: List<GtfsRoute>,
click: ItemClicker,
- private val layoutId: Int = R.layout.entry_line_num_descr) :
+ private val layoutId: Int = R.layout.entry_line_name_description) :
RecyclerView.Adapter<RouteAdapter.ViewHolder>()
{
val clickreference: WeakReference<ItemClicker>
@@ -45,7 +45,7 @@
// Get element from your dataset at this position and replace the
// contents of the view with that element
val route = routes[position]
- holder.nameTextView.text = route.shortName
+ holder.nameTextView.text = route.getShortNameDisplay()
holder.descrptionTextView.text = route.longName
holder.itemView.setOnClickListener{
diff --git a/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt b/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt
--- a/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt
+++ b/app/src/main/java/it/reyboz/bustorino/adapters/RouteOnlyLineAdapter.kt
@@ -6,6 +6,7 @@
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import it.reyboz.bustorino.R
+import it.reyboz.bustorino.backend.FiveTNormalizer
import it.reyboz.bustorino.backend.Palina
import java.lang.ref.WeakReference
@@ -47,7 +48,8 @@
// Get element from your dataset at this position and replace the
// contents of the view with that element
- viewHolder.textView.text = routeNames[position]
+ // SHOW "STAR" as "ST"
+ viewHolder.textView.text = FiveTNormalizer.filterFullStarName(routeNames[position])
viewHolder.itemView.setOnClickListener{
clickreference?.get()?.onItemClick(position, routeNames[position])
}
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
@@ -182,6 +182,7 @@
if(routeID.length() == 3 && routeID.charAt(2) == 'B') {
return routeID.substring(0,2).concat("/");
}
+ //TODO: Decide what to do about the "+" lines (68+, 13+)
switch(routeID) {
case "1C":
@@ -380,4 +381,13 @@
return sb.toString();
}
+
+ public static String filterFullStarName(String name){
+ String outName = name;
+ if(name.contains("STAR ")){
+ //FIX FOR THE MaTO data
+ outName = outName.replace("STAR ","ST");
+ }
+ return outName;
+ }
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Palina.java b/app/src/main/java/it/reyboz/bustorino/backend/Palina.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/Palina.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/Palina.java
@@ -1,6 +1,7 @@
/*
BusTO (backend components)
Copyright (C) 2016 Ludovico Pavesi
+ Copyright (c) 2026 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
@@ -416,7 +417,64 @@
return mList;
}
- //private void mergeRoute
+
+ private static String pick(String a, String b) {
+ return (a != null && !a.isEmpty()) ? a : b;
+ }
+
+ /**
+ * Merge two Palinas, including information from both
+ * @param p1 the first one, which has priority
+ * @param p2 the second one
+ * @return the merged Palina data
+ */
+ public static @Nullable Palina mergePaline(@Nullable Palina p1, @Nullable Palina p2) {
+ if (p1 == null) return p2;
+ if (p2 == null) return p1;
+
+ // --- Campi base (Stop) ---
+ String id = p1.ID; // assumiamo stesso ID
+
+ String name = pick(p1.getStopDefaultName(), p2.getStopDefaultName());
+ String userName = pick(p1.getStopUserName(), p2.getStopUserName());
+ String location = pick(p1.location, p2.location);
+
+ Double lat = p1.getLatitude() != null ? p1.getLatitude() : p2.getLatitude();
+ Double lon = p1.getLongitude() != null ? p1.getLongitude() : p2.getLongitude();
+
+ String gtfsID = pick(p1.gtfsID, p2.gtfsID);
+
+ Palina result = new Palina(id, name, userName, location, lat, lon, gtfsID);
+
+ // --- Routes ---
+ List<Route> mergedRoutes = new ArrayList<>();
+ boolean addFromSecond = false;
+
+ if (p1.queryAllRoutes() != null)
+ mergedRoutes.addAll(p1.routes);
+
+ else if (p2.queryAllRoutes() != null)
+ mergedRoutes.addAll(p2.routes);
+ else {
+ //assume the first one has more important imformation
+ mergedRoutes.addAll(p1.routes);
+ addFromSecond = true;
+
+ }
+
+ result.setRoutes(mergedRoutes);
+ if(addFromSecond){
+ result.addInfoFromRoutes(p2.routes);
+ }
+
+ // Unisci eventuali duplicati (stesso routeID)
+ result.mergeDuplicateRoutes(0);
+
+ // Aggiorna stringa routes
+ result.buildRoutesString();
+
+ return result;
+ }
/// ------- Parcelable stuff ---
protected Palina(Parcel in) {
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/Route.java b/app/src/main/java/it/reyboz/bustorino/backend/Route.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/Route.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/Route.java
@@ -432,6 +432,15 @@
return adjusted;
}
+ public String getRouteLongDisplayName() {
+
+ String routeName = FiveTNormalizer.routeInternalToDisplay(this.name);
+ if (routeName == null) {
+ routeName = this.displayCode;
+ }
+ return routeName;
+ }
+
// ---- Parcelable implem ---
protected Route(Parcel in) {
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
@@ -216,9 +216,7 @@
/**
* Returns stop name or username (if set).<br>
- * - empty string means "already searched everywhere, can't find it"<br>
- * - null means "didn't search, yet. Maybe you should try."<br>
- * - string means "here's the name.", obviously.<br>
+ * If null, we should try to look somewhere else.
*
* @return string if known, null if still unknown
*/
@@ -282,6 +280,13 @@
return String.format(Locale.US, "geo:%f,%f", this.lat, this.lon);
}
+ /**
+ * Check if the stop contains the coordinates
+ * @return true if both the latitude and the longitude are not null
+ */
+ public final boolean hasCoords(){
+ return (this.lat!=null)&&(this.lon!=null);
+ }
public final @Nullable String getGeoURLWithAddress() {
String url = getGeoURL();
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsUtils.java b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsUtils.java
--- a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsUtils.java
+++ b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsUtils.java
@@ -18,6 +18,7 @@
package it.reyboz.bustorino.backend.gtfs;
import androidx.core.util.Pair;
+import it.reyboz.bustorino.backend.FiveTNormalizer;
import it.reyboz.bustorino.backend.ServiceType;
abstract public class GtfsUtils {
@@ -69,4 +70,13 @@
public static String getLineNameFromGtfsID(String routeID){
return getRouteInfoFromGTFS(routeID).second;
}
+
+ public static String lineNameDisplayFromGtfsID(String routeID){
+ String name = getRouteInfoFromGTFS(routeID).second;
+
+ String altName = FiveTNormalizer.routeInternalToDisplay(name);
+ if (altName==null) //WTF WHY DOES IT HAVE TO BE NULL
+ return name;
+ else return altName;
+ }
}
diff --git a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
--- a/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
+++ b/app/src/main/java/it/reyboz/bustorino/backend/mato/MatoAPIFetcher.kt
@@ -199,7 +199,7 @@
for (i in 0 until routesStoppingJSON.length()){
val routeBaseInfo = routesStoppingJSON.getJSONObject(i)
val r = Route(routeBaseInfo.getString("shortName"), Route.Type.UNKNOWN,"")
- r.setGtfsId(routeBaseInfo.getString("gtfsId").trim())
+ r.gtfsId = routeBaseInfo.getString("gtfsId").trim()
baseRoutes.add(r)
}
@@ -233,7 +233,7 @@
//val gtfsRoutes = mutableListOf<>()
return palina
}
- fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{
+ private fun parseRouteStoptimesJSON(jsonPatternWithStops: JSONObject): Route{
val patternJSON = jsonPatternWithStops.getJSONObject("pattern")
val routeJSON = patternJSON.getJSONObject("route")
@@ -258,7 +258,7 @@
"TRAM" -> routeType = Route.Type.TRAM
}
val route = Route(
- routeJSON.getString("shortName"),
+ FiveTNormalizer.filterFullStarName(routeJSON.getString("shortName")),
patternJSON.getString("headsign"),
routeType,
passages,
diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt
--- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt
+++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsRoute.kt
@@ -20,6 +20,7 @@
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
+import it.reyboz.bustorino.backend.FiveTNormalizer
@Entity(tableName=GtfsRoute.DB_TABLE)
data class GtfsRoute(
@@ -80,4 +81,8 @@
override fun getColumns(): Array<String> {
return COLUMNS
}
+
+ fun getShortNameDisplay(): String {
+ return FiveTNormalizer.filterFullStarName(shortName)
+ }
}
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
@@ -65,8 +65,10 @@
//Views
protected lateinit var addToFavorites: ImageButton
+ protected lateinit var openInMapButton: ImageButton
protected lateinit var timesSourceTextView: TextView
- protected lateinit var messageTextView: TextView
+ private lateinit var messageTextView: TextView
+ private lateinit var preMessageTextView: TextView // this hold the "Arrivals at: " text
protected lateinit var arrivalsRecyclerView: RecyclerView
private var mListAdapter: PalinaAdapter? = null
@@ -95,14 +97,8 @@
private val palinaClickListener: PalinaClickListener = object : PalinaClickListener {
override fun showRouteFullDirection(route: Route) {
- var routeName: String?
+ var routeName = route.routeLongDisplayName
Log.d(DEBUG_TAG, "Make toast for line " + route.name)
-
-
- routeName = FiveTNormalizer.routeInternalToDisplay(route.name)
- if (routeName == null) {
- routeName = route.displayCode
- }
if (context == null) Log.e(DEBUG_TAG, "Touched on a route but Context is null")
else if (route.destinazione == null || route.destinazione.length == 0) {
Toast.makeText(
@@ -168,7 +164,9 @@
): View? {
val root = inflater.inflate(R.layout.fragment_arrivals, container, false)
messageTextView = root.findViewById(R.id.messageTextView)
+ preMessageTextView = root.findViewById(R.id.arrivalsTextView)
addToFavorites = root.findViewById(R.id.addToFavorites)
+ openInMapButton = root.findViewById(R.id.openInMapButton)
// "How does it work part"
howDoesItWorkTextView = root.findViewById(R.id.howDoesItWorkTextView)
hideHintButton = root.findViewById(R.id.hideHintButton)
@@ -219,7 +217,7 @@
val displayName = requireArguments().getString(STOP_TITLE)
if (displayName != null) setTextViewMessage(
String.format(
- getString(R.string.passages), displayName
+ getString(R.string.passages_fill), displayName
)
)
@@ -261,6 +259,7 @@
arrivalsViewModel.palinaLiveData.observe(viewLifecycleOwner){
mListener.toggleSpinner(false)
+ Log.d(DEBUG_TAG, "New result palina observed, has coords: ${it.hasCoords()}")
if(arrivalsViewModel.resultLiveData.value==Fetcher.Result.OK){
//the result is true
changeUIFirstSearchActive(false)
@@ -456,21 +455,34 @@
if (p == null) Log.w(DEBUG_TAG, "Asked to update the data, but we're not attached and the data is null")
else needUpdateOnAttach = true
} else {
+ //set title
+ if(stopName==null && p?.stopDisplayName != null){
+ stopName = p.stopDisplayName
+ updateMessage()
+ }
+
val adapter = PalinaAdapter(context, lastUpdatedPalina, palinaClickListener, true)
showArrivalsSources(lastUpdatedPalina!!)
resetListAdapter(adapter)
+ lastUpdatedPalina?.let{ pal ->
+ openInMapButton.setOnClickListener {
+ if (pal.hasCoords())
+ mListener.showMapCenteredOnStop(pal)
+ }
+ }
+
val routesWithNoPassages = lastUpdatedPalina!!.routesNamesWithNoPassages
if (routesWithNoPassages.isEmpty()) {
//hide the views if there are no empty routes
- noArrivalsRecyclerView!!.visibility = View.GONE
+ noArrivalsRecyclerView.visibility = View.GONE
noArrivalsTitleView!!.visibility = View.GONE
} else {
Collections.sort(routesWithNoPassages, LinesNameSorter())
noArrivalsAdapter = RouteOnlyLineAdapter(routesWithNoPassages, null)
- noArrivalsRecyclerView!!.adapter = noArrivalsAdapter
+ noArrivalsRecyclerView.adapter = noArrivalsAdapter
- noArrivalsRecyclerView!!.visibility = View.VISIBLE
+ noArrivalsRecyclerView.visibility = View.VISIBLE
noArrivalsTitleView!!.visibility = View.VISIBLE
}
@@ -525,9 +537,7 @@
}
/**
- * Update the message in the fragment
- *
- * It may eventually change the "Add to Favorite" icon
+ * Update the stop title in the fragment
*/
private fun updateMessage() {
var message = ""
@@ -539,13 +549,21 @@
Log.e("ArrivalsFragm$tag", "NO ID FOR THIS FRAGMENT - something went horribly wrong")
}
if (message.isNotEmpty()) {
- setTextViewMessage(getString(R.string.passages, message))
+ //setTextViewMessage(getString(R.string.passages_fill, message))
+ setTextViewMessage(message)
}
+ }
- // whatever is the case, update the star icon
- //updateStarIconFromLastBusStop();
+ /**
+ * Set the message textView
+ * @param message the whole message to write in the textView
+ */
+ fun setTextViewMessage(message: String?) {
+ messageTextView.text = message
+ messageTextView.visibility = View.VISIBLE
}
+
override fun onCreateLoader(id: Int, p1: Bundle?): Loader<Cursor> {
val args = arguments
//if (args?.getString(KEY_STOP_ID) == null) throw
@@ -592,7 +610,7 @@
stopIsInFavorites = false
}
updateStarIcon()
-
+ /*
if (stopName == null) {
//stop is not inside the favorites and wasn't provided
Log.d("ArrivalsFragment$tag", "Stop wasn't in the favorites and has no name, looking in the DB")
@@ -601,8 +619,10 @@
arguments, this
)
}
+ 6
+ */
}
-
+ /*
loaderStopId -> if (data.count > 0) {
data.moveToFirst()
val index = data.getColumnIndex(
@@ -616,6 +636,8 @@
} else {
Log.w("ArrivalsFragment$tag", "Stop is not inside the database... CLOISTER BELL")
}
+
+ */
}
}
@@ -629,15 +651,6 @@
arrivalsRecyclerView.visibility = View.VISIBLE
}
- /**
- * Set the message textView
- * @param message the whole message to write in the textView
- */
- fun setTextViewMessage(message: String?) {
- messageTextView.text = message
- messageTextView.visibility = View.VISIBLE
- }
-
fun toggleLastStopToFavorites() {
val stop: Stop? = lastUpdatedPalina
if (stop != null) {
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
@@ -45,6 +45,7 @@
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
@@ -326,7 +327,7 @@
}
// Hide the bottom sheet and remove extra symbol
- protected fun hideStopOrBusBottomSheet(){
+ protected open fun hideStopOrBusBottomSheet(){
if (stopActiveSymbol!=null){
symbolManager?.delete(stopActiveSymbol)
stopActiveSymbol = null
@@ -534,7 +535,7 @@
*/
protected fun showVehicleTripInBottomSheet(
veh: String,
- onDirectionsClick: (patternCode: String) -> Unit
+ onDirectionsClick: (patternCode: String, veh: String) -> Unit
) {
val data = updatesByVehDict[veh] ?: run {
Log.w(DEBUG_TAG, "Asked to show vehicle $veh, but it's not present in the updates")
@@ -542,7 +543,7 @@
}
bottomLayout?.let {
val lineName = FiveTNormalizer.fixShortNameForDisplay(
- GtfsUtils.getLineNameFromGtfsID(data.posUpdate.routeID), true
+ GtfsUtils.getLineNameFromGtfsID(data.posUpdate.routeID), false
)
val pat = data.pattern
if (pat != null) {
@@ -554,7 +555,7 @@
stopNumberTextView.text = getString(R.string.line_fill, lineName)
}
directionsCard.setOnClickListener {
- onDirectionsClick(pat?.code ?: "")
+ onDirectionsClick(pat?.code ?: "", veh)
}
directionsCard.visibility = View.VISIBLE
bottomrightImage.setImageDrawable(
@@ -700,6 +701,7 @@
val string_show = if (stop.numRoutesStopping==0) ""
else requireContext().getString(R.string.lines_fill, stop.routesThatStopHereToString())
linesPassingTextView.text = string_show
+ linesPassingTextView.visibility = View.VISIBLE
//SET ON CLICK LISTENER
arrivalsCard.setOnClickListener{
@@ -911,6 +913,13 @@
updatePositionsIcons(forced = false)
}
+ protected fun setCameraPosition(latitude: Double, longitude: Double, zoom: Double) {
+ map?.cameraPosition = CameraPosition.Builder()
+ .target(LatLng(latitude, longitude))
+ .zoom(zoom)
+ .build()
+ }
+
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
@@ -23,7 +23,6 @@
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
-import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
@@ -35,18 +34,15 @@
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
-import androidx.core.view.ViewCompat
import androidx.fragment.app.viewModels
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.gson.JsonObject
import it.reyboz.bustorino.R
import it.reyboz.bustorino.adapters.NameCapitalize
import it.reyboz.bustorino.adapters.StopAdapterListener
import it.reyboz.bustorino.adapters.StopRecyclerAdapter
-import it.reyboz.bustorino.backend.FiveTNormalizer
import it.reyboz.bustorino.backend.Stop
import it.reyboz.bustorino.backend.gtfs.GtfsUtils
import it.reyboz.bustorino.backend.gtfs.PolylineParser
@@ -101,7 +97,7 @@
private var patternShown: MatoPatternWithStops? = null
private val viewModel: LinesViewModel by viewModels()
- private var firstInit = true
+ //private var firstInit = true
private var pausedFragment = false
private lateinit var switchButton: ImageButton
@@ -263,8 +259,7 @@
}
val titleTextView = rootView.findViewById<TextView>(R.id.titleTextView)
- titleTextView.text = getString(R.string.line)+" "+FiveTNormalizer.fixShortNameForDisplay(
- GtfsUtils.getLineNameFromGtfsID(lineID), true)
+ titleTextView.text = getString(R.string.line)+" "+ GtfsUtils.lineNameDisplayFromGtfsID(lineID)
favoritesButton?.isClickable = true
favoritesButton?.setOnClickListener {
@@ -342,24 +337,27 @@
usingMQTTPositions = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getString(keySourcePositions, "mqtt").contentEquals("mqtt")
- viewModel.patternsWithStopsByRouteLiveData.observe(viewLifecycleOwner){
- patterns -> savePatternsToShow(patterns)
- }
+ viewModel.patternsWithStopsByRouteLiveData.observe(viewLifecycleOwner, this::savePatternsToShow)
/*
*/
viewModel.stopsForPatternLiveData.observe(viewLifecycleOwner) { stops ->
- 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!")
- }
- else{
- if(stopsRecyclerView.visibility==View.VISIBLE)
+ val pattern = viewModel.selectedPatternLiveData.value
+ if (pattern == null) {
+ Log.w(DEBUG_TAG, "The selectedPattern is null!")
+ return@observe
+ }
+ if(mapView.visibility ==View.VISIBLE) {
+ // 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
+
+ // val shouldZoom = (shownStopInBottomSheet == null) //use this if we want to avoid zoom when we're keeping the stop open
+ displayPatternWithStopsOnMap(pattern, stops, true)
+ } else {
+ if(stopsRecyclerView.visibility==View.VISIBLE) {
+ patternShown = pattern
showStopsInRecyclerView(stops)
+ }
}
}
viewModel.gtfsRoute.observe(viewLifecycleOwner){route->
@@ -395,6 +393,20 @@
updatePositionsIcons(true)
livePositionsViewModel.retriggerPositionUpdate()
}
+ if (shownStopInBottomSheet!=null){
+ //check if the stop is inside the new pattern
+ /*val s = shownStopInBottomSheet!!
+ val newPatternStops = patternWithStops.stopsIndices
+ val filterPStops = newPatternStops.filter { ps -> ps.stopGtfsId == "gtt:${s.ID}" }
+ if (filterPStops.isEmpty()){
+ hideStopOrBusBottomSheet()
+ }
+ */
+ // do another thing, just close the stop when the pattern is changed
+ if (patt.code != patternWithStops.pattern.code){
+ hideStopOrBusBottomSheet()
+ }
+ }
}
}
livePositionsViewModel.setGtfsLineToFilterPos(lineID, patternWithStops.pattern)
@@ -576,16 +588,16 @@
mapReady.addOnMapClickListener { point ->
val screenPoint = mapReady.projection.toScreenLocation(point)
- val features = mapReady.queryRenderedFeatures(screenPoint, STOPS_LAYER_ID)
+ val stopsNearby = mapReady.queryRenderedFeatures(screenPoint, STOPS_LAYER_ID)
val busNearby = mapReady.queryRenderedFeatures(screenPoint, BUSES_LAYER_ID)
- if (features.isNotEmpty()) {
- val feature = features[0]
+ //Log.d(DEBUG_TAG, "onMapClick, stopsNearby: $stopsNearby \nstopShown: $shownStopInBottomSheet \nbusNearby: $busNearby,")
+
+ if (stopsNearby.isNotEmpty()) {
+ val feature = stopsNearby[0]
val id = feature.getStringProperty("id")
- val name = feature.getStringProperty("name")
- //Toast.makeText(requireContext(), "Clicked on $name ($id)", Toast.LENGTH_SHORT).show()
val stop = viewModel.getStopByID(id)
stop?.let {
- if (isBottomSheetShowing() || vehShowing.isNotEmpty()){
+ if (isBottomSheetShowing() || vehShowing.isNotEmpty()) {
hideStopOrBusBottomSheet()
}
openStopInBottomSheet(it)
@@ -597,21 +609,7 @@
return@addOnMapClickListener true
} else if (busNearby.isNotEmpty()){
val feature = busNearby[0]
- val vehid = feature.getStringProperty("veh")
- val route = feature.getStringProperty("line")
- if(isBottomSheetShowing())
- hideStopOrBusBottomSheet()
- //if(context!=null){
- // Toast.makeText(context, "Veh $vehid on route ${route.slice(0..route.length-2)}", Toast.LENGTH_SHORT).show()
- //}
- showVehicleTripInBottomSheet(vehid)
- updatesByVehDict[vehid]?.let {
- //if (it.posUpdate.latitude != null && it.longitude != null)
- mapReady.animateCamera(
- CameraUpdateFactory.newLatLng(LatLng(it.posUpdate.latitude, it.posUpdate.longitude)),
- 750
- )
- }
+ openBusFromMapClick(feature)
return@addOnMapClickListener true
}
@@ -626,7 +624,7 @@
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()
+ mapReady.cameraPosition = savedCameraPosition ?:CameraPosition.Builder().target(latlngTarget).zoom(zoom).build()
savedCameraPosition = null
@@ -637,9 +635,23 @@
return true
}
- private fun observeBusPositionUpdates(){
-
+ /**
+ * Separate function to find the vehicle associated with a feature and display it
+ */
+ private fun openBusFromMapClick(feature: Feature){
+ val vehid = feature.getStringProperty("veh")
+ if(isBottomSheetShowing())
+ hideStopOrBusBottomSheet()
+ showVehicleTripInBottomSheet(vehid)
+ updatesByVehDict[vehid]?.let {
+ map?.animateCamera(
+ CameraUpdateFactory.newLatLng(LatLng(it.posUpdate.latitude, it.posUpdate.longitude)),
+ 750
+ )
+ }
+ }
+ private fun observeBusPositionUpdates(){
//live bus positions
livePositionsViewModel.filteredLocationUpdates.observe(viewLifecycleOwner){ pair ->
//Log.d(DEBUG_TAG, "Received ${updates.size} updates for the positions")
@@ -672,12 +684,27 @@
}
private fun showVehicleTripInBottomSheet(veh: String) {
- super.showVehicleTripInBottomSheet(veh) { patternCode ->
+ super.showVehicleTripInBottomSheet(veh) { patternCode, veh ->
//this is checked in @GeneralMapLibreFragment
//val data = updatesByVehDict[veh] ?: return@showVehicleTripInBottomSheet
if (patternCode.isEmpty()) return@showVehicleTripInBottomSheet
if (patternShown?.pattern?.code == patternCode) {
- Toast.makeText(context, R.string.showing_same_direction, Toast.LENGTH_SHORT).show()
+ //center view on vehicle
+ updatesByVehDict[veh]?.let { up->
+ map?.let{
+ /*
+ val c = it.cameraPosition
+ it.moveCamera(CameraUpdateFactory.CameraPositionUpdate(c.bearing,
+ LatLng(up.posUpdate.latitude, up.posUpdate.longitude),
+ c.tilt,c.zoom, c.padding)
+ )
+ */
+ it.animateCamera(CameraUpdateFactory.newLatLng(LatLng(up.posUpdate.latitude, up.posUpdate.longitude)))
+ }
+ } ?: {
+ Toast.makeText(context, R.string.showing_same_direction, Toast.LENGTH_SHORT).show()
+ }
+
} else {
showPatternWithCode(patternCode)
}
@@ -749,7 +776,7 @@
var p: MatoPatternWithStops? = null
if (patternIdToShow.isNotEmpty()){
- for (patt in currentPatterns) {
+ for (patt in patterns) {
if (patt.pattern.code == patternIdToShow){
p = patt
}
@@ -763,7 +790,7 @@
else if(stopIDFromToShow.isNotEmpty()) {
val stopGtfsID = "gtt:$stopIDFromToShow"
var pLength = 0
- for (patt in currentPatterns) {
+ for (patt in patterns) {
for (pstop in patt.stopsIndices) {
if (pstop.stopGtfsId == stopGtfsID) {
//found
@@ -781,9 +808,9 @@
else
Log.d(DEBUG_TAG, "Requesting to show pattern from stop $stopIDFromToShow, found pattern ${p.pattern.code}")
}
-
- stopIDFromToShow = ""
+ // the flag of showing pattern is not necessary anymore, we have set the pattern
patternIdToShow = ""
+ // the flag of selecting from stop needs to be used again when displaying the pattern
return p
}
/**
@@ -798,7 +825,7 @@
it.addAll(currentPatterns.map { p->"${p.pattern.directionId} - ${p.pattern.headsign}" })
it.notifyDataSetChanged()
}
- val patternToShow = filterPatternFromArgs(patterns)
+ val patternToShow = filterPatternFromArgs(currentPatterns)
if(patternToShow!=null) {
//showPattern(patternToShow)
patternShown = patternToShow
@@ -816,7 +843,6 @@
Log.d(DEBUG_TAG, "Requesting stops for pattern ${patternWithStops.pattern.code}")
viewModel.selectedPatternLiveData.value = patternWithStops
viewModel.currentPatternStops.value = patternWithStops.stopsIndices.sortedBy { i-> i.order }
- patternShown = patternWithStops
viewModel.requestStopsForPatternWithStops(patternWithStops)
}
@@ -831,12 +857,11 @@
}
}
Log.d(DEBUG_TAG, "Requesting stops fro pattern $code in position: $pos")
+ // this triggers the showing on the map / recyclerview
if (pos !=-2)
patternsSpinner.setSelection(pos)
else
Log.e(DEBUG_TAG, "Pattern with code $code not found!!")
- //request pattern stops from DB
- //setPatternAndReqStops(patternWs)
}
/**
@@ -957,12 +982,28 @@
Log.e(DEBUG_TAG, "Stops layer is not started!!")
}
+ var reallyZoomToPattern = zoomToPattern
+ if(stopIDFromToShow.isNotEmpty()){
+ //open the stop
+ val stopfilt = stopsSorted.filter { s -> s.ID == stopIDFromToShow }
+ if (stopfilt.isEmpty()){
+ Log.e(DEBUG_TAG, "Tried to show stop but it's not in the selected pattern")
+ } else{
+ val stop = stopfilt[0]
+ openStopInBottomSheet(stop)
+
+ if(stop.hasCoords()) {
+ reallyZoomToPattern = false
+ setCameraPosition(stop.latitude!!, stop.longitude!!, 13.5)
+ }
+
+ }
+ // Reset this to avoid checking again when showing
+ stopIDFromToShow = ""
+ //camera set
+ }
+ if(reallyZoomToPattern) zoomToCurrentPattern()
- //POINTS LIST IS NOT IN ORDER ANY MORE
- //if(!map.overlayManager.contains(stopsOverlay)){
- // map.overlayManager.add(stopsOverlay)
- //}
- if(zoomToPattern) zoomToCurrentPattern()
}
private fun initializeRecyclerView(){
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
@@ -17,6 +17,9 @@
import androidx.recyclerview.widget.RecyclerView
import androidx.work.WorkInfo
import androidx.work.WorkManager
+import com.google.android.flexbox.FlexDirection
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.flexbox.JustifyContent
import it.reyboz.bustorino.R
import it.reyboz.bustorino.adapters.RouteAdapter
import it.reyboz.bustorino.adapters.RouteOnlyLineAdapter
@@ -30,7 +33,6 @@
import it.reyboz.bustorino.util.ViewUtils
import it.reyboz.bustorino.viewmodels.LinesGridShowingViewModel
-
class LinesGridShowingFragment : ScreenBaseFragment() {
@@ -63,6 +65,13 @@
private val linesComparator = Comparator<GtfsRoute> { a,b ->
return@Comparator linesNameSorter.compare(a.shortName, b.shortName)
}
+ private val linesPriorityComparator = Comparator<Pair<GtfsRoute,Int>> { pa, pb ->
+ if (pa.second != pb.second){
+ return@Comparator pa.second - pb.second
+ } else{
+ return@Comparator linesNameSorter.compare(pa.first.shortName, pb.first.shortName)
+ }
+ }
private val routeClickListener = RouteAdapter.ItemClicker {
fragmentListener.openLineFromStop(it.gtfsId, null)
@@ -73,6 +82,13 @@
private val lastQueryEmptyForAgency = HashMap<String, Boolean>(3)
private var openRecyclerView = "AG_URBAN"
+ private fun getFlexLayoutManager(context: Context): FlexboxLayoutManager{
+ val layoutManager = FlexboxLayoutManager(context)
+ layoutManager.flexDirection = FlexDirection.ROW
+ layoutManager.justifyContent = JustifyContent.FLEX_START
+
+ return layoutManager
+ }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -105,37 +121,36 @@
recyView.layoutManager = gridLayoutManager
}
//init favorites recyclerview
- val gridLayoutManager = AutoFitGridLayoutManager(
- requireContext().applicationContext,
- (utils.convertDipToPixels(context, 70f)).toInt()
- )
- favoritesRecyclerView.layoutManager = gridLayoutManager
+ favoritesRecyclerView.layoutManager = getFlexLayoutManager(requireContext())
+
+ viewModel.getLinesLiveData().observe(viewLifecycleOwner){ rL ->
- viewModel.getLinesLiveData().observe(viewLifecycleOwner){
- //routesList = ArrayList(it)
- //routesList.sortWith(linesComparator)
routesByAgency.clear()
for (k in AGENCIES){
routesByAgency[k] = ArrayList()
}
-
- for(route in it){
+ val routesPrioByAg = HashMap<String, ArrayList<Pair<GtfsRoute,Int>>>()
+ for (ag in AGENCIES){
+ routesPrioByAg[ag] = ArrayList()
+ }
+ for(p in rL){
+ val route = p.first
val agency = route.agencyID
if(agency !in routesByAgency.keys){
- Log.e(DEBUG_TAG, "The agency $agency is not present in the predefined agencies (${routesByAgency.keys})")
+ Log.e(DEBUG_TAG, "The agency $agency for route ${p.first.gtfsId} is not in the predefined agencies (${routesByAgency.keys})")
}
routesByAgency[agency]?.add(route)
+ routesPrioByAg[agency]?.add(p) // I would print a debug here, but it's the same as above
}
//zip agencies and recyclerviews
- Companion.AGENCIES.zip(recViews) { ag, recView ->
- routesByAgency[ag]?.let { routeList ->
- if (routeList.size > 0) {
- routeList.sortWith(linesComparator)
- //val adapter = RouteOnlyLineAdapter(it.map { rt -> rt.shortName })
- val adapter = RouteAdapter(routeList, routeClickListener)
+ AGENCIES.zip(recViews) { ag, recView ->
+ routesPrioByAg[ag]?.let { routePrioList ->
+ if (routePrioList.isNotEmpty()) {
+ routePrioList.sortWith(linesPriorityComparator)
+ val adapter = RouteAdapter(routePrioList.map { it.first }, routeClickListener)
val lastQueryEmpty = if(ag in lastQueryEmptyForAgency.keys) lastQueryEmptyForAgency[ag]!! else true
if (lastQueryEmpty)
recView.adapter = adapter
@@ -148,7 +163,7 @@
lastQueryEmptyForAgency[ag] = true
}
- durations[ag] = if(routeList.size < 20) ViewUtils.DEF_DURATION else 1000
+ durations[ag] = if(routePrioList.size < 20) ViewUtils.DEF_DURATION else 1000
}
}
@@ -390,7 +405,7 @@
companion object {
- private const val COLUMN_WIDTH_DP=200
+ private const val COLUMN_WIDTH_DP=250
private const val AG_FAV = "fav"
private const val AG_URBAN = "gtt:U"
private const val AG_EXTRAURB ="gtt:E"
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
@@ -161,6 +161,12 @@
super.onCreate(savedInstanceState)
arguments?.let {
initialStopToShow = Stop.fromBundle(arguments)
+ if (initialStopToShow==null){
+
+ } else if(!initialStopToShow!!.hasCoords()){
+ //null the stop if it doesn't have coordinates, we cannot find it
+ initialStopToShow = null
+ }
}
}
@@ -352,9 +358,11 @@
if (initialStopToShow!=null){
val s = initialStopToShow!!
- mapReady.cameraPosition = CameraPosition.Builder().target(
- LatLng(s.latitude!!, s.longitude!!)
- ).zoom(DEFAULT_ZOOM).build()
+ if(s.hasCoords()){
+ mapReady.cameraPosition = CameraPosition.Builder().target(
+ LatLng(s.latitude!!, s.longitude!!)
+ ).zoom(DEFAULT_ZOOM).build()
+ }
restoredMapCamera.set(true)
} else{
var boundsRestored = false
@@ -437,6 +445,12 @@
override fun showOpenStopWithSymbolLayer(): Boolean {
return false
}
+ override fun hideStopOrBusBottomSheet(){
+ if (shownStopInBottomSheet?.ID == initialStopToShow?.ID){
+ initialStopToShow = null
+ }
+ super.hideStopOrBusBottomSheet()
+ }
override fun onAttach(context: Context) {
super.onAttach(context)
@@ -536,7 +550,7 @@
private fun showVehicleTripInBottomSheet(veh: String) {
val data = updatesByVehDict[veh] ?: return
- super.showVehicleTripInBottomSheet(veh) { patternCode ->
+ super.showVehicleTripInBottomSheet(veh) { patternCode, _ ->
map?.let { mapStateViewModel.saveMapState(it) }
fragmentListener?.openLineFromVehicle(
data.posUpdate.getLineGTFSFormat(),
@@ -553,7 +567,8 @@
initialStopToShow?.let{ s->
//show the stop in the bottom sheet
if(!initialStopShown && (s.ID in stopsShowing.map { it.ID })) {
- openStopInBottomSheet(s)
+ val stopToShow = stopsShowing.first { it.ID == s.ID }
+ openStopInBottomSheet(stopToShow)
initialStopShown = true
}
}
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ResultListFragment.java
@@ -34,7 +34,6 @@
import android.view.ViewGroup;
import android.widget.*;
-import com.google.android.material.floatingactionbutton.FloatingActionButton;
import it.reyboz.bustorino.R;
import it.reyboz.bustorino.backend.FiveTNormalizer;
@@ -167,7 +166,7 @@
});
String displayName = getArguments().getString(ArrivalsFragment.STOP_TITLE);
setTextViewMessage(String.format(
- getString(R.string.passages), displayName));
+ getString(R.string.passages_fill), displayName));
break;
default:
throw new IllegalStateException("Argument passed was not of a supported type");
diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java
--- a/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java
+++ b/app/src/main/java/it/reyboz/bustorino/fragments/ScreenBaseFragment.java
@@ -3,8 +3,6 @@
import android.Manifest;
import android.content.Context;
import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.view.Gravity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/ArrivalsViewModel.kt
@@ -5,22 +5,22 @@
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import it.reyboz.bustorino.backend.*
import it.reyboz.bustorino.backend.mato.MatoAPIFetcher
import it.reyboz.bustorino.data.NextGenDB
+import it.reyboz.bustorino.data.OldDataRepository
import it.reyboz.bustorino.middleware.RecursionHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicReference
class ArrivalsViewModel(application: Application): AndroidViewModel(application) {
// Arrivals of palina
val appContext: Context
- init {
- appContext = application.applicationContext
- }
val palinaLiveData = MediatorLiveData<Palina>()
val sourcesLiveData = MediatorLiveData<Passaggio.Source>()
@@ -29,9 +29,47 @@
val currentFetchers = MediatorLiveData<List<ArrivalsFetcher>>()
+ /// OLD REPO for stops instance
+ private val executor = Executors.newFixedThreadPool(2)
+ private val oldRepo = OldDataRepository(executor, NextGenDB.getInstance(application))
+
+ private var stopIdRequested = ""
+ private val stopFromDB = MutableLiveData<Stop>()
+ private val oldRepoStopCallback = OldDataRepository.Callback<List<Stop>>{ stopListRes ->
+ if(stopIdRequested.isEmpty()) return@Callback
+
+ if(stopListRes.isSuccess) {
+ val stopF = stopListRes.result!!.filter { s -> s.ID == stopIdRequested }
+ if (stopF.isEmpty()) {
+ Log.w(DEBUG_TAG, "Requested stop $stopIdRequested but is not in the list from database: ${stopListRes.result}")
+ } else{
+ stopFromDB.postValue(stopF[0])
+ Log.d(DEBUG_TAG, "Setting new stop ${stopF[0]} from database")
+ }
+ } else{
+ Log.e(DEBUG_TAG, "Requested stop ${stopIdRequested} from database but error occured: ${stopListRes.exception}")
+ }
+ }
+
+ init {
+ appContext = application.applicationContext
+ palinaLiveData.addSource(stopFromDB){
+ s ->
+ val hasSource = palinaLiveData.value?.passaggiSourceIfAny
+ Log.d(DEBUG_TAG, "Have current palina ${palinaLiveData.value!=null}, source passaggi $hasSource, new incoming stop $s from database")
+ val newp = Palina.mergePaline(palinaLiveData.value, Palina(s))
+ newp?.let { palinaLiveData.value = it }
+ }
+ }
+
+
fun requestArrivalsForStop(stopId: String, fetchers: List<ArrivalsFetcher>){
val context = appContext //application.applicationContext
currentFetchers.value = fetchers
+ //request stop from the DB
+ stopIdRequested = stopId
+ oldRepo.requestStopsWithGtfsIDs(listOf("gtt:$stopId"), oldRepoStopCallback)
+
viewModelScope.launch(Dispatchers.IO){
runArrivalsFetching(stopId, fetchers, context)
}
@@ -127,9 +165,7 @@
// Se abbiamo un risultato OK, restituiamo la palina
if (resultRef.get() == Fetcher.Result.OK) {
- //set data
- resultLiveData.postValue(Fetcher.Result.OK)
- palinaLiveData.postValue(palina)
+ setResultAndPalinaFromFetchers(palina, Fetcher.Result.OK)
//TODO: Rotate the fetchers appropriately
return
}
@@ -140,13 +176,18 @@
//failedAll = true
// Se abbiamo comunque una palina, la restituiamo
- if (resultPalina != null) {
- resultLiveData.postValue(resultRef.get())
- palinaLiveData.postValue(resultPalina)
+ resultPalina?.let {
+ setResultAndPalinaFromFetchers(it, resultRef.get())
}
}
+ private fun setResultAndPalinaFromFetchers(palina: Palina, fetcherResult: Fetcher.Result) {
+ resultLiveData.postValue(fetcherResult)
+ Log.d(DEBUG_TAG, "Have new result palina for stop ${palina.ID}, source ${palina.passaggiSourceIfAny} has coords: ${palina.hasCoords()}")
+ Log.d(DEBUG_TAG, "Old palina liveData is: ${palinaLiveData.value?.stopDisplayName}, has Coords ${palinaLiveData.value?.hasCoords()}")
+ palinaLiveData.postValue(Palina.mergePaline(palina, palinaLiveData.value))
+ }
companion object{
const val DEBUG_TAG="BusTO-ArrivalsViMo"
diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
--- a/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
+++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/LinesGridShowingViewModel.kt
@@ -39,15 +39,27 @@
fun getLineQueryValue():String{
return queryLiveData.value ?: ""
}
- private val filteredLinesLiveData = MediatorLiveData<List<GtfsRoute>>()
- fun getLinesLiveData(): LiveData<List<GtfsRoute>> {
- return filteredLinesLiveData
- }
+ private val filteredLinesLiveData = MediatorLiveData<List<Pair<GtfsRoute,Int >>>()
+ fun getLinesLiveData() = filteredLinesLiveData
- private fun filterLinesForQuery(lines: List<GtfsRoute>, query: String): List<GtfsRoute>{
- val result= lines.filter { r-> query.lowercase() in r.shortName.lowercase() }
+ private fun filterLinesForQuery(lines: List<GtfsRoute>, query: String): ArrayList<Pair<GtfsRoute,Int>>{
+ var result= lines.filter { r-> query.lowercase() in r.shortName.lowercase() }
+ //EXCLUDE gtt:F - ferrovie (luckily, gtt does not run rail service anymore)
+ result = result.filter { r -> r.agencyID != "gtt:F" }
- return result
+ val out = ArrayList<Pair<GtfsRoute,Int>>()
+ for (r in result){
+ out.add(Pair(r,1))
+ }
+ // add those matching the query in the description
+ for (r: GtfsRoute in lines) {
+ if (query.lowercase() in r.description.lowercase()) {
+ if (r !in result){
+ out.add(Pair(r,2))
+ }
+ }
+ }
+ return out
}
init {
diff --git a/app/src/main/res/drawable/ic_star_filled.xml b/app/src/main/res/drawable/ic_star_filled.xml
--- a/app/src/main/res/drawable/ic_star_filled.xml
+++ b/app/src/main/res/drawable/ic_star_filled.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="28dp"
- android:height="28dp"
+ android:width="30dp"
+ android:height="30dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
diff --git a/app/src/main/res/drawable/ic_star_outline.xml b/app/src/main/res/drawable/ic_star_outline.xml
--- a/app/src/main/res/drawable/ic_star_outline.xml
+++ b/app/src/main/res/drawable/ic_star_outline.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="28dp"
- android:height="28dp"
+ android:width="30dp"
+ android:height="30dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
diff --git a/app/src/main/res/drawable/road_map_line.xml b/app/src/main/res/drawable/road_map_line.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/drawable/road_map_line.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="30dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:width="30dp">
+
+ <path android:fillColor="?colorPrimary" android:pathData="M4,6.143V18.967L9.065,16.796L15.065,19.796L20,17.681V4.857L21.303,4.299C21.557,4.19 21.851,4.307 21.96,4.561C21.986,4.624 22,4.691 22,4.758V19L15,22L9,19L2.697,21.701C2.443,21.81 2.149,21.692 2.04,21.439C2.014,21.376 2,21.309 2,21.242V7L4,6.143ZM16.243,11.243L12,15.485L7.757,11.243C5.414,8.899 5.414,5.101 7.757,2.757C10.101,0.414 13.899,0.414 16.243,2.757C18.586,5.101 18.586,8.899 16.243,11.243ZM12,12.657L14.828,9.828C16.39,8.266 16.39,5.734 14.828,4.172C13.266,2.609 10.734,2.609 9.172,4.172C7.609,5.734 7.609,8.266 9.172,9.828L12,12.657Z"/>
+
+</vector>
diff --git a/app/src/main/res/layout/entry_bus_line_passage.xml b/app/src/main/res/layout/entry_bus_line_passage.xml
--- a/app/src/main/res/layout/entry_bus_line_passage.xml
+++ b/app/src/main/res/layout/entry_bus_line_passage.xml
@@ -7,6 +7,9 @@
android:paddingBottom="8dp"
android:paddingStart="10dp"
android:paddingEnd="16dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
>
diff --git a/app/src/main/res/layout/entry_line_num_descr.xml b/app/src/main/res/layout/entry_line_name_description.xml
rename from app/src/main/res/layout/entry_line_num_descr.xml
rename to app/src/main/res/layout/entry_line_name_description.xml
--- a/app/src/main/res/layout/entry_line_num_descr.xml
+++ b/app/src/main/res/layout/entry_line_name_description.xml
@@ -4,7 +4,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
- android:layout_margin="4dp"
+ android:layout_margin="8dp"
>
<RelativeLayout
android:layout_width="match_parent"
@@ -19,7 +19,7 @@
app:cardElevation="0sp"
android:layout_gravity="center_vertical"
android:layout_margin="5dp"
- android:padding="3dp"
+ android:padding="6dp"
app:cardBackgroundColor="@color/orange_500"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
@@ -48,18 +48,23 @@
</TextView>
</RelativeLayout>
</androidx.cardview.widget.CardView>
- <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:id="@+id/lineDirectionTextView"
- android:layout_weight="8"
android:textSize="20sp"
- android:text="@string/route_towards_destination"
+ android:text="@string/hello_blank_fragment"
android:layout_margin="5dp"
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
- android:layout_marginEnd="10dp"
- android:layout_alignParentTop="true"
+ android:layout_marginEnd="2dp"
android:layout_toEndOf="@id/innerCardView"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginTop="5dp"
+ android:layout_marginStart="13dp"
+ android:layout_marginBottom="10dp"
+ android:minHeight="52sp"
/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_arrivals.xml b/app/src/main/res/layout/fragment_arrivals.xml
--- a/app/src/main/res/layout/fragment_arrivals.xml
+++ b/app/src/main/res/layout/fragment_arrivals.xml
@@ -13,37 +13,78 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
- app:cardElevation="4dp"
+ app:cardElevation="5dp"
android:layout_alignParentTop="true"
android:layout_marginStart="8sp"
android:layout_marginEnd="8sp"
android:layout_marginTop="5sp"
- android:padding="5sp"
+ android:padding="8sp"
>
-
- <TextView
- android:id="@+id/messageTextView"
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginLeft="8dp"
- android:layout_toStartOf="@+id/addToFavorites"
- android:layout_toLeftOf="@+id/addToFavorites"
-
- android:foreground="?attr/selectableItemBackground"
- android:gravity="center_vertical"
- android:minHeight="40dp"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-
- <ImageButton
- android:id="@+id/addToFavorites"
- android:layout_width="45dp"
- android:layout_height="match_parent"
- android:layout_gravity="end"
- android:background="@android:color/transparent"
- android:foreground="?attr/selectableItemBackground"
- app:srcCompat="@drawable/ic_star_outline"
- tools:ignore="OnClick"/>
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/arrivalsTextView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_margin="8dp"
+ android:gravity="center_vertical"
+ android:text="@string/passages"
+ android:minHeight="35dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ app:layout_constraintHorizontal_bias="0.0"
+ android:visibility="visible"/>
+ <TextView
+ android:id="@+id/messageTextView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginStart="12dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/addToFavorites"
+ app:layout_constraintStart_toEndOf="@id/arrivalsTextView"
+ android:foreground="?attr/selectableItemBackground"
+
+ android:gravity="center_vertical"
+ android:minHeight="35dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="19sp"/>
+ <ImageButton
+ android:id="@+id/addToFavorites"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:layout_gravity="end"
+ android:layout_margin="5dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/openInMapButton"
+ app:layout_constraintStart_toEndOf="@id/messageTextView"
+ android:background="@android:color/transparent"
+ android:foreground="?attr/selectableItemBackground"
+ app:srcCompat="@drawable/ic_star_outline"
+ tools:ignore="OnClick"
+ />
+ <ImageButton
+ android:id="@+id/openInMapButton"
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:layout_gravity="end"
+ android:layout_margin="5dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/addToFavorites"
+ android:background="@android:color/transparent"
+ android:foreground="?attr/selectableItemBackground"
+ app:srcCompat="@drawable/road_map_line"
+ app:tint="?colorPrimary"
+ tools:ignore="OnClick"
+ android:layout_marginEnd="12dp"
+ />
+ </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
diff --git a/app/src/main/res/menu/menu_arrivals_line_item.xml b/app/src/main/res/menu/menu_arrivals_line_item.xml
new file mode 100644
--- /dev/null
+++ b/app/src/main/res/menu/menu_arrivals_line_item.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/action_open_line"
+ android:title="@string/action_open_line"/>
+ <item
+ android:id="@+id/action_show_direction"
+ android:title="@string/action_show_direction"/>
+</menu>
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -41,7 +41,7 @@
<string name="message_check_at_least_one">Vérifiez cocher au moins un élément à importer !</string>
<string name="load_file_favorites">Importer les favoris depuis une sauvegarde</string>
<string name="load_preferences">Importer les préférences depuis une sauvegarde</string>
- <string name="passages">Arrivées à: %1$s</string>
+ <string name="passages_fill">Arrivées à: %1$s</string>
<string name="action_about_more">En savoir plus</string>
<string name="action_author">Rencontrer l\'auteur</string>
<string name="action_help">Aide</string>
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -18,7 +18,8 @@
<string name="no_arrivals_stop">Nessun passaggio trovato alla fermata</string>
<string name="searching_arrivals_fmt">Ricerca arrivi da %1$s</string>
<string name="parsing_error">Errore di lettura del sito 5T/GTT (dannato sito!)</string>
- <string name="passages">Fermata: %1$s</string>
+ <string name="passages_fill">Fermata: %1$s</string>
+ <string name="passages">Fermata:</string>
<string name="line">Linea</string>
<string name="lines">Linee</string>
<string name="urban_lines">Linee urbane</string>
@@ -45,6 +46,8 @@
<string name="action_source">Codice sorgente</string>
<string name="action_licence">Licenza</string>
<string name="action_author">Incontra l\'autore</string>
+ <string name="action_open_line">Mostra linea</string>
+ <string name="action_show_direction">Vedi direzione</string>
<string name="added_in_favorites">Fermata aggiunta ai preferiti</string>
<string name="cant_add_to_favorites">Impossibile aggiungere ai preferiti (memoria piena o database corrotto?)!</string>
<string name="title_activity_favorites">Preferiti</string>
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -140,7 +140,7 @@
<string name="insert_bus_stop_number_error">Voer bushalte nummer in</string>
<string name="network_error">Check je internetverbinding!</string>
<string name="query_too_short">Te korte naam, typ meer karakters en probeer opnieuw</string>
- <string name="passages">Aankomsten om: %1$s</string>
+ <string name="passages_fill">Aankomsten om: %1$s</string>
<string name="lines_fill">Lijnen: %1$s</string>
<string name="action_about_more">Meer over</string>
<string name="title_activity_favorites">Favorieten</string>
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
@@ -31,7 +31,8 @@
<string name="parsing_error">Error parsing the 5T/GTT website (damn site!)</string>
<string name="query_too_short">Name too short, type more characters and retry
</string> <!-- TODO: carry out experiments to determine the best wording for this message and publish a paper with the findings -->
- <string name="passages">Arrivals at: %1$s</string>
+ <string name="passages_fill">Arrivals at: %1$s</string>
+ <string name="passages">Arrivals at:</string>
<string name="results">Choose the bus stop…</string>
<string name="line">Line</string>
<string name="lines">Lines</string>
@@ -142,6 +143,8 @@
<string name="cant_add_to_favorites">Cannot add to favorites (storage full or corrupted database?)!</string>
<string name="action_view_on_map">View on a map</string>
+ <string name="action_open_line">Show line details</string>
+ <string name="action_show_direction">Show full direction</string>
<string name="cannot_show_on_map_no_activity">Cannot find any application to show it in</string>
<string name="cannot_show_on_map_no_position">Cannot find the position of the stop</string>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Apr 22, 15:28 (21 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1864826
Default Alt Text
D222.1776864501.diff (69 KB)
Attached To
Mode
D222: Show stop from arrivals and many other improvements
Attached
Detach File
Event Timeline
Log In to Comment