Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/org/mtransit/android/MtLogExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fun Long?.toDateTimeLog() = MTLog.formatDateTime(this)

fun Date?.toDateTimeLog() = this?.time.toDateTimeLog()

@Suppress("unused")
fun Calendar?.toDateTimeLog() = this?.time.toDateTimeLog()

@Deprecated("Use toDateTimeLog() instead", ReplaceWith("this.toDateTimeLog()"))
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/org/mtransit/android/commons/MTLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -378,11 +378,25 @@ private static String getLogMsg(@NonNull String tag, @NonNull String logMsg) {
if (Constants.DEBUG) {
logMsg = StringUtils.oneLineOneSpace(logMsg);
}
final String time = BuildConfig.DEBUG ? LOG_TIME_FORMAT.formatThreadSafe(TimeUtils.currentTimeMillis())
: String.valueOf(TimeUtils.currentTimeMillis());
final String time = makeTime(TimeUtils.currentTimeMillis());
return String.format("%s:%s>%s", time, tag, logMsg);
}

@Nullable
public static String makeTime(@Nullable Calendar calendar) {
return calendar == null ? null : makeTime(calendar.getTimeInMillis());
}

@Nullable
public static String makeTime(@Nullable Long timeInMs) {
return timeInMs == null ? null : makeTime(timeInMs.longValue());
}

@NonNull
public static String makeTime(long timeMs) {
return BuildConfig.DEBUG ? LOG_TIME_FORMAT.formatThreadSafe(timeMs) : String.valueOf(timeMs);
}

private static void logEntireMessage(@NonNull LogMethod logMethod, String logMsg) {
if (logMsg.length() < MAX_LOG_CAT_LENGTH) {
logMethod.callLog(
Expand Down
35 changes: 30 additions & 5 deletions src/main/java/org/mtransit/android/commons/data/ScheduleExt.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
package org.mtransit.android.commons.data

import org.mtransit.android.commons.Constants
import org.mtransit.android.commons.MTLog
import org.mtransit.android.commons.floorBy
import org.mtransit.android.commons.millisToInstant
import org.mtransit.android.commons.roundToNearest
import org.mtransit.android.commons.toMillis
import org.mtransit.android.toDateTimeLog
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Instant

fun makeSchedule(
id: Int? = null,
targetUUID: String,
lastUpdateInMs: Long,
validityInMs: Long,
readFromSourceAtInMs: Long,
providerPrecisionInMs: Long,
isNoPickup: Boolean = false,
sourceLabel: String? = null,
noData: Boolean = false
) = Schedule(
id,
targetUUID,
lastUpdateInMs,
validityInMs,
readFromSourceAtInMs,
providerPrecisionInMs,
isNoPickup,
sourceLabel,
noData,
)

fun Schedule.toNoData() = Schedule(
id,
targetUUID,
Expand Down Expand Up @@ -132,15 +154,18 @@ val Schedule.hasRealTime get() = this.timestamps.any { it.isRealTime }
fun Schedule.Timestamp.toStringShort() = buildString {
append("T{")
arrivalTIfDifferent?.let {
append("a=").append(if (Constants.DEBUG) MTLog.formatDateTime(arrivalT) else arrivalT)
append("a=").append(if (Constants.DEBUG) arrivalT.toDateTimeLog() else arrivalT)
if (originalArrivalDelayMs != 0L) {
append("[+/-:").append(if (Constants.DEBUG) MTLog.formatDuration(originalArrivalDelayMs) else originalArrivalDelayMs).append("]")
append("[+/-:").append(if (Constants.DEBUG) originalArrivalDelayMs.toDateTimeLog() else originalArrivalDelayMs).append("]")
}
append(",")
}
append("d=").append(if (Constants.DEBUG) MTLog.formatDateTime(departureT) else departureT)
append("d=").append(if (Constants.DEBUG) departureT.toDateTimeLog() else departureT)
if (originalDepartureDelayMs != 0L) {
append("[+/-:").append(if (Constants.DEBUG) MTLog.formatDuration(originalDepartureDelayMs) else originalDepartureDelayMs).append("]")
append("[+/-:").append(if (Constants.DEBUG) originalDepartureDelayMs.toDateTimeLog() else originalDepartureDelayMs).append("]")
}
Comment thread
mmathieum marked this conversation as resolved.
if (tripId != null) {
append("[tId:").append(tripId).append("]")
}
if (isRealTime) {
append("[RT]")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,8 @@ private static String getAGENCY_TIME_AM_PM_FORMAT(@NonNull Context context) {
/**
* Override if multiple {@link GTFSRealTimeProvider} implementations in same app.
*/
private static String getAGENCY_TIME_ZONE(@NonNull Context context) {
@NonNull
public static String getAGENCY_TIME_ZONE(@NonNull Context context) {
if (agencyTimeZone == null) {
agencyTimeZone = context.getResources().getString(R.string.gtfs_real_time_agency_time_zone);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.mtransit.android.commons.data.RouteDirectionStop
import org.mtransit.android.commons.data.Stop
import org.mtransit.android.commons.provider.GTFSRealTimeProvider
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.MT_HASH_SECRET_AND_DATE
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_TIME_ZONE
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_URL_HEADER_NAMES
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_URL_HEADER_VALUES
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAGENCY_URL_TOKEN
Expand All @@ -20,6 +21,7 @@ import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRoute
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyRouteTypeTagTargetUUID
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyStopTagTargetUUID
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.getAgencyTagTargetUUID
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.isIGNORE_DIRECTION
import org.mtransit.android.commons.provider.GTFSRealTimeProvider.isUSE_URL_HASH_SECRET_AND_DATE
import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optRouteId
import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optStopId
Expand All @@ -31,6 +33,9 @@ import com.google.transit.realtime.GtfsRealtime.EntitySelector as GEntitySelecto
import com.google.transit.realtime.GtfsRealtime.TripDescriptor as GTripDescriptor
import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate as GTUStopTimeUpdate

val GTFSRealTimeProvider.ignoreDirection get() = isIGNORE_DIRECTION(requireContextCompat())
val GTFSRealTimeProvider.timeZone get() = getAGENCY_TIME_ZONE(requireContextCompat())

private val GTFSRealTimeProvider.routeIdCleanupPattern get() = getRouteIdCleanupPattern(requireContextCompat())
fun GTFSRealTimeProvider.parseRouteId(es: GEntitySelector) = es.optRouteId?.let { parseRouteId(it) }
fun GTFSRealTimeProvider.parseRouteId(td: GTripDescriptor) = td.optRouteId?.let { parseRouteId(it) }
Expand All @@ -40,10 +45,11 @@ private val GTFSRealTimeProvider.tripIdCleanupPattern get() = getTripIdCleanupPa
fun GTFSRealTimeProvider.parseTripId(td: GTripDescriptor) = td.optTripId?.let { parseTripId(it) }
fun GTFSRealTimeProvider.parseTripId(gTripId: String) = gTripId.originalIdToId(tripIdCleanupPattern)

private val GTFSRealTimeProvider.stopIdCleanupPattern get() = getStopIdCleanupPattern(requireContextCompat())
fun GTFSRealTimeProvider.parseStopId(es: GEntitySelector) = es.optStopId?.let { parseStopId(it) }
@Suppress("unused")
fun GTFSRealTimeProvider.parseStopId(stu: GTUStopTimeUpdate) = stu.optStopId?.let { parseStopId(it) }
fun GTFSRealTimeProvider.parseStopId(es: GEntitySelector) = es.optStopId?.let { parseStopId(it) }
fun GTFSRealTimeProvider.parseStopId(gStopId: String) = gStopId.originalIdToHash(stopIdCleanupPattern)
private val GTFSRealTimeProvider.stopIdCleanupPattern get() = getStopIdCleanupPattern(requireContextCompat())

val GTFSRealTimeProvider.agencyTag get() = getAgencyTag(requireContextCompat())

Expand All @@ -62,6 +68,50 @@ fun RouteDirectionStop.getRouteTag(provider: GTFSRealTimeProvider) = this.route.
fun RouteDirectionStop.getDirectionTag(provider: GTFSRealTimeProvider) = this.direction.getDirectionTag(provider)
fun RouteDirectionStop.getStopTag(provider: GTFSRealTimeProvider) = this.stop.getStopTag(provider)

fun GTFSRealTimeProviderFilter.getPrimaryTargetUUIDs(
provider: GTFSRealTimeProvider,
ignoreDirection: Boolean = false,
includeStopTags: Boolean = false
): Pair<String, String>? =
(poi as? RouteDirectionStop)?.getPrimaryTargetUUIDs(provider, ignoreDirection, includeStopTags)
?: routeDirection?.getPrimaryTargetUUIDs(provider, ignoreDirection)
?: route?.getPrimaryTargetUUIDs(provider)

fun RouteDirectionStop.getPrimaryTargetUUIDs(
provider: GTFSRealTimeProvider,
ignoreDirection: Boolean = false,
includeStopTags: Boolean = false
) = when {
includeStopTags && ignoreDirection -> getAgencyRouteStopTagTargetUUID(provider.agencyTag, getRouteTag(provider), getStopTag(provider))?.let { it to uuid }
includeStopTags ->
getAgencyRouteDirectionStopTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider), getStopTag(provider))?.let { it to uuid }

else -> getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { it to routeDirectionUUID }
}

fun RouteDirection.getPrimaryTargetUUIDs(
provider: GTFSRealTimeProvider,
ignoreDirection: Boolean = false,
) = when {
ignoreDirection -> getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)) to route.uuid
else -> getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { it to uuid }
}

fun Route.getPrimaryTargetUUIDs(
provider: GTFSRealTimeProvider,
) =
getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)) to uuid

fun GTFSRealTimeProviderFilter.getTargetUUIDs(
provider: GTFSRealTimeProvider,
includeAgencyTag: Boolean = false,
includeRouteType: Boolean = false,
includeStopTags: Boolean = false,
) =
(poi as? RouteDirectionStop)?.getTargetUUIDs(provider, includeAgencyTag, includeRouteType, includeStopTags)
?: routeDirection?.getTargetUUIDs(provider, includeAgencyTag, includeRouteType)
?: route?.getTargetUUIDs(provider, includeAgencyTag, includeRouteType)

fun RouteDirectionStop.getTargetUUIDs(
provider: GTFSRealTimeProvider,
includeAgencyTag: Boolean = false,
Expand All @@ -81,14 +131,22 @@ fun RouteDirectionStop.getTargetUUIDs(
}
}

fun RouteDirection.getTargetUUIDs(provider: GTFSRealTimeProvider, includeAgencyTag: Boolean = false, includeRouteType: Boolean = false) = buildMap {
fun RouteDirection.getTargetUUIDs(
provider: GTFSRealTimeProvider,
includeAgencyTag: Boolean = false,
includeRouteType: Boolean = false,
) = buildMap {
if (includeAgencyTag) put(getAgencyTagTargetUUID(provider.agencyTag), authority)
if (includeRouteType) getAgencyRouteTypeTagTargetUUID(provider.agencyTag, getRouteTypeTag(provider))?.let { put(it, authority) }
put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), route.uuid)
getAgencyRouteDirectionTagTargetUUID(provider.agencyTag, getRouteTag(provider), getDirectionTag(provider))?.let { put(it, uuid) }
}

fun Route.getTargetUUIDs(provider: GTFSRealTimeProvider, includeAgencyTag: Boolean = false, includeRouteType: Boolean = false) = buildMap {
fun Route.getTargetUUIDs(
provider: GTFSRealTimeProvider,
includeAgencyTag: Boolean = false,
includeRouteType: Boolean = false,
) = buildMap {
if (includeAgencyTag) put(getAgencyTagTargetUUID(provider.agencyTag), authority)
if (includeRouteType) getAgencyRouteTypeTagTargetUUID(provider.agencyTag, getRouteTypeTag(provider))?.let { put(it, authority) }
put(getAgencyRouteTagTargetUUID(provider.agencyTag, getRouteTag(provider)), uuid)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.mtransit.android.commons.provider.gtfs

import org.mtransit.android.commons.data.POI
import org.mtransit.android.commons.data.Route
import org.mtransit.android.commons.data.RouteDirection

interface GTFSRealTimeProviderFilter {
val poi: POI?
val routeDirection: RouteDirection?
val route: Route?
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public static POIStatus getNewStatus(@NonNull GTFSProvider provider, @NonNull St
final Schedule schedule = new Schedule(
null,
scheduleStatusFilter.getTargetUUID(),
scheduleStatusFilter.getTimestampOrDefault(),
scheduleStatusFilter.getTimestampOrDefault(), // NOW
getStatusMaxValidityInMs(),
PROVIDER_READ_FROM_SOURCE_AT_IN_MS,
PROVIDER_PRECISION_IN_MS,
Expand Down Expand Up @@ -233,14 +233,14 @@ private static String getROUTE_FREQUENCY_RAW_FILE_FORMAT(@NonNull Context contex
private static final int GTFS_ROUTE_FREQUENCY_FILE_COL_COUNT = 5;

@NonNull
private static ArrayList<Schedule.Timestamp> findTimestamps(@NonNull GTFSProvider provider, Schedule.ScheduleStatusFilter filter) {
private static ArrayList<Schedule.Timestamp> findTimestamps(@NonNull GTFSProvider provider, @NonNull Schedule.ScheduleStatusFilter filter) {
ArrayList<Schedule.Timestamp> allTimestamps = new ArrayList<>();
final RouteDirectionStop rds = filter.getRouteDirectionStop();
final int maxDataRequests = filter.getMaxDataRequestsOrDefault();
final int minUsefulResults = filter.getMinUsefulResultsOrDefault();
final long minDurationCoveredInMs = filter.getMinUsefulDurationCoveredInMsOrDefault();
final long lookBehindInMs = filter.getLookBehindInMsOrDefault();
final long timestamp = filter.getTimestampOrDefault();
final long timestamp = filter.getTimestampOrDefault(); // NOW
final long minTimestampCoveredIntMs = timestamp + minDurationCoveredInMs;
final Context context = provider.requireContextCompat();
final ThreadSafeDateFormatter dateFormat = getDateFormat(context);
Expand Down Expand Up @@ -324,7 +324,12 @@ private static ArrayList<Schedule.Timestamp> findTimestamps(@NonNull GTFSProvide
return allTimestamps;
}

protected static void alignLookupStartTime(Integer lastServiceDate, ThreadSafeDateFormatter dateFormat, Calendar lookupStartAt, long lastDepartureInMs) {
static void alignLookupStartTime(
@Nullable Integer lastServiceDate,
@NonNull ThreadSafeDateFormatter dateFormat,
@NonNull Calendar lookupStartAt,
long lastDepartureInMs
) {
if (lastServiceDate != null) {
try {
while (Integer.parseInt(dateFormat.formatThreadSafe(lookupStartAt)) > lastServiceDate) {
Expand Down Expand Up @@ -593,7 +598,7 @@ private static ArrayList<Schedule.Frequency> findFrequencies(@NonNull GTFSProvid
final RouteDirectionStop rds = filter.getRouteDirectionStop();
final int maxDataRequests = filter.getMaxDataRequestsOrDefault();
final long minDurationCoveredInMs = filter.getMinUsefulDurationCoveredInMsOrDefault();
final long timestamp = filter.getTimestampOrDefault();
final long timestamp = filter.getTimestampOrDefault(); // NOW
final long minTimestampCovered = timestamp + minDurationCoveredInMs;
final Context context = provider.requireContextCompat();
final ThreadSafeDateFormatter dateFormat = getDateFormat(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ object GtfsRealtimeExt {
append(
buildList {
optDelay?.let { add(if (short) "d=$it" else "delay=$it") }
optTime?.let { add(if (short) "t=$it" else "time=$it") }
optTimeMs?.let { add(if (short) "t=${it.toDateTimeLog()}" else "time=${it.toDateTimeLog()}") }
optUncertainty?.let { add(if (short) "u=$it" else "uncertainty=$it") }
optScheduledTime?.let { add(if (short) "sT=$it" else "schedTime=$it") }
}.joinToStringList()
Expand All @@ -286,6 +286,7 @@ object GtfsRealtimeExt {
val GTUStopTimeEvent.optDelay get() = if (hasDelay()) delay else null
val GTUStopTimeEvent.optDelayDuration: Duration? get() = this.optDelay?.seconds
val GTUStopTimeEvent.optTime get() = if (hasTime()) time else null
val GTUStopTimeEvent.optTimeMs get() = optTime?.secToMs()
val GTUStopTimeEvent.optTimeInstant get() = if (hasTime()) time.secsToInstant() else null
val GTUStopTimeEvent.optUncertainty get() = if (hasUncertainty()) uncertainty else null
val GTUStopTimeEvent.optScheduledTime get() = if (hasScheduledTime()) scheduledTime else null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,34 @@ import org.mtransit.android.commons.UriUtils
import org.mtransit.android.commons.data.RouteDirectionStop
import org.mtransit.android.commons.data.Schedule
import org.mtransit.android.commons.provider.status.StatusProviderContract
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours

private const val DEFAULT_MAX_DATA_REQUEST = 3 // yesterday service ending + today + tomorrow?
private val DEFAULT_LOOK_BEHIND = 1.hours

fun Context.getRDSSchedule(
authority: String,
rdsList: Iterable<RouteDirectionStop>,
includeCancelledTimestamps: Boolean = false,
lookBehind: Duration = DEFAULT_LOOK_BEHIND,
maxDataRequest: Int = DEFAULT_MAX_DATA_REQUEST,
) = rdsList.mapNotNull {
getRDSSchedule(authority, it, includeCancelledTimestamps)
getRDSSchedule(
authority = authority,
rds = it,
includeCancelledTimestamps = includeCancelledTimestamps,
lookBehind = lookBehind,
maxDataRequest = maxDataRequest,
)
}

fun Context.getRDSSchedule(
authority: String,
rds: RouteDirectionStop,
includeCancelledTimestamps: Boolean = false,
lookBehind: Duration = DEFAULT_LOOK_BEHIND,
maxDataRequest: Int = DEFAULT_MAX_DATA_REQUEST,
): Schedule? = try {
contentResolver.query(
Uri.withAppendedPath(
Expand All @@ -29,8 +43,8 @@ fun Context.getRDSSchedule(
),
StatusProviderContract.PROJECTION_STATUS,
Schedule.ScheduleStatusFilter(rds).apply {
setLookBehindInMs(1.hours.inWholeMilliseconds)
setMaxDataRequests(3) // yesterday service ending + today + tomorrow?
setLookBehindInMs(lookBehind.inWholeMilliseconds)
setMaxDataRequests(maxDataRequest)
setIncludeCancelledTimestamps(includeCancelledTimestamps)
}.let { it.toJSONStringStatic(it) },
null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.mtransit.android.commons.provider.serviceupdate

import org.mtransit.android.commons.MTLog
import org.mtransit.android.commons.data.RouteDirectionStop
import org.mtransit.android.commons.data.ServiceUpdate
import org.mtransit.android.commons.data.makeServiceUpdateNoneList
import org.mtransit.android.commons.provider.GTFSRealTimeProvider
Expand Down Expand Up @@ -30,22 +29,26 @@ object GTFSRealTimeServiceAlertsProvider {

@JvmStatic
fun GTFSRealTimeProvider.getCached(filter: ServiceUpdateProviderContract.Filter) =
((filter.poi as? RouteDirectionStop)?.getTargetUUIDs(this, includeAgencyTag = true, includeRouteType = true, includeStopTags = true)
?: filter.routeDirection?.getTargetUUIDs(this, includeAgencyTag = true, includeRouteType = true)
?: filter.route?.getTargetUUIDs(this, includeAgencyTag = true, includeRouteType = true))
filter.getTargetUUIDs(this, includeAgencyTag = true, includeRouteType = true, includeStopTags = true)
?.let { targetUUIDs ->
val tripIds = filter.targetAuthority?.let { targetAuthority ->
filter.routeId?.let { routeId ->
context?.getTripIds(targetAuthority, routeId, filter.directionId)
}
}
targetUUIDs to tripIds // trip IDs not required for GTFS Alerts
targetUUIDs to tripIds?.takeIf { it.isNotEmpty() } // trip IDs not required for GTFS Alerts
}?.let { (targetUUIDs, tripIds) ->
getCached(targetUUIDs, tripIds)
}

fun GTFSRealTimeProvider.getCached(targetUUIDs: Map<String, String>, tripIds: List<String>?) = buildList {
getCachedServiceUpdatesS(targetUUIDs.keys, tripIds)?.let {
tripIds?.let {
// trip IDs preferred for all result filtered correctly
getCachedServiceUpdatesS(targetUUIDs.keys, tripIds)?.takeIf { it.isNotEmpty() }
} ?: run {
// fallback to showing all w/o filtering trip IDs (main issue would be RDS UI showing other Direction alerts)
getCachedServiceUpdatesS(targetUUIDs.keys)
}?.let {
addAll(it)
}
}.map { it.apply { targetUUID = targetUUIDs[it.targetUUID] ?: it.targetUUID } }
Expand Down
Loading