Skip to content

Commit

Permalink
Replace Date with Instant (readium#506)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickael-menu committed Apr 30, 2024
1 parent 2cc906c commit 374c9c3
Show file tree
Hide file tree
Showing 34 changed files with 310 additions and 221 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ All notable changes to this project will be documented in this file. Take a look

* All the `completion` parameters of the `Navigator` APIs are removed.

### Changed

* All the APIs using or returning a `Date` objects are now using a custom `Instant` type.

### Fixed

#### Navigator
Expand Down
6 changes: 6 additions & 0 deletions docs/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ All migration steps necessary in reading apps to upgrade to major versions of th

If you target Android devices running below API 26, you now must enable [core library desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) in your application module.

### Removing JVM dependencies

To reduce our depency to the JVM, we no longer use `Date` objects in the toolkit. Instead, we added a custom `Instant` type.

You can still translate from and to a `Date` object with `Instant.fromJavaDate()` and `instant.toJavaDate()`.


## 3.0.0-alpha.2

Expand Down
14 changes: 7 additions & 7 deletions readium/lcp/src/main/java/org/readium/r2/lcp/LcpError.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
package org.readium.r2.lcp

import java.net.SocketTimeoutException
import java.util.*
import kotlinx.coroutines.CancellationException
import org.readium.r2.lcp.service.NetworkException
import org.readium.r2.shared.util.DebugError
import org.readium.r2.shared.util.Error
import org.readium.r2.shared.util.ErrorException
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.ThrowableError
import org.readium.r2.shared.util.Url

Expand Down Expand Up @@ -72,16 +72,16 @@ public sealed class LcpError(
cause: Error? = null
) : LcpError(message, cause) {

public class Cancelled(public val date: Date) :
public class Cancelled(public val date: Instant) :
LicenseStatus("This license was cancelled on $date")

public class Returned(public val date: Date) :
public class Returned(public val date: Instant) :
LicenseStatus("This license has been returned on $date")

public class NotStarted(public val start: Date) :
public class NotStarted(public val start: Instant) :
LicenseStatus("This license starts on $start")

public class Expired(public val end: Date) :
public class Expired(public val end: Instant) :
LicenseStatus("This license expired on $end")

/**
Expand All @@ -90,7 +90,7 @@ public sealed class LcpError(
* in the status document. If no event is logged in the status document, no such message should
* appear (certainly not "The license was registered by 0 devices").
*/
public class Revoked(public val date: Date, public val devicesCount: Int) :
public class Revoked(public val date: Instant, public val devicesCount: Int) :
LicenseStatus(
"This license was revoked by its provider on $date. It was registered by $devicesCount device(s)."
)
Expand All @@ -109,7 +109,7 @@ public sealed class LcpError(
Renew("Publication could not be renewed properly")

/** Incorrect renewal period, your publication could not be renewed. */
public class InvalidRenewalPeriod(public val maxRenewDate: Date?) :
public class InvalidRenewalPeriod(public val maxRenewDate: Instant?) :
Renew("Incorrect renewal period, your publication could not be renewed")

/** An unexpected error has occurred on the licensing server. */
Expand Down
8 changes: 4 additions & 4 deletions readium/lcp/src/main/java/org/readium/r2/lcp/LcpLicense.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
package org.readium.r2.lcp

import java.net.URL
import java.util.*
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.StateFlow
Expand All @@ -18,6 +17,7 @@ import org.readium.r2.lcp.license.model.LicenseDocument
import org.readium.r2.lcp.license.model.StatusDocument
import org.readium.r2.shared.publication.services.ContentProtectionService
import org.readium.r2.shared.util.Closeable
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.Url
import org.readium.r2.shared.util.toDebugDescription
Expand Down Expand Up @@ -59,15 +59,15 @@ public interface LcpLicense : ContentProtectionService.UserRights, Closeable {
* The maximum potential date to renew to.
* If null, then the renew date might not be customizable.
*/
public val maxRenewDate: Date?
public val maxRenewDate: Instant?

/**
* Renews the loan by starting a renew LSD interaction.
*
* @param prefersWebPage Indicates whether the loan should be renewed through a web page if
* available, instead of programmatically.
*/
public suspend fun renewLoan(listener: RenewListener, prefersWebPage: Boolean = false): Try<Date?, LcpError>
public suspend fun renewLoan(listener: RenewListener, prefersWebPage: Boolean = false): Try<Instant?, LcpError>

/**
* Can the user return the loaned publication?
Expand Down Expand Up @@ -98,7 +98,7 @@ public interface LcpLicense : ContentProtectionService.UserRights, Closeable {
*
* The returned date can't exceed [maximumDate].
*/
public suspend fun preferredEndDate(maximumDate: Date?): Date?
public suspend fun preferredEndDate(maximumDate: Instant?): Instant?

/**
* Called when the renew interaction uses an HTML web page.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.browser.customtabs.CustomTabsIntent
import androidx.fragment.app.FragmentManager
import com.google.android.material.datepicker.*
import java.util.*
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.suspendCancellableCoroutine
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.Url

/**
Expand All @@ -37,9 +37,9 @@ public class MaterialRenewListener(
private val fragmentManager: FragmentManager
) : LcpLicense.RenewListener {

override suspend fun preferredEndDate(maximumDate: Date?): Date? = suspendCancellableCoroutine { cont ->
val start = (license.license.rights.end ?: Date()).time
val end = maximumDate?.time
override suspend fun preferredEndDate(maximumDate: Instant?): Instant? = suspendCancellableCoroutine { cont ->
val start = (license.license.rights.end ?: Instant.now()).toEpochMilliseconds()
val end = maximumDate?.toEpochMilliseconds()

MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(
Expand Down Expand Up @@ -67,7 +67,7 @@ public class MaterialRenewListener(
addOnNegativeButtonClickListener { cont.cancel() }

addOnPositiveButtonClickListener { selection ->
cont.resume(Date(selection))
cont.resume(Instant.fromEpochMilliseconds(selection))
}
}
.show(fragmentManager, "MaterialRenewListener.DatePicker")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
package org.readium.r2.lcp.license

import java.net.HttpURLConnection
import java.util.Date
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -30,8 +29,8 @@ import org.readium.r2.lcp.service.DeviceService
import org.readium.r2.lcp.service.LcpClient
import org.readium.r2.lcp.service.LicensesRepository
import org.readium.r2.lcp.service.NetworkService
import org.readium.r2.shared.extensions.toIso8601String
import org.readium.r2.shared.extensions.tryOrNull
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.getOrElse
import org.readium.r2.shared.util.getOrThrow
Expand Down Expand Up @@ -144,10 +143,10 @@ internal class License private constructor(
override val canRenewLoan: Boolean
get() = status?.link(StatusDocument.Rel.Renew) != null

override val maxRenewDate: Date?
override val maxRenewDate: Instant?
get() = status?.potentialRights?.end

override suspend fun renewLoan(listener: LcpLicense.RenewListener, prefersWebPage: Boolean): Try<Date?, LcpError> {
override suspend fun renewLoan(listener: LcpLicense.RenewListener, prefersWebPage: Boolean): Try<Instant?, LcpError> {
// Finds the renew link according to `prefersWebPage`.
fun findRenewLink(): Link? {
val status = documents.status ?: return null
Expand Down Expand Up @@ -179,7 +178,7 @@ internal class License private constructor(

val parameters = this.device.asQueryParameters.toMutableMap()
if (endDate != null) {
parameters["end"] = endDate.toIso8601String()
parameters["end"] = endDate.toString()
}

val url = link.url(parameters = parameters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.readium.r2.lcp.service.DeviceService
import org.readium.r2.lcp.service.LcpClient
import org.readium.r2.lcp.service.NetworkService
import org.readium.r2.lcp.service.PassphrasesService
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.getOrElse
import org.readium.r2.shared.util.mediatype.MediaType
import timber.log.Timber
Expand Down Expand Up @@ -388,7 +389,7 @@ internal class LicenseValidation(
statusDocumentTakesPrecedence: Boolean
) {
var error: LcpError.LicenseStatus? = null
val now = Date()
val now = Instant.now()
val start = license.rights.start ?: now
val end = license.rights.end ?: now
val isLicenseExpired = (start > now || now > end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
package org.readium.r2.lcp.license.model

import java.nio.charset.Charset
import java.util.*
import org.json.JSONObject
import org.readium.r2.lcp.LcpError
import org.readium.r2.lcp.LcpException
Expand All @@ -21,9 +20,9 @@ import org.readium.r2.lcp.license.model.components.lcp.Rights
import org.readium.r2.lcp.license.model.components.lcp.Signature
import org.readium.r2.lcp.license.model.components.lcp.User
import org.readium.r2.lcp.service.URLParameters
import org.readium.r2.shared.extensions.iso8601ToDate
import org.readium.r2.shared.extensions.optNullableString
import org.readium.r2.shared.util.AbsoluteUrl
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.Try
import org.readium.r2.shared.util.Url
import org.readium.r2.shared.util.mediatype.MediaType
Expand Down Expand Up @@ -63,14 +62,14 @@ public class LicenseDocument internal constructor(public val json: JSONObject) {
json.optNullableString("id")
?: throw LcpException(LcpError.Parsing.LicenseDocument)

public val issued: Date =
public val issued: Instant =
json.optNullableString("issued")
?.iso8601ToDate()
?.let { Instant.parse(it) }
?: throw LcpException(LcpError.Parsing.LicenseDocument)

public val updated: Date =
public val updated: Instant =
json.optNullableString("updated")
?.iso8601ToDate()
?.let { Instant.parse(it) }
?: issued

public val encryption: Encryption =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ import org.readium.r2.lcp.license.model.components.Links
import org.readium.r2.lcp.license.model.components.lsd.Event
import org.readium.r2.lcp.license.model.components.lsd.PotentialRights
import org.readium.r2.lcp.service.URLParameters
import org.readium.r2.shared.extensions.iso8601ToDate
import org.readium.r2.shared.extensions.mapNotNull
import org.readium.r2.shared.extensions.optNullableString
import org.readium.r2.shared.util.Instant
import org.readium.r2.shared.util.Url
import org.readium.r2.shared.util.mediatype.MediaType

public class StatusDocument(public val data: ByteArray) {
public val id: String
public val status: Status
public val message: String
public val licenseUpdated: Date
public val statusUpdated: Date
public val licenseUpdated: Instant
public val statusUpdated: Instant
public val links: Links
public val potentialRights: PotentialRights?
public val events: List<Event>
Expand All @@ -49,7 +49,7 @@ public class StatusDocument(public val data: ByteArray) {
public val rawValue: String get() = value

public companion object {
public operator fun invoke(value: String): Status? = values().firstOrNull { it.value == value }
public operator fun invoke(value: String): Status? = entries.firstOrNull { it.value == value }
}
}

Expand All @@ -63,7 +63,7 @@ public class StatusDocument(public val data: ByteArray) {
public val rawValue: String get() = value

public companion object {
public operator fun invoke(value: String): Rel? = values().firstOrNull { it.value == value }
public operator fun invoke(value: String): Rel? = entries.firstOrNull { it.value == value }
}
}

Expand All @@ -83,10 +83,10 @@ public class StatusDocument(public val data: ByteArray) {
)

val updated = json.optJSONObject("updated") ?: JSONObject()
licenseUpdated = updated.optNullableString("license")?.iso8601ToDate() ?: throw LcpException(
licenseUpdated = updated.optNullableString("license")?.let { Instant.parse(it) } ?: throw LcpException(
LcpError.Parsing.StatusDocument
)
statusUpdated = updated.optNullableString("status")?.iso8601ToDate() ?: throw LcpException(
statusUpdated = updated.optNullableString("status")?.let { Instant.parse(it) } ?: throw LcpException(
LcpError.Parsing.StatusDocument
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,25 @@

package org.readium.r2.lcp.license.model.components.lcp

import java.util.*
import org.json.JSONObject
import org.readium.r2.shared.extensions.iso8601ToDate
import org.readium.r2.shared.extensions.optNullableInt
import org.readium.r2.shared.extensions.optNullableString
import org.readium.r2.shared.util.Instant

public data class Rights(val json: JSONObject) {
val print: Int?
val copy: Int?
val start: Date?
val end: Date?
val start: Instant?
val end: Instant?
val extensions: JSONObject

init {
val clone = JSONObject(json.toString())

print = clone.optNullableInt("print", remove = true)
copy = clone.optNullableInt("copy", remove = true)
start = clone.optNullableString("start", remove = true)?.iso8601ToDate()
end = clone.optNullableString("end", remove = true)?.iso8601ToDate()
start = clone.optNullableString("start", remove = true)?.let { Instant.parse(it) }
end = clone.optNullableString("end", remove = true)?.let { Instant.parse(it) }

extensions = clone
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@

package org.readium.r2.lcp.license.model.components.lsd

import java.util.*
import org.json.JSONObject
import org.readium.r2.shared.extensions.iso8601ToDate
import org.readium.r2.shared.extensions.optNullableString
import org.readium.r2.shared.util.Instant

public data class Event(val json: JSONObject) {
val type: String = json.optNullableString("type") ?: ""
val name: String = json.optNullableString("name") ?: ""
val id: String = json.optNullableString("id") ?: ""
val date: Date? = json.optNullableString("timestamp")?.iso8601ToDate()
val date: Instant? = json.optNullableString("timestamp")?.let { Instant.parse(it) }

public enum class EventType(public val value: String) {
Register("register"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@

package org.readium.r2.lcp.license.model.components.lsd

import java.util.*
import org.json.JSONObject
import org.readium.r2.shared.extensions.iso8601ToDate
import org.readium.r2.shared.extensions.optNullableString
import org.readium.r2.shared.util.Instant

public data class PotentialRights(val json: JSONObject) {
val end: Date? = json.optNullableString("end")?.iso8601ToDate()
val end: Instant? = json.optNullableString("end")?.let { Instant.parse(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,12 @@ public class ImageNavigatorFragment private constructor(

@Suppress("DEPRECATION")
@Deprecated(
"Use `presentation.value.readingProgression` instead",
replaceWith = ReplaceWith("presentation.value.readingProgression"),
"Use `overflow.value.readingProgression` instead",
replaceWith = ReplaceWith("overflow.value.readingProgression"),
level = DeprecationLevel.ERROR
)
override val readingProgression: PublicationReadingProgression =
publication.metadata.effectiveReadingProgression
throw NotImplementedError()

@ExperimentalReadiumApi
override val overflow: StateFlow<OverflowableNavigator.Overflow> =
Expand Down
Loading

0 comments on commit 374c9c3

Please sign in to comment.