From fa37b8e6c809e983ba7ef06ae64c1a81f683675f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 1 Dec 2025 11:16:23 -0500 Subject: [PATCH 01/36] Vehicles locations --- .../android/commons/data/RouteDirection.java | 8 +- .../provider/ContentProviderConstants.java | 2 + .../provider/GTFSRealTimeProvider.java | 31 ++++++ .../commons/provider/gtfs/GtfsRealtimeExt.kt | 20 ++++ .../GTFSRealTimeVehiclePositionsProvider.kt | 2 + .../NextBusVehicleLocationsProvider.kt | 2 + .../VehicleLocationProvider.kt | 27 +++++ .../VehicleLocationProviderContract.kt | 105 ++++++++++++++++++ .../vehiclelocations/model/VehicleLocation.kt | 16 +++ src/main/res/values/gtfs_real_time_values.xml | 2 + 10 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt create mode 100644 src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt create mode 100644 src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt create mode 100644 src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt create mode 100644 src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt diff --git a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java index 891f9fdd..86ff32f3 100644 --- a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java +++ b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java @@ -70,11 +70,9 @@ public String toString() { @SuppressWarnings("unused") @NonNull public String toStringSimple() { - StringBuilder sb = new StringBuilder(); - sb.append(getRoute().getShortName()).append('-'); - sb.append(getDirection().getHeadsignValue()).append('>'); - sb.append('(').append(getAuthority()).append(')'); - return sb.toString(); + return getRoute().getShortName() + '-' + + getDirection().getHeadsignValue() + '>' + + '(' + getAuthority() + ')'; } @SuppressWarnings("unused") diff --git a/src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java b/src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java index 663271a2..e5c70e4a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java +++ b/src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java @@ -40,5 +40,7 @@ public final class ContentProviderConstants { // public static final int NEWS = 115; // + public static final int VEHICLE_LOCATION = 118; + // public static final int ALL = 999; } diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index dc156c0a..d72a81e1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -273,6 +273,37 @@ private static String getAGENCY_SERVICE_ALERTS_URL_CACHED(@NonNull Context conte return agencyServiceAlertsUrlCached; } + @Nullable + private static String agencyVehiclePositionsUrl = null; + + /** + * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. + */ + @NonNull + @SuppressLint("StringFormatInvalid") // empty string: set in module app + private static String getAGENCY_VEHICLE_POSITIONS_URL(@NonNull Context context, + @NonNull String token, + @SuppressWarnings("SameParameterValue") @NonNull String hash) { + if (agencyVehiclePositionsUrl == null) { + agencyVehiclePositionsUrl = context.getResources().getString(R.string.gtfs_real_time_agency_vehicle_positions_url, token, hash); + } + return agencyVehiclePositionsUrl; + } + + @Nullable + private static String agencyVehiclePositionsUrlCached = null; + + /** + * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. + */ + @NonNull + private static String getAGENCY_VEHICLE_POSITIONS_URL_CACHED(@NonNull Context context) { + if (agencyVehiclePositionsUrlCached == null) { + agencyVehiclePositionsUrlCached = context.getResources().getString(R.string.gtfs_real_time_agency_vehicle_positions_url_cached); + } + return agencyVehiclePositionsUrlCached; + } + @Nullable private static String serviceIdCleanupRegex = null; 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 6aa4c444..c6862e34 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 @@ -22,6 +22,26 @@ object GtfsRealtimeExt { } } + @JvmStatic + fun List.toVehicles(): List = + this.filter { it.hasAlert() }.map { it.vehicle }.distinct() + + @JvmStatic + fun List.toVehiclesWithIdPair(): List> = + this.filter { it.hasAlert() }.map { it.vehicle to it.id }.distinctBy { it.first } + + @JvmStatic + fun List.sort(nowMs: Long = TimeUtils.currentTimeMillis()): List = + this.sortedBy { vehiclePosition -> + vehiclePosition.timestamp + } + + @JvmStatic + fun List>.sortPair(nowMs: Long = TimeUtils.currentTimeMillis()): List> = + this.sortedBy { (vehiclePosition, _) -> + vehiclePosition.timestamp + } + @JvmStatic fun List.toAlerts(): List = this.filter { it.hasAlert() }.map { it.alert }.distinct() diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt new file mode 100644 index 00000000..015802ee --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -0,0 +1,2 @@ +package org.mtransit.android.commons.provider.vehiclelocations + diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt new file mode 100644 index 00000000..015802ee --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt @@ -0,0 +1,2 @@ +package org.mtransit.android.commons.provider.vehiclelocations + diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt new file mode 100644 index 00000000..73a4af89 --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -0,0 +1,27 @@ +package org.mtransit.android.commons.provider.vehiclelocations + +import android.content.UriMatcher +import org.mtransit.android.commons.provider.ContentProviderConstants +import org.mtransit.android.commons.provider.MTContentProvider + +abstract class VehicleLocationProvider : MTContentProvider(), + VehicleLocationProviderContract +{ + companion object { + val LOG_TAG: String = VehicleLocationProvider::class.java.simpleName + + fun getNewUriMatcher(authority: String) = UriMatcher(UriMatcher.NO_MATCH).apply { + append(this, authority) + } + + fun append(uriMatcher: UriMatcher, authority: String) { + uriMatcher.addURI(authority, VehicleLocationProviderContract.PING_PATH, ContentProviderConstants.PING) + uriMatcher.addURI(authority, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH, ContentProviderConstants.VEHICLE_LOCATION) + } + } + + override fun getLogTag() = LOG_TAG + + + +} \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt new file mode 100644 index 00000000..7a348ef3 --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -0,0 +1,105 @@ +package org.mtransit.android.commons.provider.vehiclelocations + +import android.net.Uri +import android.provider.BaseColumns +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import org.mtransit.android.commons.MTLog.Loggable +import org.mtransit.android.commons.data.RouteDirection +import org.mtransit.android.commons.data.RouteDirectionStop +import org.mtransit.android.commons.provider.ProviderContract +import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation + +interface VehicleLocationProviderContract : ProviderContract { + + companion object { + const val VEHICLE_LOCATION_PATH = "vehicle" + + const val PING_PATH = ProviderContract.PING_PATH + } + + val authorityUri: Uri + + val vehicleLocationMaxValidityInMs: Long + + val vehicleLocationValidityInMs: Long + + val minDurationBetweenVehicleLocationRefreshInMs: Long + + fun cacheVehicleLocations(newVehicleLocations: List) + + fun getCachedVehicleLocations(vehicleLocationFilter: Filter): List + + fun getNewVehicleLocations(vehicleLocationFilter: Filter): List + + fun deleteCachedVehicleLocation(vehicleLocationId: Int): Boolean + fun deleteCachedVehicleLocation(targetUUID: String, sourceId: String): Boolean + fun purgeUselessCachedVehicleLocations() + + val vehicleLocationDbTableName: String + + /** + * see [VehicleLocation] + */ + interface Columns { + companion object { + const val T_VEHICLE_LOCATION_K_ID: String = BaseColumns._ID + const val T_VEHICLE_LOCATION_K_TARGET_UUID = "target" + const val T_VEHICLE_LOCATION_K_LAST_UPDATE = "last_update" + const val T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS = "max_validity" + const val T_VEHICLE_LOCATION_K_LATITUDE = "latitude" + const val T_VEHICLE_LOCATION_K_LONGITUDE = "longitude" + // TODO... + } + } + + + @Serializable + data class Filter( + @SerialName("r_id") + val routeIdHash: Int?, // original // GTFS // cleaned + hash + @SerialName("r_sn") + val routeShortName: String, // used for NextBus + @SerialName("d_id") + val directionId: Int?, // original // GTFS + @SerialName("d_hv") + val directionHeadsignValue: String, // used for NextBus + @SerialName("t_ids") + val tripIds: List?, // original // GTFS // cleaned + @SerialName("s_id") + val stopIdHash: Int?, // original // GTFS // cleaned + hash + ) : Loggable { + + companion object { + private val LOG_TAG: String = VehicleLocationProviderContract::class.java.simpleName + ">" + Filter::class.java.simpleName + + fun from(rd: RouteDirection, stopIdHash: Int? = null, tripIds: List? = null) = Filter( + routeIdHash = rd.route.originalIdHash, + routeShortName = rd.route.shortName, + directionId = rd.direction.originalDirectionIdOrNull, + directionHeadsignValue = rd.direction.headsignValue, + tripIds = tripIds, + stopIdHash = stopIdHash, + ) + + fun from(rds: RouteDirectionStop, tripIds: List? = null) = Filter( + routeIdHash = rds.route.originalIdHash, + routeShortName = rds.route.shortName, + directionId = rds.direction.originalDirectionIdOrNull, + directionHeadsignValue = rds.direction.headsignValue, + tripIds = tripIds, + stopIdHash = rds.stop.originalIdHash, + ) + + fun fromJSON(jsonString: String): Filter = Json.decodeFromString(serializer(), jsonString) + + } + + override fun getLogTag() = LOG_TAG + + fun toJSON() = Json.encodeToString(serializer(), this) + + } + +} \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt new file mode 100644 index 00000000..ae837ca4 --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -0,0 +1,16 @@ +package org.mtransit.android.commons.provider.vehiclelocations.model + +data class VehicleLocation( + val id: Int, + val targetTripId: String?, + val targetUUID: String, // route+direction or just route / routeTag / routeTag+dirTag + val lastUpdateInMs: Long, + val maxValidityInMs: Long, + // + val vehicleId: String?, // not user visible + val vehicleLabel: String?, // user visible + val latitude: Double, + val longitude: Double, + val bearing: Float?, // in degree + val speed: Float?, // m/s OR km/h +) diff --git a/src/main/res/values/gtfs_real_time_values.xml b/src/main/res/values/gtfs_real_time_values.xml index 6a1e0da8..2e8bdd5c 100755 --- a/src/main/res/values/gtfs_real_time_values.xml +++ b/src/main/res/values/gtfs_real_time_values.xml @@ -4,6 +4,8 @@ -1 + + false From 9fbd49288adb851c046b3de5a811d4282abd068c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Tue, 2 Dec 2025 11:50:35 -0500 Subject: [PATCH 02/36] wip & cleanup --- .../android/commons/AppUpdateLauncher.kt | 4 +- .../android/commons/AppUpdateUtils.kt | 4 +- .../org/mtransit/android/commons/CursorExt.kt | 37 ++++++---- .../mtransit/android/commons/KeyboardUtils.kt | 6 +- .../android/commons/data/AppStatus.java | 2 +- .../org/mtransit/android/commons/data/Area.kt | 6 +- .../commons/data/AvailabilityPercent.java | 2 +- .../android/commons/data/DefaultPOI.java | 2 +- .../mtransit/android/commons/data/News.java | 2 +- .../android/commons/data/POIStatus.java | 2 +- .../android/commons/data/Schedule.java | 2 +- .../commons/data/ScheduleTimestamps.java | 2 +- .../android/commons/data/ServiceUpdate.java | 2 +- .../android/commons/data/ServiceUpdateKtx.kt | 2 +- .../commons/provider/CaEdmontonProvider.java | 5 ++ .../commons/provider/CaLTCOnlineProvider.java | 5 ++ .../commons/provider/CaSTOProvider.java | 5 ++ .../commons/provider/CaTransLinkProvider.java | 5 ++ .../provider/CleverDevicesProvider.java | 5 ++ .../commons/provider/GBFSProvider.java | 7 ++ .../commons/provider/GTFSPOIProvider.java | 3 + .../commons/provider/GTFSProvider.java | 8 ++- .../provider/GTFSProviderContract.java | 1 + .../provider/GTFSProviderDbHelper.java | 2 + .../commons/provider/GTFSRDSProvider.java | 1 + .../provider/GTFSRealTimeProvider.java | 72 +++++++++++++++++-- .../GTFSScheduleTimestampsProvider.java | 2 + .../commons/provider/GTFSStatusProvider.java | 2 + .../provider/GrandRiverTransitProvider.java | 5 ++ .../provider/GreaterSudburyProvider.java | 5 ++ .../commons/provider/InstagramNewsProvider.kt | 3 + .../provider/JCDecauxBikeStationProvider.java | 7 ++ .../commons/provider/NextBusProvider.java | 8 +++ .../commons/provider/OCTranspoProvider.java | 8 +++ .../commons/provider/OneBusAwayProvider.java | 5 ++ .../commons/provider/RSSNewsProvider.java | 2 + .../commons/provider/RTCQuebecProvider.java | 9 ++- .../provider/ReginaTransitProvider.java | 5 ++ .../commons/provider/StmInfoApiProvider.java | 8 +++ .../provider/StmInfoSubwayProvider.java | 5 ++ .../provider/StrategicMappingProvider.java | 5 ++ .../commons/provider/TwitterNewsProvider.kt | 3 + .../provider/WinnipegTransitProvider.java | 7 ++ .../commons/provider/YouTubeNewsProvider.kt | 5 ++ .../provider/{ => agency}/AgencyProvider.java | 5 +- .../{ => agency}/AgencyProviderContract.java | 4 +- .../AgencyProviderDeployWorker.kt | 20 +++--- .../{ => bike}/BikeStationDbHelper.java | 9 ++- .../{ => bike}/BikeStationProvider.java | 29 ++++---- .../ContentProviderConstants.java | 2 +- .../{ => common}/MTContentProvider.java | 2 +- .../{ => common}/MTSQLiteOpenHelper.java | 2 +- .../MTSearchRecentSuggestionsProvider.java | 2 +- .../{ => common}/ProviderContract.java | 2 +- .../gbfs/data/api/v3/GBFSDateAdapter.kt | 4 +- .../commons/provider/gtfs/GtfsRealtimeExt.kt | 8 +-- .../provider/{ => news}/NewsProvider.java | 17 +++-- .../{ => news}/NewsProviderContract.java | 3 +- .../news/twitter/TwitterDateAdapter.kt | 4 +- .../news/twitter/TwitterNewsDbHelper.kt | 2 +- .../news/youtube/YouTubeDateAdapter.kt | 4 +- .../news/youtube/YouTubeNewsDbHelper.kt | 4 +- .../provider/{ => poi}/POIProvider.java | 8 ++- .../{ => poi}/POIProviderContract.java | 4 +- .../ScheduleTimestampsProvider.java | 5 +- .../ScheduleTimestampsProviderContract.java | 3 +- .../ServiceUpdateCleaner.kt | 3 +- .../ServiceUpdateProvider.java | 5 +- .../ServiceUpdateProviderContract.java | 3 +- .../provider/{ => status}/StatusProvider.java | 5 +- .../{ => status}/StatusProviderContract.java | 3 +- .../GTFSRealTimeVehiclePositionsProvider.kt | 24 +++++++ .../VehicleLocationDbHelper.kt | 25 +++++++ .../VehicleLocationProvider.kt | 68 +++++++++++++++--- .../VehicleLocationProviderContract.kt | 17 +++-- .../vehiclelocations/model/VehicleLocation.kt | 50 ++++++++++++- .../commons/receiver/GenericReceiver.kt | 8 ++- .../commons/receiver/ModuleReceiver.kt | 9 +-- .../android/commons/ui/AppUpdateActivity.kt | 4 +- .../android/commons/ui/InvisibleActivity.kt | 4 +- .../commons/ui/ModuleRedirectActivity.java | 2 +- 81 files changed, 525 insertions(+), 136 deletions(-) rename src/main/java/org/mtransit/android/commons/provider/{ => agency}/AgencyProvider.java (97%) rename src/main/java/org/mtransit/android/commons/provider/{ => agency}/AgencyProviderContract.java (87%) rename src/main/java/org/mtransit/android/commons/provider/{ => agency}/AgencyProviderDeployWorker.kt (80%) rename src/main/java/org/mtransit/android/commons/provider/{ => bike}/BikeStationDbHelper.java (86%) rename src/main/java/org/mtransit/android/commons/provider/{ => bike}/BikeStationProvider.java (94%) rename src/main/java/org/mtransit/android/commons/provider/{ => common}/ContentProviderConstants.java (96%) rename src/main/java/org/mtransit/android/commons/provider/{ => common}/MTContentProvider.java (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => common}/MTSQLiteOpenHelper.java (97%) rename src/main/java/org/mtransit/android/commons/provider/{ => common}/MTSearchRecentSuggestionsProvider.java (97%) rename src/main/java/org/mtransit/android/commons/provider/{ => common}/ProviderContract.java (92%) rename src/main/java/org/mtransit/android/commons/provider/{ => news}/NewsProvider.java (96%) rename src/main/java/org/mtransit/android/commons/provider/{ => news}/NewsProviderContract.java (99%) rename src/main/java/org/mtransit/android/commons/provider/{ => poi}/POIProvider.java (97%) rename src/main/java/org/mtransit/android/commons/provider/{ => poi}/POIProviderContract.java (99%) rename src/main/java/org/mtransit/android/commons/provider/{ => scheduletimestamp}/ScheduleTimestampsProvider.java (89%) rename src/main/java/org/mtransit/android/commons/provider/{ => scheduletimestamp}/ScheduleTimestampsProviderContract.java (96%) rename src/main/java/org/mtransit/android/commons/provider/{ => serviceupdate}/ServiceUpdateCleaner.kt (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => serviceupdate}/ServiceUpdateProvider.java (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => serviceupdate}/ServiceUpdateProviderContract.java (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => status}/StatusProvider.java (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => status}/StatusProviderContract.java (98%) create mode 100644 src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt diff --git a/src/main/java/org/mtransit/android/commons/AppUpdateLauncher.kt b/src/main/java/org/mtransit/android/commons/AppUpdateLauncher.kt index 24173da5..e320c709 100644 --- a/src/main/java/org/mtransit/android/commons/AppUpdateLauncher.kt +++ b/src/main/java/org/mtransit/android/commons/AppUpdateLauncher.kt @@ -7,9 +7,9 @@ import org.mtransit.android.commons.ui.AppUpdateActivity object AppUpdateLauncher : MTLog.Loggable { - val LOG_TAG: String = AppUpdateLauncher::class.java.simpleName + private val LOG_TAG: String = AppUpdateLauncher::class.java.simpleName - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG @Suppress("SimplifyBooleanWithConstants", "KotlinConstantConditions") @JvmStatic diff --git a/src/main/java/org/mtransit/android/commons/AppUpdateUtils.kt b/src/main/java/org/mtransit/android/commons/AppUpdateUtils.kt index 26fa2ecd..7affda47 100644 --- a/src/main/java/org/mtransit/android/commons/AppUpdateUtils.kt +++ b/src/main/java/org/mtransit/android/commons/AppUpdateUtils.kt @@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit object AppUpdateUtils : MTLog.Loggable { - val LOG_TAG: String = AppUpdateUtils::class.java.simpleName + private val LOG_TAG: String = AppUpdateUtils::class.java.simpleName private const val FORCE_CHECK_IN_DEBUG = false // private const val FORCE_CHECK_IN_DEBUG = true // DEBUG @@ -28,7 +28,7 @@ object AppUpdateUtils : MTLog.Loggable { private const val FORCE_UPDATE_AVAILABLE = false // private const val FORCE_UPDATE_AVAILABLE = true // DEBUG - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG private const val PREF_KEY_AVAILABLE_VERSION_CODE = "pAvailableVersionCode" diff --git a/src/main/java/org/mtransit/android/commons/CursorExt.kt b/src/main/java/org/mtransit/android/commons/CursorExt.kt index 58a9cd55..f1de1562 100644 --- a/src/main/java/org/mtransit/android/commons/CursorExt.kt +++ b/src/main/java/org/mtransit/android/commons/CursorExt.kt @@ -1,3 +1,5 @@ +@file:Suppress("unused") + package org.mtransit.android.commons import android.database.Cursor @@ -17,17 +19,28 @@ fun Cursor.getDouble(columnName: String) = this.getDouble(getColumnIndexOrThrow( // endregion +// region Float + +fun Cursor.getFloat(columnName: String) = this.getFloat(getColumnIndexOrThrow(columnName)) + +fun Cursor.optFloat(columnIndex: Int, fallback: Float? = null) = + optNotNull(columnIndex)?.let { getFloat(it) } ?: fallback + +fun Cursor.optFloat(columnName: String, fallback: Float? = null) = + this.optFloat(getColumnIndex(columnName), fallback) + +// endregion + // region Int -fun Cursor.optInt(columnIndex: Int, fallback: Int? = null): Int? { - return optNotNull(columnIndex)?.let { getInt(it) } ?: fallback -} +fun Cursor.optInt(columnIndex: Int, fallback: Int? = null) = + optNotNull(columnIndex)?.let { getInt(it) } ?: fallback -fun Cursor.optInt(columnName: String, fallback: Int? = null) = this.optInt(getColumnIndex(columnName), fallback) +fun Cursor.optInt(columnName: String, fallback: Int? = null) = + this.optInt(getColumnIndex(columnName), fallback) -fun Cursor.optIntNN(columnIndex: Int, fallback: Int): Int { - return optNotNull(columnIndex)?.let { getInt(it) } ?: fallback -} +fun Cursor.optIntNN(columnIndex: Int, fallback: Int) = + optNotNull(columnIndex)?.let { getInt(it) } ?: fallback fun Cursor.optIntNN(columnName: String, fallback: Int) = this.optIntNN(getColumnIndex(columnName), fallback) @@ -45,15 +58,13 @@ fun Cursor.getLong(columnName: String) = this.getLong(getColumnIndexOrThrow(colu fun Cursor.getString(columnName: String) = this.getString(getColumnIndexOrThrow(columnName)).orEmpty() -fun Cursor.optString(columnIndex: Int, fallback: String? = null): String? { - return optNotNull(columnIndex)?.let { getString(it) } ?: fallback -} +fun Cursor.optString(columnIndex: Int, fallback: String? = null) = + optNotNull(columnIndex)?.let { getString(it) } ?: fallback fun Cursor.optString(columnName: String, fallback: String? = null) = this.optString(getColumnIndex(columnName), fallback) -fun Cursor.optStringNN(columnIndex: Int, fallback: String): String { - return optNotNull(columnIndex)?.let { getString(it) } ?: fallback -} +fun Cursor.optStringNN(columnIndex: Int, fallback: String): String = + optNotNull(columnIndex)?.let { getString(it) } ?: fallback fun Cursor.optStringNN(columnName: String, fallback: String) = this.optStringNN(getColumnIndex(columnName), fallback) diff --git a/src/main/java/org/mtransit/android/commons/KeyboardUtils.kt b/src/main/java/org/mtransit/android/commons/KeyboardUtils.kt index 20773a46..eff3b270 100644 --- a/src/main/java/org/mtransit/android/commons/KeyboardUtils.kt +++ b/src/main/java/org/mtransit/android/commons/KeyboardUtils.kt @@ -8,12 +8,10 @@ import android.view.inputmethod.InputMethodManager @Suppress("unused") class KeyboardUtils : MTLog.Loggable { - override fun getLogTag(): String { - return TAG - } + override fun getLogTag() = LOG_TAG companion object { - private val TAG = KeyboardUtils::class.java.simpleName + private val LOG_TAG: String = KeyboardUtils::class.java.simpleName @JvmStatic fun showKeyboard(activity: Activity?, view: View?) { diff --git a/src/main/java/org/mtransit/android/commons/data/AppStatus.java b/src/main/java/org/mtransit/android/commons/data/AppStatus.java index ac0514a8..016323fb 100644 --- a/src/main/java/org/mtransit/android/commons/data/AppStatus.java +++ b/src/main/java/org/mtransit/android/commons/data/AppStatus.java @@ -15,7 +15,7 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.R; import org.mtransit.android.commons.SpanUtils; -import org.mtransit.android.commons.provider.StatusProviderContract; +import org.mtransit.android.commons.provider.status.StatusProviderContract; @SuppressWarnings({"WeakerAccess", "unused"}) public class AppStatus extends POIStatus implements MTLog.Loggable { diff --git a/src/main/java/org/mtransit/android/commons/data/Area.kt b/src/main/java/org/mtransit/android/commons/data/Area.kt index bbb09c69..ebde240b 100644 --- a/src/main/java/org/mtransit/android/commons/data/Area.kt +++ b/src/main/java/org/mtransit/android/commons/data/Area.kt @@ -5,7 +5,7 @@ import androidx.room.Ignore import org.mtransit.android.commons.LocationUtils import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.getDouble -import org.mtransit.android.commons.provider.AgencyProviderContract +import org.mtransit.android.commons.provider.agency.AgencyProviderContract import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -23,7 +23,7 @@ data class Area( val maxLng: Double, ) : MTLog.Loggable { - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG @Ignore val northLat = this.maxLat @@ -73,7 +73,7 @@ data class Area( companion object { - private val LOG_TAG = Area::class.java.simpleName + private val LOG_TAG: String = Area::class.java.simpleName const val MAX_LAT: Double = 90.0 const val MIN_LAT: Double = -90.0 diff --git a/src/main/java/org/mtransit/android/commons/data/AvailabilityPercent.java b/src/main/java/org/mtransit/android/commons/data/AvailabilityPercent.java index d3ecea6c..e919a145 100644 --- a/src/main/java/org/mtransit/android/commons/data/AvailabilityPercent.java +++ b/src/main/java/org/mtransit/android/commons/data/AvailabilityPercent.java @@ -19,7 +19,7 @@ import org.mtransit.android.commons.R; import org.mtransit.android.commons.SpanUtils; import org.mtransit.android.commons.StringUtils; -import org.mtransit.android.commons.provider.StatusProviderContract; +import org.mtransit.android.commons.provider.status.StatusProviderContract; @SuppressWarnings({"WeakerAccess", "unused"}) public class AvailabilityPercent extends POIStatus implements MTLog.Loggable { diff --git a/src/main/java/org/mtransit/android/commons/data/DefaultPOI.java b/src/main/java/org/mtransit/android/commons/data/DefaultPOI.java index 8bdda295..3945f01c 100644 --- a/src/main/java/org/mtransit/android/commons/data/DefaultPOI.java +++ b/src/main/java/org/mtransit/android/commons/data/DefaultPOI.java @@ -17,7 +17,7 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.StringUtils; import org.mtransit.android.commons.data.DataSourceTypeId.DataSourceType; -import org.mtransit.android.commons.provider.POIProviderContract; +import org.mtransit.android.commons.provider.poi.POIProviderContract; import org.mtransit.commons.CommonsApp; import java.text.Normalizer; diff --git a/src/main/java/org/mtransit/android/commons/data/News.java b/src/main/java/org/mtransit/android/commons/data/News.java index ede811f7..4ec4a38c 100644 --- a/src/main/java/org/mtransit/android/commons/data/News.java +++ b/src/main/java/org/mtransit/android/commons/data/News.java @@ -13,7 +13,7 @@ import org.mtransit.android.commons.ColorUtils; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.TimeUtils; -import org.mtransit.android.commons.provider.NewsProviderContract; +import org.mtransit.android.commons.provider.news.NewsProviderContract; import org.mtransit.commons.CollectionUtils; import java.util.ArrayList; diff --git a/src/main/java/org/mtransit/android/commons/data/POIStatus.java b/src/main/java/org/mtransit/android/commons/data/POIStatus.java index afd4b455..c14e28dc 100644 --- a/src/main/java/org/mtransit/android/commons/data/POIStatus.java +++ b/src/main/java/org/mtransit/android/commons/data/POIStatus.java @@ -17,7 +17,7 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.SpanUtils; import org.mtransit.android.commons.TimeUtils; -import org.mtransit.android.commons.provider.StatusProviderContract; +import org.mtransit.android.commons.provider.status.StatusProviderContract; // TODO abstract public class POIStatus implements MTLog.Loggable { diff --git a/src/main/java/org/mtransit/android/commons/data/Schedule.java b/src/main/java/org/mtransit/android/commons/data/Schedule.java index 9f2cbbff..52a31afa 100644 --- a/src/main/java/org/mtransit/android/commons/data/Schedule.java +++ b/src/main/java/org/mtransit/android/commons/data/Schedule.java @@ -17,7 +17,7 @@ import org.mtransit.android.commons.R; import org.mtransit.android.commons.StringUtils; import org.mtransit.android.commons.TimeUtils; -import org.mtransit.android.commons.provider.StatusProviderContract; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CollectionUtils; import java.util.ArrayList; diff --git a/src/main/java/org/mtransit/android/commons/data/ScheduleTimestamps.java b/src/main/java/org/mtransit/android/commons/data/ScheduleTimestamps.java index 9f8e94b7..b9d55846 100644 --- a/src/main/java/org/mtransit/android/commons/data/ScheduleTimestamps.java +++ b/src/main/java/org/mtransit/android/commons/data/ScheduleTimestamps.java @@ -10,7 +10,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.mtransit.android.commons.MTLog; -import org.mtransit.android.commons.provider.ScheduleTimestampsProviderContract; +import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProviderContract; import org.mtransit.commons.CollectionUtils; import java.util.ArrayList; 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 45887d0d..0a9b97f3 100644 --- a/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java +++ b/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java @@ -11,7 +11,7 @@ import org.mtransit.android.commons.CursorExtKt; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.TimeUtils; -import org.mtransit.android.commons.provider.ServiceUpdateProviderContract; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; import java.util.Comparator; diff --git a/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt b/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt index 333311ec..ee4e62a0 100644 --- a/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt +++ b/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt @@ -3,7 +3,7 @@ package org.mtransit.android.commons.data import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.StringUtils import org.mtransit.android.commons.TimeUtils -import org.mtransit.android.commons.provider.ServiceUpdateProviderContract +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract fun ServiceUpdate.syncTargetUUID(targetUUIDs: Map?) { targetUUIDs?.takeIf { it.isNotEmpty() } ?: return diff --git a/src/main/java/org/mtransit/android/commons/provider/CaEdmontonProvider.java b/src/main/java/org/mtransit/android/commons/provider/CaEdmontonProvider.java index dbe0adb1..0e51e0e2 100644 --- a/src/main/java/org/mtransit/android/commons/provider/CaEdmontonProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/CaEdmontonProvider.java @@ -32,6 +32,10 @@ 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.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; @@ -51,6 +55,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class CaEdmontonProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/CaLTCOnlineProvider.java b/src/main/java/org/mtransit/android/commons/provider/CaLTCOnlineProvider.java index 03e04a7d..216e0304 100644 --- a/src/main/java/org/mtransit/android/commons/provider/CaLTCOnlineProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/CaLTCOnlineProvider.java @@ -35,6 +35,10 @@ import org.mtransit.android.commons.provider.CaLTCOnlineProvider.JBusTimes.JResult.JRealTimeResult; import org.mtransit.android.commons.provider.CaLTCOnlineProvider.JBusTimes.JResult.JStopTimeResult; import org.mtransit.android.commons.provider.CaLTCOnlineProvider.JBusTimes.JResult.JStopTimeResult.JStopTime; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; @@ -57,6 +61,7 @@ import javax.net.ssl.HttpsURLConnection; +// DO NOT MOVE: referenced in modules AndroidManifest.xml // Nov 15, 2020: DOES NOT WORK because Real-Time API stop IDs do not match with GTFS static (DISABLED) @SuppressLint("Registered") public class CaLTCOnlineProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/CaSTOProvider.java b/src/main/java/org/mtransit/android/commons/provider/CaSTOProvider.java index 9313f550..0ad27d44 100644 --- a/src/main/java/org/mtransit/android/commons/provider/CaSTOProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/CaSTOProvider.java @@ -29,6 +29,10 @@ import org.mtransit.android.commons.UriUtils; import org.mtransit.android.commons.data.News; import org.mtransit.android.commons.helpers.MTDefaultHandler; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.news.NewsProvider; +import org.mtransit.android.commons.provider.news.NewsProviderContract; import org.mtransit.android.commons.provider.news.NewsTextFormatter; import org.mtransit.commons.SourceUtils; import org.xml.sax.Attributes; @@ -56,6 +60,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +// DO NOT MOVE: referenced in modules AndroidManifest.xml /** @noinspection deprecation*/ @Deprecated // web site updated, not working anymore @SuppressLint("Registered") diff --git a/src/main/java/org/mtransit/android/commons/provider/CaTransLinkProvider.java b/src/main/java/org/mtransit/android/commons/provider/CaTransLinkProvider.java index 3d193fe3..43dc564e 100644 --- a/src/main/java/org/mtransit/android/commons/provider/CaTransLinkProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/CaTransLinkProvider.java @@ -32,6 +32,10 @@ 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.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; import org.mtransit.commons.provider.CaVancouverTransLinkProviderCommons; @@ -50,6 +54,7 @@ import java.util.TimeZone; import java.util.concurrent.TimeUnit; +// DO NOT MOVE: referenced in modules AndroidManifest.xml // The RTTI Open API is deprecated and will no longer be available after December 2, 2024. // https://www.translink.ca/about-us/doing-business-with-translink/app-developer-resources/rtti @Deprecated diff --git a/src/main/java/org/mtransit/android/commons/provider/CleverDevicesProvider.java b/src/main/java/org/mtransit/android/commons/provider/CleverDevicesProvider.java index 88e76c49..76ebd869 100644 --- a/src/main/java/org/mtransit/android/commons/provider/CleverDevicesProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/CleverDevicesProvider.java @@ -30,6 +30,10 @@ import org.mtransit.android.commons.data.Schedule.Timestamp; import org.mtransit.android.commons.helpers.MTDefaultHandler; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.SourceUtils; @@ -54,6 +58,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class CleverDevicesProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/GBFSProvider.java b/src/main/java/org/mtransit/android/commons/provider/GBFSProvider.java index 5152a864..86ce23b4 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GBFSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GBFSProvider.java @@ -22,7 +22,13 @@ import org.mtransit.android.commons.data.POI; import org.mtransit.android.commons.data.POIStatus; import org.mtransit.android.commons.data.POITools; +import org.mtransit.android.commons.provider.bike.BikeStationDbHelper; +import org.mtransit.android.commons.provider.bike.BikeStationProvider; import org.mtransit.android.commons.provider.gbfs.GBFSStorage; +import org.mtransit.android.commons.provider.poi.POIProvider; +import org.mtransit.android.commons.provider.poi.POIProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.SourceUtils; import java.net.HttpURLConnection; @@ -36,6 +42,7 @@ import javax.net.ssl.SSLHandshakeException; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class GBFSProvider extends BikeStationProvider { diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java index a87b0a93..11117d6c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java @@ -16,6 +16,9 @@ import org.mtransit.android.commons.R; import org.mtransit.android.commons.SqlUtils; import org.mtransit.android.commons.data.POI; +import org.mtransit.android.commons.provider.common.ProviderContract; +import org.mtransit.android.commons.provider.poi.POIProvider; +import org.mtransit.android.commons.provider.poi.POIProviderContract; import org.mtransit.commons.Constants; import java.util.Map; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java index 8a8f6db0..7c9966db 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java @@ -28,12 +28,18 @@ import org.mtransit.android.commons.data.DataSourceTypeId; import org.mtransit.android.commons.data.POIStatus; import org.mtransit.android.commons.data.ScheduleTimestamps; +import org.mtransit.android.commons.provider.agency.AgencyProvider; +import org.mtransit.android.commons.provider.bike.BikeStationProvider; +import org.mtransit.android.commons.provider.poi.POIProviderContract; +import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProviderContract; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import java.io.BufferedReader; import java.io.InputStreamReader; @SuppressWarnings("WeakerAccess") -@SuppressLint({"Registered"}) +// DO NOT MOVE: referenced in modules AndroidManifest.xml +@SuppressLint("Registered") public class GTFSProvider extends AgencyProvider implements POIProviderContract, StatusProviderContract, ScheduleTimestampsProviderContract, GTFSProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java index abdecebf..9f893af0 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java @@ -5,6 +5,7 @@ import androidx.annotation.NonNull; import org.mtransit.android.commons.ArrayUtils; +import org.mtransit.android.commons.provider.poi.POIProvider; import java.util.ArrayList; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java index 09a32994..2cd9c4b5 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java @@ -16,6 +16,8 @@ import org.mtransit.android.commons.R; import org.mtransit.android.commons.SqlUtils; import org.mtransit.android.commons.TimeUtils; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; import org.mtransit.commons.FeatureFlags; import org.mtransit.commons.GTFSCommons; import org.mtransit.commons.sql.SQLUtils; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java index 9614c705..9522822e 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java @@ -14,6 +14,7 @@ import org.mtransit.android.commons.Constants; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.SqlUtils; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; import org.mtransit.commons.sql.SQLJoinBuilder; import java.util.Locale; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index d72a81e1..bc0cf261 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -41,9 +41,19 @@ import org.mtransit.android.commons.data.ServiceUpdateKtxKt; import org.mtransit.android.commons.data.Stop; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage; import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt; import org.mtransit.android.commons.provider.gtfs.alert.GTFSRTAlertsManager; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; +import org.mtransit.android.commons.provider.vehiclelocations.GTFSRealTimeVehiclePositionsProvider; +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationDbHelper; +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProvider; +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract; +import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.GTFSCommons; @@ -77,8 +87,11 @@ import okhttp3.Request; import okhttp3.Response; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") -public class GTFSRealTimeProvider extends MTContentProvider implements ServiceUpdateProviderContract { +public class GTFSRealTimeProvider extends MTContentProvider implements + ServiceUpdateProviderContract, + VehicleLocationProviderContract { private static final String LOG_TAG = GTFSRealTimeProvider.class.getSimpleName(); @@ -281,9 +294,11 @@ private static String getAGENCY_SERVICE_ALERTS_URL_CACHED(@NonNull Context conte */ @NonNull @SuppressLint("StringFormatInvalid") // empty string: set in module app - private static String getAGENCY_VEHICLE_POSITIONS_URL(@NonNull Context context, - @NonNull String token, - @SuppressWarnings("SameParameterValue") @NonNull String hash) { + private static String getAGENCY_VEHICLE_POSITIONS_URL( + @NonNull Context context, + @NonNull String token, + @SuppressWarnings("SameParameterValue") @NonNull String hash + ) { if (agencyVehiclePositionsUrl == null) { agencyVehiclePositionsUrl = context.getResources().getString(R.string.gtfs_real_time_agency_vehicle_positions_url, token, hash); } @@ -428,6 +443,51 @@ private static String getAGENCY_TIME_ZONE(@NonNull Context context) { return agencyTimeZone; } + @Override + public long getMinDurationBetweenVehicleLocationRefreshInMs(boolean inFocus) { + return GTFSRealTimeVehiclePositionsProvider.getMinDurationBetweenRefreshInMs(inFocus); + } + + @Override + public long getVehicleLocationMaxValidityInMs() { + return GTFSRealTimeVehiclePositionsProvider.getMaxValidityInMs(); + } + + @Override + public long getVehicleLocationValidityInMs(boolean inFocus) { + return GTFSRealTimeVehiclePositionsProvider.getValidityInMs(inFocus); + } + + @Override + public void cacheVehicleLocations(@NonNull List newVehicleLocations) { + VehicleLocationProvider.cacheVehicleLocationsS(this, newVehicleLocations); + } + + @Override + public @NonNull List getCachedVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { + return Collections.emptyList(); // TODO + } + + @Override + public @NonNull List getNewVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { + return Collections.emptyList(); // TODO + } + + @Override + public boolean deleteCachedVehicleLocation(int vehicleLocationId) { + return VehicleLocationProvider.deleteCachedVehicleLocation(this, vehicleLocationId); + } + + @Override + public boolean purgeUselessCachedVehicleLocations() { + return VehicleLocationProvider.purgeUselessCachedVehicleLocations(this); + } + + @Override + public @NonNull String getVehicleLocationDbTableName() { + return GTFSRealTimeDbHelper.T_GTFS_REAL_TIME_VEHICLE_LOCATION; + } + private static final long SERVICE_UPDATE_MAX_VALIDITY_IN_MS = TimeUnit.DAYS.toMillis(1L); private static final long SERVICE_UPDATE_VALIDITY_IN_MS = TimeUnit.MINUTES.toMillis(30L); private static final long SERVICE_UPDATE_VALIDITY_IN_FOCUS_IN_MS = TimeUnit.MINUTES.toMillis(1L); @@ -852,7 +912,7 @@ private ArrayList loadAgencyServiceUpdateDataFromWWW(@NonNull Con try { GtfsRealtime.FeedMessage gFeedMessage = GtfsRealtime.FeedMessage.parseFrom(response.body().bytes()); List> alertsWithIdPair = GtfsRealtimeExt.toAlertsWithIdPair(gFeedMessage.getEntityList()); - for (Pair gAlertAndId : GtfsRealtimeExt.sortPair(alertsWithIdPair, newLastUpdateInMs)) { + for (Pair gAlertAndId : GtfsRealtimeExt.sortAlertsPair(alertsWithIdPair, newLastUpdateInMs)) { final GtfsRealtime.Alert gAlert = gAlertAndId.getFirst(); final String feedEntityId = gAlertAndId.getSecond(); if (Constants.DEBUG) { @@ -1534,6 +1594,8 @@ public String getLogTag() { */ protected static final String DB_NAME = "gtfsrealtime.db"; + static final String T_GTFS_REAL_TIME_VEHICLE_LOCATION = VehicleLocationDbHelper.T_VEHICLE_LOCATION; + static final String T_GTFS_REAL_TIME_SERVICE_UPDATE = ServiceUpdateProvider.ServiceUpdateDbHelper.T_SERVICE_UPDATE; private static final String T_GTFS_REAL_TIME_SERVICE_UPDATE_SQL_CREATE = ServiceUpdateProvider.ServiceUpdateDbHelper.getSqlCreateBuilder( diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java index 0664e8a2..9b639f08 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java @@ -17,6 +17,8 @@ import org.mtransit.android.commons.provider.agency.AgencyUtils; import org.mtransit.android.commons.provider.gtfs.GTFSStringsUtils; import org.mtransit.android.commons.provider.gtfs.GTFSTripIdsUtils; +import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProvider; +import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProviderContract; import org.mtransit.commons.FeatureFlags; import java.util.ArrayList; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java index d534666b..0d760bd7 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java @@ -26,6 +26,8 @@ import org.mtransit.android.commons.provider.agency.AgencyUtils; import org.mtransit.android.commons.provider.gtfs.GTFSStringsUtils; import org.mtransit.android.commons.provider.gtfs.GTFSTripIdsUtils; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CharUtils; import org.mtransit.commons.FeatureFlags; import org.mtransit.commons.GTFSCommons; diff --git a/src/main/java/org/mtransit/android/commons/provider/GrandRiverTransitProvider.java b/src/main/java/org/mtransit/android/commons/provider/GrandRiverTransitProvider.java index adcdbcc8..c38e39ab 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GrandRiverTransitProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GrandRiverTransitProvider.java @@ -33,6 +33,10 @@ import org.mtransit.android.commons.data.RouteDirectionStop; import org.mtransit.android.commons.data.Schedule; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; @@ -49,6 +53,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class GrandRiverTransitProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/GreaterSudburyProvider.java b/src/main/java/org/mtransit/android/commons/provider/GreaterSudburyProvider.java index df5fc6d9..b02dd2b5 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GreaterSudburyProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GreaterSudburyProvider.java @@ -34,6 +34,10 @@ import org.mtransit.android.commons.data.RouteDirectionStop; import org.mtransit.android.commons.data.Schedule; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.SourceUtils; import org.mtransit.commons.provider.GreaterSudburyProviderCommons; @@ -57,6 +61,7 @@ // https://opendata.greatersudbury.ca/datasets/mybus-transit-api // https://dataportal.greatersudbury.ca/swagger/ui/index#/MyBus +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class GreaterSudburyProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/InstagramNewsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/InstagramNewsProvider.kt index 7c7ad4b2..230ab652 100644 --- a/src/main/java/org/mtransit/android/commons/provider/InstagramNewsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/InstagramNewsProvider.kt @@ -24,6 +24,8 @@ import org.mtransit.android.commons.data.News import org.mtransit.android.commons.provider.InstagramNewsProvider.InstagramApi.JEdgeOwnerToTimelineMediaNode import org.mtransit.android.commons.provider.InstagramNewsProvider.InstagramApi.JProfileUser import org.mtransit.android.commons.provider.agency.AgencyUtils +import org.mtransit.android.commons.provider.news.NewsProvider +import org.mtransit.android.commons.provider.news.NewsProviderContract import retrofit2.Call import retrofit2.create import retrofit2.http.GET @@ -32,6 +34,7 @@ import java.io.IOException import java.util.Locale import java.util.concurrent.TimeUnit +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") class InstagramNewsProvider : NewsProvider() { diff --git a/src/main/java/org/mtransit/android/commons/provider/JCDecauxBikeStationProvider.java b/src/main/java/org/mtransit/android/commons/provider/JCDecauxBikeStationProvider.java index d9026ce4..f8f12332 100644 --- a/src/main/java/org/mtransit/android/commons/provider/JCDecauxBikeStationProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/JCDecauxBikeStationProvider.java @@ -20,6 +20,12 @@ import org.mtransit.android.commons.data.DefaultPOI; import org.mtransit.android.commons.data.POI; import org.mtransit.android.commons.data.POIStatus; +import org.mtransit.android.commons.provider.bike.BikeStationDbHelper; +import org.mtransit.android.commons.provider.bike.BikeStationProvider; +import org.mtransit.android.commons.provider.poi.POIProvider; +import org.mtransit.android.commons.provider.poi.POIProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import java.net.HttpURLConnection; import java.net.SocketException; @@ -31,6 +37,7 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @Deprecated // use GBFS @SuppressLint("Registered") public class JCDecauxBikeStationProvider extends BikeStationProvider { diff --git a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java index 3acf8a91..038bbdd1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java @@ -38,6 +38,13 @@ import org.mtransit.android.commons.data.Stop; import org.mtransit.android.commons.helpers.MTDefaultHandler; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; @@ -71,6 +78,7 @@ // https://retro.umoiq.com/xmlFeedDocs/NextBusXMLFeed.pdf // https://retro.umoiq.com/service/publicXMLFeed?command=agencyList // https://retro.umoiq.com/service/publicJSONFeed?command=agencyList +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class NextBusProvider extends MTContentProvider implements ServiceUpdateProviderContract, StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java b/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java index 5c3009e7..489936c3 100644 --- a/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java @@ -46,6 +46,13 @@ import org.mtransit.android.commons.provider.OCTranspoProvider.JGetNextTripsForStop.JGetNextTripsForStopResult.JRoute.JRouteDirection; import org.mtransit.android.commons.provider.OCTranspoProvider.JGetNextTripsForStop.JGetNextTripsForStopResult.JRoute.JRouteDirection.JTrips.JTrip; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; @@ -86,6 +93,7 @@ // https://www.octranspo.com/en/plan-your-trip/travel-tools/developers/legacy-oc-transpo-api-2.0 // https://octranspo-new.3scale.net/ // "API 2.0 is fully deprecated in Q1, 2025" +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressWarnings({"deprecation", "DeprecatedIsStillUsed"}) @Deprecated @SuppressLint("Registered") diff --git a/src/main/java/org/mtransit/android/commons/provider/OneBusAwayProvider.java b/src/main/java/org/mtransit/android/commons/provider/OneBusAwayProvider.java index 897241c3..20d6ee30 100644 --- a/src/main/java/org/mtransit/android/commons/provider/OneBusAwayProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/OneBusAwayProvider.java @@ -33,6 +33,10 @@ import org.mtransit.android.commons.data.RouteDirectionStop; import org.mtransit.android.commons.data.Schedule; import org.mtransit.android.commons.provider.agency.AgencyUtils; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; import org.mtransit.commons.provider.OneBusAwayProviderCommons; @@ -49,6 +53,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class OneBusAwayProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/RSSNewsProvider.java b/src/main/java/org/mtransit/android/commons/provider/RSSNewsProvider.java index 9b434ba9..e1a428cc 100644 --- a/src/main/java/org/mtransit/android/commons/provider/RSSNewsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/RSSNewsProvider.java @@ -27,6 +27,7 @@ import org.mtransit.android.commons.UriUtils; import org.mtransit.android.commons.data.News; import org.mtransit.android.commons.helpers.MTDefaultHandler; +import org.mtransit.android.commons.provider.news.NewsProvider; import org.mtransit.android.commons.provider.news.NewsTextFormatter; import org.mtransit.android.commons.provider.news.rss.RssNewProviderUtils; import org.mtransit.commons.CleanUtils; @@ -60,6 +61,7 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class RSSNewsProvider extends NewsProvider { diff --git a/src/main/java/org/mtransit/android/commons/provider/RTCQuebecProvider.java b/src/main/java/org/mtransit/android/commons/provider/RTCQuebecProvider.java index 19950a2d..93e4b608 100644 --- a/src/main/java/org/mtransit/android/commons/provider/RTCQuebecProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/RTCQuebecProvider.java @@ -40,10 +40,16 @@ import org.mtransit.android.commons.data.ServiceUpdateKtxKt; import org.mtransit.android.commons.data.Stop; import org.mtransit.android.commons.helpers.MTDefaultHandler; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; import org.mtransit.android.commons.provider.news.NewsTextFormatter; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; -import org.mtransit.commons.FeatureFlags; import org.mtransit.commons.SourceUtils; import org.mtransit.commons.provider.RTCQuebecProviderCommons; import org.xml.sax.Attributes; @@ -73,6 +79,7 @@ import okhttp3.Request; import okhttp3.Response; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class RTCQuebecProvider extends MTContentProvider implements StatusProviderContract, ServiceUpdateProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/ReginaTransitProvider.java b/src/main/java/org/mtransit/android/commons/provider/ReginaTransitProvider.java index 8d435a3d..3d382381 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ReginaTransitProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/ReginaTransitProvider.java @@ -33,6 +33,10 @@ 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.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; @@ -51,6 +55,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class ReginaTransitProvider extends MTContentProvider implements StatusProviderContract { 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 580f2ec4..05d3f685 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java @@ -50,6 +50,13 @@ import org.mtransit.android.commons.data.ServiceUpdate; import org.mtransit.android.commons.data.ServiceUpdateKtxKt; import org.mtransit.android.commons.data.Stop; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.SourceUtils; @@ -75,6 +82,7 @@ import okhttp3.Request; import okhttp3.Response; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class StmInfoApiProvider extends MTContentProvider implements StatusProviderContract, ServiceUpdateProviderContract, ProviderInstaller.ProviderInstallListener { diff --git a/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java b/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java index d388b1ac..0ed3ab70 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java @@ -39,6 +39,10 @@ import org.mtransit.android.commons.data.RouteDirectionStop; import org.mtransit.android.commons.data.ServiceUpdate; import org.mtransit.android.commons.data.ServiceUpdateKtxKt; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; +import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.SourceUtils; @@ -65,6 +69,7 @@ import okhttp3.Request; import okhttp3.Response; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class StmInfoSubwayProvider extends MTContentProvider implements ServiceUpdateProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/StrategicMappingProvider.java b/src/main/java/org/mtransit/android/commons/provider/StrategicMappingProvider.java index 0e93b4b4..49ec087a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StrategicMappingProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StrategicMappingProvider.java @@ -30,6 +30,10 @@ 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.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.SourceUtils; import java.net.HttpURLConnection; @@ -44,6 +48,7 @@ import java.util.TimeZone; import java.util.concurrent.TimeUnit; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class StrategicMappingProvider extends MTContentProvider implements StatusProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt index 5777c310..9912cdf5 100644 --- a/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/TwitterNewsProvider.kt @@ -24,6 +24,8 @@ import org.mtransit.android.commons.TimeUtils import org.mtransit.android.commons.UriUtils import org.mtransit.android.commons.data.News import org.mtransit.android.commons.provider.agency.AgencyUtils +import org.mtransit.android.commons.provider.news.NewsProvider +import org.mtransit.android.commons.provider.news.NewsProviderContract import org.mtransit.android.commons.provider.news.NewsTextFormatter import org.mtransit.android.commons.provider.news.twitter.model.Tweet import org.mtransit.android.commons.provider.news.twitter.model.TweetMediaType @@ -39,6 +41,7 @@ import java.util.Date import java.util.Locale import java.util.concurrent.TimeUnit +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") class TwitterNewsProvider : NewsProvider() { diff --git a/src/main/java/org/mtransit/android/commons/provider/WinnipegTransitProvider.java b/src/main/java/org/mtransit/android/commons/provider/WinnipegTransitProvider.java index dc810ee5..d093b7e7 100644 --- a/src/main/java/org/mtransit/android/commons/provider/WinnipegTransitProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/WinnipegTransitProvider.java @@ -39,6 +39,12 @@ 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.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.news.NewsProvider; +import org.mtransit.android.commons.provider.news.NewsProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.SourceUtils; import org.mtransit.commons.provider.WinnipegTransitProviderCommons; @@ -60,6 +66,7 @@ import javax.net.ssl.SSLHandshakeException; +// DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") public class WinnipegTransitProvider extends MTContentProvider implements StatusProviderContract, NewsProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/YouTubeNewsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/YouTubeNewsProvider.kt index 53a5de03..96b3dd02 100644 --- a/src/main/java/org/mtransit/android/commons/provider/YouTubeNewsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/YouTubeNewsProvider.kt @@ -1,5 +1,6 @@ package org.mtransit.android.commons.provider +import android.annotation.SuppressLint import android.content.Context import android.content.UriMatcher import android.net.Uri @@ -20,6 +21,8 @@ import org.mtransit.android.commons.UriUtils import org.mtransit.android.commons.data.News import org.mtransit.android.commons.linkifyAllURLs import org.mtransit.android.commons.provider.agency.AgencyUtils +import org.mtransit.android.commons.provider.news.NewsProvider +import org.mtransit.android.commons.provider.news.NewsProviderContract import org.mtransit.android.commons.provider.news.NewsTextFormatter import org.mtransit.android.commons.provider.news.youtube.YouTubeDateAdapter import org.mtransit.android.commons.provider.news.youtube.YouTubeV3Api @@ -35,6 +38,8 @@ import java.util.concurrent.TimeUnit // https://developers.google.com/youtube/v3 // https://developers.google.com/youtube/v3/docs/channels/list // https://developers.google.com/youtube/v3/docs/playlistItems/list +// DO NOT MOVE: referenced in modules AndroidManifest.xml +@SuppressLint("Registered") class YouTubeNewsProvider : NewsProvider() { companion object { diff --git a/src/main/java/org/mtransit/android/commons/provider/AgencyProvider.java b/src/main/java/org/mtransit/android/commons/provider/agency/AgencyProvider.java similarity index 97% rename from src/main/java/org/mtransit/android/commons/provider/AgencyProvider.java rename to src/main/java/org/mtransit/android/commons/provider/agency/AgencyProvider.java index 1e7468d2..ca55aceb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/AgencyProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/agency/AgencyProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.agency; import android.content.Context; import android.content.Intent; @@ -18,6 +18,9 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.data.Area; import org.mtransit.android.commons.data.DataSourceTypeId; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.GTFSProvider; +import org.mtransit.android.commons.provider.common.MTContentProvider; import org.mtransit.commons.FeatureFlags; public abstract class AgencyProvider extends MTContentProvider implements AgencyProviderContract, ProviderInstaller.ProviderInstallListener { diff --git a/src/main/java/org/mtransit/android/commons/provider/AgencyProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/agency/AgencyProviderContract.java similarity index 87% rename from src/main/java/org/mtransit/android/commons/provider/AgencyProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/agency/AgencyProviderContract.java index 6aee5bae..ae20ce5f 100644 --- a/src/main/java/org/mtransit/android/commons/provider/AgencyProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/agency/AgencyProviderContract.java @@ -1,4 +1,6 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.agency; + +import org.mtransit.android.commons.provider.common.ProviderContract; public interface AgencyProviderContract extends ProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/AgencyProviderDeployWorker.kt b/src/main/java/org/mtransit/android/commons/provider/agency/AgencyProviderDeployWorker.kt similarity index 80% rename from src/main/java/org/mtransit/android/commons/provider/AgencyProviderDeployWorker.kt rename to src/main/java/org/mtransit/android/commons/provider/agency/AgencyProviderDeployWorker.kt index f0517e20..80ce8b78 100644 --- a/src/main/java/org/mtransit/android/commons/provider/AgencyProviderDeployWorker.kt +++ b/src/main/java/org/mtransit/android/commons/provider/agency/AgencyProviderDeployWorker.kt @@ -1,8 +1,7 @@ -package org.mtransit.android.commons.provider +package org.mtransit.android.commons.provider.agency import android.content.Context import android.content.pm.ProviderInfo -import android.database.Cursor import android.net.Uri import androidx.work.Constraints import androidx.work.CoroutineWorker @@ -14,13 +13,13 @@ import kotlinx.coroutines.withContext import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.PackageManagerUtils import org.mtransit.android.commons.R -import org.mtransit.android.commons.SqlUtils import org.mtransit.android.commons.UriUtils class AgencyProviderDeployWorker( private val context: Context, - params: WorkerParameters -) : CoroutineWorker(context, params), MTLog.Loggable { + params: WorkerParameters, +) : CoroutineWorker(context, params), + MTLog.Loggable { companion object { @JvmStatic @@ -34,13 +33,13 @@ class AgencyProviderDeployWorker( ) .build() - val LOG_TAG: String = AgencyProviderDeployWorker::class.java.simpleName + private val LOG_TAG: String = AgencyProviderDeployWorker::class.java.simpleName const val WORK_MANAGER_TAG = "agency_deploy" const val FROM_WORKER = "from_worker" } - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG override suspend fun doWork() = withContext(Dispatchers.IO) { val agencyProviderMetaData = context.getString(R.string.agency_provider) @@ -55,16 +54,15 @@ class AgencyProviderDeployWorker( private fun ping(agencyProvider: ProviderInfo) { val authorityUri = UriUtils.newContentUri(agencyProvider.authority) - var cursor: Cursor? = null try { val pingUri = Uri.withAppendedPath(authorityUri, AgencyProviderContract.PING_PATH) val selection = FROM_WORKER MTLog.i(this, "Ping: ${agencyProvider.authority} ($selection)") - cursor = context.contentResolver.query(pingUri, null, selection, null, null) + context.contentResolver.query(pingUri, null, selection, null, null).use { + // do nothing + } } catch (e: Exception) { MTLog.w(this, e, "Error!") - } finally { - SqlUtils.closeQuietly(cursor) } } } \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/BikeStationDbHelper.java b/src/main/java/org/mtransit/android/commons/provider/bike/BikeStationDbHelper.java similarity index 86% rename from src/main/java/org/mtransit/android/commons/provider/BikeStationDbHelper.java rename to src/main/java/org/mtransit/android/commons/provider/bike/BikeStationDbHelper.java index 9c9a7c2c..cce2e271 100644 --- a/src/main/java/org/mtransit/android/commons/provider/BikeStationDbHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/bike/BikeStationDbHelper.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.bike; import android.content.Context; import android.database.sqlite.SQLiteDatabase; @@ -8,6 +8,9 @@ import org.mtransit.android.commons.PreferenceUtils; import org.mtransit.android.commons.R; import org.mtransit.android.commons.SqlUtils; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.poi.POIProvider; +import org.mtransit.android.commons.provider.status.StatusProvider; public class BikeStationDbHelper extends MTSQLiteOpenHelper { @@ -27,11 +30,11 @@ public String getLogTag() { /** * Override if multiple {@link BikeStationDbHelper} implementations in same app. */ - static final String PREF_KEY_LAST_UPDATE_MS = "pBikeStationLastUpdate"; + public static final String PREF_KEY_LAST_UPDATE_MS = "pBikeStationLastUpdate"; /** * Override if multiple {@link BikeStationDbHelper} implementations in same app. */ - static final String PREF_KEY_STATUS_LAST_UPDATE_MS = "pBikeStationStatusLastUpdate"; + public static final String PREF_KEY_STATUS_LAST_UPDATE_MS = "pBikeStationStatusLastUpdate"; static final String T_BIKE_STATION = POIProvider.POIDbHelper.T_POI; private static final String T_BIKE_STATION_SQL_CREATE = POIProvider.POIDbHelper.getSqlCreateBuilder(T_BIKE_STATION).build(); diff --git a/src/main/java/org/mtransit/android/commons/provider/BikeStationProvider.java b/src/main/java/org/mtransit/android/commons/provider/bike/BikeStationProvider.java similarity index 94% rename from src/main/java/org/mtransit/android/commons/provider/BikeStationProvider.java rename to src/main/java/org/mtransit/android/commons/provider/bike/BikeStationProvider.java index a9f829b1..7812f980 100644 --- a/src/main/java/org/mtransit/android/commons/provider/BikeStationProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/bike/BikeStationProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.bike; import android.content.ContentValues; import android.content.Context; @@ -30,6 +30,11 @@ import org.mtransit.android.commons.data.DataSourceTypeId; import org.mtransit.android.commons.data.POI; import org.mtransit.android.commons.data.POIStatus; +import org.mtransit.android.commons.provider.agency.AgencyProvider; +import org.mtransit.android.commons.provider.poi.POIProvider; +import org.mtransit.android.commons.provider.poi.POIProviderContract; +import org.mtransit.android.commons.provider.status.StatusProvider; +import org.mtransit.android.commons.provider.status.StatusProviderContract; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -108,7 +113,7 @@ private static Uri getAUTHORITYURI(@NonNull Context context) { * Override if multiple {@link BikeStationProvider} implementations in same app. */ @NonNull - static String getDATA_URL(@NonNull Context context) { + public static String getDATA_URL(@NonNull Context context) { if (dataUrl == null) { dataUrl = context.getResources().getString(R.string.bike_station_data_url); } @@ -135,7 +140,7 @@ public static String getTIME_ZONE(@NonNull Context context) { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getValue1Color(@NonNull Context context) { + public static int getValue1Color(@NonNull Context context) { if (value1Color < 0) { value1Color = ContextCompat.getColor(context, R.color.bike_station_value1_color); } @@ -147,7 +152,7 @@ static int getValue1Color(@NonNull Context context) { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getValue1ColorBg(@NonNull Context context) { + public static int getValue1ColorBg(@NonNull Context context) { if (value1ColorBg < 0) { value1ColorBg = ContextCompat.getColor(context, R.color.bike_station_value1_color_bg); } @@ -159,7 +164,7 @@ static int getValue1ColorBg(@NonNull Context context) { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getValue1SubValue1Color(@NonNull Context context) { + public static int getValue1SubValue1Color(@NonNull Context context) { if (value1SubValue1Color < 0) { value1SubValue1Color = ContextCompat.getColor(context, R.color.bike_station_value1_sub_value1_color); } @@ -171,7 +176,7 @@ static int getValue1SubValue1Color(@NonNull Context context) { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getValue1SubValue1ColorBg(@NonNull Context context) { + public static int getValue1SubValue1ColorBg(@NonNull Context context) { if (value1ColorSubValue1Bg < 0) { value1ColorSubValue1Bg = ContextCompat.getColor(context, R.color.bike_station_value1_sub_value1_color_bg); } @@ -183,7 +188,7 @@ static int getValue1SubValue1ColorBg(@NonNull Context context) { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getValue2Color(@NonNull Context context) { + public static int getValue2Color(@NonNull Context context) { if (value2Color < 0) { value2Color = ContextCompat.getColor(context, R.color.bike_station_value2_color); } @@ -195,7 +200,7 @@ static int getValue2Color(@NonNull Context context) { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getValue2ColorBg(@NonNull Context context) { + public static int getValue2ColorBg(@NonNull Context context) { if (value2ColorBg < 0) { value2ColorBg = ContextCompat.getColor(context, R.color.bike_station_value2_color_bg); } @@ -373,7 +378,7 @@ public int deleteMT(@NonNull Uri uri, @Nullable String selection, @Nullable Stri } @SuppressWarnings("UnusedReturnValue") - int deleteAllBikeStationData() { + public int deleteAllBikeStationData() { int affectedRows = 0; try { affectedRows = getWriteDB().delete(BikeStationDbHelper.T_BIKE_STATION, null, null); @@ -384,7 +389,7 @@ int deleteAllBikeStationData() { } @SuppressWarnings("UnusedReturnValue") - int deleteAllBikeStationStatusData() { + public int deleteAllBikeStationStatusData() { int affectedRows = 0; try { affectedRows = getWriteDB().delete(BikeStationDbHelper.T_BIKE_STATION_STATUS, null, null); @@ -561,7 +566,7 @@ public UriMatcher getURI_MATCHER() { /** * Override if multiple {@link BikeStationProvider} implementations in same app. */ - static int getAGENCY_TYPE_ID(@NonNull Context context) { + public static int getAGENCY_TYPE_ID(@NonNull Context context) { if (agencyTypeId < 0) { agencyTypeId = context.getResources().getInteger(R.integer.bike_station_agency_type); } @@ -662,7 +667,7 @@ public ArrayMap getSearchSuggestProjectionMap() { private static final String CLEAN_PARENTHESES_2_REPLACEMENT = "$1)"; @NonNull - static String cleanBikeStationName(@Nullable String name) { + public static String cleanBikeStationName(@Nullable String name) { if (name == null || name.isEmpty()) { return StringUtils.EMPTY; } diff --git a/src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java b/src/main/java/org/mtransit/android/commons/provider/common/ContentProviderConstants.java similarity index 96% rename from src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java rename to src/main/java/org/mtransit/android/commons/provider/common/ContentProviderConstants.java index e5c70e4a..9790167f 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ContentProviderConstants.java +++ b/src/main/java/org/mtransit/android/commons/provider/common/ContentProviderConstants.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.common; import android.database.Cursor; import android.database.MatrixCursor; diff --git a/src/main/java/org/mtransit/android/commons/provider/MTContentProvider.java b/src/main/java/org/mtransit/android/commons/provider/common/MTContentProvider.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/MTContentProvider.java rename to src/main/java/org/mtransit/android/commons/provider/common/MTContentProvider.java index 2743b428..dd58c628 100644 --- a/src/main/java/org/mtransit/android/commons/provider/MTContentProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/common/MTContentProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.common; import android.content.ContentProvider; import android.content.ContentValues; diff --git a/src/main/java/org/mtransit/android/commons/provider/MTSQLiteOpenHelper.java b/src/main/java/org/mtransit/android/commons/provider/common/MTSQLiteOpenHelper.java similarity index 97% rename from src/main/java/org/mtransit/android/commons/provider/MTSQLiteOpenHelper.java rename to src/main/java/org/mtransit/android/commons/provider/common/MTSQLiteOpenHelper.java index dd22c127..e5a512d7 100644 --- a/src/main/java/org/mtransit/android/commons/provider/MTSQLiteOpenHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/common/MTSQLiteOpenHelper.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.common; import android.content.Context; import android.database.DatabaseErrorHandler; diff --git a/src/main/java/org/mtransit/android/commons/provider/MTSearchRecentSuggestionsProvider.java b/src/main/java/org/mtransit/android/commons/provider/common/MTSearchRecentSuggestionsProvider.java similarity index 97% rename from src/main/java/org/mtransit/android/commons/provider/MTSearchRecentSuggestionsProvider.java rename to src/main/java/org/mtransit/android/commons/provider/common/MTSearchRecentSuggestionsProvider.java index 8a5cc852..249f16e0 100644 --- a/src/main/java/org/mtransit/android/commons/provider/MTSearchRecentSuggestionsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/common/MTSearchRecentSuggestionsProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.common; import android.content.ContentValues; import android.content.Context; diff --git a/src/main/java/org/mtransit/android/commons/provider/ProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/common/ProviderContract.java similarity index 92% rename from src/main/java/org/mtransit/android/commons/provider/ProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/common/ProviderContract.java index 91edf835..fda619e7 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/common/ProviderContract.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.common; import android.content.Context; import android.content.UriMatcher; diff --git a/src/main/java/org/mtransit/android/commons/provider/gbfs/data/api/v3/GBFSDateAdapter.kt b/src/main/java/org/mtransit/android/commons/provider/gbfs/data/api/v3/GBFSDateAdapter.kt index a42706e3..2e03aade 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gbfs/data/api/v3/GBFSDateAdapter.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gbfs/data/api/v3/GBFSDateAdapter.kt @@ -23,10 +23,10 @@ import java.util.TimeZone // 2023-07-17T13:34:13+02:00 | yyyy-MM-dd'T'HH:mm:ss:XXX class GBFSDateAdapter : JsonDeserializer, MTLog.Loggable { - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG companion object { - private val LOG_TAG = GBFSDateAdapter::class.java.simpleName + private val LOG_TAG: String = GBFSDateAdapter::class.java.simpleName // ISO 8601 @Deprecated("Not converted to Date anymore") 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 c6862e34..a20784ae 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 @@ -31,13 +31,13 @@ object GtfsRealtimeExt { this.filter { it.hasAlert() }.map { it.vehicle to it.id }.distinctBy { it.first } @JvmStatic - fun List.sort(nowMs: Long = TimeUtils.currentTimeMillis()): List = + fun List.sortVehicles(nowMs: Long = TimeUtils.currentTimeMillis()): List = this.sortedBy { vehiclePosition -> vehiclePosition.timestamp } @JvmStatic - fun List>.sortPair(nowMs: Long = TimeUtils.currentTimeMillis()): List> = + fun List>.sortVehiclesPair(nowMs: Long = TimeUtils.currentTimeMillis()): List> = this.sortedBy { (vehiclePosition, _) -> vehiclePosition.timestamp } @@ -51,7 +51,7 @@ object GtfsRealtimeExt { this.filter { it.hasAlert() }.map { it.alert to it.id }.distinctBy { it.first } @JvmStatic - fun List.sort(nowMs: Long = TimeUtils.currentTimeMillis()): List = + fun List.sortAlerts(nowMs: Long = TimeUtils.currentTimeMillis()): List = this.sortedBy { alert -> (alert.getActivePeriod(nowMs)?.startMs() ?: alert.activePeriodList.firstOrNull { it.hasStart() }?.startMs()) @@ -59,7 +59,7 @@ object GtfsRealtimeExt { } @JvmStatic - fun List>.sortPair(nowMs: Long = TimeUtils.currentTimeMillis()): List> = + fun List>.sortAlertsPair(nowMs: Long = TimeUtils.currentTimeMillis()): List> = this.sortedBy { (alert, _) -> (alert.getActivePeriod(nowMs)?.startMs() ?: alert.activePeriodList.firstOrNull { it.hasStart() }?.startMs()) diff --git a/src/main/java/org/mtransit/android/commons/provider/NewsProvider.java b/src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java similarity index 96% rename from src/main/java/org/mtransit/android/commons/provider/NewsProvider.java rename to src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java index dfdf96cd..41e5e888 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NewsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/news/NewsProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.news; import android.annotation.SuppressLint; import android.content.ContentValues; @@ -24,6 +24,9 @@ import org.mtransit.android.commons.StringUtils; import org.mtransit.android.commons.TimeUtils; import org.mtransit.android.commons.data.News; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.sql.SQLCreateBuilder; import org.mtransit.commons.sql.SQLInsertBuilder; @@ -220,7 +223,7 @@ public Cursor getNewsFromDB(@NonNull NewsProviderContract.Filter newsFilter) { private static final String LATEST_NEWS_LIMIT = "100"; @Nullable - static Cursor getDefaultNewsFromDB(@NonNull NewsProviderContract.Filter newsFilter, @NonNull NewsProviderContract provider) { + public static Cursor getDefaultNewsFromDB(@NonNull NewsProviderContract.Filter newsFilter, @NonNull NewsProviderContract provider) { try { String selection = newsFilter.getSqlSelection( NewsProviderContract.Columns.T_NEWS_K_UUID, @@ -273,7 +276,7 @@ public ArrayMap getNewsProjectionMap() { return newsProjectionMap; } - static ArrayMap getNewNewsProjectionMap(String authority) { + public static ArrayMap getNewNewsProjectionMap(String authority) { return SqlUtils.ProjectionMapBuilder.getNew() // .appendValue(SqlUtils.escapeString(authority), Columns.T_NEWS_K_AUTHORITY_META) // .appendTableColumn(NewsDbHelper.T_NEWS, NewsDbHelper.T_NEWS_K_ID, Columns.T_NEWS_K_ID) // @@ -345,7 +348,7 @@ public Uri insertMT(@NonNull Uri uri, @Nullable ContentValues values) { } @SuppressWarnings("UnusedReturnValue") - static synchronized int cacheNewsS(NewsProviderContract provider, ArrayList newNews) { + public static synchronized int cacheNewsS(NewsProviderContract provider, ArrayList newNews) { int affectedRows = 0; SQLiteDatabase db = null; try { @@ -378,7 +381,7 @@ public static void cacheNewsS(@NonNull NewsProviderContract provider, @NonNull N } @Nullable - static ArrayList getCachedNewsS(@NonNull NewsProviderContract provider, @NonNull Filter newFilter) { + public static ArrayList getCachedNewsS(@NonNull NewsProviderContract provider, @NonNull Filter newFilter) { Uri uri = getNewsContentUri(provider); String filterSelection = newFilter.getSqlSelection( NewsProviderContract.Columns.T_NEWS_K_UUID, @@ -423,7 +426,7 @@ private static Uri getNewsContentUri(@NonNull NewsProviderContract provider) { return Uri.withAppendedPath(provider.getAuthorityUri(), NewsProviderContract.NEWS_PATH); } - static boolean deleteCachedNews(@NonNull NewsProviderContract provider, @Nullable Integer newsId) { + public static boolean deleteCachedNews(@NonNull NewsProviderContract provider, @Nullable Integer newsId) { if (newsId == null) { return false; } @@ -437,7 +440,7 @@ static boolean deleteCachedNews(@NonNull NewsProviderContract provider, @Nullabl return deletedRows > 0; } - static boolean purgeUselessCachedNews(@NonNull NewsProviderContract provider) { + public static boolean purgeUselessCachedNews(@NonNull NewsProviderContract provider) { long oldestLastUpdate = TimeUtils.currentTimeMillis() - provider.getNewsMaxValidityInMs(); String selection = SqlUtils.getWhereInferior(NewsProviderContract.Columns.T_NEWS_K_LAST_UPDATE, oldestLastUpdate); int deletedRows = 0; diff --git a/src/main/java/org/mtransit/android/commons/provider/NewsProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java similarity index 99% rename from src/main/java/org/mtransit/android/commons/provider/NewsProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java index 3bd583d2..c26ae2eb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NewsProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/news/NewsProviderContract.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.news; import android.database.Cursor; import android.net.Uri; @@ -19,6 +19,7 @@ import org.mtransit.android.commons.data.News; import org.mtransit.android.commons.data.POI; import org.mtransit.android.commons.data.RouteDirectionStop; +import org.mtransit.android.commons.provider.common.ProviderContract; import org.mtransit.commons.CollectionUtils; import java.util.ArrayList; diff --git a/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterDateAdapter.kt b/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterDateAdapter.kt index b7eefc0c..82303655 100644 --- a/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterDateAdapter.kt +++ b/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterDateAdapter.kt @@ -27,10 +27,10 @@ class TwitterDateAdapter : JsonSerializer, MTLog.Loggable { - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG companion object { - private val LOG_TAG = TwitterDateAdapter::class.java.simpleName + private val LOG_TAG: String = TwitterDateAdapter::class.java.simpleName // 025-09-02T22:00:01.000Z // (added in v2.3)- diff --git a/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterNewsDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterNewsDbHelper.kt index 75debf72..10a5b5e1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterNewsDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/news/twitter/TwitterNewsDbHelper.kt @@ -5,7 +5,7 @@ import android.database.sqlite.SQLiteDatabase import org.mtransit.android.commons.R import org.mtransit.android.commons.SqlUtils import org.mtransit.android.commons.StringUtils -import org.mtransit.android.commons.provider.NewsProvider.NewsDbHelper +import org.mtransit.android.commons.provider.news.NewsProvider.NewsDbHelper class TwitterNewsDbHelper( val context: Context, diff --git a/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeDateAdapter.kt b/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeDateAdapter.kt index e20dc81e..2ab526fe 100644 --- a/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeDateAdapter.kt +++ b/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeDateAdapter.kt @@ -23,10 +23,10 @@ import java.util.TimeZone class YouTubeDateAdapter : JsonDeserializer, MTLog.Loggable { // TODO JsonSerializer to send start_time - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG companion object { - private val LOG_TAG = YouTubeDateAdapter::class.java.simpleName + private val LOG_TAG: String = YouTubeDateAdapter::class.java.simpleName // 025-09-02T22:00:01.000Z // (added in v2.3)- diff --git a/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeNewsDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeNewsDbHelper.kt index b571c848..bc9a100c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeNewsDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/news/youtube/YouTubeNewsDbHelper.kt @@ -5,9 +5,7 @@ import android.database.sqlite.SQLiteDatabase import org.mtransit.android.commons.R import org.mtransit.android.commons.SqlUtils import org.mtransit.android.commons.StringUtils -import org.mtransit.android.commons.provider.NewsProvider.NewsDbHelper -import org.mtransit.android.commons.provider.NewsProvider.NewsDbHelper.T_NEWS -import org.mtransit.android.commons.provider.NewsProvider.NewsDbHelper.getSqlCreateBuilder +import org.mtransit.android.commons.provider.news.NewsProvider.NewsDbHelper class YouTubeNewsDbHelper( val context: Context, diff --git a/src/main/java/org/mtransit/android/commons/provider/POIProvider.java b/src/main/java/org/mtransit/android/commons/provider/poi/POIProvider.java similarity index 97% rename from src/main/java/org/mtransit/android/commons/provider/POIProvider.java rename to src/main/java/org/mtransit/android/commons/provider/poi/POIProvider.java index eed8eec2..03ae1589 100644 --- a/src/main/java/org/mtransit/android/commons/provider/POIProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/poi/POIProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.poi; import android.annotation.SuppressLint; import android.app.SearchManager; @@ -24,6 +24,10 @@ import org.mtransit.android.commons.StringUtils; import org.mtransit.android.commons.data.DefaultPOI; import org.mtransit.android.commons.data.POI.POIUtils; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.common.ProviderContract; import org.mtransit.commons.sql.SQLCreateBuilder; import org.mtransit.commons.sql.SQLInsertBuilder; @@ -389,7 +393,7 @@ public Uri insertMT(@NonNull Uri uri, ContentValues values) { return null; } - protected static synchronized int insertDefaultPOIs(@NonNull POIProviderContract provider, Collection defaultPOIs) { + public static synchronized int insertDefaultPOIs(@NonNull POIProviderContract provider, Collection defaultPOIs) { int affectedRows = 0; SQLiteDatabase db = null; try { diff --git a/src/main/java/org/mtransit/android/commons/provider/POIProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/poi/POIProviderContract.java similarity index 99% rename from src/main/java/org/mtransit/android/commons/provider/POIProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/poi/POIProviderContract.java index 69c17294..208580f5 100644 --- a/src/main/java/org/mtransit/android/commons/provider/POIProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/poi/POIProviderContract.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.poi; import android.app.SearchManager; import android.database.Cursor; @@ -18,6 +18,8 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.SqlUtils; import org.mtransit.android.commons.StringUtils; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.common.ProviderContract; import org.mtransit.commons.CollectionUtils; import java.util.ArrayList; diff --git a/src/main/java/org/mtransit/android/commons/provider/ScheduleTimestampsProvider.java b/src/main/java/org/mtransit/android/commons/provider/scheduletimestamp/ScheduleTimestampsProvider.java similarity index 89% rename from src/main/java/org/mtransit/android/commons/provider/ScheduleTimestampsProvider.java rename to src/main/java/org/mtransit/android/commons/provider/scheduletimestamp/ScheduleTimestampsProvider.java index d2b5b3b8..36fed081 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ScheduleTimestampsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/scheduletimestamp/ScheduleTimestampsProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.scheduletimestamp; import android.content.UriMatcher; import android.database.Cursor; @@ -9,6 +9,9 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.data.ScheduleTimestamps; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.status.StatusProvider; public abstract class ScheduleTimestampsProvider extends MTContentProvider implements ScheduleTimestampsProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/ScheduleTimestampsProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/scheduletimestamp/ScheduleTimestampsProviderContract.java similarity index 96% rename from src/main/java/org/mtransit/android/commons/provider/ScheduleTimestampsProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/scheduletimestamp/ScheduleTimestampsProviderContract.java index 012bd17a..4d459a9b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ScheduleTimestampsProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/scheduletimestamp/ScheduleTimestampsProviderContract.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.scheduletimestamp; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -8,6 +8,7 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.data.RouteDirectionStop; import org.mtransit.android.commons.data.ScheduleTimestamps; +import org.mtransit.android.commons.provider.common.ProviderContract; public interface ScheduleTimestampsProviderContract extends ProviderContract { diff --git a/src/main/java/org/mtransit/android/commons/provider/ServiceUpdateCleaner.kt b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateCleaner.kt similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/ServiceUpdateCleaner.kt rename to src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateCleaner.kt index 7159879a..f68c2b9b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ServiceUpdateCleaner.kt +++ b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateCleaner.kt @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider +package org.mtransit.android.commons.provider.serviceupdate import org.mtransit.android.commons.HtmlUtils import org.mtransit.android.commons.LocaleUtils @@ -7,7 +7,6 @@ import org.mtransit.commons.Cleaner import org.mtransit.commons.RegexUtils.groupOr import org.mtransit.commons.RegexUtils.matchGroup import org.mtransit.commons.RegexUtils.maybe -import java.util.Locale @Suppress("MemberVisibilityCanBePrivate", "unused") object ServiceUpdateCleaner { diff --git a/src/main/java/org/mtransit/android/commons/provider/ServiceUpdateProvider.java b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/ServiceUpdateProvider.java rename to src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java index c5770c27..6ac276f6 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ServiceUpdateProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.serviceupdate; import android.content.Context; import android.content.UriMatcher; @@ -20,6 +20,9 @@ import org.mtransit.android.commons.StringUtils; import org.mtransit.android.commons.TimeUtils; import org.mtransit.android.commons.data.ServiceUpdate; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; import org.mtransit.commons.CollectionUtils; import org.mtransit.commons.sql.SQLCreateBuilder; diff --git a/src/main/java/org/mtransit/android/commons/provider/ServiceUpdateProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/ServiceUpdateProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java index 078e49ee..9a3aaa70 100644 --- a/src/main/java/org/mtransit/android/commons/provider/ServiceUpdateProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.serviceupdate; import android.net.Uri; import android.provider.BaseColumns; @@ -16,6 +16,7 @@ import org.mtransit.android.commons.data.Route; import org.mtransit.android.commons.data.RouteDirection; import org.mtransit.android.commons.data.ServiceUpdate; +import org.mtransit.android.commons.provider.common.ProviderContract; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/main/java/org/mtransit/android/commons/provider/StatusProvider.java b/src/main/java/org/mtransit/android/commons/provider/status/StatusProvider.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/StatusProvider.java rename to src/main/java/org/mtransit/android/commons/provider/status/StatusProvider.java index c842dfdf..103e7a72 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StatusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/status/StatusProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.status; import android.content.Context; import android.content.UriMatcher; @@ -21,6 +21,9 @@ import org.mtransit.android.commons.data.POI; import org.mtransit.android.commons.data.POIStatus; import org.mtransit.android.commons.data.Schedule; +import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.android.commons.provider.common.MTContentProvider; +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; import org.mtransit.commons.sql.SQLCreateBuilder; import java.util.Collection; diff --git a/src/main/java/org/mtransit/android/commons/provider/StatusProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/status/StatusProviderContract.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/StatusProviderContract.java rename to src/main/java/org/mtransit/android/commons/provider/status/StatusProviderContract.java index b22dfe02..7884a049 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StatusProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/status/StatusProviderContract.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.status; import android.net.Uri; import android.provider.BaseColumns; @@ -12,6 +12,7 @@ import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.SecureStringUtils; import org.mtransit.android.commons.data.POIStatus; +import org.mtransit.android.commons.provider.common.ProviderContract; import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 015802ee..6e6542bb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -1,2 +1,26 @@ package org.mtransit.android.commons.provider.vehiclelocations +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.minutes + +object GTFSRealTimeVehiclePositionsProvider { + + val VEHICLE_LOCATION_MAX_VALIDITY_IN_MS = 1.hours.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_MS = 30.minutes.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 10.minutes.inWholeMilliseconds + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds + + @JvmStatic + fun getMinDurationBetweenRefreshInMs(inFocus: Boolean) = + if (inFocus) VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS + else VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS + + @JvmStatic + fun getValidityInMs(inFocus: Boolean) = + if (inFocus) VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS + else VEHICLE_LOCATION_VALIDITY_IN_MS + + @JvmStatic + val maxValidityInMs: Long get() = VEHICLE_LOCATION_MAX_VALIDITY_IN_MS +} \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt new file mode 100644 index 00000000..9972c831 --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt @@ -0,0 +1,25 @@ +package org.mtransit.android.commons.provider.vehiclelocations + +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.provider.BaseColumns +import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper + +abstract class VehicleLocationDbHelper( + context: Context?, + dbName: String, + factory: SQLiteDatabase.CursorFactory?, + dbVersion: Int, +) : MTSQLiteOpenHelper( + context, + dbName, + factory, + dbVersion, +) { + companion object { + const val T_VEHICLE_LOCATION = "vehicle_location" + const val T_VEHICLE_LOCATION_K_ID = BaseColumns._ID + } + + abstract val dbName: String +} \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index 73a4af89..3177a644 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -1,14 +1,19 @@ package org.mtransit.android.commons.provider.vehiclelocations import android.content.UriMatcher -import org.mtransit.android.commons.provider.ContentProviderConstants -import org.mtransit.android.commons.provider.MTContentProvider +import androidx.core.database.sqlite.transaction +import org.mtransit.android.commons.MTLog +import org.mtransit.android.commons.SqlUtils +import org.mtransit.android.commons.TimeUtils +import org.mtransit.android.commons.provider.common.ContentProviderConstants +import org.mtransit.android.commons.provider.common.MTContentProvider +import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation abstract class VehicleLocationProvider : MTContentProvider(), - VehicleLocationProviderContract -{ + VehicleLocationProviderContract { + companion object { - val LOG_TAG: String = VehicleLocationProvider::class.java.simpleName + private val LOG_TAG: String = VehicleLocationProvider::class.java.simpleName fun getNewUriMatcher(authority: String) = UriMatcher(UriMatcher.NO_MATCH).apply { append(this, authority) @@ -18,10 +23,57 @@ abstract class VehicleLocationProvider : MTContentProvider(), uriMatcher.addURI(authority, VehicleLocationProviderContract.PING_PATH, ContentProviderConstants.PING) uriMatcher.addURI(authority, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH, ContentProviderConstants.VEHICLE_LOCATION) } - } - override fun getLogTag() = LOG_TAG + // TODO read DB here + + @JvmStatic + @Synchronized + fun cacheVehicleLocationsS(provider: VehicleLocationProviderContract, newVehicleLocations: List?): Int { + var affectedRows = 0 + try { + provider.getWriteDB().transaction { + newVehicleLocations?.forEach { vehicleLocation -> + insert(provider.dbTableName, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_ID, vehicleLocation.toContentValues()) + .let { rowId -> + if (rowId > 0L) affectedRows++ + } + } + } + } catch (e: Exception) { + MTLog.w(LOG_TAG, e, "ERROR while applying batch update to the database!") + } + return affectedRows + } + @JvmStatic + fun deleteCachedVehicleLocation(provider: VehicleLocationProviderContract, vehicleLocationId: Int?): Boolean { + vehicleLocationId ?: return false + val selection = SqlUtils.getWhereEquals(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_ID, vehicleLocationId) + var deletedRows = 0 + try { + deletedRows = provider.getWriteDB().delete(provider.dbTableName, selection, null) + } catch (e: Exception) { + MTLog.w(LOG_TAG, e, "Error while deleting cached vehicle location '%s'!", vehicleLocationId) + } + return deletedRows > 0 + } + @JvmStatic + fun purgeUselessCachedVehicleLocations(provider: VehicleLocationProviderContract): Boolean { + val oldestLastUpdate = TimeUtils.currentTimeMillis() - provider.vehicleLocationMaxValidityInMs + val selection = SqlUtils.getWhereInferior(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LAST_UPDATE, oldestLastUpdate) + var deletedRows = 0 + try { + deletedRows = provider.getWriteDB().delete(provider.dbTableName, selection, null) + } catch (e: Exception) { + MTLog.w(LOG_TAG, e, "Error while deleting cached vehicle locations!") + } + return deletedRows > 0 + } + } + + override fun getLogTag() = LOG_TAG +} -} \ No newline at end of file +private val VehicleLocationProviderContract.dbTableName: String + get() = this.vehicleLocationDbTableName diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 7a348ef3..4f2dcaef 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.json.Json import org.mtransit.android.commons.MTLog.Loggable import org.mtransit.android.commons.data.RouteDirection import org.mtransit.android.commons.data.RouteDirectionStop -import org.mtransit.android.commons.provider.ProviderContract +import org.mtransit.android.commons.provider.common.ProviderContract import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation interface VehicleLocationProviderContract : ProviderContract { @@ -23,9 +23,9 @@ interface VehicleLocationProviderContract : ProviderContract { val vehicleLocationMaxValidityInMs: Long - val vehicleLocationValidityInMs: Long + fun getVehicleLocationValidityInMs(inFocus: Boolean): Long - val minDurationBetweenVehicleLocationRefreshInMs: Long + fun getMinDurationBetweenVehicleLocationRefreshInMs(inFocus: Boolean): Long fun cacheVehicleLocations(newVehicleLocations: List) @@ -34,8 +34,7 @@ interface VehicleLocationProviderContract : ProviderContract { fun getNewVehicleLocations(vehicleLocationFilter: Filter): List fun deleteCachedVehicleLocation(vehicleLocationId: Int): Boolean - fun deleteCachedVehicleLocation(targetUUID: String, sourceId: String): Boolean - fun purgeUselessCachedVehicleLocations() + fun purgeUselessCachedVehicleLocations(): Boolean val vehicleLocationDbTableName: String @@ -46,11 +45,15 @@ interface VehicleLocationProviderContract : ProviderContract { companion object { const val T_VEHICLE_LOCATION_K_ID: String = BaseColumns._ID const val T_VEHICLE_LOCATION_K_TARGET_UUID = "target" + const val T_VEHICLE_LOCATION_K_TARGET_TRIP_ID = "target_trip_id" const val T_VEHICLE_LOCATION_K_LAST_UPDATE = "last_update" const val T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS = "max_validity" + const val T_VEHICLE_LOCATION_K_VEHICLE_ID = "vehicle_id" + const val T_VEHICLE_LOCATION_K_VEHICLE_LABEL = "vehicle_label" const val T_VEHICLE_LOCATION_K_LATITUDE = "latitude" const val T_VEHICLE_LOCATION_K_LONGITUDE = "longitude" - // TODO... + const val T_VEHICLE_LOCATION_K_BEARING = "bearing" + const val T_VEHICLE_LOCATION_K_SPEED = "speed" } } @@ -102,4 +105,4 @@ interface VehicleLocationProviderContract : ProviderContract { } -} \ No newline at end of file +} diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index ae837ca4..60a24130 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -1,9 +1,19 @@ package org.mtransit.android.commons.provider.vehiclelocations.model +import android.content.ContentValues +import android.database.Cursor +import org.mtransit.android.commons.getDouble +import org.mtransit.android.commons.getLong +import org.mtransit.android.commons.optInt +import org.mtransit.android.commons.getString +import org.mtransit.android.commons.optFloat +import org.mtransit.android.commons.optString +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract + data class VehicleLocation( - val id: Int, - val targetTripId: String?, + val id: Int?, val targetUUID: String, // route+direction or just route / routeTag / routeTag+dirTag + val targetTripId: String?, // cleaned val lastUpdateInMs: Long, val maxValidityInMs: Long, // @@ -13,4 +23,38 @@ data class VehicleLocation( val longitude: Double, val bearing: Float?, // in degree val speed: Float?, // m/s OR km/h -) +) { + + companion object { + @JvmStatic + fun fromCursor(cursor: Cursor) = VehicleLocation( + id = cursor.optInt(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_ID), + targetUUID = cursor.getString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID), + targetTripId = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID), + lastUpdateInMs = cursor.getLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LAST_UPDATE), + maxValidityInMs = cursor.getLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS), + // + vehicleId = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID), + vehicleLabel = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL), + latitude = cursor.getDouble(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE), + longitude = cursor.getDouble(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE), + bearing = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING), + speed = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED), + ) + } + + fun toContentValues() = ContentValues().apply { + id?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_ID, it) } // ELSE AUTO INCREMENT + put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUID) + targetTripId?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID, it) } + put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LAST_UPDATE, lastUpdateInMs) + put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS, maxValidityInMs) + // + vehicleId?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID, it) } + vehicleLabel?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, it) } + put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE, latitude) + put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE, longitude) + bearing?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) } + speed?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED, it) } + } +} diff --git a/src/main/java/org/mtransit/android/commons/receiver/GenericReceiver.kt b/src/main/java/org/mtransit/android/commons/receiver/GenericReceiver.kt index a247d122..a62e84e1 100644 --- a/src/main/java/org/mtransit/android/commons/receiver/GenericReceiver.kt +++ b/src/main/java/org/mtransit/android/commons/receiver/GenericReceiver.kt @@ -7,11 +7,13 @@ import org.mtransit.android.commons.MTLog class GenericReceiver : BroadcastReceiver(), MTLog.Loggable { - override fun getLogTag(): String { - return GenericReceiver::class.java.simpleName + companion object { + private val LOG_TAG: String = GenericReceiver::class.java.simpleName } + override fun getLogTag() = LOG_TAG + override fun onReceive(context: Context?, intent: Intent?) { // DO NOTHING } -} \ No newline at end of file +} diff --git a/src/main/java/org/mtransit/android/commons/receiver/ModuleReceiver.kt b/src/main/java/org/mtransit/android/commons/receiver/ModuleReceiver.kt index 70f86d6f..7f37595d 100644 --- a/src/main/java/org/mtransit/android/commons/receiver/ModuleReceiver.kt +++ b/src/main/java/org/mtransit/android/commons/receiver/ModuleReceiver.kt @@ -14,13 +14,16 @@ import org.mtransit.android.commons.R import org.mtransit.android.commons.SqlUtils import org.mtransit.android.commons.UriUtils import org.mtransit.android.commons.data.DataSourceTypeId.isGTFSType -import org.mtransit.android.commons.provider.AgencyProviderContract +import org.mtransit.android.commons.provider.agency.AgencyProviderContract import org.mtransit.android.commons.provider.GTFSProvider import org.mtransit.commons.FeatureFlags class ModuleReceiver : BroadcastReceiver(), MTLog.Loggable { companion object { + + private val LOG_TAG: String = ModuleReceiver::class.java.simpleName + @Suppress("DEPRECATION") private val ACTIONS_PACKAGE = listOf( @@ -50,9 +53,7 @@ class ModuleReceiver : BroadcastReceiver(), MTLog.Loggable { private val ACTIONS_SUPPORTED = ACTIONS_PACKAGE + ACTIONS_MY_PACKAGE } - override fun getLogTag(): String { - return ModuleReceiver::class.java.simpleName - } + override fun getLogTag() = LOG_TAG override fun onReceive(context: Context?, intent: Intent?) { if (context == null) { diff --git a/src/main/java/org/mtransit/android/commons/ui/AppUpdateActivity.kt b/src/main/java/org/mtransit/android/commons/ui/AppUpdateActivity.kt index 8e30f540..18e958ab 100644 --- a/src/main/java/org/mtransit/android/commons/ui/AppUpdateActivity.kt +++ b/src/main/java/org/mtransit/android/commons/ui/AppUpdateActivity.kt @@ -17,7 +17,7 @@ class AppUpdateActivity : Activity(), MTLog.Loggable { companion object { - val LOG_TAG: String = AppUpdateActivity::class.java.simpleName + private val LOG_TAG: String = AppUpdateActivity::class.java.simpleName private const val APP_UPDATE_IN_PROGRESS = "app_update_in_progress" private const val APP_UPDATE_IN_PROGRESS_DEFAULT = false @@ -25,7 +25,7 @@ class AppUpdateActivity : Activity(), val CLASS_NAME: String = AppUpdateActivity::class.java.canonicalName ?: "org.mtransit.android.commons.ui.AppUpdateActivity" } - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG private val progressBar: View by lazy { findViewById(R.id.progress_bar) } diff --git a/src/main/java/org/mtransit/android/commons/ui/InvisibleActivity.kt b/src/main/java/org/mtransit/android/commons/ui/InvisibleActivity.kt index 031e1a3e..22724919 100644 --- a/src/main/java/org/mtransit/android/commons/ui/InvisibleActivity.kt +++ b/src/main/java/org/mtransit/android/commons/ui/InvisibleActivity.kt @@ -12,13 +12,13 @@ class InvisibleActivity : Activity(), MTLog.Loggable { companion object { - val LOG_TAG: String = InvisibleActivity::class.java.simpleName + private val LOG_TAG: String = InvisibleActivity::class.java.simpleName @Suppress("unused") val CLASS_NAME: String = InvisibleActivity::class.java.canonicalName ?: "org.mtransit.android.commons.ui.InvisibleActivity" } - override fun getLogTag(): String = LOG_TAG + override fun getLogTag() = LOG_TAG override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/src/main/java/org/mtransit/android/commons/ui/ModuleRedirectActivity.java b/src/main/java/org/mtransit/android/commons/ui/ModuleRedirectActivity.java index ea03e45d..50c3daba 100644 --- a/src/main/java/org/mtransit/android/commons/ui/ModuleRedirectActivity.java +++ b/src/main/java/org/mtransit/android/commons/ui/ModuleRedirectActivity.java @@ -38,7 +38,7 @@ import org.mtransit.android.commons.TaskUtils; import org.mtransit.android.commons.UriUtils; import org.mtransit.android.commons.data.DataSourceTypeId; -import org.mtransit.android.commons.provider.AgencyProviderContract; +import org.mtransit.android.commons.provider.agency.AgencyProviderContract; import org.mtransit.android.commons.task.MTCancellableAsyncTask; import org.mtransit.commons.FeatureFlags; From b7fec2aca6e455d0be1dac0626a72476fb095fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Tue, 2 Dec 2025 16:17:10 -0500 Subject: [PATCH 03/36] wip --- .../provider/GTFSRealTimeProvider.java | 10 +- .../GTFSRealTimeVehiclePositionsProvider.kt | 25 +++ .../VehicleLocationDbHelper.kt | 14 +- .../VehicleLocationProvider.kt | 66 +++++++- .../VehicleLocationProviderContract.kt | 156 +++++++++++++----- .../vehiclelocations/model/VehicleLocation.kt | 3 + 6 files changed, 229 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index bc0cf261..209d92ae 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -464,8 +464,8 @@ public void cacheVehicleLocations(@NonNull List newVehicleLocat } @Override - public @NonNull List getCachedVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { - return Collections.emptyList(); // TODO + public @Nullable List getCachedVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { + return GTFSRealTimeVehiclePositionsProvider.getCached(this, vehicleLocationFilter); } @Override @@ -627,12 +627,12 @@ private Map getProviderTargetUUIDs(@NonNull Context context, @No } @NonNull - private String getAgencyTag(@NonNull Context context) { + public String getAgencyTag(@NonNull Context context) { return getRDS_AGENCY_ID(context); } @NonNull - private String getRouteTag(@NonNull RouteDirectionStop rds) { + public String getRouteTag(@NonNull RouteDirectionStop rds) { return getRouteTag(rds.getRoute()); } @@ -692,7 +692,7 @@ protected static String getAgencyStopTagTargetUUID(@NonNull String agencyTag, @N } @NonNull - protected static String getAgencyRouteTagTargetUUID(@NonNull String agencyTag, @NonNull String routeTag) { + public static String getAgencyRouteTagTargetUUID(@NonNull String agencyTag, @NonNull String routeTag) { return POI.POIUtils.getUUID(agencyTag, "ri" + routeTag); } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 6e6542bb..512cbac4 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -1,5 +1,11 @@ package org.mtransit.android.commons.provider.vehiclelocations +import android.content.Context +import org.mtransit.android.commons.data.Route +import org.mtransit.android.commons.data.RouteDirection +import org.mtransit.android.commons.data.RouteDirectionStop +import org.mtransit.android.commons.provider.GTFSRealTimeProvider +import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes @@ -23,4 +29,23 @@ object GTFSRealTimeVehiclePositionsProvider { @JvmStatic val maxValidityInMs: Long get() = VEHICLE_LOCATION_MAX_VALIDITY_IN_MS + + @JvmStatic + fun getCached(context: Context, provider: GTFSRealTimeProvider, vehicleLocationFilter: VehicleLocationProviderContract.Filter): List? { + return (vehicleLocationFilter.poi as? RouteDirectionStop)?.let { getCached(context, provider, it) } + ?: vehicleLocationFilter.routeDirection?.let { getCached(context, provider, it) } + ?: vehicleLocationFilter.route?.let { getCached(context, provider, it) } + } + + private fun getCached(context: Context, provider: GTFSRealTimeProvider, rds: RouteDirectionStop): List? { + TODO() + } + + private fun getCached(context: Context, provider: GTFSRealTimeProvider, rd: RouteDirection): List? { + TODO() + } + + private fun getCached(context: Context, provider: GTFSRealTimeProvider, r: Route): List? { + TODO() + } } \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt index 9972c831..9893b21c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt @@ -18,7 +18,19 @@ abstract class VehicleLocationDbHelper( ) { companion object { const val T_VEHICLE_LOCATION = "vehicle_location" - const val T_VEHICLE_LOCATION_K_ID = BaseColumns._ID + + const val T_VEHICLE_LOCATION_K_ID: String = BaseColumns._ID + const val T_VEHICLE_LOCATION_K_TARGET_UUID = "target" + const val T_VEHICLE_LOCATION_K_TARGET_TRIP_ID = "target_trip_id" + const val T_VEHICLE_LOCATION_K_LAST_UPDATE = "last_update" + const val T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS = "max_validity" + + const val T_VEHICLE_LOCATION_K_VEHICLE_ID = "vehicle_id" + const val T_VEHICLE_LOCATION_K_VEHICLE_LABEL = "vehicle_label" + const val T_VEHICLE_LOCATION_K_LATITUDE = "latitude" + const val T_VEHICLE_LOCATION_K_LONGITUDE = "longitude" + const val T_VEHICLE_LOCATION_K_BEARING = "bearing" + const val T_VEHICLE_LOCATION_K_SPEED = "speed" } abstract val dbName: String diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index 3177a644..259b8f2b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -1,6 +1,8 @@ package org.mtransit.android.commons.provider.vehiclelocations import android.content.UriMatcher +import android.database.sqlite.SQLiteQueryBuilder +import android.net.Uri import androidx.core.database.sqlite.transaction import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.SqlUtils @@ -24,7 +26,69 @@ abstract class VehicleLocationProvider : MTContentProvider(), uriMatcher.addURI(authority, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH, ContentProviderConstants.VEHICLE_LOCATION) } - // TODO read DB here + @JvmStatic + fun getCachedVehicleLocationsS(provider: VehicleLocationProviderContract, targetUUIDs: Collection): List? { + return getCachedVehicleLocationsS( + provider, + provider.contentUri, + SqlUtils.getWhereInString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUIDs) + ) + } + + @JvmStatic + fun getCachedVehicleLocationsS(provider: VehicleLocationProviderContract, targetUUID: String): List? { + return getCachedVehicleLocationsS( + provider, + provider.contentUri, + SqlUtils.getWhereEqualsString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUID) + ) + } + + //@formatter:off + @JvmStatic + private val VEHICLE_LOCATION_PROJECTION_MAP = SqlUtils.ProjectionMapBuilder.getNew() + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_ID, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_ID) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_TARGET_UUID, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_LAST_UPDATE, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LAST_UPDATE) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS) + + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_VEHICLE_ID, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_LATITUDE, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_LONGITUDE, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_BEARING, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_SPEED, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED) + .build() + //@formatter:on + + private fun getCachedVehicleLocationsS(provider: VehicleLocationProviderContract, uri: Uri?, selection: String?): List? = + try { + SQLiteQueryBuilder() + .apply { + tables = provider.dbTableName + projectionMap = VEHICLE_LOCATION_PROJECTION_MAP + }.query( + provider.getReadDB(), VehicleLocationProviderContract.PROJECTION_VEHICLE_LOCATION, selection, null, null, + null, null, null + ).use { cursor -> + buildList { + if (cursor != null && cursor.count > 0) { + if (cursor.moveToFirst()) { + do { + add(VehicleLocation.fromCursor(cursor)) + } while (cursor.moveToNext()) + } + } + } + } + } catch (e: Exception) { + MTLog.w(LOG_TAG, e, "Error!") + null + } + + private val VehicleLocationProviderContract.contentUri: Uri + get() = Uri.withAppendedPath(this.authorityUri, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH) @JvmStatic @Synchronized diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 4f2dcaef..17b57178 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -1,13 +1,19 @@ package org.mtransit.android.commons.provider.vehiclelocations +import android.annotation.SuppressLint import android.net.Uri import android.provider.BaseColumns +import androidx.annotation.Discouraged import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json +import org.json.JSONException +import org.json.JSONObject +import org.mtransit.android.commons.JSONUtils +import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.MTLog.Loggable +import org.mtransit.android.commons.data.DefaultPOI +import org.mtransit.android.commons.data.POI +import org.mtransit.android.commons.data.Route import org.mtransit.android.commons.data.RouteDirection -import org.mtransit.android.commons.data.RouteDirectionStop import org.mtransit.android.commons.provider.common.ProviderContract import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation @@ -17,6 +23,24 @@ interface VehicleLocationProviderContract : ProviderContract { const val VEHICLE_LOCATION_PATH = "vehicle" const val PING_PATH = ProviderContract.PING_PATH + + /** + * see [VehicleLocation] + */ + val PROJECTION_VEHICLE_LOCATION = arrayOf( + Columns.T_VEHICLE_LOCATION_K_ID, + Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, + Columns.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID, + Columns.T_VEHICLE_LOCATION_K_LAST_UPDATE, + Columns.T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS, + // + Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID, + Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, + Columns.T_VEHICLE_LOCATION_K_LATITUDE, + Columns.T_VEHICLE_LOCATION_K_LONGITUDE, + Columns.T_VEHICLE_LOCATION_K_BEARING, + Columns.T_VEHICLE_LOCATION_K_SPEED, + ) } val authorityUri: Uri @@ -29,9 +53,9 @@ interface VehicleLocationProviderContract : ProviderContract { fun cacheVehicleLocations(newVehicleLocations: List) - fun getCachedVehicleLocations(vehicleLocationFilter: Filter): List + fun getCachedVehicleLocations(vehicleLocationFilter: Filter): List? - fun getNewVehicleLocations(vehicleLocationFilter: Filter): List + fun getNewVehicleLocations(vehicleLocationFilter: Filter): List? fun deleteCachedVehicleLocation(vehicleLocationId: Int): Boolean fun purgeUselessCachedVehicleLocations(): Boolean @@ -48,6 +72,7 @@ interface VehicleLocationProviderContract : ProviderContract { const val T_VEHICLE_LOCATION_K_TARGET_TRIP_ID = "target_trip_id" const val T_VEHICLE_LOCATION_K_LAST_UPDATE = "last_update" const val T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS = "max_validity" + const val T_VEHICLE_LOCATION_K_VEHICLE_ID = "vehicle_id" const val T_VEHICLE_LOCATION_K_VEHICLE_LABEL = "vehicle_label" const val T_VEHICLE_LOCATION_K_LATITUDE = "latitude" @@ -58,50 +83,105 @@ interface VehicleLocationProviderContract : ProviderContract { } - @Serializable - data class Filter( - @SerialName("r_id") - val routeIdHash: Int?, // original // GTFS // cleaned + hash - @SerialName("r_sn") - val routeShortName: String, // used for NextBus - @SerialName("d_id") - val directionId: Int?, // original // GTFS - @SerialName("d_hv") - val directionHeadsignValue: String, // used for NextBus + data class Filter @Discouraged("use from() instead") constructor( + val authority: String, + val poi: POI? = null, // RouteDirectionStop or DefaultPOI + val route: Route? = null, + val routeDirection: RouteDirection? = null, @SerialName("t_ids") val tripIds: List?, // original // GTFS // cleaned - @SerialName("s_id") - val stopIdHash: Int?, // original // GTFS // cleaned + hash ) : Loggable { + @SuppressLint("DiscouragedApi") + constructor(poi: POI, tripIds: List? = null) : this( + authority = poi.authority, + poi = poi, + tripIds = tripIds, + ) + + @SuppressLint("DiscouragedApi") + constructor(route: Route, tripIds: List? = null) : this( + authority = route.authority, + route = route, + tripIds = tripIds, + ) + + @SuppressLint("DiscouragedApi") + constructor(routeDirection: RouteDirection, tripIds: List? = null) : this( + authority = routeDirection.authority, + routeDirection = routeDirection, + tripIds = tripIds, + ) + companion object { private val LOG_TAG: String = VehicleLocationProviderContract::class.java.simpleName + ">" + Filter::class.java.simpleName - fun from(rd: RouteDirection, stopIdHash: Int? = null, tripIds: List? = null) = Filter( - routeIdHash = rd.route.originalIdHash, - routeShortName = rd.route.shortName, - directionId = rd.direction.originalDirectionIdOrNull, - directionHeadsignValue = rd.direction.headsignValue, - tripIds = tripIds, - stopIdHash = stopIdHash, - ) - - fun from(rds: RouteDirectionStop, tripIds: List? = null) = Filter( - routeIdHash = rds.route.originalIdHash, - routeShortName = rds.route.shortName, - directionId = rds.direction.originalDirectionIdOrNull, - directionHeadsignValue = rds.direction.headsignValue, - tripIds = tripIds, - stopIdHash = rds.stop.originalIdHash, - ) - - fun fromJSON(jsonString: String): Filter = Json.decodeFromString(serializer(), jsonString) - + private const val JSON_AUTHORITY = "authority" + private const val JSON_POI = "poi" + private const val JSON_ROUTE = "route" + private const val JSON_ROUTE_DIRECTION = "routeDirection" + private const val JSON_TRIP_IDS = "tripIds" + + fun fromJSONString(jsonString: String?): Filter? { + try { + return if (jsonString == null) null else fromJSON(JSONObject(jsonString)) + } catch (jsone: JSONException) { + MTLog.w(LOG_TAG, jsone, "Error while parsing JSON string '%s'", jsonString) + return null + } + } + + @SuppressLint("DiscouragedApi") + fun fromJSON(json: JSONObject): Filter? { + val poi = json.optJSONObject(JSON_POI)?.let { jPoi -> + DefaultPOI.fromJSONStatic(jPoi) + } + val authority = JSONUtils.optString(json, JSON_AUTHORITY) + val route = json.optJSONObject(JSON_ROUTE)?.let { jRoute -> + authority?.let { Route.fromJSON(jRoute, it) } + } + val routeDirection = json.optJSONObject(JSON_ROUTE_DIRECTION)?.let { jRouteDirection -> + authority?.let { RouteDirection.fromJSON(jRouteDirection, it) } + } + val tripIds = json.optJSONArray(JSON_TRIP_IDS)?.let { jTripIds -> + buildList { + for (i in 0 until jTripIds.length()) { + add(jTripIds.getString(i)) + } + } + } + return poi?.let { Filter(authority = it.authority, poi = it, tripIds = tripIds) } + ?: route?.let { Filter(authority = route.authority, route = it, tripIds = tripIds) } + ?: routeDirection?.let { Filter(authority = routeDirection.authority, routeDirection = it, tripIds = tripIds) } + } + + fun toJSONString(vehicleLocationFilter: Filter) = + toJSON(vehicleLocationFilter)?.toString() + + fun toJSON(vehicleLocationFilter: Filter): JSONObject? { + return try { + JSONObject().apply { + put(JSON_AUTHORITY, vehicleLocationFilter.authority) + vehicleLocationFilter.poi?.let { put(JSON_POI, it.toJSON()) } + vehicleLocationFilter.route?.let { put(JSON_ROUTE, Route.toJSON(it)) } + vehicleLocationFilter.routeDirection?.let { put(JSON_ROUTE_DIRECTION, RouteDirection.toJSON(it)) } + vehicleLocationFilter.tripIds?.let { put(JSON_TRIP_IDS, it) } + } + } catch (jsone: JSONException) { + MTLog.w(LOG_TAG, jsone, "Error while making JSON object '%s'", vehicleLocationFilter) + null + } + } } override fun getLogTag() = LOG_TAG - fun toJSON() = Json.encodeToString(serializer(), this) + fun toJSONString() = toJSONString(this) + + val uuid: String? + get() = poi?.uuid + ?: route?.uuid + ?: routeDirection?.uuid } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 60a24130..333fde12 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -10,6 +10,9 @@ import org.mtransit.android.commons.optFloat import org.mtransit.android.commons.optString import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract +/** + * See [VehicleLocationProviderContract] + */ data class VehicleLocation( val id: Int?, val targetUUID: String, // route+direction or just route / routeTag / routeTag+dirTag From ac994e3adeaa9b83b813efa99b9f5e0ab75f205d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 4 Dec 2025 15:37:33 -0500 Subject: [PATCH 04/36] wip --- .../provider/GTFSProviderDbHelper.java | 51 +++- .../provider/GTFSRealTimeProvider.java | 50 ++-- .../commons/provider/GTFSStatusProvider.java | 15 +- .../gtfs/GTFSProviderDBHelperUtils.kt | 92 ++++--- .../provider/gtfs/GtfsRealTimeStorage.kt | 41 +++ .../commons/provider/gtfs/GtfsRealtimeExt.kt | 30 +++ .../ServiceUpdateProviderContract.java | 8 +- .../GTFSRealTimeVehiclePositionsProvider.kt | 244 +++++++++++++++++- .../VehicleLocationProvider.kt | 35 ++- .../VehicleLocationProviderContract.kt | 87 ++++++- .../vehiclelocations/model/VehicleLocation.kt | 12 +- ...hedule_trip_ids => current_gtfs_rts_paths} | 0 .../raw/current_gtfs_schedule_path_ids} | 0 .../raw/next_gtfs_rts_paths} | 0 .../res-next/raw/next_gtfs_schedule_path_ids | 0 src/main/res/raw/gtfs_rts_paths | 0 src/main/res/raw/gtfs_schedule_path_ids | 0 17 files changed, 557 insertions(+), 108 deletions(-) rename src/main/res-current/raw/{current_gtfs_schedule_trip_ids => current_gtfs_rts_paths} (100%) rename src/main/{res-next/raw/next_gtfs_schedule_trip_ids => res-current/raw/current_gtfs_schedule_path_ids} (100%) rename src/main/{res/raw/gtfs_schedule_trip_ids => res-next/raw/next_gtfs_rts_paths} (100%) create mode 100644 src/main/res-next/raw/next_gtfs_schedule_path_ids create mode 100644 src/main/res/raw/gtfs_rts_paths create mode 100644 src/main/res/raw/gtfs_schedule_path_ids diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java index 2cd9c4b5..ce5b5c49 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java @@ -95,6 +95,17 @@ public String getLogTag() { private static final String T_DIRECTION_STOPS_SQL_INSERT = GTFSCommons.getT_DIRECTION_STOPS_SQL_INSERT(); private static final String T_DIRECTION_STOPS_SQL_DROP = GTFSCommons.getT_DIRECTION_STOPS_SQL_DROP(); + static final String T_TRIP = GTFSCommons.T_TRIP; + static final String T_TRIP_K_ID = GTFSCommons.T_TRIP_K_ID; + static final String T_TRIP_K_ROUTE_ID = GTFSCommons.T_TRIP_K_ROUTE_ID; + static final String T_TRIP_K_DIRECTION_ID = GTFSCommons.T_TRIP_K_DIRECTION_ID; + static final String T_TRIP_K_SERVICE_ID = GTFSCommons.T_TRIP_K_SERVICE_ID; + private static final int T_TRIP_SAME_COLUMNS_COUNT = GTFSCommons.T_TRIP_SAME_COLUMNS_COUNT; + private static final int T_TRIP_OTHER_COLUMNS_COUNT = GTFSCommons.T_TRIP_OTHER_COLUMNS_COUNT; + private static final String T_TRIP_SQL_CREATE = GTFSCommons.getT_TRIP_SQL_CREATE(); + private static final String T_TRIP_SQL_INSERT = GTFSCommons.getT_TRIP_SQL_INSERT(); + private static final String T_TRIP_SQL_DROP = GTFSCommons.getT_TRIP_SQL_DROP(); + public static final String T_TRIP_IDS = GTFSCommons.T_TRIP_IDS; public static final String T_TRIP_IDS_K_ID = GTFSCommons.T_TRIP_IDS_K_ID; public static final String T_TRIP_IDS_K_ID_INT = GTFSCommons.T_TRIP_IDS_K_ID_INT; @@ -117,6 +128,8 @@ public String getLogTag() { static final String T_SERVICE_DATES_K_SERVICE_ID_INT = GTFSCommons.T_SERVICE_DATES_K_SERVICE_ID_INT; static final String T_SERVICE_DATES_K_DATE = GTFSCommons.T_SERVICE_DATES_K_DATE; static final String T_SERVICE_DATES_K_EXCEPTION_TYPE = GTFSCommons.T_SERVICE_DATES_K_EXCEPTION_TYPE; + private static final int T_SERVICE_DATES_SAME_COLUMNS_COUNT = GTFSCommons.getT_SERVICE_DATES_OTHER_COLUMNS_COUNT(); + private static final int T_SERVICE_DATES_OTHER_COLUMNS_COUNT = GTFSCommons.getT_SERVICE_DATES_SAME_COLUMNS_COUNT(); private static final String T_SERVICE_DATES_SQL_CREATE = GTFSCommons.getT_SERVICE_DATES_SQL_CREATE(); private static final String T_SERVICE_DATES_SQL_INSERT = GTFSCommons.getT_SERVICE_DATES_SQL_INSERT(); private static final String T_SERVICE_DATES_SQL_DROP = GTFSCommons.getT_SERVICE_DATES_SQL_DROP(); @@ -177,6 +190,7 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { final int nId = TimeUtils.currentTimeSec(); final long startInMs = TimeUtils.currentTimeMillis(); int nbTotalOperations = 7; + if (FeatureFlags.F_EXPORT_TRIP_ID) nbTotalOperations++; if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) nbTotalOperations++; if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) nbTotalOperations++; int progress = -1; @@ -198,7 +212,7 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); final Map allStrings = new HashMap<>(); if (FeatureFlags.F_EXPORT_STRINGS || FeatureFlags.F_EXPORT_SCHEDULE_STRINGS) { - initDbTableWithRetry(context, db, T_STRINGS, T_STRINGS_SQL_CREATE, T_STRINGS_SQL_INSERT, T_STRINGS_SQL_DROP, getStringsFiles(), null, null, + initDbTableWithRetry(context, db, T_STRINGS, T_STRINGS_SQL_CREATE, T_STRINGS_SQL_INSERT, T_STRINGS_SQL_DROP, getStringsFiles(), 0, 0, null, null, (id, string) -> { allStrings.put(id, string); return Unit.INSTANCE; @@ -206,19 +220,23 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { ); // 1st } 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(), allStrings, T_ROUTE_STRINGS_COLUMN_IDX); + 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); 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(), allStrings, T_DIRECTION_STRINGS_COLUMN_IDX); + 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); 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(), allStrings, T_STOP_STRINGS_COLUMN_IDX); + 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); 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()); - if (notifEnabled) NotificationUtils.setProgressAndNotify(nm, nb, nId, nbTotalOperations, ++progress); + 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); + } 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()); } 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()); + 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); 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()); @@ -240,12 +258,27 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { private int[] getTripIdsFiles() { if (GTFSCurrentNextProvider.hasCurrentData(context)) { if (GTFSCurrentNextProvider.isNextData(context)) { - return new int[]{R.raw.next_gtfs_schedule_trip_ids}; + return new int[]{R.raw.next_gtfs_schedule_path_ids}; // do not change to avoid breaking compat w/ old modules + } else { // CURRENT = default + return new int[]{R.raw.current_gtfs_schedule_path_ids}; // do not change to avoid breaking compat w/ old modules + } + } else { + return new int[]{R.raw.gtfs_schedule_path_ids}; // do not change to avoid breaking compat w/ old modules + } + } + + /** + * Override if multiple {@link GTFSProviderDbHelper} in same app. + */ + private int[] getTripFiles() { + if (GTFSCurrentNextProvider.hasCurrentData(context)) { + if (GTFSCurrentNextProvider.isNextData(context)) { + return new int[]{R.raw.next_gtfs_rts_paths}; // do not change to avoid breaking compat w/ old modules } else { // CURRENT = default - return new int[]{R.raw.current_gtfs_schedule_trip_ids}; + return new int[]{R.raw.current_gtfs_rts_paths}; // do not change to avoid breaking compat w/ old modules } } else { - return new int[]{R.raw.gtfs_schedule_trip_ids}; + return new int[]{R.raw.gtfs_rts_paths}; // do not change to avoid breaking compat w/ old modules } } diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 209d92ae..16201c22 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -101,7 +101,7 @@ public String getLogTag() { return LOG_TAG; } - private static final String MT_HASH_SECRET_AND_DATE = "MtHashSecretAndDate"; + public static final String MT_HASH_SECRET_AND_DATE = "MtHashSecretAndDate"; @NonNull private static UriMatcher getNewUriMatcher(String authority) { @@ -215,7 +215,7 @@ private static java.util.List getAGENCY_EXTRA_BOLD_WORDS(@NonNull Contex * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. */ @NonNull - private static String getAGENCY_URL_TOKEN(@NonNull Context context) { + public static String getAGENCY_URL_TOKEN(@NonNull Context context) { if (agencyUrlToken == null) { agencyUrlToken = context.getResources().getString(R.string.gtfs_real_time_agency_url_token); } @@ -223,7 +223,7 @@ private static String getAGENCY_URL_TOKEN(@NonNull Context context) { } @Nullable - private String providedAgencyUrlToken = null; + public String providedAgencyUrlToken = null; @Nullable private static String agencyUrlSecret = null; @@ -248,7 +248,7 @@ private static String getAGENCY_URL_SECRET(@NonNull Context context) { /** * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. */ - private static boolean isUSE_URL_HASH_SECRET_AND_DATE(@NonNull Context context) { + public static boolean isUSE_URL_HASH_SECRET_AND_DATE(@NonNull Context context) { if (useURLHashSecretAndDate == null) { useURLHashSecretAndDate = context.getResources().getBoolean(R.bool.gtfs_real_time_url_use_hash_secret_and_date); } @@ -286,6 +286,20 @@ private static String getAGENCY_SERVICE_ALERTS_URL_CACHED(@NonNull Context conte return agencyServiceAlertsUrlCached; } + @Nullable + private static String agencyVehiclesUrl = null; + + @NonNull + public static String getAgencyVehiclePositionsUrlString(@NonNull Context context, @NonNull String token) { + if (agencyVehiclesUrl == null) { + agencyVehiclesUrl = getAGENCY_VEHICLE_POSITIONS_URL(context, + token, // 1st (some agency config have only 1 "%s") + MT_HASH_SECRET_AND_DATE + ); + } + return agencyVehiclesUrl; + } + @Nullable private static String agencyVehiclePositionsUrl = null; @@ -294,7 +308,7 @@ private static String getAGENCY_SERVICE_ALERTS_URL_CACHED(@NonNull Context conte */ @NonNull @SuppressLint("StringFormatInvalid") // empty string: set in module app - private static String getAGENCY_VEHICLE_POSITIONS_URL( + public static String getAGENCY_VEHICLE_POSITIONS_URL( @NonNull Context context, @NonNull String token, @SuppressWarnings("SameParameterValue") @NonNull String hash @@ -312,7 +326,7 @@ private static String getAGENCY_VEHICLE_POSITIONS_URL( * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. */ @NonNull - private static String getAGENCY_VEHICLE_POSITIONS_URL_CACHED(@NonNull Context context) { + public static String getAGENCY_VEHICLE_POSITIONS_URL_CACHED(@NonNull Context context) { if (agencyVehiclePositionsUrlCached == null) { agencyVehiclePositionsUrlCached = context.getResources().getString(R.string.gtfs_real_time_agency_vehicle_positions_url_cached); } @@ -469,8 +483,10 @@ public void cacheVehicleLocations(@NonNull List newVehicleLocat } @Override - public @NonNull List getNewVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { - return Collections.emptyList(); // TODO + public @Nullable List getNewVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { + this.providedAgencyUrlToken = SecureStringUtils.dec(vehicleLocationFilter.getProvidedEncryptKey(KeysIds.GTFS_REAL_TIME_URL_TOKEN)); + this.providedAgencyUrlSecret = SecureStringUtils.dec(vehicleLocationFilter.getProvidedEncryptKey(KeysIds.GTFS_REAL_TIME_URL_SECRET)); + return GTFSRealTimeVehiclePositionsProvider.getNew(this, vehicleLocationFilter); } @Override @@ -478,6 +494,10 @@ public boolean deleteCachedVehicleLocation(int vehicleLocationId) { return VehicleLocationProvider.deleteCachedVehicleLocation(this, vehicleLocationId); } + public boolean deleteAllCachedVehicleLocations() { + return VehicleLocationProvider.deleteAllCachedVehicleLocations(this); + } + @Override public boolean purgeUselessCachedVehicleLocations() { return VehicleLocationProvider.purgeUselessCachedVehicleLocations(this); @@ -632,7 +652,7 @@ public String getAgencyTag(@NonNull Context context) { } @NonNull - public String getRouteTag(@NonNull RouteDirectionStop rds) { + private String getRouteTag(@NonNull RouteDirectionStop rds) { return getRouteTag(rds.getRoute()); } @@ -642,7 +662,7 @@ private String getRouteTag(@NonNull RouteDirection rd) { } @NonNull - private String getRouteTag(@NonNull Route route) { + public String getRouteTag(@NonNull Route route) { return String.valueOf(route.getOriginalIdHash()); } @@ -672,7 +692,7 @@ private Integer getDirectionTag(@NonNull RouteDirection rd) { } @Nullable - private Integer getDirectionTag(@NonNull Direction direction) { + public Integer getDirectionTag(@NonNull Direction direction) { return direction.getOriginalDirectionIdOrNull(); } @@ -708,7 +728,7 @@ protected static String getAgencyRouteTypeTagTargetUUID(@NonNull String agencyTa } @Nullable - protected static String getAgencyRouteDirectionTagTargetUUID(@NonNull String agencyTag, @NonNull String routeTag, @Nullable Integer directionTag) { + public static String getAgencyRouteDirectionTagTargetUUID(@NonNull String agencyTag, @NonNull String routeTag, @Nullable Integer directionTag) { if (directionTag == null) return null; return POI.POIUtils.getUUID(agencyTag, "ri" + routeTag, "d" + directionTag); } @@ -864,7 +884,7 @@ private static String getAgencyServiceAlertsUrlString(@NonNull Context context, private OkHttpClient okHttpClient = null; @NonNull - private OkHttpClient getOkHttpClient(@NonNull Context context) { + public OkHttpClient getOkHttpClient(@NonNull Context context) { if (this.okHttpClient == null) { this.okHttpClient = NetworkUtils.makeNewOkHttpClientWithInterceptor(context); } @@ -956,7 +976,7 @@ private ArrayList loadAgencyServiceUpdateDataFromWWW(@NonNull Con } @Nullable - private String getHashSecretAndDate(@NonNull Context context) { + public String getHashSecretAndDate(@NonNull Context context) { try { final String secret = this.providedAgencyUrlSecret != null ? this.providedAgencyUrlSecret : getAGENCY_URL_SECRET(context); final String date = HASH_DATE_FORMATTER.formatThreadSafe(new Date()); @@ -1290,7 +1310,7 @@ private Pattern getServiceIdCleanupPattern(@NonNull Context context) { private boolean routeIdCleanupPatternSet = false; @Nullable - private Pattern getRouteIdCleanupPattern(@NonNull Context context) { + public Pattern getRouteIdCleanupPattern(@NonNull Context context) { if (this.routeIdCleanupPattern == null && !routeIdCleanupPatternSet) { this.routeIdCleanupPattern = GTFSCommons.makeIdCleanupPattern(getROUTE_ID_CLEANUP_REGEX(context)); this.routeIdCleanupPatternSet = true; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java index 0d760bd7..f7a32529 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java @@ -365,8 +365,13 @@ private static String getSTOP_SCHEDULE_RAW_FILE_FORMAT(@NonNull Context context) static { int idx = -1; - GTFS_SCHEDULE_STOP_FILE_COL_SERVICE_IDX = ++idx; // 0 - GTFS_SCHEDULE_STOP_FILE_COL_DIRECTION_IDX = ++idx; // 1 + if (FeatureFlags.F_EXPORT_SCHEDULE_SORTED_BY_ROUTE_DIRECTION) { + GTFS_SCHEDULE_STOP_FILE_COL_DIRECTION_IDX = ++idx; // 0 + GTFS_SCHEDULE_STOP_FILE_COL_SERVICE_IDX = ++idx; // 1 + } else { + GTFS_SCHEDULE_STOP_FILE_COL_SERVICE_IDX = ++idx; // 0 + GTFS_SCHEDULE_STOP_FILE_COL_DIRECTION_IDX = ++idx; // 1 + } // GTFS_SCHEDULE_STOP_FILE_COL_DEPARTURE_IDX = ++idx; // 2 if (FeatureFlags.F_EXPORT_TRIP_ID) { @@ -391,8 +396,8 @@ private static String getSTOP_SCHEDULE_RAW_FILE_FORMAT(@NonNull Context context) @NonNull static Set findScheduleList( @NonNull GTFSProvider provider, - @SuppressWarnings("unused") long routeId, - long directionId, + @SuppressWarnings("unused") long routeId, // included inside direction Id + long directionId, // includes routeId, int stopId, String dateS, String timeS, long diffWithRealityInMs @@ -445,7 +450,7 @@ static Set findScheduleList( continue; } lineDirectionId = Long.parseLong(lineItems[GTFS_SCHEDULE_STOP_FILE_COL_DIRECTION_IDX]); - if (directionId != lineDirectionId) { + if (directionId != lineDirectionId) { // includes route ID continue; } lineDeparture = 0; // 1st departure contains full time "HHMMSS" diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt index 23cd237e..85cb352f 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt @@ -2,16 +2,18 @@ package org.mtransit.android.commons.provider.gtfs import android.content.Context import android.database.sqlite.SQLiteDatabase +import androidx.core.database.sqlite.transaction import org.mtransit.android.commons.FileUtils import org.mtransit.android.commons.MTLog -import org.mtransit.android.commons.SqlUtils import org.mtransit.android.commons.provider.GTFSProviderDbHelper import org.mtransit.commons.FeatureFlags import org.mtransit.commons.GTFSCommons +import org.mtransit.commons.sql.SQLUtils import java.io.BufferedReader +import java.io.InputStream import java.io.InputStreamReader -object GTFSProviderDBHelperUtils: MTLog.Loggable { +object GTFSProviderDBHelperUtils : MTLog.Loggable { private val LOG_TAG: String = GTFSProviderDBHelperUtils::class.java.simpleName @@ -29,15 +31,18 @@ object GTFSProviderDBHelperUtils: MTLog.Loggable { sqlInsert: String, sqlDrop: String?, files: IntArray, + sameColumnsCount: Int = 0, + otherColumnsCount: Int = 0, allStrings: Map? = null, stringsColumnIdx: IntArray? = null, addStrings: (Int, String) -> Unit = { _, _ -> }, + openRawResource: (Int) -> InputStream = { context.resources.openRawResource(it) } ) { var tried = 0 var success: Boolean do { try { - success = initDbTable(context, db, table, sqlCreate, sqlInsert, sqlDrop, files, allStrings, stringsColumnIdx, addStrings) + success = db.initDbTable(table, sqlCreate, sqlInsert, sqlDrop, files, allStrings, stringsColumnIdx, addStrings, sameColumnsCount, otherColumnsCount, openRawResource) } catch (e: Exception) { MTLog.w(this, e, "Error while deploying DB table '$table'!") success = false @@ -46,9 +51,7 @@ object GTFSProviderDBHelperUtils: MTLog.Loggable { } while (!success && tried < MAX_DB_INIT_RETRIES) } - private fun initDbTable( - context: Context, - db: SQLiteDatabase, + private fun SQLiteDatabase.initDbTable( table: String, sqlCreate: String?, sqlInsert: String, @@ -57,48 +60,69 @@ object GTFSProviderDBHelperUtils: MTLog.Loggable { allStrings: Map?, stringsColumnIdx: IntArray?, addStrings: (Int, String) -> Unit, + sameColumnsCount: Int, + otherColumnsCount: Int, + openRawResource: (Int) -> InputStream, ): Boolean { try { - db.beginTransaction() - db.execSQL(sqlDrop) // drop if exists - db.execSQL(sqlCreate) // create if not exists - for (file in files) { - try { - context.resources.openRawResource(file).use { inputStream -> - InputStreamReader(inputStream, FileUtils.getUTF8()).use { inputStreamReader -> - BufferedReader(inputStreamReader).forEachLine { - var line = it - if (FeatureFlags.F_EXPORT_STRINGS) { - if (allStrings != null && stringsColumnIdx != null && stringsColumnIdx.isNotEmpty()) { - line = GTFSStringsUtils.replaceLineStrings(line, allStrings, stringsColumnIdx) - } else if (table == GTFSCommons.T_STRINGS) { - GTFSStringsUtils.fromFileLine(line)?.let { (id, string) -> - addStrings(id, string) + transaction { + execSQL(sqlDrop) // drop if exists + execSQL(sqlCreate) // create if not exists + for (file in files) { + try { + openRawResource(file).use { inputStream -> + InputStreamReader(inputStream, FileUtils.getUTF8()).use { inputStreamReader -> + BufferedReader(inputStreamReader).forEachLine { flattenLines -> + flattenLines.toLines(sameColumnsCount, otherColumnsCount).forEach { unflattenLine -> + var line = unflattenLine + if (FeatureFlags.F_EXPORT_STRINGS) { + if (allStrings != null && stringsColumnIdx != null && stringsColumnIdx.isNotEmpty()) { + line = GTFSStringsUtils.replaceLineStrings(line, allStrings, stringsColumnIdx) + } else if (table == GTFSCommons.T_STRINGS) { + GTFSStringsUtils.fromFileLine(line)?.let { (id, string) -> + addStrings(id, string) + } + } + } + val sql = String.format(sqlInsert, line) + try { + execSQL(sql) + } catch (e: Exception) { + MTLog.w( + this, + e, + "ERROR while executing '$sql' on database '${GTFSProviderDbHelper.DB_NAME}' table '$table' file '$file'!" + ) + throw e } } } - val sql = String.format(sqlInsert, line) - try { - db.execSQL(sql) - } catch (e: Exception) { - MTLog.w(this, e, "ERROR while executing '$sql' on database '${GTFSProviderDbHelper.DB_NAME}' table '$table' file '$file'!") - throw e - } } } + } catch (e: Exception) { + MTLog.w(this, e, "ERROR while copying the database '${GTFSProviderDbHelper.DB_NAME}' table '$table' file '$file'!") + return false } - } catch (e: Exception) { - MTLog.w(this, e, "ERROR while copying the database '${GTFSProviderDbHelper.DB_NAME}' table '$table' file '$file'!") - return false } } - db.setTransactionSuccessful() return true } catch (e: Exception) { MTLog.w(this, e, "ERROR while copying the database '${GTFSProviderDbHelper.DB_NAME}' table '$table' file!") return false - } finally { - SqlUtils.endTransactionQuietly(db) } } + + private fun String.toLines(sameColumnsCount: Int, otherColumnsCount: Int): List { + if (sameColumnsCount == 0 || otherColumnsCount == 0) return listOf(this) + return split(SQLUtils.COLUMN_SEPARATOR) + .let { + it.take(sameColumnsCount) to it.drop(sameColumnsCount).chunked(otherColumnsCount) + }.let { (sameColumns, otherColumns) -> + buildList { + otherColumns.forEach { otherColumn -> + add((sameColumns + otherColumn).joinToString(SQLUtils.COLUMN_SEPARATOR)) + } + } + } + } } diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt index 187ea99e..9eddab22 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt @@ -6,6 +6,44 @@ import org.mtransit.android.commons.PreferenceUtils object GtfsRealTimeStorage { + // region Vehicle location + + /** + * Override if multiple {@link GTFSRealTimeDbHelper} implementations in same app. + */ + private const val PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_MS = "pGTFSRealTimeVehicleLocationsLastUpdate" + + @JvmStatic + @WorkerThread + fun getVehicleLocationLastUpdateMs(context: Context, default: Long) = + PreferenceUtils.getPrefLcl(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_MS, default) + + @JvmStatic + @WorkerThread + fun saveVehicleLocationLastUpdateMs(context: Context, lastUpdateInMs: Long) { + PreferenceUtils.savePrefLclSync(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_MS, lastUpdateInMs) + } + + /** + * Override if multiple {@link GTFSRealTimeDbHelper} implementations in same app. + */ + private const val PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_CODE = "pGTFSRealTimeVehicleLocationLastUpdateCode" + + @JvmStatic + @WorkerThread + fun getVehicleLocationLastUpdateCode(context: Context, default: Int) = + PreferenceUtils.getPrefLcl(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_CODE, default) + + @JvmStatic + @WorkerThread + fun saveVehicleLocationLastUpdateCode(context: Context, code: Int) { + PreferenceUtils.savePrefLclSync(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_CODE, code) + } + + // endregion + + // region Service alerts + /** * Override if multiple {@link GTFSRealTimeDbHelper} implementations in same app. */ @@ -53,4 +91,7 @@ object GtfsRealTimeStorage { fun saveServiceUpdateLanguages(context: Context, languages: Set?) { PreferenceUtils.savePrefLclSync(context, PREF_KEY_SERVICE_UPDATE_LANGUAGES, languages) } + + // endregion + } 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 a20784ae..1a9bbf43 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 @@ -114,6 +114,36 @@ object GtfsRealtimeExt { fun GtfsRealtime.TimeRange.endMs(): Long? = this.end.takeIf { this.hasEnd() }?.secToMs() + @JvmStatic + @JvmOverloads + fun GtfsRealtime.VehiclePosition.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { + append("VehiclePosition:") + append("{") + append(trip.toStringExt(short = true)) + append(", ") + append(position.toStringExt(short = true)) + append(", ") + append("currentStopSequence=").append(currentStopSequence) + append(", ") + append("currentStatus=").append(currentStatus) + append(", ") + append("stopId=").append(stopId) + append(", ") + append("timestamp=").append(timestamp) + append("}") + } + + @JvmStatic + @JvmOverloads + fun GtfsRealtime.Position.toStringExt(short: Boolean = false) = buildString { + append(if (short) "P:" else "Position:") + append("{") + append("lat=").append(latitude) + append(", ") + append("lon=").append(longitude) + append("}") + } + @JvmStatic @JvmOverloads fun GtfsRealtime.Alert.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { diff --git a/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java index 9a3aaa70..cb89c3a9 100644 --- a/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProviderContract.java @@ -299,12 +299,8 @@ public static Filter fromJSON(@NonNull JSONObject json) { } else { return null; // WTF? } - if (json.has(JSON_CACHE_ONLY)) { - serviceUpdateFilter.cacheOnly = json.getBoolean(JSON_CACHE_ONLY); - } - if (json.has(JSON_IN_FOCUS)) { - serviceUpdateFilter.inFocus = json.getBoolean(JSON_IN_FOCUS); - } + serviceUpdateFilter.cacheOnly = JSONUtils.optBoolean(json, JSON_CACHE_ONLY); + serviceUpdateFilter.inFocus = JSONUtils.optBoolean(json, JSON_IN_FOCUS); if (json.has(JSON_CACHE_VALIDITY_IN_MS)) { serviceUpdateFilter.cacheValidityInMs = json.getLong(JSON_CACHE_VALIDITY_IN_MS); } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 512cbac4..c36dcc0a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -1,11 +1,32 @@ package org.mtransit.android.commons.provider.vehiclelocations import android.content.Context +import com.google.transit.realtime.GtfsRealtime +import com.google.transit.realtime.GtfsRealtime.FeedMessage +import okhttp3.Request +import org.mtransit.android.commons.Constants +import org.mtransit.android.commons.MTLog +import org.mtransit.android.commons.TimeUtils +import org.mtransit.android.commons.data.Direction import org.mtransit.android.commons.data.Route import org.mtransit.android.commons.data.RouteDirection import org.mtransit.android.commons.data.RouteDirectionStop import org.mtransit.android.commons.provider.GTFSRealTimeProvider +import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteDirectionTagTargetUUID +import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteTagTargetUUID +import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage +import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToHash +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehiclesPair +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toStringExt +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toVehiclesWithIdPair +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProvider.Companion.getCachedVehicleLocationsS import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation +import java.net.HttpURLConnection +import java.net.SocketException +import java.net.URL +import java.net.UnknownHostException +import kotlin.math.min import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes @@ -31,21 +52,222 @@ object GTFSRealTimeVehiclePositionsProvider { val maxValidityInMs: Long get() = VEHICLE_LOCATION_MAX_VALIDITY_IN_MS @JvmStatic - fun getCached(context: Context, provider: GTFSRealTimeProvider, vehicleLocationFilter: VehicleLocationProviderContract.Filter): List? { - return (vehicleLocationFilter.poi as? RouteDirectionStop)?.let { getCached(context, provider, it) } - ?: vehicleLocationFilter.routeDirection?.let { getCached(context, provider, it) } - ?: vehicleLocationFilter.route?.let { getCached(context, provider, it) } + fun GTFSRealTimeProvider.getCached(vehicleLocationFilter: VehicleLocationProviderContract.Filter) = + ((vehicleLocationFilter.poi as? RouteDirectionStop)?.getTargetUUIDs(this) + ?: vehicleLocationFilter.routeDirection?.getTargetUUIDs(this) + ?: vehicleLocationFilter.route?.getTargetUUIDs(this)) + ?.let { targetUUIDs -> + getCached(targetUUIDs) + } + + private fun RouteDirectionStop.getTargetUUIDs(provider: GTFSRealTimeProvider) = buildMap { + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.getUUID()) + getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { put(it, routeDirectionUUID) } + } + + private fun RouteDirection.getTargetUUIDs(provider: GTFSRealTimeProvider) = buildMap { + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.getUUID()) + getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { put(it, uuid) } + } + + private fun Route.getTargetUUIDs(provider: GTFSRealTimeProvider) = mapOf( + getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)) to uuid, + ) + + fun GTFSRealTimeProvider.getCached(targetUUIDs: Map) = buildList { + getCachedVehicleLocationsS(targetUUIDs.keys)?.let { + addAll(it) + } + }.map { it.copy(targetUUID = targetUUIDs[it.targetUUID] ?: it.targetUUID) } + + @JvmStatic + fun GTFSRealTimeProvider.getNew(vehicleLocationFilter: VehicleLocationProviderContract.Filter): List? { + updateAgencyDataIfRequired(vehicleLocationFilter.inFocusOrDefault) + return getCached(vehicleLocationFilter) + } + + private fun GTFSRealTimeProvider.updateAgencyDataIfRequired(inFocus: Boolean) { + val context = requireContextCompat() + var inFocus = inFocus + val lastUpdateInMs = GtfsRealTimeStorage.getVehicleLocationLastUpdateMs(context, 0L) + val lastUpdateCode = GtfsRealTimeStorage.getVehicleLocationLastUpdateCode(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(vehicleLocationMaxValidityInMs, getVehicleLocationValidityInMs(inFocus)) + val nowInMs = TimeUtils.currentTimeMillis() + if (lastUpdateInMs + minUpdateMs > nowInMs) { + return + } + updateAgencyDataIfRequiredSync(lastUpdateInMs, inFocus) } - private fun getCached(context: Context, provider: GTFSRealTimeProvider, rds: RouteDirectionStop): List? { - TODO() + @Synchronized + private fun GTFSRealTimeProvider.updateAgencyDataIfRequiredSync(lastUpdateInMs: Long, inFocus: Boolean) { + val context = requireContextCompat() + if (GtfsRealTimeStorage.getVehicleLocationLastUpdateMs(context, 0L) > lastUpdateInMs) { + return // too late, another thread already updated + } + val nowInMs = TimeUtils.currentTimeMillis() + var deleteAllRequired = false + if (lastUpdateInMs + vehicleLocationMaxValidityInMs < nowInMs) { + deleteAllRequired = true // too old to display + } + val minUpdateMs = min(vehicleLocationMaxValidityInMs, getVehicleLocationValidityInMs(inFocus)) + if (deleteAllRequired || lastUpdateInMs + minUpdateMs < nowInMs) { + updateAllAgencyDataFromWWW(context, deleteAllRequired) // try to update + } } - private fun getCached(context: Context, provider: GTFSRealTimeProvider, rd: RouteDirection): List? { - TODO() + private fun GTFSRealTimeProvider.updateAllAgencyDataFromWWW(context: Context, deleteAllRequired: Boolean) { + var deleteAllDone = false + if (deleteAllRequired) { + deleteAllCachedVehicleLocations() + deleteAllDone = true + } + val newVehicleLocations: List? = loadAgencyDataFromWWW(context) + if (newVehicleLocations != null) { // empty is OK + if (!deleteAllDone) { + deleteAllCachedVehicleLocations() + } + cacheVehicleLocations(newVehicleLocations) + } // else keep whatever we have until max validity reached } - private fun getCached(context: Context, provider: GTFSRealTimeProvider, r: Route): List? { - TODO() + private fun GTFSRealTimeProvider.loadAgencyDataFromWWW(context: Context): List? { + try { + val url: URL + val urlCachedString = GTFSRealTimeProvider.getAGENCY_VEHICLE_POSITIONS_URL_CACHED(context) + if (urlCachedString.isBlank()) { + val token = GTFSRealTimeProvider.getAGENCY_URL_TOKEN(context) // use local token 1st for new/updated API URL & tokens + .takeIf { it.isNotBlank() } ?: this.providedAgencyUrlToken + ?: "" // compat w/ API w/o token + var urlString = GTFSRealTimeProvider.getAgencyVehiclePositionsUrlString(context, token) + if (GTFSRealTimeProvider.isUSE_URL_HASH_SECRET_AND_DATE(context)) { + getHashSecretAndDate(context)?.let { hash -> + urlString = urlString.replace(GTFSRealTimeProvider.MT_HASH_SECRET_AND_DATE.toRegex(), hash.trim()) + } + } + url = URL(urlString) + MTLog.i(this, "Loading from '%s'...", url.host) + MTLog.d(this, "Using token '%s' (length: %d)", if (!token.isEmpty()) "***" else "(none)", token.length) + } else { + url = URL(urlCachedString) + MTLog.i(this, "Loading from cached API (length: %d) '***'...", urlCachedString.length) + } + val urlRequest = Request.Builder().url(url).build() + getOkHttpClient(context).newCall(urlRequest).execute().use { response -> + GtfsRealTimeStorage.saveVehicleLocationLastUpdateCode(context, response.code) + saveServiceUpdateLastUpdateMs(context, TimeUtils.currentTimeMillis()) + when (response.code) { + HttpURLConnection.HTTP_OK -> { + val newLastUpdateInMs = TimeUtils.currentTimeMillis() + val vehicleLocations = mutableListOf() + try { + val gFeedMessage = FeedMessage.parseFrom(response.body.bytes()) + val vehiclePositionsWithIdPair = gFeedMessage.entityList.toVehiclesWithIdPair() + for (gVehiclePositionAndId in vehiclePositionsWithIdPair.sortVehiclesPair(newLastUpdateInMs)) { + val gVehiclePosition = gVehiclePositionAndId.first + if (Constants.DEBUG) { + MTLog.d( + this@GTFSRealTimeVehiclePositionsProvider, + "loadAgencyDataFromWWW() > GTFS vehicle: ${gVehiclePosition.toStringExt()}." + ) + } + processVehiclePositions(newLastUpdateInMs, gVehiclePosition) + ?.takeIf { it.isNotEmpty() } + ?.let { + vehicleLocations.addAll(it) + } + } + } catch (e: Exception) { + MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, e, "loadAgencyDataFromWWW() > error while parsing GTFS Real Time data!") + } + MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Found %d service updates.", vehicleLocations.size) + if (Constants.DEBUG) { + for (serviceUpdate in vehicleLocations) { + MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > service update: $serviceUpdate.") + } + } + return vehicleLocations + } + + else -> { + MTLog.w( + this@GTFSRealTimeVehiclePositionsProvider, + "ERROR: HTTP URL-Connection Response Code ${response.code} (Message: ${response.message})" + ) + return null + } + } + } + } catch (uhe: UnknownHostException) { + if (MTLog.isLoggable(android.util.Log.DEBUG)) { + MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, uhe, "No Internet Connection!") + } else { + MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, "No Internet Connection!") + } + return null + } catch (se: SocketException) { + MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, se, "No Internet Connection!") + return null + } catch (e: Exception) { // Unknown error + MTLog.e(this@GTFSRealTimeVehiclePositionsProvider, e, "INTERNAL ERROR: Unknown Exception") + return null + } + } + + private fun GTFSRealTimeProvider.processVehiclePositions( + newLastUpdateInMs: Long, + gVehiclePosition: GtfsRealtime.VehiclePosition + ): Set? { + val targetUUIDs = parseProviderTargetUUID(gVehiclePosition)?.takeIf { it.isNotBlank() } ?: return null + return setOf(VehicleLocation( + targetUUID = targetUUIDs, + targetTripId = gVehiclePosition.trip.tripId.originalIdToHash(tripIdCleanupPattern), + lastUpdateInMs = newLastUpdateInMs, + maxValidityInMs = this@processVehiclePositions.vehicleLocationMaxValidityInMs, + // + vehicleId = gVehiclePosition.vehicle.id, + vehicleLabel = gVehiclePosition.vehicle.label, + latitude = gVehiclePosition.position.latitude, + longitude = gVehiclePosition.position.longitude, + bearing = gVehiclePosition.position.bearing, + speed = gVehiclePosition.position.speed, + )) + } + + private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition): String? { + val tripDescriptor = gVehiclePosition.trip + if (tripDescriptor.hasRouteId()) { + return if (tripDescriptor.hasDirectionId()) { + getAgencyRouteDirectionTagTargetUUID( + agencyTag, + tripDescriptor.routeId.originalIdToHash(routeIdCleanupPattern), + tripDescriptor.directionId, + ) + } else { + getAgencyRouteTagTargetUUID( + agencyTag, + tripDescriptor.routeId.originalIdToHash(routeIdCleanupPattern), + ) + } + } + MTLog.w(this, "parseTargetUUID() > unexpected trip selector: ${tripDescriptor.toStringExt()} (IGNORED)") + return null } -} \ No newline at end of file +} + +private val GTFSRealTimeProvider.routeIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat()) +private val GTFSRealTimeProvider.tripIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat()) + +private val GTFSRealTimeProvider.agencyTag get() = getAgencyTag(requireContextCompat()) + +private fun Route.getRouteTag(provider: GTFSRealTimeProvider) = provider.getRouteTag(this) +private fun Direction.getDirectionTag(provider: GTFSRealTimeProvider) = provider.getDirectionTag(this) + +private fun RouteDirection.getRouteTag(provider: GTFSRealTimeProvider) = this.route.getRouteTag(provider) +private fun RouteDirection.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider) + +private fun RouteDirectionStop.getRouteTag(provider: GTFSRealTimeProvider) = this.route.getRouteTag(provider) +private fun RouteDirectionStop.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index 259b8f2b..cf11ed20 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -26,20 +26,17 @@ abstract class VehicleLocationProvider : MTContentProvider(), uriMatcher.addURI(authority, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH, ContentProviderConstants.VEHICLE_LOCATION) } - @JvmStatic - fun getCachedVehicleLocationsS(provider: VehicleLocationProviderContract, targetUUIDs: Collection): List? { + fun

P.getCachedVehicleLocationsS(targetUUIDs: Collection): List? { return getCachedVehicleLocationsS( - provider, - provider.contentUri, + this.contentUri, SqlUtils.getWhereInString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUIDs) ) } - @JvmStatic - fun getCachedVehicleLocationsS(provider: VehicleLocationProviderContract, targetUUID: String): List? { - return getCachedVehicleLocationsS( - provider, - provider.contentUri, + @Suppress("unused") + fun

P.getCachedVehicleLocationsS(targetUUID: String): List? { + return this.getCachedVehicleLocationsS( + this.contentUri, SqlUtils.getWhereEqualsString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUID) ) } @@ -62,14 +59,17 @@ abstract class VehicleLocationProvider : MTContentProvider(), .build() //@formatter:on - private fun getCachedVehicleLocationsS(provider: VehicleLocationProviderContract, uri: Uri?, selection: String?): List? = + private fun

P.getCachedVehicleLocationsS( + @Suppress("unused") uri: Uri?, + selection: String?, + ): List? = try { SQLiteQueryBuilder() .apply { - tables = provider.dbTableName + tables = dbTableName projectionMap = VEHICLE_LOCATION_PROJECTION_MAP }.query( - provider.getReadDB(), VehicleLocationProviderContract.PROJECTION_VEHICLE_LOCATION, selection, null, null, + getReadDB(), VehicleLocationProviderContract.PROJECTION_VEHICLE_LOCATION, selection, null, null, null, null, null ).use { cursor -> buildList { @@ -109,6 +109,17 @@ abstract class VehicleLocationProvider : MTContentProvider(), return affectedRows } + @JvmStatic + fun deleteAllCachedVehicleLocations(provider: VehicleLocationProviderContract): Boolean { + var deletedRows = 0 + try { + deletedRows = provider.getWriteDB().delete(provider.dbTableName, null, null) + } catch (e: Exception) { + MTLog.w(LOG_TAG, e, "Error while deleting ALL cached vehicle locations!") + } + return deletedRows > 0 + } + @JvmStatic fun deleteCachedVehicleLocation(provider: VehicleLocationProviderContract, vehicleLocationId: Int?): Boolean { vehicleLocationId ?: return false diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 17b57178..78fe7f8c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.net.Uri import android.provider.BaseColumns import androidx.annotation.Discouraged -import kotlinx.serialization.SerialName import org.json.JSONException import org.json.JSONObject import org.mtransit.android.commons.JSONUtils @@ -88,31 +87,65 @@ interface VehicleLocationProviderContract : ProviderContract { val poi: POI? = null, // RouteDirectionStop or DefaultPOI val route: Route? = null, val routeDirection: RouteDirection? = null, - @SerialName("t_ids") val tripIds: List?, // original // GTFS // cleaned + val inFocus: Boolean?, + val cacheOnly: Boolean?, + val providedEncryptKeysMap: Map?, ) : Loggable { @SuppressLint("DiscouragedApi") - constructor(poi: POI, tripIds: List? = null) : this( + constructor( + poi: POI, + tripIds: List? = null, + inFocus: Boolean? = null, + cacheOnly: Boolean? = null, + providedEncryptKeysMap: Map? = null + ) : this( authority = poi.authority, poi = poi, tripIds = tripIds, + inFocus = inFocus, + cacheOnly = cacheOnly, + providedEncryptKeysMap = providedEncryptKeysMap, ) @SuppressLint("DiscouragedApi") - constructor(route: Route, tripIds: List? = null) : this( + constructor( + route: Route, + tripIds: List? = null, + inFocus: Boolean? = null, + cacheOnly: Boolean? = null, + providedEncryptKeysMap: Map? = null, + ) : this( authority = route.authority, route = route, tripIds = tripIds, + inFocus = inFocus, + cacheOnly = cacheOnly, + providedEncryptKeysMap = providedEncryptKeysMap, ) @SuppressLint("DiscouragedApi") - constructor(routeDirection: RouteDirection, tripIds: List? = null) : this( + constructor( + routeDirection: RouteDirection, + tripIds: List? = null, + inFocus: Boolean? = null, + cacheOnly: Boolean? = null, + providedEncryptKeysMap: Map? = null, + ) : this( authority = routeDirection.authority, routeDirection = routeDirection, tripIds = tripIds, + inFocus = inFocus, + cacheOnly = cacheOnly, + providedEncryptKeysMap = providedEncryptKeysMap, ) + val inFocusOrDefault = inFocus ?: false + + fun getProvidedEncryptKey(key: String) = + this.providedEncryptKeysMap?.get(key)?.takeIf { it.isNotBlank() } + companion object { private val LOG_TAG: String = VehicleLocationProviderContract::class.java.simpleName + ">" + Filter::class.java.simpleName @@ -121,6 +154,9 @@ interface VehicleLocationProviderContract : ProviderContract { private const val JSON_ROUTE = "route" private const val JSON_ROUTE_DIRECTION = "routeDirection" private const val JSON_TRIP_IDS = "tripIds" + private const val JSON_CACHE_ONLY = "cacheOnly" + private const val JSON_IN_FOCUS = "inFocus" + private const val JSON_PROVIDED_ENCRYPT_KEYS_MAP = "providedEncryptKeysMap" fun fromJSONString(jsonString: String?): Filter? { try { @@ -150,9 +186,40 @@ interface VehicleLocationProviderContract : ProviderContract { } } } - return poi?.let { Filter(authority = it.authority, poi = it, tripIds = tripIds) } - ?: route?.let { Filter(authority = route.authority, route = it, tripIds = tripIds) } - ?: routeDirection?.let { Filter(authority = routeDirection.authority, routeDirection = it, tripIds = tripIds) } + val inFocus = JSONUtils.optBoolean(json, JSON_IN_FOCUS) + val cacheOnly = JSONUtils.optBoolean(json, JSON_CACHE_ONLY) + val providedEncryptKeysMap: Map? = json.optJSONObject(JSON_PROVIDED_ENCRYPT_KEYS_MAP)?.let { jProvidedEncryptKeysMap -> + JSONUtils.toMapOfStrings(jProvidedEncryptKeysMap) + } + return poi?.let { + Filter( + authority = it.authority, + poi = it, + tripIds = tripIds, + inFocus = inFocus, + cacheOnly = cacheOnly, + providedEncryptKeysMap = providedEncryptKeysMap + ) + } ?: route?.let { + Filter( + authority = route.authority, + route = it, + tripIds = tripIds, + inFocus = inFocus, + cacheOnly = cacheOnly, + providedEncryptKeysMap = providedEncryptKeysMap + ) + } + ?: routeDirection?.let { + Filter( + authority = routeDirection.authority, + routeDirection = it, + tripIds = tripIds, + inFocus = inFocus, + cacheOnly = cacheOnly, + providedEncryptKeysMap = providedEncryptKeysMap + ) + } } fun toJSONString(vehicleLocationFilter: Filter) = @@ -166,6 +233,7 @@ interface VehicleLocationProviderContract : ProviderContract { vehicleLocationFilter.route?.let { put(JSON_ROUTE, Route.toJSON(it)) } vehicleLocationFilter.routeDirection?.let { put(JSON_ROUTE_DIRECTION, RouteDirection.toJSON(it)) } vehicleLocationFilter.tripIds?.let { put(JSON_TRIP_IDS, it) } + vehicleLocationFilter.providedEncryptKeysMap?.let { put(JSON_PROVIDED_ENCRYPT_KEYS_MAP, JSONUtils.toJSONObject(it)) } } } catch (jsone: JSONException) { MTLog.w(LOG_TAG, jsone, "Error while making JSON object '%s'", vehicleLocationFilter) @@ -176,13 +244,12 @@ interface VehicleLocationProviderContract : ProviderContract { override fun getLogTag() = LOG_TAG + @Suppress("unused") fun toJSONString() = toJSONString(this) val uuid: String? get() = poi?.uuid ?: route?.uuid ?: routeDirection?.uuid - } - } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 333fde12..86f42581 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -2,7 +2,7 @@ package org.mtransit.android.commons.provider.vehiclelocations.model import android.content.ContentValues import android.database.Cursor -import org.mtransit.android.commons.getDouble +import org.mtransit.android.commons.getFloat import org.mtransit.android.commons.getLong import org.mtransit.android.commons.optInt import org.mtransit.android.commons.getString @@ -14,7 +14,7 @@ import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationPro * See [VehicleLocationProviderContract] */ data class VehicleLocation( - val id: Int?, + val id: Int? = null, val targetUUID: String, // route+direction or just route / routeTag / routeTag+dirTag val targetTripId: String?, // cleaned val lastUpdateInMs: Long, @@ -22,8 +22,8 @@ data class VehicleLocation( // val vehicleId: String?, // not user visible val vehicleLabel: String?, // user visible - val latitude: Double, - val longitude: Double, + val latitude: Float, + val longitude: Float, val bearing: Float?, // in degree val speed: Float?, // m/s OR km/h ) { @@ -39,8 +39,8 @@ data class VehicleLocation( // vehicleId = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID), vehicleLabel = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL), - latitude = cursor.getDouble(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE), - longitude = cursor.getDouble(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE), + latitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE), + longitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE), bearing = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING), speed = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED), ) diff --git a/src/main/res-current/raw/current_gtfs_schedule_trip_ids b/src/main/res-current/raw/current_gtfs_rts_paths similarity index 100% rename from src/main/res-current/raw/current_gtfs_schedule_trip_ids rename to src/main/res-current/raw/current_gtfs_rts_paths diff --git a/src/main/res-next/raw/next_gtfs_schedule_trip_ids b/src/main/res-current/raw/current_gtfs_schedule_path_ids similarity index 100% rename from src/main/res-next/raw/next_gtfs_schedule_trip_ids rename to src/main/res-current/raw/current_gtfs_schedule_path_ids diff --git a/src/main/res/raw/gtfs_schedule_trip_ids b/src/main/res-next/raw/next_gtfs_rts_paths similarity index 100% rename from src/main/res/raw/gtfs_schedule_trip_ids rename to src/main/res-next/raw/next_gtfs_rts_paths diff --git a/src/main/res-next/raw/next_gtfs_schedule_path_ids b/src/main/res-next/raw/next_gtfs_schedule_path_ids new file mode 100644 index 00000000..e69de29b diff --git a/src/main/res/raw/gtfs_rts_paths b/src/main/res/raw/gtfs_rts_paths new file mode 100644 index 00000000..e69de29b diff --git a/src/main/res/raw/gtfs_schedule_path_ids b/src/main/res/raw/gtfs_schedule_path_ids new file mode 100644 index 00000000..e69de29b From 4da8657075584b2cfabfb9be4f97a10c4c61cb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 5 Dec 2025 09:35:13 -0500 Subject: [PATCH 05/36] wip --- .../org/mtransit/android/commons/CursorExt.kt | 6 + .../commons/provider/GTFSProvider.java | 6 + .../provider/GTFSProviderContract.java | 12 +- .../provider/GTFSRealTimeProvider.java | 3 +- .../commons/provider/StmInfoApiProvider.java | 1 + .../{ => gtfs}/GTFSCurrentNextProvider.java | 11 +- .../provider/{ => gtfs}/GTFSPOIProvider.java | 4 +- .../gtfs/GTFSProviderDBHelperUtils.kt | 1 - .../{ => gtfs}/GTFSProviderDbHelper.java | 8 +- .../provider/{ => gtfs}/GTFSRDSProvider.java | 172 ++++++++++++++++-- .../GTFSScheduleTimestampsProvider.java | 9 +- .../{ => gtfs}/GTFSStatusProvider.java | 17 +- .../commons/provider/gtfs/GTFSStringsUtils.kt | 1 - .../commons/provider/gtfs/GTFSTripIdsUtils.kt | 1 - .../commons/provider/gtfs/GtfsRealtimeExt.kt | 32 +++- .../GTFSRealTimeVehiclePositionsProvider.kt | 40 ++-- .../provider/GTFSStatusProviderTest.kt | 1 + 17 files changed, 258 insertions(+), 67 deletions(-) rename src/main/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSCurrentNextProvider.java (94%) rename src/main/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSPOIProvider.java (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSProviderDbHelper.java (98%) rename src/main/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSRDSProvider.java (75%) rename src/main/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSScheduleTimestampsProvider.java (93%) rename src/main/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSStatusProvider.java (98%) diff --git a/src/main/java/org/mtransit/android/commons/CursorExt.kt b/src/main/java/org/mtransit/android/commons/CursorExt.kt index f1de1562..b10fb88c 100644 --- a/src/main/java/org/mtransit/android/commons/CursorExt.kt +++ b/src/main/java/org/mtransit/android/commons/CursorExt.kt @@ -52,6 +52,12 @@ fun Cursor.getInt(columnName: String) = this.getInt(getColumnIndexOrThrow(column fun Cursor.getLong(columnName: String) = this.getLong(getColumnIndexOrThrow(columnName)) +fun Cursor.optLong(columnIndex: Int, fallback: Long? = null) = + optNotNull(columnIndex)?.let { getLong(it) } ?: fallback + +fun Cursor.optLong(columnName: String, fallback: Long? = null) = + this.optLong(getColumnIndex(columnName), fallback) + // endregion // region String diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java index 7c9966db..295a5117 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProvider.java @@ -30,6 +30,12 @@ import org.mtransit.android.commons.data.ScheduleTimestamps; import org.mtransit.android.commons.provider.agency.AgencyProvider; import org.mtransit.android.commons.provider.bike.BikeStationProvider; +import org.mtransit.android.commons.provider.gtfs.GTFSCurrentNextProvider; +import org.mtransit.android.commons.provider.gtfs.GTFSPOIProvider; +import org.mtransit.android.commons.provider.gtfs.GTFSProviderDbHelper; +import org.mtransit.android.commons.provider.gtfs.GTFSRDSProvider; +import org.mtransit.android.commons.provider.gtfs.GTFSScheduleTimestampsProvider; +import org.mtransit.android.commons.provider.gtfs.GTFSStatusProvider; import org.mtransit.android.commons.provider.poi.POIProviderContract; import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProviderContract; import org.mtransit.android.commons.provider.status.StatusProviderContract; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java index 9f893af0..ad67ed61 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java @@ -9,7 +9,7 @@ import java.util.ArrayList; -@SuppressWarnings("WeakerAccess") +@SuppressWarnings("WeakerAccess") // used from main app public interface GTFSProviderContract { String POI_FILTER_EXTRA_NO_PICKUP = "descentOnly"; @@ -22,6 +22,7 @@ public interface GTFSProviderContract { String ROUTE_DIRECTION_STOP_SEARCH_PATH = "route/trip/stop/*"; // do not change to avoid breaking compat w/ old modules String ROUTE_DIRECTION_PATH = "route/trip"; // do not change to avoid breaking compat w/ old modules String DIRECTION_STOP_PATH = "trip/stop"; // do not change to avoid breaking compat w/ old modules + String TRIP_PATH = "path"; // do not change to avoid breaking compat w/ old modules @NonNull static String[] makePROJECTION_ROUTE_DIRECTION_STOP() { @@ -161,4 +162,13 @@ class DirectionStopColumns { public static final String T_DIRECTION_STOPS_K_STOP_SEQUENCE = T_DIRECTION_STOPS + "_" + "stop_sequence"; public static final String T_DIRECTION_STOPS_K_NO_PICKUP = T_DIRECTION_STOPS + "_" + "decent_only"; } + + class TripColumns { + public static final String T_TRIP_K_TRIP_ID = "trip_id"; + public static final String T_TRIP_K_TRIP_ID_INT = "trip_id_int"; + public static final String T_TRIP_K_ROUTE_ID = "route_id"; + public static final String T_TRIP_K_DIRECTION_ID = "direction_id"; + public static final String T_TRIP_K_SERVICE_ID = "service_id"; + public static final String T_TRIP_K_SERVICE_ID_INT = "service_id_int"; + } } diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 16201c22..58b9997d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -43,6 +43,7 @@ import org.mtransit.android.commons.provider.agency.AgencyUtils; import org.mtransit.android.commons.provider.common.MTContentProvider; import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.gtfs.GTFSRDSProvider; import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage; import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt; import org.mtransit.android.commons.provider.gtfs.alert.GTFSRTAlertsManager; @@ -740,7 +741,7 @@ protected static String getAgencyRouteDirectionStopTagTargetUUID(@NonNull String } @NonNull - protected static String getAgencyTagTargetUUID(@NonNull String agencyTag) { + public static String getAgencyTagTargetUUID(@NonNull String agencyTag) { return POI.POIUtils.getUUID(agencyTag); } 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 05d3f685..c3392aaf 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java @@ -52,6 +52,7 @@ import org.mtransit.android.commons.data.Stop; import org.mtransit.android.commons.provider.common.MTContentProvider; import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.gtfs.GTFSRDSProvider; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSCurrentNextProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSCurrentNextProvider.java similarity index 94% rename from src/main/java/org/mtransit/android/commons/provider/GTFSCurrentNextProvider.java rename to src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSCurrentNextProvider.java index 60136982..33719f58 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSCurrentNextProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSCurrentNextProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.gtfs; import android.content.Context; @@ -9,6 +9,7 @@ import org.mtransit.android.commons.PreferenceUtils; import org.mtransit.android.commons.R; import org.mtransit.android.commons.TimeUtils; +import org.mtransit.android.commons.provider.GTFSProvider; import org.mtransit.android.commons.receiver.DataChange; public class GTFSCurrentNextProvider implements MTLog.Loggable { @@ -106,12 +107,12 @@ private static void setCurrentNextData(@NonNull Context context, @NonNull String PreferenceUtils.savePrefLclAsync(context, PREF_KEY_CURRENT_NEXT_DATA, currentNextData); } - static boolean isNextData(@NonNull Context context) { + public static boolean isNextData(@NonNull Context context) { checkForNextData(context); return CURRENT_NEXT_DATA_NEXT.equals(getCurrentNextData(context)); } - static void checkForNextData(@NonNull Context context) { + public static void checkForNextData(@NonNull Context context) { final String oldCurrentNextData = getCurrentNextData(context); final int now = TimeUtils.currentTimeSec(); boolean isNextData = hasNextData(context) // @@ -151,7 +152,7 @@ static int getLAST_DEPARTURE_IN_SEC(@NonNull Context context) { } } - static int getLAST_LAST_DEPARTURE_IN_SEC(@NonNull Context context) { + public static int getLAST_LAST_DEPARTURE_IN_SEC(@NonNull Context context) { final int nextLastDepartureInSec = getNEXT_LAST_DEPARTURE_IN_SEC(context); if (nextLastDepartureInSec > 0) { return nextLastDepartureInSec; @@ -163,7 +164,7 @@ private static boolean hasNextData(@NonNull Context context) { return getNEXT_FIRST_DEPARTURE_IN_SEC(context) > 0 && getNEXT_LAST_DEPARTURE_IN_SEC(context) > 0; } - static boolean hasCurrentData(@NonNull Context context) { + public static boolean hasCurrentData(@NonNull Context context) { return getCURRENT_FIRST_DEPARTURE_IN_SEC(context) > 0 && getCURRENT_LAST_DEPARTURE_IN_SEC(context) > 0; } } diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSPOIProvider.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java rename to src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSPOIProvider.java index 11117d6c..c481f248 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSPOIProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSPOIProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.gtfs; import android.app.SearchManager; import android.content.Context; @@ -16,6 +16,8 @@ import org.mtransit.android.commons.R; import org.mtransit.android.commons.SqlUtils; import org.mtransit.android.commons.data.POI; +import org.mtransit.android.commons.provider.GTFSProvider; +import org.mtransit.android.commons.provider.GTFSProviderContract; import org.mtransit.android.commons.provider.common.ProviderContract; import org.mtransit.android.commons.provider.poi.POIProvider; import org.mtransit.android.commons.provider.poi.POIProviderContract; diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt index 85cb352f..b2764acf 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDBHelperUtils.kt @@ -5,7 +5,6 @@ import android.database.sqlite.SQLiteDatabase import androidx.core.database.sqlite.transaction import org.mtransit.android.commons.FileUtils import org.mtransit.android.commons.MTLog -import org.mtransit.android.commons.provider.GTFSProviderDbHelper import org.mtransit.commons.FeatureFlags import org.mtransit.commons.GTFSCommons import org.mtransit.commons.sql.SQLUtils diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java rename to src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java index ce5b5c49..9e21f6af 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderDbHelper.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSProviderDbHelper.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.gtfs; import static org.mtransit.android.commons.provider.gtfs.GTFSProviderDBHelperUtils.initDbTableWithRetry; @@ -96,10 +96,12 @@ public String getLogTag() { private static final String T_DIRECTION_STOPS_SQL_DROP = GTFSCommons.getT_DIRECTION_STOPS_SQL_DROP(); static final String T_TRIP = GTFSCommons.T_TRIP; - static final String T_TRIP_K_ID = GTFSCommons.T_TRIP_K_ID; + static final String T_TRIP_K_TRIP_ID = GTFSCommons.T_TRIP_K_TRIP_ID; + static final String T_TRIP_K_TRIP_ID_INT = GTFSCommons.T_TRIP_K_TRIP_ID_INT; static final String T_TRIP_K_ROUTE_ID = GTFSCommons.T_TRIP_K_ROUTE_ID; static final String T_TRIP_K_DIRECTION_ID = GTFSCommons.T_TRIP_K_DIRECTION_ID; static final String T_TRIP_K_SERVICE_ID = GTFSCommons.T_TRIP_K_SERVICE_ID; + static final String T_TRIP_K_SERVICE_ID_INT = GTFSCommons.T_TRIP_K_SERVICE_ID_INT; private static final int T_TRIP_SAME_COLUMNS_COUNT = GTFSCommons.T_TRIP_SAME_COLUMNS_COUNT; private static final int T_TRIP_OTHER_COLUMNS_COUNT = GTFSCommons.T_TRIP_OTHER_COLUMNS_COUNT; private static final String T_TRIP_SQL_CREATE = GTFSCommons.getT_TRIP_SQL_CREATE(); @@ -153,7 +155,7 @@ public static int getDbVersion(@NonNull Context context) { return dbVersion; } - GTFSProviderDbHelper(@NonNull Context context) { + public GTFSProviderDbHelper(@NonNull Context context) { super(context, DB_NAME, null, getDbVersion(context)); this.context = context; } diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java similarity index 75% rename from src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java rename to src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java index 9522822e..d91ff3cc 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRDSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.gtfs; import android.content.ContentResolver; import android.content.UriMatcher; @@ -10,14 +10,24 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.ArrayMap; +import androidx.core.util.Pair; import org.mtransit.android.commons.Constants; +import org.mtransit.android.commons.CursorExtKt; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.SqlUtils; +import org.mtransit.android.commons.provider.GTFSProvider; +import org.mtransit.android.commons.provider.GTFSProviderContract; import org.mtransit.android.commons.provider.common.ContentProviderConstants; +import org.mtransit.commons.FeatureFlags; import org.mtransit.commons.sql.SQLJoinBuilder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.Locale; +import java.util.Map; public class GTFSRDSProvider implements MTLog.Loggable { @@ -29,13 +39,14 @@ public String getLogTag() { return LOG_TAG; } - protected static final int ROUTES = 1; - protected static final int STOPS = 2; - protected static final int DIRECTIONS = 3; - protected static final int ROUTES_DIRECTIONS_STOPS = 4; - protected static final int ROUTES_DIRECTIONS_STOPS_SEARCH = 5; - protected static final int ROUTES_DIRECTIONS = 6; - protected static final int DIRECTIONS_STOPS = 7; + private static final int ROUTES = 1; + private static final int STOPS = 2; + private static final int DIRECTIONS = 3; + private static final int ROUTES_DIRECTIONS_STOPS = 4; + private static final int ROUTES_DIRECTIONS_STOPS_SEARCH = 5; + private static final int ROUTES_DIRECTIONS = 6; + private static final int DIRECTIONS_STOPS = 7; + private static final int TRIPS = 8; public static void append(@NonNull UriMatcher uriMatcher, @NonNull String authority) { uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_PATH, ROUTES); @@ -45,6 +56,7 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_DIRECTION_STOP_SEARCH_PATH, ROUTES_DIRECTIONS_STOPS_SEARCH); uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_DIRECTION_PATH, ROUTES_DIRECTIONS); uriMatcher.addURI(authority, GTFSProviderContract.DIRECTION_STOP_PATH, DIRECTIONS_STOPS); + uriMatcher.addURI(authority, GTFSProviderContract.TRIP_PATH, TRIPS); } private static final ArrayMap ROUTE_PROJECTION_MAP; @@ -150,12 +162,12 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author DIRECTION_STOP_PROJECTION_MAP = sb.build(); } - public static final String ROUTE_DIRECTION_DIRECTION_STOPS_STOP_JOIN = SQLJoinBuilder.getNew(GTFSProviderDbHelper.T_STOP) // + static final String ROUTE_DIRECTION_DIRECTION_STOPS_STOP_JOIN = SQLJoinBuilder.getNew(GTFSProviderDbHelper.T_STOP) // .innerJoin(GTFSProviderDbHelper.T_DIRECTION_STOPS, // - GTFSProviderDbHelper.T_STOP, GTFSProviderDbHelper.T_STOP_K_ID,// + GTFSProviderDbHelper.T_STOP, GTFSProviderDbHelper.T_STOP_K_ID, // GTFSProviderDbHelper.T_DIRECTION_STOPS, GTFSProviderDbHelper.T_DIRECTION_STOPS_K_STOP_ID) // .innerJoin(GTFSProviderDbHelper.T_DIRECTION, // - GTFSProviderDbHelper.T_DIRECTION_STOPS, GTFSProviderDbHelper.T_DIRECTION_STOPS_K_DIRECTION_ID,// + GTFSProviderDbHelper.T_DIRECTION_STOPS, GTFSProviderDbHelper.T_DIRECTION_STOPS_K_DIRECTION_ID, // GTFSProviderDbHelper.T_DIRECTION, GTFSProviderDbHelper.T_DIRECTION_K_ID) // .innerJoin(GTFSProviderDbHelper.T_ROUTE, // GTFSProviderDbHelper.T_DIRECTION, GTFSProviderDbHelper.T_DIRECTION_K_ROUTE_ID, // @@ -171,19 +183,38 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author private static final String DIRECTION_DIRECTION_STOPS_STOP_JOIN = SQLJoinBuilder.getNew(GTFSProviderDbHelper.T_STOP) // .innerJoin(GTFSProviderDbHelper.T_DIRECTION_STOPS, // - GTFSProviderDbHelper.T_STOP, GTFSProviderDbHelper.T_STOP_K_ID,// + GTFSProviderDbHelper.T_STOP, GTFSProviderDbHelper.T_STOP_K_ID, // GTFSProviderDbHelper.T_DIRECTION_STOPS, GTFSProviderDbHelper.T_DIRECTION_STOPS_K_STOP_ID) // .innerJoin(GTFSProviderDbHelper.T_DIRECTION, // - GTFSProviderDbHelper.T_DIRECTION_STOPS, GTFSProviderDbHelper.T_DIRECTION_STOPS_K_DIRECTION_ID,// + GTFSProviderDbHelper.T_DIRECTION_STOPS, GTFSProviderDbHelper.T_DIRECTION_STOPS_K_DIRECTION_ID, // GTFSProviderDbHelper.T_DIRECTION, GTFSProviderDbHelper.T_DIRECTION_K_ID) // .build(); private static final String ROUTE_DIRECTION_JOIN = SQLJoinBuilder.getNew(GTFSProviderDbHelper.T_DIRECTION) // - .innerJoin(GTFSProviderDbHelper.T_ROUTE,// + .innerJoin(GTFSProviderDbHelper.T_ROUTE, // GTFSProviderDbHelper.T_DIRECTION, GTFSProviderDbHelper.T_DIRECTION_K_ROUTE_ID, // GTFSProviderDbHelper.T_ROUTE, GTFSProviderDbHelper.T_ROUTE_K_ID)// .build(); + private static final ArrayMap TRIP_PROJECTION_MAP; + + static { + final SqlUtils.ProjectionMapBuilder sb = SqlUtils.ProjectionMapBuilder.getNew(); + if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT, GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID_INT); + } else { + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID, GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID); + } + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID, GTFSProviderContract.TripColumns.T_TRIP_K_ROUTE_ID); + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID, GTFSProviderContract.TripColumns.T_TRIP_K_DIRECTION_ID); + if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_INT, GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID_INT); + } else { + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID, GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID); + } + TRIP_PROJECTION_MAP = sb.build(); + } + @Nullable public static Cursor queryS(@NonNull GTFSProvider provider, @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { try { @@ -218,6 +249,10 @@ public static Cursor queryS(@NonNull GTFSProvider provider, @NonNull Uri uri, @N qb.setTables(DIRECTION_DIRECTION_STOPS_STOP_JOIN); qb.setProjectionMap(DIRECTION_STOP_PROJECTION_MAP); break; + case TRIPS: + qb.setTables(GTFSProviderDbHelper.T_TRIP); + qb.setProjectionMap(TRIP_PROJECTION_MAP); + break; default: return null; // not processed } @@ -256,6 +291,98 @@ private static void appendRouteDirectionStopSearch(Uri uri, SQLiteQueryBuilder q } } + private static final String[] PROJECTION_TRIPS; + + static { + ArrayList list = new ArrayList<>(); + list.add(GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID); + list.add(GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID); + if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { + list.add(GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT); + } else { + list.add(GTFSProviderDbHelper.T_TRIP_K_TRIP_ID); + } + if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { + list.add(GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_INT); + } else { + list.add(GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID); + } + + PROJECTION_TRIPS = list.toArray(new String[0]); + } + + @NonNull + public static HashSet findDirectionTripIdOrIntList(@NonNull GTFSProvider provider, long directionId) { + final HashSet routeTripIdsOrInt = new HashSet<>(); + Cursor cursor = null; + try { + final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(GTFSProviderDbHelper.T_TRIP); + final String tripIdOrIntColumnName = FeatureFlags.F_EXPORT_TRIP_ID_INTS ? GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT : GTFSProviderDbHelper.T_TRIP_K_TRIP_ID; + cursor = qb.query(provider.getReadDB(), + new String[]{tripIdOrIntColumnName}, + SqlUtils.getWhereEquals(GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID, directionId), + null, + null, + null, + null, + null); + if (cursor != null && cursor.getCount() > 0) { + if (cursor.moveToFirst()) { + do { + final String tripIdOrInt = CursorExtKt.getString(cursor, tripIdOrIntColumnName); + if (!TextUtils.isEmpty(tripIdOrInt)) { + routeTripIdsOrInt.add(tripIdOrInt); + } + } while (cursor.moveToNext()); + } + } + } catch (Exception e) { + MTLog.w(LOG_TAG, e, "Error!"); + } finally { + SqlUtils.closeQuietly(cursor); + } + return routeTripIdsOrInt; + } + + /** + * @return should only return 1 route+direction for a trip ID + */ + @NonNull + public static Map> findTripRouteAndDirectionIds(@NonNull GTFSProvider provider, @NonNull Collection tripIdOrInts) { + final Map> tripRouteDirectionIds = new HashMap<>(); + Cursor cursor = null; + try { + final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + qb.setTables(GTFSProviderDbHelper.T_TRIP); + final String tripIdOrIntColumnName = FeatureFlags.F_EXPORT_TRIP_ID_INTS ? GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT : GTFSProviderDbHelper.T_TRIP_K_TRIP_ID; + cursor = qb.query(provider.getReadDB(), + new String[]{GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID, GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID}, + SqlUtils.getWhereInString(tripIdOrIntColumnName, tripIdOrInts), + null, + null, + null, + null, + null); + if (cursor != null && cursor.getCount() > 0) { + if (cursor.moveToFirst()) { + do { + final String tripIdOrInt = CursorExtKt.getString(cursor, tripIdOrIntColumnName); + final Long routeId = CursorExtKt.getLong(cursor, GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID); + final Long directionId = CursorExtKt.optLong(cursor, GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID, null); + tripRouteDirectionIds.put(tripIdOrInt, new Pair<>(routeId, directionId)); + } + while (cursor.moveToNext()); + } + } + } catch (Exception e) { + MTLog.w(LOG_TAG, e, "Error!"); + } finally { + SqlUtils.closeQuietly(cursor); + } + return tripRouteDirectionIds; + } + private static final String ROUTE_SORT_ORDER = SqlUtils.getSortOrderAscending(SqlUtils.getTableColumn(GTFSProviderDbHelper.T_ROUTE, GTFSProviderDbHelper.T_ROUTE_K_ID)); private static final String DIRECTION_SORT_ORDER = @@ -265,6 +392,9 @@ private static void appendRouteDirectionStopSearch(Uri uri, SQLiteQueryBuilder q private static final String ROUTE_DIRECTION_STOP_SORT_ORDER = SqlUtils.mergeSortOrder(ROUTE_SORT_ORDER, DIRECTION_SORT_ORDER, STOP_SORT_ORDER); private static final String ROUTE_DIRECTION_SORT_ORDER = SqlUtils.mergeSortOrder(ROUTE_SORT_ORDER, DIRECTION_SORT_ORDER); private static final String DIRECTION_STOP_SORT_ORDER = SqlUtils.mergeSortOrder(DIRECTION_SORT_ORDER, STOP_SORT_ORDER); + private static final String TRIP_SORT_ORDER = SqlUtils.getSortOrderAscending(SqlUtils.getTableColumn(GTFSProviderDbHelper.T_TRIP, + FeatureFlags.F_EXPORT_TRIP_ID_INTS ? GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT : GTFSProviderDbHelper.T_TRIP_K_TRIP_ID + )); @Nullable public static String getSortOrderS(@NonNull GTFSProvider provider, @NonNull Uri uri) { @@ -282,6 +412,8 @@ public static String getSortOrderS(@NonNull GTFSProvider provider, @NonNull Uri return DIRECTION_STOP_SORT_ORDER; case ROUTES_DIRECTIONS: return ROUTE_DIRECTION_SORT_ORDER; + case TRIPS: + return TRIP_SORT_ORDER; default: return null; // not processed } @@ -289,12 +421,13 @@ public static String getSortOrderS(@NonNull GTFSProvider provider, @NonNull Uri // do not change to avoid breaking changes private static final String ROUTE_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".route"; - private static final String DIRECTION_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".trip"; + private static final String DIRECTION_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".trip"; // do not change to avoid breaking changes private static final String STOP_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".stop"; private static final String ROUTE_DIRECTION_STOP_CONTENT_TYPE = - ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".routetripstop"; - private static final String DIRECTION_STOP_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".tripstop"; - private static final String ROUTE_DIRECTION_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".routetrip"; + ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".routetripstop"; // do not change to avoid breaking changes + private static final String DIRECTION_STOP_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".tripstop"; // do not change to avoid breaking changes + private static final String ROUTE_DIRECTION_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".routetrip"; // do not change to avoid breaking changes + private static final String TRIP_CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + Constants.MAIN_APP_PACKAGE_NAME + ".path"; // do not change to avoid breaking changes @Nullable public static String getTypeS(@NonNull GTFSProvider provider, @NonNull Uri uri) { @@ -312,9 +445,10 @@ public static String getTypeS(@NonNull GTFSProvider provider, @NonNull Uri uri) return ROUTE_DIRECTION_CONTENT_TYPE; case DIRECTIONS_STOPS: return DIRECTION_STOP_CONTENT_TYPE; + case TRIPS: + return TRIP_CONTENT_TYPE; default: return null; // not processed } } - } diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSScheduleTimestampsProvider.java similarity index 93% rename from src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java rename to src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSScheduleTimestampsProvider.java index 9b639f08..8e8a7843 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSScheduleTimestampsProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSScheduleTimestampsProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.gtfs; import android.content.Context; import android.content.UriMatcher; @@ -14,9 +14,8 @@ import org.mtransit.android.commons.data.RouteDirectionStop; import org.mtransit.android.commons.data.Schedule; import org.mtransit.android.commons.data.ScheduleTimestamps; +import org.mtransit.android.commons.provider.GTFSProvider; import org.mtransit.android.commons.provider.agency.AgencyUtils; -import org.mtransit.android.commons.provider.gtfs.GTFSStringsUtils; -import org.mtransit.android.commons.provider.gtfs.GTFSTripIdsUtils; import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProvider; import org.mtransit.android.commons.provider.scheduletimestamp.ScheduleTimestampsProviderContract; import org.mtransit.commons.FeatureFlags; @@ -27,7 +26,7 @@ import java.util.TimeZone; import java.util.concurrent.TimeUnit; -class GTFSScheduleTimestampsProvider implements MTLog.Loggable { +public class GTFSScheduleTimestampsProvider implements MTLog.Loggable { private static final String LOG_TAG = GTFSScheduleTimestampsProvider.class.getSimpleName(); @@ -42,7 +41,7 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author } @NonNull - static ScheduleTimestamps getScheduleTimestamps(@NonNull GTFSProvider provider, @NonNull ScheduleTimestampsProviderContract.Filter filter) { + public static ScheduleTimestamps getScheduleTimestamps(@NonNull GTFSProvider provider, @NonNull ScheduleTimestampsProviderContract.Filter filter) { ArrayList allTimestamps = new ArrayList<>(); final RouteDirectionStop rds = filter.getRouteDirectionStop(); final long startsAtInMs = filter.getStartsAtInMs(); diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java similarity index 98% rename from src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java rename to src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java index f7a32529..5c007701 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSStatusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider; +package org.mtransit.android.commons.provider.gtfs; import android.annotation.SuppressLint; import android.content.Context; @@ -23,9 +23,8 @@ 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.provider.GTFSProvider; import org.mtransit.android.commons.provider.agency.AgencyUtils; -import org.mtransit.android.commons.provider.gtfs.GTFSStringsUtils; -import org.mtransit.android.commons.provider.gtfs.GTFSTripIdsUtils; import org.mtransit.android.commons.provider.status.StatusProvider; import org.mtransit.android.commons.provider.status.StatusProviderContract; import org.mtransit.commons.CharUtils; @@ -45,7 +44,7 @@ import java.util.TimeZone; import java.util.concurrent.TimeUnit; -class GTFSStatusProvider implements MTLog.Loggable { +public class GTFSStatusProvider implements MTLog.Loggable { private static final String LOG_TAG = GTFSStatusProvider.class.getSimpleName(); @@ -766,11 +765,11 @@ public static Integer findLastServiceDate(@NonNull GTFSProvider provider) { @NonNull private static final String[] PROJECTION_SERVICE_DATES = FeatureFlags.F_EXPORT_SERVICE_ID_INTS ? new String[]{ - GTFSCommons.T_SERVICE_DATES_K_SERVICE_ID_INT, - GTFSCommons.T_SERVICE_DATES_K_EXCEPTION_TYPE + GTFSProviderDbHelper.T_SERVICE_DATES_K_SERVICE_ID_INT, + GTFSProviderDbHelper.T_SERVICE_DATES_K_EXCEPTION_TYPE } : new String[]{ - GTFSCommons.T_SERVICE_DATES_K_SERVICE_ID, - GTFSCommons.T_SERVICE_DATES_K_EXCEPTION_TYPE + GTFSProviderDbHelper.T_SERVICE_DATES_K_SERVICE_ID, + GTFSProviderDbHelper.T_SERVICE_DATES_K_EXCEPTION_TYPE }; @NonNull @@ -833,7 +832,7 @@ public static Cursor queryS(@NonNull GTFSProvider provider, @NonNull Uri uri, @N return StatusProvider.queryS(provider, uri, selection); } - static String getSortOrderS(@NonNull GTFSProvider provider, Uri uri) { + public static String getSortOrderS(@NonNull GTFSProvider provider, Uri uri) { return StatusProvider.getSortOrderS(provider, uri); } diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStringsUtils.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStringsUtils.kt index 6fd35a70..ad32be5c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStringsUtils.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStringsUtils.kt @@ -4,7 +4,6 @@ import android.database.Cursor import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.data.Schedule import org.mtransit.android.commons.provider.GTFSProvider -import org.mtransit.android.commons.provider.GTFSProviderDbHelper import org.mtransit.commons.FeatureFlags import org.mtransit.commons.GTFSCommons import org.mtransit.commons.sql.SQLUtils diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSTripIdsUtils.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSTripIdsUtils.kt index 5d1f2feb..76f41560 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSTripIdsUtils.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSTripIdsUtils.kt @@ -4,7 +4,6 @@ import android.database.Cursor import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.data.Schedule import org.mtransit.android.commons.provider.GTFSProvider -import org.mtransit.android.commons.provider.GTFSProviderDbHelper import org.mtransit.commons.FeatureFlags object GTFSTripIdsUtils : MTLog.Loggable { 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 1a9bbf43..c0c671c3 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 @@ -8,7 +8,7 @@ import org.mtransit.commons.GTFSCommons import org.mtransit.commons.secToMs import java.util.regex.Pattern -@Suppress("MemberVisibilityCanBePrivate") +@Suppress("MemberVisibilityCanBePrivate", "unused") object GtfsRealtimeExt { private const val MAX_LIST_ITEMS: Int = 5 @@ -263,6 +263,36 @@ object GtfsRealtimeExt { append(if (short) "r=" else "routeId=").append(routeId) append("|") } + if (hasModifiedTrip()) { + append(modifiedTrip.toStringExt()) + } + if (hasScheduleRelationship()) { + append(if (short) "sr=" else "schedRelation=").append(scheduleRelationship) + append("|") + } + if (hasStartDate()) { + append(if (short) "sd=" else "startDate=").append(startDate) + append("|") + } + if (hasStartTime()) { + append(if (short) "st=" else "startTime=").append(startTime) + } + append("}") + } + + @JvmStatic + @JvmOverloads + fun GtfsRealtime.TripDescriptor.ModifiedTripSelector.toStringExt(short: Boolean = false) = buildString { + append(if (short) "MTS:" else "ModifiedTripSelector:") + append("{") + if (hasModificationsId()) { + append(if (short) "m=" else "modificationsId=").append(modificationsId) + append("|") + } + if (hasAffectedTripId()) { + append(if (short) "at=" else "affectedTripId=").append(affectedTripId) + append("|") + } if (hasStartDate()) { append(if (short) "sd=" else "startDate=").append(startDate) append("|") diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index c36dcc0a..88d2477b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -14,12 +14,13 @@ import org.mtransit.android.commons.data.RouteDirectionStop import org.mtransit.android.commons.provider.GTFSRealTimeProvider import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteDirectionTagTargetUUID import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteTagTargetUUID +import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyTagTargetUUID import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToHash -import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehiclesPair +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehicles import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toStringExt -import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toVehiclesWithIdPair +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toVehicles import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProvider.Companion.getCachedVehicleLocationsS import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation import java.net.HttpURLConnection @@ -125,7 +126,7 @@ object GTFSRealTimeVehiclePositionsProvider { deleteAllCachedVehicleLocations() deleteAllDone = true } - val newVehicleLocations: List? = loadAgencyDataFromWWW(context) + val newVehicleLocations = loadAgencyDataFromWWW(context) if (newVehicleLocations != null) { // empty is OK if (!deleteAllDone) { deleteAllCachedVehicleLocations() @@ -165,9 +166,8 @@ object GTFSRealTimeVehiclePositionsProvider { val vehicleLocations = mutableListOf() try { val gFeedMessage = FeedMessage.parseFrom(response.body.bytes()) - val vehiclePositionsWithIdPair = gFeedMessage.entityList.toVehiclesWithIdPair() - for (gVehiclePositionAndId in vehiclePositionsWithIdPair.sortVehiclesPair(newLastUpdateInMs)) { - val gVehiclePosition = gVehiclePositionAndId.first + val gVehiclePositions = gFeedMessage.entityList.toVehicles() + for (gVehiclePosition in gVehiclePositions.sortVehicles(newLastUpdateInMs)) { if (Constants.DEBUG) { MTLog.d( this@GTFSRealTimeVehiclePositionsProvider, @@ -222,19 +222,21 @@ object GTFSRealTimeVehiclePositionsProvider { gVehiclePosition: GtfsRealtime.VehiclePosition ): Set? { val targetUUIDs = parseProviderTargetUUID(gVehiclePosition)?.takeIf { it.isNotBlank() } ?: return null - return setOf(VehicleLocation( - targetUUID = targetUUIDs, - targetTripId = gVehiclePosition.trip.tripId.originalIdToHash(tripIdCleanupPattern), - lastUpdateInMs = newLastUpdateInMs, - maxValidityInMs = this@processVehiclePositions.vehicleLocationMaxValidityInMs, - // - vehicleId = gVehiclePosition.vehicle.id, - vehicleLabel = gVehiclePosition.vehicle.label, - latitude = gVehiclePosition.position.latitude, - longitude = gVehiclePosition.position.longitude, - bearing = gVehiclePosition.position.bearing, - speed = gVehiclePosition.position.speed, - )) + return setOf( + VehicleLocation( + targetUUID = targetUUIDs, + targetTripId = gVehiclePosition.trip.tripId.originalIdToHash(tripIdCleanupPattern), + lastUpdateInMs = newLastUpdateInMs, + maxValidityInMs = this@processVehiclePositions.vehicleLocationMaxValidityInMs, + // + vehicleId = gVehiclePosition.vehicle.id, + vehicleLabel = gVehiclePosition.vehicle.label, + latitude = gVehiclePosition.position.latitude, + longitude = gVehiclePosition.position.longitude, + bearing = gVehiclePosition.position.bearing, + speed = gVehiclePosition.position.speed, + ) + ) } private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition): String? { diff --git a/src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt b/src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt index 9e550363..c3114eed 100644 --- a/src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt +++ b/src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt @@ -3,6 +3,7 @@ package org.mtransit.android.commons.provider import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test +import org.mtransit.android.commons.provider.gtfs.GTFSStatusProvider import org.mtransit.commons.GTFSCommons import androidx.core.util.Pair as androidXPair From 7585dcfc4ccee5ed016d353e4fae7c462c2dffe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 5 Dec 2025 11:19:16 -0500 Subject: [PATCH 06/36] wip --- .../commons/provider/gtfs/GTFSProviderDbHelper.java | 4 ++-- .../GTFSRealTimeVehiclePositionsProvider.kt | 13 +++++++++---- 2 files changed, 11 insertions(+), 6 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 9e21f6af..1102b321 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 @@ -130,8 +130,8 @@ public String getLogTag() { static final String T_SERVICE_DATES_K_SERVICE_ID_INT = GTFSCommons.T_SERVICE_DATES_K_SERVICE_ID_INT; static final String T_SERVICE_DATES_K_DATE = GTFSCommons.T_SERVICE_DATES_K_DATE; static final String T_SERVICE_DATES_K_EXCEPTION_TYPE = GTFSCommons.T_SERVICE_DATES_K_EXCEPTION_TYPE; - private static final int T_SERVICE_DATES_SAME_COLUMNS_COUNT = GTFSCommons.getT_SERVICE_DATES_OTHER_COLUMNS_COUNT(); - private static final int T_SERVICE_DATES_OTHER_COLUMNS_COUNT = GTFSCommons.getT_SERVICE_DATES_SAME_COLUMNS_COUNT(); + private static final int T_SERVICE_DATES_SAME_COLUMNS_COUNT = GTFSCommons.getT_SERVICE_DATES_SAME_COLUMNS_COUNT(); + private static final int T_SERVICE_DATES_OTHER_COLUMNS_COUNT = GTFSCommons.getT_SERVICE_DATES_OTHER_COLUMNS_COUNT(); private static final String T_SERVICE_DATES_SQL_CREATE = GTFSCommons.getT_SERVICE_DATES_SQL_CREATE(); private static final String T_SERVICE_DATES_SQL_INSERT = GTFSCommons.getT_SERVICE_DATES_SQL_INSERT(); private static final String T_SERVICE_DATES_SQL_DROP = GTFSCommons.getT_SERVICE_DATES_SQL_DROP(); diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 88d2477b..550cdbcb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -241,8 +241,11 @@ object GTFSRealTimeVehiclePositionsProvider { private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition): String? { val tripDescriptor = gVehiclePosition.trip - if (tripDescriptor.hasRouteId()) { - return if (tripDescriptor.hasDirectionId()) { + if (tripDescriptor.hasModifiedTrip() || tripDescriptor.hasScheduleRelationship() || tripDescriptor.hasStartTime() || tripDescriptor.hasStartDate()) { + MTLog.d(this, "parseTargetUUID() > unhandled values: ${tripDescriptor.toStringExt()}") + } + return if (tripDescriptor.hasRouteId()) { + if (tripDescriptor.hasDirectionId()) { getAgencyRouteDirectionTagTargetUUID( agencyTag, tripDescriptor.routeId.originalIdToHash(routeIdCleanupPattern), @@ -254,9 +257,11 @@ object GTFSRealTimeVehiclePositionsProvider { tripDescriptor.routeId.originalIdToHash(routeIdCleanupPattern), ) } + } else { + getAgencyTagTargetUUID( + agencyTag + ) } - MTLog.w(this, "parseTargetUUID() > unexpected trip selector: ${tripDescriptor.toStringExt()} (IGNORED)") - return null } } From 2ee62df58be6d12129e38726b3a2e6c343f7f625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 5 Dec 2025 11:22:45 -0500 Subject: [PATCH 07/36] wip --- .../org/mtransit/android/commons/data/Trip.kt | 30 +++++ .../provider/GTFSProviderContract.java | 9 +- .../provider/gtfs/GTFSProviderDbHelper.java | 6 +- .../provider/gtfs/GTFSRDSProvider.java | 54 ++++----- .../provider/gtfs/GTFSStatusProvider.java | 10 +- .../VehicleLocationProviderContract.kt | 105 ++++++------------ 6 files changed, 107 insertions(+), 107 deletions(-) create mode 100644 src/main/java/org/mtransit/android/commons/data/Trip.kt diff --git a/src/main/java/org/mtransit/android/commons/data/Trip.kt b/src/main/java/org/mtransit/android/commons/data/Trip.kt new file mode 100644 index 00000000..9a13c16e --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/data/Trip.kt @@ -0,0 +1,30 @@ +package org.mtransit.android.commons.data + +import android.database.Cursor +import org.mtransit.android.commons.MTLog +import org.mtransit.android.commons.getLong +import org.mtransit.android.commons.getString +import org.mtransit.android.commons.provider.GTFSProviderContract + +data class Trip( + val tripId: String, + val routeId: Long, + val directionId: Long, + val serviceId: String, +) : MTLog.Loggable { + + companion object { + private val LOG_TAG: String = Trip::class.java.simpleName + + @JvmStatic + fun fromCursor(c: Cursor) = Trip( + tripId = c.getString(GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID), + routeId = c.getLong(GTFSProviderContract.TripColumns.T_TRIP_K_ROUTE_ID), + directionId = c.getLong(GTFSProviderContract.TripColumns.T_TRIP_K_DIRECTION_ID), + serviceId = c.getString(GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID), + ) + } + + override fun getLogTag() = LOG_TAG +} + diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java index ad67ed61..82688fba 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSProviderContract.java @@ -77,6 +77,13 @@ static String[] makePROJECTION_ROUTE() { String[] PROJECTION_RDS_POI = ArrayUtils.addAllNonNull(POIProvider.PROJECTION_POI, PROJECTION_ROUTE_DIRECTION_STOP); + String[] PROJECTION_TRIP = new String[] { + TripColumns.T_TRIP_K_TRIP_ID, + TripColumns.T_TRIP_K_ROUTE_ID, + TripColumns.T_TRIP_K_DIRECTION_ID, + TripColumns.T_TRIP_K_SERVICE_ID + }; + class RouteColumns { public static final String T_ROUTE_K_ID = BaseColumns._ID; public static final String T_ROUTE_K_SHORT_NAME = "short_name"; @@ -165,10 +172,8 @@ class DirectionStopColumns { class TripColumns { public static final String T_TRIP_K_TRIP_ID = "trip_id"; - public static final String T_TRIP_K_TRIP_ID_INT = "trip_id_int"; public static final String T_TRIP_K_ROUTE_ID = "route_id"; public static final String T_TRIP_K_DIRECTION_ID = "direction_id"; public static final String T_TRIP_K_SERVICE_ID = "service_id"; - public static final String T_TRIP_K_SERVICE_ID_INT = "service_id_int"; } } 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 1102b321..c4af435f 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 @@ -96,12 +96,10 @@ public String getLogTag() { private static final String T_DIRECTION_STOPS_SQL_DROP = GTFSCommons.getT_DIRECTION_STOPS_SQL_DROP(); static final String T_TRIP = GTFSCommons.T_TRIP; - static final String T_TRIP_K_TRIP_ID = GTFSCommons.T_TRIP_K_TRIP_ID; - static final String T_TRIP_K_TRIP_ID_INT = GTFSCommons.T_TRIP_K_TRIP_ID_INT; + static final String T_TRIP_K_TRIP_ID_OR_INT = GTFSCommons.getT_TRIP_K_TRIP_ID_OR_INT(); static final String T_TRIP_K_ROUTE_ID = GTFSCommons.T_TRIP_K_ROUTE_ID; static final String T_TRIP_K_DIRECTION_ID = GTFSCommons.T_TRIP_K_DIRECTION_ID; - static final String T_TRIP_K_SERVICE_ID = GTFSCommons.T_TRIP_K_SERVICE_ID; - static final String T_TRIP_K_SERVICE_ID_INT = GTFSCommons.T_TRIP_K_SERVICE_ID_INT; + static final String T_TRIP_K_SERVICE_ID_OR_INT = GTFSCommons.getT_TRIP_K_SERVICE_ID_OR_INT(); private static final int T_TRIP_SAME_COLUMNS_COUNT = GTFSCommons.T_TRIP_SAME_COLUMNS_COUNT; private static final int T_TRIP_OTHER_COLUMNS_COUNT = GTFSCommons.T_TRIP_OTHER_COLUMNS_COUNT; private static final String T_TRIP_SQL_CREATE = GTFSCommons.getT_TRIP_SQL_CREATE(); diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java index d91ff3cc..0950d623 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java @@ -196,21 +196,38 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author GTFSProviderDbHelper.T_ROUTE, GTFSProviderDbHelper.T_ROUTE_K_ID)// .build(); + private static final String TRIP_TRIP_IDS_SERVICE_IDS_JOIN; + + static { + final SQLJoinBuilder bd = SQLJoinBuilder.getNew(GTFSProviderDbHelper.T_TRIP); + if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { + bd.innerJoin(GTFSProviderDbHelper.T_TRIP_IDS, // + GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT, // + GTFSProviderDbHelper.T_TRIP_IDS, GTFSProviderDbHelper.T_TRIP_IDS_K_ID); + } + if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { + bd.innerJoin(GTFSProviderDbHelper.T_SERVICE_IDS, // + GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_OR_INT, // + GTFSProviderDbHelper.T_SERVICE_IDS, GTFSProviderDbHelper.T_SERVICE_IDS_K_ID); + } + TRIP_TRIP_IDS_SERVICE_IDS_JOIN = bd.build(); + } + private static final ArrayMap TRIP_PROJECTION_MAP; static { final SqlUtils.ProjectionMapBuilder sb = SqlUtils.ProjectionMapBuilder.getNew(); if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { - sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT, GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID_INT); + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP_IDS, GTFSProviderDbHelper.T_TRIP_IDS_K_ID, GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID); } else { - sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID, GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID); + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT, GTFSProviderContract.TripColumns.T_TRIP_K_TRIP_ID); } sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID, GTFSProviderContract.TripColumns.T_TRIP_K_ROUTE_ID); sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID, GTFSProviderContract.TripColumns.T_TRIP_K_DIRECTION_ID); if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { - sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_INT, GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID_INT); + sb.appendTableColumn(GTFSProviderDbHelper.T_SERVICE_IDS, GTFSProviderDbHelper.T_SERVICE_IDS_K_ID, GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID); } else { - sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID, GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID); + sb.appendTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_OR_INT, GTFSProviderContract.TripColumns.T_TRIP_K_SERVICE_ID); } TRIP_PROJECTION_MAP = sb.build(); } @@ -250,7 +267,7 @@ public static Cursor queryS(@NonNull GTFSProvider provider, @NonNull Uri uri, @N qb.setProjectionMap(DIRECTION_STOP_PROJECTION_MAP); break; case TRIPS: - qb.setTables(GTFSProviderDbHelper.T_TRIP); + qb.setTables(TRIP_TRIP_IDS_SERVICE_IDS_JOIN); qb.setProjectionMap(TRIP_PROJECTION_MAP); break; default: @@ -297,17 +314,8 @@ private static void appendRouteDirectionStopSearch(Uri uri, SQLiteQueryBuilder q ArrayList list = new ArrayList<>(); list.add(GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID); list.add(GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID); - if (FeatureFlags.F_EXPORT_TRIP_ID_INTS) { - list.add(GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT); - } else { - list.add(GTFSProviderDbHelper.T_TRIP_K_TRIP_ID); - } - if (FeatureFlags.F_EXPORT_SERVICE_ID_INTS) { - list.add(GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_INT); - } else { - list.add(GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID); - } - + list.add(GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT); + list.add(GTFSProviderDbHelper.T_TRIP_K_SERVICE_ID_OR_INT); PROJECTION_TRIPS = list.toArray(new String[0]); } @@ -318,9 +326,8 @@ public static HashSet findDirectionTripIdOrIntList(@NonNull GTFSProvider try { final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(GTFSProviderDbHelper.T_TRIP); - final String tripIdOrIntColumnName = FeatureFlags.F_EXPORT_TRIP_ID_INTS ? GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT : GTFSProviderDbHelper.T_TRIP_K_TRIP_ID; cursor = qb.query(provider.getReadDB(), - new String[]{tripIdOrIntColumnName}, + new String[]{GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT}, SqlUtils.getWhereEquals(GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID, directionId), null, null, @@ -330,7 +337,7 @@ public static HashSet findDirectionTripIdOrIntList(@NonNull GTFSProvider if (cursor != null && cursor.getCount() > 0) { if (cursor.moveToFirst()) { do { - final String tripIdOrInt = CursorExtKt.getString(cursor, tripIdOrIntColumnName); + final String tripIdOrInt = CursorExtKt.getString(cursor, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT); if (!TextUtils.isEmpty(tripIdOrInt)) { routeTripIdsOrInt.add(tripIdOrInt); } @@ -355,10 +362,9 @@ public static Map> findTripRouteAndDirectionIds(@NonNul try { final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(GTFSProviderDbHelper.T_TRIP); - final String tripIdOrIntColumnName = FeatureFlags.F_EXPORT_TRIP_ID_INTS ? GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT : GTFSProviderDbHelper.T_TRIP_K_TRIP_ID; cursor = qb.query(provider.getReadDB(), new String[]{GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID, GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID}, - SqlUtils.getWhereInString(tripIdOrIntColumnName, tripIdOrInts), + SqlUtils.getWhereInString(GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT, tripIdOrInts), null, null, null, @@ -367,7 +373,7 @@ public static Map> findTripRouteAndDirectionIds(@NonNul if (cursor != null && cursor.getCount() > 0) { if (cursor.moveToFirst()) { do { - final String tripIdOrInt = CursorExtKt.getString(cursor, tripIdOrIntColumnName); + final String tripIdOrInt = CursorExtKt.getString(cursor, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT); final Long routeId = CursorExtKt.getLong(cursor, GTFSProviderDbHelper.T_TRIP_K_ROUTE_ID); final Long directionId = CursorExtKt.optLong(cursor, GTFSProviderDbHelper.T_TRIP_K_DIRECTION_ID, null); tripRouteDirectionIds.put(tripIdOrInt, new Pair<>(routeId, directionId)); @@ -392,9 +398,7 @@ public static Map> findTripRouteAndDirectionIds(@NonNul private static final String ROUTE_DIRECTION_STOP_SORT_ORDER = SqlUtils.mergeSortOrder(ROUTE_SORT_ORDER, DIRECTION_SORT_ORDER, STOP_SORT_ORDER); private static final String ROUTE_DIRECTION_SORT_ORDER = SqlUtils.mergeSortOrder(ROUTE_SORT_ORDER, DIRECTION_SORT_ORDER); private static final String DIRECTION_STOP_SORT_ORDER = SqlUtils.mergeSortOrder(DIRECTION_SORT_ORDER, STOP_SORT_ORDER); - private static final String TRIP_SORT_ORDER = SqlUtils.getSortOrderAscending(SqlUtils.getTableColumn(GTFSProviderDbHelper.T_TRIP, - FeatureFlags.F_EXPORT_TRIP_ID_INTS ? GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_INT : GTFSProviderDbHelper.T_TRIP_K_TRIP_ID - )); + private static final String TRIP_SORT_ORDER = SqlUtils.getSortOrderAscending(SqlUtils.getTableColumn(GTFSProviderDbHelper.T_TRIP, GTFSProviderDbHelper.T_TRIP_K_TRIP_ID_OR_INT)); @Nullable public static String getSortOrderS(@NonNull GTFSProvider provider, @NonNull Uri uri) { diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java index 5c007701..9d35e3ff 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProvider.java @@ -777,10 +777,16 @@ private static HashSet> findServicesAndExceptionTypes(@Non final HashSet> serviceIdOrIntAndExceptionTypes = new HashSet<>(); Cursor cursor = null; try { - final String selection = SqlUtils.getWhereEquals(GTFSProviderDbHelper.T_SERVICE_DATES_K_DATE, dateS); final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(GTFSProviderDbHelper.T_SERVICE_DATES); - cursor = qb.query(provider.getReadDB(), PROJECTION_SERVICE_DATES, selection, null, null, null, null, null); + cursor = qb.query(provider.getReadDB(), + PROJECTION_SERVICE_DATES, + SqlUtils.getWhereEquals(GTFSProviderDbHelper.T_SERVICE_DATES_K_DATE, dateS), + null, + null, + null, + null, + null); if (cursor != null && cursor.getCount() > 0) { if (cursor.moveToFirst()) { do { diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 78fe7f8c..5e2c8c6d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -9,12 +9,14 @@ import org.json.JSONObject import org.mtransit.android.commons.JSONUtils import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.MTLog.Loggable +import org.mtransit.android.commons.SecureStringUtils import org.mtransit.android.commons.data.DefaultPOI import org.mtransit.android.commons.data.POI import org.mtransit.android.commons.data.Route import org.mtransit.android.commons.data.RouteDirection import org.mtransit.android.commons.provider.common.ProviderContract import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation +import org.mtransit.commons.mapNotNullToMap interface VehicleLocationProviderContract : ProviderContract { @@ -88,61 +90,37 @@ interface VehicleLocationProviderContract : ProviderContract { val route: Route? = null, val routeDirection: RouteDirection? = null, val tripIds: List?, // original // GTFS // cleaned - val inFocus: Boolean?, - val cacheOnly: Boolean?, - val providedEncryptKeysMap: Map?, ) : Loggable { + var inFocus: Boolean? = null + var cacheOnly: Boolean? = null + + var providedEncryptKeysMap: Map? = null + private set + @SuppressLint("DiscouragedApi") - constructor( - poi: POI, - tripIds: List? = null, - inFocus: Boolean? = null, - cacheOnly: Boolean? = null, - providedEncryptKeysMap: Map? = null - ) : this( - authority = poi.authority, - poi = poi, - tripIds = tripIds, - inFocus = inFocus, - cacheOnly = cacheOnly, - providedEncryptKeysMap = providedEncryptKeysMap, - ) + constructor(poi: POI, tripIds: List? = null) : + this(authority = poi.authority, poi = poi, tripIds = tripIds) @SuppressLint("DiscouragedApi") - constructor( - route: Route, - tripIds: List? = null, - inFocus: Boolean? = null, - cacheOnly: Boolean? = null, - providedEncryptKeysMap: Map? = null, - ) : this( - authority = route.authority, - route = route, - tripIds = tripIds, - inFocus = inFocus, - cacheOnly = cacheOnly, - providedEncryptKeysMap = providedEncryptKeysMap, - ) + constructor(route: Route, tripIds: List? = null) : + this(authority = route.authority, route = route, tripIds = tripIds) @SuppressLint("DiscouragedApi") - constructor( - routeDirection: RouteDirection, - tripIds: List? = null, - inFocus: Boolean? = null, - cacheOnly: Boolean? = null, - providedEncryptKeysMap: Map? = null, - ) : this( - authority = routeDirection.authority, - routeDirection = routeDirection, - tripIds = tripIds, - inFocus = inFocus, - cacheOnly = cacheOnly, - providedEncryptKeysMap = providedEncryptKeysMap, - ) + constructor(routeDirection: RouteDirection, tripIds: List? = null) : + this(authority = routeDirection.authority, routeDirection = routeDirection, tripIds = tripIds) val inFocusOrDefault = inFocus ?: false + fun appendProvidedKeys(keysMap: Map?): Filter { + keysMap?.mapNotNullToMap { (key, value) -> + SecureStringUtils.enc(value)?.let { encValue -> key to encValue } + }?.let { + providedEncryptKeysMap = it + } + return this + } + fun getProvidedEncryptKey(key: String) = this.providedEncryptKeysMap?.get(key)?.takeIf { it.isNotBlank() } @@ -191,35 +169,14 @@ interface VehicleLocationProviderContract : ProviderContract { val providedEncryptKeysMap: Map? = json.optJSONObject(JSON_PROVIDED_ENCRYPT_KEYS_MAP)?.let { jProvidedEncryptKeysMap -> JSONUtils.toMapOfStrings(jProvidedEncryptKeysMap) } - return poi?.let { - Filter( - authority = it.authority, - poi = it, - tripIds = tripIds, - inFocus = inFocus, - cacheOnly = cacheOnly, - providedEncryptKeysMap = providedEncryptKeysMap - ) - } ?: route?.let { - Filter( - authority = route.authority, - route = it, - tripIds = tripIds, - inFocus = inFocus, - cacheOnly = cacheOnly, - providedEncryptKeysMap = providedEncryptKeysMap - ) - } - ?: routeDirection?.let { - Filter( - authority = routeDirection.authority, - routeDirection = it, - tripIds = tripIds, - inFocus = inFocus, - cacheOnly = cacheOnly, - providedEncryptKeysMap = providedEncryptKeysMap - ) - } + return (poi?.let { Filter(authority = it.authority, poi = it, tripIds = tripIds) } + ?: route?.let { Filter(authority = route.authority, route = it, tripIds = tripIds) } + ?: routeDirection?.let { Filter(authority = routeDirection.authority, routeDirection = it, tripIds = tripIds) }) + ?.apply { + this.inFocus = inFocus + this.cacheOnly = cacheOnly + this.providedEncryptKeysMap = providedEncryptKeysMap + } } fun toJSONString(vehicleLocationFilter: Filter) = From 8b6e37d2240a4d92d13af9a2f5fb9f5ac9e7e943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 5 Dec 2025 14:36:19 -0500 Subject: [PATCH 08/36] wip --- .../vehiclelocations/VehicleLocationProviderContract.kt | 2 +- .../commons/provider/{ => gtfs}/GTFSStatusProviderTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/java/org/mtransit/android/commons/provider/{ => gtfs}/GTFSStatusProviderTest.kt (99%) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 5e2c8c6d..28f75525 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -201,7 +201,7 @@ interface VehicleLocationProviderContract : ProviderContract { override fun getLogTag() = LOG_TAG - @Suppress("unused") + @Suppress("unused") // used from main app fun toJSONString() = toJSONString(this) val uuid: String? diff --git a/src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt b/src/test/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProviderTest.kt similarity index 99% rename from src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt rename to src/test/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProviderTest.kt index c3114eed..6c779849 100644 --- a/src/test/java/org/mtransit/android/commons/provider/GTFSStatusProviderTest.kt +++ b/src/test/java/org/mtransit/android/commons/provider/gtfs/GTFSStatusProviderTest.kt @@ -1,4 +1,4 @@ -package org.mtransit.android.commons.provider +package org.mtransit.android.commons.provider.gtfs import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue From cc16b2e8aee5df0d099e1d2c56bd82973ee7c59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 5 Dec 2025 14:56:27 -0500 Subject: [PATCH 09/36] wip --- src/debug/res/values/constants.xml | 3 +++ src/main/res/values/constants.xml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/debug/res/values/constants.xml b/src/debug/res/values/constants.xml index 39787d54..06e50838 100644 --- a/src/debug/res/values/constants.xml +++ b/src/debug/res/values/constants.xml @@ -8,7 +8,10 @@ org.mtransit.android.debug.provider.ROUTE_TRIP_STOP_PROVIDER org.mtransit.android.debug.provider.SCHEDULE_PROVIDER + org.mtransit.android.debug.providerSCHEDULE_PROVIDER_TARGET + org.mtransit.android.debug.provider.VEHICLE_LOCATION_PROVIDER + org.mtransit.android.debug.provider.VEHICLE_LOCATION_PROVIDER_TARGET org.mtransit.android.debug.provider.SERVICE_UPDATE_PROVIDER org.mtransit.android.debug.provider.SERVICE_UPDATE_PROVIDER_TARGET org.mtransit.android.debug.provider.NEWS_PROVIDER diff --git a/src/main/res/values/constants.xml b/src/main/res/values/constants.xml index e6d3fb54..75137316 100644 --- a/src/main/res/values/constants.xml +++ b/src/main/res/values/constants.xml @@ -8,7 +8,10 @@ org.mtransit.android.provider.ROUTE_TRIP_STOP_PROVIDER org.mtransit.android.provider.SCHEDULE_PROVIDER + org.mtransit.android.providerSCHEDULE_PROVIDER_TARGET + org.mtransit.android.provider.VEHICLE_LOCATION_PROVIDER + org.mtransit.android.provider.VEHICLE_LOCATION_PROVIDER_TARGET org.mtransit.android.provider.SERVICE_UPDATE_PROVIDER org.mtransit.android.provider.SERVICE_UPDATE_PROVIDER_TARGET org.mtransit.android.provider.NEWS_PROVIDER From 9ab028821632cc33c19f5c5a824bacb0e0054bd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 5 Dec 2025 15:52:05 -0500 Subject: [PATCH 10/36] wip --- .../provider/GTFSRealTimeProvider.java | 19 +++- .../serviceupdate/ServiceUpdateProvider.java | 5 +- .../GTFSRealTimeVehiclePositionsProvider.kt | 23 ++-- .../VehicleLocationDbHelper.kt | 17 +++ .../VehicleLocationProvider.kt | 107 +++++++++++++++++- .../VehicleLocationProviderContract.kt | 5 +- .../vehiclelocations/model/VehicleLocation.kt | 23 +++- 7 files changed, 181 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 58b9997d..a08b5eb6 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -108,6 +108,7 @@ public String getLogTag() { private static UriMatcher getNewUriMatcher(String authority) { UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); ServiceUpdateProvider.append(URI_MATCHER, authority); + VehicleLocationProvider.append(URI_MATCHER, authority); return URI_MATCHER; } @@ -309,7 +310,7 @@ public static String getAgencyVehiclePositionsUrlString(@NonNull Context context */ @NonNull @SuppressLint("StringFormatInvalid") // empty string: set in module app - public static String getAGENCY_VEHICLE_POSITIONS_URL( + private static String getAGENCY_VEHICLE_POSITIONS_URL( @NonNull Context context, @NonNull String token, @SuppressWarnings("SameParameterValue") @NonNull String hash @@ -1568,6 +1569,10 @@ public Cursor queryMT(@NonNull Uri uri, @Nullable String[] projection, @Nullable if (cursor != null) { return cursor; } + cursor = VehicleLocationProvider.queryS(this, uri, selection); + if (cursor != null) { + return cursor; + } throw new IllegalArgumentException(String.format("Unknown URI (query): '%s'", uri)); } @@ -1578,6 +1583,10 @@ public String getTypeMT(@NonNull Uri uri) { if (type != null) { return type; } + type = VehicleLocationProvider.getTypeS(this, uri); + if (type != null) { + return type; + } throw new IllegalArgumentException(String.format("Unknown URI (type): '%s'", uri)); } @@ -1617,6 +1626,11 @@ public String getLogTag() { static final String T_GTFS_REAL_TIME_VEHICLE_LOCATION = VehicleLocationDbHelper.T_VEHICLE_LOCATION; + private static final String T_GTFS_REAL_TIME_VEHICLE_LOCATION_SQL_CREATE = VehicleLocationDbHelper.getSqlCreateBuilder( + T_GTFS_REAL_TIME_VEHICLE_LOCATION).build(); + + private static final String T_GTFS_REAL_TIME_VEHICLE_LOCATION_SQL_DROP = SqlUtils.getSQLDropIfExistsQuery(T_GTFS_REAL_TIME_VEHICLE_LOCATION); + static final String T_GTFS_REAL_TIME_SERVICE_UPDATE = ServiceUpdateProvider.ServiceUpdateDbHelper.T_SERVICE_UPDATE; private static final String T_GTFS_REAL_TIME_SERVICE_UPDATE_SQL_CREATE = ServiceUpdateProvider.ServiceUpdateDbHelper.getSqlCreateBuilder( @@ -1633,6 +1647,7 @@ public static int getDbVersion(@NonNull Context context) { if (dbVersion < 0) { dbVersion = context.getResources().getInteger(R.integer.gtfs_real_time_db_version); dbVersion++; // add "service_update.original_id" column + dbVersion++; // add "vehicle_location" table } return dbVersion; } @@ -1651,6 +1666,7 @@ public void onCreateMT(@NonNull SQLiteDatabase db) { @Override public void onUpgradeMT(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL(T_GTFS_REAL_TIME_VEHICLE_LOCATION_SQL_DROP); db.execSQL(T_GTFS_REAL_TIME_SERVICE_UPDATE_SQL_DROP); GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs(context, 0L); initAllDbTables(db); @@ -1661,6 +1677,7 @@ public boolean isDbExist(@NonNull Context context) { } private void initAllDbTables(@NonNull SQLiteDatabase db) { + db.execSQL(T_GTFS_REAL_TIME_VEHICLE_LOCATION_SQL_CREATE); db.execSQL(T_GTFS_REAL_TIME_SERVICE_UPDATE_SQL_CREATE); } } diff --git a/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java index 6ac276f6..20024ba6 100644 --- a/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateProvider.java @@ -221,7 +221,10 @@ public static ArrayList getCachedServiceUpdatesS(@NonNull Service } @Nullable - private static ArrayList getCachedServiceUpdatesS(@NonNull ServiceUpdateProviderContract provider, Uri uri, String selection) { + private static ArrayList getCachedServiceUpdatesS( + @NonNull ServiceUpdateProviderContract provider, + @SuppressWarnings("unused") Uri uri, + String selection) { ArrayList cache = new ArrayList<>(); Cursor cursor = null; try { diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 550cdbcb..1c6eebe4 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -139,7 +139,10 @@ object GTFSRealTimeVehiclePositionsProvider { try { val url: URL val urlCachedString = GTFSRealTimeProvider.getAGENCY_VEHICLE_POSITIONS_URL_CACHED(context) - if (urlCachedString.isBlank()) { + if (urlCachedString.isNotBlank()) { + url = URL(urlCachedString) + MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Loading from cached API (length: %d) '***'...", urlCachedString.length) + } else { val token = GTFSRealTimeProvider.getAGENCY_URL_TOKEN(context) // use local token 1st for new/updated API URL & tokens .takeIf { it.isNotBlank() } ?: this.providedAgencyUrlToken ?: "" // compat w/ API w/o token @@ -149,12 +152,14 @@ object GTFSRealTimeVehiclePositionsProvider { urlString = urlString.replace(GTFSRealTimeProvider.MT_HASH_SECRET_AND_DATE.toRegex(), hash.trim()) } } - url = URL(urlString) - MTLog.i(this, "Loading from '%s'...", url.host) - MTLog.d(this, "Using token '%s' (length: %d)", if (!token.isEmpty()) "***" else "(none)", token.length) - } else { - url = URL(urlCachedString) - MTLog.i(this, "Loading from cached API (length: %d) '***'...", urlCachedString.length) + if (urlString.isNotBlank()) { + url = URL(urlString) + MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Loading from '%s'...", url.host) + MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "Using token '%s' (length: %d)", if (!token.isEmpty()) "***" else "(none)", token.length) + } else { + MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, "No valid URL to load vehicles!") + return null + } } val urlRequest = Request.Builder().url(url).build() getOkHttpClient(context).newCall(urlRequest).execute().use { response -> @@ -183,10 +188,10 @@ object GTFSRealTimeVehiclePositionsProvider { } catch (e: Exception) { MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, e, "loadAgencyDataFromWWW() > error while parsing GTFS Real Time data!") } - MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Found %d service updates.", vehicleLocations.size) + MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Found %d vehicle locations.", vehicleLocations.size) if (Constants.DEBUG) { for (serviceUpdate in vehicleLocations) { - MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > service update: $serviceUpdate.") + MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > vehicle location: $serviceUpdate.") } } return vehicleLocations diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt index 9893b21c..0c326015 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt @@ -3,7 +3,9 @@ package org.mtransit.android.commons.provider.vehiclelocations import android.content.Context import android.database.sqlite.SQLiteDatabase import android.provider.BaseColumns +import org.mtransit.android.commons.SqlUtils import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper +import org.mtransit.commons.sql.SQLCreateBuilder.Companion.getNew abstract class VehicleLocationDbHelper( context: Context?, @@ -31,6 +33,21 @@ abstract class VehicleLocationDbHelper( const val T_VEHICLE_LOCATION_K_LONGITUDE = "longitude" const val T_VEHICLE_LOCATION_K_BEARING = "bearing" const val T_VEHICLE_LOCATION_K_SPEED = "speed" + + @JvmStatic + fun getSqlCreateBuilder(table: String) = getNew(table) + .appendColumn(T_VEHICLE_LOCATION_K_ID, SqlUtils.INT_PK_AUTO) + .appendColumn(T_VEHICLE_LOCATION_K_TARGET_UUID, SqlUtils.TXT) + .appendColumn(T_VEHICLE_LOCATION_K_TARGET_TRIP_ID, SqlUtils.TXT) + .appendColumn(T_VEHICLE_LOCATION_K_LAST_UPDATE, SqlUtils.INT) + .appendColumn(T_VEHICLE_LOCATION_K_MAX_VALIDITY_IN_MS, SqlUtils.INT) + // + .appendColumn(T_VEHICLE_LOCATION_K_VEHICLE_ID, SqlUtils.TXT) + .appendColumn(T_VEHICLE_LOCATION_K_VEHICLE_LABEL, SqlUtils.TXT) + .appendColumn(T_VEHICLE_LOCATION_K_LATITUDE, SqlUtils.REAL) + .appendColumn(T_VEHICLE_LOCATION_K_LONGITUDE, SqlUtils.REAL) + .appendColumn(T_VEHICLE_LOCATION_K_BEARING, SqlUtils.REAL) + .appendColumn(T_VEHICLE_LOCATION_K_SPEED, SqlUtils.REAL) } abstract val dbName: String diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index cf11ed20..f3cf5865 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -1,11 +1,14 @@ package org.mtransit.android.commons.provider.vehiclelocations import android.content.UriMatcher +import android.database.Cursor +import android.database.MatrixCursor import android.database.sqlite.SQLiteQueryBuilder import android.net.Uri import androidx.core.database.sqlite.transaction import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.SqlUtils +import org.mtransit.android.commons.StringUtils import org.mtransit.android.commons.TimeUtils import org.mtransit.android.commons.provider.common.ContentProviderConstants import org.mtransit.android.commons.provider.common.MTContentProvider @@ -18,12 +21,108 @@ abstract class VehicleLocationProvider : MTContentProvider(), private val LOG_TAG: String = VehicleLocationProvider::class.java.simpleName fun getNewUriMatcher(authority: String) = UriMatcher(UriMatcher.NO_MATCH).apply { - append(this, authority) + append(authority) } - fun append(uriMatcher: UriMatcher, authority: String) { - uriMatcher.addURI(authority, VehicleLocationProviderContract.PING_PATH, ContentProviderConstants.PING) - uriMatcher.addURI(authority, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH, ContentProviderConstants.VEHICLE_LOCATION) + @JvmStatic + fun UriMatcher.append(authority: String) { + addURI(authority, VehicleLocationProviderContract.PING_PATH, ContentProviderConstants.PING) + addURI(authority, VehicleLocationProviderContract.VEHICLE_LOCATION_PATH, ContentProviderConstants.VEHICLE_LOCATION) + } + + @JvmStatic + fun

P.queryS(uri: Uri, selection: String?): Cursor? { + return when (getURI_MATCHER().match(uri)) { + ContentProviderConstants.PING -> ContentProviderConstants.EMPTY_CURSOR // empty cursor = processed + ContentProviderConstants.VEHICLE_LOCATION -> getVehicleLocations(selection) + else -> null // not processed + } + } + + private fun

P.getVehicleLocations(selection: String?): Cursor { + val vehicleLocationFilter = VehicleLocationProviderContract.Filter.fromJSONString(selection) + if (vehicleLocationFilter == null) { + MTLog.w(LOG_TAG, "Error while parsing vehicle location filter! (%s)", selection) + return getVehicleLocationCursor(null) + } + val nowInMs = TimeUtils.currentTimeMillis() + val cachedVehicleLocations = getCachedVehicleLocations(vehicleLocationFilter)?.toMutableList() + var purgeNecessary = false + if (cachedVehicleLocations != null) { + val iterator = cachedVehicleLocations.iterator() + while (iterator.hasNext()) { + val cachedVehicleLocation = iterator.next() + if (cachedVehicleLocation.lastUpdateInMs + vehicleLocationMaxValidityInMs < nowInMs) { + iterator.remove() + purgeNecessary = true + } + } + } + if (purgeNecessary) { + purgeUselessCachedVehicleLocations() + } + if (cachedVehicleLocations != null) { + val it = cachedVehicleLocations.iterator() + while (it.hasNext()) { + val cachedVehicleLocation = it.next() + if (!cachedVehicleLocation.useful) { + cachedVehicleLocation.id?.let { + deleteCachedVehicleLocation(it) + } + it.remove() + } + } + } + if (vehicleLocationFilter.cacheOnlyOrDefault) { + if (cachedVehicleLocations.isNullOrEmpty()) { + MTLog.w(LOG_TAG, "getVehicleLocations() > No useful cache found!") + } + return getVehicleLocationCursor(cachedVehicleLocations) + } + val cacheValidityInMs = getVehicleLocationValidityInMs(vehicleLocationFilter.inFocusOrDefault) + // TODO filter cache validity override like service update? + var loadNewVehicleLocations = false + if (cachedVehicleLocations.isNullOrEmpty()) { + loadNewVehicleLocations = true + } else { + for (cachedVehicleLocation in cachedVehicleLocations) { + if (cachedVehicleLocation.lastUpdateInMs + cacheValidityInMs < nowInMs) { + loadNewVehicleLocations = true + break + } + } + } + if (loadNewVehicleLocations) { + val newVehicleLocations = getNewVehicleLocations(vehicleLocationFilter) + if (!newVehicleLocations.isNullOrEmpty()) { + return getVehicleLocationCursor(newVehicleLocations) + } + } + if (cachedVehicleLocations.isNullOrEmpty()) { + MTLog.w(LOG_TAG, "getVehicleLocations() > no cache & no data from provider for %s!", vehicleLocationFilter.uuid) + } + return getVehicleLocationCursor(cachedVehicleLocations) + } + + fun getVehicleLocationCursor(vehicleLocations: List?): Cursor { + if (vehicleLocations == null) { + return ContentProviderConstants.EMPTY_CURSOR + } + return MatrixCursor(VehicleLocationProviderContract.PROJECTION_VEHICLE_LOCATION) + .apply { + vehicleLocations.forEach { vehicleLocation -> + addRow(vehicleLocation.cursorRow) + } + } + } + + @JvmStatic + fun

P.getTypeS(uri: Uri): String? { + return when (getURI_MATCHER().match(uri)) { + ContentProviderConstants.PING, + ContentProviderConstants.VEHICLE_LOCATION -> StringUtils.EMPTY // empty string = processed + else -> null // not processed + } } fun

P.getCachedVehicleLocationsS(targetUUIDs: Collection): List? { diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 28f75525..a2f02d8d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -83,7 +83,6 @@ interface VehicleLocationProviderContract : ProviderContract { } } - data class Filter @Discouraged("use from() instead") constructor( val authority: String, val poi: POI? = null, // RouteDirectionStop or DefaultPOI @@ -93,7 +92,10 @@ interface VehicleLocationProviderContract : ProviderContract { ) : Loggable { var inFocus: Boolean? = null + val inFocusOrDefault get() = inFocus ?: false + var cacheOnly: Boolean? = null + val cacheOnlyOrDefault get() = cacheOnly ?: false var providedEncryptKeysMap: Map? = null private set @@ -110,7 +112,6 @@ interface VehicleLocationProviderContract : ProviderContract { constructor(routeDirection: RouteDirection, tripIds: List? = null) : this(authority = routeDirection.authority, routeDirection = routeDirection, tripIds = tripIds) - val inFocusOrDefault = inFocus ?: false fun appendProvidedKeys(keysMap: Map?): Filter { keysMap?.mapNotNullToMap { (key, value) -> diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 86f42581..4879e7ca 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -2,11 +2,12 @@ package org.mtransit.android.commons.provider.vehiclelocations.model import android.content.ContentValues import android.database.Cursor +import org.mtransit.android.commons.TimeUtils import org.mtransit.android.commons.getFloat import org.mtransit.android.commons.getLong -import org.mtransit.android.commons.optInt import org.mtransit.android.commons.getString import org.mtransit.android.commons.optFloat +import org.mtransit.android.commons.optInt import org.mtransit.android.commons.optString import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract @@ -60,4 +61,24 @@ data class VehicleLocation( bearing?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) } speed?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED, it) } } + + /** + * see [VehicleLocationProviderContract.PROJECTION_VEHICLE_LOCATION] + */ + val cursorRow: Array get() = arrayOf( + id, + targetUUID, + targetTripId, + lastUpdateInMs, + maxValidityInMs, + // + vehicleId, + vehicleLabel, + latitude, + longitude, + bearing, + speed, + ) + + val useful: Boolean get() = this.lastUpdateInMs + this.maxValidityInMs >= TimeUtils.currentTimeMillis() } From 643c8cbd1a79bdafbdffdc6553e76f1f75c28283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 8 Dec 2025 12:06:06 -0500 Subject: [PATCH 11/36] fix --- .../mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt | 4 ++-- .../vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) 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 c0c671c3..5ef15eac 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 @@ -24,11 +24,11 @@ object GtfsRealtimeExt { @JvmStatic fun List.toVehicles(): List = - this.filter { it.hasAlert() }.map { it.vehicle }.distinct() + this.filter { it.hasVehicle() }.map { it.vehicle }.distinct() @JvmStatic fun List.toVehiclesWithIdPair(): List> = - this.filter { it.hasAlert() }.map { it.vehicle to it.id }.distinctBy { it.first } + this.filter { it.hasVehicle() }.map { it.vehicle to it.id }.distinctBy { it.first } @JvmStatic fun List.sortVehicles(nowMs: Long = TimeUtils.currentTimeMillis()): List = diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 1c6eebe4..ba65f48b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -16,7 +16,6 @@ import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRoute import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteTagTargetUUID import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyTagTargetUUID import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage -import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToHash import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehicles import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toStringExt @@ -164,7 +163,7 @@ object GTFSRealTimeVehiclePositionsProvider { val urlRequest = Request.Builder().url(url).build() getOkHttpClient(context).newCall(urlRequest).execute().use { response -> GtfsRealTimeStorage.saveVehicleLocationLastUpdateCode(context, response.code) - saveServiceUpdateLastUpdateMs(context, TimeUtils.currentTimeMillis()) + GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs(context, TimeUtils.currentTimeMillis()) when (response.code) { HttpURLConnection.HTTP_OK -> { val newLastUpdateInMs = TimeUtils.currentTimeMillis() From 89c38f9ee9bd2cc780b43a82ff7c18eebf7fccfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 5 Jan 2026 15:15:29 -0500 Subject: [PATCH 12/36] Filter with trip IDs --- .../android/commons/provider/gtfs/GtfsRealtimeExt.kt | 4 ++++ .../GTFSRealTimeVehiclePositionsProvider.kt | 9 +++++---- .../vehiclelocations/VehicleLocationProvider.kt | 12 +++++++++--- 3 files changed, 18 insertions(+), 7 deletions(-) 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 5ef15eac..b1e3691e 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 @@ -99,6 +99,10 @@ object GtfsRealtimeExt { fun String.originalIdToHash(idCleanupRegex: Pattern? = null): String = GTFSCommons.stringIdToHash(this, idCleanupRegex).toString() + @JvmStatic + fun String.originalIdToId(idCleanupRegex: Pattern? = null): String = + GTFSCommons.originalIdToId(this, idCleanupRegex) + fun GtfsRealtime.TimeRange.isActive(nowMs: Long = TimeUtils.currentTimeMillis()) = isStarted(nowMs) && !isEnded(nowMs) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index ba65f48b..149116c3 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -17,6 +17,7 @@ import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRoute import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyTagTargetUUID import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToHash +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToId import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehicles import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toStringExt import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toVehicles @@ -57,7 +58,7 @@ object GTFSRealTimeVehiclePositionsProvider { ?: vehicleLocationFilter.routeDirection?.getTargetUUIDs(this) ?: vehicleLocationFilter.route?.getTargetUUIDs(this)) ?.let { targetUUIDs -> - getCached(targetUUIDs) + getCached(targetUUIDs, vehicleLocationFilter.tripIds) } private fun RouteDirectionStop.getTargetUUIDs(provider: GTFSRealTimeProvider) = buildMap { @@ -74,8 +75,8 @@ object GTFSRealTimeVehiclePositionsProvider { getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)) to uuid, ) - fun GTFSRealTimeProvider.getCached(targetUUIDs: Map) = buildList { - getCachedVehicleLocationsS(targetUUIDs.keys)?.let { + fun GTFSRealTimeProvider.getCached(targetUUIDs: Map, tripIds: List? = null) = buildList { + getCachedVehicleLocationsS(targetUUIDs.keys, tripIds)?.let { addAll(it) } }.map { it.copy(targetUUID = targetUUIDs[it.targetUUID] ?: it.targetUUID) } @@ -229,7 +230,7 @@ object GTFSRealTimeVehiclePositionsProvider { return setOf( VehicleLocation( targetUUID = targetUUIDs, - targetTripId = gVehiclePosition.trip.tripId.originalIdToHash(tripIdCleanupPattern), + targetTripId = gVehiclePosition.trip.tripId.originalIdToId(tripIdCleanupPattern), lastUpdateInMs = newLastUpdateInMs, maxValidityInMs = this@processVehiclePositions.vehicleLocationMaxValidityInMs, // diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index f3cf5865..26457eb1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -125,16 +125,22 @@ abstract class VehicleLocationProvider : MTContentProvider(), } } - fun

P.getCachedVehicleLocationsS(targetUUIDs: Collection): List? { + fun

P.getCachedVehicleLocationsS(targetUUIDs: Collection, tripIds: List? = null): List? { return getCachedVehicleLocationsS( this.contentUri, - SqlUtils.getWhereInString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUIDs) + buildString { + append(SqlUtils.getWhereInString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUIDs)) + tripIds?.takeIf { it.isNotEmpty() }?.let { + append(SqlUtils.AND) + append(SqlUtils.getWhereInString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID, it)) + } + } ) } @Suppress("unused") fun

P.getCachedVehicleLocationsS(targetUUID: String): List? { - return this.getCachedVehicleLocationsS( + return getCachedVehicleLocationsS( this.contentUri, SqlUtils.getWhereEqualsString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID, targetUUID) ) From 0f0f8c3f9d99d7f8a72dc5295c0440715c7148d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 5 Jan 2026 15:30:04 -0500 Subject: [PATCH 13/36] sched relationship --- .../GTFSRealTimeVehiclePositionsProvider.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 149116c3..6f63e913 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -246,9 +246,20 @@ object GTFSRealTimeVehiclePositionsProvider { private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition): String? { val tripDescriptor = gVehiclePosition.trip - if (tripDescriptor.hasModifiedTrip() || tripDescriptor.hasScheduleRelationship() || tripDescriptor.hasStartTime() || tripDescriptor.hasStartDate()) { + if (tripDescriptor.hasModifiedTrip() || tripDescriptor.hasStartTime() || tripDescriptor.hasStartDate()) { MTLog.d(this, "parseTargetUUID() > unhandled values: ${tripDescriptor.toStringExt()}") } + when (tripDescriptor.scheduleRelationship) { + GtfsRealtime.TripDescriptor.ScheduleRelationship.SCHEDULED -> {} // handled + GtfsRealtime.TripDescriptor.ScheduleRelationship.ADDED, + GtfsRealtime.TripDescriptor.ScheduleRelationship.UNSCHEDULED, + GtfsRealtime.TripDescriptor.ScheduleRelationship.CANCELED, + GtfsRealtime.TripDescriptor.ScheduleRelationship.REPLACEMENT, + GtfsRealtime.TripDescriptor.ScheduleRelationship.DUPLICATED, + GtfsRealtime.TripDescriptor.ScheduleRelationship.DELETED, + GtfsRealtime.TripDescriptor.ScheduleRelationship.NEW, + -> MTLog.d(this, "parseTargetUUID() > unhandled schedule relationship: ${tripDescriptor.scheduleRelationship}") + } return if (tripDescriptor.hasRouteId()) { if (tripDescriptor.hasDirectionId()) { getAgencyRouteDirectionTagTargetUUID( From 922d9a7e5b9a129a4f4d7dc6fd92fe4fd5e91618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 5 Jan 2026 15:30:29 -0500 Subject: [PATCH 14/36] cleanup --- .../mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b1e3691e..47b9f65d 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 @@ -271,7 +271,7 @@ object GtfsRealtimeExt { append(modifiedTrip.toStringExt()) } if (hasScheduleRelationship()) { - append(if (short) "sr=" else "schedRelation=").append(scheduleRelationship) + append(if (short) "sr=" else "schedRel=").append(scheduleRelationship) append("|") } if (hasStartDate()) { From 72c6e6982be9cd67f6ec16e996967bfe51d56ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 5 Jan 2026 16:16:25 -0500 Subject: [PATCH 15/36] fix --- .../vehiclelocations/VehicleLocationProviderContract.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index a2f02d8d..883028b9 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.net.Uri import android.provider.BaseColumns import androidx.annotation.Discouraged +import org.json.JSONArray import org.json.JSONException import org.json.JSONObject import org.mtransit.android.commons.JSONUtils @@ -141,7 +142,7 @@ interface VehicleLocationProviderContract : ProviderContract { try { return if (jsonString == null) null else fromJSON(JSONObject(jsonString)) } catch (jsone: JSONException) { - MTLog.w(LOG_TAG, jsone, "Error while parsing JSON string '%s'", jsonString) + MTLog.w(LOG_TAG, jsone, "Error while parsing JSON string '$jsonString'") return null } } @@ -190,11 +191,11 @@ interface VehicleLocationProviderContract : ProviderContract { vehicleLocationFilter.poi?.let { put(JSON_POI, it.toJSON()) } vehicleLocationFilter.route?.let { put(JSON_ROUTE, Route.toJSON(it)) } vehicleLocationFilter.routeDirection?.let { put(JSON_ROUTE_DIRECTION, RouteDirection.toJSON(it)) } - vehicleLocationFilter.tripIds?.let { put(JSON_TRIP_IDS, it) } + vehicleLocationFilter.tripIds?.let { put(JSON_TRIP_IDS, JSONArray(it)) } vehicleLocationFilter.providedEncryptKeysMap?.let { put(JSON_PROVIDED_ENCRYPT_KEYS_MAP, JSONUtils.toJSONObject(it)) } } } catch (jsone: JSONException) { - MTLog.w(LOG_TAG, jsone, "Error while making JSON object '%s'", vehicleLocationFilter) + MTLog.w(LOG_TAG, jsone, "Error while making JSON object '$vehicleLocationFilter'!") null } } From 0345f75e2003028a2ce783de668ef25a34f1127f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Tue, 6 Jan 2026 15:05:31 -0500 Subject: [PATCH 16/36] add vehicle location reporting timestamp --- src/main/java/org/mtransit/android/commons/MTLog.java | 7 ++++++- .../android/commons/provider/GTFSRealTimeProvider.java | 1 + .../GTFSRealTimeVehiclePositionsProvider.kt | 1 + .../provider/vehiclelocations/VehicleLocationDbHelper.kt | 2 ++ .../provider/vehiclelocations/VehicleLocationProvider.kt | 1 + .../vehiclelocations/VehicleLocationProviderContract.kt | 2 ++ .../provider/vehiclelocations/model/VehicleLocation.kt | 4 ++++ 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mtransit/android/commons/MTLog.java b/src/main/java/org/mtransit/android/commons/MTLog.java index 4331463e..15c12bb7 100644 --- a/src/main/java/org/mtransit/android/commons/MTLog.java +++ b/src/main/java/org/mtransit/android/commons/MTLog.java @@ -9,6 +9,7 @@ import java.util.Calendar; import java.util.Date; +import java.util.Locale; // adb logcat -s "MT" // adb logcat -s "MTD" @@ -367,11 +368,15 @@ private static void doLog(@NonNull LogMethod logMethod, @NonNull String logMsg) ); } + private static final ThreadSafeDateFormatter LOG_TIME_FORMAT = new ThreadSafeDateFormatter("yyyy-MM-dd_HH-mm-ss.SSS", Locale.ENGLISH); + private static String getLogMsg(@NonNull String tag, @NonNull String logMsg) { if (Constants.DEBUG) { logMsg = StringUtils.oneLineOneSpace(logMsg); } - return String.format("%s:%s>%s", TimeUtils.currentTimeMillis(), tag, logMsg); + final String time = BuildConfig.DEBUG ? LOG_TIME_FORMAT.formatThreadSafe(TimeUtils.currentTimeMillis()) + : String.valueOf(TimeUtils.currentTimeMillis()); + return String.format("%s:%s>%s", time, tag, logMsg); } private static void logEntireMessage(@NonNull LogMethod logMethod, String logMsg) { diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index a08b5eb6..c4777809 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -1648,6 +1648,7 @@ public static int getDbVersion(@NonNull Context context) { dbVersion = context.getResources().getInteger(R.integer.gtfs_real_time_db_version); dbVersion++; // add "service_update.original_id" column dbVersion++; // add "vehicle_location" table + dbVersion++; // add "vehicle_location.report_timestamp" column } return dbVersion; } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 6f63e913..4e8d7b76 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -236,6 +236,7 @@ object GTFSRealTimeVehiclePositionsProvider { // vehicleId = gVehiclePosition.vehicle.id, vehicleLabel = gVehiclePosition.vehicle.label, + reportTimestamp = gVehiclePosition.timestamp, latitude = gVehiclePosition.position.latitude, longitude = gVehiclePosition.position.longitude, bearing = gVehiclePosition.position.bearing, diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt index 0c326015..2c01f40a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt @@ -29,6 +29,7 @@ abstract class VehicleLocationDbHelper( const val T_VEHICLE_LOCATION_K_VEHICLE_ID = "vehicle_id" const val T_VEHICLE_LOCATION_K_VEHICLE_LABEL = "vehicle_label" + const val T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP = "report_timestamp" const val T_VEHICLE_LOCATION_K_LATITUDE = "latitude" const val T_VEHICLE_LOCATION_K_LONGITUDE = "longitude" const val T_VEHICLE_LOCATION_K_BEARING = "bearing" @@ -44,6 +45,7 @@ abstract class VehicleLocationDbHelper( // .appendColumn(T_VEHICLE_LOCATION_K_VEHICLE_ID, SqlUtils.TXT) .appendColumn(T_VEHICLE_LOCATION_K_VEHICLE_LABEL, SqlUtils.TXT) + .appendColumn(T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, SqlUtils.INT) .appendColumn(T_VEHICLE_LOCATION_K_LATITUDE, SqlUtils.REAL) .appendColumn(T_VEHICLE_LOCATION_K_LONGITUDE, SqlUtils.REAL) .appendColumn(T_VEHICLE_LOCATION_K_BEARING, SqlUtils.REAL) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index 26457eb1..ac85d738 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -157,6 +157,7 @@ abstract class VehicleLocationProvider : MTContentProvider(), .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_VEHICLE_ID, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID) .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL) + .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP) .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_LATITUDE, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE) .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_LONGITUDE, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE) .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_BEARING, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 883028b9..efb9be82 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -38,6 +38,7 @@ interface VehicleLocationProviderContract : ProviderContract { // Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID, Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, + Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, Columns.T_VEHICLE_LOCATION_K_LATITUDE, Columns.T_VEHICLE_LOCATION_K_LONGITUDE, Columns.T_VEHICLE_LOCATION_K_BEARING, @@ -77,6 +78,7 @@ interface VehicleLocationProviderContract : ProviderContract { const val T_VEHICLE_LOCATION_K_VEHICLE_ID = "vehicle_id" const val T_VEHICLE_LOCATION_K_VEHICLE_LABEL = "vehicle_label" + const val T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP = "report_timestamp" const val T_VEHICLE_LOCATION_K_LATITUDE = "latitude" const val T_VEHICLE_LOCATION_K_LONGITUDE = "longitude" const val T_VEHICLE_LOCATION_K_BEARING = "bearing" diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 4879e7ca..1efe16c0 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -8,6 +8,7 @@ import org.mtransit.android.commons.getLong import org.mtransit.android.commons.getString import org.mtransit.android.commons.optFloat import org.mtransit.android.commons.optInt +import org.mtransit.android.commons.optLong import org.mtransit.android.commons.optString import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract @@ -23,6 +24,7 @@ data class VehicleLocation( // val vehicleId: String?, // not user visible val vehicleLabel: String?, // user visible + val reportTimestamp: Long?, val latitude: Float, val longitude: Float, val bearing: Float?, // in degree @@ -40,6 +42,7 @@ data class VehicleLocation( // vehicleId = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID), vehicleLabel = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL), + reportTimestamp = cursor.optLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP), latitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE), longitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE), bearing = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING), @@ -56,6 +59,7 @@ data class VehicleLocation( // vehicleId?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID, it) } vehicleLabel?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, it) } + reportTimestamp?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, it) } put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE, latitude) put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE, longitude) bearing?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) } From 890013cf4eb5c972d47fb503a6fc8f127d22ffcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 8 Jan 2026 08:29:05 -0500 Subject: [PATCH 17/36] fix & sh --- pub/generate_all_dpis_from_xxxhdpi.sh | 48 +++++++++++++++++++ .../vehiclelocations/model/VehicleLocation.kt | 1 + 2 files changed, 49 insertions(+) create mode 100755 pub/generate_all_dpis_from_xxxhdpi.sh diff --git a/pub/generate_all_dpis_from_xxxhdpi.sh b/pub/generate_all_dpis_from_xxxhdpi.sh new file mode 100755 index 00000000..225e0dbc --- /dev/null +++ b/pub/generate_all_dpis_from_xxxhdpi.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euo pipefail # -x + +if [[ "$#" -ne 1 ]]; then + echo "> Wrong $# parameters '$*'!" + exit 1 #error +fi + +SOURCE=$1 +echo "SOURCE: $SOURCE." + +if [ ! -f "$SOURCE" ]; then + echo "> File '$SOURCE' does not exist!" + exit 1 #error +fi + +SOURCE_DIR=$(dirname "$SOURCE") +echo "SOURCE_DIR: $SOURCE_DIR." + +SOURCE_NAME=$(basename "$SOURCE") +echo "SOURCE_NAME: $SOURCE_NAME." + +SOURCE_DIR_NAME=$(basename "$SOURCE_DIR") +echo "SOURCE_DIR_NAME: $SOURCE_DIR_NAME." + +if [ "$SOURCE_DIR_NAME" != "drawable-xxxhdpi" ]; then + echo "> Wrong source drawable DPI '$SOURCE_DIR_NAME'!" + exit 1 #error +fi + +RES_DIR=$(dirname "$SOURCE_DIR") +echo "RES_DIR: $RES_DIR." + +RES_DIR_NAME=$(basename "$RES_DIR") +echo "RES_DIR_NAME: $RES_DIR_NAME." + +if [ "$RES_DIR_NAME" != "res" ]; then + echo "> Wrong source resource directory '$RES_DIR_NAME'!" + exit 1 #error +fi + +convert $SOURCE -resize 75% $RES_DIR/drawable-xxhdpi/$SOURCE_NAME +convert $SOURCE -resize 50% $RES_DIR/drawable-xhdpi/$SOURCE_NAME +convert $SOURCE -resize 37.5% $RES_DIR/drawable-hdpi/$SOURCE_NAME +convert $SOURCE -resize 25% $RES_DIR/drawable-mdpi/$SOURCE_NAME + +echo "Done" +exit 0 #ok diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 1efe16c0..9a6c0d35 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -78,6 +78,7 @@ data class VehicleLocation( // vehicleId, vehicleLabel, + reportTimestamp, latitude, longitude, bearing, From c5620aa71dfc4caa15a0443348d05aafab48b2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 8 Jan 2026 15:16:19 -0500 Subject: [PATCH 18/36] fix report timestamp unit mess --- .../GTFSRealTimeVehiclePositionsProvider.kt | 3 ++- .../vehiclelocations/model/VehicleLocation.kt | 14 ++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 4e8d7b76..3f39c3f1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -30,6 +30,7 @@ import java.net.UnknownHostException import kotlin.math.min import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds object GTFSRealTimeVehiclePositionsProvider { @@ -236,7 +237,7 @@ object GTFSRealTimeVehiclePositionsProvider { // vehicleId = gVehiclePosition.vehicle.id, vehicleLabel = gVehiclePosition.vehicle.label, - reportTimestamp = gVehiclePosition.timestamp, + reportTimestamp = gVehiclePosition.timestamp.seconds, latitude = gVehiclePosition.position.latitude, longitude = gVehiclePosition.position.longitude, bearing = gVehiclePosition.position.bearing, diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 9a6c0d35..7540f0c1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -11,6 +11,8 @@ import org.mtransit.android.commons.optInt import org.mtransit.android.commons.optLong import org.mtransit.android.commons.optString import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds /** * See [VehicleLocationProviderContract] @@ -24,13 +26,17 @@ data class VehicleLocation( // val vehicleId: String?, // not user visible val vehicleLabel: String?, // user visible - val reportTimestamp: Long?, + val reportTimestamp: Duration?, // in SECONDS val latitude: Float, val longitude: Float, val bearing: Float?, // in degree val speed: Float?, // m/s OR km/h ) { + val reportTimestampSec: Long? get() = reportTimestamp?.inWholeSeconds + + val reportTimestampMs: Long? get() = reportTimestamp?.inWholeMilliseconds + companion object { @JvmStatic fun fromCursor(cursor: Cursor) = VehicleLocation( @@ -42,7 +48,7 @@ data class VehicleLocation( // vehicleId = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID), vehicleLabel = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL), - reportTimestamp = cursor.optLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP), + reportTimestamp = cursor.optLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP)?.seconds, latitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE), longitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE), bearing = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING), @@ -59,7 +65,7 @@ data class VehicleLocation( // vehicleId?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_ID, it) } vehicleLabel?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_LABEL, it) } - reportTimestamp?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, it) } + reportTimestampSec?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, it) } put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE, latitude) put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE, longitude) bearing?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) } @@ -78,7 +84,7 @@ data class VehicleLocation( // vehicleId, vehicleLabel, - reportTimestamp, + reportTimestampSec, latitude, longitude, bearing, From 49c0e56238af759b741c2c30eb21e5e18fe1f9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 8 Jan 2026 15:58:04 -0500 Subject: [PATCH 19/36] fix in focus --- .../vehiclelocations/VehicleLocationProviderContract.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index efb9be82..0cd74229 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -194,6 +194,8 @@ interface VehicleLocationProviderContract : ProviderContract { vehicleLocationFilter.route?.let { put(JSON_ROUTE, Route.toJSON(it)) } vehicleLocationFilter.routeDirection?.let { put(JSON_ROUTE_DIRECTION, RouteDirection.toJSON(it)) } vehicleLocationFilter.tripIds?.let { put(JSON_TRIP_IDS, JSONArray(it)) } + vehicleLocationFilter.inFocus?.let { put(JSON_IN_FOCUS, it) } + vehicleLocationFilter.cacheOnly?.let { put(JSON_CACHE_ONLY, it) } vehicleLocationFilter.providedEncryptKeysMap?.let { put(JSON_PROVIDED_ENCRYPT_KEYS_MAP, JSONUtils.toJSONObject(it)) } } } catch (jsone: JSONException) { From 9c52afda9d0aa29fc85eb31d28c2a1da63040ddf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 9 Jan 2026 09:42:10 -0500 Subject: [PATCH 20/36] Tweak validity & add authority --- .../commons/provider/GTFSRealTimeProvider.java | 7 ++++++- .../GTFSRealTimeVehiclePositionsProvider.kt | 11 ++++++----- .../vehiclelocations/VehicleLocationProvider.kt | 2 +- .../VehicleLocationProviderContract.kt | 4 +++- .../vehiclelocations/model/VehicleLocation.kt | 10 ++++++++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index c4777809..b8cb7072 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -133,7 +133,7 @@ private static UriMatcher getURIMATCHER(@NonNull Context context) { * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. */ @NonNull - private static String getAUTHORITY(@NonNull Context context) { + public static String getAUTHORITY(@NonNull Context context) { if (authority == null) { authority = context.getResources().getString(R.string.gtfs_real_time_authority); } @@ -1539,6 +1539,11 @@ public UriMatcher getURI_MATCHER() { return getURIMATCHER(requireContextCompat()); } + @NonNull + public String getAuthority() { + return getAUTHORITY(requireContextCompat()); + } + @NonNull @Override public Uri getAuthorityUri() { diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 3f39c3f1..da7850d4 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -35,13 +35,13 @@ import kotlin.time.Duration.Companion.seconds object GTFSRealTimeVehiclePositionsProvider { val VEHICLE_LOCATION_MAX_VALIDITY_IN_MS = 1.hours.inWholeMilliseconds - val VEHICLE_LOCATION_VALIDITY_IN_MS = 30.minutes.inWholeMilliseconds - val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds - val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 10.minutes.inWholeMilliseconds - val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_MS = 10.minutes.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 30.seconds.inWholeMilliseconds + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 3.minutes.inWholeMilliseconds // UNUSED? + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds // UNUSED? @JvmStatic - fun getMinDurationBetweenRefreshInMs(inFocus: Boolean) = + fun getMinDurationBetweenRefreshInMs(inFocus: Boolean) = // UNUSED? if (inFocus) VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS else VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS @@ -230,6 +230,7 @@ object GTFSRealTimeVehiclePositionsProvider { val targetUUIDs = parseProviderTargetUUID(gVehiclePosition)?.takeIf { it.isNotBlank() } ?: return null return setOf( VehicleLocation( + authority = this.authority, targetUUID = targetUUIDs, targetTripId = gVehiclePosition.trip.tripId.originalIdToId(tripIdCleanupPattern), lastUpdateInMs = newLastUpdateInMs, diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt index ac85d738..4aaaec8b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt @@ -182,7 +182,7 @@ abstract class VehicleLocationProvider : MTContentProvider(), if (cursor != null && cursor.count > 0) { if (cursor.moveToFirst()) { do { - add(VehicleLocation.fromCursor(cursor)) + add(VehicleLocation.fromCursor(cursor, this@getCachedVehicleLocationsS.authority)) } while (cursor.moveToNext()) } } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index 0cd74229..ef2bb83f 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -46,6 +46,8 @@ interface VehicleLocationProviderContract : ProviderContract { ) } + val authority: String + val authorityUri: Uri val vehicleLocationMaxValidityInMs: Long @@ -115,7 +117,7 @@ interface VehicleLocationProviderContract : ProviderContract { constructor(routeDirection: RouteDirection, tripIds: List? = null) : this(authority = routeDirection.authority, routeDirection = routeDirection, tripIds = tripIds) - + @Suppress("unused") // main app only fun appendProvidedKeys(keysMap: Map?): Filter { keysMap?.mapNotNullToMap { (key, value) -> SecureStringUtils.enc(value)?.let { encValue -> key to encValue } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 7540f0c1..79b9f8bd 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -18,7 +18,8 @@ import kotlin.time.Duration.Companion.seconds * See [VehicleLocationProviderContract] */ data class VehicleLocation( - val id: Int? = null, + val id: Int? = null, // DB id + val authority: String, val targetUUID: String, // route+direction or just route / routeTag / routeTag+dirTag val targetTripId: String?, // cleaned val lastUpdateInMs: Long, @@ -37,10 +38,15 @@ data class VehicleLocation( val reportTimestampMs: Long? get() = reportTimestamp?.inWholeMilliseconds + private val _uid: String? = this.vehicleId ?: this.vehicleLabel + + val uuid: String? = _uid?.let { "${this.authority}-$it" } + companion object { @JvmStatic - fun fromCursor(cursor: Cursor) = VehicleLocation( + fun fromCursor(cursor: Cursor, authority: String) = VehicleLocation( id = cursor.optInt(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_ID), + authority = authority, targetUUID = cursor.getString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_UUID), targetTripId = cursor.optString(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_TARGET_TRIP_ID), lastUpdateInMs = cursor.getLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LAST_UPDATE), From 5139c6c8172d632331de6244ab1dd09e65313a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 12 Jan 2026 09:24:01 -0500 Subject: [PATCH 21/36] validity in focus > 10 sec (remote config from main app) --- .../vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index da7850d4..22b6df30 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -36,7 +36,7 @@ object GTFSRealTimeVehiclePositionsProvider { val VEHICLE_LOCATION_MAX_VALIDITY_IN_MS = 1.hours.inWholeMilliseconds val VEHICLE_LOCATION_VALIDITY_IN_MS = 10.minutes.inWholeMilliseconds - val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 30.seconds.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 10.seconds.inWholeMilliseconds val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 3.minutes.inWholeMilliseconds // UNUSED? val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds // UNUSED? From 7df46015b8a989ba8b810f129b2b6ec0a8bba9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 12 Jan 2026 16:01:12 -0500 Subject: [PATCH 22/36] fix tripIdCleanupPattern --- .../mtransit/android/commons/provider/GTFSRealTimeProvider.java | 2 +- .../vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index b8cb7072..7dc10a12 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -1326,7 +1326,7 @@ public Pattern getRouteIdCleanupPattern(@NonNull Context context) { private boolean tripIdCleanupPatternSet = false; @Nullable - private Pattern getTripIdCleanupPattern(@NonNull Context context) { + public Pattern getTripIdCleanupPattern(@NonNull Context context) { if (this.tripIdCleanupPattern == null && !tripIdCleanupPatternSet) { this.tripIdCleanupPattern = GTFSCommons.makeIdCleanupPattern(getTRIP_ID_CLEANUP_REGEX(context)); this.tripIdCleanupPatternSet = true; diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 22b6df30..70e0fc08 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -285,7 +285,7 @@ object GTFSRealTimeVehiclePositionsProvider { } private val GTFSRealTimeProvider.routeIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat()) -private val GTFSRealTimeProvider.tripIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat()) +private val GTFSRealTimeProvider.tripIdCleanupPattern get() = getTripIdCleanupPattern(requireContextCompat()) private val GTFSRealTimeProvider.agencyTag get() = getAgencyTag(requireContextCompat()) From 4e0141630921398ba62a22b6775a787c30c9c07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Tue, 13 Jan 2026 15:16:29 -0500 Subject: [PATCH 23/36] Compat w/ headers (name=value) in GTFS-RT & ignore direction #STM --- .../provider/GTFSRealTimeProvider.java | 88 ++++++++++++------- .../provider/gtfs/GTFSRealTimeProviderExt.kt | 40 +++++++++ .../GTFSRealTimeVehiclePositionsProvider.kt | 45 +++------- src/main/res/values/gtfs_real_time_values.xml | 3 + 4 files changed, 110 insertions(+), 66 deletions(-) create mode 100644 src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 7dc10a12..4df45d9a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -44,6 +44,7 @@ import org.mtransit.android.commons.provider.common.MTContentProvider; import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; import org.mtransit.android.commons.provider.gtfs.GTFSRDSProvider; +import org.mtransit.android.commons.provider.gtfs.GTFSRealTimeProviderExtKt; import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage; import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt; import org.mtransit.android.commons.provider.gtfs.alert.GTFSRTAlertsManager; @@ -62,7 +63,6 @@ import java.net.HttpURLConnection; import java.net.SocketException; -import java.net.URL; import java.net.UnknownHostException; import java.security.MessageDigest; import java.text.ParseException; @@ -257,6 +257,34 @@ public static boolean isUSE_URL_HASH_SECRET_AND_DATE(@NonNull Context context) { return useURLHashSecretAndDate; } + @Nullable + private static java.util.List agencyUrlHeaderName = null; + + /** + * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. + */ + @NonNull + public static java.util.List getAGENCY_URL_HEADER_NAMES(@NonNull Context context) { + if (agencyUrlHeaderName == null) { + agencyUrlHeaderName = Arrays.asList(context.getResources().getStringArray(R.array.gtfs_real_time_agency_url_header_names)); + } + return agencyUrlHeaderName; + } + + @Nullable + private static java.util.List agencyUrlHeaderValue = null; + + /** + * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. + */ + @NonNull + public static java.util.List getAGENCY_URL_HEADER_VALUES(@NonNull Context context) { + if (agencyUrlHeaderValue == null) { + agencyUrlHeaderValue = Arrays.asList(context.getResources().getStringArray(R.array.gtfs_real_time_agency_url_header_values)); + } + return agencyUrlHeaderValue; + } + @Nullable private static String agencyServiceAlertsUrl = null; @@ -335,6 +363,19 @@ public static String getAGENCY_VEHICLE_POSITIONS_URL_CACHED(@NonNull Context con return agencyVehiclePositionsUrlCached; } + @Nullable + private static Boolean ignoreDirection = null; + + /** + * Override if multiple {@link GTFSRealTimeProvider} implementations in same app. + */ + public static boolean isIGNORE_DIRECTION(@NonNull Context context) { + if (ignoreDirection == null) { + ignoreDirection = context.getResources().getBoolean(R.bool.gtfs_real_time_agency_ignore_direction); + } + return ignoreDirection; + } + @Nullable private static String serviceIdCleanupRegex = null; @@ -896,34 +937,12 @@ public OkHttpClient getOkHttpClient(@NonNull Context context) { @Nullable private ArrayList loadAgencyServiceUpdateDataFromWWW(@NonNull Context context) { try { - final URL url; - String urlCachedString = getAGENCY_SERVICE_ALERTS_URL_CACHED(context); - if (urlCachedString.isBlank()) { - String token = getAGENCY_URL_TOKEN(context); // use local token 1st for new/updated API URL & tokens - if (token.isBlank()) { - token = this.providedAgencyUrlToken; - } - if (token == null) { - token = ""; // compat w/ API w/o token - } - String urlString = getAgencyServiceAlertsUrlString(context, token); - if (isUSE_URL_HASH_SECRET_AND_DATE(context)) { - final String hash = getHashSecretAndDate(context); - if (hash != null) { - urlString = urlString.replaceAll(MT_HASH_SECRET_AND_DATE, hash.trim()); - } - } - url = new URL(urlString); - MTLog.i(this, "Loading from '%s'...", url.getHost()); - MTLog.d(this, "Using token '%s' (length: %d)", !token.isEmpty() ? "***" : "(none)", token.length()); - } else { - url = new URL(urlCachedString); - MTLog.i(this, "Loading from cached API (length: %d) '***'...", urlCachedString.length()); - } - final String sourceLabel = SourceUtils.getSourceLabel( // always use source from official API - getAgencyServiceAlertsUrlString(context, "T") + final Request urlRequest = GTFSRealTimeProviderExtKt.makeRequest(this, + context, + getAGENCY_SERVICE_ALERTS_URL_CACHED(context), + token -> getAgencyServiceAlertsUrlString(context, token) ); - final Request urlRequest = new Request.Builder().url(url).build(); + if (urlRequest == null) return null; try (Response response = getOkHttpClient(context).newCall(urlRequest).execute()) { setServiceUpdateLastUpdateCode(response.code()); GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs(context, TimeUtils.currentTimeMillis()); @@ -931,6 +950,9 @@ private ArrayList loadAgencyServiceUpdateDataFromWWW(@NonNull Con case HttpURLConnection.HTTP_OK: final long newLastUpdateInMs = TimeUtils.currentTimeMillis(); final ArrayList serviceUpdates = new ArrayList<>(); + final String sourceLabel = SourceUtils.getSourceLabel( // always use source from official API + getAgencyServiceAlertsUrlString(context, "T") + ); try { GtfsRealtime.FeedMessage gFeedMessage = GtfsRealtime.FeedMessage.parseFrom(response.body().bytes()); List> alertsWithIdPair = GtfsRealtimeExt.toAlertsWithIdPair(gFeedMessage.getEntityList()); @@ -1091,12 +1113,10 @@ private static Cleaner getExtraBoldWords(@NonNull Context context, String langua if (!extraBoldWords.containsKey(language)) { try { final int index = getAGENCY_EXTRA_LANGUAGES(context).indexOf(language); - if (index >= 0) { - if (index < getAGENCY_EXTRA_BOLD_WORDS(context).size()) { - final String regex = getAGENCY_EXTRA_BOLD_WORDS(context).get(index); - if (!regex.isEmpty()) { - extraBoldWords.put(language, new Cleaner(regex, true)); - } + if (index >= 0 && index < getAGENCY_EXTRA_BOLD_WORDS(context).size()) { + final String regex = getAGENCY_EXTRA_BOLD_WORDS(context).get(index); + if (!regex.isEmpty()) { + extraBoldWords.put(language, new Cleaner(regex, true)); } } } catch (Exception e) { diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt new file mode 100644 index 00000000..428b3e90 --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt @@ -0,0 +1,40 @@ +package org.mtransit.android.commons.provider.gtfs + +import android.content.Context +import okhttp3.Request +import org.mtransit.android.commons.MTLog +import org.mtransit.android.commons.provider.GTFSRealTimeProvider +import java.net.URL + +fun GTFSRealTimeProvider.makeRequest(context: Context, urlCachedString: String = "", getUrlString: (token: String) -> String): Request? { + if (urlCachedString.isNotBlank()) { + MTLog.i(this, "Loading from cached API (length: %d) '***'...", urlCachedString.length) + return Request.Builder().url(URL(urlCachedString)).build() + } + val token = GTFSRealTimeProvider.getAGENCY_URL_TOKEN(context) // use local token 1st for new/updated API URL & tokens + .takeIf { it.isNotBlank() } ?: this.providedAgencyUrlToken + ?: "" // compat w/ API w/o token + var urlString = getUrlString(token) + if (GTFSRealTimeProvider.isUSE_URL_HASH_SECRET_AND_DATE(context)) { + getHashSecretAndDate(context)?.let { hash -> + urlString = urlString.replace(GTFSRealTimeProvider.MT_HASH_SECRET_AND_DATE.toRegex(), hash.trim()) + } + } + if (urlString.isBlank()) { + MTLog.w(this, "No valid URL!") + return null + } + val url = URL(urlString) + MTLog.i(this, "Loading from '%s'...", url.host) + MTLog.d(this, "Using token '%s' (length: %d)", if (!token.isEmpty()) "***" else "(none)", token.length) + return Request.Builder() + .url(url) + .apply { + val agencyUrlHeaderNames = GTFSRealTimeProvider.getAGENCY_URL_HEADER_NAMES(context) + val agencyUrlHeaderValues = GTFSRealTimeProvider.getAGENCY_URL_HEADER_VALUES(context) + if (agencyUrlHeaderNames.size != agencyUrlHeaderValues.size) return@apply + for (i in agencyUrlHeaderNames.indices) { + addHeader(agencyUrlHeaderNames[i], agencyUrlHeaderValues[i]) + } + }.build() +} diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 70e0fc08..fbc7552e 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -3,7 +3,6 @@ package org.mtransit.android.commons.provider.vehiclelocations import android.content.Context import com.google.transit.realtime.GtfsRealtime import com.google.transit.realtime.GtfsRealtime.FeedMessage -import okhttp3.Request import org.mtransit.android.commons.Constants import org.mtransit.android.commons.MTLog import org.mtransit.android.commons.TimeUtils @@ -21,11 +20,11 @@ import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToId import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehicles import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toStringExt import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toVehicles +import org.mtransit.android.commons.provider.gtfs.makeRequest import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProvider.Companion.getCachedVehicleLocationsS import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation import java.net.HttpURLConnection import java.net.SocketException -import java.net.URL import java.net.UnknownHostException import kotlin.math.min import kotlin.time.Duration.Companion.hours @@ -138,31 +137,11 @@ object GTFSRealTimeVehiclePositionsProvider { private fun GTFSRealTimeProvider.loadAgencyDataFromWWW(context: Context): List? { try { - val url: URL - val urlCachedString = GTFSRealTimeProvider.getAGENCY_VEHICLE_POSITIONS_URL_CACHED(context) - if (urlCachedString.isNotBlank()) { - url = URL(urlCachedString) - MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Loading from cached API (length: %d) '***'...", urlCachedString.length) - } else { - val token = GTFSRealTimeProvider.getAGENCY_URL_TOKEN(context) // use local token 1st for new/updated API URL & tokens - .takeIf { it.isNotBlank() } ?: this.providedAgencyUrlToken - ?: "" // compat w/ API w/o token - var urlString = GTFSRealTimeProvider.getAgencyVehiclePositionsUrlString(context, token) - if (GTFSRealTimeProvider.isUSE_URL_HASH_SECRET_AND_DATE(context)) { - getHashSecretAndDate(context)?.let { hash -> - urlString = urlString.replace(GTFSRealTimeProvider.MT_HASH_SECRET_AND_DATE.toRegex(), hash.trim()) - } - } - if (urlString.isNotBlank()) { - url = URL(urlString) - MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Loading from '%s'...", url.host) - MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "Using token '%s' (length: %d)", if (!token.isEmpty()) "***" else "(none)", token.length) - } else { - MTLog.w(this@GTFSRealTimeVehiclePositionsProvider, "No valid URL to load vehicles!") - return null - } - } - val urlRequest = Request.Builder().url(url).build() + val urlRequest = makeRequest( + context, + urlCachedString = GTFSRealTimeProvider.getAGENCY_VEHICLE_POSITIONS_URL_CACHED(context), + getUrlString = { token -> GTFSRealTimeProvider.getAgencyVehiclePositionsUrlString(context, token) } + ) ?: return null getOkHttpClient(context).newCall(urlRequest).execute().use { response -> GtfsRealTimeStorage.saveVehicleLocationLastUpdateCode(context, response.code) GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs(context, TimeUtils.currentTimeMillis()) @@ -170,6 +149,7 @@ object GTFSRealTimeVehiclePositionsProvider { HttpURLConnection.HTTP_OK -> { val newLastUpdateInMs = TimeUtils.currentTimeMillis() val vehicleLocations = mutableListOf() + val ignoreDirection = GTFSRealTimeProvider.isIGNORE_DIRECTION(context) try { val gFeedMessage = FeedMessage.parseFrom(response.body.bytes()) val gVehiclePositions = gFeedMessage.entityList.toVehicles() @@ -180,7 +160,7 @@ object GTFSRealTimeVehiclePositionsProvider { "loadAgencyDataFromWWW() > GTFS vehicle: ${gVehiclePosition.toStringExt()}." ) } - processVehiclePositions(newLastUpdateInMs, gVehiclePosition) + processVehiclePositions(newLastUpdateInMs, gVehiclePosition, ignoreDirection) ?.takeIf { it.isNotEmpty() } ?.let { vehicleLocations.addAll(it) @@ -225,9 +205,10 @@ object GTFSRealTimeVehiclePositionsProvider { private fun GTFSRealTimeProvider.processVehiclePositions( newLastUpdateInMs: Long, - gVehiclePosition: GtfsRealtime.VehiclePosition + gVehiclePosition: GtfsRealtime.VehiclePosition, + ignoreDirection: Boolean, ): Set? { - val targetUUIDs = parseProviderTargetUUID(gVehiclePosition)?.takeIf { it.isNotBlank() } ?: return null + val targetUUIDs = parseProviderTargetUUID(gVehiclePosition, ignoreDirection)?.takeIf { it.isNotBlank() } ?: return null return setOf( VehicleLocation( authority = this.authority, @@ -247,7 +228,7 @@ object GTFSRealTimeVehiclePositionsProvider { ) } - private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition): String? { + private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition, ignoreDirection: Boolean): String? { val tripDescriptor = gVehiclePosition.trip if (tripDescriptor.hasModifiedTrip() || tripDescriptor.hasStartTime() || tripDescriptor.hasStartDate()) { MTLog.d(this, "parseTargetUUID() > unhandled values: ${tripDescriptor.toStringExt()}") @@ -264,7 +245,7 @@ object GTFSRealTimeVehiclePositionsProvider { -> MTLog.d(this, "parseTargetUUID() > unhandled schedule relationship: ${tripDescriptor.scheduleRelationship}") } return if (tripDescriptor.hasRouteId()) { - if (tripDescriptor.hasDirectionId()) { + if (tripDescriptor.hasDirectionId() && !ignoreDirection) { getAgencyRouteDirectionTagTargetUUID( agencyTag, tripDescriptor.routeId.originalIdToHash(routeIdCleanupPattern), diff --git a/src/main/res/values/gtfs_real_time_values.xml b/src/main/res/values/gtfs_real_time_values.xml index 2e8bdd5c..7ed6b17c 100755 --- a/src/main/res/values/gtfs_real_time_values.xml +++ b/src/main/res/values/gtfs_real_time_values.xml @@ -4,11 +4,14 @@ -1 + false false + + From b9f08d8d3539411e32b99c8870a26c61189a09f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Wed, 14 Jan 2026 10:54:05 -0500 Subject: [PATCH 24/36] rename --- .../java/org/mtransit/android/commons/PreferenceUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/PreferenceUtils.java b/src/main/java/org/mtransit/android/commons/PreferenceUtils.java index db280857..e73cce8a 100644 --- a/src/main/java/org/mtransit/android/commons/PreferenceUtils.java +++ b/src/main/java/org/mtransit/android/commons/PreferenceUtils.java @@ -109,11 +109,11 @@ public static String getPREFS_LCL_RDS_ROUTE_DIRECTION_ID_TAB(@NonNull String aut return PREFS_LCL_RDS_ROUTE_DIRECTION_ID_TAB + authority + routeId; } - private static final String PREFS_LCL_RDS_ROUTE_DIRECTION_ID_KEY = "pRTSRouteTripIdKey"; // do not change to avoid breaking compat w/ old modules + private static final String PREFS_LCL_RDS_DIRECTION_SHOWING_LIST_INSTEAD_OF_MAP_KEY = "pRTSRouteTripIdKey"; // do not change to avoid breaking compat w/ old modules @NonNull - public static String getPREFS_LCL_RDS_ROUTE_DIRECTION_ID_KEY(@NonNull String authority, long routeId, long directionId) { - return PREFS_LCL_RDS_ROUTE_DIRECTION_ID_KEY + authority + routeId + "-" + directionId; + public static String getPREFS_LCL_RDS_DIRECTION_SHOWING_LIST_INSTEAD_OF_MAP_KEY(@NonNull String authority, long routeId, long directionId) { + return PREFS_LCL_RDS_DIRECTION_SHOWING_LIST_INSTEAD_OF_MAP_KEY + authority + routeId + "-" + directionId; } public static final String PREFS_LCL_MAP_FILTER_TYPE_IDS = "pMapFilterTypeIds"; From 33eac0cd71f9578c926634379aa56c8eef4eead3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Wed, 14 Jan 2026 13:49:45 -0500 Subject: [PATCH 25/36] logs++ --- .../commons/provider/gtfs/GtfsRealtimeExt.kt | 129 +++++------------- 1 file changed, 35 insertions(+), 94 deletions(-) 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 47b9f65d..69490172 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 @@ -123,17 +123,15 @@ object GtfsRealtimeExt { fun GtfsRealtime.VehiclePosition.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { append("VehiclePosition:") append("{") - append(trip.toStringExt(short = true)) - append(", ") - append(position.toStringExt(short = true)) - append(", ") - append("currentStopSequence=").append(currentStopSequence) - append(", ") - append("currentStatus=").append(currentStatus) - append(", ") - append("stopId=").append(stopId) - append(", ") - append("timestamp=").append(timestamp) + if (hasTrip()) append(trip.toStringExt(short = true)).append(", ") + if (hasPosition()) append(position.toStringExt(short = true)).append(", ") + if (hasCurrentStopSequence()) append("currentStopSequence=").append(currentStopSequence).append(", ") + if (hasCurrentStatus()) append("currentStatus=").append(currentStatus).append(", ") + if (hasStopId()) append("stopId=").append(stopId).append(", ") + if (hasTimestamp()) append("timestamp=").append(timestamp).append(", ") + if (hasOccupancyPercentage()) append("occupancyPct=").append(occupancyPercentage).append(", ") + if (hasOccupancyStatus()) append("occupancyStatus=").append(occupancyStatus).append(", ") + if (hasCongestionLevel()) append("congestionLevel=").append(congestionLevel).append(", ") append("}") } @@ -153,25 +151,15 @@ object GtfsRealtimeExt { fun GtfsRealtime.Alert.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { append("Alert:") append("{") - append(informedEntityList.toStringExt(short = true, debug)) - append(", ") - append(activePeriodList.toStringExt(short = true, debug)) - append(", ") - append("cause=").append(cause) - if (debug && hasCauseDetail()) { - append("(").append(causeDetail.toStringExt("detail")).append(")") - } - append(", ") - append("effect=").append(effect) - if (debug && hasEffectDetail()) { - append("(").append(effectDetail.toStringExt("detail")).append(")") - } - append(", ") - append(headerText.toStringExt("header", debug)) - append(", ") - append(descriptionText.toStringExt("desc", debug)) - append(", ") - append(url.toStringExt("url", debug)) + append(informedEntityList.toStringExt(short = true, debug)).append(", ") + append(activePeriodList.toStringExt(short = true, debug)).append(", ") + if (hasCause()) append("cause=").append(cause).append(", ") + if (debug && hasCauseDetail()) append("(").append(causeDetail.toStringExt("detail")).append(")").append(",") + if (hasEffect()) append("effect=").append(effect).append(", ") + if (debug && hasEffectDetail()) append("(").append(effectDetail.toStringExt("detail")).append(")").append(", ") + if (hasHeaderText()) append(headerText.toStringExt("header", debug)).append(", ") + if (hasDescriptionText()) append(descriptionText.toStringExt("desc", debug)).append(", ") + if (hasUrl()) append(url.toStringExt("url", debug)).append(", ") append("}") } @@ -224,29 +212,12 @@ object GtfsRealtimeExt { fun GtfsRealtime.EntitySelector.toStringExt(short: Boolean = false) = buildString { append(if (short) "ES:" else "EntitySelector:") append("{") - if (hasAgencyId()) { - append(if (short) "a=" else "agencyId=").append(agencyId) - append("|") - } - if (hasRouteType()) { - append(if (short) "rt=" else "routeType=").append(routeType) - append("|") - } - if (hasRouteId()) { - append(if (short) "r=" else "routeId=").append(routeId) - append("|") - } - if (hasStopId()) { - append(if (short) "s=" else "stopId=").append(stopId) - append("|") - } - if (hasDirectionId()) { - append(if (short) "d=" else "directionId=").append(directionId) - append("|") - } - if (hasTrip()) { - append(trip.toStringExt(short)) - } + if (hasAgencyId()) append(if (short) "a=" else "agencyId=").append(agencyId).append("|") + if (hasRouteType()) append(if (short) "rt=" else "routeType=").append(routeType).append("|") + if (hasRouteId()) append(if (short) "r=" else "routeId=").append(routeId).append("|") + if (hasStopId()) append(if (short) "s=" else "stopId=").append(stopId).append("|") + if (hasDirectionId()) append(if (short) "d=" else "directionId=").append(directionId).append("|") + if (hasTrip()) append(trip.toStringExt(short)) append("}") } @@ -255,32 +226,13 @@ object GtfsRealtimeExt { fun GtfsRealtime.TripDescriptor.toStringExt(short: Boolean = false) = buildString { append(if (short) "TD:" else "TripDescriptor:") append("{") - if (hasTripId()) { - append(if (short) "t=" else "tripId=").append(tripId) - append("|") - } - if (hasDirectionId()) { - append(if (short) "d=" else "directionId=").append(directionId) - append("|") - } - if (hasRouteId()) { - append(if (short) "r=" else "routeId=").append(routeId) - append("|") - } - if (hasModifiedTrip()) { - append(modifiedTrip.toStringExt()) - } - if (hasScheduleRelationship()) { - append(if (short) "sr=" else "schedRel=").append(scheduleRelationship) - append("|") - } - if (hasStartDate()) { - append(if (short) "sd=" else "startDate=").append(startDate) - append("|") - } - if (hasStartTime()) { - append(if (short) "st=" else "startTime=").append(startTime) - } + if (hasTripId()) append(if (short) "t=" else "tripId=").append(tripId).append("|") + if (hasDirectionId()) append(if (short) "d=" else "directionId=").append(directionId).append("|") + if (hasRouteId()) append(if (short) "r=" else "routeId=").append(routeId).append("|") + if (hasModifiedTrip()) append(modifiedTrip.toStringExt()) + if (hasScheduleRelationship()) append(if (short) "sr=" else "schedRel=").append(scheduleRelationship).append("|") + if (hasStartDate()) append(if (short) "sd=" else "startDate=").append(startDate).append("|") + if (hasStartTime()) append(if (short) "st=" else "startTime=").append(startTime).append("|") append("}") } @@ -289,21 +241,10 @@ object GtfsRealtimeExt { fun GtfsRealtime.TripDescriptor.ModifiedTripSelector.toStringExt(short: Boolean = false) = buildString { append(if (short) "MTS:" else "ModifiedTripSelector:") append("{") - if (hasModificationsId()) { - append(if (short) "m=" else "modificationsId=").append(modificationsId) - append("|") - } - if (hasAffectedTripId()) { - append(if (short) "at=" else "affectedTripId=").append(affectedTripId) - append("|") - } - if (hasStartDate()) { - append(if (short) "sd=" else "startDate=").append(startDate) - append("|") - } - if (hasStartTime()) { - append(if (short) "st=" else "startTime=").append(startTime) - } + if (hasModificationsId()) append(if (short) "m=" else "modificationsId=").append(modificationsId).append("|") + if (hasAffectedTripId()) append(if (short) "at=" else "affectedTripId=").append(affectedTripId).append("|") + if (hasStartDate()) append(if (short) "sd=" else "startDate=").append(startDate).append("|") + if (hasStartTime()) append(if (short) "st=" else "startTime=").append(startTime).append("|") append("}") } From 9c690912f2efd9b4c297b855fd2d7ebb4e3aaa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 19 Jan 2026 10:54:26 -0500 Subject: [PATCH 26/36] GTFS Vehicle cached URL validity *=2 & Direction += authority --- .../android/commons/PreferenceUtils.java | 3 +-- .../android/commons/data/Direction.java | 27 ++++++++++++++----- .../android/commons/data/RouteDirection.java | 3 ++- .../commons/data/RouteDirectionStop.java | 21 ++++++++++----- .../provider/GTFSRealTimeProvider.java | 6 ++--- .../provider/gtfs/GTFSRealTimeProviderExt.kt | 2 +- .../GTFSRealTimeVehiclePositionsProvider.kt | 19 ++++++++----- .../android/commons/LocationUtilsTests.java | 2 +- .../provider/CaLTCOnlineProviderTest.java | 2 +- .../GrandRiverTransitProviderTests.java | 8 +++++- .../provider/GreaterSudburyProviderTests.kt | 2 +- .../provider/OCTranspoProviderTest.java | 10 +++---- .../provider/OneBusAwayProviderTests.java | 2 ++ .../provider/StmInfoApiProviderTests.java | 4 +-- 14 files changed, 72 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/PreferenceUtils.java b/src/main/java/org/mtransit/android/commons/PreferenceUtils.java index e73cce8a..afd3a841 100644 --- a/src/main/java/org/mtransit/android/commons/PreferenceUtils.java +++ b/src/main/java/org/mtransit/android/commons/PreferenceUtils.java @@ -94,10 +94,9 @@ public static String getPREFS_LCL_AGENCY_TYPE_TAB_AGENCY(int typeId) { } private static final String PREFS_LCL_AGENCY_LAST_OPENED = "pAgencyLastOpened"; - public static final long PREFS_LCL_AGENCY_LAST_OPENED_DEFAULT = -1L; @NonNull - public static String getPREFS_LCL_AGENCY_LAST_OPENED_DEFAULT(String authority) { + public static String getPREFS_LCL_AGENCY_LAST_OPENED_DEFAULT(@NonNull String authority) { return PREFS_LCL_AGENCY_LAST_OPENED + authority; } diff --git a/src/main/java/org/mtransit/android/commons/data/Direction.java b/src/main/java/org/mtransit/android/commons/data/Direction.java index ba253d96..65e9626b 100644 --- a/src/main/java/org/mtransit/android/commons/data/Direction.java +++ b/src/main/java/org/mtransit/android/commons/data/Direction.java @@ -41,6 +41,8 @@ public class Direction { public static final int HEADSIGN_TYPE_STOP_ID = 3; public static final int HEADSIGN_TYPE_NO_PICKUP = 4; + @NonNull + private final String authority; private final long id; @HeadSignType private final int headsignType; @@ -48,10 +50,13 @@ public class Direction { private final String headsignValue; private final long routeId; - public Direction(long id, - @HeadSignType int headsignType, - @NonNull String headsignValue, - long routeId) { + public Direction( + @NonNull String authority, + long id, + @HeadSignType int headsignType, + @NonNull String headsignValue, + long routeId) { + this.authority = authority; this.id = id; this.headsignType = headsignType; this.headsignValue = headsignValue; @@ -59,8 +64,9 @@ public Direction(long id, } @NonNull - public static Direction fromCursor(@NonNull Cursor c) { + public static Direction fromCursor(@NonNull Cursor c, @NonNull String authority) { return new Direction( + authority, CursorExtKt.getLong(c, GTFSProviderContract.DirectionColumns.T_DIRECTION_K_ID), CursorExtKt.getInt(c, GTFSProviderContract.DirectionColumns.T_DIRECTION_K_HEADSIGN_TYPE), CursorExtKt.getString(c, GTFSProviderContract.DirectionColumns.T_DIRECTION_K_HEADSIGN_VALUE), @@ -72,7 +78,8 @@ public static Direction fromCursor(@NonNull Cursor c) { @Override public String toString() { return Direction.class.getSimpleName() + "{" + - "id=" + id + + "authority='" + authority + '\'' + + ", id=" + id + ", headsignType=" + headsignType + ", headsignValue='" + headsignValue + '\'' + ", routeId=" + routeId + @@ -100,9 +107,10 @@ public static JSONObject toJSON(@NonNull Direction direction) { } @NonNull - public static Direction fromJSON(@NonNull JSONObject jDirection) throws JSONException { + public static Direction fromJSON(@NonNull JSONObject jDirection, @NonNull String authority) throws JSONException { try { return new Direction( + authority, jDirection.getLong(JSON_ID), jDirection.getInt(JSON_HEADSIGN_TYPE), jDirection.getString(JSON_HEADSIGN_VALUE), @@ -219,6 +227,11 @@ public static boolean isSameHeadsign(@Nullable String stringHeadsign1, @Nullable return StringUtils.equalsAlphabeticsAndDigits(stringHeadsign1, stringHeadsign2); } + @NonNull + public String getAuthority() { + return this.authority; + } + @NonNull public String getUUID(@NonNull String authority) { return POI.POIUtils.getUUID(authority, this.routeId, this.id); diff --git a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java index 86ff32f3..f0cdc54c 100644 --- a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java +++ b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java @@ -109,7 +109,7 @@ public static RouteDirection fromJSON(@NonNull JSONObject json, @NonNull String try { return new RouteDirection( Route.fromJSON(json.getJSONObject(JSON_ROUTE), authority), - Direction.fromJSON(json.getJSONObject(JSON_DIRECTION)) + Direction.fromJSON(json.getJSONObject(JSON_DIRECTION), authority) ); } catch (JSONException jsone) { MTLog.w(LOG_TAG, jsone, "Error while parsing JSON '%s'!", json); @@ -153,6 +153,7 @@ public static RouteDirection fromCursorStatic(@NonNull Cursor c, @NonNull String CursorExtKt.optInt(c, GTFSProviderContract.RouteDirectionColumns.T_ROUTE_K_TYPE, GTFSCommons.DEFAULT_ROUTE_TYPE) ), new Direction( + authority, CursorExtKt.getLong(c, GTFSProviderContract.RouteDirectionColumns.T_DIRECTION_K_ID), CursorExtKt.getInt(c, GTFSProviderContract.RouteDirectionColumns.T_DIRECTION_K_HEADSIGN_TYPE), CursorExtKt.getString(c, GTFSProviderContract.RouteDirectionColumns.T_DIRECTION_K_HEADSIGN_VALUE), diff --git a/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java b/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java index 0030571f..734ae39d 100644 --- a/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java +++ b/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java @@ -56,11 +56,12 @@ public RouteDirectionStop( } public RouteDirectionStop( - @DataSourceType int dataSourceTypeId, - @NonNull Route route, - @NonNull Direction direction, - @NonNull Stop stop, - boolean noPickup) { + @DataSourceType int dataSourceTypeId, + @NonNull Route route, + @NonNull Direction direction, + @NonNull Stop stop, + boolean noPickup + ) { super(route.getAuthority(), -1, dataSourceTypeId, POI.ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, POI.ITEM_STATUS_TYPE_SCHEDULE, POI.ITEM_ACTION_TYPE_ROUTE_DIRECTION_STOP); this.route = route; this.direction = direction; @@ -82,11 +83,16 @@ public int getId() { @Override public String getUUID() { if (this.uuid == null) { - this.uuid = POI.POIUtils.getUUID(getAuthority(), getRoute().getId(), getDirection().getId(), getStop().getId()); + this.uuid = makeUUID(getAuthority(), getRoute().getId(), getDirection().getId(), getStop().getId()); } return this.uuid; } + @NonNull + public static String makeUUID(@NonNull String authority, long routeId, long directionId, int stopId) { + return POI.POIUtils.getUUID(authority, routeId, directionId, stopId); + } + @Override public void resetUUID() { this.uuid = null; @@ -201,7 +207,7 @@ public static RouteDirectionStop fromJSONStatic(@NonNull JSONObject json) { final RouteDirectionStop rds = new RouteDirectionStop( // DefaultPOI.getDSTypeIdFromJSON(json),// Route.fromJSON(json.getJSONObject(JSON_ROUTE), authority), // - Direction.fromJSON(json.getJSONObject(JSON_DIRECTION)), // + Direction.fromJSON(json.getJSONObject(JSON_DIRECTION), authority), // Stop.fromJSON(json.getJSONObject(JSON_STOP)), // json.getBoolean(JSON_NO_PICKUP) // ); @@ -261,6 +267,7 @@ public static RouteDirectionStop fromCursorStatic(@NonNull Cursor c, @NonNull St CursorExtKt.optInt(c, GTFSProviderContract.RouteDirectionStopColumns.T_ROUTE_K_TYPE, GTFSCommons.DEFAULT_ROUTE_TYPE) ), new Direction( + authority, CursorExtKt.getLong(c, GTFSProviderContract.RouteDirectionStopColumns.T_DIRECTION_K_ID), CursorExtKt.getInt(c, GTFSProviderContract.RouteDirectionStopColumns.T_DIRECTION_K_HEADSIGN_TYPE), CursorExtKt.getString(c, GTFSProviderContract.RouteDirectionStopColumns.T_DIRECTION_K_HEADSIGN_VALUE), diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 7cc52598..13590a1d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -502,17 +502,17 @@ private static String getAGENCY_TIME_ZONE(@NonNull Context context) { @Override public long getMinDurationBetweenVehicleLocationRefreshInMs(boolean inFocus) { - return GTFSRealTimeVehiclePositionsProvider.getMinDurationBetweenRefreshInMs(inFocus); + return GTFSRealTimeVehiclePositionsProvider.getMinDurationBetweenRefreshInMs(this, inFocus); } @Override public long getVehicleLocationMaxValidityInMs() { - return GTFSRealTimeVehiclePositionsProvider.getMaxValidityInMs(); + return GTFSRealTimeVehiclePositionsProvider.getMaxValidityInMs(this); } @Override public long getVehicleLocationValidityInMs(boolean inFocus) { - return GTFSRealTimeVehiclePositionsProvider.getValidityInMs(inFocus); + return GTFSRealTimeVehiclePositionsProvider.getValidityInMs(this, inFocus); } @Override diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt index 428b3e90..dd5f78f0 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRealTimeProviderExt.kt @@ -26,7 +26,7 @@ fun GTFSRealTimeProvider.makeRequest(context: Context, urlCachedString: String = } val url = URL(urlString) MTLog.i(this, "Loading from '%s'...", url.host) - MTLog.d(this, "Using token '%s' (length: %d)", if (!token.isEmpty()) "***" else "(none)", token.length) + MTLog.d(this, "Using token '%s' (length: %d)", if (token.isEmpty()) "(none)" else "***", token.length) return Request.Builder() .url(url) .apply { diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index fbc7552e..7d1dd389 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -40,17 +40,22 @@ object GTFSRealTimeVehiclePositionsProvider { val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds // UNUSED? @JvmStatic - fun getMinDurationBetweenRefreshInMs(inFocus: Boolean) = // UNUSED? - if (inFocus) VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS - else VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS + fun GTFSRealTimeProvider.getMinDurationBetweenRefreshInMs(inFocus: Boolean) = // UNUSED? + if (inFocus) VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS.adaptForCachedAPI(this.context) + else VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS.adaptForCachedAPI(this.context) @JvmStatic - fun getValidityInMs(inFocus: Boolean) = - if (inFocus) VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS - else VEHICLE_LOCATION_VALIDITY_IN_MS + fun GTFSRealTimeProvider.getValidityInMs(inFocus: Boolean) = + if (inFocus) VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS.adaptForCachedAPI(this.context) + else VEHICLE_LOCATION_VALIDITY_IN_MS.adaptForCachedAPI(this.context) @JvmStatic - val maxValidityInMs: Long get() = VEHICLE_LOCATION_MAX_VALIDITY_IN_MS + val GTFSRealTimeProvider.maxValidityInMs: Long get() = VEHICLE_LOCATION_MAX_VALIDITY_IN_MS.adaptForCachedAPI(this.context) + + private fun Long.adaptForCachedAPI(context: Context?) = + if (context?.let { GTFSRealTimeProvider.getAGENCY_VEHICLE_POSITIONS_URL_CACHED(it) }?.isNotBlank() == true) { + this * 2L // less calls to Cached API $$ + } else this @JvmStatic fun GTFSRealTimeProvider.getCached(vehicleLocationFilter: VehicleLocationProviderContract.Filter) = diff --git a/src/test/java/org/mtransit/android/commons/LocationUtilsTests.java b/src/test/java/org/mtransit/android/commons/LocationUtilsTests.java index e858ddd4..88cf5bc1 100644 --- a/src/test/java/org/mtransit/android/commons/LocationUtilsTests.java +++ b/src/test/java/org/mtransit/android/commons/LocationUtilsTests.java @@ -166,7 +166,7 @@ public POI getPOI() { return new RouteDirectionStop( 1, new Route(authority, rdsRouteTag, "R" + rdsRouteTag, "Route " + rdsRouteTag, "000000", rdsRouteTag.hashCode(), 0), - new Direction(rdsDirectionTag, Direction.HEADSIGN_TYPE_NONE, "head-sign " + rdsDirectionTag, rdsRouteTag), + new Direction(authority, rdsDirectionTag, Direction.HEADSIGN_TYPE_NONE, "head-sign " + rdsDirectionTag, rdsRouteTag), new Stop(rdsStopTag, String.valueOf(rdsStopTag), "Stop #" + rdsStopTag, 0.0d, 0.0d, 0, rdsStopTag.hashCode()), false ); diff --git a/src/test/java/org/mtransit/android/commons/provider/CaLTCOnlineProviderTest.java b/src/test/java/org/mtransit/android/commons/provider/CaLTCOnlineProviderTest.java index c32e4455..9c14b462 100644 --- a/src/test/java/org/mtransit/android/commons/provider/CaLTCOnlineProviderTest.java +++ b/src/test/java/org/mtransit/android/commons/provider/CaLTCOnlineProviderTest.java @@ -33,7 +33,7 @@ public class CaLTCOnlineProviderTest { private static final String AUTHORITY = "authority.test"; private static final Route DEFAULT_ROUTE = new Route(AUTHORITY, 1, "1", "route 1", "color", 1, 0); - private static final Direction DEFAULT_DIRECTION = new Direction(1, Direction.HEADSIGN_TYPE_STRING, "direction 1", 1); + private static final Direction DEFAULT_DIRECTION = new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "direction 1", 1); private static final Stop DEFAULT_STOP = new Stop(1, "1", "stop 1", 0, 0, 0, 1); private final CaLTCOnlineProvider provider = new CaLTCOnlineProvider(); diff --git a/src/test/java/org/mtransit/android/commons/provider/GrandRiverTransitProviderTests.java b/src/test/java/org/mtransit/android/commons/provider/GrandRiverTransitProviderTests.java index 6c726676..5b698d73 100644 --- a/src/test/java/org/mtransit/android/commons/provider/GrandRiverTransitProviderTests.java +++ b/src/test/java/org/mtransit/android/commons/provider/GrandRiverTransitProviderTests.java @@ -29,8 +29,10 @@ public class GrandRiverTransitProviderTests { private static final String VEHICLE_ID = "vehicle_id"; + private static final String AUTHORITY = "authority.test"; + private static final Route DEFAULT_ROUTE = new Route( - "authority.test", + AUTHORITY, 1, "1", "route 1", @@ -53,6 +55,7 @@ public void testParseAgencyJSONFirstAndLast() { // Arrange boolean noPickup = false; Direction direction = new Direction( + AUTHORITY, 1L, Direction.HEADSIGN_TYPE_STRING, "The Boardwalk", @@ -88,6 +91,7 @@ public void testParseAgencyJSONFirstAndLastNoPickup() { // Arrange boolean noPickup = true; Direction direction = new Direction( + AUTHORITY, 1L, Direction.HEADSIGN_TYPE_STRING, "Charles Term", // cleaned by parser @@ -123,6 +127,7 @@ public void testParseAgencyJSONSameTripDirectionWithDifferentHeadSign() { // Arrange boolean noPickup = false; Direction direction = new Direction( + AUTHORITY, 1L, Direction.HEADSIGN_TYPE_STRING, "The Boardwalk", @@ -158,6 +163,7 @@ public void testParseAgencyJSONSplitCircleWithEmptyHeadSign() { // Arrange boolean noPickup = false; Direction direction = new Direction( + AUTHORITY, 1L, Direction.HEADSIGN_TYPE_STRING, "Ainslie Term", // cleaned by parser diff --git a/src/test/java/org/mtransit/android/commons/provider/GreaterSudburyProviderTests.kt b/src/test/java/org/mtransit/android/commons/provider/GreaterSudburyProviderTests.kt index 8e500e52..ada3efea 100644 --- a/src/test/java/org/mtransit/android/commons/provider/GreaterSudburyProviderTests.kt +++ b/src/test/java/org/mtransit/android/commons/provider/GreaterSudburyProviderTests.kt @@ -21,7 +21,7 @@ class GreaterSudburyProviderTests { private const val AUTHORITY = "authority.test" private val DEFAULT_ROUTE = Route(AUTHORITY, 1, "1", "route 1", "color", 1, 0) - private val DEFAULT_Direction = Direction(1, Direction.HEADSIGN_TYPE_STRING, "Direction 1", 1) + private val DEFAULT_Direction = Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "Direction 1", 1) private val DEFAULT_STOP = Stop(1, "1", "stop 1", 0.0, 0.0, 0, 1) } diff --git a/src/test/java/org/mtransit/android/commons/provider/OCTranspoProviderTest.java b/src/test/java/org/mtransit/android/commons/provider/OCTranspoProviderTest.java index 1d5eb523..f8fa59b7 100644 --- a/src/test/java/org/mtransit/android/commons/provider/OCTranspoProviderTest.java +++ b/src/test/java/org/mtransit/android/commons/provider/OCTranspoProviderTest.java @@ -38,7 +38,7 @@ public class OCTranspoProviderTest { private static final String AUTHORITY = "authority.test"; private static final Route DEFAULT_ROUTE = new Route(AUTHORITY, 1, "1", "route 1", "color"); - private static final Direction DEFAULT_DIRECTION = new Direction(1, Direction.HEADSIGN_TYPE_STRING, "direction 1", 1); + private static final Direction DEFAULT_DIRECTION = new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "direction 1", 1); private static final Stop DEFAULT_STOP = new Stop(1, "1", "stop 1", 0, 0, 0, 1); private final Context context = mock(); @@ -89,7 +89,7 @@ public void testParseAgencyJSONArrivalsResults() { rds = new RouteDirectionStop( POI.ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, DEFAULT_ROUTE, - new Direction(1, Direction.HEADSIGN_TYPE_STRING, "Greenboro", 1), + new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "Greenboro", 1), DEFAULT_STOP, false); long lastUpdateInMs = 1576984339000L; // December 21, 2019 10:12:19 PM GMT-05:00 @@ -128,7 +128,7 @@ public void testParseAgencyJSONArrivalsResults_OtherDirection() { rds = new RouteDirectionStop( POI.ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, DEFAULT_ROUTE, - new Direction(1, Direction.HEADSIGN_TYPE_STRING, "Rockcliffe", 1), + new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "Rockcliffe", 1), DEFAULT_STOP, true); long lastUpdateInMs = 1576984339000L; // December 21, 2019 10:12:19 PM GMT-05:00 @@ -160,7 +160,7 @@ public void testParseAgencyJSONArrivalsResults_OneDirection() { rds = new RouteDirectionStop( POI.ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, DEFAULT_ROUTE, - new Direction(1, Direction.HEADSIGN_TYPE_STRING, "Greenboro", 1), + new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "Greenboro", 1), DEFAULT_STOP, false); long lastUpdateInMs = 1576984339000L; // December 21, 2019 10:12:19 PM GMT-05:00 @@ -202,7 +202,7 @@ public void testParseAgencyJSONArrivalsResults_TwoDirections() { // stop: Lyon # rds = new RouteDirectionStop( POI.ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, DEFAULT_ROUTE, - new Direction(1, Direction.HEADSIGN_TYPE_STRING, "Tunney's Pasture", 1), + new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "Tunney's Pasture", 1), DEFAULT_STOP, true); long lastUpdateInMs = 1576984320000L; // December 21, 2019 10:12:10 PM GMT-05:00 diff --git a/src/test/java/org/mtransit/android/commons/provider/OneBusAwayProviderTests.java b/src/test/java/org/mtransit/android/commons/provider/OneBusAwayProviderTests.java index 50dcff75..7301ec0d 100644 --- a/src/test/java/org/mtransit/android/commons/provider/OneBusAwayProviderTests.java +++ b/src/test/java/org/mtransit/android/commons/provider/OneBusAwayProviderTests.java @@ -165,6 +165,7 @@ public void testCleanDirectionHeadsignRDS() { String tripHeadsign = "Martin Grv Via Vaughan Metropolitan Ctr"; Direction direction = new Direction( + AUTHORITY, -1, Direction.HEADSIGN_TYPE_STRING, "Martin Grv", @@ -187,6 +188,7 @@ private RouteDirectionStop getRouteDirectionStop(String routeShortName, long rou "color" ); Direction direction = new Direction( + AUTHORITY, directionId, Direction.HEADSIGN_TYPE_STRING, "direction " + directionId, diff --git a/src/test/java/org/mtransit/android/commons/provider/StmInfoApiProviderTests.java b/src/test/java/org/mtransit/android/commons/provider/StmInfoApiProviderTests.java index 6144ca2b..43583cfd 100644 --- a/src/test/java/org/mtransit/android/commons/provider/StmInfoApiProviderTests.java +++ b/src/test/java/org/mtransit/android/commons/provider/StmInfoApiProviderTests.java @@ -45,7 +45,7 @@ public class StmInfoApiProviderTests { private static final String SOURCE_LABEL = "example.org"; private static final Route DEFAULT_ROUTE = new Route(AUTHORITY, 1, "1", "route 1", "color"); - private static final Direction DEFAULT_DIRECTION = new Direction(1, Direction.HEADSIGN_TYPE_STRING, "trip 1", 1); + private static final Direction DEFAULT_DIRECTION = new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, "trip 1", 1); private static final Stop DEFAULT_STOP = new Stop(1, "1", "stop 1", 0, 0, 0, 1); private final Context context = mock(); @@ -582,7 +582,7 @@ public void testParseAgencyJSONMessageResultsNoMessages() { rds = new RouteDirectionStop( POI.ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, new Route(AUTHORITY, 1, routeShortName, "route 1", "color"), - new Direction(1, Direction.HEADSIGN_TYPE_STRING, headsignValue, 1), + new Direction(AUTHORITY, 1, Direction.HEADSIGN_TYPE_STRING, headsignValue, 1), DEFAULT_STOP, false); long newLastUpdateInMs = System.currentTimeMillis(); From d10b5669f8daff813cde79e1d709ae88e7792548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 19 Jan 2026 16:09:08 -0500 Subject: [PATCH 27/36] PR comments :love: --- .../android/commons/provider/gtfs/GtfsRealTimeStorage.kt | 4 ++-- .../vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt b/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt index 9eddab22..9cc08c55 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GtfsRealTimeStorage.kt @@ -32,12 +32,12 @@ object GtfsRealTimeStorage { @JvmStatic @WorkerThread fun getVehicleLocationLastUpdateCode(context: Context, default: Int) = - PreferenceUtils.getPrefLcl(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_CODE, default) + PreferenceUtils.getPrefLcl(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_CODE, default) @JvmStatic @WorkerThread fun saveVehicleLocationLastUpdateCode(context: Context, code: Int) { - PreferenceUtils.savePrefLclSync(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_CODE, code) + PreferenceUtils.savePrefLclSync(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_CODE, code) } // endregion diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 7d1dd389..183d730d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -149,7 +149,7 @@ object GTFSRealTimeVehiclePositionsProvider { ) ?: return null getOkHttpClient(context).newCall(urlRequest).execute().use { response -> GtfsRealTimeStorage.saveVehicleLocationLastUpdateCode(context, response.code) - GtfsRealTimeStorage.saveServiceUpdateLastUpdateMs(context, TimeUtils.currentTimeMillis()) + GtfsRealTimeStorage.saveVehicleLocationLastUpdateMs(context, TimeUtils.currentTimeMillis()) when (response.code) { HttpURLConnection.HTTP_OK -> { val newLastUpdateInMs = TimeUtils.currentTimeMillis() From 138e3a5adeb226649c2001757b482e9e4ca8086e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Wed, 21 Jan 2026 15:20:16 -0500 Subject: [PATCH 28/36] NextBus compat... --- .../android/commons/data/Direction.java | 4 +- .../mtransit/android/commons/data/Route.java | 1 + .../android/commons/data/RouteDirection.java | 3 +- .../commons/data/RouteDirectionStop.java | 3 +- .../provider/GTFSRealTimeProvider.java | 2 + .../commons/provider/NextBusProvider.java | 252 ++++++++++++------ .../provider/nextbus/NextBusStorage.kt | 64 +++++ .../provider/nextbus/api/NextBusApi.kt | 21 ++ .../nextbus/api/VehicleLocationsResponse.kt | 29 ++ .../GTFSRealTimeVehiclePositionsProvider.kt | 32 ++- .../NextBusVehicleLocationsProvider.kt | 242 +++++++++++++++++ .../VehicleLocationProviderContract.kt | 8 + .../vehiclelocations/model/VehicleLocation.kt | 4 +- .../commons/provider/NextBusProviderTest.kt | 4 +- 14 files changed, 573 insertions(+), 96 deletions(-) create mode 100644 src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt create mode 100644 src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt create mode 100644 src/main/java/org/mtransit/android/commons/provider/nextbus/api/VehicleLocationsResponse.kt diff --git a/src/main/java/org/mtransit/android/commons/data/Direction.java b/src/main/java/org/mtransit/android/commons/data/Direction.java index 65e9626b..cbdbb938 100644 --- a/src/main/java/org/mtransit/android/commons/data/Direction.java +++ b/src/main/java/org/mtransit/android/commons/data/Direction.java @@ -233,8 +233,8 @@ public String getAuthority() { } @NonNull - public String getUUID(@NonNull String authority) { - return POI.POIUtils.getUUID(authority, this.routeId, this.id); + public String getUUID() { + return POI.POIUtils.getUUID(this.authority, this.routeId, this.id); } public long getId() { diff --git a/src/main/java/org/mtransit/android/commons/data/Route.java b/src/main/java/org/mtransit/android/commons/data/Route.java index 500d4aa6..6c48ccef 100644 --- a/src/main/java/org/mtransit/android/commons/data/Route.java +++ b/src/main/java/org/mtransit/android/commons/data/Route.java @@ -221,6 +221,7 @@ public void resetUUID() { this.uuid = null; } + @SuppressWarnings("unused") // main app only @NonNull public Collection getAllUUIDs() { return Arrays.asList( diff --git a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java index f0cdc54c..22726fee 100644 --- a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java +++ b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java @@ -41,9 +41,10 @@ public RouteDirection( @NonNull public String getUUID() { - return direction.getUUID(getAuthority()); + return direction.getUUID(); } + @SuppressWarnings("unused") // main app only @NonNull public Collection getAllUUIDs() { return Arrays.asList( diff --git a/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java b/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java index 734ae39d..a031f512 100644 --- a/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java +++ b/src/main/java/org/mtransit/android/commons/data/RouteDirectionStop.java @@ -315,9 +315,10 @@ public Direction getDirection() { @NonNull public String getRouteDirectionUUID() { - return direction.getUUID(getAuthority()); + return direction.getUUID(); } + @SuppressWarnings("unused") // main app only @NonNull public Collection getRouteDirectionAllUUIDs() { return Arrays.asList( diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 13590a1d..81ee1a37 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -500,6 +500,7 @@ private static String getAGENCY_TIME_ZONE(@NonNull Context context) { return agencyTimeZone; } + @SuppressWarnings("unused") @Override public long getMinDurationBetweenVehicleLocationRefreshInMs(boolean inFocus) { return GTFSRealTimeVehiclePositionsProvider.getMinDurationBetweenRefreshInMs(this, inFocus); @@ -1566,6 +1567,7 @@ public UriMatcher getURI_MATCHER() { } @NonNull + @Override public String getAuthority() { return getAUTHORITY(requireContextCompat()); } diff --git a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java index 038bbdd1..39c060f3 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java @@ -20,7 +20,6 @@ import org.mtransit.android.commons.LocaleUtils; import org.mtransit.android.commons.MTLog; import org.mtransit.android.commons.NetworkUtils; -import org.mtransit.android.commons.PreferenceUtils; import org.mtransit.android.commons.R; import org.mtransit.android.commons.SqlUtils; import org.mtransit.android.commons.TimeUtils; @@ -40,11 +39,18 @@ import org.mtransit.android.commons.provider.agency.AgencyUtils; import org.mtransit.android.commons.provider.common.MTContentProvider; import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; +import org.mtransit.android.commons.provider.nextbus.NextBusStorage; +import org.mtransit.android.commons.provider.nextbus.api.NextBusApi; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateCleaner; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProvider; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; import org.mtransit.android.commons.provider.status.StatusProvider; import org.mtransit.android.commons.provider.status.StatusProviderContract; +import org.mtransit.android.commons.provider.vehiclelocations.NextBusVehicleLocationsProvider; +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationDbHelper; +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProvider; +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract; +import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation; import org.mtransit.commons.CleanUtils; import org.mtransit.commons.Cleaner; import org.mtransit.commons.CollectionUtils; @@ -64,6 +70,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -74,13 +81,17 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; +import retrofit2.Retrofit; // https://retro.umoiq.com/xmlFeedDocs/NextBusXMLFeed.pdf // https://retro.umoiq.com/service/publicXMLFeed?command=agencyList // https://retro.umoiq.com/service/publicJSONFeed?command=agencyList // DO NOT MOVE: referenced in modules AndroidManifest.xml @SuppressLint("Registered") -public class NextBusProvider extends MTContentProvider implements ServiceUpdateProviderContract, StatusProviderContract { +public class NextBusProvider extends MTContentProvider implements + VehicleLocationProviderContract, + ServiceUpdateProviderContract, + StatusProviderContract { private static final String LOG_TAG = NextBusProvider.class.getSimpleName(); @@ -90,16 +101,12 @@ public String getLogTag() { return LOG_TAG; } - /** - * Override if multiple {@link NextBusProvider} implementations in same app. - */ - private static final String PREF_KEY_AGENCY_LAST_UPDATE_MS = NextBusDbHelper.PREF_KEY_AGENCY_LAST_UPDATE_MS; - @NonNull public static UriMatcher getNewUriMatcher(@NonNull String authority) { UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); ServiceUpdateProvider.append(URI_MATCHER, authority); StatusProvider.append(URI_MATCHER, authority); + VehicleLocationProvider.append(URI_MATCHER, authority); return URI_MATCHER; } @@ -166,7 +173,7 @@ private static Uri getAUTHORITY_URI(@NonNull Context context) { * Override if multiple {@link NextBusProvider} implementations in same app. */ @NonNull - private static String getAGENCY_TAG(@NonNull Context context) { + public static String getAGENCY_TAG(@NonNull Context context) { if (agencyTag == null) { agencyTag = context.getResources().getString(R.string.next_bus_agency_tag); } @@ -261,7 +268,7 @@ private static String getTEXT_SECONDARY_BOLD_WORDS(@NonNull Context context) { /** * Override if multiple {@link NextBusProvider} implementations in same app. */ - private static boolean isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(@NonNull Context context) { + public static boolean isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(@NonNull Context context) { if (appendHeadSignValueToRouteTag == null) { appendHeadSignValueToRouteTag = context.getResources().getBoolean(R.bool.next_bus_route_tag_append_headsign_value); } @@ -501,6 +508,77 @@ private static java.util.List getSCHEDULE_HEAD_SIGN_PREDICTIONS_ROUTE_TI return scheduleHeadSignPredictionsRouteTitleReplacement; } + @Nullable + private NextBusApi nextBusApi = null; + + @NonNull + public NextBusApi getNextBusApi(@NonNull Context context) { + if (this.nextBusApi == null) { + this.nextBusApi = createNextBusApi(context); + } + return this.nextBusApi; + } + + @NonNull + private NextBusApi createNextBusApi(@NonNull Context context) { + final Retrofit retrofit = NetworkUtils.makeNewRetrofitWithGson( + NextBusApi.BASE_HOST_URL, + context, + NetworkUtils.makeNewOkHttpClientWithInterceptor(context) + ); + return retrofit.create(NextBusApi.class); + } + + @SuppressWarnings("unused") + @Override + public long getMinDurationBetweenVehicleLocationRefreshInMs(boolean inFocus) { + return NextBusVehicleLocationsProvider.getMinDurationBetweenRefreshInMs(inFocus); + } + + @Override + public long getVehicleLocationMaxValidityInMs() { + return NextBusVehicleLocationsProvider.getMaxValidityInMs(); + } + + @Override + public long getVehicleLocationValidityInMs(boolean inFocus) { + return NextBusVehicleLocationsProvider.getValidityInMs(inFocus); + } + + @Override + public void cacheVehicleLocations(@NonNull List newVehicleLocations) { + VehicleLocationProvider.cacheVehicleLocationsS(this, newVehicleLocations); + } + + @Override + public @Nullable List getCachedVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { + return NextBusVehicleLocationsProvider.getCached(this, vehicleLocationFilter); + } + + @Override + public @Nullable List getNewVehicleLocations(@NonNull VehicleLocationProviderContract.Filter vehicleLocationFilter) { + return NextBusVehicleLocationsProvider.getNew(this, vehicleLocationFilter); + } + + @Override + public boolean deleteCachedVehicleLocation(int vehicleLocationId) { + return VehicleLocationProvider.deleteCachedVehicleLocation(this, vehicleLocationId); + } + + public boolean deleteAllCachedVehicleLocations() { + return VehicleLocationProvider.deleteAllCachedVehicleLocations(this); + } + + @Override + public boolean purgeUselessCachedVehicleLocations() { + return VehicleLocationProvider.purgeUselessCachedVehicleLocations(this); + } + + @Override + public @NonNull String getVehicleLocationDbTableName() { + return NextBusDbHelper.T_NEXT_BUS_VEHICLE_LOCATION; + } + private static final long SERVICE_UPDATE_MIN_DURATION_BETWEEN_REFRESH_IN_MS = TimeUnit.MINUTES.toMillis(10L); private static final long SERVICE_UPDATE_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = TimeUnit.MINUTES.toMillis(1L); @@ -546,27 +624,28 @@ public void cacheServiceUpdates(@NonNull ArrayList newServiceUpda @Nullable @Override public ArrayList getCachedServiceUpdates(@NonNull ServiceUpdateProviderContract.Filter serviceUpdateFilter) { + final Context context = requireContextCompat(); if ((serviceUpdateFilter.getPoi() instanceof RouteDirectionStop)) { - return getCachedServiceUpdates((RouteDirectionStop) serviceUpdateFilter.getPoi()); + return getCachedServiceUpdates(context, (RouteDirectionStop) serviceUpdateFilter.getPoi()); } else if ((serviceUpdateFilter.getRouteDirection() != null)) { // depends on agency routeTag: Toronto TTC: YES, Laval STL: NO - return getCachedServiceUpdates(serviceUpdateFilter.getRouteDirection()); + return getCachedServiceUpdates(context, serviceUpdateFilter.getRouteDirection()); } else if ((serviceUpdateFilter.getRoute() != null)) { // depends on agency routeTag: Toronto TTC: YES, Laval STL: NO - return getCachedServiceUpdates(serviceUpdateFilter.getRoute()); + return getCachedServiceUpdates(context, serviceUpdateFilter.getRoute()); } else { MTLog.w(this, "getCachedServiceUpdates() > no service update (poi null or not RDS or no route)"); return null; } } - private ArrayList getCachedServiceUpdates(@NonNull RouteDirectionStop rds) { - final Map targetUUIDs = getServiceUpdateTargetUUIDs(rds); + private ArrayList getCachedServiceUpdates(@NonNull Context context, @NonNull RouteDirectionStop rds) { + final Map targetUUIDs = getServiceUpdateTargetUUIDs(context, rds); ArrayList cachedServiceUpdates = ServiceUpdateProvider.getCachedServiceUpdatesS(this, targetUUIDs.keySet()); enhanceRDServiceUpdateForStop(cachedServiceUpdates, targetUUIDs); return cachedServiceUpdates; } - private ArrayList getCachedServiceUpdates(@NonNull RouteDirection rd) { - final Map targetUUIDs = getServiceUpdateTargetUUIDs(rd); + private ArrayList getCachedServiceUpdates(@NonNull Context context, @NonNull RouteDirection rd) { + final Map targetUUIDs = getServiceUpdateTargetUUIDs(context, rd); ArrayList cachedServiceUpdates = ServiceUpdateProvider.getCachedServiceUpdatesS(this, targetUUIDs.keySet()); enhanceRDServiceUpdateForStop(cachedServiceUpdates, targetUUIDs); // if (org.mtransit.commons.Constants.DEBUG) { @@ -580,8 +659,8 @@ private ArrayList getCachedServiceUpdates(@NonNull RouteDirection return cachedServiceUpdates; } - private ArrayList getCachedServiceUpdates(@NonNull Route route) { - final Map targetUUIDs = getServiceUpdateTargetUUIDs(route); + private ArrayList getCachedServiceUpdates(@NonNull Context context, @NonNull Route route) { + final Map targetUUIDs = getServiceUpdateTargetUUIDs(context, route); ArrayList cachedServiceUpdates = ServiceUpdateProvider.getCachedServiceUpdatesS(this, targetUUIDs.keySet()); enhanceRDServiceUpdateForStop(cachedServiceUpdates, targetUUIDs); // if (org.mtransit.commons.Constants.DEBUG) { @@ -610,56 +689,59 @@ private void enhanceRDServiceUpdateForStop(@Nullable ArrayList se } @NonNull - private Map getServiceUpdateTargetUUIDs(@NonNull RouteDirectionStop rds) { + private Map getServiceUpdateTargetUUIDs(@NonNull Context context, @NonNull RouteDirectionStop rds) { final HashMap targetUUIDs = new HashMap<>(); - targetUUIDs.put(getServiceUpdateAgencyTargetUUID(rds.getAuthority()), rds.getAuthority()); + final String agencyTag = getAGENCY_TAG(context); + targetUUIDs.put(getAgencyTargetUUID(agencyTag), rds.getAuthority()); if (!isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(requireContextCompat())) { - targetUUIDs.put(getServiceUpdateAgencyRouteTagTargetUUID(rds.getAuthority(), getRouteTag(rds.getRoute(), null)), rds.getRoute().getUUID()); + targetUUIDs.put(getAgencyRouteTagTargetUUID(agencyTag, getRouteTag(rds.getRoute(), null)), rds.getRoute().getUUID()); } else { // STLaval - targetUUIDs.put(getServiceUpdateAgencyRouteTagTargetUUID(rds.getAuthority(), getRouteTag(rds)), rds.getRouteDirectionUUID()); + targetUUIDs.put(getAgencyRouteTagTargetUUID(agencyTag, getRouteTag(rds)), rds.getRouteDirectionUUID()); } - targetUUIDs.put(getAgencyRouteStopTagTargetUUID(rds), rds.getUUID()); + targetUUIDs.put(getAgencyRouteStopTagTargetUUID(context, rds), rds.getUUID()); return targetUUIDs; } @NonNull - private Map getServiceUpdateTargetUUIDs(@NonNull RouteDirection rd) { + private Map getServiceUpdateTargetUUIDs(@NonNull Context context, @NonNull RouteDirection rd) { final HashMap targetUUIDs = new HashMap<>(); - targetUUIDs.put(getServiceUpdateAgencyTargetUUID(rd.getAuthority()), rd.getAuthority()); - if (!isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(requireContextCompat())) { - targetUUIDs.put(getServiceUpdateAgencyRouteTagTargetUUID(rd.getAuthority(), getRouteTag(rd.getRoute(), null)), rd.getRoute().getUUID()); + final String agencyTag = getAGENCY_TAG(context); + targetUUIDs.put(getAgencyTargetUUID(agencyTag), rd.getAuthority()); + if (!isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(context)) { + targetUUIDs.put(getAgencyRouteTagTargetUUID(agencyTag, getRouteTag(rd.getRoute(), null)), rd.getRoute().getUUID()); } else { // STLaval - targetUUIDs.put(getServiceUpdateAgencyRouteTagTargetUUID(rd.getAuthority(), getRouteTag(rd)), rd.getUUID()); + targetUUIDs.put(getAgencyRouteTagTargetUUID(agencyTag, getRouteTag(rd)), rd.getUUID()); } return targetUUIDs; } @NonNull - private Map getServiceUpdateTargetUUIDs(@NonNull Route route) { + private Map getServiceUpdateTargetUUIDs(@NonNull Context context, @NonNull Route route) { final HashMap targetUUIDs = new HashMap<>(); - targetUUIDs.put(getServiceUpdateAgencyTargetUUID(route.getAuthority()), route.getAuthority()); + final String agencyTag = getAGENCY_TAG(context); + targetUUIDs.put(getAgencyTargetUUID(agencyTag), route.getAuthority()); if (!isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(requireContextCompat())) { - targetUUIDs.put(getServiceUpdateAgencyRouteTagTargetUUID(route.getAuthority(), getRouteTag(route, null)), route.getUUID()); + targetUUIDs.put(getAgencyRouteTagTargetUUID(agencyTag, getRouteTag(route, null)), route.getUUID()); } // ELSE // STLaval return targetUUIDs; } - private String getAgencyRouteStopTagTargetUUID(@NonNull RouteDirectionStop rds) { - return getAgencyRouteStopTagTargetUUID(rds.getAuthority(), getRouteTag(rds), getStopTag(rds)); + private String getAgencyRouteStopTagTargetUUID(@NonNull Context context, @NonNull RouteDirectionStop rds) { + return getAgencyRouteStopTagTargetUUID(getAGENCY_TAG(context), getRouteTag(rds), getStopTag(rds)); } @NonNull - private String getRouteTag(@NonNull RouteDirectionStop rds) { + public String getRouteTag(@NonNull RouteDirectionStop rds) { return getRouteTag(rds.getRoute(), rds.getDirection()); } @NonNull - private String getRouteTag(@NonNull RouteDirection rd) { + public String getRouteTag(@NonNull RouteDirection rd) { return getRouteTag(rd.getRoute(), rd.getDirection()); } @NonNull - private String getRouteTag(@NonNull Route route, @Nullable Direction direction) { + public String getRouteTag(@NonNull Route route, @Nullable Direction direction) { final StringBuilder sb = new StringBuilder(); sb.append(route.getShortName()); final Context context = requireContextCompat(); @@ -718,18 +800,18 @@ private String cleanStopTag(@NonNull String stopTag) { } @NonNull - protected static String getServiceUpdateAgencyRouteTagTargetUUID(@NonNull String agencyAuthority, @NonNull String routeTag) { - return POI.POIUtils.getUUID(agencyAuthority, routeTag); + public static String getAgencyRouteStopTagTargetUUID(@NonNull String agencyTag, @NonNull String routeTag, @NonNull String stopTag) { + return POI.POIUtils.getUUID(agencyTag, routeTag, stopTag); } @NonNull - protected static String getAgencyRouteStopTagTargetUUID(@NonNull String agencyAuthority, @NonNull String routeTag, @NonNull String stopTag) { - return POI.POIUtils.getUUID(agencyAuthority, routeTag, stopTag); + public static String getAgencyRouteTagTargetUUID(@NonNull String agencyTag, @NonNull String routeTag) { + return POI.POIUtils.getUUID(agencyTag, routeTag); } @NonNull - protected static String getServiceUpdateAgencyTargetUUID(@NonNull String agencyAuthority) { - return POI.POIUtils.getUUID(agencyAuthority); + public static String getAgencyTargetUUID(@NonNull String agencyTag) { + return POI.POIUtils.getUUID(agencyTag); } @Override @@ -750,43 +832,44 @@ public boolean deleteCachedServiceUpdate(@NonNull String targetUUID, @NonNull St @Nullable @Override public ArrayList getNewServiceUpdates(@NonNull ServiceUpdateProviderContract.Filter serviceUpdateFilter) { + final Context context = requireContextCompat(); if ((serviceUpdateFilter.getPoi() instanceof RouteDirectionStop)) { - return getNewServiceUpdates((RouteDirectionStop) serviceUpdateFilter.getPoi(), serviceUpdateFilter.isInFocusOrDefault()); + return getNewServiceUpdates(context, (RouteDirectionStop) serviceUpdateFilter.getPoi(), serviceUpdateFilter.isInFocusOrDefault()); } else if ((serviceUpdateFilter.getRouteDirection() != null)) { // depends on agency routeTag: Toronto TTC: YES, Laval STL: NO - return getNewServiceUpdates(serviceUpdateFilter.getRouteDirection(), serviceUpdateFilter.isInFocusOrDefault()); + return getNewServiceUpdates(context, serviceUpdateFilter.getRouteDirection(), serviceUpdateFilter.isInFocusOrDefault()); } else if ((serviceUpdateFilter.getRoute() != null)) { // depends on agency routeTag: Toronto TTC: YES, Laval STL: NO - return getNewServiceUpdates(serviceUpdateFilter.getRoute(), serviceUpdateFilter.isInFocusOrDefault()); + return getNewServiceUpdates(context, serviceUpdateFilter.getRoute(), serviceUpdateFilter.isInFocusOrDefault()); } else { MTLog.w(this, "getNewServiceUpdates() > no service update (poi null or not RDS or no route)"); return null; } } - private ArrayList getNewServiceUpdates(@NonNull RouteDirectionStop rds, boolean inFocus) { + private ArrayList getNewServiceUpdates(@NonNull Context context, @NonNull RouteDirectionStop rds, boolean inFocus) { updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), inFocus); - ArrayList cachedServiceUpdates = getCachedServiceUpdates(rds); + ArrayList cachedServiceUpdates = getCachedServiceUpdates(context, rds); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - cachedServiceUpdates = makeServiceUpdateNoneList(this, getServiceUpdateAgencyTargetUUID(rds.getAuthority()), AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, getAgencyTargetUUID(rds.getAuthority()), AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, Collections.emptyMap()); } return cachedServiceUpdates; } - private ArrayList getNewServiceUpdates(@NonNull RouteDirection rd, boolean inFocus) { + private ArrayList getNewServiceUpdates(@NonNull Context context, @NonNull RouteDirection rd, boolean inFocus) { updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), inFocus); - ArrayList cachedServiceUpdates = getCachedServiceUpdates(rd); + ArrayList cachedServiceUpdates = getCachedServiceUpdates(context, rd); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - cachedServiceUpdates = makeServiceUpdateNoneList(this, getServiceUpdateAgencyTargetUUID(rd.getAuthority()), AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, getAgencyTargetUUID(rd.getAuthority()), AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, Collections.emptyMap()); } return cachedServiceUpdates; } - private ArrayList getNewServiceUpdates(@NonNull Route route, boolean inFocus) { + private ArrayList getNewServiceUpdates(@NonNull Context context, @NonNull Route route, boolean inFocus) { updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), inFocus); - ArrayList cachedServiceUpdates = getCachedServiceUpdates(route); + ArrayList cachedServiceUpdates = getCachedServiceUpdates(context, route); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - cachedServiceUpdates = makeServiceUpdateNoneList(this, getServiceUpdateAgencyTargetUUID(route.getAuthority()), AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, getAgencyTargetUUID(route.getAuthority()), AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, Collections.emptyMap()); } return cachedServiceUpdates; @@ -795,7 +878,7 @@ private ArrayList getNewServiceUpdates(@NonNull Route route, bool private static final String AGENCY_SOURCE_ID = "next_bus_com_messages"; private void updateAgencyServiceUpdateDataIfRequired(@NonNull Context context, boolean inFocus) { - long lastUpdateInMs = PreferenceUtils.getPrefLcl(context, PREF_KEY_AGENCY_LAST_UPDATE_MS, 0L); + long lastUpdateInMs = NextBusStorage.getServiceUpdateLastUpdateMs(context, 0L); long minUpdateMs = Math.min(getServiceUpdateMaxValidityInMs(), getServiceUpdateValidityInMs(inFocus)); long nowInMs = TimeUtils.currentTimeMillis(); if (lastUpdateInMs + minUpdateMs > nowInMs) { @@ -805,7 +888,7 @@ private void updateAgencyServiceUpdateDataIfRequired(@NonNull Context context, b } private synchronized void updateAgencyServiceUpdateDataIfRequiredSync(@NonNull Context context, long lastUpdateInMs, boolean inFocus) { - if (PreferenceUtils.getPrefLcl(context, PREF_KEY_AGENCY_LAST_UPDATE_MS, 0L) > lastUpdateInMs) { + if (NextBusStorage.getServiceUpdateLastUpdateMs(context, 0L) > lastUpdateInMs) { return; // too late, another thread already updated } long nowInMs = TimeUtils.currentTimeMillis(); @@ -833,7 +916,7 @@ private void updateAllAgencyServiceUpdateDataFromWWW(@NonNull Context context, b deleteAllAgencyServiceUpdateData(); } cacheServiceUpdates(newServiceUpdates); - PreferenceUtils.savePrefLclSync(context, PREF_KEY_AGENCY_LAST_UPDATE_MS, nowInMs); + NextBusStorage.saveServiceUpdateLastUpdateMs(context, nowInMs); } // else keep whatever we have until max validity reached } @@ -981,15 +1064,15 @@ public POIStatus getCachedStatus(@NonNull StatusProviderContract.Filter statusFi MTLog.w(this, "getNewStatus() > Can't find new schedule w/o schedule filter!"); return null; } - Schedule.ScheduleStatusFilter scheduleStatusFilter = (Schedule.ScheduleStatusFilter) statusFilter; - RouteDirectionStop rds = scheduleStatusFilter.getRouteDirectionStop(); - String targetUUID = getAgencyRouteStopTagTargetUUID(rds); - POIStatus cachedStatus = StatusProvider.getCachedStatusS(this, targetUUID); + final Schedule.ScheduleStatusFilter scheduleStatusFilter = (Schedule.ScheduleStatusFilter) statusFilter; + final RouteDirectionStop rds = scheduleStatusFilter.getRouteDirectionStop(); + final String targetUUID = getAgencyRouteStopTagTargetUUID(requireContextCompat(), rds); + final POIStatus cachedStatus = StatusProvider.getCachedStatusS(this, targetUUID); if (cachedStatus != null) { cachedStatus.setTargetUUID(rds.getUUID()); // target RDS UUID instead of custom NextBus Route & Stop tags if (rds.isNoPickup()) { if (cachedStatus instanceof Schedule) { - Schedule schedule = (Schedule) cachedStatus; + final Schedule schedule = (Schedule) cachedStatus; schedule.setNoPickup(true); // API doesn't know about "descent only" } } @@ -1141,6 +1224,12 @@ public UriMatcher getURI_MATCHER() { return getURIMATCHER(requireContextCompat()); } + @NonNull + @Override + public String getAuthority() { + return getAUTHORITY(requireContextCompat()); + } + @NonNull @Override public Uri getAuthorityUri() { @@ -1175,6 +1264,10 @@ public Cursor queryMT(@NonNull Uri uri, @Nullable String[] projection, @Nullable if (cursor != null) { return cursor; } + cursor = VehicleLocationProvider.queryS(this, uri, selection); + if (cursor != null) { + return cursor; + } throw new IllegalArgumentException(String.format("Unknown URI (query): '%s'", uri)); } @@ -1189,6 +1282,10 @@ public String getTypeMT(@NonNull Uri uri) { if (type != null) { return type; } + type = VehicleLocationProvider.getTypeS(this, uri); + if (type != null) { + return type; + } throw new IllegalArgumentException(String.format("Unknown URI (type): '%s'", uri)); } @@ -1255,7 +1352,7 @@ public String getLogTag() { private final HashMap statuses = new HashMap<>(); private final NextBusProvider provider; - private final String authority; + private final String agencyTag; @Nullable private final String sourceLabel; private final long lastUpdateInMs; @@ -1264,7 +1361,7 @@ public String getLogTag() { NextBusPredictionsDataHandler(@NonNull NextBusProvider provider, @Nullable String sourceLabel, long lastUpdateInMs, @NonNull String localTimeZoneId) { this.provider = provider; - this.authority = NextBusProvider.getTARGET_AUTHORITY(this.provider.requireContextCompat()); + this.agencyTag = NextBusProvider.getAGENCY_TAG(this.provider.requireContextCompat()); this.sourceLabel = sourceLabel; this.lastUpdateInMs = lastUpdateInMs; this.localTimeZoneId = localTimeZoneId; @@ -1348,7 +1445,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc if (TextUtils.isEmpty(this.currentRouteTag) || TextUtils.isEmpty(this.currentStopTag)) { return; } - String targetUUID = NextBusProvider.getAgencyRouteStopTagTargetUUID(this.authority, this.currentRouteTag, this.currentStopTag); + String targetUUID = NextBusProvider.getAgencyRouteStopTagTargetUUID(this.agencyTag, this.currentRouteTag, this.currentStopTag); Schedule status = this.statuses.get(targetUUID); if (status == null) { status = new Schedule( @@ -1711,14 +1808,14 @@ public void endElement(String uri, String localName, String qName) throws SAXExc final HashSet currentRouteConfiguredForMessageRoute = this.currentRouteConfiguredForMessage.get(routeTag); final int stopCount = currentRouteConfiguredForMessageRoute == null ? 0 : currentRouteConfiguredForMessageRoute.size(); if (stopCount == 0) { - final String routeTargetUUID = NextBusProvider.getServiceUpdateAgencyRouteTagTargetUUID(this.authority, routeTag); + final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.authority, routeTag); final int severity = findRouteSeverity(); //noinspection UnnecessaryLocalVariable final String title = routeTag; addServiceUpdates(routeTargetUUID, severity, title); } else { for (String stopTag : currentRouteConfiguredForMessageRoute) { - final String routeStopTargetUUID = NextBusProvider.getAgencyRouteStopTagTargetUUID(this.authority, routeTag, stopTag); + final String routeStopTargetUUID = NextBusProvider.getAgencyRouteStopTagTargetUUID(this.agencyTag, routeTag, stopTag); final String title = stopCount < 10 ? this.currentStopTabAndTitle.getOrDefault(stopTag, stopTag) : routeTag; @@ -1726,7 +1823,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc addServiceUpdates(routeStopTargetUUID, severity, title); } // ADD duplicates for routeTag (UI will only show it once) - final String routeTargetUUID = NextBusProvider.getServiceUpdateAgencyRouteTagTargetUUID(this.authority, routeTag); + final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.authority, routeTag); final int severity = ServiceUpdate.SEVERITY_INFO_RELATED_POI; //noinspection UnnecessaryLocalVariable final String title = routeTag; @@ -1734,12 +1831,12 @@ public void endElement(String uri, String localName, String qName) throws SAXExc } } } else if (this.currentRouteTag != null) { - final String routeTargetUUID = NextBusProvider.getServiceUpdateAgencyRouteTagTargetUUID(this.authority, this.currentRouteTag); + final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.authority, this.currentRouteTag); final String title = this.currentRouteTag; final int severity = findAgencySeverity(); addServiceUpdates(routeTargetUUID, severity, title); } else if (this.currentRouteAll) { // AGENCY - final String agencyTargetUUID = NextBusProvider.getServiceUpdateAgencyTargetUUID(this.authority); + final String agencyTargetUUID = NextBusProvider.getAgencyTargetUUID(this.authority); final String title = this.agencyTag.toUpperCase(Locale.ROOT); final int severity = findAgencySeverity(); addServiceUpdates(agencyTargetUUID, severity, title); @@ -1877,10 +1974,12 @@ public String getLogTag() { */ protected static final String DB_NAME = "nextbus.db"; - /** - * Override if multiple {@link NextBusDbHelper} implementations in same app. - */ - static final String PREF_KEY_AGENCY_LAST_UPDATE_MS = "pNextBusMessagesLastUpdate"; + static final String T_NEXT_BUS_VEHICLE_LOCATION = VehicleLocationDbHelper.T_VEHICLE_LOCATION; + + private static final String T_NEXT_BUS_VEHICLE_LOCATION_SQL_CREATE = VehicleLocationDbHelper.getSqlCreateBuilder( + T_NEXT_BUS_VEHICLE_LOCATION).build(); + + private static final String T_NEXT_BUS_VEHICLE_LOCATION_SQL_DROP = SqlUtils.getSQLDropIfExistsQuery(T_NEXT_BUS_VEHICLE_LOCATION); static final String T_NEXT_BUS_SERVICE_UPDATE = ServiceUpdateProvider.ServiceUpdateDbHelper.T_SERVICE_UPDATE; @@ -1904,6 +2003,7 @@ public static int getDbVersion(@NonNull Context context) { if (dbVersion < 0) { dbVersion = context.getResources().getInteger(R.integer.next_bus_db_version); dbVersion++; // add "service_update.original_id" column + dbVersion++; // add "vehicle_location" table } return dbVersion; } @@ -1925,7 +2025,8 @@ public void onCreateMT(@NonNull SQLiteDatabase db) { public void onUpgradeMT(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(T_NEXT_BUS_SERVICE_UPDATE_SQL_DROP); db.execSQL(T_NEXT_BUS_STATUS_SQL_DROP); - PreferenceUtils.savePrefLclSync(this.context, PREF_KEY_AGENCY_LAST_UPDATE_MS, 0L); + db.execSQL(T_NEXT_BUS_VEHICLE_LOCATION_SQL_DROP); + NextBusStorage.saveServiceUpdateLastUpdateMs(context, 0L); initAllDbTables(db); } @@ -1936,6 +2037,7 @@ public boolean isDbExist(@NonNull Context context) { private void initAllDbTables(@NonNull SQLiteDatabase db) { db.execSQL(T_NEXT_BUS_SERVICE_UPDATE_SQL_CREATE); db.execSQL(T_NEXT_BUS_STATUS_SQL_CREATE); + db.execSQL(T_NEXT_BUS_VEHICLE_LOCATION_SQL_CREATE); } } } diff --git a/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt b/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt new file mode 100644 index 00000000..2945cf4d --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt @@ -0,0 +1,64 @@ +package org.mtransit.android.commons.provider.nextbus + +import android.content.Context +import androidx.annotation.WorkerThread +import org.mtransit.android.commons.PreferenceUtils + +object NextBusStorage { + + // region Vehicle location + + /** + * Override if multiple [org.mtransit.android.commons.provider.NextBusProvider] implementations in same app. + */ + private const val PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_MS = "pNextBusVehicleLocationsLastUpdate" + + @JvmStatic + @WorkerThread + fun getVehicleLocationLastUpdateMs(context: Context, default: Long) = + PreferenceUtils.getPrefLcl(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_MS, default) + + @JvmStatic + @WorkerThread + fun saveVehicleLocationLastUpdateMs(context: Context, lastUpdateInMs: Long) { + PreferenceUtils.savePrefLclSync(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_MS, lastUpdateInMs) + } + + /** + * Override if multiple [org.mtransit.android.commons.provider.NextBusProvider] implementations in same app. + */ + private const val PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_CODE = "pNextBusVehicleLocationLastUpdateCode" + + @JvmStatic + @WorkerThread + fun getVehicleLocationLastUpdateCode(context: Context, default: Int) = + PreferenceUtils.getPrefLcl(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_CODE, default) + + @JvmStatic + @WorkerThread + fun saveVehicleLocationLastUpdateCode(context: Context, code: Int) { + PreferenceUtils.savePrefLclSync(context, PREF_KEY_VEHICLE_LOCATION_LAST_UPDATE_CODE, code) + } + + // endregion + + // region Service update (messages) + + /** + * Override if multiple [org.mtransit.android.commons.provider.NextBusProvider] implementations in same app. + */ + private const val PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_MS = "pNextBusMessagesLastUpdate" + + @JvmStatic + @WorkerThread + fun getServiceUpdateLastUpdateMs(context: Context, default: Long) = + PreferenceUtils.getPrefLcl(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_MS, default) + + @JvmStatic + @WorkerThread + fun saveServiceUpdateLastUpdateMs(context: Context, lastUpdateInMs: Long) { + PreferenceUtils.savePrefLclSync(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_MS, lastUpdateInMs) + } + + //endregion +} diff --git a/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt b/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt new file mode 100644 index 00000000..59ec70c1 --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt @@ -0,0 +1,21 @@ +package org.mtransit.android.commons.provider.nextbus.api + +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface NextBusApi { + + companion object { + const val BASE_HOST_URL = "https://retro.umoiq.com/service/" + } + + // https://developers.google.com/youtube/v3/docs/channels/list + @GET("publicJSONFeed") + fun getVehicleLocations( + @Query("command") command: String = "vehicleLocations", + @Query("a") agencyTag: String, + @Query("r") routeTag: String? = null, + @Query("t") lastTimestamp: Long? = 0, // 0 == all (avoid error in JSON response) + ): Call +} diff --git a/src/main/java/org/mtransit/android/commons/provider/nextbus/api/VehicleLocationsResponse.kt b/src/main/java/org/mtransit/android/commons/provider/nextbus/api/VehicleLocationsResponse.kt new file mode 100644 index 00000000..0df8bfce --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/provider/nextbus/api/VehicleLocationsResponse.kt @@ -0,0 +1,29 @@ +package org.mtransit.android.commons.provider.nextbus.api + +import com.google.gson.annotations.SerializedName + +data class VehicleLocationsResponse( + @SerializedName("vehicle") + val vehicle: List?, +) { + data class Vehicle( + @SerializedName("id") + val id: String?, + @SerializedName("routeTag") + val routeTag: String?, + @SerializedName("dirTag") + val dirTag: String?, + @SerializedName("lat") + val lat: Double?, + @SerializedName("lon") + val lon: Double?, + @SerializedName("secsSinceReport") + val secsSinceReport: Int?, + @SerializedName("predictable") + val predictable: Boolean?, + @SerializedName("heading") + val heading: Int?, + @SerializedName("speedKmHr") + val speedKmHr: Double?, + ) +} diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 183d730d..86e09717 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -34,13 +34,19 @@ import kotlin.time.Duration.Companion.seconds object GTFSRealTimeVehiclePositionsProvider { val VEHICLE_LOCATION_MAX_VALIDITY_IN_MS = 1.hours.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_MS = 10.minutes.inWholeMilliseconds val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 10.seconds.inWholeMilliseconds - val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 3.minutes.inWholeMilliseconds // UNUSED? - val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds // UNUSED? + @Suppress("unused") + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 3.minutes.inWholeMilliseconds + + @Suppress("unused") + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds + + @Suppress("unused") @JvmStatic - fun GTFSRealTimeProvider.getMinDurationBetweenRefreshInMs(inFocus: Boolean) = // UNUSED? + fun GTFSRealTimeProvider.getMinDurationBetweenRefreshInMs(inFocus: Boolean) = if (inFocus) VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS.adaptForCachedAPI(this.context) else VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS.adaptForCachedAPI(this.context) @@ -268,18 +274,18 @@ object GTFSRealTimeVehiclePositionsProvider { ) } } -} -private val GTFSRealTimeProvider.routeIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat()) -private val GTFSRealTimeProvider.tripIdCleanupPattern get() = getTripIdCleanupPattern(requireContextCompat()) + private val GTFSRealTimeProvider.routeIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat()) + private val GTFSRealTimeProvider.tripIdCleanupPattern get() = getTripIdCleanupPattern(requireContextCompat()) -private val GTFSRealTimeProvider.agencyTag get() = getAgencyTag(requireContextCompat()) + private val GTFSRealTimeProvider.agencyTag get() = getAgencyTag(requireContextCompat()) -private fun Route.getRouteTag(provider: GTFSRealTimeProvider) = provider.getRouteTag(this) -private fun Direction.getDirectionTag(provider: GTFSRealTimeProvider) = provider.getDirectionTag(this) + private fun Route.getRouteTag(provider: GTFSRealTimeProvider) = provider.getRouteTag(this) + private fun Direction.getDirectionTag(provider: GTFSRealTimeProvider) = provider.getDirectionTag(this) -private fun RouteDirection.getRouteTag(provider: GTFSRealTimeProvider) = this.route.getRouteTag(provider) -private fun RouteDirection.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider) + private fun RouteDirection.getRouteTag(provider: GTFSRealTimeProvider) = this.route.getRouteTag(provider) + private fun RouteDirection.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider) -private fun RouteDirectionStop.getRouteTag(provider: GTFSRealTimeProvider) = this.route.getRouteTag(provider) -private fun RouteDirectionStop.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider) + private fun RouteDirectionStop.getRouteTag(provider: GTFSRealTimeProvider) = this.route.getRouteTag(provider) + private fun RouteDirectionStop.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider) +} diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt index 015802ee..2715451d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt @@ -1,2 +1,244 @@ package org.mtransit.android.commons.provider.vehiclelocations +import android.content.Context +import org.mtransit.android.commons.Constants +import org.mtransit.android.commons.MTLog +import org.mtransit.android.commons.TimeUtils +import org.mtransit.android.commons.data.Route +import org.mtransit.android.commons.data.RouteDirection +import org.mtransit.android.commons.data.RouteDirectionStop +import org.mtransit.android.commons.provider.NextBusProvider +import org.mtransit.android.commons.provider.NextBusProvider.getAGENCY_TAG +import org.mtransit.android.commons.provider.NextBusProvider.getAgencyRouteTagTargetUUID +import org.mtransit.android.commons.provider.NextBusProvider.isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG +import org.mtransit.android.commons.provider.nextbus.NextBusStorage +import org.mtransit.android.commons.provider.nextbus.api.NextBusApi +import org.mtransit.android.commons.provider.nextbus.api.VehicleLocationsResponse +import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProvider.Companion.getCachedVehicleLocationsS +import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation +import java.net.HttpURLConnection +import java.net.SocketException +import java.net.UnknownHostException +import kotlin.math.min +import kotlin.time.Duration +import kotlin.time.Duration.Companion.hours +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +object NextBusVehicleLocationsProvider { + + val VEHICLE_LOCATION_MAX_VALIDITY_IN_MS = 1.hours.inWholeMilliseconds + + val VEHICLE_LOCATION_VALIDITY_IN_MS = 10.minutes.inWholeMilliseconds + val VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS = 5.seconds.inWholeMilliseconds + + @Suppress("unused") + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS = 3.minutes.inWholeMilliseconds + + @Suppress("unused") + val VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS = 1.minutes.inWholeMilliseconds + + @Suppress("unused") + @JvmStatic + fun getMinDurationBetweenRefreshInMs(inFocus: Boolean) = + if (inFocus) VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_FOCUS_IN_MS + else VEHICLE_LOCATION_MIN_DURATION_BETWEEN_REFRESH_IN_MS + + @JvmStatic + fun getValidityInMs(inFocus: Boolean) = + if (inFocus) VEHICLE_LOCATION_VALIDITY_IN_FOCUS_IN_MS + else VEHICLE_LOCATION_VALIDITY_IN_MS + + @JvmStatic + val maxValidityInMs: Long get() = VEHICLE_LOCATION_MAX_VALIDITY_IN_MS + + @JvmStatic + fun NextBusProvider.getCached(vehicleLocationFilter: VehicleLocationProviderContract.Filter): List? = + ((vehicleLocationFilter.poi as? RouteDirectionStop)?.getTargetUUIDs(this) + ?: vehicleLocationFilter.routeDirection?.getTargetUUIDs(this) + ?: vehicleLocationFilter.route?.getTargetUUIDs(this)) + ?.let { targetUUIDs -> + getCached(targetUUIDs, tripIds = null) // NO GTFS trip.id information available + } + + private fun RouteDirectionStop.getTargetUUIDs(provider: NextBusProvider) = buildMap { + if (!provider.isAppendHeadSignValueToRouteTag) { + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.uuid) + } else { // STLaval + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), routeDirectionUUID) + } + } + + private fun RouteDirection.getTargetUUIDs(provider: NextBusProvider) = buildMap { + if (!provider.isAppendHeadSignValueToRouteTag) { + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.uuid) + } else { // STLaval + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), uuid) + } + } + + private fun Route.getTargetUUIDs(provider: NextBusProvider) = buildMap { + if (!provider.isAppendHeadSignValueToRouteTag) { + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), uuid) + } // ELSE // STLaval + } + + fun NextBusProvider.getCached(targetUUIDs: Map, tripIds: List? = null) = buildList { + getCachedVehicleLocationsS(targetUUIDs.keys, tripIds)?.let { + addAll(it) + } + }.map { it.copy(targetUUID = targetUUIDs[it.targetUUID] ?: it.targetUUID) } + + @JvmStatic + fun NextBusProvider.getNew(vehicleLocationFilter: VehicleLocationProviderContract.Filter): List? { + updateAgencyDataIfRequired(vehicleLocationFilter.inFocusOrDefault) + return getCached(vehicleLocationFilter) + } + + private fun NextBusProvider.updateAgencyDataIfRequired(inFocus: Boolean) { + val context = requireContextCompat() + var inFocus = inFocus + val lastUpdateInMs = NextBusStorage.getVehicleLocationLastUpdateMs(context, 0L) + val lastUpdateCode = NextBusStorage.getVehicleLocationLastUpdateCode(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(vehicleLocationMaxValidityInMs, getVehicleLocationValidityInMs(inFocus)) + val nowInMs = TimeUtils.currentTimeMillis() + if (lastUpdateInMs + minUpdateMs > nowInMs) { + return + } + updateAgencyDataIfRequiredSync(lastUpdateInMs, inFocus) + } + + @Synchronized + private fun NextBusProvider.updateAgencyDataIfRequiredSync(lastUpdateInMs: Long, inFocus: Boolean) { + val context = requireContextCompat() + if (NextBusStorage.getVehicleLocationLastUpdateMs(context, 0L) > lastUpdateInMs) { + return // too late, another thread already updated + } + val nowInMs = TimeUtils.currentTimeMillis() + var deleteAllRequired = false + if (lastUpdateInMs + vehicleLocationMaxValidityInMs < nowInMs) { + deleteAllRequired = true // too old to display + } + val minUpdateMs = min(vehicleLocationMaxValidityInMs, getVehicleLocationValidityInMs(inFocus)) + if (deleteAllRequired || lastUpdateInMs + minUpdateMs < nowInMs) { + updateAllAgencyDataFromWWW(context, deleteAllRequired) // try to update + } + } + + private fun NextBusProvider.updateAllAgencyDataFromWWW(context: Context, deleteAllRequired: Boolean) { + var deleteAllDone = false + if (deleteAllRequired) { + deleteAllCachedVehicleLocations() + deleteAllDone = true + } + val newVehicleLocations = loadAgencyDataFromWWW(context) + if (newVehicleLocations != null) { // empty is OK + if (!deleteAllDone) { + deleteAllCachedVehicleLocations() + } + cacheVehicleLocations(newVehicleLocations) + } // else keep whatever we have until max validity reached + } + + private fun NextBusProvider.loadAgencyDataFromWWW(context: Context): List? { + try { + MTLog.i(this, "Loading from '%s' for agency '%s'...", NextBusApi.BASE_HOST_URL, agencyTag) + val response = getNextBusApi(context) + .getVehicleLocations(agencyTag = agencyTag) + .execute() + NextBusStorage.saveVehicleLocationLastUpdateCode(context, response.code()) + val newLastUpdate = TimeUtils.currentTimeMillis().milliseconds + NextBusStorage.saveVehicleLocationLastUpdateMs(context, newLastUpdate.inWholeMilliseconds) + when (response.code()) { + HttpURLConnection.HTTP_OK -> { + val vehicleLocations = mutableListOf() + try { + response.body()?.let { vehicleLocationsResponse -> + vehicleLocationsResponse.vehicle?.forEach { nVehicle -> + if (Constants.DEBUG) { + MTLog.d( + this@NextBusVehicleLocationsProvider, + "loadAgencyDataFromWWW() > NextBus nVehicle: ${nVehicle}." + ) + } + processVehiclePositions(newLastUpdate, nVehicle) + ?.takeIf { it.isNotEmpty() } + ?.let { + vehicleLocations.addAll(it) + } + } + } + } catch (e: Exception) { + MTLog.w(this@NextBusVehicleLocationsProvider, e, "loadAgencyDataFromWWW() > error while parsing NextBus Real Time data!") + } + MTLog.i(this@NextBusVehicleLocationsProvider, "Found %d vehicle locations.", vehicleLocations.size) + if (Constants.DEBUG) { + for (serviceUpdate in vehicleLocations) { + MTLog.d(this@NextBusVehicleLocationsProvider, "loadAgencyDataFromWWW() > vehicle location: $serviceUpdate.") + } + } + return vehicleLocations + } + + else -> { + MTLog.w( + this@NextBusVehicleLocationsProvider, + "ERROR: HTTP URL-Connection Response Code ${response.code()} (Message: ${response.message()})" + ) + return null + } + } + } catch (uhe: UnknownHostException) { + if (MTLog.isLoggable(android.util.Log.DEBUG)) { + MTLog.w(this@NextBusVehicleLocationsProvider, uhe, "No Internet Connection!") + } else { + MTLog.w(this@NextBusVehicleLocationsProvider, "No Internet Connection!") + } + return null + } catch (se: SocketException) { + MTLog.w(this@NextBusVehicleLocationsProvider, se, "No Internet Connection!") + return null + } catch (e: Exception) { // Unknown error + MTLog.e(this@NextBusVehicleLocationsProvider, e, "INTERNAL ERROR: Unknown Exception") + return null + } + } + + private fun NextBusProvider.processVehiclePositions( + newLastUpdate: Duration, + nVehicle: VehicleLocationsResponse.Vehicle, + ): Set? { + val targetUUIDs = parseProviderTargetUUID(nVehicle)?.takeIf { it.isNotBlank() } ?: return null + return setOf( + VehicleLocation( + authority = this.authority, + targetUUID = targetUUIDs, + targetTripId = null, // no GTFS trip.id info returned + lastUpdateInMs = newLastUpdate.inWholeMilliseconds, + maxValidityInMs = this@processVehiclePositions.vehicleLocationMaxValidityInMs, + // + vehicleId = nVehicle.id, + vehicleLabel = null, + reportTimestamp = nVehicle.secsSinceReport?.seconds?.let { newLastUpdate - it }, + latitude = nVehicle.lat?.toFloat() ?: return null, + longitude = nVehicle.lon?.toFloat() ?: return null, + bearing = nVehicle.heading?.toFloat(), + speed = nVehicle.speedKmHr?.toFloat(), + ) + ) + } + + private fun NextBusProvider.parseProviderTargetUUID(nVehicle: VehicleLocationsResponse.Vehicle) = + nVehicle.routeTag?.let { getAgencyRouteTagTargetUUID(agencyTag, it) } + + private val NextBusProvider.agencyTag get() = getAGENCY_TAG(requireContextCompat()) + private val NextBusProvider.isAppendHeadSignValueToRouteTag get() = isAPPEND_HEAD_SIGN_VALUE_TO_ROUTE_TAG(requireContextCompat()) + + private fun Route.getRouteTag(provider: NextBusProvider) = provider.getRouteTag(this, null) + private fun RouteDirection.getRouteTag(provider: NextBusProvider) = provider.getRouteTag(this) + private fun RouteDirectionStop.getRouteTag(provider: NextBusProvider) = provider.getRouteTag(this) +} diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt index ef2bb83f..f930e0b0 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt @@ -54,6 +54,7 @@ interface VehicleLocationProviderContract : ProviderContract { fun getVehicleLocationValidityInMs(inFocus: Boolean): Long + @Suppress("unused") fun getMinDurationBetweenVehicleLocationRefreshInMs(inFocus: Boolean): Long fun cacheVehicleLocations(newVehicleLocations: List) @@ -105,6 +106,13 @@ interface VehicleLocationProviderContract : ProviderContract { var providedEncryptKeysMap: Map? = null private set + @Discouraged("only for logs") + val targetUUIDs: List = buildList { + poi?.uuid?.let { add(it) } + route?.uuid?.let { add(it) } + routeDirection?.uuid?.let { add(it) } + } + @SuppressLint("DiscouragedApi") constructor(poi: POI, tripIds: List? = null) : this(authority = poi.authority, poi = poi, tripIds = tripIds) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 79b9f8bd..3854326a 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -30,8 +30,8 @@ data class VehicleLocation( val reportTimestamp: Duration?, // in SECONDS val latitude: Float, val longitude: Float, - val bearing: Float?, // in degree - val speed: Float?, // m/s OR km/h + val bearing: Float?, // in degree // TODO Int + val speed: Float?, // m/s OR km/h // TODO Int ) { val reportTimestampSec: Long? get() = reportTimestamp?.inWholeSeconds diff --git a/src/test/java/org/mtransit/android/commons/provider/NextBusProviderTest.kt b/src/test/java/org/mtransit/android/commons/provider/NextBusProviderTest.kt index 7bbb3329..bf4c5906 100644 --- a/src/test/java/org/mtransit/android/commons/provider/NextBusProviderTest.kt +++ b/src/test/java/org/mtransit/android/commons/provider/NextBusProviderTest.kt @@ -12,9 +12,9 @@ class NextBusProviderTest { val stopTag = "1" val rdsTargetUUIDs = listOf( - NextBusProvider.getServiceUpdateAgencyTargetUUID(agencyTag), + NextBusProvider.getAgencyTargetUUID(agencyTag), NextBusProvider.getAgencyRouteStopTagTargetUUID(agencyTag, routeTag, stopTag), - NextBusProvider.getServiceUpdateAgencyRouteTagTargetUUID(agencyTag, routeTag), + NextBusProvider.getAgencyRouteTagTargetUUID(agencyTag, routeTag), ) Assert.assertEquals( From 57bfd7c93fa254ee99424a0fffc5d0e86c9828c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 22 Jan 2026 14:05:00 -0500 Subject: [PATCH 29/36] PR comments --- .../android/commons/data/Direction.java | 3 ++- .../mtransit/android/commons/data/POI.java | 5 +---- .../mtransit/android/commons/data/Route.java | 3 ++- .../android/commons/data/RouteDirection.java | 3 ++- .../android/commons/data/ServiceUpdateKtx.kt | 9 ++++----- .../android/commons/data/Targetable.kt | 8 ++++++++ .../provider/GTFSRealTimeProvider.java | 15 ++++++--------- .../commons/provider/NextBusProvider.java | 19 ++++++++----------- .../commons/provider/OCTranspoProvider.java | 9 +++------ .../commons/provider/StmInfoApiProvider.java | 4 ++-- .../provider/StmInfoSubwayProvider.java | 9 +++------ .../GTFSRealTimeVehiclePositionsProvider.kt | 4 ++-- 12 files changed, 43 insertions(+), 48 deletions(-) create mode 100644 src/main/java/org/mtransit/android/commons/data/Targetable.kt diff --git a/src/main/java/org/mtransit/android/commons/data/Direction.java b/src/main/java/org/mtransit/android/commons/data/Direction.java index cbdbb938..a55359ab 100644 --- a/src/main/java/org/mtransit/android/commons/data/Direction.java +++ b/src/main/java/org/mtransit/android/commons/data/Direction.java @@ -22,7 +22,7 @@ import java.util.Comparator; @SuppressWarnings("WeakerAccess") -public class Direction { +public class Direction implements Targetable { private static final String LOG_TAG = Direction.class.getSimpleName(); @@ -233,6 +233,7 @@ public String getAuthority() { } @NonNull + @Override public String getUUID() { return POI.POIUtils.getUUID(this.authority, this.routeId, this.id); } diff --git a/src/main/java/org/mtransit/android/commons/data/POI.java b/src/main/java/org/mtransit/android/commons/data/POI.java index 6cd68e13..40172c72 100644 --- a/src/main/java/org/mtransit/android/commons/data/POI.java +++ b/src/main/java/org/mtransit/android/commons/data/POI.java @@ -17,7 +17,7 @@ import java.lang.annotation.Retention; -public interface POI extends MTLog.Loggable { +public interface POI extends Targetable, MTLog.Loggable { @Retention(SOURCE) @IntDef({ITEM_VIEW_TYPE_ROUTE_DIRECTION_STOP, ITEM_VIEW_TYPE_BASIC_POI, ITEM_VIEW_TYPE_MODULE, ITEM_VIEW_TYPE_TEXT_MESSAGE, ITEM_VIEW_TYPE_PLACE}) @@ -71,9 +71,6 @@ public interface POI extends MTLog.Loggable { boolean hasLocation(); - @NonNull - String getUUID(); - @NonNull String getAuthority(); diff --git a/src/main/java/org/mtransit/android/commons/data/Route.java b/src/main/java/org/mtransit/android/commons/data/Route.java index 6c48ccef..ba2e2bea 100644 --- a/src/main/java/org/mtransit/android/commons/data/Route.java +++ b/src/main/java/org/mtransit/android/commons/data/Route.java @@ -25,7 +25,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class Route implements MTLog.Loggable { +public class Route implements Targetable, MTLog.Loggable { private static final String LOG_TAG = Route.class.getSimpleName(); @@ -210,6 +210,7 @@ public long getId() { private String uuid = null; @NonNull + @Override public String getUUID() { if (this.uuid == null) { this.uuid = POI.POIUtils.getUUID(this.authority, this.id); diff --git a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java index 22726fee..edb0db84 100644 --- a/src/main/java/org/mtransit/android/commons/data/RouteDirection.java +++ b/src/main/java/org/mtransit/android/commons/data/RouteDirection.java @@ -16,7 +16,7 @@ import java.util.Arrays; import java.util.Collection; -public class RouteDirection implements MTLog.Loggable { +public class RouteDirection implements Targetable, MTLog.Loggable { private static final String LOG_TAG = RouteDirection.class.getSimpleName(); @@ -40,6 +40,7 @@ public RouteDirection( } @NonNull + @Override public String getUUID() { return direction.getUUID(); } diff --git a/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt b/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt index ee4e62a0..a913b660 100644 --- a/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt +++ b/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt @@ -24,13 +24,13 @@ fun Iterable?.isSeverityWarningInfo(): Pair { fun Iterable.distinctByOriginalId() = this.distinctBy { it.originalId ?: it.id } // keep 1st occurrence from sorted list (in *Manager) -fun ServiceUpdateProviderContract.makeServiceUpdateNoneList(targetUUID: String, sourceId: String): ArrayList = +fun ServiceUpdateProviderContract.makeServiceUpdateNoneList(targetable: Targetable, sourceId: String): ArrayList = ArrayList().apply { - add(makeServiceUpdateNone(targetUUID, sourceId)) + add(makeServiceUpdateNone(targetable.uuid, sourceId)) } -fun ServiceUpdateProviderContract.makeServiceUpdateNone(targetUUID: String, sourceId: String): ServiceUpdate { - return ServiceUpdate( +fun ServiceUpdateProviderContract.makeServiceUpdateNone(targetUUID: String, sourceId: String) = + ServiceUpdate( null, targetUUID, TimeUtils.currentTimeMillis(), @@ -43,4 +43,3 @@ fun ServiceUpdateProviderContract.makeServiceUpdateNone(targetUUID: String, sour null, getServiceUpdateLanguage(), ) -} diff --git a/src/main/java/org/mtransit/android/commons/data/Targetable.kt b/src/main/java/org/mtransit/android/commons/data/Targetable.kt new file mode 100644 index 00000000..9a4945ff --- /dev/null +++ b/src/main/java/org/mtransit/android/commons/data/Targetable.kt @@ -0,0 +1,8 @@ +package org.mtransit.android.commons.data + +interface Targetable { + + val uUID: String + val uuid: String get() = this.uUID + +} diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 81ee1a37..6bd4a1c2 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -40,6 +40,7 @@ import org.mtransit.android.commons.data.ServiceUpdate; import org.mtransit.android.commons.data.ServiceUpdateKtxKt; import org.mtransit.android.commons.data.Stop; +import org.mtransit.android.commons.data.Targetable; import org.mtransit.android.commons.provider.agency.AgencyUtils; import org.mtransit.android.commons.provider.common.MTContentProvider; import org.mtransit.android.commons.provider.common.MTSQLiteOpenHelper; @@ -808,31 +809,27 @@ public ArrayList getNewServiceUpdates(@NonNull ServiceUpdateProvi private ArrayList getNewServiceUpdates(@NonNull RouteDirectionStop rds, boolean inFocus) { final Context context = requireContextCompat(); updateAgencyServiceUpdateDataIfRequired(context, inFocus); - final String authority = rds.getAuthority(); ArrayList cachedServiceUpdates = getCachedServiceUpdates(rds); - return getServiceUpdatesOrNone(context, authority, cachedServiceUpdates); + return getServiceUpdatesOrNone(context, rds, cachedServiceUpdates); } private ArrayList getNewServiceUpdates(@NonNull RouteDirection rd, boolean inFocus) { final Context context = requireContextCompat(); updateAgencyServiceUpdateDataIfRequired(context, inFocus); - final String authority = rd.getAuthority(); ArrayList cachedServiceUpdates = getCachedServiceUpdates(rd); - return getServiceUpdatesOrNone(context, authority, cachedServiceUpdates); + return getServiceUpdatesOrNone(context, rd, cachedServiceUpdates); } private ArrayList getNewServiceUpdates(@NonNull Route route, boolean inFocus) { final Context context = requireContextCompat(); updateAgencyServiceUpdateDataIfRequired(context, inFocus); - final String authority = route.getAuthority(); ArrayList cachedServiceUpdates = getCachedServiceUpdates(route); - return getServiceUpdatesOrNone(context, authority, cachedServiceUpdates); + return getServiceUpdatesOrNone(context, route, cachedServiceUpdates); } - private ArrayList getServiceUpdatesOrNone(Context context, String authority, ArrayList cachedServiceUpdates) { + private ArrayList getServiceUpdatesOrNone(Context context, Targetable target, ArrayList cachedServiceUpdates) { if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - final String agencyProviderTargetUUID = getAgencyTagTargetUUID(authority); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyProviderTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, target, AGENCY_SOURCE_ID); enhanceServiceUpdate(context, cachedServiceUpdates, Collections.emptyMap()); // convert to stop service update } return cachedServiceUpdates; diff --git a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java index 39c060f3..ecd23b77 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java @@ -849,7 +849,7 @@ private ArrayList getNewServiceUpdates(@NonNull Context context, updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(context, rds); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - cachedServiceUpdates = makeServiceUpdateNoneList(this, getAgencyTargetUUID(rds.getAuthority()), AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, rds, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, Collections.emptyMap()); } return cachedServiceUpdates; @@ -859,7 +859,7 @@ private ArrayList getNewServiceUpdates(@NonNull Context context, updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(context, rd); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - cachedServiceUpdates = makeServiceUpdateNoneList(this, getAgencyTargetUUID(rd.getAuthority()), AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, rd, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, Collections.emptyMap()); } return cachedServiceUpdates; @@ -869,7 +869,7 @@ private ArrayList getNewServiceUpdates(@NonNull Context context, updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(context, route); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - cachedServiceUpdates = makeServiceUpdateNoneList(this, getAgencyTargetUUID(route.getAuthority()), AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, route, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, Collections.emptyMap()); } return cachedServiceUpdates; @@ -959,7 +959,6 @@ private ArrayList loadAgencyServiceUpdateDataFromWWW(@NonNull Con sourceLabel, newLastUpdateInMs, getAGENCY_TAG(context), - getTARGET_AUTHORITY(context), getServiceUpdateMaxValidityInMs(), getTEXT_LANGUAGE_CODE(context), getTEXT_SECONDARY_LANGUAGE_CODE(context), @@ -1626,7 +1625,6 @@ public String getLogTag() { private final ArrayList serviceUpdates = new ArrayList<>(); private final String agencyTag; - private final String authority; private String currentRouteTag = null; @@ -1659,7 +1657,7 @@ public String getLogTag() { private final NextBusProvider provider; NextBusMessagesDataHandler(NextBusProvider provider, @NonNull String sourceLabel, long newLastUpdateInMs, - String agencyTag, String authority, + String agencyTag, long serviceUpdateMaxValidityInMs, String textLanguageCode, String textSecondaryLanguageCode, String textBoldWordsRegex, String textSecondaryBoldWordsRegex @@ -1668,7 +1666,6 @@ public String getLogTag() { this.sourceLabel = sourceLabel; this.newLastUpdateInMs = newLastUpdateInMs; this.agencyTag = agencyTag; - this.authority = authority; this.serviceUpdateMaxValidityInMs = serviceUpdateMaxValidityInMs; this.textLanguageCode = textLanguageCode; this.textSecondaryLanguageCode = textSecondaryLanguageCode; @@ -1808,7 +1805,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc final HashSet currentRouteConfiguredForMessageRoute = this.currentRouteConfiguredForMessage.get(routeTag); final int stopCount = currentRouteConfiguredForMessageRoute == null ? 0 : currentRouteConfiguredForMessageRoute.size(); if (stopCount == 0) { - final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.authority, routeTag); + final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.agencyTag, routeTag); final int severity = findRouteSeverity(); //noinspection UnnecessaryLocalVariable final String title = routeTag; @@ -1823,7 +1820,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc addServiceUpdates(routeStopTargetUUID, severity, title); } // ADD duplicates for routeTag (UI will only show it once) - final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.authority, routeTag); + final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.agencyTag, routeTag); final int severity = ServiceUpdate.SEVERITY_INFO_RELATED_POI; //noinspection UnnecessaryLocalVariable final String title = routeTag; @@ -1831,12 +1828,12 @@ public void endElement(String uri, String localName, String qName) throws SAXExc } } } else if (this.currentRouteTag != null) { - final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.authority, this.currentRouteTag); + final String routeTargetUUID = NextBusProvider.getAgencyRouteTagTargetUUID(this.agencyTag, this.currentRouteTag); final String title = this.currentRouteTag; final int severity = findAgencySeverity(); addServiceUpdates(routeTargetUUID, severity, title); } else if (this.currentRouteAll) { // AGENCY - final String agencyTargetUUID = NextBusProvider.getAgencyTargetUUID(this.authority); + final String agencyTargetUUID = NextBusProvider.getAgencyTargetUUID(this.agencyTag); final String title = this.agencyTag.toUpperCase(Locale.ROOT); final int severity = findAgencySeverity(); addServiceUpdates(agencyTargetUUID, severity, title); diff --git a/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java b/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java index 489936c3..547f78c4 100644 --- a/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java @@ -722,8 +722,7 @@ private ArrayList getNewServiceUpdates(@NonNull RouteDirectionSto updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), rds.getAuthority(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(rds); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - String agencyTargetUUID = getAgencyTargetUUID(rds.getAuthority()); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, rds, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, rds.getStop(), Collections.emptyMap()); } return cachedServiceUpdates; @@ -733,8 +732,7 @@ private ArrayList getNewServiceUpdates(@NonNull RouteDirection rd updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), rd.getAuthority(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(rd); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - String agencyTargetUUID = getAgencyTargetUUID(rd.getAuthority()); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, rd, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, null, Collections.emptyMap()); } return cachedServiceUpdates; @@ -744,8 +742,7 @@ private ArrayList getNewServiceUpdates(@NonNull Route route, bool updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), route.getAuthority(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(route); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - String agencyTargetUUID = getAgencyTargetUUID(route.getAuthority()); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, route, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, null, Collections.emptyMap()); } return cachedServiceUpdates; 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 c3392aaf..f4c041b1 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java @@ -262,7 +262,7 @@ public ArrayList getCachedServiceUpdates(@NonNull ServiceUpdatePr } else if (serviceUpdateFilter.getRouteDirection() != null) { return getCachedServiceUpdates(context, serviceUpdateFilter.getRouteDirection()); } else if ((serviceUpdateFilter.getRoute() != null)) { // NOT SUPPORTED - return makeServiceUpdateNoneList(this, serviceUpdateFilter.getRoute().getUUID(), SERVICE_UPDATE_SOURCE_ID); + return makeServiceUpdateNoneList(this, serviceUpdateFilter.getRoute(), SERVICE_UPDATE_SOURCE_ID); } else { MTLog.w(this, "getCachedServiceUpdates() > no service update (poi null or not RDS or no route)"); return null; @@ -571,7 +571,7 @@ public ArrayList getNewServiceUpdates(@NonNull ServiceUpdateProvi } else if (serviceUpdateFilter.getRouteDirection() != null) { return getNewServiceUpdates(context, serviceUpdateFilter.getRouteDirection()); } else if ((serviceUpdateFilter.getRoute() != null)) { // NOT SUPPORTED - return makeServiceUpdateNoneList(this, serviceUpdateFilter.getRoute().getUUID(), SERVICE_UPDATE_SOURCE_ID); + return makeServiceUpdateNoneList(this, serviceUpdateFilter.getRoute(), SERVICE_UPDATE_SOURCE_ID); } else { MTLog.w(this, "getNewServiceUpdates() > no service update (poi null or not RDS or no route)"); return null; diff --git a/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java b/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java index 0ed3ab70..14afcf10 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java @@ -328,8 +328,7 @@ private ArrayList getNewServiceUpdates(@NonNull RouteDirectionSto updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), rds.getAuthority(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(rds); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - String agencyTargetUUID = rds.getUUID(); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, rds, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, rds.getRoute(), Collections.emptyMap()); // convert to stop service update } return cachedServiceUpdates; @@ -340,8 +339,7 @@ private ArrayList getNewServiceUpdates(@NonNull RouteDirection rd updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), rd.getAuthority(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(rd); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - String agencyTargetUUID = rd.getUUID(); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, rd, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, rd.getRoute(), Collections.emptyMap()); // convert to stop service update } return cachedServiceUpdates; @@ -352,8 +350,7 @@ private ArrayList getNewServiceUpdates(@NonNull Route route, bool updateAgencyServiceUpdateDataIfRequired(requireContextCompat(), route.getAuthority(), inFocus); ArrayList cachedServiceUpdates = getCachedServiceUpdates(route); if (CollectionUtils.getSize(cachedServiceUpdates) == 0) { - String agencyTargetUUID = route.getUUID(); - cachedServiceUpdates = makeServiceUpdateNoneList(this, agencyTargetUUID, AGENCY_SOURCE_ID); + cachedServiceUpdates = makeServiceUpdateNoneList(this, route, AGENCY_SOURCE_ID); enhanceRDServiceUpdateForStop(cachedServiceUpdates, route, Collections.emptyMap()); // convert to stop service update } return cachedServiceUpdates; diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 86e09717..0164adf9 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -73,12 +73,12 @@ object GTFSRealTimeVehiclePositionsProvider { } private fun RouteDirectionStop.getTargetUUIDs(provider: GTFSRealTimeProvider) = buildMap { - put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.getUUID()) + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.uuid) getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { put(it, routeDirectionUUID) } } private fun RouteDirection.getTargetUUIDs(provider: GTFSRealTimeProvider) = buildMap { - put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.getUUID()) + put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.uuid) getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { put(it, uuid) } } From b28d0ee5840788c20382a3755830a7982fd3ac7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 22 Jan 2026 15:21:33 -0500 Subject: [PATCH 30/36] VL > fix units --- .../commons/provider/gtfs/GtfsRealtimeExt.kt | 36 +++++++++++++++++-- .../GTFSRealTimeVehiclePositionsProvider.kt | 29 ++++++++++----- .../NextBusVehicleLocationsProvider.kt | 4 +-- .../vehiclelocations/model/VehicleLocation.kt | 17 +++++---- 4 files changed, 63 insertions(+), 23 deletions(-) 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 69490172..2a7d1128 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 @@ -125,6 +125,7 @@ object GtfsRealtimeExt { append("{") if (hasTrip()) append(trip.toStringExt(short = true)).append(", ") if (hasPosition()) append(position.toStringExt(short = true)).append(", ") + if (hasVehicle()) append(vehicle.toStringExt(short = true)).append(", ") if (hasCurrentStopSequence()) append("currentStopSequence=").append(currentStopSequence).append(", ") if (hasCurrentStatus()) append("currentStatus=").append(currentStatus).append(", ") if (hasStopId()) append("stopId=").append(stopId).append(", ") @@ -134,18 +135,45 @@ object GtfsRealtimeExt { if (hasCongestionLevel()) append("congestionLevel=").append(congestionLevel).append(", ") append("}") } + val GtfsRealtime.VehiclePosition.optTrip get() = if (hasTrip()) trip else null + val GtfsRealtime.VehiclePosition.optTimestamp get() = if (hasTimestamp()) timestamp else null + val GtfsRealtime.VehiclePosition.optPosition get() = if (hasPosition()) position else null + val GtfsRealtime.VehiclePosition.optVehicle get() = if (hasVehicle()) vehicle else null @JvmStatic @JvmOverloads fun GtfsRealtime.Position.toStringExt(short: Boolean = false) = buildString { append(if (short) "P:" else "Position:") append("{") - append("lat=").append(latitude) - append(", ") - append("lon=").append(longitude) + if (hasLatitude()) append("lat=").append(latitude).append(", ") + if (hasLongitude()) append("lon=").append(longitude).append(", ") + if (hasBearing()) append("bearing=").append(bearing).append(", ") + if (hasSpeed()) append("speed=").append(speed).append(", ") + if (hasOdometer()) append("odometer=").append(odometer).append(", ") append("}") } + val GtfsRealtime.Position.optLatitude get() = if (hasLatitude()) latitude else null + val GtfsRealtime.Position.optLongitude get() = if (hasLongitude()) longitude else null + val GtfsRealtime.Position.optBearing get() = if (hasBearing()) bearing else null + val GtfsRealtime.Position.optSpeed get() = if (hasSpeed()) speed else null + val GtfsRealtime.Position.optOdometer get() = if (hasOdometer()) odometer else null + + @JvmStatic + @JvmOverloads + fun GtfsRealtime.VehicleDescriptor.toStringExt(short: Boolean = false) = buildString { + append(if (short) "VD:" else "VehicleDescriptor:") + append("{") + if (hasId()) append("id=").append(id).append(", ") + if (hasLabel()) append("lbl=").append(label).append(", ") + if (hasLicensePlate()) append("licensePlate=").append(licensePlate).append(", ") + if (hasWheelchairAccessible()) append("a18n=").append(wheelchairAccessible).append(", ") + append("}") + } + + val GtfsRealtime.VehicleDescriptor.optId get() = if (hasId()) id else null + val GtfsRealtime.VehicleDescriptor.optLabel get() = if (hasLabel()) label else null + @JvmStatic @JvmOverloads fun GtfsRealtime.Alert.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { @@ -236,6 +264,8 @@ object GtfsRealtimeExt { append("}") } + val GtfsRealtime.TripDescriptor.optTripId get() = if (hasTripId()) tripId else null + @JvmStatic @JvmOverloads fun GtfsRealtime.TripDescriptor.ModifiedTripSelector.toStringExt(short: Boolean = false) = buildString { diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 0164adf9..3df45af4 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -15,6 +15,17 @@ import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRoute import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteTagTargetUUID import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyTagTargetUUID import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optBearing +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optId +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optLabel +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optLatitude +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optLongitude +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optPosition +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optSpeed +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optTimestamp +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optTrip +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optTripId +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optVehicle import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToHash import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.originalIdToId import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortVehicles @@ -224,23 +235,23 @@ object GTFSRealTimeVehiclePositionsProvider { VehicleLocation( authority = this.authority, targetUUID = targetUUIDs, - targetTripId = gVehiclePosition.trip.tripId.originalIdToId(tripIdCleanupPattern), + targetTripId = gVehiclePosition.optTrip?.optTripId?.originalIdToId(tripIdCleanupPattern), lastUpdateInMs = newLastUpdateInMs, maxValidityInMs = this@processVehiclePositions.vehicleLocationMaxValidityInMs, // - vehicleId = gVehiclePosition.vehicle.id, - vehicleLabel = gVehiclePosition.vehicle.label, - reportTimestamp = gVehiclePosition.timestamp.seconds, - latitude = gVehiclePosition.position.latitude, - longitude = gVehiclePosition.position.longitude, - bearing = gVehiclePosition.position.bearing, - speed = gVehiclePosition.position.speed, + vehicleId = gVehiclePosition.optVehicle?.optId, + vehicleLabel = gVehiclePosition.optVehicle?.optLabel, + reportTimestamp = gVehiclePosition.optTimestamp?.seconds, + latitude = gVehiclePosition.optPosition?.optLatitude ?: return null, + longitude = gVehiclePosition.optPosition?.optLongitude ?: return null, + bearingDegrees = gVehiclePosition.optPosition?.optBearing?.toInt(), // in degrees + speedMetersPerSecond = gVehiclePosition.optPosition?.optSpeed?.toInt(), // in meters per second ) ) } private fun GTFSRealTimeProvider.parseProviderTargetUUID(gVehiclePosition: GtfsRealtime.VehiclePosition, ignoreDirection: Boolean): String? { - val tripDescriptor = gVehiclePosition.trip + val tripDescriptor = gVehiclePosition.optTrip ?: return null if (tripDescriptor.hasModifiedTrip() || tripDescriptor.hasStartTime() || tripDescriptor.hasStartDate()) { MTLog.d(this, "parseTargetUUID() > unhandled values: ${tripDescriptor.toStringExt()}") } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt index 2715451d..10425c0b 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/NextBusVehicleLocationsProvider.kt @@ -226,8 +226,8 @@ object NextBusVehicleLocationsProvider { reportTimestamp = nVehicle.secsSinceReport?.seconds?.let { newLastUpdate - it }, latitude = nVehicle.lat?.toFloat() ?: return null, longitude = nVehicle.lon?.toFloat() ?: return null, - bearing = nVehicle.heading?.toFloat(), - speed = nVehicle.speedKmHr?.toFloat(), + bearingDegrees = nVehicle.heading, // in degrees + speedMetersPerSecond = nVehicle.speedKmHr?.div(3.6)?.toInt(), // in km/h (m/s = km/h * 1000 meters / 3600 seconds) ) ) } diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index 3854326a..b529f6e7 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -6,7 +6,6 @@ import org.mtransit.android.commons.TimeUtils import org.mtransit.android.commons.getFloat import org.mtransit.android.commons.getLong import org.mtransit.android.commons.getString -import org.mtransit.android.commons.optFloat import org.mtransit.android.commons.optInt import org.mtransit.android.commons.optLong import org.mtransit.android.commons.optString @@ -30,8 +29,8 @@ data class VehicleLocation( val reportTimestamp: Duration?, // in SECONDS val latitude: Float, val longitude: Float, - val bearing: Float?, // in degree // TODO Int - val speed: Float?, // m/s OR km/h // TODO Int + val bearingDegrees: Int?, // in degrees + val speedMetersPerSecond: Int?, // in m/s ) { val reportTimestampSec: Long? get() = reportTimestamp?.inWholeSeconds @@ -57,8 +56,8 @@ data class VehicleLocation( reportTimestamp = cursor.optLong(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP)?.seconds, latitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE), longitude = cursor.getFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE), - bearing = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING), - speed = cursor.optFloat(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED), + bearingDegrees = cursor.optInt(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING), + speedMetersPerSecond = cursor.optInt(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED), ) } @@ -74,8 +73,8 @@ data class VehicleLocation( reportTimestampSec?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, it) } put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LATITUDE, latitude) put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_LONGITUDE, longitude) - bearing?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) } - speed?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED, it) } + bearingDegrees?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) } + speedMetersPerSecond?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED, it) } } /** @@ -93,8 +92,8 @@ data class VehicleLocation( reportTimestampSec, latitude, longitude, - bearing, - speed, + bearingDegrees, + speedMetersPerSecond, ) val useful: Boolean get() = this.lastUpdateInMs + this.maxValidityInMs >= TimeUtils.currentTimeMillis() From b57c626fde7fe30747c5c8ab25fcca6e8e43ca0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Thu, 22 Jan 2026 16:00:51 -0500 Subject: [PATCH 31/36] compat with export trip id still OFF --- .../provider/gtfs/GTFSRDSProvider.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java index e98b27b6..ebe9ed57 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java @@ -56,7 +56,9 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_DIRECTION_STOP_SEARCH_PATH, ROUTES_DIRECTIONS_STOPS_SEARCH); uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_DIRECTION_PATH, ROUTES_DIRECTIONS); uriMatcher.addURI(authority, GTFSProviderContract.DIRECTION_STOP_PATH, DIRECTIONS_STOPS); - uriMatcher.addURI(authority, GTFSProviderContract.TRIP_PATH, TRIPS); + if (FeatureFlags.F_EXPORT_TRIP_ID) { + uriMatcher.addURI(authority, GTFSProviderContract.TRIP_PATH, TRIPS); + } } private static final ArrayMap ROUTE_PROJECTION_MAP; @@ -267,9 +269,11 @@ public static Cursor queryS(@NonNull GTFSProvider provider, @NonNull Uri uri, @N qb.setProjectionMap(DIRECTION_STOP_PROJECTION_MAP); break; case TRIPS: - qb.setTables(TRIP_TRIP_IDS_SERVICE_IDS_JOIN); - qb.setProjectionMap(TRIP_PROJECTION_MAP); - break; + if (FeatureFlags.F_EXPORT_TRIP_ID) { + qb.setTables(TRIP_TRIP_IDS_SERVICE_IDS_JOIN); + qb.setProjectionMap(TRIP_PROJECTION_MAP); + break; + } default: return null; // not processed } @@ -420,7 +424,9 @@ public static String getSortOrderS(@NonNull GTFSProvider provider, @NonNull Uri case ROUTES_DIRECTIONS: return ROUTE_DIRECTION_SORT_ORDER; case TRIPS: - return TRIP_SORT_ORDER; + if (FeatureFlags.F_EXPORT_TRIP_ID) { + return TRIP_SORT_ORDER; + } default: return null; // not processed } @@ -453,7 +459,9 @@ public static String getTypeS(@NonNull GTFSProvider provider, @NonNull Uri uri) case DIRECTIONS_STOPS: return DIRECTION_STOP_CONTENT_TYPE; case TRIPS: - return TRIP_CONTENT_TYPE; + if (FeatureFlags.F_EXPORT_TRIP_ID) { + return TRIP_CONTENT_TYPE; + } default: return null; // not processed } From b55d22f96fb2ed4d238e6906f57286410ce5681c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 23 Jan 2026 11:25:59 -0500 Subject: [PATCH 32/36] clean --- .../android/commons/provider/gtfs/GTFSProviderDbHelper.java | 4 +--- 1 file changed, 1 insertion(+), 3 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 c4af435f..ec1063b3 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 @@ -25,8 +25,6 @@ import java.util.HashMap; import java.util.Map; -import kotlin.Unit; - public class GTFSProviderDbHelper extends MTSQLiteOpenHelper { private static final String LOG_TAG = GTFSProviderDbHelper.class.getSimpleName(); @@ -215,7 +213,7 @@ private void initAllDbTables(@NonNull SQLiteDatabase db, boolean upgrade) { initDbTableWithRetry(context, db, T_STRINGS, T_STRINGS_SQL_CREATE, T_STRINGS_SQL_INSERT, T_STRINGS_SQL_DROP, getStringsFiles(), 0, 0, null, null, (id, string) -> { allStrings.put(id, string); - return Unit.INSTANCE; + return kotlin.Unit.INSTANCE; } ); // 1st } From 2e4931e5cdbf4dd81a209cabd13d2a8ff6830c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 23 Jan 2026 14:35:43 -0500 Subject: [PATCH 33/36] clean --- .../android/commons/provider/GTFSRealTimeProvider.java | 1 + .../mtransit/android/commons/provider/gtfs/GtfsRealtimeExt.kt | 1 + .../android/commons/provider/nextbus/NextBusStorage.kt | 4 ++-- .../android/commons/provider/nextbus/api/NextBusApi.kt | 2 +- .../provider/vehiclelocations/VehicleLocationDbHelper.kt | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java index 6bd4a1c2..3537230d 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -1679,6 +1679,7 @@ public static int getDbVersion(@NonNull Context context) { dbVersion++; // add "service_update.original_id" column dbVersion++; // add "vehicle_location" table dbVersion++; // add "vehicle_location.report_timestamp" column + dbVersion++; // change "vehicle_location.[bearing|speed] unit to Int } return dbVersion; } 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 2a7d1128..4d562b43 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 @@ -135,6 +135,7 @@ object GtfsRealtimeExt { if (hasCongestionLevel()) append("congestionLevel=").append(congestionLevel).append(", ") append("}") } + val GtfsRealtime.VehiclePosition.optTrip get() = if (hasTrip()) trip else null val GtfsRealtime.VehiclePosition.optTimestamp get() = if (hasTimestamp()) timestamp else null val GtfsRealtime.VehiclePosition.optPosition get() = if (hasPosition()) position else null diff --git a/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt b/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt index 2945cf4d..7e8f0f8e 100644 --- a/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt +++ b/src/main/java/org/mtransit/android/commons/provider/nextbus/NextBusStorage.kt @@ -42,7 +42,7 @@ object NextBusStorage { // endregion - // region Service update (messages) + // region Service update (messages) /** * Override if multiple [org.mtransit.android.commons.provider.NextBusProvider] implementations in same app. @@ -60,5 +60,5 @@ object NextBusStorage { PreferenceUtils.savePrefLclSync(context, PREF_KEY_SERVICE_UPDATE_LAST_UPDATE_MS, lastUpdateInMs) } - //endregion + // endregion } diff --git a/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt b/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt index 59ec70c1..73249690 100644 --- a/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt +++ b/src/main/java/org/mtransit/android/commons/provider/nextbus/api/NextBusApi.kt @@ -4,13 +4,13 @@ import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Query +// https://retro.umoiq.com/xmlFeedDocs/NextBusXMLFeed.pdf interface NextBusApi { companion object { const val BASE_HOST_URL = "https://retro.umoiq.com/service/" } - // https://developers.google.com/youtube/v3/docs/channels/list @GET("publicJSONFeed") fun getVehicleLocations( @Query("command") command: String = "vehicleLocations", diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt index 2c01f40a..44ba542c 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt @@ -48,8 +48,8 @@ abstract class VehicleLocationDbHelper( .appendColumn(T_VEHICLE_LOCATION_K_VEHICLE_REPORT_TIMESTAMP, SqlUtils.INT) .appendColumn(T_VEHICLE_LOCATION_K_LATITUDE, SqlUtils.REAL) .appendColumn(T_VEHICLE_LOCATION_K_LONGITUDE, SqlUtils.REAL) - .appendColumn(T_VEHICLE_LOCATION_K_BEARING, SqlUtils.REAL) - .appendColumn(T_VEHICLE_LOCATION_K_SPEED, SqlUtils.REAL) + .appendColumn(T_VEHICLE_LOCATION_K_BEARING, SqlUtils.INT) + .appendColumn(T_VEHICLE_LOCATION_K_SPEED, SqlUtils.INT) } abstract val dbName: String From e32179ad4fc6959b295c6bacdf82639fe82e0f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 23 Jan 2026 14:59:55 -0500 Subject: [PATCH 34/36] fix --- .../commons/provider/NextBusProvider.java | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java index ecd23b77..20d8dea2 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java @@ -138,20 +138,6 @@ private static String getAUTHORITY(@NonNull Context context) { return authority; } - @Nullable - private static String targetAuthority = null; - - /** - * Override if multiple {@link NextBusProvider} implementations in same app. - */ - @NonNull - private static String getTARGET_AUTHORITY(@NonNull Context context) { - if (targetAuthority == null) { - targetAuthority = context.getResources().getString(R.string.next_bus_for_poi_authority); - } - return targetAuthority; - } - @Nullable private static Uri authorityUri = null; @@ -1483,8 +1469,10 @@ private String cleanTripHeadSign(String tripHeadSign) { if (isSCHEDULE_HEAD_SIGN_CLEAN_STREET_TYPES(context)) { tripHeadSign = CleanUtils.cleanStreetTypes(tripHeadSign); } + Locale locale = Locale.ENGLISH; if (isSCHEDULE_HEAD_SIGN_CLEAN_STREET_TYPES_FR_CA(context)) { tripHeadSign = CleanUtils.cleanStreetTypesFRCA(tripHeadSign); + locale = Locale.FRENCH; } for (int c = 0; c < getSCHEDULE_HEAD_SIGN_CLEAN_REGEX(context).size(); c++) { try { @@ -1500,7 +1488,7 @@ private String cleanTripHeadSign(String tripHeadSign) { tripHeadSign = CleanUtils.CLEAN_AT.matcher(tripHeadSign).replaceAll(CleanUtils.CLEAN_AT_REPLACEMENT); tripHeadSign = CleanUtils.CLEAN_AND.matcher(tripHeadSign).replaceAll(CleanUtils.CLEAN_AND_REPLACEMENT); tripHeadSign = CleanUtils.CLEAN_ET.matcher(tripHeadSign).replaceAll(CleanUtils.CLEAN_ET_REPLACEMENT); - tripHeadSign = CleanUtils.cleanLabel(tripHeadSign); + tripHeadSign = CleanUtils.cleanLabel(locale, tripHeadSign); return tripHeadSign; } catch (Exception e) { MTLog.w(this, e, "Error while cleaning trip head sign '%s'!", tripHeadSign); From 2031336868841680b589545a18f60854f1e746db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Fri, 23 Jan 2026 15:28:48 -0500 Subject: [PATCH 35/36] merge --- .../android/commons/provider/gtfs/GTFSRDSProvider.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java index a26496f2..b03a1789 100644 --- a/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/gtfs/GTFSRDSProvider.java @@ -56,9 +56,7 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_DIRECTION_STOP_SEARCH_PATH, ROUTES_DIRECTIONS_STOPS_SEARCH); uriMatcher.addURI(authority, GTFSProviderContract.ROUTE_DIRECTION_PATH, ROUTES_DIRECTIONS); uriMatcher.addURI(authority, GTFSProviderContract.DIRECTION_STOP_PATH, DIRECTIONS_STOPS); - if (FeatureFlags.F_EXPORT_TRIP_ID) { - uriMatcher.addURI(authority, GTFSProviderContract.TRIP_PATH, TRIPS); - } + uriMatcher.addURI(authority, GTFSProviderContract.TRIP_PATH, TRIPS); } private static final ArrayMap ROUTE_PROJECTION_MAP; From 4786006bfa3ac6720b858671d989a50a5da704d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathieu=20M=C3=A9a?= Date: Mon, 26 Jan 2026 14:41:52 -0500 Subject: [PATCH 36/36] minor --- .../GTFSRealTimeVehiclePositionsProvider.kt | 4 ++-- .../vehiclelocations/model/VehicleLocation.kt | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt index 3df45af4..e30f4d56 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/GTFSRealTimeVehiclePositionsProvider.kt @@ -193,8 +193,8 @@ object GTFSRealTimeVehiclePositionsProvider { } MTLog.i(this@GTFSRealTimeVehiclePositionsProvider, "Found %d vehicle locations.", vehicleLocations.size) if (Constants.DEBUG) { - for (serviceUpdate in vehicleLocations) { - MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > vehicle location: $serviceUpdate.") + for (vehicleLocation in vehicleLocations) { + MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > - new ${vehicleLocation.toStringShort()}.") } } return vehicleLocations diff --git a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt index b529f6e7..1f63e3be 100644 --- a/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt +++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt @@ -11,6 +11,7 @@ import org.mtransit.android.commons.optLong import org.mtransit.android.commons.optString import org.mtransit.android.commons.provider.vehiclelocations.VehicleLocationProviderContract import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds /** @@ -35,8 +36,11 @@ data class VehicleLocation( val reportTimestampSec: Long? get() = reportTimestamp?.inWholeSeconds + @Suppress("unused") val reportTimestampMs: Long? get() = reportTimestamp?.inWholeMilliseconds + val reportTimestampCountdown: Duration? get() = reportTimestamp?.let { (TimeUtils.currentTimeMillis().milliseconds - it) } + private val _uid: String? = this.vehicleId ?: this.vehicleLabel val uuid: String? = _uid?.let { "${this.authority}-$it" } @@ -97,4 +101,15 @@ data class VehicleLocation( ) val useful: Boolean get() = this.lastUpdateInMs + this.maxValidityInMs >= TimeUtils.currentTimeMillis() + + @Suppress("unused") + fun toStringShort() = buildString { + append("VLoc:{") + vehicleId?.let { append("vId:").append(it).append(",") } + vehicleLabel?.let { append("vLabel:").append(it).append(",") } + targetTripId?.let { append("tTripId:").append(it).append(",") } + targetUUID.let { append("tUUID:").append(it).append(",") } + reportTimestampCountdown?.let { append("rCtSec:").append(it.inWholeSeconds).append(",") } + append("}") + } }