From fe7aebb50147d131b8c59ff9d3250670bc6db3c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 12:57:03 +0000 Subject: [PATCH 1/6] Initial plan From c526e4664713dd50431a38eeb01a5a020f474598 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 13:00:44 +0000 Subject: [PATCH 2/6] Fix duplicate service updates in StmInfoServiceUpdateProvider Co-authored-by: mmathieum <177998+mmathieum@users.noreply.github.com> Agent-Logs-Url: https://github.com/mtransitapps/commons-android/sessions/e34048c6-d065-4b3e-b0ef-828aae84c6e6 --- .../provider/ca/info/stm/StmInfoServiceUpdateProvider.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt index df716026..b269be04 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt @@ -258,13 +258,14 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { } } } - MTLog.i(this@StmInfoServiceUpdateProvider, "Found %d service updates.", serviceUpdates.size) + val distinctServiceUpdates = serviceUpdates.distinctBy { it.targetUUID to it.language } + MTLog.i(this@StmInfoServiceUpdateProvider, "Found %d service updates (%d before dedup).", distinctServiceUpdates.size, serviceUpdates.size) if (Constants.DEBUG) { - for (serviceUpdate in serviceUpdates) { + for (serviceUpdate in distinctServiceUpdates) { MTLog.d(this@StmInfoServiceUpdateProvider, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) } } - return serviceUpdates + return distinctServiceUpdates } else -> { From 0647c55002c0c9826d8d074c9f526f6bb2233902 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:21:29 +0000 Subject: [PATCH 3/6] Fix distinct languages and extract parseServiceUpdates for testability Co-authored-by: mmathieum <177998+mmathieum@users.noreply.github.com> Agent-Logs-Url: https://github.com/mtransitapps/commons-android/sessions/caa1e2fd-d1b3-483b-8614-2e5bb63df628 --- .../info/stm/StmInfoServiceUpdateProvider.kt | 186 ++++++++++-------- .../stm/StmInfoServiceUpdateProviderTest.kt | 146 ++++++++++++++ 2 files changed, 247 insertions(+), 85 deletions(-) create mode 100644 src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt diff --git a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt index b269be04..b7c6670a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt @@ -32,6 +32,7 @@ import java.net.SocketException import java.net.UnknownHostException import java.util.Locale import javax.net.ssl.SSLHandshakeException +import kotlin.time.Duration import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes @@ -176,96 +177,18 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { StmInfoServiceUpdateStorage.saveServiceUpdateLastUpdate(context, now) when (response.code()) { HttpURLConnection.HTTP_OK -> { - val serviceUpdates = mutableListOf() val sourceLabel = SourceUtils.getSourceLabel( // always use source from official API SERVICE_UPDATE_URL ) val etatServiceResponse = response.body() val headerTimestamp = etatServiceResponse?.header?.timestamp ?: now - etatServiceResponse?.alerts?.forEach { alert -> - if (!alert.isActive()) { - MTLog.d(this@StmInfoServiceUpdateProvider, "Ignore inactive alert. ($alert)") - return@forEach - } - val informedEntities = alert.informedEntities?.takeIf { it.isNotEmpty() } - ?: run { - MTLog.w(this@StmInfoServiceUpdateProvider, "Ignore alert w/o informed entities! ($alert)") - return@forEach - } - val routeShortNames = informedEntities.mapNotNull { it.routeShortName }.takeIf { it.isNotEmpty() } - ?: run { - MTLog.w(this@StmInfoServiceUpdateProvider, "Ignore alert w/o route short names! ($alert)") - return@forEach - } - val directionId = informedEntities.singleOrNull { !it.directionId.isNullOrBlank() }?.directionId - val stopIds = informedEntities.mapNotNull { it.stopCode }.toSet() - - val targetUUIDs: Set = buildSet { - routeShortNames.forEach { routeShortName -> - if (stopIds.isEmpty()) { - (getAgencyRouteDirectionTagTargetUUID(routeShortName, directionId) - ?: getAgencyRouteTagTargetUUID(routeShortName)).let { - add(it) - } - } else { - stopIds.forEach { stopId -> - (getAgencyRouteDirectionStopTagTargetUUID(routeShortName, directionId, stopId) - ?: getAgencyRouteStopTagTargetUUID(routeShortName, stopId)).let { - add(it) - } - } - } - } - } - val headerTexts = alert.headerTexts?.parseTranslations() - val descriptionTexts = alert.descriptionTexts?.parseTranslations() - val languages = headerTexts?.keys.orEmpty() + descriptionTexts?.keys.orEmpty() - if (languages.isEmpty()) { - MTLog.w(this@StmInfoServiceUpdateProvider, "Ignore alert w/o translations! ($alert)") - return@forEach - } - targetUUIDs.forEach { targetUUID -> - val severity = if (stopIds.isNotEmpty()) { - ServiceUpdate.SEVERITY_WARNING_POI - } else { - ServiceUpdate.SEVERITY_INFO_RELATED_POI - } // else ServiceUpdate.SEVERITY_INFO_UNKNOWN? - languages.forEach { language -> - val header = headerTexts?.get(language) - val description = descriptionTexts?.get(language) - ?: return@forEach // no description == no service update to show - val replacement = ServiceUpdateCleaner.getReplacement(severity) - val descriptionHtml = description.let { - var textHtml = it - textHtml = HtmlUtils.toHTML(textHtml) - textHtml = HtmlUtils.fixTextViewBR(textHtml) - textHtml = ServiceUpdateCleaner.clean(textHtml, replacement, language) - textHtml - } - serviceUpdates.add( - makeServiceUpdate( - targetUUID = targetUUID, - lastUpdate = headerTimestamp, - maxValidity = serviceUpdateMaxValidity, - text = ServiceUpdateCleaner.makeText(header, description), - optTextHTML = ServiceUpdateCleaner.makeTextHTML(header, descriptionHtml), - severity = severity, - sourceId = AGENCY_SOURCE_ID, - sourceLabel = sourceLabel, - language = language - ) - ) - } - } - } - val distinctServiceUpdates = serviceUpdates.distinctBy { it.targetUUID to it.language } - MTLog.i(this@StmInfoServiceUpdateProvider, "Found %d service updates (%d before dedup).", distinctServiceUpdates.size, serviceUpdates.size) - if (Constants.DEBUG) { - for (serviceUpdate in distinctServiceUpdates) { - MTLog.d(this@StmInfoServiceUpdateProvider, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) - } - } - return distinctServiceUpdates + val serviceUpdates = parseServiceUpdates( + etatServiceResponse = etatServiceResponse, + headerTimestamp = headerTimestamp, + maxValidity = serviceUpdateMaxValidity, + sourceLabel = sourceLabel, + ) + return serviceUpdates } else -> { @@ -300,6 +223,99 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { } } + internal fun parseServiceUpdates( + etatServiceResponse: EtatServiceResponse?, + headerTimestamp: Instant, + maxValidity: Duration, + sourceLabel: String, + ): List { + val serviceUpdates = mutableListOf() + etatServiceResponse?.alerts?.forEach { alert -> + if (!alert.isActive()) { + MTLog.d(this, "Ignore inactive alert. ($alert)") + return@forEach + } + val informedEntities = alert.informedEntities?.takeIf { it.isNotEmpty() } + ?: run { + MTLog.w(this, "Ignore alert w/o informed entities! ($alert)") + return@forEach + } + val routeShortNames = informedEntities.mapNotNull { it.routeShortName }.takeIf { it.isNotEmpty() } + ?: run { + MTLog.w(this, "Ignore alert w/o route short names! ($alert)") + return@forEach + } + val directionId = informedEntities.singleOrNull { !it.directionId.isNullOrBlank() }?.directionId + val stopIds = informedEntities.mapNotNull { it.stopCode }.toSet() + + val targetUUIDs: Set = buildSet { + routeShortNames.forEach { routeShortName -> + if (stopIds.isEmpty()) { + (getAgencyRouteDirectionTagTargetUUID(routeShortName, directionId) + ?: getAgencyRouteTagTargetUUID(routeShortName)).let { + add(it) + } + } else { + stopIds.forEach { stopId -> + (getAgencyRouteDirectionStopTagTargetUUID(routeShortName, directionId, stopId) + ?: getAgencyRouteStopTagTargetUUID(routeShortName, stopId)).let { + add(it) + } + } + } + } + } + val headerTexts = alert.headerTexts?.parseTranslations() + val descriptionTexts = alert.descriptionTexts?.parseTranslations() + val languages = (headerTexts?.keys.orEmpty() + descriptionTexts?.keys.orEmpty()).toSet() + if (languages.isEmpty()) { + MTLog.w(this, "Ignore alert w/o translations! ($alert)") + return@forEach + } + targetUUIDs.forEach { targetUUID -> + val severity = if (stopIds.isNotEmpty()) { + ServiceUpdate.SEVERITY_WARNING_POI + } else { + ServiceUpdate.SEVERITY_INFO_RELATED_POI + } // else ServiceUpdate.SEVERITY_INFO_UNKNOWN? + languages.forEach { language -> + val header = headerTexts?.get(language) + val description = descriptionTexts?.get(language) + ?: return@forEach // no description == no service update to show + val replacement = ServiceUpdateCleaner.getReplacement(severity) + val descriptionHtml = description.let { + var textHtml = it + textHtml = HtmlUtils.toHTML(textHtml) + textHtml = HtmlUtils.fixTextViewBR(textHtml) + textHtml = ServiceUpdateCleaner.clean(textHtml, replacement, language) + textHtml + } + serviceUpdates.add( + makeServiceUpdate( + targetUUID = targetUUID, + lastUpdate = headerTimestamp, + maxValidity = maxValidity, + text = ServiceUpdateCleaner.makeText(header, description), + optTextHTML = ServiceUpdateCleaner.makeTextHTML(header, descriptionHtml), + severity = severity, + sourceId = AGENCY_SOURCE_ID, + sourceLabel = sourceLabel, + language = language + ) + ) + } + } + } + val distinctServiceUpdates = serviceUpdates.distinctBy { it.targetUUID to it.language } + MTLog.i(this, "Found %d service updates (%d before dedup).", distinctServiceUpdates.size, serviceUpdates.size) + if (Constants.DEBUG) { + for (serviceUpdate in distinctServiceUpdates) { + MTLog.d(this, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) + } + } + return distinctServiceUpdates + } + private fun List.parseTranslations(): Map? { this.takeIf { it.isNotEmpty() } ?: return null var hasDefaultLanguage = false diff --git a/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt b/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt new file mode 100644 index 00000000..8e1ac599 --- /dev/null +++ b/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt @@ -0,0 +1,146 @@ +package org.mtransit.android.commons.provider.ca.info.stm + +import org.junit.Assert.assertEquals +import org.junit.Test +import kotlin.time.Duration.Companion.days +import kotlin.time.Instant + +class StmInfoServiceUpdateProviderTest { + + private val now = Instant.fromEpochSeconds(1_000_000L) + private val maxValidity = 1.days + private val sourceLabel = "stm.info" + + private fun makeAlert( + routeShortName: String, + directionId: String? = null, + stopCode: String? = null, + startInSec: Long? = null, + endInSec: Long? = null, + headerFr: String = "Titre", + headerEn: String = "Title", + descFr: String = "Description FR", + descEn: String = "Description EN", + ) = EtatServiceResponse.Alert( + activePeriods = EtatServiceResponse.Alert.ActivePeriods( + startInSec = startInSec, + endInSec = endInSec, + ), + cause = null, + effect = null, + // The STM API sends each attribute (route, direction, stop) as a separate InformedEntity object + informedEntities = listOfNotNull( + EtatServiceResponse.Alert.InformedEntity( + routeShortName = routeShortName, + directionId = null, + stopCode = null, + ), + directionId?.let { + EtatServiceResponse.Alert.InformedEntity( + routeShortName = null, + directionId = it, + stopCode = null, + ) + }, + stopCode?.let { + EtatServiceResponse.Alert.InformedEntity( + routeShortName = null, + directionId = null, + stopCode = it, + ) + }, + ), + headerTexts = listOf( + EtatServiceResponse.Alert.TranslatedText(language = "fr", text = headerFr), + EtatServiceResponse.Alert.TranslatedText(language = "en", text = headerEn), + ), + descriptionTexts = listOf( + EtatServiceResponse.Alert.TranslatedText(language = "fr", text = descFr), + EtatServiceResponse.Alert.TranslatedText(language = "en", text = descEn), + ), + ) + + @Test + fun testParseServiceUpdates_deduplicatesDuplicateAlerts() { + // Two identical alerts for route "34" -> should produce 2 service updates (one per language) + val alert = makeAlert(routeShortName = "34") + val response = EtatServiceResponse( + header = null, + alerts = listOf(alert, alert), + ) + + val result = StmInfoServiceUpdateProvider.parseServiceUpdates( + etatServiceResponse = response, + headerTimestamp = now, + maxValidity = maxValidity, + sourceLabel = sourceLabel, + ) + + // 2 languages (fr, en) x 1 unique targetUUID -> 2 distinct service updates (not 4) + assertEquals(2, result.size) + } + + @Test + fun testParseServiceUpdates_distinctLanguagesPerAlert() { + // Alert has both "fr" and "en" in both header and description (potential duplicates in languages list) + val alert = EtatServiceResponse.Alert( + activePeriods = null, + cause = null, + effect = null, + informedEntities = listOf( + EtatServiceResponse.Alert.InformedEntity(routeShortName = "14", directionId = null, stopCode = null), + ), + headerTexts = listOf( + EtatServiceResponse.Alert.TranslatedText(language = "fr", text = "Titre"), + EtatServiceResponse.Alert.TranslatedText(language = "en", text = "Title"), + ), + descriptionTexts = listOf( + // Same languages as headerTexts - should not produce duplicate entries + EtatServiceResponse.Alert.TranslatedText(language = "fr", text = "Desc FR"), + EtatServiceResponse.Alert.TranslatedText(language = "en", text = "Desc EN"), + ), + ) + val response = EtatServiceResponse(header = null, alerts = listOf(alert)) + + val result = StmInfoServiceUpdateProvider.parseServiceUpdates( + etatServiceResponse = response, + headerTimestamp = now, + maxValidity = maxValidity, + sourceLabel = sourceLabel, + ) + + // Should produce exactly 2: one for "fr", one for "en" + assertEquals(2, result.size) + assertEquals(result.size, result.distinctBy { it.targetUUID to it.language }.size) + } + + @Test + fun testParseServiceUpdates_nullResponse_returnsEmpty() { + val result = StmInfoServiceUpdateProvider.parseServiceUpdates( + etatServiceResponse = null, + headerTimestamp = now, + maxValidity = maxValidity, + sourceLabel = sourceLabel, + ) + + assertEquals(0, result.size) + } + + @Test + fun testParseServiceUpdates_multipleDistinctAlerts() { + // Alerts for two different routes -> should produce 4 service updates (2 per route, one per language) + val alert34 = makeAlert(routeShortName = "34") + val alert35 = makeAlert(routeShortName = "35") + val response = EtatServiceResponse(header = null, alerts = listOf(alert34, alert35)) + + val result = StmInfoServiceUpdateProvider.parseServiceUpdates( + etatServiceResponse = response, + headerTimestamp = now, + maxValidity = maxValidity, + sourceLabel = sourceLabel, + ) + + assertEquals(4, result.size) + assertEquals(result.size, result.distinctBy { it.targetUUID to it.language }.size) + } +} From 9ca57fc8b912ed33166966e3857fd42d4455b6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Sat, 21 Mar 2026 16:54:26 -0400 Subject: [PATCH 4/6] fix --- .../mtransit/android/commons/TimeUtilsK.kt | 2 + .../android/commons/data/ServiceUpdate.java | 26 +++ .../commons/provider/StmInfoApiProvider.java | 2 +- .../info/stm/StmInfoServiceUpdateProvider.kt | 59 +++---- .../provider/gtfs/GTFSProviderDbHelper.java | 13 +- .../commons/provider/gtfs/GtfsRealtimeExt.kt | 20 +-- .../android/commons/data/ScheduleExtTests.kt | 2 +- .../stm/StmInfoServiceUpdateProviderTest.kt | 153 +++++++++--------- 8 files changed, 158 insertions(+), 119 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/TimeUtilsK.kt b/src/main/java/org/mtransit/android/commons/TimeUtilsK.kt index 0b3c44c2..27bf2ab1 100644 --- a/src/main/java/org/mtransit/android/commons/TimeUtilsK.kt +++ b/src/main/java/org/mtransit/android/commons/TimeUtilsK.kt @@ -21,8 +21,10 @@ object TimeUtilsK { if (negative) insert(0, "-") }.trim() + @JvmStatic fun currentInstant() = TimeUtils.currentTimeMillis().millisToInstant() + @JvmStatic val EPOCH_TIME_0: Instant = 0L.millisToInstant() } diff --git a/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java b/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java index 26dd8080..c226f881 100644 --- a/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java +++ b/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java @@ -15,6 +15,7 @@ import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; import java.util.Comparator; +import java.util.Objects; public class ServiceUpdate implements MTLog.Loggable { @@ -228,6 +229,31 @@ public String toString() { return sb.toString(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ServiceUpdate that = (ServiceUpdate) o; + return lastUpdateInMs == that.lastUpdateInMs && + maxValidityInMs == that.maxValidityInMs && + severity == that.severity && + Objects.equals(id, that.id) && + Objects.equals(targetUUID, that.targetUUID) && + Objects.equals(targetTripId, that.targetTripId) && + Objects.equals(text, that.text) && + Objects.equals(textHTML, that.textHTML) && + Objects.equals(noService, that.noService) && + Objects.equals(language, that.language) && + Objects.equals(sourceLabel, that.sourceLabel) && + Objects.equals(sourceId, that.sourceId) && + Objects.equals(originalId, that.originalId); + } + + @Override + public int hashCode() { + return Objects.hash(id, targetUUID, targetTripId, lastUpdateInMs, maxValidityInMs, text, textHTML, severity, noService, language, sourceLabel, sourceId, originalId); + } + public boolean isUseful() { return this.lastUpdateInMs + this.maxValidityInMs >= TimeUtils.currentTimeMillis(); } diff --git a/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java b/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java index 3d4f9b6e..ad839de3 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java @@ -684,7 +684,7 @@ private static String getApiDirection(@NonNull Direction direction) { return StringUtils.EMPTY; } - private static final String SERVICE_UPDATE_SOURCE_ID = "api_stm_info_arrivals_messages"; + public static final String SERVICE_UPDATE_SOURCE_ID = "api_stm_info_arrivals_messages"; private static final String APPLICATION_JSON = "application/JSON"; private static final String ACCEPT = "accept"; diff --git a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt index b7c6670a..4fee36a3 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt @@ -2,6 +2,7 @@ package org.mtransit.android.commons.provider.ca.info.stm import android.content.Context import android.util.Log +import androidx.annotation.VisibleForTesting import org.mtransit.android.commons.Constants import org.mtransit.android.commons.HtmlUtils import org.mtransit.android.commons.LocaleUtils @@ -46,12 +47,17 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { @JvmStatic val SERVICE_UPDATE_MAX_VALIDITY_IN_MS = 1.days.inWholeMilliseconds + // .takeUnless { Constants.DEBUG } ?: 1.minutes.inWholeMilliseconds val SERVICE_UPDATE_VALIDITY_IN_MS = 1.hours.inWholeMilliseconds + // .takeUnless { Constants.DEBUG } ?: 1.minutes.inWholeMilliseconds val SERVICE_UPDATE_VALIDITY_IN_FOCUS_IN_MS = 10.minutes.inWholeMilliseconds + // .takeUnless { Constants.DEBUG } ?: 1.minutes.inWholeMilliseconds val SERVICE_UPDATE_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 10.minutes.inWholeMilliseconds + // .takeUnless { Constants.DEBUG } ?: 1.minutes.inWholeMilliseconds val SERVICE_UPDATE_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds + // .takeUnless { Constants.DEBUG } ?: 1.minutes.inWholeMilliseconds @JvmStatic fun getValidityInMs(inFocus: Boolean) = @@ -61,8 +67,6 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { fun getMinDurationBetweenRefreshInMs(inFocus: Boolean) = if (inFocus) SERVICE_UPDATE_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS else SERVICE_UPDATE_MIN_DURATION_BETWEEN_REFRESH_IN_MS - private const val AGENCY_SOURCE_ID = "api_stm_info_messages" - @JvmStatic fun StmInfoApiProvider.getCached(filter: ServiceUpdateProviderContract.Filter): List? { return ((filter.poi as? RouteDirectionStop)?.getTargetUUIDs(includeStopTags = true) @@ -130,7 +134,7 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { if (!deleteAllDone) { deleteAllAgencyServiceUpdateData() } - cacheServiceUpdates(newServiceUpdates) + cacheServiceUpdates(newServiceUpdates.toList()) } // else keep whatever we have until max validity reached } @@ -155,7 +159,7 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { private const val SERVICE_UPDATE_URL = "https://api.stm.info/pub/od/i3/v2/messages/etatservice" - private fun StmInfoApiProvider.loadAgencyDataFromWWW(context: Context): List? { + private fun StmInfoApiProvider.loadAgencyDataFromWWW(context: Context): Collection? { try { val call = getSERVICE_UPDATES_URL_CACHED(context).takeIf { it.isNotBlank() }?.let { urlCachedString -> getStmInfoApi(context).getV2MessageEtatService(urlCachedString) @@ -181,13 +185,17 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { SERVICE_UPDATE_URL ) val etatServiceResponse = response.body() - val headerTimestamp = etatServiceResponse?.header?.timestamp ?: now - val serviceUpdates = parseServiceUpdates( - etatServiceResponse = etatServiceResponse, - headerTimestamp = headerTimestamp, + val serviceUpdates = etatServiceResponse.toServiceUpdates( maxValidity = serviceUpdateMaxValidity, sourceLabel = sourceLabel, + now = now, ) + MTLog.i(this, "Found %d service updates.", serviceUpdates.size) + if (Constants.DEBUG) { + for (serviceUpdate in serviceUpdates) { + MTLog.d(this, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) + } + } return serviceUpdates } @@ -223,26 +231,28 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { } } - internal fun parseServiceUpdates( - etatServiceResponse: EtatServiceResponse?, - headerTimestamp: Instant, + @VisibleForTesting + internal fun EtatServiceResponse?.toServiceUpdates( maxValidity: Duration, sourceLabel: String, - ): List { - val serviceUpdates = mutableListOf() - etatServiceResponse?.alerts?.forEach { alert -> + now: Instant, + ): Collection { + val serviceUpdates = mutableSetOf() + val alerts = this?.alerts?.takeIf { it.isNotEmpty() } ?: return serviceUpdates + val headerTimestamp = this.header?.timestamp ?: now + alerts.forEach { alert -> if (!alert.isActive()) { - MTLog.d(this, "Ignore inactive alert. ($alert)") + MTLog.d(this@StmInfoServiceUpdateProvider, "Ignore inactive alert. ($alert)") return@forEach } val informedEntities = alert.informedEntities?.takeIf { it.isNotEmpty() } ?: run { - MTLog.w(this, "Ignore alert w/o informed entities! ($alert)") + MTLog.w(this@StmInfoServiceUpdateProvider, "Ignore alert w/o informed entities! ($alert)") return@forEach } val routeShortNames = informedEntities.mapNotNull { it.routeShortName }.takeIf { it.isNotEmpty() } ?: run { - MTLog.w(this, "Ignore alert w/o route short names! ($alert)") + MTLog.w(this@StmInfoServiceUpdateProvider, "Ignore alert w/o route short names! ($alert)") return@forEach } val directionId = informedEntities.singleOrNull { !it.directionId.isNullOrBlank() }?.directionId @@ -267,9 +277,9 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { } val headerTexts = alert.headerTexts?.parseTranslations() val descriptionTexts = alert.descriptionTexts?.parseTranslations() - val languages = (headerTexts?.keys.orEmpty() + descriptionTexts?.keys.orEmpty()).toSet() + val languages = headerTexts?.keys.orEmpty() + descriptionTexts?.keys.orEmpty() if (languages.isEmpty()) { - MTLog.w(this, "Ignore alert w/o translations! ($alert)") + MTLog.w(this@StmInfoServiceUpdateProvider, "Ignore alert w/o translations! ($alert)") return@forEach } targetUUIDs.forEach { targetUUID -> @@ -298,7 +308,7 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { text = ServiceUpdateCleaner.makeText(header, description), optTextHTML = ServiceUpdateCleaner.makeTextHTML(header, descriptionHtml), severity = severity, - sourceId = AGENCY_SOURCE_ID, + sourceId = StmInfoApiProvider.SERVICE_UPDATE_SOURCE_ID, sourceLabel = sourceLabel, language = language ) @@ -306,14 +316,7 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { } } } - val distinctServiceUpdates = serviceUpdates.distinctBy { it.targetUUID to it.language } - MTLog.i(this, "Found %d service updates (%d before dedup).", distinctServiceUpdates.size, serviceUpdates.size) - if (Constants.DEBUG) { - for (serviceUpdate in distinctServiceUpdates) { - MTLog.d(this, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) - } - } - return distinctServiceUpdates + return serviceUpdates } private fun List.parseTranslations(): Map? { diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java index 3f4c089a..3639884a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java @@ -217,38 +217,47 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { return kotlin.Unit.INSTANCE; } ); // 1st + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_STRINGS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_ROUTE, T_ROUTE_SQL_CREATE, T_ROUTE_SQL_INSERT, T_ROUTE_SQL_DROP, getRouteFiles(), 0, 0, allStrings, T_ROUTE_STRINGS_COLUMN_IDX); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_ROUTE, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_DIRECTION, T_DIRECTION_SQL_CREATE, T_DIRECTION_SQL_INSERT, T_DIRECTION_SQL_DROP, getDirectionFiles(), 0, 0, allStrings, T_DIRECTION_STRINGS_COLUMN_IDX); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_DIRECTION, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_STOP, T_STOP_SQL_CREATE, T_STOP_SQL_INSERT, T_STOP_SQL_DROP, getStopFiles(), 0, 0, allStrings, T_STOP_STRINGS_COLUMN_IDX); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_STOP, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_DIRECTION_STOPS, T_DIRECTION_STOPS_SQL_CREATE, T_DIRECTION_STOPS_SQL_INSERT, T_DIRECTION_STOPS_SQL_DROP, getDirectionStopsFiles()); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_DIRECTION_STOPS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (FeatureFlags.F_EXPORT_TRIP_ID) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_TRIP, T_TRIP_SQL_CREATE, T_TRIP_SQL_INSERT, T_TRIP_SQL_DROP, getTripFiles(), T_TRIP_SAME_COLUMNS_COUNT, T_TRIP_OTHER_COLUMNS_COUNT); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_TRIP, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_SERVICE_IDS, T_SERVICE_IDS_SQL_CREATE, T_SERVICE_IDS_SQL_INSERT, T_SERVICE_IDS_SQL_DROP, getServiceIdsFiles()); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_SERVICE_IDS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_SERVICE_DATES, T_SERVICE_DATES_SQL_CREATE, T_SERVICE_DATES_SQL_INSERT, T_SERVICE_DATES_SQL_DROP, getServiceDatesFiles(), T_SERVICE_DATES_SAME_COLUMNS_COUNT, T_SERVICE_DATES_OTHER_COLUMNS_COUNT); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_SERVICE_DATES, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_TRIP_IDS, T_TRIP_IDS_SQL_CREATE, T_TRIP_IDS_SQL_INSERT, T_TRIP_IDS_SQL_DROP, getTripIdsFiles()); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_TRIP_IDS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); db.execSQL(T_ROUTE_DIRECTION_STOP_STATUS_SQL_CREATE); + MTLog.d(this, "Data: deploying DB... %s done (%s)", T_ROUTE_DIRECTION_STOP_STATUS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) { nb.setSmallIcon(android.R.drawable.stat_notify_sync_noanim); NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, nbTotalOperations); nm.cancel(nId); } - final long durationInMs = TimeUtils.currentTimeMillis() - startInMs; - MTLog.i(this, "Data: deploying DB... DONE (%s)", MTLog.formatDuration(durationInMs)); + MTLog.i(this, "Data: deploying DB... DONE (%s)", MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } /** diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt index 7a68406a..7dd14684 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt @@ -248,12 +248,12 @@ object GtfsRealtimeExt { append(if (short) "STU:" else "StopTimeUpdate:") append( buildList { - optStopSequence?.let { add("stopSeq=$stopSequence") } - optStopId?.let { add("stopId=$stopId") } - optArrival?.let { add("arrival=" + it.toStringExt(short = true)) } - optDeparture?.let { add("departure=" + it.toStringExt(short = true)) } - optDepartureOccupancyStatus?.let { add("depOcc=$departureOccupancyStatus") } - optScheduleRelationship?.let { add("schedRel=$scheduleRelationship") } + optStopSequence?.let { add(if (short) "seq=$stopSequence" else "stopSeq=$stopSequence") } + optStopId?.let { add(if (short) "id=$stopId" else "stopId=$stopId") } + optArrival?.let { add((if (short) "a=" else "arrival=") + it.toStringExt(short = true)) } + optDeparture?.let { add((if (short) "d=" else "departure=") + it.toStringExt(short = true)) } + optDepartureOccupancyStatus?.let { add(if (short) "oc=$departureOccupancyStatus" else "depOcc=$departureOccupancyStatus") } + optScheduleRelationship?.let { add(if (short) "sR=$scheduleRelationship" else "schedRel=$scheduleRelationship") } optStopTimeProperties?.let { add(it.toStringExt(short = true)) } }.joinToStringList() ) @@ -273,10 +273,10 @@ object GtfsRealtimeExt { append(if (short) "STE:" else "StopTimeEvent:") append( buildList { - optDelay?.let { add("delay=$delay") } - optTime?.let { add("time=$time") } - optUncertainty?.let { add("uncertainty=$uncertainty") } - optScheduledTime?.let { add("schedTime=$scheduledTime") } + optDelay?.let { add(if (short) "d=$delay" else "delay=$delay") } + optTime?.let { add(if (short) "t=$time" else "time=$time") } + optUncertainty?.let { add(if (short) "u=$uncertainty" else "uncertainty=$uncertainty") } + optScheduledTime?.let { add(if (short) "sT=$scheduledTime" else "schedTime=$scheduledTime") } }.joinToStringList() ) } diff --git a/src/test/java/org/mtransit/android/commons/data/ScheduleExtTests.kt b/src/test/java/org/mtransit/android/commons/data/ScheduleExtTests.kt index 49cd2c60..35cd5d90 100644 --- a/src/test/java/org/mtransit/android/commons/data/ScheduleExtTests.kt +++ b/src/test/java/org/mtransit/android/commons/data/ScheduleExtTests.kt @@ -12,7 +12,7 @@ class ScheduleExtTests { companion object { private const val LOCAL_TZ_ID: String = "America/Montreal" - private const val DEPARTURE_SEC = 1772722800L // 2026-03-06 10:00: + private const val DEPARTURE_SEC = 1772722800L // 2026-03-06 10:00 } @Test diff --git a/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt b/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt index 8e1ac599..50a5bb5b 100644 --- a/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt +++ b/src/test/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProviderTest.kt @@ -2,66 +2,20 @@ package org.mtransit.android.commons.provider.ca.info.stm import org.junit.Assert.assertEquals import org.junit.Test +import org.mtransit.android.commons.provider.ca.info.stm.StmInfoServiceUpdateProvider.toServiceUpdates +import org.mtransit.android.commons.secsToInstant import kotlin.time.Duration.Companion.days -import kotlin.time.Instant class StmInfoServiceUpdateProviderTest { - private val now = Instant.fromEpochSeconds(1_000_000L) - private val maxValidity = 1.days - private val sourceLabel = "stm.info" - - private fun makeAlert( - routeShortName: String, - directionId: String? = null, - stopCode: String? = null, - startInSec: Long? = null, - endInSec: Long? = null, - headerFr: String = "Titre", - headerEn: String = "Title", - descFr: String = "Description FR", - descEn: String = "Description EN", - ) = EtatServiceResponse.Alert( - activePeriods = EtatServiceResponse.Alert.ActivePeriods( - startInSec = startInSec, - endInSec = endInSec, - ), - cause = null, - effect = null, - // The STM API sends each attribute (route, direction, stop) as a separate InformedEntity object - informedEntities = listOfNotNull( - EtatServiceResponse.Alert.InformedEntity( - routeShortName = routeShortName, - directionId = null, - stopCode = null, - ), - directionId?.let { - EtatServiceResponse.Alert.InformedEntity( - routeShortName = null, - directionId = it, - stopCode = null, - ) - }, - stopCode?.let { - EtatServiceResponse.Alert.InformedEntity( - routeShortName = null, - directionId = null, - stopCode = it, - ) - }, - ), - headerTexts = listOf( - EtatServiceResponse.Alert.TranslatedText(language = "fr", text = headerFr), - EtatServiceResponse.Alert.TranslatedText(language = "en", text = headerEn), - ), - descriptionTexts = listOf( - EtatServiceResponse.Alert.TranslatedText(language = "fr", text = descFr), - EtatServiceResponse.Alert.TranslatedText(language = "en", text = descEn), - ), - ) + companion object { + private val NOW = 1772722800L.secsToInstant() // 2026-03-06 10:00 + private val MAX_VALIDITY = 1.days + private const val SOURCE_LABEL = "stm.info" + } @Test - fun testParseServiceUpdates_deduplicatesDuplicateAlerts() { + fun testToServiceUpdates_deduplicatesDuplicateAlerts() { // Two identical alerts for route "34" -> should produce 2 service updates (one per language) val alert = makeAlert(routeShortName = "34") val response = EtatServiceResponse( @@ -69,11 +23,10 @@ class StmInfoServiceUpdateProviderTest { alerts = listOf(alert, alert), ) - val result = StmInfoServiceUpdateProvider.parseServiceUpdates( - etatServiceResponse = response, - headerTimestamp = now, - maxValidity = maxValidity, - sourceLabel = sourceLabel, + val result = response.toServiceUpdates( + maxValidity = MAX_VALIDITY, + sourceLabel = SOURCE_LABEL, + now = NOW, ) // 2 languages (fr, en) x 1 unique targetUUID -> 2 distinct service updates (not 4) @@ -81,7 +34,7 @@ class StmInfoServiceUpdateProviderTest { } @Test - fun testParseServiceUpdates_distinctLanguagesPerAlert() { + fun testToServiceUpdates_distinctLanguagesPerAlert() { // Alert has both "fr" and "en" in both header and description (potential duplicates in languages list) val alert = EtatServiceResponse.Alert( activePeriods = null, @@ -102,11 +55,10 @@ class StmInfoServiceUpdateProviderTest { ) val response = EtatServiceResponse(header = null, alerts = listOf(alert)) - val result = StmInfoServiceUpdateProvider.parseServiceUpdates( - etatServiceResponse = response, - headerTimestamp = now, - maxValidity = maxValidity, - sourceLabel = sourceLabel, + val result = response.toServiceUpdates( + maxValidity = MAX_VALIDITY, + sourceLabel = SOURCE_LABEL, + now = NOW, ) // Should produce exactly 2: one for "fr", one for "en" @@ -115,32 +67,79 @@ class StmInfoServiceUpdateProviderTest { } @Test - fun testParseServiceUpdates_nullResponse_returnsEmpty() { - val result = StmInfoServiceUpdateProvider.parseServiceUpdates( - etatServiceResponse = null, - headerTimestamp = now, - maxValidity = maxValidity, - sourceLabel = sourceLabel, + fun testToServiceUpdates_nullResponse_returnsEmpty() { + val result = null.toServiceUpdates( + maxValidity = MAX_VALIDITY, + sourceLabel = SOURCE_LABEL, + now = NOW, ) assertEquals(0, result.size) } @Test - fun testParseServiceUpdates_multipleDistinctAlerts() { + fun testToServiceUpdates_multipleDistinctAlerts() { // Alerts for two different routes -> should produce 4 service updates (2 per route, one per language) val alert34 = makeAlert(routeShortName = "34") val alert35 = makeAlert(routeShortName = "35") val response = EtatServiceResponse(header = null, alerts = listOf(alert34, alert35)) - val result = StmInfoServiceUpdateProvider.parseServiceUpdates( - etatServiceResponse = response, - headerTimestamp = now, - maxValidity = maxValidity, - sourceLabel = sourceLabel, + val result = response.toServiceUpdates( + maxValidity = MAX_VALIDITY, + sourceLabel = SOURCE_LABEL, + now = NOW, ) assertEquals(4, result.size) assertEquals(result.size, result.distinctBy { it.targetUUID to it.language }.size) } + + private fun makeAlert( + routeShortName: String, + directionId: String? = null, + stopCode: String? = null, + startInSec: Long? = null, + endInSec: Long? = null, + headerFr: String = "Titre", + headerEn: String = "Title", + descFr: String = "Description FR", + descEn: String = "Description EN", + ) = EtatServiceResponse.Alert( + activePeriods = EtatServiceResponse.Alert.ActivePeriods( + startInSec = startInSec, + endInSec = endInSec, + ), + cause = null, + effect = null, + // The STM API sends each attribute (route, direction, stop) as a separate InformedEntity object + informedEntities = listOfNotNull( + EtatServiceResponse.Alert.InformedEntity( + routeShortName = routeShortName, + directionId = null, + stopCode = null, + ), + directionId?.let { + EtatServiceResponse.Alert.InformedEntity( + routeShortName = null, + directionId = it, + stopCode = null, + ) + }, + stopCode?.let { + EtatServiceResponse.Alert.InformedEntity( + routeShortName = null, + directionId = null, + stopCode = it, + ) + }, + ), + headerTexts = listOf( + EtatServiceResponse.Alert.TranslatedText(language = "fr", text = headerFr), + EtatServiceResponse.Alert.TranslatedText(language = "en", text = headerEn), + ), + descriptionTexts = listOf( + EtatServiceResponse.Alert.TranslatedText(language = "fr", text = descFr), + EtatServiceResponse.Alert.TranslatedText(language = "en", text = descEn), + ), + ) } From 15934ca01a515398b380de758d443ab440ada739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Sat, 21 Mar 2026 16:55:52 -0400 Subject: [PATCH 5/6] cleanup --- .../provider/ca/info/stm/StmInfoServiceUpdateProvider.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt index 4fee36a3..0526de71 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/ca/info/stm/StmInfoServiceUpdateProvider.kt @@ -190,10 +190,10 @@ object StmInfoServiceUpdateProvider : MTLog.Loggable { sourceLabel = sourceLabel, now = now, ) - MTLog.i(this, "Found %d service updates.", serviceUpdates.size) + MTLog.i(this@StmInfoServiceUpdateProvider, "Found %d service updates.", serviceUpdates.size) if (Constants.DEBUG) { for (serviceUpdate in serviceUpdates) { - MTLog.d(this, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) + MTLog.d(this@StmInfoServiceUpdateProvider, "loadAgencyServiceUpdateDataFromWWW() > service update: %s.", serviceUpdate) } } return serviceUpdates From ddf98058fd9430bfe08a0bcb9956be88c3957332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Sat, 21 Mar 2026 16:56:54 -0400 Subject: [PATCH 6/6] cleanup --- .../provider/gtfs/GTFSProviderDbHelper.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java index 3639884a..fc5554e2 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java @@ -10,6 +10,7 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; +import org.mtransit.android.commons.Constants; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.NotificationUtils; import org.mtransit.android.commons.PackageManagerUtils; @@ -217,41 +218,41 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { return kotlin.Unit.INSTANCE; } ); // 1st - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_STRINGS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_STRINGS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_ROUTE, T_ROUTE_SQL_CREATE, T_ROUTE_SQL_INSERT, T_ROUTE_SQL_DROP, getRouteFiles(), 0, 0, allStrings, T_ROUTE_STRINGS_COLUMN_IDX); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_ROUTE, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_ROUTE, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_DIRECTION, T_DIRECTION_SQL_CREATE, T_DIRECTION_SQL_INSERT, T_DIRECTION_SQL_DROP, getDirectionFiles(), 0, 0, allStrings, T_DIRECTION_STRINGS_COLUMN_IDX); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_DIRECTION, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_DIRECTION, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_STOP, T_STOP_SQL_CREATE, T_STOP_SQL_INSERT, T_STOP_SQL_DROP, getStopFiles(), 0, 0, allStrings, T_STOP_STRINGS_COLUMN_IDX); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_STOP, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_STOP, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_DIRECTION_STOPS, T_DIRECTION_STOPS_SQL_CREATE, T_DIRECTION_STOPS_SQL_INSERT, T_DIRECTION_STOPS_SQL_DROP, getDirectionStopsFiles()); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_DIRECTION_STOPS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_DIRECTION_STOPS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (FeatureFlags.F_EXPORT_TRIP_ID) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_TRIP, T_TRIP_SQL_CREATE, T_TRIP_SQL_INSERT, T_TRIP_SQL_DROP, getTripFiles(), T_TRIP_SAME_COLUMNS_COUNT, T_TRIP_OTHER_COLUMNS_COUNT); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_TRIP, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_TRIP, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_SERVICE_IDS, T_SERVICE_IDS_SQL_CREATE, T_SERVICE_IDS_SQL_INSERT, T_SERVICE_IDS_SQL_DROP, getServiceIdsFiles()); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_SERVICE_IDS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_SERVICE_IDS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_SERVICE_DATES, T_SERVICE_DATES_SQL_CREATE, T_SERVICE_DATES_SQL_INSERT, T_SERVICE_DATES_SQL_DROP, getServiceDatesFiles(), T_SERVICE_DATES_SAME_COLUMNS_COUNT, T_SERVICE_DATES_OTHER_COLUMNS_COUNT); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_SERVICE_DATES, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_SERVICE_DATES, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); initDbTableWithRetry(context, db, T_TRIP_IDS, T_TRIP_IDS_SQL_CREATE, T_TRIP_IDS_SQL_INSERT, T_TRIP_IDS_SQL_DROP, getTripIdsFiles()); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_TRIP_IDS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_TRIP_IDS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); } if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); db.execSQL(T_ROUTE_DIRECTION_STOP_STATUS_SQL_CREATE); - MTLog.d(this, "Data: deploying DB... %s done (%s)", T_ROUTE_DIRECTION_STOP_STATUS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); + if (Constants.DEBUG) MTLog.d(this, "Data: deploying DB... %s done (%s)", T_ROUTE_DIRECTION_STOP_STATUS, MTLog.formatDuration(TimeUtils.currentTimeMillis() - startInMs)); if (notifEnabled) { nb.setSmallIcon(android.R.drawable.stat_notify_sync_noanim); NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, nbTotalOperations);