Skip to content

Commit 27d64b1

Browse files
mickael-menuqnga
andauthored
Fix a bunch of thread violations (#490)
Co-authored-by: qnga <[email protected]>
1 parent 9d18784 commit 27d64b1

File tree

15 files changed

+66
-63
lines changed

15 files changed

+66
-63
lines changed

.idea/.gitignore

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradle/libs.versions.toml

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ junit = "4.13.2"
5555

5656
kotlinx-coroutines = "1.7.3"
5757
kotlinx-coroutines-test = "1.7.3"
58+
kotlinx-datetime = "0.6.0-RC.2"
5859
kotlinx-serialization-json = "1.6.2"
5960

6061
# Make sure to align with the Kotlin version.
@@ -139,6 +140,7 @@ kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", versio
139140

140141
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
141142
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-test" }
143+
kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" }
142144
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
143145

144146
pdfium = { group = "com.github.barteksc", name = "pdfium-android", version.ref="pdfium" }

readium/adapters/pdfium/document/src/main/java/org/readium/adapter/pdfium/document/PdfiumDocument.kt

+6-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import kotlinx.coroutines.Dispatchers
1717
import kotlinx.coroutines.withContext
1818
import org.readium.r2.shared.InternalReadiumApi
1919
import org.readium.r2.shared.extensions.md5
20+
import org.readium.r2.shared.extensions.tryOrLog
2021
import org.readium.r2.shared.extensions.tryOrNull
2122
import org.readium.r2.shared.util.Try
2223
import org.readium.r2.shared.util.data.ReadError
@@ -69,7 +70,11 @@ public class PdfiumDocument(
6970
core.getTableOfContents(document).map { it.toOutlineNode() }
7071
}
7172

72-
override suspend fun close() {}
73+
override suspend fun close() {
74+
tryOrLog {
75+
core.closeDocument(document)
76+
}
77+
}
7378

7479
public companion object
7580
}

readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt

-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
package org.readium.r2.navigator
88

99
import android.content.Context
10-
import android.content.SharedPreferences
1110
import android.graphics.PointF
1211
import android.graphics.Rect
1312
import android.graphics.RectF
@@ -112,7 +111,6 @@ internal open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebV
112111
}
113112

114113
var listener: Listener? = null
115-
internal var preferences: SharedPreferences? = null
116114

117115
var resourceUrl: AbsoluteUrl? = null
118116

readium/navigator/src/main/java/org/readium/r2/navigator/image/ImageNavigatorFragment.kt

-8
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
package org.readium.r2.navigator.image
88

9-
import android.content.Context
10-
import android.content.SharedPreferences
119
import android.graphics.PointF
1210
import android.os.Bundle
1311
import android.view.LayoutInflater
@@ -65,8 +63,6 @@ public class ImageNavigatorFragment private constructor(
6563
internal lateinit var positions: List<Locator>
6664
internal lateinit var resourcePager: R2ViewPager
6765

68-
internal lateinit var preferences: SharedPreferences
69-
7066
internal lateinit var adapter: R2PagerAdapter
7167
private lateinit var currentActivity: FragmentActivity
7268

@@ -102,10 +98,6 @@ public class ImageNavigatorFragment private constructor(
10298
_binding = ReadiumNavigatorViewpagerBinding.inflate(inflater, container, false)
10399
val view = binding.root
104100

105-
preferences = requireContext().getSharedPreferences(
106-
"org.readium.r2.settings",
107-
Context.MODE_PRIVATE
108-
)
109101
resourcePager = binding.resourcePager
110102
resourcePager.publicationType = R2ViewPager.PublicationType.CBZ
111103

readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt

-8
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
package org.readium.r2.navigator.pager
1111

1212
import android.annotation.SuppressLint
13-
import android.content.Context
14-
import android.content.SharedPreferences
1513
import android.graphics.PointF
1614
import android.os.Bundle
1715
import android.util.DisplayMetrics
@@ -63,7 +61,6 @@ internal class R2EpubPageFragment : Fragment() {
6361
private set
6462

6563
private lateinit var containerView: View
66-
private lateinit var preferences: SharedPreferences
6764
private val viewModel: EpubNavigatorViewModel by viewModels(
6865
ownerProducer = { requireParentFragment() }
6966
)
@@ -137,10 +134,6 @@ internal class R2EpubPageFragment : Fragment() {
137134
): View {
138135
_binding = ReadiumNavigatorViewpagerFragmentEpubBinding.inflate(inflater, container, false)
139136
containerView = binding.root
140-
preferences = activity?.getSharedPreferences(
141-
"org.readium.r2.settings",
142-
Context.MODE_PRIVATE
143-
)!!
144137

145138
val webView = binding.webView
146139
this.webView = webView
@@ -158,7 +151,6 @@ internal class R2EpubPageFragment : Fragment() {
158151
}
159152
}
160153
}
161-
webView.preferences = preferences
162154

163155
webView.settings.javaScriptEnabled = true
164156
webView.isVerticalScrollBarEnabled = false

readium/shared/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ dependencies {
1717
implementation(libs.androidx.appcompat)
1818
implementation(libs.androidx.browser)
1919
implementation(libs.timber)
20-
implementation(libs.joda.time)
2120
implementation(libs.kotlin.reflect)
2221
implementation(libs.kotlinx.coroutines.android)
22+
implementation(libs.kotlinx.datetime)
2323
implementation(libs.kotlinx.serialization.json)
2424
implementation(libs.jsoup)
2525

readium/shared/src/main/java/org/readium/r2/shared/extensions/Date.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99

1010
package org.readium.r2.shared.extensions
1111

12-
import java.util.*
13-
import org.joda.time.DateTime
14-
import org.joda.time.DateTimeZone
12+
import java.util.Date
13+
import kotlinx.datetime.Instant
14+
import kotlinx.datetime.format
15+
import kotlinx.datetime.format.DateTimeComponents
1516
import org.readium.r2.shared.InternalReadiumApi
1617

1718
@InternalReadiumApi
18-
public fun Date.toIso8601String(timeZone: TimeZone = TimeZone.getTimeZone("UTC")): String =
19-
DateTime(this, DateTimeZone.forTimeZone(timeZone)).toString()
19+
public fun Date.toIso8601String(): String =
20+
Instant.fromEpochMilliseconds(time).format(DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)

readium/shared/src/main/java/org/readium/r2/shared/extensions/String.kt

+15-19
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,26 @@ package org.readium.r2.shared.extensions
1212
import android.net.Uri
1313
import java.net.URL
1414
import java.security.MessageDigest
15-
import java.util.*
16-
import org.joda.time.DateTime
17-
import org.joda.time.DateTimeZone
15+
import java.util.Date
16+
import kotlinx.datetime.Instant
17+
import kotlinx.datetime.LocalDate
18+
import kotlinx.datetime.LocalDateTime
19+
import kotlinx.datetime.TimeZone
20+
import kotlinx.datetime.atStartOfDayIn
21+
import kotlinx.datetime.toInstant
1822
import org.json.JSONException
1923
import org.json.JSONObject
2024
import org.readium.r2.shared.InternalReadiumApi
2125

2226
@InternalReadiumApi
23-
public fun String.iso8601ToDate(): Date? =
24-
try {
25-
// We assume that a date without a time zone component is in UTC. To handle this properly,
26-
// we need to set the default time zone of Joda to UTC, since by default it uses the local
27-
// time zone. This ensures that apps see exactly the same dates (e.g. published) no matter
28-
// where they are located.
29-
// For the same reason, the output Date will be in UTC. Apps should convert it to the local
30-
// time zone for display purposes, or keep it as UTC for storage.
31-
val defaultTZ = DateTimeZone.getDefault()
32-
DateTimeZone.setDefault(DateTimeZone.UTC)
33-
val date = DateTime(this).toDateTime(DateTimeZone.UTC).toDate()
34-
DateTimeZone.setDefault(defaultTZ)
35-
date
36-
} catch (e: Exception) {
37-
null
38-
}
27+
public fun String.iso8601ToDate(): Date? {
28+
val instant = tryOrNull { Instant.parse(this) }
29+
?: tryOrNull { LocalDateTime.parse(this).toInstant(TimeZone.UTC) }
30+
?: tryOrNull { LocalDate.parse(this).atStartOfDayIn(TimeZone.UTC) }
31+
?: return null
32+
33+
return Date(instant.toEpochMilliseconds())
34+
}
3935

4036
/**
4137
* If this string starts with the given [prefix], returns this string.

readium/shared/src/main/java/org/readium/r2/shared/util/file/FileResource.kt

+9-6
Original file line numberDiff line numberDiff line change
@@ -99,21 +99,24 @@ public class FileResource(
9999
}
100100

101101
override suspend fun length(): Try<Long, ReadError> =
102-
metadataLength?.let { Try.success(it) }
103-
?: Try.failure(
104-
ReadError.UnsupportedOperation(
105-
DebugError("Length not available for file at ${file.path}.")
102+
withContext(Dispatchers.IO) {
103+
metadataLength?.let { Try.success(it) }
104+
?: Try.failure(
105+
ReadError.UnsupportedOperation(
106+
DebugError("Length not available for file at ${file.path}.")
107+
)
106108
)
107-
)
109+
}
108110

109-
private val metadataLength: Long? =
111+
private val metadataLength: Long? by lazy {
110112
tryOrNull {
111113
if (file.isFile) {
112114
file.length()
113115
} else {
114116
null
115117
}
116118
}
119+
}
117120

118121
private inline fun <T> Try.Companion.catching(closure: () -> T): Try<T, ReadError> =
119122
try {

readium/shared/src/test/java/org/readium/r2/shared/extensions/StringTest.kt

+13
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,25 @@
66

77
package org.readium.r2.shared.extensions
88

9+
import kotlin.test.assertEquals
910
import kotlin.test.assertFalse
11+
import kotlin.test.assertNull
1012
import kotlin.test.assertTrue
1113
import org.junit.Test
1214

1315
class StringTest {
1416

17+
@Test
18+
fun `converts a ISO-8601 string to a Date`() {
19+
assertNull("invalid".iso8601ToDate())
20+
21+
assertEquals(1712707200000, "2024-04-10".iso8601ToDate()?.time)
22+
assertEquals(1712746680000, "2024-04-10T10:58".iso8601ToDate()?.time)
23+
assertEquals(1712746724000, "2024-04-10T10:58:44".iso8601ToDate()?.time)
24+
assertEquals(1712746724000, "2024-04-10T10:58:44Z".iso8601ToDate()?.time)
25+
assertEquals(1712746724000, "2024-04-10T10:58:44.000Z".iso8601ToDate()?.time)
26+
}
27+
1528
@Test
1629
fun `checks if a string is made of printable ASCII characters`() {
1730
assertTrue("".isPrintableAscii())

readium/shared/src/test/java/org/readium/r2/shared/opds/AvailabilityTest.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ class AvailabilityTest {
7474
JSONObject(
7575
"""{
7676
'state': 'available',
77-
'since': '2001-02-01T13:36:27.000Z',
78-
'until': '2001-02-01T13:36:27.000Z'
77+
'since': '2001-02-01T13:36:27Z',
78+
'until': '2001-02-01T13:36:27Z'
7979
}"""
8080
),
8181
Availability(
8282
state = Availability.State.AVAILABLE,
83-
since = "2001-02-01T13:36:27.000Z".iso8601ToDate(),
84-
until = "2001-02-01T13:36:27.000Z".iso8601ToDate()
83+
since = "2001-02-01T13:36:27Z".iso8601ToDate(),
84+
until = "2001-02-01T13:36:27Z".iso8601ToDate()
8585
).toJSON()
8686
)
8787
}

readium/shared/src/test/java/org/readium/r2/shared/publication/MetadataTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ class MetadataTest {
218218
],
219219
"title": {"en": "Title", "fr": "Titre"},
220220
"subtitle": {"en": "Subtitle", "fr": "Sous-titre"},
221-
"modified": "2001-01-01T12:36:27.000Z",
222-
"published": "2001-01-02T12:36:27.000Z",
221+
"modified": "2001-01-01T12:36:27Z",
222+
"published": "2001-01-02T12:36:27Z",
223223
"accessibility": {
224224
"conformsTo": ["http://www.idpf.org/epub/a11y/accessibility-20170105.html#wcag-a"],
225225
"accessMode": ["textual"],

test-app/src/main/java/org/readium/r2/testapp/Application.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class Application : android.app.Application() {
5252

5353
override fun onCreate() {
5454
if (DEBUG) {
55-
// enableStrictMode()
55+
enableStrictMode()
5656
Timber.plant(Timber.DebugTree())
5757
}
5858

test-app/src/main/java/org/readium/r2/testapp/domain/CoverStorage.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,10 @@ import org.readium.r2.shared.util.http.fetchWithDecoder
1818
import org.readium.r2.testapp.utils.tryOrLog
1919

2020
class CoverStorage(
21-
appStorageDir: File,
21+
private val appStorageDir: File,
2222
private val httpClient: HttpClient
2323
) {
2424

25-
private val coverDir: File =
26-
File(appStorageDir, "covers/")
27-
.apply { if (!exists()) mkdirs() }
28-
2925
suspend fun storeCover(publication: Publication, overrideUrl: AbsoluteUrl?): Try<File, Exception> {
3026
val coverBitmap: Bitmap? = overrideUrl?.fetchBitmap()
3127
?: publication.cover()
@@ -59,12 +55,16 @@ class CoverStorage(
5955

6056
private suspend fun storeCover(cover: Bitmap?): File =
6157
withContext(Dispatchers.IO) {
62-
val coverImageFile = File(coverDir, "${UUID.randomUUID()}.png")
58+
val coverImageFile = File(coverDir(), "${UUID.randomUUID()}.png")
6359
val resized = cover?.let { Bitmap.createScaledBitmap(it, 120, 200, true) }
6460
val fos = FileOutputStream(coverImageFile)
6561
resized?.compress(Bitmap.CompressFormat.PNG, 80, fos)
6662
fos.flush()
6763
fos.close()
6864
coverImageFile
6965
}
66+
67+
private fun coverDir(): File =
68+
File(appStorageDir, "covers/")
69+
.apply { if (!exists()) mkdirs() }
7070
}

0 commit comments

Comments
 (0)