diff --git a/app/src/main/java/dev/itsvic/parceltracker/api/ColissimoDeliveryService.kt b/app/src/main/java/dev/itsvic/parceltracker/api/ColissimoDeliveryService.kt new file mode 100644 index 0000000..7de48a0 --- /dev/null +++ b/app/src/main/java/dev/itsvic/parceltracker/api/ColissimoDeliveryService.kt @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +package dev.itsvic.parceltracker.api + +import android.content.Context +import android.os.LocaleList +import com.squareup.moshi.JsonClass +import dev.itsvic.parceltracker.R +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import retrofit2.HttpException +import retrofit2.Retrofit +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query + +object ColissimoDeliveryService : DeliveryService { + override val nameResource: Int = R.string.service_colissimo + override val acceptsPostCode: Boolean = false + override val requiresPostCode: Boolean = false + override val requiresApiKey: Boolean = false + + override fun acceptsFormat(trackingId: String): Boolean { + val parcelFormat = """^[A-Za-z0-9]{11,15}$""".toRegex() + return parcelFormat.accepts(trackingId) + } + + override suspend fun getParcel( + context: Context, + trackingId: String, + postalCode: String? + ): Parcel { + val locale = LocaleList.getDefault().get(0) + + val resp = + try { + service.getShipments(trackingId, locale.language) + } catch (_: HttpException) { + try { + service.getShipments(trackingId, "en") // Retry once with fallback to english + } catch (_: HttpException) { + throw ParcelNonExistentException() + } + } + + if (resp.isEmpty()) throw ParcelNonExistentException() + val shipment = resp[0].shipment + + var status = Status.Unknown + val events = shipment.event.sortedByDescending { it.order } + + if (events.isNotEmpty()) { + val lastEvent = events.first() + status = + when (lastEvent.code) { + "DR1" -> Status.Preadvice // Delivery declaration received + "DR2" -> Status.DeliveryFailure // Issue during preparation + "PC1" -> Status.InWarehouse // Handled + "PC2" -> Status.InWarehouse // Handled in the expediting country + "ET1" -> Status.InTransit // Processing + "ET2" -> Status.InTransit // Processing in the expediting country + "ET3" -> Status.InTransit // Processing in the destination country + "ET4" -> Status.InTransit // Processing in a transit country + "DO1" -> Status.Customs // Entered customs + "DO2" -> Status.Customs // Out of customs + "DO3" -> Status.Customs // Held in customs + "MD2" -> Status.OutForDelivery // Distributing + "ND1" -> Status.DeliveryFailure // Impossible to distribute + "AG1" -> Status.AwaitingPickup // Awaiting pickup at counter + "RE1" -> Status.DeliveryFailure // Returned to the expeditor + "DI0" -> Status.Delivered // Distributed in lot + "DI1" -> Status.Delivered // Distributed + "DI2" -> Status.DeliveryFailure // Distributed to the expeditor (If sent back I suppose) + "DI3" -> Status.OutForDelivery // Delayed (This probably means still distributing) + "ID0" -> Status.Customs // Customs information + // EP1 (Waiting presentation), PB1 (Issue in progress), PB2 (Issue resolved) + // Ignored as unknown statuses, these errors are mid process statuses so + // it shouldn't be a big deal + else -> logUnknownStatus("Colissimo", lastEvent.code) + } + } + + val history = + events.map { + ParcelHistoryItem( + it.label, LocalDateTime.parse(it.date, DateTimeFormatter.ISO_DATE_TIME), it.country) + } + + return Parcel(shipment.idShip, history, status) + } + + private val retrofit = + Retrofit.Builder() + .baseUrl("https://www.laposte.fr/ssu/sun/back/suivi-unifie/") + .client(api_client) + .addConverterFactory(api_factory) + .build() + private val service = retrofit.create(API::class.java) + + private interface API { + @GET("{id}") + suspend fun getShipments( + @Path("id") trackingId: String, + @Query("lang") lang: String = "en_GB" + ): List + } + + @JsonClass(generateAdapter = true) + internal data class ShipmentResponse( + val shipment: Shipment, + ) + + @JsonClass(generateAdapter = true) + internal data class Shipment( + val idShip: String, + val event: List, + ) + + @JsonClass(generateAdapter = true) + internal data class Event( + val code: String, + val type: String, + val group: String, + val label: String, + val date: String, + val country: String, + val order: Int + ) +} diff --git a/app/src/main/java/dev/itsvic/parceltracker/api/Core.kt b/app/src/main/java/dev/itsvic/parceltracker/api/Core.kt index 7f3eb65..135b62d 100644 --- a/app/src/main/java/dev/itsvic/parceltracker/api/Core.kt +++ b/app/src/main/java/dev/itsvic/parceltracker/api/Core.kt @@ -47,6 +47,7 @@ enum class Service { SAMEDAY_RO, UKRPOSHTA, POSTNORD, + COLISSIMO, // Asia EKART, @@ -87,6 +88,7 @@ fun getDeliveryService(service: Service): DeliveryService? { Service.SAMEDAY_RO -> SamedayRomaniaDeliveryService Service.UKRPOSHTA -> UkrposhtaDeliveryService Service.POSTNORD -> PostNordDeliveryService + Service.COLISSIMO -> ColissimoDeliveryService Service.EKART -> EKartDeliveryService Service.SPX_TH -> SPXThailandDeliveryService diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89d365b..690d2a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ An Post Belpost Cainiao + Colissimo DHL DPD Germany DPD UK