Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -5,10 +5,15 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.count
import kotlinx.coroutines.flow.first
import org.hamcrest.CoreMatchers.*
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.notNullValue
import org.hamcrest.CoreMatchers.nullValue
import org.hamcrest.MatcherAssert.assertThat
import org.junit.After
import org.junit.Before
Expand All @@ -22,7 +27,7 @@ import org.wikipedia.search.db.RecentSearchDao
import org.wikipedia.talk.db.TalkPageSeen
import org.wikipedia.talk.db.TalkPageSeenDao
import org.wikipedia.util.log.L
import java.util.*
import java.util.Date

@RunWith(AndroidJUnit4::class)
class AppDatabaseTests {
Expand Down Expand Up @@ -100,8 +105,8 @@ class AppDatabaseTests {

notificationDao.insertNotifications(notifications)

var enWikiList = notificationDao.getNotificationsByWiki(listOf("enwiki")).first()
val zhWikiList = notificationDao.getNotificationsByWiki(listOf("zhwiki")).first()
var enWikiList = notificationDao.getNotificationsByWiki(listOf("enwiki"))
val zhWikiList = notificationDao.getNotificationsByWiki(listOf("zhwiki"))
assertThat(enWikiList, notNullValue())
assertThat(enWikiList.first().id, equalTo(123759827))
assertThat(zhWikiList.first().id, equalTo(2470933))
Expand All @@ -114,18 +119,18 @@ class AppDatabaseTests {
notificationDao.updateNotification(firstEnNotification)

// get updated item
enWikiList = notificationDao.getNotificationsByWiki(listOf("enwiki")).first()
enWikiList = notificationDao.getNotificationsByWiki(listOf("enwiki"))
assertThat(enWikiList.first().id, equalTo(123759827))
assertThat(enWikiList.first().isUnread, equalTo(true))

notificationDao.deleteNotification(firstEnNotification)
assertThat(notificationDao.getAllNotifications().size, equalTo(2))
assertThat(notificationDao.getNotificationsByWiki(listOf("enwiki")).first().size, equalTo(1))
assertThat(notificationDao.getNotificationsByWiki(listOf("enwiki")).size, equalTo(1))

notificationDao.deleteNotification(notificationDao.getNotificationsByWiki(listOf("enwiki")).first().first())
assertThat(notificationDao.getNotificationsByWiki(listOf("enwiki")).first().isEmpty(), equalTo(true))
notificationDao.deleteNotification(notificationDao.getNotificationsByWiki(listOf("enwiki")).first())
assertThat(notificationDao.getNotificationsByWiki(listOf("enwiki")).isEmpty(), equalTo(true))

notificationDao.deleteNotification(notificationDao.getNotificationsByWiki(listOf("zhwiki")).first().first())
notificationDao.deleteNotification(notificationDao.getNotificationsByWiki(listOf("zhwiki")).first())
assertThat(notificationDao.getAllNotifications().isEmpty(), equalTo(true))
}
}
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
4 changes: 3 additions & 1 deletion app/src/main/java/org/wikipedia/WikipediaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,9 @@ class WikipediaApp : Application() {
Prefs.tempAccountCreateDay = 0L
Prefs.tempAccountDialogShown = false
SharedPreferenceCookieManager.instance.clearAllCookies()
AppDatabase.instance.notificationDao().deleteAll()
MainScope().launch {
AppDatabase.instance.notificationDao().deleteAll()
}
FlowEventBus.post(LoggedOutEvent())
L.d("Logout complete.")
}
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import android.os.SystemClock
import androidx.annotation.StringRes
import androidx.core.app.PendingIntentCompat
import androidx.core.app.RemoteInput
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.wikipedia.Constants
import org.wikipedia.R
Expand Down Expand Up @@ -146,26 +149,30 @@ class NotificationPollBroadcastReceiver : BroadcastReceiver() {
return
}

// The notifications that we need to display are those that don't exist in our db yet.
val notificationsToDisplay = notifications.filter {
AppDatabase.instance.notificationDao().getNotificationById(it.wiki, it.id) == null
}
AppDatabase.instance.notificationDao().insertNotifications(notificationsToDisplay)
MainScope().launch(CoroutineExceptionHandler { _, throwable ->
L.w(throwable)
}) {
// The notifications that we need to display are those that don't exist in our db yet.
val notificationsToDisplay = notifications.filter {
AppDatabase.instance.notificationDao().getNotificationById(it.wiki, it.id) == null
}
AppDatabase.instance.notificationDao().insertNotifications(notificationsToDisplay)

if (notificationsToDisplay.isNotEmpty()) {
Prefs.notificationUnreadCount = notificationsToDisplay.size
FlowEventBus.post(UnreadNotificationsEvent())
}
if (notificationsToDisplay.isNotEmpty()) {
Prefs.notificationUnreadCount = notificationsToDisplay.size
FlowEventBus.post(UnreadNotificationsEvent())
}

if (notificationsToDisplay.size > 2) {
// Record that there is an incoming notification to track/compare further actions on it.
NotificationPresenter.showMultipleUnread(context, notificationsToDisplay.size)
} else {
for (n in notificationsToDisplay) {
if (notificationsToDisplay.size > 2) {
// Record that there is an incoming notification to track/compare further actions on it.
NotificationPresenter.showNotification(context, n,
dbWikiNameMap.getOrElse(n.wiki) { n.wiki },
dbWikiSiteMap.getValue(n.wiki).languageCode)
NotificationPresenter.showMultipleUnread(context, notificationsToDisplay.size)
} else {
for (n in notificationsToDisplay) {
// Record that there is an incoming notification to track/compare further actions on it.
NotificationPresenter.showNotification(context, n,
dbWikiNameMap.getOrElse(n.wiki) { n.wiki },
dbWikiSiteMap.getValue(n.wiki).languageCode)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import org.wikipedia.notifications.db.NotificationDao

class NotificationRepository(private val notificationDao: NotificationDao) {

fun getAllNotifications() = notificationDao.getAllNotifications()

private fun insertNotifications(notifications: List<Notification>) {
notificationDao.insertNotifications(notifications)
}
suspend fun getAllNotifications() = notificationDao.getAllNotifications()

suspend fun updateNotification(notification: Notification) {
notificationDao.updateNotification(notification)
Expand All @@ -28,7 +24,7 @@ class NotificationRepository(private val notificationDao: NotificationDao) {
var newContinueStr: String? = null
val response = ServiceFactory.get(WikipediaApp.instance.wikiSite).getAllNotifications(wikiList, filter, continueStr)
response.query?.notifications?.let {
insertNotifications(it.list.orEmpty())
notificationDao.insertNotifications(it.list.orEmpty())
newContinueStr = it.continueStr
}
return newContinueStr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class NotificationViewModel : ViewModel() {
fetchAndSave()
}

private fun filterAndPostNotifications() {
private suspend fun filterAndPostNotifications() {
val pair = Pair(processList(notificationRepository.getAllNotifications()), !currentContinueStr.isNullOrEmpty())
_uiState.value = Resource.Success(pair)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow

@Dao
interface NotificationDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertNotifications(notifications: List<Notification>)
suspend fun insertNotifications(notifications: List<Notification>)

@Update(onConflict = OnConflictStrategy.REPLACE)
suspend fun updateNotification(notification: Notification)
Expand All @@ -20,14 +19,14 @@ interface NotificationDao {
suspend fun deleteNotification(notification: Notification)

@Query("DELETE FROM Notification")
fun deleteAll()
suspend fun deleteAll()

@Query("SELECT * FROM Notification")
fun getAllNotifications(): List<Notification>
suspend fun getAllNotifications(): List<Notification>

@Query("SELECT * FROM Notification WHERE `wiki` IN (:wiki)")
fun getNotificationsByWiki(wiki: List<String>): Flow<List<Notification>>
suspend fun getNotificationsByWiki(wiki: List<String>): List<Notification>

@Query("SELECT * FROM Notification WHERE `wiki` IN (:wiki) AND `id` IN (:id)")
fun getNotificationById(wiki: String, id: Long): Notification?
suspend fun getNotificationById(wiki: String, id: Long): Notification?
}
Loading
Loading