commit 33d3e0ab4087aeea4f45ee56c3bd306c88b9be4e Author: Fabio Mazza Date: Fri May 22 13:56:19 2026 +0200 Restore GTFS realtime, show type of vehicle in the map Summary: Fix URLs (needed https asd) Prepare for showing alerts in a stop Incomplete implementation of showing a warning when there alerts related to a stop. Need more data (GTFS stops.txt table) to show the alerts for a stop Fix T1113 Test Plan: Check that GTFS realtime positions work. Check that alerts are shown for a route/line. Open a bus on the map and see that there is an icon showing the vehicle type Reviewers: #libre_busto_hackers, valerio.bozzolan Reviewed By: #libre_busto_hackers, valerio.bozzolan Subscribers: valerio.bozzolan Project Tags: #libre_busto Maniphest Tasks: T1113 Differential Revision: https://gitpull.it/D236 diff --git a/app/src/main/java/it/reyboz/bustorino/backend/VehicleUtils.kt b/app/src/main/java/it/reyboz/bustorino/backend/VehicleUtils.kt new file mode 100644 index 0000000..e87bd82 --- /dev/null +++ b/app/src/main/java/it/reyboz/bustorino/backend/VehicleUtils.kt @@ -0,0 +1,92 @@ +package it.reyboz.bustorino.backend + +import it.reyboz.bustorino.backend.VehicleUtils.VehicleType +import java.util.Locale.getDefault + +data class VehicleClassInfo( + val vehClass: Int, + val name: String, + val kindString: String, + val type: VehicleType, + val matricolaStart: Int, + val matricolaEnd: Int, +) { + constructor(vehicleClass: Int, name: String, type: String, matricolaStart: Int, matricolaEnd: Int) : this( + vehicleClass, name, type,VehicleType.fromString(type), matricolaStart, matricolaEnd + ) +} + +object VehicleUtils { + private val items = listOf( + VehicleClassInfo(2800, "Arancio", "Tram", 2801, 2857), + VehicleClassInfo(2800, "Arancio", "Tram", 2858, 2902), + VehicleClassInfo(5000, "5000", "Tram", 5000, 5053), + VehicleClassInfo(6000, "6000", "Tram", 6000, 6005), + VehicleClassInfo(6000, "6000", "Tram", 6006, 6054), + VehicleClassInfo(8000, "Hitachi", "Tram", 8000, 8099), + VehicleClassInfo(800, "Citelis 18m", "Bus 18m", 790, 797), + VehicleClassInfo(800, "Citelis 18m", "Bus 18m", 800, 869), + VehicleClassInfo(800, "Citelis 18m", "Bus 18m", 870, 874), + VehicleClassInfo(800, "Citelis 18m", "Bus 18m", 1310, 1313), + VehicleClassInfo(1350, "Conecto 18m", "Bus 18m", 1350, 1396), + VehicleClassInfo(9300, "UrbanWay 18m", "Bus 18m", 9300, 9318), + VehicleClassInfo(9300, "UrbanWay 18m", "Bus 18m", 9320, 9356), + VehicleClassInfo(30, "BYD K9", "E-Bus", 30, 49), + VehicleClassInfo(50, "BYD K7", "E-Bus", 50, 57), + VehicleClassInfo(60, "MiniBusE", "E-Bus", 60, 81), + VehicleClassInfo(110, "Neocity", "Bus", 110, 115), + VehicleClassInfo(2300, "Cityclass", "Bus", 2300, 2349), + VehicleClassInfo(3400, "Conecto", "Bus", 2400, 2447), + VehicleClassInfo(2300, "Cityclass", "Bus", 2700, 2787), + VehicleClassInfo(3000, "Citelis", "Bus", 3000, 3099), + VehicleClassInfo(3000, "Citelis", "Bus", 3300, 3380), + VehicleClassInfo(3400, "Conecto", "Bus", 3400, 3440), + VehicleClassInfo(9000, "BYD K9", "E-Bus", 9000, 9059), + VehicleClassInfo(9000, "BYD K9", "E-Bus", 9060, 9119), + VehicleClassInfo(9000, "BYD K9", "E-Bus", 9120, 9121), + VehicleClassInfo(9200, "Citymood", "Bus", 9200, 9251), + VehicleClassInfo(9200, "Citymood", "Bus", 9252, 9261), + VehicleClassInfo(9400, "E-Way", "E-Bus", 9400, 9599), + VehicleClassInfo(9600, "E-Way 18m", "E-Bus", 9600, 9699) + ) + + fun getTypeForLabel(label: String): VehicleClassInfo? { + try { + val matricola = Integer.parseInt(label) + for (el in items) { + if(matricola >= el.matricolaStart && matricola<= el.matricolaEnd) { + return el + } + } + return null + + } catch (e: Exception) { + return null + } + } + enum class VehicleType { + BUS, TRAM, ELECTRIC_BUS; + + fun getName(): String { + return when (this) { + BUS -> "Bus" + TRAM -> "Tram" + ELECTRIC_BUS -> "E-Bus" + } + } + + companion object { + @JvmStatic + fun fromString(string: String): VehicleType { + return when (string.lowercase(getDefault())) { + "bus" -> BUS + "bus 18m" -> BUS + "tram" -> TRAM + "e-bus" -> ELECTRIC_BUS + "e-bus 18m" -> ELECTRIC_BUS + else -> throw IllegalArgumentException("Unknown vehicle type: $string") + } + } + } + } +} \ No newline at end of file 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 index 8ace7f2..f4e28a5 100644 --- a/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsUtils.java +++ b/app/src/main/java/it/reyboz/bustorino/backend/gtfs/GtfsUtils.java @@ -23,10 +23,10 @@ import it.reyboz.bustorino.backend.ServiceType; abstract public class GtfsUtils { - public static final String GTFSRT_URL_POSITION = "http://percorsieorari.gtt.to.it/das_gtfsrt/vehicle_position.aspx"; + public static final String GTFSRT_URL_POSITION = "https://percorsieorari.gtt.to.it/das_gtfsrt/vehicle_position.aspx"; - public static final String GTFSRT_URL_TRIP_UPDATES ="http://percorsieorari.gtt.to.it/das_gtfsrt/trip_update.aspx"; - public static final String GTFSRT_URL_ALERTS = "http://percorsieorari.gtt.to.it/das_gtfsrt/alerts.aspx"; + public static final String GTFSRT_URL_TRIP_UPDATES ="https://percorsieorari.gtt.to.it/das_gtfsrt/trip_update.aspx"; + public static final String GTFSRT_URL_ALERTS = "https://percorsieorari.gtt.to.it/das_gtfsrt/alerts.aspx"; public static String stripGtfsPrefix(String routeID){ String[] explo = routeID.split(":"); diff --git a/app/src/main/java/it/reyboz/bustorino/data/GtfsAlertDBDownloadWorker.kt b/app/src/main/java/it/reyboz/bustorino/data/GtfsAlertDBDownloadWorker.kt index 32d7b15..6f54cb4 100644 --- a/app/src/main/java/it/reyboz/bustorino/data/GtfsAlertDBDownloadWorker.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/GtfsAlertDBDownloadWorker.kt @@ -3,12 +3,15 @@ package it.reyboz.bustorino.data import android.app.NotificationManager import android.content.Context import android.util.Log +import androidx.lifecycle.LiveData import androidx.work.BackoffPolicy import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.ForegroundInfo import androidx.work.OneTimeWorkRequest import androidx.work.OutOfQuotaPolicy +import androidx.work.WorkInfo +import androidx.work.WorkManager import androidx.work.WorkerParameters import com.android.volley.Response import com.android.volley.VolleyError @@ -18,6 +21,7 @@ import it.reyboz.bustorino.R import it.reyboz.bustorino.backend.NetworkVolleyManager import it.reyboz.bustorino.backend.Notifications import it.reyboz.bustorino.backend.gtfs.GtfsRtAlertsRequest +import it.reyboz.bustorino.data.DBUpdateWorker.Companion.WORK_NAME import it.reyboz.bustorino.data.GtfsMaintenanceWorker.Companion.OPERATION_TYPE import it.reyboz.bustorino.data.gtfs.GtfsAlertsActivePeriods import it.reyboz.bustorino.data.gtfs.GtfsAlertsTranslation @@ -48,26 +52,26 @@ class GtfsAlertDBDownloadWorker(appContext: Context, workerParams: WorkerParamet val req = GtfsRtAlertsRequest(object : Response.ErrorListener { override fun onErrorResponse(err: VolleyError) { - Log.e(DEBUG_TAG, "Error getting alerts: ${err.message}", err) + Log.e(DEBUG_TAG, "Error getting alerts, message: ${err.message}", err) } }, future) volleyManager.requestQueue.add(req) try { - resuList = future.get(10, TimeUnit.SECONDS) + resuList = future.get(15, TimeUnit.SECONDS) if (resuList.isNotEmpty()){ Log.d(DEBUG_TAG, "Have no alerts, attempt $attempts") notOK = false } } catch (e: InterruptedException) { - e.printStackTrace() - Log.e(DEBUG_TAG, e.message, e) + //e.printStackTrace() + Log.w(DEBUG_TAG, "Interrupted: ", e) } catch (e: ExecutionException) { - e.printStackTrace() - Log.e(DEBUG_TAG, e.message, e) + //e.printStackTrace() + Log.w(DEBUG_TAG, e.message, e) } catch (e: TimeoutException) { - e.printStackTrace() - Log.e(DEBUG_TAG, e.message, e) + //e.printStackTrace() + Log.w(DEBUG_TAG, "Timeout for download", e) } attempts++ @@ -109,12 +113,20 @@ class GtfsAlertDBDownloadWorker(appContext: Context, workerParams: WorkerParamet private const val NOTIFICATION_ID = 271899102 private const val DEBUG_TAG = "BusTO-GTFSRTAlertsDown" + @JvmStatic fun makeOneTimeRequest(tag: String): OneTimeWorkRequest { //val data = Data.Builder().putString(OPERATION_TYPE, type).build() return OneTimeWorkRequest.Builder(GtfsAlertDBDownloadWorker::class.java) .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.SECONDS) .addTag(tag) .build() } + + @JvmStatic + fun getWorkInfoLiveData(context: Context): LiveData> { + val workManager = WorkManager.getInstance(context) + return workManager.getWorkInfosForUniqueWorkLiveData(WORK_NAME) + } } } \ No newline at end of file diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/AlertsDao.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/AlertsDao.kt index 5487ad4..f2ed29d 100644 --- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/AlertsDao.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/AlertsDao.kt @@ -110,10 +110,10 @@ interface AlertsDao { @Query(""" SELECT a.* FROM gtfsrt_alerts a INNER JOIN alerts_informed_entities ie ON ie.alertId = a.id - WHERE ie.stopId = :stopId + WHERE ie.stopId = :stopGtfsId ORDER BY a.fetchedAt DESC """) - fun getAlertsForStop(stopId: String): LiveData> + fun getAlertsForStopGtfsId(stopGtfsId: String): LiveData> @Transaction @Query(""" diff --git a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsAlertsDBConverter.kt b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsAlertsDBConverter.kt index 293e247..9e425be 100644 --- a/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsAlertsDBConverter.kt +++ b/app/src/main/java/it/reyboz/bustorino/data/gtfs/GtfsAlertsDBConverter.kt @@ -108,7 +108,7 @@ public object GtfsAlertsDBConverter { //agencyId = if (e.hasAgencyId()) e.agencyId else null, routeId = if (e.hasRouteId()) "gtt:${e.routeId}" else null, routeType = if (e.hasRouteType()) e.routeType else null, - stopId = if (e.hasStopId()) e.stopId else null, + stopId = if (e.hasStopId()) "gtt:${e.stopId}" else null, tripId = tripId, tripRouteId = tripRouteId, directionId = directionId diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt index 585af4e..16ca5c8 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsDialogFragment.kt @@ -44,7 +44,7 @@ import kotlin.getValue import kotlin.collections.HashMap -class AlertsDialogFragment(private val gtfsLineShow: String) : DialogFragment() { +class AlertsDialogFragment(private val gtfsLineShow: String, private val stopToShow: String) : DialogFragment() { private lateinit var titleTextView: TextView private lateinit var messageTextView: TextView @@ -55,7 +55,7 @@ class AlertsDialogFragment(private val gtfsLineShow: String) : DialogFragment() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Log.d(DEBUG_TAG, "created DialogFragment for line ${gtfsLineShow}") + Log.d(DEBUG_TAG, "created DialogFragment for line ${gtfsLineShow} and/or stop ${stopToShow}") } override fun onCreateView( @@ -66,13 +66,24 @@ class AlertsDialogFragment(private val gtfsLineShow: String) : DialogFragment() val root = inflater.inflate(R.layout.fragment_dialog_alerts_line, container, false) titleTextView = root.findViewById(R.id.titleTextView) - titleTextView.setText(getString(R.string.alert_line_fill,GtfsUtils.lineNameDisplayFromGtfsID(gtfsLineShow))) + val text = if (gtfsLineShow.isNotEmpty()) + getString(R.string.alert_line_fill,GtfsUtils.lineNameDisplayFromGtfsID(gtfsLineShow)) + else if(stopToShow.isNotEmpty()){ + getString(R.string.alert_stop_fill,stopToShow) + } else{ + throw Exception("Either text or line has to be filled") + } + titleTextView.setText(text) recyclerView = root.findViewById(R.id.alertsRecyclerView) recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) messageTextView = root.findViewById(R.id.alertMessageTextView) statusCardView = root.findViewById(R.id.statusCard) - alertsViewModel.alertsByRouteLiveData.observe(viewLifecycleOwner){ alerts -> - showAlerts(alerts) + if(gtfsLineShow.isNotEmpty()) + alertsViewModel.alertsByRouteLiveData.observe(viewLifecycleOwner){ alerts -> + showAlerts(alerts) + } + else if(stopToShow.isNotEmpty()){ + alertsViewModel.alertsByStopLiveData.observe(viewLifecycleOwner){ alerts -> showAlerts(alerts) } } val btnClose = root.findViewById(R.id.btnClose) @@ -150,8 +161,11 @@ class AlertsDialogFragment(private val gtfsLineShow: String) : DialogFragment() * @return A new instance of fragment LineAlertsDialogFragment. */ @JvmStatic - fun newInstance(gtfsLine: String) = - AlertsDialogFragment(gtfsLine) + fun newInstanceForLine(gtfsLine: String) = + AlertsDialogFragment(gtfsLine, "") + @JvmStatic + fun newInstanceForStop(stop: String) = + AlertsDialogFragment("", stop) private const val GTFS_LINE_ARG = "gtfsLine" diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt index 85234b1..b2915be 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/AlertsFragment.kt @@ -30,6 +30,7 @@ import androidx.fragment.app.viewModels import androidx.recyclerview.widget.RecyclerView import com.google.transit.realtime.GtfsRealtime import it.reyboz.bustorino.R +import it.reyboz.bustorino.backend.Palina import it.reyboz.bustorino.viewmodels.ServiceAlertsViewModel import java.text.SimpleDateFormat import java.util.Date @@ -47,6 +48,7 @@ class AlertsFragment : ScreenBaseFragment() { private val alertsViewModel: ServiceAlertsViewModel by activityViewModels() private lateinit var textView: TextView + private lateinit var statusTextView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { @@ -62,20 +64,41 @@ class AlertsFragment : ScreenBaseFragment() { // Inflate the layout for this fragment val root = inflater.inflate(R.layout.fragment_alerts, container, false) textView = root.findViewById(R.id.simpleTextView) + statusTextView = root.findViewById(R.id.statusTextView) alertsViewModel.allAlertsLiveData.observe(viewLifecycleOwner, { alerts -> - val sb = StringBuilder() - val unixTimestamp = (System.currentTimeMillis() / 1000) - for (x in alerts) { - sb.append(x.longPrint()) - sb.append("----- Alert active: ").append(x.isActive(unixTimestamp)).append("\n\n") + if(alerts==null){ + return@observe } + if(alerts.isEmpty()){ + textView.text = "No Alerts to show" + } else { + val sb = StringBuilder() + val unixTimestamp = (System.currentTimeMillis() / 1000) + for (x in alerts) { + sb.append(x.longPrint()) + sb.append("----- Alert active: ").append(x.isActive(unixTimestamp)).append("\n\n") + } - textView.text = sb.toString() + textView.text = sb.toString() + } }) + alertsViewModel.getDownloadStatusLiveData(requireContext()).observe(viewLifecycleOwner, { workinfos -> + val sb = StringBuilder() + var c = 1 + if(workinfos!=null && workinfos.isNotEmpty()){ + for (worki in workinfos){ + sb.append("$c - state: ${worki.state}, attempt ${worki.runAttemptCount}").append("\n") + c++ + } + } + statusTextView.text = sb.toString() + }) + + - alertsViewModel.setStopFilter("472") + //alertsViewModel.setStopFilter(Palina("472") /*alertsViewModel.alertsForStop.observe(viewLifecycleOwner){ Log.d(DEBUG_TAG, "Got ${it.size} alerts") it?.let { diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt index fcca08b..32737b9 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/ArrivalsFragment.kt @@ -25,6 +25,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.* +import androidx.cardview.widget.CardView +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.loader.app.LoaderManager import androidx.loader.content.CursorLoader @@ -48,6 +50,7 @@ import it.reyboz.bustorino.data.UserDB import it.reyboz.bustorino.middleware.CoroutineFavoriteAction import it.reyboz.bustorino.util.LinesNameSorter import it.reyboz.bustorino.viewmodels.ArrivalsViewModel +import it.reyboz.bustorino.viewmodels.ServiceAlertsViewModel import java.util.* @@ -79,6 +82,7 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks = ArrayList() private val arrivalsViewModel : ArrivalsViewModel by viewModels() - + private val alertsViewModel: ServiceAlertsViewModel by activityViewModels() private var reloadOnResume = true private var routesNoPassages = listOf() @@ -179,6 +183,7 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks this.onHideHint(v) } @@ -335,6 +340,17 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks updateStarIcon(isFavorite) }) + + alertsViewModel.alertsByStopLiveData.observe(viewLifecycleOwner) { alerts -> + if(alerts!=null && alerts.isNotEmpty()){ + alertsCardView.visibility = View.VISIBLE + alertsCardView.setOnClickListener { + AlertsDialogFragment.newInstanceForStop(stopID).show(parentFragmentManager, "AlertsDialogStop$stopID") + } + } else{ + alertsCardView.visibility = View.GONE + } + } } private fun showShortToast(id: Int) = showToastMessage(id,true) @@ -432,6 +448,8 @@ class ArrivalsFragment : ResultBaseFragment(), LoaderManager.LoaderCallbacks + protected lateinit var bottomSheetBehavior: BottomSheetBehavior protected var locationEngine: MapLibreLocationEngine? = null protected lateinit var locationProvider: FusedNativeLocationProvider @@ -161,7 +163,7 @@ abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback } ) //Bottom sheet behavior in GeneralMapLibreFragment - protected var bottomLayout: RelativeLayout? = null + protected var bottomLayout: ConstraintLayout? = null protected lateinit var stopTitleTextView: TextView protected lateinit var stopNumberTextView: TextView protected lateinit var linesPassingTextView: TextView @@ -171,6 +173,7 @@ abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback protected lateinit var bottomrightImage: ImageView protected lateinit var locationComponent: LocationComponent protected lateinit var busPositionsIconButton: ImageButton + protected lateinit var vehicleIcon: ImageView protected var lastLocation : Location? = null @@ -255,22 +258,29 @@ abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback return super.onCreateView(inflater, container, savedInstanceState) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - //init bottom sheet - val bottomSheet = view.findViewById(R.id.bottom_sheet) + protected fun initBottomSheet(view: View){ + val bottomSheet = view.findViewById(R.id.bottom_sheet) bottomLayout = bottomSheet stopTitleTextView = view.findViewById(R.id.stopTitleTextView) stopNumberTextView = view.findViewById(R.id.stopNumberTextView) - linesPassingTextView = view.findViewById(R.id.linesPassingTextView) + linesPassingTextView = view.findViewById(R.id.descriptionTextView) arrivalsCard = view.findViewById(R.id.arrivalsCardButton) directionsCard = view.findViewById(R.id.directionsCardButton) - bottomrightImage = view.findViewById(R.id.rightmostImageView) + vehicleIcon = view.findViewById(R.id.vehicleIcon) bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) + bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //init bottom sheet + initBottomSheet(view) + + bottomrightImage = view.findViewById(R.id.rightmostImageView) extraBottomTextView = view.findViewById(R.id.extraBottomTextView) - bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN } override fun onResume() { @@ -662,6 +672,7 @@ abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback bottomrightImage.setImageDrawable( ResourcesCompat.getDrawable(resources, R.drawable.ic_magnifying_glass, activity?.theme) ) + // if you change this, remember to change the color of the vehicleIcon 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) @@ -669,6 +680,25 @@ abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback extraBottomTextView.text = getString(R.string.updated_fill, utils.unixTimestampToLocalTime(data.posUpdate.timestamp)) extraBottomTextView.visibility = View.VISIBLE + val update = data.posUpdate + val vehInfo = VehicleUtils.getTypeForLabel(update.vehicle) + if(vehInfo == null){ + vehicleIcon.visibility = View.GONE + } else{ + val ico = when(vehInfo.type){ + VehicleUtils.VehicleType.BUS -> R.drawable.ic_bus_small + VehicleUtils.VehicleType.ELECTRIC_BUS -> R.drawable.ic_bus_electric_small + VehicleUtils.VehicleType.TRAM -> R.drawable.ic_tram_24 + } + vehicleIcon.setImageDrawable(ResourcesCompat.getDrawable(resources, ico, activity?.theme)) + vehicleIcon.visibility = View.VISIBLE + + vehicleIcon.setOnClickListener { + val print = "${vehInfo.type.getName()} ${vehInfo.name}" + makeToast(print) + } + } + } vehShowing = veh bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED @@ -822,6 +852,8 @@ abstract class GeneralMapLibreFragment: ScreenBaseFragment(), OnMapReadyCallback bottomrightImage.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.navigation_right, activity?.theme)) + vehicleIcon.visibility = View.GONE + } //add stop marker if (stop.latitude!=null && stop.longitude!=null) { diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt index 59bbe00..83118e7 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/LinesDetailFragment.kt @@ -283,7 +283,7 @@ class LinesDetailFragment() : GeneralMapLibreFragment() { //set lineInfoButton.setOnClickListener { - AlertsDialogFragment(lineID).show(parentFragmentManager, "Alerts-Line$lineID") + AlertsDialogFragment.newInstanceForLine(lineID).show(parentFragmentManager, "Alerts-Line$lineID") } /* @@ -426,6 +426,9 @@ class LinesDetailFragment() : GeneralMapLibreFragment() { } else lineInfoButton.visibility = View.GONE } + lineInfoButton.setOnClickListener { + AlertsDialogFragment.newInstanceForLine(lineID).show(parentFragmentManager, "Alerts-Line$lineID") + } } // ------------- UI switch stuff --------- diff --git a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt index 30a0614..9b01873 100644 --- a/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt +++ b/app/src/main/java/it/reyboz/bustorino/fragments/MapLibreFragment.kt @@ -137,13 +137,7 @@ class MapLibreFragment : GeneralMapLibreFragment() { mapView!!.getMapAsync(this) //init bottom sheet - val bottomSheet = rootView.findViewById(R.id.bottom_sheet) - bottomLayout = bottomSheet - stopTitleTextView = bottomSheet.findViewById(R.id.stopTitleTextView) - stopNumberTextView = bottomSheet.findViewById(R.id.stopNumberTextView) - linesPassingTextView = bottomSheet.findViewById(R.id.linesPassingTextView) - arrivalsCard = bottomSheet.findViewById(R.id.arrivalsCardButton) - directionsCard = bottomSheet.findViewById(R.id.directionsCardButton) + initBottomSheet(rootView) userLocationButton = rootView.findViewById(R.id.locationEnableIcon) userLocationButton.setOnClickListener(this::switchUserLocationStatus) @@ -153,8 +147,6 @@ class MapLibreFragment : GeneralMapLibreFragment() { busPositionsIconButton.setOnClickListener { LivePositionsDialogFragment().show(parentFragmentManager, "LivePositionsDialog") } - bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) - bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN arrivalsCard.setOnClickListener { if(context!=null){ diff --git a/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt b/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt index 31fdad3..fbe3375 100644 --- a/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt +++ b/app/src/main/java/it/reyboz/bustorino/viewmodels/ServiceAlertsViewModel.kt @@ -18,17 +18,21 @@ package it.reyboz.bustorino.viewmodels import android.app.Application +import android.content.Context import android.util.Log import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.map import androidx.lifecycle.switchMap import androidx.lifecycle.viewModelScope import androidx.room.concurrent.AtomicBoolean import androidx.work.ExistingWorkPolicy +import androidx.work.WorkInfo import androidx.work.WorkManager import com.google.transit.realtime.GtfsRealtime.Alert import it.reyboz.bustorino.backend.NetworkVolleyManager +import it.reyboz.bustorino.backend.Stop import it.reyboz.bustorino.data.GtfsAlertDBDownloadWorker import it.reyboz.bustorino.data.GtfsRepository import it.reyboz.bustorino.data.gtfs.GtfsDatabase @@ -48,7 +52,7 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { //val alertsLiveData = MutableLiveData>(ArrayList()) - private val stopToFilter = MutableLiveData("") + private val stopGtfsIdToFilter = MutableLiveData() private val routeToFilter = MutableLiveData("") val lastTimeRunningDownload = MutableLiveData(0L) @@ -64,8 +68,8 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { gtfsRepo.getAlertsByRouteID(it).map{ l -> l.filter { al->al.isActive(unixTimestamp) }} } - val alertsByStopLiveData = stopToFilter.switchMap { - gtfsRepo.alertsDao.getAlertsForStop(it) + val alertsByStopLiveData = stopGtfsIdToFilter.switchMap { + if(it.gtfsID!=null) gtfsRepo.alertsDao.getAlertsForStopGtfsId(it.gtfsID!!) else MutableLiveData() } val allAlertsLiveData = gtfsRepo.alertsDao.getAllAlertsLiveData() @@ -94,9 +98,15 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { } */ - fun setStopFilter(stopId: String) { - stopToFilter.value = stopId + /// WE DO NOT KNOW HOW TO GET THE GTFS STOP ID, the one given by MaTO is the same as the stop CODE + /// but we need the ID from the stops.txt table of GTT GTFS data + /// DISABLING THIS FUNCTION + /*fun setStopFilter(stop: Stop) { + Log.d(DEBUG_TAG, "Setting stop to filter: ${stop.ID} - ${stop.stopDisplayName}, gtfsID: ${stop.gtfsID}") + stopGtfsIdToFilter.value = stop } + + */ fun setGtfsLineFilter(routeId: String) { routeToFilter.value = routeId } @@ -111,7 +121,7 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { currentTime > lastTimeRunningDownload.value!! + MINUTES_CHECK*60*1000){ //actually enqueue request Log.d(DEBUG_TAG, "Launching request to download alerts") - val req = GtfsAlertDBDownloadWorker.makeOneTimeRequest("alertsrn") + val req = GtfsAlertDBDownloadWorker.makeOneTimeRequest(WORK_TAG) workManager.enqueueUniqueWork("AlertsDownloadsRun", ExistingWorkPolicy.KEEP, req) lastTimeRunningDownload.postValue(System.currentTimeMillis()) } @@ -129,7 +139,10 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { downloadWorkIfTimePassed() } - + fun getDownloadStatusLiveData(context: Context): LiveData>{ + val workManager = WorkManager.getInstance(context) + return workManager.getWorkInfosByTagLiveData(WORK_TAG) + } private fun filterAlertsForStop(stopId: String, alerts: ArrayList) : ArrayList{ @@ -191,6 +204,7 @@ class ServiceAlertsViewModel(app: Application) : AndroidViewModel(app) { companion object{ private const val DEBUG_TAG = "BusTO-GTFSRTAlerts" + public const val WORK_TAG = "AlertsDownloadWorker" } } \ No newline at end of file diff --git a/app/src/main/res/drawable/bus_marker.xml b/app/src/main/res/drawable/bus_marker.xml deleted file mode 100644 index 21d020d..0000000 --- a/app/src/main/res/drawable/bus_marker.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_bus_electric_small.xml b/app/src/main/res/drawable/ic_bus_electric_small.xml new file mode 100644 index 0000000..7df1b74 --- /dev/null +++ b/app/src/main/res/drawable/ic_bus_electric_small.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_bus_small.xml b/app/src/main/res/drawable/ic_bus_small.xml new file mode 100644 index 0000000..8d595f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_bus_small.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_tram_24.xml b/app/src/main/res/drawable/ic_tram_24.xml new file mode 100644 index 0000000..ea3140c --- /dev/null +++ b/app/src/main/res/drawable/ic_tram_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_alerts.xml b/app/src/main/res/layout/fragment_alerts.xml index d4dffd9..e2e2bcd 100644 --- a/app/src/main/res/layout/fragment_alerts.xml +++ b/app/src/main/res/layout/fragment_alerts.xml @@ -7,15 +7,23 @@ android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".fragments.AlertsFragment"> - - + + android:layout_height="0dp" + android:layout_margin="10dp" + > + android:layout_width="match_parent" + android:layout_height="wrap_content"> + +