? {
+ 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,
+ bearingDegrees = nVehicle.heading, // in degrees
+ speedMetersPerSecond = nVehicle.speedKmHr?.div(3.6)?.toInt(), // in km/h (m/s = km/h * 1000 meters / 3600 seconds)
+ )
+ )
+ }
+
+ 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/VehicleLocationDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt
new file mode 100644
index 00000000..44ba542c
--- /dev/null
+++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationDbHelper.kt
@@ -0,0 +1,56 @@
+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?,
+ 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: 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_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"
+ 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_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.INT)
+ .appendColumn(T_VEHICLE_LOCATION_K_SPEED, SqlUtils.INT)
+ }
+
+ 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
new file mode 100644
index 00000000..4aaaec8b
--- /dev/null
+++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProvider.kt
@@ -0,0 +1,260 @@
+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
+import org.mtransit.android.commons.provider.vehiclelocations.model.VehicleLocation
+
+abstract class VehicleLocationProvider : MTContentProvider(),
+ VehicleLocationProviderContract {
+
+ companion object {
+ private val LOG_TAG: String = VehicleLocationProvider::class.java.simpleName
+
+ fun getNewUriMatcher(authority: String) = UriMatcher(UriMatcher.NO_MATCH).apply {
+ append(authority)
+ }
+
+ @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, tripIds: List? = null): List? {
+ return getCachedVehicleLocationsS(
+ this.contentUri,
+ 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 getCachedVehicleLocationsS(
+ this.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_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)
+ .appendTableColumn(VehicleLocationDbHelper.T_VEHICLE_LOCATION, VehicleLocationDbHelper.T_VEHICLE_LOCATION_K_SPEED, VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_SPEED)
+ .build()
+ //@formatter:on
+
+ private fun P.getCachedVehicleLocationsS(
+ @Suppress("unused") uri: Uri?,
+ selection: String?,
+ ): List? =
+ try {
+ SQLiteQueryBuilder()
+ .apply {
+ tables = dbTableName
+ projectionMap = VEHICLE_LOCATION_PROJECTION_MAP
+ }.query(
+ 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, this@getCachedVehicleLocationsS.authority))
+ } 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
+ 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 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
+ 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
+}
+
+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
new file mode 100644
index 00000000..f930e0b0
--- /dev/null
+++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/VehicleLocationProviderContract.kt
@@ -0,0 +1,228 @@
+package org.mtransit.android.commons.provider.vehiclelocations
+
+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
+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 {
+
+ companion object {
+ 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_VEHICLE_REPORT_TIMESTAMP,
+ 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 authority: String
+
+ val authorityUri: Uri
+
+ val vehicleLocationMaxValidityInMs: Long
+
+ fun getVehicleLocationValidityInMs(inFocus: Boolean): Long
+
+ @Suppress("unused")
+ fun getMinDurationBetweenVehicleLocationRefreshInMs(inFocus: Boolean): Long
+
+ fun cacheVehicleLocations(newVehicleLocations: List)
+
+ fun getCachedVehicleLocations(vehicleLocationFilter: Filter): List?
+
+ fun getNewVehicleLocations(vehicleLocationFilter: Filter): List?
+
+ fun deleteCachedVehicleLocation(vehicleLocationId: Int): Boolean
+ fun purgeUselessCachedVehicleLocations(): Boolean
+
+ 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_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_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"
+ const val T_VEHICLE_LOCATION_K_SPEED = "speed"
+ }
+ }
+
+ 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,
+ val tripIds: List?, // original // GTFS // cleaned
+ ) : 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
+
+ @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)
+
+ @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)
+
+ @Suppress("unused") // main app only
+ 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() }
+
+ companion object {
+ private val LOG_TAG: String = VehicleLocationProviderContract::class.java.simpleName + ">" + Filter::class.java.simpleName
+
+ 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"
+ 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 {
+ return if (jsonString == null) null else fromJSON(JSONObject(jsonString))
+ } catch (jsone: JSONException) {
+ MTLog.w(LOG_TAG, jsone, "Error while parsing JSON string '$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))
+ }
+ }
+ }
+ 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) }
+ ?: 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) =
+ 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, 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) {
+ MTLog.w(LOG_TAG, jsone, "Error while making JSON object '$vehicleLocationFilter'!")
+ null
+ }
+ }
+ }
+
+ override fun getLogTag() = LOG_TAG
+
+ @Suppress("unused") // used from main app
+ 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
new file mode 100644
index 00000000..1f63e3be
--- /dev/null
+++ b/src/main/java/org/mtransit/android/commons/provider/vehiclelocations/model/VehicleLocation.kt
@@ -0,0 +1,115 @@
+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.getString
+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.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+/**
+ * See [VehicleLocationProviderContract]
+ */
+data class VehicleLocation(
+ 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,
+ val maxValidityInMs: Long,
+ //
+ val vehicleId: String?, // not user visible
+ val vehicleLabel: String?, // user visible
+ val reportTimestamp: Duration?, // in SECONDS
+ val latitude: Float,
+ val longitude: Float,
+ val bearingDegrees: Int?, // in degrees
+ val speedMetersPerSecond: Int?, // in m/s
+) {
+
+ 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" }
+
+ companion object {
+ @JvmStatic
+ 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),
+ 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),
+ 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),
+ bearingDegrees = cursor.optInt(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING),
+ speedMetersPerSecond = cursor.optInt(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) }
+ 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)
+ bearingDegrees?.let { put(VehicleLocationProviderContract.Columns.T_VEHICLE_LOCATION_K_BEARING, it) }
+ speedMetersPerSecond?.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,
+ reportTimestampSec,
+ latitude,
+ longitude,
+ bearingDegrees,
+ speedMetersPerSecond,
+ )
+
+ 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("}")
+ }
+}
diff --git a/src/main/res/values/constants.xml b/src/main/res/values/constants.xml
index b64dfab7..75137316 100644
--- a/src/main/res/values/constants.xml
+++ b/src/main/res/values/constants.xml
@@ -10,6 +10,8 @@
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
diff --git a/src/main/res/values/gtfs_real_time_values.xml b/src/main/res/values/gtfs_real_time_values.xml
index 9898db30..7ed6b17c 100755
--- a/src/main/res/values/gtfs_real_time_values.xml
+++ b/src/main/res/values/gtfs_real_time_values.xml
@@ -5,6 +5,8 @@
false
+
+
false
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/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(
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();