Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
282c486
Initial commit of adding suspend modifier to DAO functions
cooltey Jun 27, 2025
30eb36e
More updates
cooltey Jun 27, 2025
5319c7b
Merge branch 'main' into optimize-dao-functions
cooltey Jun 27, 2025
14e57c7
fix errors
cooltey Jun 27, 2025
50c8e45
Merge branch 'main' into optimize-dao-functions
cooltey Jun 27, 2025
9a1d229
More updates regarding syntax fixes
cooltey Jun 27, 2025
84710be
tune up
cooltey Jun 27, 2025
73dfef1
Seen vs unseen
cooltey Jun 27, 2025
f2c4669
Merge branch 'main' into optimize-dao-functions
cooltey Jul 1, 2025
54a6e62
Merge branch 'main' into optimize-dao-functions
cooltey Jul 1, 2025
273c78f
update function for talk page holder
cooltey Jul 1, 2025
80fa4fe
Remove Transaction
cooltey Jul 2, 2025
e5f2295
Merge branch 'main' into optimize-dao-functions
cooltey Jul 2, 2025
c172b88
Merge branch 'main' into optimize-dao-functions
cooltey Jul 2, 2025
586d455
Add transaction back
cooltey Jul 2, 2025
6b7ce39
Revert "Add transaction back"
cooltey Jul 2, 2025
c4cdb3c
Update the logic of posting the flowBus
cooltey Jul 2, 2025
b84d662
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 7, 2025
c7cb29d
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 7, 2025
dd132c6
Adding suspend to Notification functions
cooltey Jul 7, 2025
bf2f259
Fix test
cooltey Jul 7, 2025
bb4666a
remove duplicated function
cooltey Jul 7, 2025
eb73431
Simplify
cooltey Jul 7, 2025
79aeba4
Update transation for moving lists
cooltey Jul 7, 2025
893fd5e
Merge branch 'main' into optimize-dao-functions
cooltey Jul 8, 2025
7b8f727
Merge branch 'main' into optimize-dao-functions
cooltey Jul 10, 2025
3ec2864
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 10, 2025
e108b69
Merge branch 'main' into optimize-dao-functions
cooltey Jul 11, 2025
be964c9
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 14, 2025
4faa4ac
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 15, 2025
dc10f57
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 15, 2025
357bea1
Merge branch 'main' into optimize-dao-functions
cooltey Jul 15, 2025
a451570
Merge branch 'main' into optimize-dao-functions
cooltey Jul 16, 2025
2f841e5
Keep same reset and logout and add coroutine for notificationDao only
cooltey Jul 16, 2025
0430f13
Merge branch 'main' into optimize-dao-functions
cooltey Jul 16, 2025
0b0bb63
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 17, 2025
eea8ce7
Merge branch 'main' into optimize-dao-functions
Williamrai Jul 21, 2025
033cf7d
Merge branch 'main' into optimize-dao-functions
cooltey Jul 22, 2025
eacff91
Merge branch 'main' into optimize-dao-functions
Williamrai Aug 4, 2025
a3652dd
Merge branch 'main' into optimize-dao-functions
cooltey Aug 14, 2025
cee1007
Merge branch 'main' into optimize-dao-functions
dbrant Aug 18, 2025
37fd707
Follow-up to updating DAO: improve efficiency. (#5854)
dbrant Aug 19, 2025
941208d
Merge branch 'main' into optimize-dao-functions
cooltey Aug 19, 2025
5bdb303
Use MainScope instead of AppCompatActivity
cooltey Aug 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import androidx.room.Room
import androidx.room.testing.MigrationTestHelper
import androidx.test.core.app.ApplicationProvider
import androidx.test.platform.app.InstrumentationRegistry
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.nullValue
Expand Down Expand Up @@ -123,7 +122,7 @@ class UpgradeFromPreRoomTest(private val fromVersion: Int) {
val historyEntry = historyDao.findEntryBy("ru.wikipedia.org", "ru", "Обама,_Барак")!!
assertThat(historyEntry.displayTitle, equalTo("Обама, Барак"))

val talkPageSeen = talkPageSeenDao.getAll().first()
val talkPageSeen = talkPageSeenDao.getAll()
if (fromVersion == 22) {
assertThat(talkPageSeen.count(), equalTo(2))
assertThat(offlineObjectDao.getOfflineObject("https://en.wikipedia.org/api/rest_v1/page/summary/Joe_Biden")!!.path, equalTo("/data/user/0/org.wikipedia.dev/files/offline_files/481b1ef996728fd9994bd97ab19733d8"))
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/org/wikipedia/database/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,6 @@ abstract class AppDatabase : RoomDatabase() {
MIGRATION_23_24, MIGRATION_24_25, MIGRATION_25_26, MIGRATION_26_27,
MIGRATION_26_28, MIGRATION_27_28, MIGRATION_28_29, MIGRATION_29_30,
MIGRATION_30_31)
.allowMainThreadQueries() // TODO: remove after resolving main thread issues in DAO classes
.fallbackToDestructiveMigration(false)
.build()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
package org.wikipedia.dataclient.okhttp

import okhttp3.*
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okio.*
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody
import okio.Buffer
import okio.BufferedSink
import okio.BufferedSource
import okio.Source
import okio.Timeout
import okio.buffer
import okio.sink
import okio.source
import okio.use
import org.wikipedia.WikipediaApp
import org.wikipedia.database.AppDatabase
import org.wikipedia.offline.db.OfflineObject
import org.wikipedia.util.StringUtil
import org.wikipedia.util.UriUtil
import org.wikipedia.util.log.L
import java.io.*
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.*
import java.io.InputStreamReader
import java.io.OutputStreamWriter

class OfflineCacheInterceptor : Interceptor {

Expand Down Expand Up @@ -49,7 +67,9 @@ class OfflineCacheInterceptor : Interceptor {
throw networkException
}
}
val obj = AppDatabase.instance.offlineObjectDao().findObject(url, lang)
val obj = runBlocking {
AppDatabase.instance.offlineObjectDao().findObject(url, lang)
}
if (obj == null) {
L.w("Offline object not present in database.")
throw networkException
Expand Down Expand Up @@ -139,8 +159,8 @@ class OfflineCacheInterceptor : Interceptor {
} ?: response
}

private inner class CacheWritingSource constructor(private val source: BufferedSource, private val cacheSink: BufferedSink,
private val obj: OfflineObject, private val title: String) : Source {
private inner class CacheWritingSource(private val source: BufferedSource, private val cacheSink: BufferedSink,
private val obj: OfflineObject, private val title: String) : Source {
private var cacheRequestClosed = false
private var failed = false

Expand All @@ -163,8 +183,9 @@ class OfflineCacheInterceptor : Interceptor {
// The cache response is complete!
cacheSink.close()
if (!failed) {
// update the record in the database!
AppDatabase.instance.offlineObjectDao().addObject(obj.url, obj.lang, obj.path, title)
runBlocking {
AppDatabase.instance.offlineObjectDao().addObject(obj.url, obj.lang, obj.path, title)
}
}
}
return -1
Expand Down Expand Up @@ -192,9 +213,9 @@ class OfflineCacheInterceptor : Interceptor {
}
}

private inner class CacheWritingResponseBody constructor(private val source: Source,
private val contentType: String?,
private val contentLength: Long) : ResponseBody() {
private inner class CacheWritingResponseBody(private val source: Source,
private val contentType: String?,
private val contentLength: Long) : ResponseBody() {
override fun contentType(): MediaType? {
return contentType?.toMediaTypeOrNull()
}
Expand All @@ -208,8 +229,8 @@ class OfflineCacheInterceptor : Interceptor {
}
}

private inner class CachedResponseBody constructor(private val file: File,
private val contentType: String?) : ResponseBody() {
private inner class CachedResponseBody(private val file: File,
private val contentType: String?) : ResponseBody() {
override fun contentType(): MediaType? {
return contentType?.toMediaTypeOrNull()
}
Expand Down
31 changes: 16 additions & 15 deletions app/src/main/java/org/wikipedia/offline/db/OfflineObjectDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import org.wikipedia.database.AppDatabase
import org.wikipedia.dataclient.WikiSite
Expand All @@ -15,33 +16,31 @@ import java.io.File
@Dao
interface OfflineObjectDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertOfflineObject(obj: OfflineObject)
suspend fun insertOfflineObject(obj: OfflineObject)

@Update(onConflict = OnConflictStrategy.REPLACE)
fun updateOfflineObject(obj: OfflineObject)
suspend fun updateOfflineObject(obj: OfflineObject)

@Query("SELECT * FROM OfflineObject WHERE url = :url AND lang = :lang LIMIT 1")
fun getOfflineObject(url: String, lang: String): OfflineObject?
suspend fun getOfflineObject(url: String, lang: String): OfflineObject?

@Query("SELECT * FROM OfflineObject WHERE url = :url LIMIT 1")
fun getOfflineObject(url: String): OfflineObject?
suspend fun getOfflineObject(url: String): OfflineObject?

@Query("SELECT * FROM OfflineObject WHERE url LIKE '%/' || :urlFragment || '/%' LIMIT 1")
fun searchForOfflineObject(urlFragment: String): OfflineObject?
suspend fun searchForOfflineObject(urlFragment: String): OfflineObject?

@Query("SELECT * FROM OfflineObject WHERE url LIKE '%' || :urlFragment || '%'")
fun searchForOfflineObjects(urlFragment: String): List<OfflineObject>
suspend fun searchForOfflineObjects(urlFragment: String): List<OfflineObject>

@Query("SELECT * FROM OfflineObject WHERE usedByStr LIKE '%|' || :id || '|%'")
fun getFromUsedById(id: Long): List<OfflineObject>
suspend fun getFromUsedById(id: Long): List<OfflineObject>

@Delete
fun deleteOfflineObject(obj: OfflineObject)
suspend fun deleteOfflineObject(obj: OfflineObject)

@Query("DELETE FROM OfflineObject")
fun deleteAll()

fun findObject(url: String, lang: String?): OfflineObject? {
@Transaction
suspend fun findObject(url: String, lang: String?): OfflineObject? {
var obj = if (lang.isNullOrEmpty()) getOfflineObject(url) else getOfflineObject(url, lang)

// Couldn't find an exact match, so...
Expand All @@ -56,7 +55,8 @@ interface OfflineObjectDao {
return obj
}

fun addObject(url: String, lang: String, path: String, pageTitle: String) {
@Transaction
suspend fun addObject(url: String, lang: String, path: String, pageTitle: String) {
// first find this item if it already exists in the db
var obj = getOfflineObject(url, lang)

Expand Down Expand Up @@ -88,7 +88,8 @@ interface OfflineObjectDao {
}
}

fun deleteObjectsForPageId(ids: List<Long>) {
@Transaction
suspend fun deleteObjectsForPageId(ids: List<Long>) {
ids.forEach { id ->
getFromUsedById(id).forEach { obj ->
if (obj.usedBy.contains(id)) {
Expand All @@ -105,7 +106,7 @@ interface OfflineObjectDao {
}
}

fun getTotalBytesForPageId(id: Long): Long {
suspend fun getTotalBytesForPageId(id: Long): Long {
var totalBytes: Long = 0
try {
totalBytes = getFromUsedById(id).sumOf { File("${it.path}.1").length() }
Expand Down
17 changes: 10 additions & 7 deletions app/src/main/java/org/wikipedia/page/PageFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,12 @@ class PageFragment : Fragment(), BackPressedHandler, CommunicationBridge.Communi
L.e(t)
}) {
if (!page.thumbUrl.equals(title.thumbUrl, true) || !page.description.equals(title.description, true)) {
AppDatabase.instance.readingListPageDao().updateMetadataByTitle(page, title.description, title.thumbUrl)
AppDatabase.instance.readingListPageDao().updateThumbAndDescriptionByName(
lang = page.wiki.languageCode,
apiTitle = page.apiTitle,
thumbUrl = title.thumbUrl,
description = title.description
)
}
}
}
Expand Down Expand Up @@ -1048,13 +1053,11 @@ class PageFragment : Fragment(), BackPressedHandler, CommunicationBridge.Communi
requireActivity().invalidateOptionsMenu()
}

fun updateBookmarkAndMenuOptionsFromDao() {
suspend fun updateBookmarkAndMenuOptionsFromDao() {
title?.let {
lifecycleScope.launch {
model.readingListPage = AppDatabase.instance.readingListPageDao().findPageInAnyList(it)
updateQuickActionsAndMenuOptions()
requireActivity().invalidateOptionsMenu()
}
model.readingListPage = AppDatabase.instance.readingListPageDao().findPageInAnyList(it)
updateQuickActionsAndMenuOptions()
requireActivity().invalidateOptionsMenu()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ open class AddToReadingListDialog : ExtendedBottomSheetDialogFragment() {
private fun showCreateListDialog() {
readingListTitleDialog(requireActivity(), "", "", readingLists.map { it.title }, callback = object : ReadingListTitleDialog.Callback {
override fun onSuccess(text: String, description: String) {
addAndDismiss(AppDatabase.instance.readingListDao().createList(text, description), titles)
lifecycleScope.launch(CoroutineExceptionHandler { _, throwable ->
L.e(throwable)
}) {
addAndDismiss(AppDatabase.instance.readingListDao().createList(text, description), titles)
}
}
}).show()
}
Expand Down
Loading
Loading