From fee0f2c89244a2311bc3b966a8da3cadec0f22b5 Mon Sep 17 00:00:00 2001 From: deka91 Date: Wed, 20 May 2026 13:56:10 +0200 Subject: [PATCH 1/7] Implement DTOs for StopEventRequest --- .../ojp/data/dto/request/ser/LineDto.kt | 24 +++++++++ .../ojp/data/dto/request/ser/LineFilterDto.kt | 21 ++++++++ .../ojp/data/dto/request/ser/LocationDto.kt | 19 +++++++ .../ojp/data/dto/request/ser/ModeFilterDto.kt | 23 +++++++++ .../data/dto/request/ser/OperatorFilterDto.kt | 24 +++++++++ .../dto/request/ser/StopEventParamsDto.kt | 49 +++++++++++++++++++ .../dto/request/ser/StopEventRequestDto.kt | 30 ++++++++++++ .../ojp/domain/model/StopEventType.kt | 19 +++++++ 8 files changed, 209 insertions(+) create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineFilterDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/ModeFilterDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/OperatorFilterDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventRequestDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventType.kt diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineDto.kt new file mode 100644 index 00000000..83b0a3ca --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineDto.kt @@ -0,0 +1,24 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_PREFIX +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("Line", OJP_NAME_SPACE, "") +internal data class LineDto( + + @XmlElement(true) + @XmlSerialName("LineRef", SIRI_NAME_SPACE, SIRI_PREFIX) + val lineRef: String, + + @XmlElement(true) + @XmlSerialName("DirectionRef", SIRI_NAME_SPACE, SIRI_PREFIX) + val directionRef: String? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineFilterDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineFilterDto.kt new file mode 100644 index 00000000..b8f056e4 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LineFilterDto.kt @@ -0,0 +1,21 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("LineFilter", OJP_NAME_SPACE, "") +internal data class LineFilterDto( + + @XmlElement(true) + val line: List? = null, + + @XmlElement(true) + @XmlSerialName("Exclude", OJP_NAME_SPACE, "") + val exclude: Boolean? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt new file mode 100644 index 00000000..24eeaa31 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt @@ -0,0 +1,19 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("Location", OJP_NAME_SPACE, "") +internal data class LocationDto( + + @XmlElement(true) + @XmlSerialName("PlaceRef", OJP_NAME_SPACE, "") + val placeReference: PlaceReferenceDto, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/ModeFilterDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/ModeFilterDto.kt new file mode 100644 index 00000000..e26ca585 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/ModeFilterDto.kt @@ -0,0 +1,23 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.domain.model.PtMode +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("ModeFilter", OJP_NAME_SPACE, "") +internal data class ModeFilterDto( + + @XmlElement(true) + @XmlSerialName("PtMode", OJP_NAME_SPACE, "") + val ptMode: List? = null, + + @XmlElement(true) + @XmlSerialName("Exclude", OJP_NAME_SPACE, "") + val exclude: Boolean? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/OperatorFilterDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/OperatorFilterDto.kt new file mode 100644 index 00000000..c5722dc2 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/OperatorFilterDto.kt @@ -0,0 +1,24 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_PREFIX +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("OperatorFilter", OJP_NAME_SPACE, "") +internal data class OperatorFilterDto( + + @XmlElement(true) + @XmlSerialName("OperatorRef", SIRI_NAME_SPACE, SIRI_PREFIX) + val operatorRef: List? = null, + + @XmlElement(true) + @XmlSerialName("Exclude", OJP_NAME_SPACE, "") + val exclude: Boolean? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt new file mode 100644 index 00000000..180c1ccc --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt @@ -0,0 +1,49 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.domain.model.RealtimeData +import ch.opentransportdata.ojp.domain.model.StopEventType +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("Params", OJP_NAME_SPACE, "") +internal data class StopEventParamsDto( + + @XmlElement(true) + val modeFilter: ModeFilterDto? = null, + + @XmlElement(true) + val lineFilter: LineFilterDto? = null, + + @XmlElement(true) + val operatorFilter: OperatorFilterDto? = null, + + @XmlElement(true) + @XmlSerialName("NumberOfResults", OJP_NAME_SPACE, "") + val numberOfResults: Int? = null, + + @XmlElement(true) + @XmlSerialName("StopEventType", OJP_NAME_SPACE, "") + val stopEventType: StopEventType? = null, + + @XmlElement(true) + @XmlSerialName("IncludePreviousCalls", OJP_NAME_SPACE, "") + val includePreviousCalls: Boolean? = null, + + @XmlElement(true) + @XmlSerialName("IncludeOnwardCalls", OJP_NAME_SPACE, "") + val includeOnwardCalls: Boolean? = null, + + @XmlElement(true) + @XmlSerialName("IncludeOperatingDays", OJP_NAME_SPACE, "") + val includeOperatingDays: Boolean? = null, + + @XmlElement(true) + @XmlSerialName("UseRealtimeData", OJP_NAME_SPACE, "") + val useRealtimeData: RealtimeData? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventRequestDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventRequestDto.kt new file mode 100644 index 00000000..cd5a2719 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventRequestDto.kt @@ -0,0 +1,30 @@ +package ch.opentransportdata.ojp.data.dto.request.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_PREFIX +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName +import java.time.LocalDateTime + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("OJPStopEventRequest", OJP_NAME_SPACE, "") +internal data class StopEventRequestDto( + + @XmlElement(true) + @XmlSerialName("RequestTimestamp", SIRI_NAME_SPACE, SIRI_PREFIX) + @Contextual + val requestTimestamp: LocalDateTime, + + @XmlElement(true) + val location: LocationDto? = null, + + @XmlElement(true) + @XmlSerialName("Params", OJP_NAME_SPACE, "") + val params: StopEventParamsDto? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventType.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventType.kt new file mode 100644 index 00000000..76ecf4d5 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventType.kt @@ -0,0 +1,19 @@ +package ch.opentransportdata.ojp.domain.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +enum class StopEventType { + @SerialName("arrival") + ARRIVAL, + + @SerialName("departure") + DEPARTURE, + + @SerialName("both") + BOTH +} \ No newline at end of file From a638418f10b8bda7abd710bcd490f30c30e9ddac Mon Sep 17 00:00:00 2001 From: deka91 Date: Wed, 20 May 2026 15:38:20 +0200 Subject: [PATCH 2/7] Add domain models and mappers for StopEventRequest --- .../ojp/data/dto/request/ServiceRequestDto.kt | 5 ++ .../data/remote/trip/RemoteTripDataSource.kt | 9 +++ .../remote/trip/RemoteTripDataSourceImpl.kt | 66 +++++++++++++++++++ .../ojp/domain/model/Line.kt | 13 ++++ .../ojp/domain/model/LineFilter.kt | 13 ++++ .../ojp/domain/model/ModeFilter.kt | 13 ++++ .../ojp/domain/model/OperatorFilter.kt | 13 ++++ .../ojp/domain/model/StopEventParam.kt | 20 ++++++ .../ojp/domain/repository/OjpRepository.kt | 8 +++ 9 files changed, 160 insertions(+) create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/Line.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/LineFilter.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/ModeFilter.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/OperatorFilter.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ServiceRequestDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ServiceRequestDto.kt index f06a7262..f7bae85a 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ServiceRequestDto.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ServiceRequestDto.kt @@ -4,6 +4,7 @@ import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE import ch.opentransportdata.ojp.data.dto.SIRI_NAME_SPACE import ch.opentransportdata.ojp.data.dto.SIRI_PREFIX import ch.opentransportdata.ojp.data.dto.request.lir.LocationInformationRequestDto +import ch.opentransportdata.ojp.data.dto.request.ser.StopEventRequestDto import ch.opentransportdata.ojp.data.dto.request.tir.TripInfoRequestDto import ch.opentransportdata.ojp.data.dto.request.tr.TripRequestDto import ch.opentransportdata.ojp.data.dto.request.trr.TripRefineRequestDto @@ -47,4 +48,8 @@ internal data class ServiceRequestDto( @XmlElement(true) @XmlSerialName("OJPTripInfoRequest", OJP_NAME_SPACE, "ojp") val tripInfoRequestDto: TripInfoRequestDto? = null, + + @XmlElement(true) + @XmlSerialName("OJPStopEventRequest", OJP_NAME_SPACE, "ojp") + val stopEventRequestDto: StopEventRequestDto? = null, ) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSource.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSource.kt index 20f04f14..f4edee05 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSource.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSource.kt @@ -1,10 +1,13 @@ package ch.opentransportdata.ojp.data.remote.trip import ch.opentransportdata.ojp.data.dto.OjpDto +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto +import ch.opentransportdata.ojp.data.dto.request.ser.StopEventParamsDto import ch.opentransportdata.ojp.data.dto.request.tr.IndividualTransportOptionDto import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto import ch.opentransportdata.ojp.data.dto.response.tr.TripResultDto import ch.opentransportdata.ojp.domain.model.LanguageCode +import ch.opentransportdata.ojp.domain.model.StopEventParam import ch.opentransportdata.ojp.domain.model.TripInfoParam import ch.opentransportdata.ojp.domain.model.TripParams import ch.opentransportdata.ojp.domain.model.TripRefineParam @@ -39,4 +42,10 @@ internal interface RemoteTripDataSource { operatingDayRef: String, params: TripInfoParam? ): OjpDto + + suspend fun requestStopEvent( + languageCode: LanguageCode, + location: LocationDto, + params: StopEventParam? + ): OjpDto } \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt index 8f2eca5c..4cc3becf 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt @@ -4,6 +4,13 @@ import ch.opentransportdata.ojp.data.dto.OjpDto import ch.opentransportdata.ojp.data.dto.request.OjpRequestDto import ch.opentransportdata.ojp.data.dto.request.ServiceRequestContextDto import ch.opentransportdata.ojp.data.dto.request.ServiceRequestDto +import ch.opentransportdata.ojp.data.dto.request.ser.LineDto +import ch.opentransportdata.ojp.data.dto.request.ser.LineFilterDto +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto +import ch.opentransportdata.ojp.data.dto.request.ser.ModeFilterDto +import ch.opentransportdata.ojp.data.dto.request.ser.OperatorFilterDto +import ch.opentransportdata.ojp.data.dto.request.ser.StopEventParamsDto +import ch.opentransportdata.ojp.data.dto.request.ser.StopEventRequestDto import ch.opentransportdata.ojp.data.dto.request.tir.TripInfoParamsDto import ch.opentransportdata.ojp.data.dto.request.tir.TripInfoRequestDto import ch.opentransportdata.ojp.data.dto.request.tr.IndividualTransportOptionDto @@ -19,6 +26,7 @@ import ch.opentransportdata.ojp.data.dto.response.tr.TripResultDto import ch.opentransportdata.ojp.data.dto.response.tr.minimalTripResult import ch.opentransportdata.ojp.data.remote.OjpService import ch.opentransportdata.ojp.domain.model.LanguageCode +import ch.opentransportdata.ojp.domain.model.StopEventParam import ch.opentransportdata.ojp.domain.model.TripInfoParam import ch.opentransportdata.ojp.domain.model.TripParams import ch.opentransportdata.ojp.domain.model.TripRefineParam @@ -134,6 +142,30 @@ internal class RemoteTripDataSourceImpl( return@withContext ojpService.serviceRequest(url, request) } + override suspend fun requestStopEvent( + languageCode: LanguageCode, + location: LocationDto, + params: StopEventParam? + ): OjpDto = withContext(Dispatchers.IO) { + val requestTime = LocalDateTime.now() + + val request = OjpDto( + ojpRequest = OjpRequestDto( + serviceRequest = ServiceRequestDto( + serviceRequestContext = ServiceRequestContextDto(language = languageCode.shortName), + requestTimestamp = requestTime, + requestorRef = initializer.requesterReference, + stopEventRequestDto = StopEventRequestDto( + requestTimestamp = requestTime, + location = location, + params = params?.mapToBackendParams(), + ) + ) + ) + ) + return@withContext ojpService.serviceRequest(url, request) + } + private fun createRequest(languageCode: LanguageCode, requestTime: LocalDateTime, tripRequest: TripRequestDto): OjpDto { return OjpDto( ojpRequest = OjpRequestDto( @@ -209,4 +241,38 @@ internal class RemoteTripDataSourceImpl( useRealtimeData = this.useRealtimeData, ) } + + private fun StopEventParam.mapToBackendParams(): StopEventParamsDto { + return StopEventParamsDto( + modeFilter = this.modeFilter?.let { filter -> + ModeFilterDto( + ptMode = filter.ptMode, + exclude = filter.exclude, + ) + }, + lineFilter = this.lineFilter?.let { filter -> + LineFilterDto( + line = filter.line?.map { line -> + LineDto( + lineRef = line.lineRef, + directionRef = line.directionRef, + ) + }, + exclude = filter.exclude, + ) + }, + operatorFilter = this.operatorFilter?.let { filter -> + OperatorFilterDto( + operatorRef = filter.operatorRef, + exclude = filter.exclude, + ) + }, + numberOfResults = this.numberOfResults, + stopEventType = this.stopEventType, + includePreviousCalls = this.includePreviousCalls, + includeOnwardCalls = this.includeOnwardCalls, + includeOperatingDays = this.includeOperatingDays, + useRealtimeData = this.useRealtimeData, + ) + } } \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/Line.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/Line.kt new file mode 100644 index 00000000..5fa77963 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/Line.kt @@ -0,0 +1,13 @@ +package ch.opentransportdata.ojp.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +data class Line( + val lineRef: String, + val directionRef: String? = null, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/LineFilter.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/LineFilter.kt new file mode 100644 index 00000000..479ce222 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/LineFilter.kt @@ -0,0 +1,13 @@ +package ch.opentransportdata.ojp.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +data class LineFilter( + val line: List? = null, + val exclude: Boolean? = null, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/ModeFilter.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/ModeFilter.kt new file mode 100644 index 00000000..99dad64f --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/ModeFilter.kt @@ -0,0 +1,13 @@ +package ch.opentransportdata.ojp.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +data class ModeFilter( + val ptMode: List? = null, + val exclude: Boolean? = null, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/OperatorFilter.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/OperatorFilter.kt new file mode 100644 index 00000000..0016ca5b --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/OperatorFilter.kt @@ -0,0 +1,13 @@ +package ch.opentransportdata.ojp.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +data class OperatorFilter( + val operatorRef: List? = null, + val exclude: Boolean? = null, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt new file mode 100644 index 00000000..2df44dab --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt @@ -0,0 +1,20 @@ +package ch.opentransportdata.ojp.domain.model + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +data class StopEventParam( + val modeFilter: ModeFilter? = null, + val lineFilter: LineFilter? = null, + val operatorFilter: OperatorFilter? = null, + val numberOfResults: Int? = null, + val stopEventType: StopEventType? = null, + val includePreviousCalls: Boolean? = null, + val includeOnwardCalls: Boolean? = null, + val includeOperatingDays: Boolean? = null, + val useRealtimeData: RealtimeData? = null, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt index cd790617..02456ff9 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt @@ -57,4 +57,12 @@ internal interface OjpRepository { operatingDayRef: String, params: TripInfoParam? ): Result + + + suspend fun requestStopEvent( + languageCode: LanguageCode, + journeyRef: String, + operatingDayRef: String, + params: TripInfoParam? + ): Result } \ No newline at end of file From ad97f16b49d79240485f3eca28b178e18705d5ec Mon Sep 17 00:00:00 2001 From: deka91 Date: Wed, 20 May 2026 16:47:12 +0200 Subject: [PATCH 3/7] Add response DTOs for OJPStopEventDelivery --- .../data/dto/response/ServiceDeliveryDto.kt | 6 +++ .../response/delivery/StopEventDeliveryDto.kt | 48 +++++++++++++++++++ .../dto/response/ser/CallAtNearStopDto.kt | 20 ++++++++ .../data/dto/response/ser/OperatingDaysDto.kt | 28 +++++++++++ .../ojp/data/dto/response/ser/StopEventDto.kt | 46 ++++++++++++++++++ .../ser/StopEventResponseContextDto.kt | 23 +++++++++ .../dto/response/ser/StopEventResultDto.kt | 23 +++++++++ .../ojp/data/repository/OjpRepositoryImpl.kt | 22 +++++++++ .../ojp/domain/repository/OjpRepository.kt | 10 ++-- 9 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/delivery/StopEventDeliveryDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/OperatingDaysDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResponseContextDto.kt create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResultDto.kt diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ServiceDeliveryDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ServiceDeliveryDto.kt index 84d6803b..06a92fae 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ServiceDeliveryDto.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ServiceDeliveryDto.kt @@ -5,6 +5,7 @@ import ch.opentransportdata.ojp.data.dto.SIRI_NAME_SPACE import ch.opentransportdata.ojp.data.dto.SIRI_PREFIX import ch.opentransportdata.ojp.data.dto.response.delivery.AbstractDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.LocationInformationDeliveryDto +import ch.opentransportdata.ojp.data.dto.response.delivery.StopEventDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripInfoDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripRefineDeliveryDto @@ -44,10 +45,15 @@ internal data class ServiceDeliveryDto( @XmlElement(true) @XmlSerialName("OJPTripInfoDelivery", OJP_NAME_SPACE, "") val ojpTripInfoDelivery: TripInfoDeliveryDto? = null, + + @XmlElement(true) + @XmlSerialName("OJPStopEventDelivery", OJP_NAME_SPACE, "") + val ojpStopEventDelivery: StopEventDeliveryDto? = null, ) { val ojpDelivery: AbstractDeliveryDto? get() = ojpLocationInformationDelivery ?: ojpTripDelivery ?: ojpTripRefineDelivery ?: ojpTripInfoDelivery + ?: ojpStopEventDelivery } diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/delivery/StopEventDeliveryDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/delivery/StopEventDeliveryDto.kt new file mode 100644 index 00000000..262a10bb --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/delivery/StopEventDeliveryDto.kt @@ -0,0 +1,48 @@ +package ch.opentransportdata.ojp.data.dto.response.delivery + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.SIRI_PREFIX +import ch.opentransportdata.ojp.data.dto.response.ser.StopEventResponseContextDto +import ch.opentransportdata.ojp.data.dto.response.ser.StopEventResultDto +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName +import java.time.LocalDateTime + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("OJPStopEventDelivery", OJP_NAME_SPACE, "") +data class StopEventDeliveryDto( + @XmlElement(true) + @XmlSerialName("ResponseTimestamp", SIRI_NAME_SPACE, SIRI_PREFIX) + @Contextual + override val responseTimestamp: LocalDateTime, + + @XmlElement(true) + @XmlSerialName("RequestMessageRef", SIRI_NAME_SPACE, SIRI_PREFIX) + override val requestMessageRef: String? = null, + + @XmlElement(true) + @XmlSerialName("DefaultLanguage", SIRI_NAME_SPACE, SIRI_PREFIX) + override val defaultLanguage: String? = null, + + @XmlElement(true) + @XmlSerialName("Status", SIRI_NAME_SPACE, SIRI_PREFIX) + val status: Boolean? = null, + + @XmlElement(true) + @XmlSerialName("CalcTime", OJP_NAME_SPACE, "") + val calcTime: Int? = null, + + @XmlElement(true) + @XmlSerialName("StopEventResponseContext", OJP_NAME_SPACE, "") + val responseContext: StopEventResponseContextDto? = null, + + @XmlElement(true) + @XmlSerialName("StopEventResult", OJP_NAME_SPACE, "") + val stopEventResults: List? = null, +) : AbstractDeliveryDto() \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt new file mode 100644 index 00000000..8057b54b --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt @@ -0,0 +1,20 @@ +package ch.opentransportdata.ojp.data.dto.response.ser + +import android.os.Parcelable +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.response.tir.CallAtStopDto +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +@Serializable +data class CallAtNearStopDto( + @XmlElement(true) + @XmlSerialName("CallAtStop", OJP_NAME_SPACE, "") + val callAtStop: CallAtStopDto, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/OperatingDaysDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/OperatingDaysDto.kt new file mode 100644 index 00000000..67a1f04b --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/OperatingDaysDto.kt @@ -0,0 +1,28 @@ +package ch.opentransportdata.ojp.data.dto.response.ser + +import android.os.Parcelable +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +@Serializable +@XmlSerialName("OperatingDays", OJP_NAME_SPACE, "") +data class OperatingDaysDto( + @XmlElement(true) + @XmlSerialName("From", OJP_NAME_SPACE, "") + val from: String, + + @XmlElement(true) + @XmlSerialName("To", OJP_NAME_SPACE, "") + val to: String, + + @XmlElement(true) + @XmlSerialName("Pattern", OJP_NAME_SPACE, "") + val pattern: String, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventDto.kt new file mode 100644 index 00000000..c311ac4f --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventDto.kt @@ -0,0 +1,46 @@ +package ch.opentransportdata.ojp.data.dto.response.ser + +import android.os.Parcelable +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.response.NameDto +import ch.opentransportdata.ojp.data.dto.response.tr.leg.DatedJourneyDto +import ch.opentransportdata.ojp.data.dto.response.tr.leg.SituationFullRefs +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +@Serializable +@XmlSerialName("StopEvent", OJP_NAME_SPACE, "") +data class StopEventDto( + @XmlElement(true) + @XmlSerialName("PreviousCall", OJP_NAME_SPACE, "") + val previousCalls: List? = null, + + @XmlElement(true) + @XmlSerialName("ThisCall", OJP_NAME_SPACE, "") + val thisCall: CallAtNearStopDto, + + @XmlElement(true) + @XmlSerialName("OnwardCall", OJP_NAME_SPACE, "") + val onwardCalls: List? = null, + + @XmlElement(true) + @XmlSerialName("Service", OJP_NAME_SPACE, "") + val service: DatedJourneyDto, + + @XmlElement(true) + val operatingDays: OperatingDaysDto? = null, + + @XmlElement(true) + @XmlSerialName("OperatingDaysDescription", OJP_NAME_SPACE, "") + val operatingDaysDescription: NameDto? = null, + + @XmlElement(true) + @XmlSerialName("SituationFullRefs", OJP_NAME_SPACE, "") + val situationFullRefs: SituationFullRefs? = null, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResponseContextDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResponseContextDto.kt new file mode 100644 index 00000000..d7edc3c1 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResponseContextDto.kt @@ -0,0 +1,23 @@ +package ch.opentransportdata.ojp.data.dto.response.ser + +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.response.PlacesDto +import ch.opentransportdata.ojp.data.dto.response.tr.situations.SituationDto +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +@XmlSerialName("StopEventResponseContext", OJP_NAME_SPACE, "") +data class StopEventResponseContextDto( + @XmlElement(true) + @XmlSerialName("Situations", OJP_NAME_SPACE, "") + val situation: SituationDto? = null, + + @XmlElement(true) + @XmlSerialName("Places", OJP_NAME_SPACE, "") + val places: PlacesDto? = null, +) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResultDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResultDto.kt new file mode 100644 index 00000000..8df1027c --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/StopEventResultDto.kt @@ -0,0 +1,23 @@ +package ch.opentransportdata.ojp.data.dto.response.ser + +import android.os.Parcelable +import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable +import nl.adaptivity.xmlutil.serialization.XmlElement +import nl.adaptivity.xmlutil.serialization.XmlSerialName + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Parcelize +@Serializable +@XmlSerialName("StopEventResult", OJP_NAME_SPACE, "") +data class StopEventResultDto( + @XmlElement(true) + @XmlSerialName("Id", OJP_NAME_SPACE, "") + val id: String, + + @XmlElement(true) + val stopEvent: StopEventDto, +) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/repository/OjpRepositoryImpl.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/repository/OjpRepositoryImpl.kt index d3ed58ab..925f1121 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/repository/OjpRepositoryImpl.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/repository/OjpRepositoryImpl.kt @@ -1,9 +1,11 @@ package ch.opentransportdata.ojp.data.repository +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto import ch.opentransportdata.ojp.data.dto.request.tr.IndividualTransportOptionDto import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto import ch.opentransportdata.ojp.data.dto.response.delivery.LocationInformationDeliveryDto +import ch.opentransportdata.ojp.data.dto.response.delivery.StopEventDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripInfoDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripRefineDeliveryDto @@ -14,6 +16,7 @@ import ch.opentransportdata.ojp.data.remote.trip.RemoteTripDataSource import ch.opentransportdata.ojp.domain.model.LanguageCode import ch.opentransportdata.ojp.domain.model.LocationInformationParams import ch.opentransportdata.ojp.domain.model.Result +import ch.opentransportdata.ojp.domain.model.StopEventParam import ch.opentransportdata.ojp.domain.model.TripInfoParam import ch.opentransportdata.ojp.domain.model.TripParams import ch.opentransportdata.ojp.domain.model.TripRefineParam @@ -149,6 +152,25 @@ internal class OjpRepositoryImpl( } } + override suspend fun requestStopEvent( + languageCode: LanguageCode, + location: LocationDto, + params: StopEventParam? + ): Result { + return try { + val response = tripDataSource.requestStopEvent( + languageCode = languageCode, + location = location, + params = params + ) + val delivery = response.ojpResponse?.serviceDelivery?.ojpDelivery as? StopEventDeliveryDto + if (delivery != null) Result.Success(delivery) else Result.Error(OjpError.Unknown(Exception("StopEvent delivery is null"))) + } catch (exception: Exception) { + val error = handleError(exception) + Result.Error(error) + } + } + private fun handleError(exception: Exception): OjpError { return when (exception) { is HttpException -> { diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt index 02456ff9..48dc7774 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/repository/OjpRepository.kt @@ -1,8 +1,10 @@ package ch.opentransportdata.ojp.domain.repository +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto import ch.opentransportdata.ojp.data.dto.request.tr.IndividualTransportOptionDto import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import ch.opentransportdata.ojp.data.dto.response.delivery.StopEventDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripInfoDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripRefineDeliveryDto @@ -10,6 +12,7 @@ import ch.opentransportdata.ojp.data.dto.response.tr.TripResultDto import ch.opentransportdata.ojp.domain.model.LanguageCode import ch.opentransportdata.ojp.domain.model.LocationInformationParams import ch.opentransportdata.ojp.domain.model.Result +import ch.opentransportdata.ojp.domain.model.StopEventParam import ch.opentransportdata.ojp.domain.model.TripInfoParam import ch.opentransportdata.ojp.domain.model.TripParams import ch.opentransportdata.ojp.domain.model.TripRefineParam @@ -61,8 +64,7 @@ internal interface OjpRepository { suspend fun requestStopEvent( languageCode: LanguageCode, - journeyRef: String, - operatingDayRef: String, - params: TripInfoParam? - ): Result + location: LocationDto, + params: StopEventParam? + ): Result } \ No newline at end of file From 465409d6e93a528f3ed45e37923eed34607a2dad Mon Sep 17 00:00:00 2001 From: deka91 Date: Wed, 20 May 2026 16:59:14 +0200 Subject: [PATCH 4/7] Expose requestStopEvent in OjpSdk --- .../java/ch/opentransportdata/ojp/OjpSdk.kt | 51 ++++++++++++++----- .../ojp/data/dto/request/ser/LocationDto.kt | 2 +- .../opentransportdata/ojp/di/UseCaseModule.kt | 1 + .../ojp/domain/usecase/RequestStopEvent.kt | 36 +++++++++++++ 4 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/usecase/RequestStopEvent.kt diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/OjpSdk.kt b/sdk/src/main/java/ch/opentransportdata/ojp/OjpSdk.kt index 7895dcff..b0216f81 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/OjpSdk.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/OjpSdk.kt @@ -1,8 +1,10 @@ package ch.opentransportdata.ojp +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto import ch.opentransportdata.ojp.data.dto.request.tr.IndividualTransportOptionDto import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import ch.opentransportdata.ojp.data.dto.response.delivery.StopEventDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripInfoDeliveryDto import ch.opentransportdata.ojp.data.dto.response.delivery.TripRefineDeliveryDto @@ -12,13 +14,15 @@ import ch.opentransportdata.ojp.di.context.OjpKoinContext import ch.opentransportdata.ojp.domain.model.LanguageCode import ch.opentransportdata.ojp.domain.model.LocationInformationParams import ch.opentransportdata.ojp.domain.model.Result -import ch.opentransportdata.ojp.domain.model.TripParams +import ch.opentransportdata.ojp.domain.model.StopEventParam import ch.opentransportdata.ojp.domain.model.TripInfoParam +import ch.opentransportdata.ojp.domain.model.TripParams import ch.opentransportdata.ojp.domain.model.TripRefineParam import ch.opentransportdata.ojp.domain.usecase.Initializer import ch.opentransportdata.ojp.domain.usecase.RequestLocationsFromCoordinates import ch.opentransportdata.ojp.domain.usecase.RequestLocationsFromSearchTerm import ch.opentransportdata.ojp.domain.usecase.RequestMockTrips +import ch.opentransportdata.ojp.domain.usecase.RequestStopEvent import ch.opentransportdata.ojp.domain.usecase.RequestTripInfo import ch.opentransportdata.ojp.domain.usecase.RequestTripRefinement import ch.opentransportdata.ojp.domain.usecase.RequestTrips @@ -178,19 +182,10 @@ class OjpSdk( trip: TripDto, individualTransportOption: IndividualTransportOptionDto? ): Result { - return OjpKoinContext.koinApp.koin.get().invoke(languageCode, origin, destination, via, params, trip, individualTransportOption) + return OjpKoinContext.koinApp.koin.get() + .invoke(languageCode, origin, destination, via, params, trip, individualTransportOption) } - /** - * Refines a previously requested trip by retrieving more detailed or updated trip information - * - * @param languageCode The [LanguageCode] for the desired results, default is [LanguageCode.DE] - * @param tripResult The result of a requested trip which should be refined - * @param params Additional refinement parameters - * - * @return [TripRefineDeliveryDto] with the refined trip information - * - */ /** * Requests detailed real-time information about a specific journey * @@ -215,8 +210,17 @@ class OjpSdk( ) } + /** + * Refines a previously requested trip by retrieving more detailed or updated trip information + * + * @param languageCode The [LanguageCode] for the desired results, default is [LanguageCode.DE] + * @param tripResult The result of a requested trip which should be refined + * @param params Additional refinement parameters + * + * @return [TripRefineDeliveryDto] with the refined trip information + */ suspend fun requestTripRefinement( - languageCode: LanguageCode, + languageCode: LanguageCode = LanguageCode.DE, tripResult: TripResultDto, params: TripRefineParam, ): Result { @@ -226,4 +230,25 @@ class OjpSdk( params = params ) } + + /** + * Requests stop event information (departures/arrivals) for a specific stop + * + * @param languageCode The [LanguageCode] for the desired results, default is [LanguageCode.DE] + * @param location The [LocationDto] describing the stop the request is about + * @param params Additional parameters to filter and control the included information + * + * @return [StopEventDeliveryDto] with the stop event results + */ + suspend fun requestStopEvent( + languageCode: LanguageCode = LanguageCode.DE, + location: LocationDto, + params: StopEventParam? = null, + ): Result { + return OjpKoinContext.koinApp.koin.get().invoke( + languageCode = languageCode, + location = location, + params = params + ) + } } \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt index 24eeaa31..c5eb167e 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt @@ -11,7 +11,7 @@ import nl.adaptivity.xmlutil.serialization.XmlSerialName */ @Serializable @XmlSerialName("Location", OJP_NAME_SPACE, "") -internal data class LocationDto( +data class LocationDto( @XmlElement(true) @XmlSerialName("PlaceRef", OJP_NAME_SPACE, "") diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/di/UseCaseModule.kt b/sdk/src/main/java/ch/opentransportdata/ojp/di/UseCaseModule.kt index 121897b6..380b3938 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/di/UseCaseModule.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/di/UseCaseModule.kt @@ -17,4 +17,5 @@ internal val useCaseModule = module { factoryOf(::UpdateTrip) factoryOf(::RequestTripRefinement) factoryOf(::RequestTripInfo) + factoryOf(::RequestStopEvent) } \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/usecase/RequestStopEvent.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/usecase/RequestStopEvent.kt new file mode 100644 index 00000000..61fa8735 --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/usecase/RequestStopEvent.kt @@ -0,0 +1,36 @@ +package ch.opentransportdata.ojp.domain.usecase + +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto +import ch.opentransportdata.ojp.data.dto.response.delivery.StopEventDeliveryDto +import ch.opentransportdata.ojp.domain.model.LanguageCode +import ch.opentransportdata.ojp.domain.model.Result +import ch.opentransportdata.ojp.domain.model.StopEventParam +import ch.opentransportdata.ojp.domain.model.error.OjpError +import ch.opentransportdata.ojp.domain.repository.OjpRepository +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.isActive +import java.util.concurrent.CancellationException + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +internal class RequestStopEvent( + private val ojpRepository: OjpRepository +) { + suspend operator fun invoke( + languageCode: LanguageCode, + location: LocationDto, + params: StopEventParam? + ): Result { + if (!currentCoroutineContext().isActive) return Result.Error(OjpError.RequestCancelled(CancellationException())) + + return when (val response = ojpRepository.requestStopEvent( + languageCode = languageCode, + location = location, + params = params + )) { + is Result.Success -> Result.Success(response.data) + is Result.Error -> Result.Error(response.error) + } + } +} \ No newline at end of file From bda91286500044db939b3a266e7d6047947d1a00 Mon Sep 17 00:00:00 2001 From: deka91 Date: Wed, 20 May 2026 17:02:40 +0200 Subject: [PATCH 5/7] Update readme file --- README.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9393e4ed..abd6319e 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,9 @@ Available APIs: - [Location Information Request](https://opentransportdata.swiss/it/cookbook/ojplocationinformationrequest-ojp-2/) - [Trip Request](https://opentransportdata.swiss/en/cookbook/ojptriprequest/) - [Trip Info Request](https://opentransportdata.swiss/de/cookbook/open-journey-planner-ojp/ojptripinforequest/) +- [Stop Event Request](https://opentransportdata.swiss/de/cookbook/open-journey-planner-ojp-landing-page/ojpstopeventrequest-2-0/) - Trip Refinement Request -Coming soon: -- [Stop Event Request](https://opentransportdata.swiss/en/cookbook/ojp-stopeventservice/) - ## Requirements Compatible with Android 8+ @@ -168,6 +166,34 @@ requestTripInfo( ) ``` +#### Get stop events (departures/arrivals) for a stop +``` +import ch.opentransportdata.ojp.OjpSdk + +requestStopEvent( + languageCode = LanguageCode.EN, + location = LocationDto( + placeReference = PlaceReferenceDto( + ref = "8507000", + stationName = NameDto(text = "Bern"), + position = null + ) + ), + params = StopEventParam( + numberOfResults = 10, + stopEventType = StopEventType.DEPARTURE, + includePreviousCalls = false, + includeOnwardCalls = true, + includeOperatingDays = true, + useRealtimeData = RealtimeData.FULL, + modeFilter = ModeFilter( + ptMode = listOf(PtMode.RAIL), + exclude = false + ) + ) +) +``` + #### Refine a trip ``` import ch.opentransportdata.ojp.OjpSdk From 8ac777ddf68f61e9840072da6abfc43f44b64be7 Mon Sep 17 00:00:00 2001 From: deka91 Date: Thu, 21 May 2026 14:51:53 +0200 Subject: [PATCH 6/7] Add StopEventRequest to demo app --- .../presentation/MainActivity.kt | 26 ++- .../stopevent/StopEventResultScreen.kt | 215 ++++++++++++++++++ .../stopevent/StopEventResultViewModel.kt | 106 +++++++++ .../stopevent/StopEventSearchScreen.kt | 92 ++++++++ .../stopevent/StopEventSearchViewModel.kt | 91 ++++++++ .../navigation/BottomNavigation.kt | 7 + .../presentation/navigation/SerNavigation.kt | 15 ++ 7 files changed, 551 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultScreen.kt create mode 100644 app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt create mode 100644 app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchScreen.kt create mode 100644 app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchViewModel.kt create mode 100644 app/src/main/java/ch/opentransportdata/presentation/navigation/SerNavigation.kt diff --git a/app/src/main/java/ch/opentransportdata/presentation/MainActivity.kt b/app/src/main/java/ch/opentransportdata/presentation/MainActivity.kt index 7e89382c..868646fc 100644 --- a/app/src/main/java/ch/opentransportdata/presentation/MainActivity.kt +++ b/app/src/main/java/ch/opentransportdata/presentation/MainActivity.kt @@ -25,12 +25,17 @@ import ch.opentransportdata.ojp.BuildConfig import ch.opentransportdata.ojp.OjpSdk import ch.opentransportdata.ojp.data.dto.response.GeoPositionDto import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import androidx.navigation.NavHostController import ch.opentransportdata.presentation.feature.location.LirScreenComposable import ch.opentransportdata.presentation.feature.map.MapScreen import ch.opentransportdata.presentation.feature.result.TripResultScreen import ch.opentransportdata.presentation.feature.search.TripSearchScreen +import ch.opentransportdata.presentation.feature.stopevent.StopEventResultScreen +import ch.opentransportdata.presentation.feature.stopevent.StopEventSearchScreen import ch.opentransportdata.presentation.navigation.BottomNavItem import ch.opentransportdata.presentation.navigation.LocationSearchMask +import ch.opentransportdata.presentation.navigation.StopEventResults +import ch.opentransportdata.presentation.navigation.StopEventSearchMask import ch.opentransportdata.presentation.navigation.TripMap import ch.opentransportdata.presentation.navigation.TripResults import ch.opentransportdata.presentation.navigation.TripSearchMask @@ -49,7 +54,7 @@ class MainActivity : ComponentActivity() { @Composable fun OjpDemoApp() { - val bottomNavigationItems = listOf(BottomNavItem.Lir, BottomNavItem.Tir) + val bottomNavigationItems = listOf(BottomNavItem.Lir, BottomNavItem.Tir, BottomNavItem.Ser) OJPAndroidSDKTheme { val navController = rememberNavController() var selectedBottomNavItem by remember { mutableIntStateOf(0) } @@ -87,6 +92,7 @@ class MainActivity : ComponentActivity() { ) { composable { LirNavHost() } composable { TirNavHost() } + composable { SerNavHost() } } } } @@ -131,6 +137,24 @@ class MainActivity : ComponentActivity() { } } + @Composable + private fun SerNavHost() { + val navController: NavHostController = rememberNavController() + + NavHost(navController = navController, startDestination = StopEventSearchMask) { + composable { StopEventSearchScreen(navHostController = navController) } + composable( + typeMap = mapOf( + typeOf() to PlaceResultType, + typeOf() to PlaceResultType, + ) + ) { navBackstackEntry -> + val parameters = navBackstackEntry.toRoute() + StopEventResultScreen(stop = parameters.stop) + } + } + } + companion object { val ojpSdk = OjpSdk( baseUrl = "https://odpch-api.clients.liip.ch/", diff --git a/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultScreen.kt b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultScreen.kt new file mode 100644 index 00000000..c3c45d85 --- /dev/null +++ b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultScreen.kt @@ -0,0 +1,215 @@ +package ch.opentransportdata.presentation.feature.stopevent + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBars +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.FilterChip +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import ch.opentransportdata.ojp.data.dto.response.ser.StopEventResultDto +import ch.opentransportdata.ojp.domain.model.StopEventType +import kotlinx.coroutines.launch +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Composable +fun StopEventResultScreen( + modifier: Modifier = Modifier, + stop: PlaceResultDto, + viewModel: StopEventResultViewModel = viewModel(), +) { + val state = viewModel.state.collectAsState() + val snackBarHostState = remember { SnackbarHostState() } + val coroutineScope = rememberCoroutineScope() + + LaunchedEffect(stop) { + viewModel.load(stop, StopEventType.DEPARTURE) + } + + Scaffold( + contentWindowInsets = WindowInsets.statusBars, + snackbarHost = { SnackbarHost(hostState = snackBarHostState) } + ) { innerPadding -> + Column( + modifier = modifier + .padding(innerPadding) + .fillMaxSize() + .padding(horizontal = 16.dp) + ) { + Text( + modifier = Modifier.padding(top = 16.dp), + text = state.value.stopName ?: stop.place?.stopPlace?.name?.text ?: "Stop events", + style = MaterialTheme.typography.headlineSmall, + fontWeight = FontWeight.Bold + ) + + Spacer(Modifier.size(12.dp)) + + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + FilterChip( + selected = state.value.stopEventType == StopEventType.DEPARTURE, + onClick = { + if (state.value.stopEventType != StopEventType.DEPARTURE) { + viewModel.toggleStopEventType(stop) + } + }, + label = { Text("Departures") } + ) + FilterChip( + selected = state.value.stopEventType == StopEventType.ARRIVAL, + onClick = { + if (state.value.stopEventType != StopEventType.ARRIVAL) { + viewModel.toggleStopEventType(stop) + } + }, + label = { Text("Arrivals") } + ) + } + + Spacer(Modifier.size(12.dp)) + + when { + state.value.isLoading -> Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + + state.value.results.isEmpty() -> Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { Text("No stop events found") } + + else -> LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) { + items(state.value.results, key = { it.id }) { result -> + StopEventRow(result, state.value.stopEventType) + HorizontalDivider() + } + } + } + } + } + + state.value.events.forEach { event -> + when (event) { + is StopEventResultViewModel.Event.ShowSnackBar -> { + coroutineScope.launch { + snackBarHostState.showSnackbar(event.message) + } + } + } + viewModel.eventHandled(event.id) + } +} + +@Composable +private fun StopEventRow(result: StopEventResultDto, stopEventType: StopEventType) { + val stopEvent = result.stopEvent + val call = stopEvent.thisCall.callAtStop + val service = stopEvent.service + + val time = when (stopEventType) { + StopEventType.ARRIVAL -> call.serviceArrival?.timetabledTime + else -> call.serviceDeparture?.timetabledTime + } + val estimatedTime = when (stopEventType) { + StopEventType.ARRIVAL -> call.serviceArrival?.estimatedTime + else -> call.serviceDeparture?.estimatedTime + } + val plannedQuay = call.plannedQuay?.text + val estimatedQuay = call.estimatedQuay?.text + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text( + text = formatTime(time), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold + ) + if (estimatedTime != null && estimatedTime != time) { + Text( + text = formatTime(estimatedTime), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.tertiary + ) + } + } + + Spacer(Modifier.width(12.dp)) + + Box( + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .background(MaterialTheme.colorScheme.primaryContainer) + .padding(horizontal = 8.dp, vertical = 4.dp) + ) { + Text( + text = service.publishedServiceName.text ?: "", + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onPrimaryContainer, + fontWeight = FontWeight.Bold + ) + } + + Spacer(Modifier.width(12.dp)) + + Column(modifier = Modifier.weight(1f)) { + Text( + text = service.destinationText?.text ?: service.originText.text ?: "", + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Medium + ) + service.mode.name?.text?.let { Text(it, style = MaterialTheme.typography.bodySmall) } + } + + if (plannedQuay != null) { + Text( + text = "Pl. ${estimatedQuay ?: plannedQuay}", + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium + ) + } + } +} + +private val timeFormatter = DateTimeFormatter.ofPattern("HH:mm") + +private fun formatTime(time: LocalDateTime?): String { + return time?.format(timeFormatter) ?: "--:--" +} \ No newline at end of file diff --git a/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt new file mode 100644 index 00000000..aea81148 --- /dev/null +++ b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt @@ -0,0 +1,106 @@ +package ch.opentransportdata.presentation.feature.stopevent + +import android.util.Log +import androidx.compose.runtime.Immutable +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import ch.opentransportdata.ojp.data.dto.request.ser.LocationDto +import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto +import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import ch.opentransportdata.ojp.data.dto.response.ser.StopEventResultDto +import ch.opentransportdata.ojp.domain.model.RealtimeData +import ch.opentransportdata.ojp.domain.model.Result +import ch.opentransportdata.ojp.domain.model.StopEventParam +import ch.opentransportdata.ojp.domain.model.StopEventType +import ch.opentransportdata.presentation.MainActivity +import ch.opentransportdata.presentation.util.toOjpLanguageCode +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import java.util.Locale +import java.util.UUID + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +class StopEventResultViewModel : ViewModel() { + + val state = MutableStateFlow(UiState()) + + fun load(stop: PlaceResultDto, stopEventType: StopEventType) { + val ref = stop.place?.stopPlace?.stopPlaceRef + if (ref == null) { + postEvent(Event.ShowSnackBar("Selected place has no stop place reference")) + return + } + + state.value = state.value.copy( + isLoading = true, + stopName = stop.place?.stopPlace?.name?.text, + stopEventType = stopEventType, + ) + + viewModelScope.launch(Dispatchers.IO) { + val result = MainActivity.ojpSdk.requestStopEvent( + languageCode = Locale.getDefault().language.toOjpLanguageCode(), + location = LocationDto( + placeReference = PlaceReferenceDto(ref = ref) + ), + params = StopEventParam( + numberOfResults = 20, + stopEventType = stopEventType, + includeOnwardCalls = true, + includeOperatingDays = false, + useRealtimeData = RealtimeData.FULL, + ) + ) + + when (result) { + is Result.Success -> state.value = state.value.copy( + results = result.data.stopEventResults.orEmpty(), + isLoading = false, + ) + + is Result.Error -> { + Log.e(TAG, "Error fetching stop events", result.error.exception) + state.value = state.value.copy(results = emptyList(), isLoading = false) + postEvent(Event.ShowSnackBar("Failed to load stop events")) + } + } + } + } + + fun toggleStopEventType(stop: PlaceResultDto) { + val next = if (state.value.stopEventType == StopEventType.DEPARTURE) { + StopEventType.ARRIVAL + } else { + StopEventType.DEPARTURE + } + load(stop, next) + } + + fun eventHandled(id: Long) { + state.value = state.value.copy(events = state.value.events.filterNot { it.id == id }) + } + + private fun postEvent(event: Event) { + state.value = state.value.copy(events = state.value.events + event) + } + + sealed class Event(val id: Long = UUID.randomUUID().mostSignificantBits) { + data class ShowSnackBar(val message: String) : Event() + } + + @Immutable + data class UiState( + val isLoading: Boolean = false, + val stopName: String? = null, + val stopEventType: StopEventType = StopEventType.DEPARTURE, + val results: List = emptyList(), + val events: List = emptyList(), + ) + + companion object { + private const val TAG = "StopEventResultViewModel" + } +} \ No newline at end of file diff --git a/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchScreen.kt b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchScreen.kt new file mode 100644 index 00000000..789a455d --- /dev/null +++ b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchScreen.kt @@ -0,0 +1,92 @@ +package ch.opentransportdata.presentation.feature.stopevent + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBars +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController +import ch.opentransportdata.presentation.components.LocationInputRow +import ch.opentransportdata.presentation.components.LocationResultList +import ch.opentransportdata.presentation.navigation.StopEventResults +import ch.opentransportdata.presentation.theme.OJPAndroidSDKTheme +import kotlinx.coroutines.launch + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Composable +fun StopEventSearchScreen( + modifier: Modifier = Modifier, + navHostController: NavHostController, + viewModel: StopEventSearchViewModel = viewModel(), +) { + val snackBarHostState = remember { SnackbarHostState() } + val coroutineScope = rememberCoroutineScope() + val state = viewModel.state.collectAsState() + + Scaffold( + contentWindowInsets = WindowInsets.statusBars, + snackbarHost = { + SnackbarHost(modifier = Modifier.imePadding(), hostState = snackBarHostState) + } + ) { innerPaddings -> + Column( + modifier = modifier + .padding(innerPaddings) + .fillMaxSize() + ) { + LocationInputRow( + modifier = Modifier.padding(16.dp), + isLocationButtonEnabled = false, + requestLocation = {}, + textInputValue = state.value.inputValue, + onTextValueChange = { viewModel.fetchLocations(it) }, + hasFocus = true, + onFocusChange = {}, + onClearInputClicked = { viewModel.fetchLocations("") } + ) + LocationResultList( + items = state.value.results, + onLocationSelected = { viewModel.onLocationSelected(it) } + ) + } + } + + state.value.events.forEach { event -> + when (event) { + is StopEventSearchViewModel.Event.ShowSnackBar -> { + coroutineScope.launch { + snackBarHostState.showSnackbar(message = event.message) + } + } + + is StopEventSearchViewModel.Event.OpenResults -> { + navHostController.navigate(StopEventResults(stop = event.location)) + viewModel.resetData() + } + } + viewModel.eventHandled(event.id) + } +} + +@PreviewLightDark +@Composable +private fun StopEventSearchScreenPreview() { + OJPAndroidSDKTheme { + StopEventSearchScreen(navHostController = rememberNavController()) + } +} \ No newline at end of file diff --git a/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchViewModel.kt b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchViewModel.kt new file mode 100644 index 00000000..08c9bb64 --- /dev/null +++ b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventSearchViewModel.kt @@ -0,0 +1,91 @@ +package ch.opentransportdata.presentation.feature.stopevent + +import android.util.Log +import androidx.compose.runtime.Immutable +import androidx.lifecycle.ViewModel +import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import ch.opentransportdata.ojp.domain.model.LocationInformationParams +import ch.opentransportdata.ojp.domain.model.PlaceTypeRestriction +import ch.opentransportdata.ojp.domain.model.Result +import ch.opentransportdata.ojp.domain.model.error.OjpError +import ch.opentransportdata.presentation.MainActivity +import ch.opentransportdata.presentation.util.toOjpLanguageCode +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import java.util.Locale +import java.util.UUID + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +class StopEventSearchViewModel : ViewModel() { + + val state = MutableStateFlow(UiState()) + + private val job = SupervisorJob() + private val searchScope = CoroutineScope(job + Dispatchers.IO) + + fun fetchLocations(input: String) { + state.value = state.value.copy(inputValue = input) + if (input.isBlank()) { + state.value = state.value.copy(results = emptyList()) + return + } + + searchScope.coroutineContext.cancelChildren() + searchScope.launch { + when (val result = MainActivity.ojpSdk.requestLocationsFromSearchTerm( + languageCode = Locale.getDefault().language.toOjpLanguageCode(), + term = input, + restrictions = LocationInformationParams( + types = listOf(PlaceTypeRestriction.STOP), + numberOfResults = 10, + ptModeIncluded = true + ) + )) { + is Result.Success -> state.value = state.value.copy(results = result.data) + is Result.Error -> { + Log.e(TAG, "Error fetching data", result.error.exception) + if (result.error !is OjpError.RequestCancelled) postEvent(Event.ShowSnackBar("Something went wrong")) + } + } + } + } + + fun onLocationSelected(location: PlaceResultDto) { + postEvent(Event.OpenResults(location)) + } + + fun eventHandled(id: Long) { + val events = state.value.events.filterNot { it.id == id } + state.value = state.value.copy(events = events) + } + + fun resetData() { + state.value = UiState() + } + + private fun postEvent(event: Event) { + state.value = state.value.copy(events = state.value.events + event) + } + + sealed class Event(val id: Long = UUID.randomUUID().mostSignificantBits) { + data class ShowSnackBar(val message: String) : Event() + data class OpenResults(val location: PlaceResultDto) : Event() + } + + @Immutable + data class UiState( + val inputValue: String = "", + val results: List = emptyList(), + val events: List = emptyList(), + ) + + companion object { + private const val TAG = "StopEventSearchViewModel" + } +} \ No newline at end of file diff --git a/app/src/main/java/ch/opentransportdata/presentation/navigation/BottomNavigation.kt b/app/src/main/java/ch/opentransportdata/presentation/navigation/BottomNavigation.kt index 49e01952..5dc92585 100644 --- a/app/src/main/java/ch/opentransportdata/presentation/navigation/BottomNavigation.kt +++ b/app/src/main/java/ch/opentransportdata/presentation/navigation/BottomNavigation.kt @@ -1,6 +1,7 @@ package ch.opentransportdata.presentation.navigation import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DepartureBoard import androidx.compose.material.icons.filled.LocationOn import androidx.compose.material.icons.filled.Search import androidx.compose.ui.graphics.vector.ImageVector @@ -24,4 +25,10 @@ sealed class BottomNavItem(@Contextual val icon: ImageVector, val label: String) icon = Icons.Default.Search, label = "TIR" ) + + @Serializable + data object Ser : BottomNavItem( + icon = Icons.Default.DepartureBoard, + label = "SER" + ) } \ No newline at end of file diff --git a/app/src/main/java/ch/opentransportdata/presentation/navigation/SerNavigation.kt b/app/src/main/java/ch/opentransportdata/presentation/navigation/SerNavigation.kt new file mode 100644 index 00000000..bf226b07 --- /dev/null +++ b/app/src/main/java/ch/opentransportdata/presentation/navigation/SerNavigation.kt @@ -0,0 +1,15 @@ +package ch.opentransportdata.presentation.navigation + +import ch.opentransportdata.ojp.data.dto.response.PlaceResultDto +import kotlinx.serialization.Serializable + +/** + * Created by Deniz Kalem on 20.05.2026 + */ +@Serializable +object StopEventSearchMask + +@Serializable +data class StopEventResults( + val stop: PlaceResultDto +) \ No newline at end of file From 770ef95fae4ba4cb4ccc7d5eed32660df5c0255e Mon Sep 17 00:00:00 2001 From: nicoti8m Date: Thu, 28 May 2026 09:28:36 +0200 Subject: [PATCH 7/7] Add missing optional parameters for stop event request --- .../stopevent/StopEventResultViewModel.kt | 4 +++- .../ojp/data/dto/request/ser/LocationDto.kt | 7 ++++++ .../dto/request/ser/StopEventParamsDto.kt | 24 +++++++++++++++++++ .../dto/response/ser/CallAtNearStopDto.kt | 11 +++++++++ .../remote/trip/RemoteTripDataSourceImpl.kt | 5 ++++ .../ojp/domain/model/StopEventParam.kt | 6 +++++ .../ojp/domain/model/StopHierarchy.kt | 22 +++++++++++++++++ 7 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopHierarchy.kt diff --git a/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt index aea81148..40dcbe37 100644 --- a/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt +++ b/app/src/main/java/ch/opentransportdata/presentation/feature/stopevent/StopEventResultViewModel.kt @@ -17,6 +17,7 @@ import ch.opentransportdata.presentation.util.toOjpLanguageCode import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch +import java.time.LocalDateTime import java.util.Locale import java.util.UUID @@ -44,7 +45,8 @@ class StopEventResultViewModel : ViewModel() { val result = MainActivity.ojpSdk.requestStopEvent( languageCode = Locale.getDefault().language.toOjpLanguageCode(), location = LocationDto( - placeReference = PlaceReferenceDto(ref = ref) + placeReference = PlaceReferenceDto(ref = ref), + depArrTime = LocalDateTime.now(), ), params = StopEventParam( numberOfResults = 20, diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt index c5eb167e..4ed1a745 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/LocationDto.kt @@ -2,9 +2,11 @@ package ch.opentransportdata.ojp.data.dto.request.ser import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE import ch.opentransportdata.ojp.data.dto.request.tr.PlaceReferenceDto +import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlSerialName +import java.time.LocalDateTime /** * Created by Deniz Kalem on 20.05.2026 @@ -16,4 +18,9 @@ data class LocationDto( @XmlElement(true) @XmlSerialName("PlaceRef", OJP_NAME_SPACE, "") val placeReference: PlaceReferenceDto, + + @XmlElement(true) + @XmlSerialName("DepArrTime", OJP_NAME_SPACE, "") + @Contextual + val depArrTime: LocalDateTime? = null, ) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt index 180c1ccc..7f6e5cc6 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/request/ser/StopEventParamsDto.kt @@ -1,11 +1,14 @@ package ch.opentransportdata.ojp.data.dto.request.ser import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.converter.DurationSerializer import ch.opentransportdata.ojp.domain.model.RealtimeData import ch.opentransportdata.ojp.domain.model.StopEventType +import ch.opentransportdata.ojp.domain.model.StopHierarchy import kotlinx.serialization.Serializable import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlSerialName +import java.time.Duration /** * Created by Deniz Kalem on 20.05.2026 @@ -23,10 +26,19 @@ internal data class StopEventParamsDto( @XmlElement(true) val operatorFilter: OperatorFilterDto? = null, + @XmlElement(true) + @XmlSerialName("IncludeAllRestrictedLines", OJP_NAME_SPACE, "") + val includeAllRestrictedLines: Boolean? = null, + @XmlElement(true) @XmlSerialName("NumberOfResults", OJP_NAME_SPACE, "") val numberOfResults: Int? = null, + @XmlElement(true) + @XmlSerialName("TimeWindow", OJP_NAME_SPACE, "") + @Serializable(with = DurationSerializer::class) + val timeWindow: Duration? = null, + @XmlElement(true) @XmlSerialName("StopEventType", OJP_NAME_SPACE, "") val stopEventType: StopEventType? = null, @@ -46,4 +58,16 @@ internal data class StopEventParamsDto( @XmlElement(true) @XmlSerialName("UseRealtimeData", OJP_NAME_SPACE, "") val useRealtimeData: RealtimeData? = null, + + @XmlElement(true) + @XmlSerialName("IncludePlacesContext", OJP_NAME_SPACE, "") + val includePlacesContext: Boolean? = null, + + @XmlElement(true) + @XmlSerialName("IncludeSituationsContext", OJP_NAME_SPACE, "") + val includeSituationsContext: Boolean? = null, + + @XmlElement(true) + @XmlSerialName("IncludeStopHierarchy", OJP_NAME_SPACE, "") + val includeStopHierarchy: StopHierarchy? = null, ) \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt index 8057b54b..a9a3586a 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/dto/response/ser/CallAtNearStopDto.kt @@ -2,11 +2,13 @@ package ch.opentransportdata.ojp.data.dto.response.ser import android.os.Parcelable import ch.opentransportdata.ojp.data.dto.OJP_NAME_SPACE +import ch.opentransportdata.ojp.data.dto.converter.DurationSerializer import ch.opentransportdata.ojp.data.dto.response.tir.CallAtStopDto import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlSerialName +import java.time.Duration /** * Created by Deniz Kalem on 20.05.2026 @@ -17,4 +19,13 @@ data class CallAtNearStopDto( @XmlElement(true) @XmlSerialName("CallAtStop", OJP_NAME_SPACE, "") val callAtStop: CallAtStopDto, + + @XmlElement(true) + @XmlSerialName("WalkDistance", OJP_NAME_SPACE, "") + val walkDistance: Int? = null, + + @XmlElement(true) + @XmlSerialName("WalkDuration", OJP_NAME_SPACE, "") + @Serializable(with = DurationSerializer::class) + val walkDuration: Duration? = null, ) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt b/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt index 4cc3becf..764d12af 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/data/remote/trip/RemoteTripDataSourceImpl.kt @@ -267,12 +267,17 @@ internal class RemoteTripDataSourceImpl( exclude = filter.exclude, ) }, + includeAllRestrictedLines = this.includeAllRestrictedLines, numberOfResults = this.numberOfResults, + timeWindow = this.timeWindow, stopEventType = this.stopEventType, includePreviousCalls = this.includePreviousCalls, includeOnwardCalls = this.includeOnwardCalls, includeOperatingDays = this.includeOperatingDays, useRealtimeData = this.useRealtimeData, + includePlacesContext = this.includePlacesContext, + includeSituationsContext = this.includeSituationsContext, + includeStopHierarchy = this.includeStopHierarchy, ) } } \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt index 2df44dab..0ff7cb23 100644 --- a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopEventParam.kt @@ -2,6 +2,7 @@ package ch.opentransportdata.ojp.domain.model import android.os.Parcelable import kotlinx.parcelize.Parcelize +import java.time.Duration /** * Created by Deniz Kalem on 20.05.2026 @@ -11,10 +12,15 @@ data class StopEventParam( val modeFilter: ModeFilter? = null, val lineFilter: LineFilter? = null, val operatorFilter: OperatorFilter? = null, + val includeAllRestrictedLines: Boolean? = null, val numberOfResults: Int? = null, + val timeWindow: Duration? = null, val stopEventType: StopEventType? = null, val includePreviousCalls: Boolean? = null, val includeOnwardCalls: Boolean? = null, val includeOperatingDays: Boolean? = null, val useRealtimeData: RealtimeData? = null, + val includePlacesContext: Boolean? = null, + val includeSituationsContext: Boolean? = null, + val includeStopHierarchy: StopHierarchy? = null, ) : Parcelable \ No newline at end of file diff --git a/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopHierarchy.kt b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopHierarchy.kt new file mode 100644 index 00000000..3b878e5b --- /dev/null +++ b/sdk/src/main/java/ch/opentransportdata/ojp/domain/model/StopHierarchy.kt @@ -0,0 +1,22 @@ +package ch.opentransportdata.ojp.domain.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +/** + * Created by Nico Brandenberger on 27.06.2026 + */ +@Serializable +enum class StopHierarchy { + @SerialName("local") + LOCAL, + + @SerialName("no") + NO, + + @SerialName("down") + DOWN, + + @SerialName("all") + ALL, +} \ No newline at end of file