diff --git a/src/main/java/org/mtransit/android/commons/SpanUtils.java b/src/main/java/org/mtransit/android/commons/SpanUtils.java index 1401683d..c64ce13b 100644 --- a/src/main/java/org/mtransit/android/commons/SpanUtils.java +++ b/src/main/java/org/mtransit/android/commons/SpanUtils.java @@ -11,6 +11,7 @@ import android.text.style.TextAppearanceSpan; import android.text.style.TypefaceSpan; +import androidx.annotation.CheckResult; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -132,13 +133,12 @@ public static ForegroundColorSpan getNewTextColor(@ColorInt int color) { return new ForegroundColorSpan(color); } + @CheckResult @NonNull - public static CharSequence setAll(@Nullable CharSequence cs, @Nullable Object... spans) { - if (cs instanceof SpannableStringBuilder) { - return setAll((SpannableStringBuilder) cs, spans); - } else { - return setAll(new SpannableStringBuilder(cs), spans); - } + public static SpannableStringBuilder setAll(@Nullable CharSequence cs, @Nullable Object... spans) { + final SpannableStringBuilder ssb = cs instanceof SpannableStringBuilder ? (SpannableStringBuilder) cs + : cs == null ? new SpannableStringBuilder() : new SpannableStringBuilder(cs); + return setAllNN(ssb, spans); } @Nullable @@ -151,13 +151,12 @@ public static SpannableStringBuilder setAllNN(@NonNull SpannableStringBuilder ss return setNN(ssb, 0, ssb.length(), spans); } + @CheckResult @NonNull - public static CharSequence set(@Nullable CharSequence cs, int start, int end, @Nullable Object... spans) { - if (cs instanceof SpannableStringBuilder) { - return set((SpannableStringBuilder) cs, start, end, spans); - } else { - return set(new SpannableStringBuilder(cs), start, end, spans); - } + public static SpannableStringBuilder set(@Nullable CharSequence cs, int start, int end, @Nullable Object... spans) { + final SpannableStringBuilder ssb = cs instanceof SpannableStringBuilder ? (SpannableStringBuilder) cs + : cs == null ? new SpannableStringBuilder() : new SpannableStringBuilder(cs); + return set(ssb, start, end, spans); } @Nullable 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 a28e72b8..26dd8080 100644 --- a/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java +++ b/src/main/java/org/mtransit/android/commons/data/ServiceUpdate.java @@ -10,6 +10,7 @@ import org.mtransit.android.commons.ComparatorUtils; import org.mtransit.android.commons.CursorExtKt; import org.mtransit.android.commons.MTLog; +import org.mtransit.android.commons.SqlUtils; import org.mtransit.android.commons.TimeUtils; import org.mtransit.android.commons.provider.serviceupdate.ServiceUpdateProviderContract; @@ -50,6 +51,8 @@ public String getLogTag() { @Nullable private String textHTML; private int severity; + @Nullable + private final Boolean noService; private final String language; @NonNull private final String sourceLabel; @@ -65,6 +68,7 @@ public ServiceUpdate( @NonNull String text, @Nullable String optTextHTML, int severity, + @Nullable Boolean noService, @NonNull String sourceId, @NonNull String sourceLabel, @Nullable String originalId, @@ -78,6 +82,7 @@ public ServiceUpdate( this.text = text; this.textHTML = optTextHTML; this.severity = severity; + this.noService = noService; this.sourceId = sourceId; this.sourceLabel = sourceLabel; this.originalId = originalId; @@ -102,6 +107,10 @@ public boolean isSeverityWarning() { return isSeverityWarning(this.severity); } + public boolean isNoService() { + return Boolean.TRUE.equals(this.noService); + } + public static boolean isSeverityWarning(int severity) { return severity == SEVERITY_WARNING_UNKNOWN // || severity == SEVERITY_WARNING_AGENCY // @@ -203,21 +212,20 @@ public boolean shouldDisplay() { @NonNull @Override public String toString() { - return ServiceUpdate.class.getSimpleName() + '[' + // - "id:" + this.id + // - ',' + // - "oId:" + this.originalId + // - ',' + // - "tUUID:" + this.targetUUID + // - ',' + // - "tTrip:" + this.targetTripId + // - ',' + // - "lang:" + this.language + // - ',' + // - "txt:" + this.text + // - ',' + // - "svrt:" + this.severity + // - ']'; + final StringBuilder sb = new StringBuilder(ServiceUpdate.class.getSimpleName()); + sb.append('['); + sb.append("id:").append(this.id).append(','); + sb.append("oId:").append(this.originalId).append(','); + sb.append("tUUID:").append(this.targetUUID).append(','); + sb.append("tTrip:").append(this.targetTripId).append(','); + sb.append("lang:").append(this.language).append(','); + sb.append("txt:").append(this.text).append(','); + sb.append("svrt:").append(this.severity); + if (isNoService()) { + sb.append("noSrv:").append(this.noService); + } + sb.append(']'); + return sb.toString(); } public boolean isUseful() { @@ -248,7 +256,8 @@ public static ServiceUpdate fromCursor(@NonNull Cursor cursor) { final String originalId = CursorExtKt.optString(cursor, ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_ORIGINAL_ID, null); final String sourceLabel = cursor.getString(cursor.getColumnIndexOrThrow(ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_SOURCE_LABEL)); final String sourceId = cursor.getString(cursor.getColumnIndexOrThrow(ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_SOURCE_ID)); - return new ServiceUpdate(id, targetUUID, targetTripId, lastUpdateInMs, maxValidityInMs, text, htmlText, severity, sourceId, sourceLabel, originalId, language); + final Boolean noService = CursorExtKt.optBoolean(cursor, ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_NO_SERVICE, null); + return new ServiceUpdate(id, targetUUID, targetTripId, lastUpdateInMs, maxValidityInMs, text, htmlText, severity, noService, sourceId, sourceLabel, originalId, language); } /** @@ -268,7 +277,8 @@ public Object[] getCursorRow() { language, originalId, sourceLabel, - sourceId + sourceId, + noService == null ? null : SqlUtils.toSQLBoolean(noService) }; } @@ -289,6 +299,9 @@ public ContentValues toContentValues() { contentValues.put(ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_ORIGINAL_ID, this.originalId); contentValues.put(ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_SOURCE_LABEL, this.sourceLabel); contentValues.put(ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_SOURCE_ID, this.sourceId); + if (this.noService != null) { + contentValues.put(ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_NO_SERVICE, SqlUtils.toSQLBoolean(this.noService)); + } return contentValues; } 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 fc3f1a32..e9b310a1 100644 --- a/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt +++ b/src/main/java/org/mtransit/android/commons/data/ServiceUpdateKtx.kt @@ -55,23 +55,25 @@ fun makeServiceUpdate( text: String, optTextHTML: String? = null, severity: Int, + noService: Boolean? = null, sourceId: String, sourceLabel: String, originalId: String? = null, language: String ) = makeServiceUpdate( - optId, - targetUUID, - targetTripId, - lastUpdate.toMillis(), - maxValidity.inWholeMilliseconds, - text, - optTextHTML, - severity, - sourceId, - sourceLabel, - originalId, - language, + optId = optId, + targetUUID = targetUUID, + targetTripId = targetTripId, + lastUpdateMs = lastUpdate.toMillis(), + maxValidityMs = maxValidity.inWholeMilliseconds, + text = text, + optTextHTML = optTextHTML, + severity = severity, + noService = noService, + sourceId = sourceId, + sourceLabel = sourceLabel, + originalId = originalId, + language = language, ) fun makeServiceUpdate( @@ -83,6 +85,7 @@ fun makeServiceUpdate( text: String, optTextHTML: String? = null, severity: Int, + noService: Boolean? = null, sourceId: String, sourceLabel: String, originalId: String? = null, @@ -96,6 +99,7 @@ fun makeServiceUpdate( text, optTextHTML, severity, + noService, sourceId, sourceLabel, originalId, 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 a948586a..d30eccae 100644 --- a/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/GTFSRealTimeProvider.java @@ -895,11 +895,12 @@ private List loadAgencyServiceUpdateDataFromWWW(@NonNull Context try { GtfsRealtime.FeedMessage gFeedMessage = GtfsRealtime.FeedMessage.parseFrom(response.body().bytes()); List> alertsWithIdPair = GtfsRealtimeExt.toAlertsWithIdPair(gFeedMessage.getEntityList()); + if (Constants.DEBUG) MTLog.d(this, "loadAgencyDataFromWWW() > GTFS alerts[%s]: ", alertsWithIdPair.size()); for (Pair gAlertAndId : GtfsRealtimeExt.sortAlertsPair(alertsWithIdPair, newLastUpdateInMs)) { final GtfsRealtime.Alert gAlert = gAlertAndId.getFirst(); final String feedEntityId = gAlertAndId.getSecond(); if (Constants.DEBUG) { - MTLog.d(this, "loadAgencyServiceUpdateDataFromWWW() > GTFS alert[%s]: %s.", feedEntityId, GtfsRealtimeExt.toStringExt(gAlert)); + MTLog.d(this, "loadAgencyServiceUpdateDataFromWWW() > - GTFS[%s] %s", feedEntityId, GtfsRealtimeExt.toStringExt(gAlert)); } final Set alertsServiceUpdates = processAlerts(context, sourceLabel, feedEntityId, newLastUpdateInMs, gAlert, ignoreDirection); if (alertsServiceUpdates != null && !alertsServiceUpdates.isEmpty()) { @@ -1012,6 +1013,7 @@ private HashSet processAlerts( languages.addAll(descriptionTexts.keySet()); languages.addAll(urlTexts.keySet()); setServiceUpdateLanguages(languages); + final boolean noService = gEffect == GtfsRealtime.Alert.Effect.NO_SERVICE; HashSet serviceUpdates = new HashSet<>(); long serviceUpdateMaxValidityInMs = getServiceUpdateMaxValidityInMs(); for (Map.Entry entry : targetUUIDAndTripId.entrySet()) { @@ -1031,6 +1033,7 @@ private HashSet processAlerts( targetUUID, targetTripId, severity == null ? ServiceUpdate.SEVERITY_INFO_UNKNOWN : severity, + noService, language ); serviceUpdates.add(newServiceUpdate); @@ -1100,6 +1103,7 @@ private ServiceUpdate generateNewServiceUpdate( @NonNull String targetUUID, @Nullable String targetTripId, int severity, + boolean noService, String language ) { final String header = headerTexts.get(language); @@ -1123,6 +1127,7 @@ private ServiceUpdate generateNewServiceUpdate( ServiceUpdateCleaner.makeText(header, description), ServiceUpdateCleaner.makeTextHTML(header, textHtml, url), severity, + noService, AGENCY_SOURCE_ID, sourceLabel, feedEntityId, @@ -1600,12 +1605,10 @@ public String getLogTag() { 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 - dbVersion++; // add "vehicle_location.report_timestamp" column - dbVersion++; // change "vehicle_location.[bearing|speed]" unit to Int - dbVersion++; // add "service_update.trip_id" column dbVersion++; // add "status" table + dbVersion++; // add "vehicle_location" table + dbVersion = ServiceUpdateDbHelper.bumpDBVersion(dbVersion); + dbVersion = VehicleLocationDbHelper.bumpDBVersion(dbVersion); } return dbVersion; } 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 be1a5697..42825f31 100644 --- a/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/NextBusProvider.java @@ -1884,6 +1884,7 @@ private void addServiceUpdates(@NonNull String targetUUID, int severity, @Nullab ) ), severity, + null, AGENCY_SOURCE_ID, this.sourceLabel, this.currentMessageId, @@ -1911,6 +1912,7 @@ private void addServiceUpdates(@NonNull String targetUUID, int severity, @Nullab ) ), severity, + null, AGENCY_SOURCE_ID, this.sourceLabel, this.currentMessageId, @@ -1991,9 +1993,9 @@ public String getLogTag() { 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 - dbVersion++; // add "service_update.trip_id" column + dbVersion = ServiceUpdateDbHelper.bumpDBVersion(dbVersion); + dbVersion = VehicleLocationDbHelper.bumpDBVersion(dbVersion); } return dbVersion; } 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 52bf2117..ff34b3c2 100644 --- a/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/OCTranspoProvider.java @@ -1160,6 +1160,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc text, textHtml, severity, + null, AGENCY_SOURCE_ID, this.sourceLabel, null, // TODO original ID? @@ -1174,6 +1175,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc text, textHtml, severity, + null, AGENCY_SOURCE_ID, this.sourceLabel, null, // TODO original ID? @@ -1520,8 +1522,7 @@ public String getLogTag() { public static int getDbVersion(@NonNull Context context) { if (dbVersion < 0) { dbVersion = context.getResources().getInteger(R.integer.oc_transpo_db_version); - dbVersion++; // add "service_update.original_id" column - dbVersion++; // add "service_update.trip_id" column + dbVersion = ServiceUpdateDbHelper.bumpDBVersion(dbVersion); } return dbVersion; } 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 d625ae88..df495030 100644 --- a/src/main/java/org/mtransit/android/commons/provider/RTCQuebecProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/RTCQuebecProvider.java @@ -1296,6 +1296,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc textSb.toString(), textHTMLSb.toString(), severity, + null, AGENCY_SOURCE_ID, sourceLabel, null, // TODO? @@ -1526,8 +1527,7 @@ public String getLogTag() { static int getDbVersion(@NonNull Context context) { if (dbVersion < 0) { dbVersion = context.getResources().getInteger(R.integer.rtc_quebec_db_version); - dbVersion++; // add "service_update.original_id" column - dbVersion++; // add "service_update.trip_id" column + dbVersion = ServiceUpdateDbHelper.bumpDBVersion(dbVersion); } return dbVersion; } 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 3176e847..10f34cd5 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoApiProvider.java @@ -830,6 +830,7 @@ private List parseAgencyJSONArrivalsServiceUpdates( routeLink ), severity, + null, SERVICE_UPDATE_SOURCE_ID, sourceLabel, null, // no original ID @@ -861,6 +862,7 @@ private List parseAgencyJSONArrivalsServiceUpdates( stopLink ), severity, + null, SERVICE_UPDATE_SOURCE_ID, sourceLabel, null, // no original ID @@ -1158,6 +1160,7 @@ protected List parseAgencyJSONMessageResults( fText, textHtml, severity, + null, SERVICE_UPDATE_SOURCE_ID, sourceLabel, originalId, @@ -1695,8 +1698,7 @@ public String getLogTag() { public static int getDbVersion(@NonNull Context context) { if (dbVersion < 0) { dbVersion = context.getResources().getInteger(R.integer.stm_info_api_db_version); - dbVersion++; // add "service_update.original_id" column - dbVersion++; // add "service_update.trip_id" column + dbVersion = ServiceUpdateDbHelper.bumpDBVersion(dbVersion); } return dbVersion; } 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 5fccc1e5..4f591cad 100644 --- a/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java +++ b/src/main/java/org/mtransit/android/commons/provider/StmInfoSubwayProvider.java @@ -519,6 +519,7 @@ private ServiceUpdate parseAgencyJsonText(JSONObject jMetroObject, String target jMetroDataText, textHtml, severity, + null, AGENCY_SOURCE_ID, sourceLabel, null, // no original ID @@ -922,8 +923,7 @@ public String getLogTag() { public static int getDbVersion(@NonNull Context context) { if (dbVersion < 0) { dbVersion = context.getResources().getInteger(R.integer.stm_info_db_version); - dbVersion++; // add "service_update.original_id" column - dbVersion++; // add "service_update.trip_id" column + dbVersion = ServiceUpdateDbHelper.bumpDBVersion(dbVersion); } 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 5a7040fc..7a68406a 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 @@ -1,8 +1,10 @@ package org.mtransit.android.commons.provider.gtfs +import com.google.transit.realtime.GtfsRealtime import com.google.transit.realtime.TripUpdateKt import com.google.transit.realtime.TripUpdateKt.StopTimeEventKt import com.google.transit.realtime.alertOrNull +import com.google.transit.realtime.headerOrNull import com.google.transit.realtime.tripUpdateOrNull import com.google.transit.realtime.vehicleOrNull import org.mtransit.android.commons.Constants @@ -22,6 +24,7 @@ import com.google.transit.realtime.GtfsRealtime.TimeRange as GTimeRange import com.google.transit.realtime.GtfsRealtime.TranslatedString as GTranslatedString import com.google.transit.realtime.GtfsRealtime.TranslatedString.Translation as GTSTranslation import com.google.transit.realtime.GtfsRealtime.TripDescriptor as GTripDescriptor +import com.google.transit.realtime.GtfsRealtime.TripDescriptor.ModifiedTripSelector as GTDModifiedTripSelector import com.google.transit.realtime.GtfsRealtime.TripUpdate as GTripUpdate import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeEvent as GTUStopTimeEvent import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate as GTUStopTimeUpdate @@ -42,15 +45,57 @@ object GtfsRealtimeExt { } } + @JvmStatic + fun GtfsRealtime.FeedMessage.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { + append("FeedMessage:") + append( + buildList { + headerOrNull?.let { add("header:${it.toStringExt(debug)}") } + add(entityList.toStringExt(debug = debug)) + }.joinToStringList() + ) + } + + @JvmStatic + fun GtfsRealtime.FeedHeader.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { + append("FeedHeader:") + append( + buildList { + optGtfsRealtimeVersion?.let { add("gtfsRTVersion:$it") } + optTimestamp?.let { add("timestamp:$it") } + optFeedVersion?.let { add("feedVersion:$it") } + }.joinToStringList() + ) + } + + val GtfsRealtime.FeedHeader.optTimestamp get() = if (hasTimestamp()) timestamp else null + val GtfsRealtime.FeedHeader.optFeedVersion get() = if (hasFeedVersion()) feedVersion else null + val GtfsRealtime.FeedHeader.optGtfsRealtimeVersion get() = if (hasGtfsRealtimeVersion()) gtfsRealtimeVersion else null + @JvmStatic fun GFeedEntity.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { append("FeedEntity:") - append("{") - append("id:").append(id).append(", ") - tripUpdateOrNull?.let { append(it.toStringExt(debug)).append(", ") } - vehicleOrNull?.let { append(it.toStringExt(debug)).append(", ") } - alertOrNull?.let { append(it.toStringExt(debug)).append(", ") } - append("}") + append( + buildList { + add("id:$id") + tripUpdateOrNull?.let { add(it.toStringExt(debug)) } + vehicleOrNull?.let { add(it.toStringExt(debug)) } + alertOrNull?.let { add(it.toStringExt(debug)) } + }.joinToStringList() + ) + } + + @JvmName("toStringExtFeedEntity") + @JvmStatic + @JvmOverloads + fun List?.toStringExt(short: Boolean = false, debug: Boolean = Constants.DEBUG): String = buildString { + append(if (short) "FEs[" else "FeedEntity[").append(this@toStringExt?.size ?: 0).append("]") + if (debug) { + this@toStringExt?.take(MAX_LIST_ITEMS)?.forEachIndexed { idx, feedEntity -> + if (idx > 0) append(",") else append("=") + append(feedEntity.toStringExt(debug)) + } + } } @JvmStatic @@ -163,7 +208,7 @@ object GtfsRealtimeExt { @JvmStatic @JvmOverloads - fun GTripUpdate.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { + fun GTripUpdate.toStringExt(debug: Boolean = Constants.DEBUG): String = buildString { append("TripUpdate:") append( buildList { @@ -172,7 +217,7 @@ object GtfsRealtimeExt { optStopTimeUpdateList?.let { add(it.toStringExt(short = true)) } optTimestamp?.let { add("timestamp=$timestamp") } optDelay?.let { add("delay=$delay") } - }.joinToString(separator = ",", prefix = "{", postfix = "}") + }.joinToStringList() ) } @@ -187,7 +232,7 @@ object GtfsRealtimeExt { @JvmName("toStringExtStopTimeUpdate") @JvmStatic @JvmOverloads - fun List?.toStringExt(short: Boolean = false, debug: Boolean = Constants.DEBUG) = buildString { + fun List?.toStringExt(short: Boolean = false, debug: Boolean = Constants.DEBUG): String = buildString { append(if (short) "STUs[" else "StopTimeUpdate[").append(this@toStringExt?.size ?: 0).append("]") if (debug) { this@toStringExt?.take(MAX_LIST_ITEMS)?.forEachIndexed { idx, stopTimeUpdate -> @@ -201,15 +246,17 @@ object GtfsRealtimeExt { @JvmOverloads fun GTUStopTimeUpdate.toStringExt(short: Boolean = false) = buildString { append(if (short) "STU:" else "StopTimeUpdate:") - append("{") - optStopSequence?.let { append("stopSeq=").append(stopSequence).append(", ") } - optStopId?.let { append("stopId=").append(stopId).append(", ") } - optArrival?.let { append("arrival=").append(it.toStringExt(short = true)).append(", ") } - optDeparture?.let { append("departure=").append(it.toStringExt(short = true)).append(", ") } - optDepartureOccupancyStatus?.let { append("depOcc=").append(departureOccupancyStatus).append(", ") } - optScheduleRelationship?.let { append("schedRel=").append(scheduleRelationship).append(", ") } - optStopTimeProperties?.let { append(it.toStringExt(short = true)).append(", ") } - append("}") + append( + buildList { + optStopSequence?.let { add("stopSeq=$stopSequence") } + optStopId?.let { add("stopId=$stopId") } + optArrival?.let { add("arrival=" + it.toStringExt(short = true)) } + optDeparture?.let { add("departure=" + it.toStringExt(short = true)) } + optDepartureOccupancyStatus?.let { add("depOcc=$departureOccupancyStatus") } + optScheduleRelationship?.let { add("schedRel=$scheduleRelationship") } + optStopTimeProperties?.let { add(it.toStringExt(short = true)) } + }.joinToStringList() + ) } val GTUStopTimeUpdate.optStopSequence get() = if (hasStopSequence()) stopSequence else null @@ -224,12 +271,14 @@ object GtfsRealtimeExt { @JvmOverloads fun GTUStopTimeEvent.toStringExt(short: Boolean = false) = buildString { append(if (short) "STE:" else "StopTimeEvent:") - append("{") - optDelay?.let { append("delay=").append(delay).append(", ") } - optTime?.let { append("time=").append(time).append(", ") } - optUncertainty?.let { append("uncertainty=").append(uncertainty).append(", ") } - optScheduledTime?.let { append("schedTime=").append(scheduledTime).append(", ") } - append("}") + append( + buildList { + optDelay?.let { add("delay=$delay") } + optTime?.let { add("time=$time") } + optUncertainty?.let { add("uncertainty=$uncertainty") } + optScheduledTime?.let { add("schedTime=$scheduledTime") } + }.joinToStringList() + ) } val GTUStopTimeEvent.optDelay get() = if (hasDelay()) delay else null @@ -243,12 +292,14 @@ object GtfsRealtimeExt { @JvmOverloads fun GTUStopTimeUpdate.StopTimeProperties.toStringExt(short: Boolean = false) = buildString { append(if (short) "STP:" else "StopTimeProperties:") - append("{") - optAssignedStopId?.let { append("aStopId=").append(assignedStopId).append(", ") } - optStopHeadsign?.let { append("stopHeadsign=").append(stopHeadsign).append(", ") } - optPickupType?.let { append("pickupType=").append(pickupType).append(", ") } - optDropOffType?.let { append("dropOffType=").append(dropOffType).append(", ") } - append("}") + append( + buildList { + optAssignedStopId?.let { add("aStopId=$assignedStopId") } + optStopHeadsign?.let { add("stopHeadsign=$stopHeadsign") } + optPickupType?.let { add("pickupType=$pickupType") } + optDropOffType?.let { add("dropOffType=$dropOffType") } + }.joinToStringList() + ) } val GTUStopTimeUpdate.StopTimeProperties.optAssignedStopId get() = if (hasAssignedStopId()) assignedStopId else null @@ -260,36 +311,46 @@ object GtfsRealtimeExt { @JvmOverloads fun GVehiclePosition.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { append("VehiclePosition:") - append("{") - optTrip?.let { append(it.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(", ") - 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("}") + append( + buildList { + optTrip?.let { add(it.toStringExt(short = true)) } + optPosition?.let { add(it.toStringExt(short = true)) } + optVehicle?.let { add(it.toStringExt(short = true)) } + optCurrentStopSequence?.let { add("currentStopSequence=$currentStopSequence") } + optCurrentStatus?.let { add("currentStatus=$currentStatus") } + optStopId?.let { add("stopId=$stopId") } + optTimestamp?.let { add("timestamp=$timestamp") } + optOccupancyPercentage?.let { add("occupancyPct=$occupancyPercentage") } + optOccupancyStatus?.let { add("occupancyStatus=$occupancyStatus") } + optCongestionLevel?.let { add("congestionLevel=$congestionLevel") } + }.joinToStringList() + ) } val GVehiclePosition.optTrip get() = if (hasTrip()) trip else null val GVehiclePosition.optTimestamp get() = if (hasTimestamp()) timestamp else null val GVehiclePosition.optPosition get() = if (hasPosition()) position else null val GVehiclePosition.optVehicle get() = if (hasVehicle()) vehicle else null + val GVehiclePosition.optCurrentStopSequence get() = if (hasCurrentStopSequence()) currentStopSequence else null + val GVehiclePosition.optCurrentStatus get() = if (hasCurrentStatus()) currentStatus else null + val GVehiclePosition.optStopId get() = if (hasStopId()) stopId else null + val GVehiclePosition.optOccupancyPercentage get() = if (hasOccupancyPercentage()) occupancyPercentage else null + val GVehiclePosition.optOccupancyStatus get() = if (hasOccupancyStatus()) occupancyStatus else null + val GVehiclePosition.optCongestionLevel get() = if (hasCongestionLevel()) congestionLevel else null @JvmStatic @JvmOverloads fun GPosition.toStringExt(short: Boolean = false) = buildString { append(if (short) "P:" else "Position:") - append("{") - 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("}") + append( + buildList { + optLatitude?.let { add("lat=$it") } + optLongitude?.let { add("lon=$it") } + optBearing?.let { add("bearing=$it") } + optSpeed?.let { add("speed=$it") } + optOdometer?.let { add("odometer=$it") } + }.joinToStringList() + ) } val GPosition.optLatitude get() = if (hasLatitude()) latitude else null @@ -302,12 +363,14 @@ object GtfsRealtimeExt { @JvmOverloads fun GVehicleDescriptor.toStringExt(short: Boolean = false) = buildString { append(if (short) "VD:" else "VehicleDescriptor:") - append("{") - optId?.let { append("id=").append(id).append(", ") } - optLabel?.let { append("label=").append(label).append(", ") } - optLicensePlate?.let { append("licensePlate=").append(licensePlate).append(", ") } - optWheelchairAccessible?.let { append("a18n=").append(wheelchairAccessible).append(", ") } - append("}") + append( + buildList { + optId?.let { add("id=$id") } + optLabel?.let { add("label=$label") } + optLicensePlate?.let { add("licensePlate=$licensePlate") } + optWheelchairAccessible?.let { add("a18n=$wheelchairAccessible") } + }.joinToStringList() + ) } val GVehicleDescriptor.optId get() = if (hasId()) id else null @@ -319,19 +382,29 @@ object GtfsRealtimeExt { @JvmOverloads fun GAlert.toStringExt(debug: Boolean = Constants.DEBUG) = buildString { append("Alert:") - append("{") - 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("}") + append( + buildList { + add(informedEntityList.toStringExt(short = true)) + add(activePeriodList.toStringExt(short = true)) + optCause?.let { add("cause=$it") } + optCauseDetail?.takeIf { debug }?.let { add("(${it.toStringExt("detail")})") } + optEffect?.let { add("effect=$it") } + optEffectDetail?.takeIf { debug }?.let { add("(${it.toStringExt("detail")})") } + optHeaderText?.let { add(it.toStringExt("header", debug)) } + optDescriptionText?.let { add(it.toStringExt("desc", debug)) } + optUrl?.let { add(it.toStringExt("url", debug)) } + }.joinToStringList() + ) } + val GAlert.optCause get() = if (hasCause()) cause else null + val GAlert.optCauseDetail get() = if (hasCauseDetail()) causeDetail else null + val GAlert.optEffect get() = if (hasEffect()) effect else null + val GAlert.optEffectDetail get() = if (hasEffectDetail()) effectDetail else null + val GAlert.optHeaderText get() = if (hasHeaderText()) headerText else null + val GAlert.optDescriptionText get() = if (hasDescriptionText()) descriptionText else null + val GAlert.optUrl get() = if (hasUrl()) url else null + @JvmName("toStringExtEntity") @JvmStatic @JvmOverloads @@ -388,7 +461,7 @@ object GtfsRealtimeExt { optStopId?.let { add((if (short) "s=" else "stopId=") + stopId) } optDirectionId?.let { add((if (short) "d=" else "directionId=") + directionId) } optTrip?.let { add(it.toStringExt(short)) } - }.joinToString(separator = "|", prefix = "{", postfix = "}") + }.joinToStringOption() ) } @@ -412,7 +485,7 @@ object GtfsRealtimeExt { optScheduleRelationship?.let { add((if (short) "sr=" else "schedRel=") + scheduleRelationship) } optStartDate?.let { add((if (short) "sd=" else "startDate=") + startDate) } optStartTime?.let { add((if (short) "st=" else "startTime=") + startTime) } - }.joinToString(separator = "|", prefix = "{", postfix = "}") + }.joinToStringOption() ) } @@ -426,16 +499,23 @@ object GtfsRealtimeExt { @JvmStatic @JvmOverloads - fun GTripDescriptor.ModifiedTripSelector.toStringExt(short: Boolean = false) = buildString { + fun GTDModifiedTripSelector.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).append("|") - append("}") + append( + buildList { + optModificationsId?.let { add((if (short) "m=" else "modificationsId=") + modificationsId) } + optAffectedTripId?.let { add((if (short) "at=" else "affectedTripId=") + affectedTripId) } + optStartDate?.let { add((if (short) "sd=" else "startDate=") + startDate) } + optStartTime?.let { add((if (short) "st=" else "startTime=") + startTime) } + }.joinToStringList() + ) } + val GTDModifiedTripSelector.optModificationsId get() = if (hasModificationsId()) modificationsId else null + val GTDModifiedTripSelector.optAffectedTripId get() = if (hasAffectedTripId()) affectedTripId else null + val GTDModifiedTripSelector.optStartDate get() = if (hasStartDate()) startDate else null + val GTDModifiedTripSelector.optStartTime get() = if (hasStartTime()) startTime else null + @JvmOverloads @JvmStatic fun GTranslatedString.toStringExt(name: String = "i18n", debug: Boolean = Constants.DEBUG) = buildString { @@ -472,4 +552,7 @@ object GtfsRealtimeExt { this.clearDelay() } } + + private fun Iterable.joinToStringList() = joinToString(separator = ",", prefix = "{", postfix = "}") + private fun Iterable.joinToStringOption() = joinToString(separator = "|", prefix = "{", postfix = "}") } \ No newline at end of file diff --git a/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateDbHelper.kt b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateDbHelper.kt index 6cca551c..cab3afcb 100644 --- a/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateDbHelper.kt +++ b/src/main/java/org/mtransit/android/commons/provider/serviceupdate/ServiceUpdateDbHelper.kt @@ -29,6 +29,7 @@ abstract class ServiceUpdateDbHelper( const val T_SERVICE_UPDATE_K_SOURCE_LABEL = "source_label" const val T_SERVICE_UPDATE_K_ORIGINAL_ID = "original_id" const val T_SERVICE_UPDATE_K_SOURCE_ID = "source_id" + const val T_SERVICE_UPDATE_K_NO_SERVICE = "no_service" @Suppress("unused") val T_SERVICE_UPDATE_SQL_CREATE = getSqlCreateBuilder(T_SERVICE_UPDATE).build() @@ -53,6 +54,16 @@ abstract class ServiceUpdateDbHelper( .appendColumn(T_SERVICE_UPDATE_K_ORIGINAL_ID, SqlUtils.TXT) .appendColumn(T_SERVICE_UPDATE_K_SOURCE_LABEL, SqlUtils.TXT) .appendColumn(T_SERVICE_UPDATE_K_SOURCE_ID, SqlUtils.TXT) + .appendColumn(T_SERVICE_UPDATE_K_NO_SERVICE, SqlUtils.INT) + + @JvmStatic + fun bumpDBVersion(dbVersion: Int): Int { + var dbVersion = dbVersion + dbVersion++ // add "original_id" column + dbVersion++ // add "trip_id" column + dbVersion++ // add "no_service" column + return dbVersion + } } abstract val dbName: String 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 a89eb407..0bd8a311 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 @@ -53,6 +53,7 @@ public static void append(@NonNull UriMatcher uriMatcher, @NonNull String author .appendTableColumn(ServiceUpdateDbHelper.T_SERVICE_UPDATE, ServiceUpdateDbHelper.T_SERVICE_UPDATE_K_ORIGINAL_ID, Columns.T_SERVICE_UPDATE_K_ORIGINAL_ID) .appendTableColumn(ServiceUpdateDbHelper.T_SERVICE_UPDATE, ServiceUpdateDbHelper.T_SERVICE_UPDATE_K_SOURCE_LABEL, ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_SOURCE_LABEL) // .appendTableColumn(ServiceUpdateDbHelper.T_SERVICE_UPDATE, ServiceUpdateDbHelper.T_SERVICE_UPDATE_K_SOURCE_ID, ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_SOURCE_ID) // + .appendTableColumn(ServiceUpdateDbHelper.T_SERVICE_UPDATE, ServiceUpdateDbHelper.T_SERVICE_UPDATE_K_NO_SERVICE, ServiceUpdateProviderContract.Columns.T_SERVICE_UPDATE_K_NO_SERVICE) // .build(); // @formatter:on 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 fc681641..294cde5c 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 @@ -75,7 +75,8 @@ public interface ServiceUpdateProviderContract extends ProviderContract { Columns.T_SERVICE_UPDATE_K_LANGUAGE, Columns.T_SERVICE_UPDATE_K_ORIGINAL_ID, Columns.T_SERVICE_UPDATE_K_SOURCE_LABEL, - Columns.T_SERVICE_UPDATE_K_SOURCE_ID + Columns.T_SERVICE_UPDATE_K_SOURCE_ID, + Columns.T_SERVICE_UPDATE_K_NO_SERVICE }; class Columns { @@ -91,6 +92,7 @@ class Columns { public static final String T_SERVICE_UPDATE_K_SOURCE_LABEL = "source_label"; public static final String T_SERVICE_UPDATE_K_ORIGINAL_ID = "original_id"; public static final String T_SERVICE_UPDATE_K_SOURCE_ID = "source_id"; + public static final String T_SERVICE_UPDATE_K_NO_SERVICE = "no_service"; } @SuppressWarnings("WeakerAccess") diff --git a/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt b/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt index 831f19a4..bd094f93 100644 --- a/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt +++ b/src/main/java/org/mtransit/android/commons/provider/status/GTFSRealTimeTripUpdatesProvider.kt @@ -18,6 +18,7 @@ import org.mtransit.android.commons.provider.GTFSRealTimeProvider.isIGNORE_DIREC import org.mtransit.android.commons.provider.gtfs.GtfsRealTimeStorage import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optDirectionId import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.optTrip +import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.sortTripUpdates import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toStringExt import org.mtransit.android.commons.provider.gtfs.GtfsRealtimeExt.toTripUpdates import org.mtransit.android.commons.provider.gtfs.getRDS @@ -279,7 +280,16 @@ object GTFSRealTimeTripUpdatesProvider : MTLog.Loggable { HttpURLConnection.HTTP_OK -> { try { try { - File(context.cacheDir, GTFS_RT_TRIP_UPDATE_PB_FILE_NAME).writeBytes(response.body.bytes()) + val responseBodyByes = response.body.bytes() + File(context.cacheDir, GTFS_RT_TRIP_UPDATE_PB_FILE_NAME).writeBytes(responseBodyByes) + if (Constants.DEBUG) { + val gFeedMessage = GFeedMessage.parseFrom(responseBodyByes) + val gTripUpdates = gFeedMessage.entityList.toTripUpdates() + MTLog.d(this@GTFSRealTimeTripUpdatesProvider, "loadAgencyDataFromWWW() > GTFS trip updates[${gTripUpdates.size}]: ") + gTripUpdates.sortTripUpdates(TimeUtils.currentTimeMillis()).forEach { gTripUpdate -> + MTLog.d(this@GTFSRealTimeTripUpdatesProvider, "loadAgencyDataFromWWW() > - GTFS ${gTripUpdate.toStringExt()}") + } + } } catch (e: IOException) { MTLog.w(this@GTFSRealTimeTripUpdatesProvider, e, "loadAgencyDataFromWWW() > error while saving GTFS RT Trip Updates data!") } 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 b65d64ed..0459555c 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 @@ -176,12 +176,12 @@ object GTFSRealTimeVehiclePositionsProvider { try { val gFeedMessage = GFeedMessage.parseFrom(response.body.bytes()) val gVehiclePositions = gFeedMessage.entityList.toVehicles() + if (Constants.DEBUG) { + MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > GTFS vehicles[${gVehiclePositions.size}]: ") + } for (gVehiclePosition in gVehiclePositions.sortVehicles(newLastUpdateInMs)) { if (Constants.DEBUG) { - MTLog.d( - this@GTFSRealTimeVehiclePositionsProvider, - "loadAgencyDataFromWWW() > GTFS vehicle: ${gVehiclePosition.toStringExt()}." - ) + MTLog.d(this@GTFSRealTimeVehiclePositionsProvider, "loadAgencyDataFromWWW() > - GTFS ${gVehiclePosition.toStringExt()}.") } processVehiclePositions(newLastUpdateInMs, gVehiclePosition, ignoreDirection) ?.takeIf { it.isNotEmpty() } 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 a82882cc..1b8445d3 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 @@ -46,6 +46,14 @@ abstract class VehicleLocationDbHelper( .appendColumn(T_VEHICLE_LOCATION_K_LONGITUDE, SqlUtils.REAL) .appendColumn(T_VEHICLE_LOCATION_K_BEARING, SqlUtils.INT) .appendColumn(T_VEHICLE_LOCATION_K_SPEED, SqlUtils.INT) + + @JvmStatic + fun bumpDBVersion(dbVersion: Int): Int { + var dbVersion = dbVersion + dbVersion++ // add "report_timestamp" column + dbVersion++ // change "[bearing|speed]" unit to Int + return dbVersion + } } abstract val dbName: String