From bf495f5844176d3e844f273fdc21edd4cbe6df7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 27 Mar 2026 10:57:57 -0400 Subject: [PATCH 1/2] GTFS-RT Trip Updates > validate GTFS-RT file available before trying to compute real-time Schedule/Timestamps --- .../status/GTFSRealTimeTripUpdatesProvider.kt | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt b/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt index b3cb2767..61d641c5 100644 --- a/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt @@ -8,7 +8,6 @@ import org.mtransit.android.commons.SecurityUtils import org.mtransit.android.commons.TimeUtils import org.mtransit.android.commons.TimeUtilsK import org.mtransit.android.commons.data.POIStatus -import org.mtransit.android.commons.data.RouteDirectionStop import org.mtransit.android.commons.data.Schedule import org.mtransit.android.commons.data.arrival import org.mtransit.android.commons.data.departure @@ -98,6 +97,9 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { filter: Schedule.ScheduleStatusFilter, tripIds: List ): POIStatus? { + val context = context ?: return null + if (!File(context.cacheDir, GTFS_RT_TRIP_UPDATE_PB_FILE_NAME).exists()) return null + if (GtfsRealTimeStorage.getTripUpdateLastUpdateMs(context, 0L) <= 0L) return null // never loaded synchronized(tripUpdateLock.getOrPut(filter.routeDirectionStop.routeDirectionUUID) { Any() }) { return getCachedStatusS(filter.targetUUID, tripIds) // try another time ?: makeCachedStatusFromAgencyData(filter, tripIds) @@ -111,6 +113,8 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { tripIds: List ): POIStatus? { val context = context ?: return null + val gtfsRealTimeTripUpdateFile = File(context.cacheDir, GTFS_RT_TRIP_UPDATE_PB_FILE_NAME) + if (!gtfsRealTimeTripUpdateFile.exists()) return null val readFromSourceMs = GtfsRealTimeStorage.getTripUpdateLastUpdateMs(context, 0L) if (readFromSourceMs <= 0L) return null // never loaded val sourceLabel = SourceUtils.getSourceLabel( // always use source from official API @@ -121,9 +125,7 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { val targetAuthority = rds.authority val routeId = rds.route.id val directionId = rds.direction.id - var sortedRDS: List? = null - var uuidSchedule: Map? = null - val gFeedMessage = GFeedMessage.parseFrom(File(context.cacheDir, GTFS_RT_TRIP_UPDATE_PB_FILE_NAME).inputStream()) + val gFeedMessage = GFeedMessage.parseFrom(gtfsRealTimeTripUpdateFile.inputStream()) val gTripUpdates = gFeedMessage.entityList.toTripUpdates() val rdTripUpdates = gTripUpdates .mapNotNull { gTripUpdate -> @@ -155,27 +157,20 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { ) } } - if (sortedRDS == null) { - sortedRDS = context.getRDS(rds.authority, routeId, directionId) - } - if (uuidSchedule == null) { - uuidSchedule = sortedRDS - ?.let { rdsList -> - context - .getRDSSchedule(targetAuthority, rdsList, filter.isIncludeCancelledTimestampsOrDefault) - .associateBy { it.targetUUID } - } - } - uuidSchedule ?: return null + val sortedRDS = context.getRDS(rds.authority, routeId, directionId) sortedRDS ?: return null + val uuidSchedule = context.getRDSSchedule(targetAuthority, sortedRDS, filter.isIncludeCancelledTimestampsOrDefault) + .takeIf { it.isNotEmpty() } + ?.associateBy { it.targetUUID } + uuidSchedule ?: return null processRDTripUpdates(rdTripUpdates, uuidSchedule, sortedRDS, filter.isIncludeCancelledTimestampsOrDefault) val tripsWithRealTime = uuidSchedule.values .asSequence() - .mapNotNull { it?.timestamps }.flatten() + .mapNotNull { schedule -> schedule.timestamps.takeIf { it.isNotEmpty() } }.flatten() .filter { it.isRealTime } .map { it.tripId } .toSet() // distinct - uuidSchedule.values.filterNotNull().forEach { schedule -> + uuidSchedule.forEach { (_, schedule) -> val now = TimeUtilsK.currentInstant() if (!schedule.timestamps.any { it.isRealTime || (it.tripId in tripsWithRealTime && it.departure < now) }) { cacheStatus(schedule.toNoData()) // avoid re-run @@ -226,13 +221,13 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { private fun GTFSRealTimeProvider.updateAgencyDataIfRequired(inFocus: Boolean) { val context = requireContextCompat() var inFocus = inFocus - val lastUpdateInMs = GtfsRealTimeStorage.getTripUpdateLastUpdateMs(context, 0L) val lastUpdateCode = GtfsRealTimeStorage.getTripUpdateLastUpdateCode(context, -1).takeIf { it >= 0 } if (lastUpdateCode != null && lastUpdateCode != HttpURLConnection.HTTP_OK) { inFocus = true // force earlier retry if last fetch returned HTTP error } val minUpdateMs = min(statusMaxValidityInMs, getStatusValidityInMs(inFocus)) val nowInMs = TimeUtils.currentTimeMillis() + val lastUpdateInMs = GtfsRealTimeStorage.getTripUpdateLastUpdateMs(context, 0L) if (lastUpdateInMs + minUpdateMs > nowInMs) { return } @@ -292,7 +287,7 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { try { val responseBodyByes = response.body.bytes() File(context.cacheDir, GTFS_RT_TRIP_UPDATE_PB_FILE_NAME).writeBytes(responseBodyByes) - @Suppress("SimplifyBooleanWithConstants") + @Suppress("SimplifyBooleanWithConstants", "KotlinConstantConditions") if (Constants.DEBUG && PRINT_ALL_LOADED_TRIP_UPDATES) { val gFeedMessage = GFeedMessage.parseFrom(responseBodyByes) val gTripUpdates = gFeedMessage.entityList.toTripUpdates() From 1d4fb471ca55b531d73c9abfbdda76314489d2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 27 Mar 2026 11:02:50 -0400 Subject: [PATCH 2/2] PR comments --- .../commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt b/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt index 61d641c5..ac6ced62 100644 --- a/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt @@ -226,9 +226,8 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { inFocus = true // force earlier retry if last fetch returned HTTP error } val minUpdateMs = min(statusMaxValidityInMs, getStatusValidityInMs(inFocus)) - val nowInMs = TimeUtils.currentTimeMillis() val lastUpdateInMs = GtfsRealTimeStorage.getTripUpdateLastUpdateMs(context, 0L) - if (lastUpdateInMs + minUpdateMs > nowInMs) { + if (lastUpdateInMs + minUpdateMs > TimeUtils.currentTimeMillis()) { return } updateAgencyDataIfRequiredSync(lastUpdateInMs, inFocus)