Refactored the code to use coroutines instead of rxJava for saving/retrieving the bookmarks from libkiwix.

* Refactored the all unit and UI test cases according to it.
This commit is contained in:
MohitMaliFtechiz 2025-05-13 12:34:29 +05:30
parent d6ef855795
commit 3e92cda80f
8 changed files with 127 additions and 134 deletions

View File

@ -28,6 +28,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import io.objectbox.Box
import io.objectbox.BoxStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
@ -130,7 +131,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
box = boxStore!!.boxFor(BookmarkEntity::class.java)
// clear the data before running the test case
clearBookmarks()
runBlocking { clearBookmarks() }
// add a file in fileSystem because we need to actual file path for making object of Archive.
val loadFileStream =
@ -162,7 +163,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
objectBoxToLibkiwixMigrator.migrateBookMarks(box)
// check if data successfully migrated to room
val actualDataAfterMigration =
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst()
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first()
assertEquals(1, actualDataAfterMigration.size)
assertEquals(actualDataAfterMigration[0].zimReaderSource?.toDatabase(), expectedZimFilePath)
assertEquals(actualDataAfterMigration[0].zimId, expectedZimId)
@ -178,7 +179,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
// Migrate data from empty ObjectBox database
objectBoxToLibkiwixMigrator.migrateBookMarks(box)
val actualDataAfterMigration =
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst()
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first()
assertTrue(actualDataAfterMigration.isEmpty())
// Clear the bookmarks list from device to not affect the other test cases.
clearBookmarks()
@ -212,7 +213,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
// Migrate data into Room database
objectBoxToLibkiwixMigrator.migrateBookMarks(box)
val actualDataAfterMigration =
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst()
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first()
assertEquals(2, actualDataAfterMigration.size)
val existingItem =
actualDataAfterMigration.find {
@ -250,7 +251,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
objectBoxToLibkiwixMigrator.migrateBookMarks(box)
// Check if data successfully migrated to Room
val actualDataAfterMigration =
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst()
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first()
assertEquals(1000, actualDataAfterMigration.size)
// Clear the bookmarks list from device to not affect the other test cases.
clearBookmarks()
@ -276,7 +277,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
objectBoxToLibkiwixMigrator.migrateBookMarks(box)
// check if data successfully migrated to room
val actualDataAfterMigration =
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst()
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first()
assertEquals(1, actualDataAfterMigration.size)
assertEquals(actualDataAfterMigration[0].zimReaderSource?.toDatabase(), null)
assertEquals(actualDataAfterMigration[0].zimId, expectedZimId)
@ -307,7 +308,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
objectBoxToLibkiwixMigrator.migrateBookMarks(box)
// check if data successfully migrated to room
val actualDataAfterMigration =
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst()
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().first()
assertEquals(1, actualDataAfterMigration.size)
assertEquals(actualDataAfterMigration[0].zimReaderSource?.toDatabase(), null)
assertEquals(actualDataAfterMigration[0].zimId, expectedZimId)
@ -317,11 +318,11 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() {
clearBookmarks()
}
private fun clearBookmarks() {
private suspend fun clearBookmarks() {
// delete bookmarks for testing other edge cases
objectBoxToLibkiwixMigrator.libkiwixBookmarks.deleteBookmarks(
objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks()
.blockingFirst() as List<LibkiwixBookmarkItem>
.first() as List<LibkiwixBookmarkItem>
)
box.removeAll()
if (::zimFile.isInitialized) {

View File

@ -31,6 +31,7 @@ import com.google.android.apps.common.testing.accessibility.framework.Accessibil
import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck
import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck
import io.objectbox.BoxStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.anyOf
@ -171,13 +172,13 @@ class ImportBookmarkTest : BaseActivityTest() {
// test with empty data file
var tempBookmarkFile = getTemporaryBookmarkFile(true)
importBookmarks(tempBookmarkFile)
var actualDataAfterImporting = libkiwixBookmarks.bookmarks().blockingFirst()
var actualDataAfterImporting = libkiwixBookmarks.bookmarks().first()
assertEquals(0, actualDataAfterImporting.size)
// import the bookmark
tempBookmarkFile = getTemporaryBookmarkFile()
importBookmarks(tempBookmarkFile)
actualDataAfterImporting = libkiwixBookmarks.bookmarks().blockingFirst()
actualDataAfterImporting = libkiwixBookmarks.bookmarks().first()
assertEquals(3, actualDataAfterImporting.size)
assertEquals(actualDataAfterImporting[0].title, "Main Page")
assertEquals(actualDataAfterImporting[0].url, "https://kiwix.app/A/Main_Page")
@ -185,7 +186,7 @@ class ImportBookmarkTest : BaseActivityTest() {
// import duplicate bookmarks
importBookmarks(tempBookmarkFile)
actualDataAfterImporting = libkiwixBookmarks.bookmarks().blockingFirst()
actualDataAfterImporting = libkiwixBookmarks.bookmarks().first()
assertEquals(3, actualDataAfterImporting.size)
// delete the temp file
@ -200,11 +201,11 @@ class ImportBookmarkTest : BaseActivityTest() {
}
}
private fun clearBookmarks() {
private suspend fun clearBookmarks() {
// delete bookmarks for testing other edge cases
libkiwixBookmarks.deleteBookmarks(
libkiwixBookmarks.bookmarks()
.blockingFirst() as List<LibkiwixBookmarkItem>
.first() as List<LibkiwixBookmarkItem>
)
}

View File

@ -21,17 +21,18 @@ package org.kiwix.kiwixmobile.core.dao
import android.os.Build
import android.os.Environment
import android.util.Base64
import io.reactivex.BackpressureStrategy
import io.reactivex.BackpressureStrategy.LATEST
import io.reactivex.Flowable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.reactive.asPublisher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.rx3.rxSingle
import kotlinx.coroutines.withContext
import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.DarkModeConfig
import org.kiwix.kiwixmobile.core.R
@ -71,15 +72,19 @@ class LibkiwixBookmarks @Inject constructor(
private var bookmarkList: List<LibkiwixBookmarkItem> = arrayListOf()
private var libraryBooksList: List<String> = arrayListOf()
@Suppress("CheckResult", "IgnoredReturnValue")
private val bookmarkListBehaviour: BehaviorSubject<List<LibkiwixBookmarkItem>>? by lazy {
BehaviorSubject.create<List<LibkiwixBookmarkItem>>().also { subject ->
rxSingle { getBookmarksList() }
.subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io())
.subscribe(subject::onNext, subject::onError)
@Suppress("InjectDispatcher", "TooGenericExceptionCaught")
private val bookmarkListFlow: MutableStateFlow<List<LibkiwixBookmarkItem>> by lazy {
MutableStateFlow<List<LibkiwixBookmarkItem>>(emptyList()).also { flow ->
CoroutineScope(Dispatchers.IO).launch {
try {
val bookmarks = getBookmarksList()
flow.emit(bookmarks)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
private val bookmarksFolderPath: String by lazy {
if (Build.DEVICE.contains("generic")) {
// Workaround for emulators: Emulators have limited memory and
@ -112,30 +117,35 @@ class LibkiwixBookmarks @Inject constructor(
manager.readBookmarkFile(bookmarkFile.canonicalPath)
}
fun bookmarks(): Flowable<List<Page>> =
flowableBookmarkList()
fun bookmarks(): Flow<List<Page>> =
bookmarkListFlow
.map { it }
override fun pages(): Flowable<List<Page>> = bookmarks()
// Currently kept in RxJava Flowable because `PageViewModel` still expects RxJava streams.
// This can be refactored to use Kotlin Flow once `PageViewModel` is migrated to coroutines.
override fun pages(): Flowable<List<Page>> =
Flowable.fromPublisher(bookmarks().asPublisher())
override fun deletePages(pagesToDelete: List<Page>) =
deleteBookmarks(pagesToDelete as List<LibkiwixBookmarkItem>)
suspend fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List<String> {
return zimFileReader?.let { reader ->
getBookmarksList()
.filter { it.zimId == reader.id }
.map(LibkiwixBookmarkItem::bookmarkUrl)
}.orEmpty()
}
@Suppress("InjectDispatcher")
suspend fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List<String> =
withContext(Dispatchers.IO) {
return@withContext zimFileReader?.let { reader ->
getBookmarksList()
.filter { it.zimId == reader.id }
.map(LibkiwixBookmarkItem::bookmarkUrl)
}.orEmpty()
}
fun bookmarkUrlsForCurrentBook(zimId: String): Flowable<List<String>> =
flowableBookmarkList()
@Suppress("InjectDispatcher")
fun bookmarkUrlsForCurrentBook(zimId: String): Flow<List<String>> =
bookmarkListFlow
.map { bookmarksList ->
bookmarksList.filter { it.zimId == zimId }
.map(LibkiwixBookmarkItem::bookmarkUrl)
}
.subscribeOn(Schedulers.io())
}.flowOn(Dispatchers.IO)
/**
* Saves bookmarks in libkiwix. The use of `shouldWriteBookmarkToFile` is primarily
@ -165,7 +175,7 @@ class LibkiwixBookmarks @Inject constructor(
library.addBookmark(bookmark).also {
if (shouldWriteBookmarkToFile) {
writeBookMarksAndSaveLibraryToFile()
updateFlowableBookmarkList()
updateFlowBookmarkList()
}
// dispose the bookmark
bookmark.dispose()
@ -185,7 +195,7 @@ class LibkiwixBookmarks @Inject constructor(
}
}
addBookToLibraryIfNotExist(book)
updateFlowableBookmarkList()
updateFlowBookmarkList()
} catch (ignore: Exception) {
Log.e(
TAG,
@ -228,7 +238,7 @@ class LibkiwixBookmarks @Inject constructor(
.also {
CoroutineScope(dispatcher).launch {
writeBookMarksAndSaveLibraryToFile()
updateFlowableBookmarkList()
updateFlowBookmarkList()
}
}
}
@ -363,27 +373,8 @@ class LibkiwixBookmarks @Inject constructor(
it.zimReaderSource == libkiwixBookmarkItem.zimReaderSource
}
private fun flowableBookmarkList(
backpressureStrategy: BackpressureStrategy = LATEST
): Flowable<List<LibkiwixBookmarkItem>> {
return Flowable.create({ emitter ->
val disposable =
bookmarkListBehaviour?.subscribe(
{ list ->
if (!emitter.isCancelled) {
emitter.onNext(list.toList())
}
},
emitter::onError,
emitter::onComplete
)
emitter.setDisposable(disposable)
}, backpressureStrategy)
}
private suspend fun updateFlowableBookmarkList() {
bookmarkListBehaviour?.onNext(getBookmarksList())
private suspend fun updateFlowBookmarkList() {
bookmarkListFlow.emit(getBookmarksList())
}
// Export the `bookmark.xml` file to the `Download/org.kiwix/` directory of internal storage.

View File

@ -41,11 +41,11 @@ interface DataSource {
fun saveHistory(history: HistoryItem): Completable
fun deleteHistory(historyList: List<HistoryListItem>): Completable
fun clearHistory(): Completable
fun getBookmarks(): Flowable<List<LibkiwixBookmarkItem>>
fun getCurrentZimBookmarksUrl(): io.reactivex.rxjava3.core.Single<List<String>>
fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem): io.reactivex.rxjava3.core.Completable
fun deleteBookmarks(bookmarks: List<LibkiwixBookmarkItem>): Completable
fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable?
fun getBookmarks(): Flow<List<LibkiwixBookmarkItem>>
suspend fun getCurrentZimBookmarksUrl(): List<String>
suspend fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem)
suspend fun deleteBookmarks(bookmarks: List<LibkiwixBookmarkItem>)
suspend fun deleteBookmark(bookId: String, bookmarkUrl: String)
fun booksOnDiskAsListItems(): Flowable<List<BooksOnDiskListItem>>
fun saveNote(noteListItem: NoteListItem): Completable
fun deleteNote(noteTitle: String): Completable

View File

@ -21,9 +21,7 @@ package org.kiwix.kiwixmobile.core.data
import io.reactivex.Completable
import io.reactivex.Flowable
import io.reactivex.Scheduler
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx3.rxCompletable
import kotlinx.coroutines.rx3.rxSingle
import kotlinx.coroutines.flow.Flow
import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
import org.kiwix.kiwixmobile.core.dao.NewBookDao
@ -122,24 +120,19 @@ class Repository @Inject internal constructor(
}.subscribeOn(ioThread)
override fun getBookmarks() =
libkiwixBookmarks.bookmarks() as Flowable<List<LibkiwixBookmarkItem>>
libkiwixBookmarks.bookmarks() as Flow<List<LibkiwixBookmarkItem>>
override fun getCurrentZimBookmarksUrl() =
rxSingle {
libkiwixBookmarks.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader)
}.subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io())
override suspend fun getCurrentZimBookmarksUrl() =
libkiwixBookmarks.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader)
override fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) =
rxCompletable { libkiwixBookmarks.saveBookmark(libkiwixBookmarkItem) }
.subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io())
override suspend fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) =
libkiwixBookmarks.saveBookmark(libkiwixBookmarkItem)
override fun deleteBookmarks(bookmarks: List<LibkiwixBookmarkItem>) =
Completable.fromAction { libkiwixBookmarks.deleteBookmarks(bookmarks) }
.subscribeOn(ioThread)
override suspend fun deleteBookmarks(bookmarks: List<LibkiwixBookmarkItem>) =
libkiwixBookmarks.deleteBookmarks(bookmarks)
override fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable? =
Completable.fromAction { libkiwixBookmarks.deleteBookmark(bookId, bookmarkUrl) }
.subscribeOn(ioThread)
override suspend fun deleteBookmark(bookId: String, bookmarkUrl: String) =
libkiwixBookmarks.deleteBookmark(bookId, bookmarkUrl)
override fun saveNote(noteListItem: NoteListItem): Completable =
Completable.fromAction { notesRoomDao.saveNote(noteListItem) }

View File

@ -94,15 +94,15 @@ import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar
import io.reactivex.Flowable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.processors.BehaviorProcessor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@ -202,7 +202,7 @@ abstract class CoreReaderFragment :
NavigationHistoryClickListener,
ShowDonationDialogCallback {
protected val webViewList: MutableList<KiwixWebView> = ArrayList()
private val webUrlsProcessor = BehaviorProcessor.create<String>()
private val webUrlsFlow = MutableStateFlow("")
private var fragmentReaderBinding: FragmentReaderBinding? = null
var toolbar: Toolbar? = null
@ -333,7 +333,7 @@ abstract class CoreReaderFragment :
private var tableDrawerRight: RecyclerView? = null
private var tabCallback: ItemTouchHelper.Callback? = null
private var donationLayout: FrameLayout? = null
private var bookmarkingDisposable: Disposable? = null
private var bookmarkingJob: Job? = null
private var isBookmarked = false
private lateinit var serviceConnection: ServiceConnection
private var readAloudService: ReadAloudService? = null
@ -1269,7 +1269,7 @@ abstract class CoreReaderFragment :
(requireActivity() as? AppCompatActivity)?.setSupportActionBar(null)
}
repositoryActions?.dispose()
safeDispose()
safelyCancelBookmarkJob()
unBindViewsAndBinding()
tabCallback = null
hideBackToTopTimer?.cancel()
@ -1546,7 +1546,7 @@ abstract class CoreReaderFragment :
tabsAdapter?.selected = currentWebViewIndex
updateBottomToolbarVisibility()
loadPrefs()
updateUrlProcessor()
updateUrlFlow()
updateTableOfContents()
updateTitle()
}
@ -1898,24 +1898,25 @@ abstract class CoreReaderFragment :
}
protected fun setUpBookmarks(zimFileReader: ZimFileReader) {
safeDispose()
bookmarkingDisposable = Flowable.combineLatest(
libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReader.id),
webUrlsProcessor,
List<String?>::contains
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ isBookmarked: Boolean ->
this.isBookmarked = isBookmarked
safelyCancelBookmarkJob()
bookmarkingJob = CoroutineScope(Dispatchers.Main).launch {
combine(
libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReader.id) ?: emptyFlow(),
webUrlsFlow,
List<String?>::contains
).collect { isBookmarked ->
this@CoreReaderFragment.isBookmarked = isBookmarked
bottomToolbarBookmark?.setImageResource(
if (isBookmarked) R.drawable.ic_bookmark_24dp else R.drawable.ic_bookmark_border_24dp
)
}, Throwable::printStackTrace)
updateUrlProcessor()
}
}
updateUrlFlow()
}
private fun safeDispose() {
bookmarkingDisposable?.dispose()
private fun safelyCancelBookmarkJob() {
bookmarkingJob?.cancel()
bookmarkingJob = null
}
private fun isNotPreviouslyOpenZim(zimReaderSource: ZimReaderSource?): Boolean =
@ -2464,8 +2465,8 @@ abstract class CoreReaderFragment :
protected fun urlIsValid(): Boolean = getCurrentWebView()?.url != null
private fun updateUrlProcessor() {
getCurrentWebView()?.url?.let(webUrlsProcessor::offer)
private fun updateUrlFlow() {
getCurrentWebView()?.url?.let { webUrlsFlow.value = it }
}
private fun updateNightMode() {
@ -2673,7 +2674,7 @@ abstract class CoreReaderFragment :
// If a URL fails to load, update the bookmark toggle.
// This fixes the scenario where the previous page is bookmarked and the next
// page fails to load, ensuring the bookmark toggle is unset correctly.
updateUrlProcessor()
updateUrlFlow()
Log.d(TAG_KIWIX, String.format(getString(R.string.error_article_url_not_found), url))
}
}
@ -2681,7 +2682,7 @@ abstract class CoreReaderFragment :
@Suppress("MagicNumber")
override fun webViewProgressChanged(progress: Int, webView: WebView) {
if (isAdded) {
updateUrlProcessor()
updateUrlFlow()
showProgressBarWithProgress(progress)
if (progress == 100) {
hideProgressBar()

View File

@ -19,6 +19,8 @@ package org.kiwix.kiwixmobile.core.main
import io.reactivex.disposables.Disposable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.di.ActivityScope
@ -35,7 +37,6 @@ private const val TAG = "MainPresenter"
@ActivityScope
class MainRepositoryActions @Inject constructor(private val dataSource: DataSource) {
private var saveHistoryDisposable: Disposable? = null
private var saveBookmarkDisposable: io.reactivex.rxjava3.disposables.Disposable? = null
private var saveNoteDisposable: Disposable? = null
private var saveBookDisposable: Disposable? = null
private var deleteNoteDisposable: Disposable? = null
@ -48,16 +49,26 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour
.subscribe({}, { e -> Log.e(TAG, "Unable to save history", e) })
}
fun saveBookmark(bookmark: LibkiwixBookmarkItem) {
saveBookmarkDisposable =
dataSource.saveBookmark(bookmark)
.subscribe({}, { e -> Log.e(TAG, "Unable to save bookmark", e) })
@Suppress("InjectDispatcher", "TooGenericExceptionCaught")
suspend fun saveBookmark(bookmark: LibkiwixBookmarkItem) {
withContext(Dispatchers.IO) {
try {
dataSource.saveBookmark(bookmark)
} catch (e: Exception) {
Log.e(TAG, "Unable to save bookmark", e)
}
}
}
fun deleteBookmark(bookId: String, bookmarkUrl: String) {
dataSource.deleteBookmark(bookId, bookmarkUrl)
?.subscribe({}, { e -> Log.e(TAG, "Unable to delete bookmark", e) })
?: Log.e(TAG, "Unable to delete bookmark")
@Suppress("InjectDispatcher", "TooGenericExceptionCaught")
suspend fun deleteBookmark(bookId: String, bookmarkUrl: String) {
withContext(Dispatchers.IO) {
try {
dataSource.deleteBookmark(bookId, bookmarkUrl)
} catch (e: Exception) {
Log.e(TAG, "Unable to delete bookmark", e)
}
}
}
fun saveNote(note: NoteListItem) {
@ -93,7 +104,6 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour
fun dispose() {
saveHistoryDisposable?.dispose()
saveBookmarkDisposable?.dispose()
saveNoteDisposable?.dispose()
deleteNoteDisposable?.dispose()
saveBookDisposable?.dispose()

View File

@ -21,11 +21,11 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.mockk
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.Schedulers
import io.reactivex.Flowable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.reactive.asPublisher
import kotlinx.coroutines.test.runTest
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
@ -45,7 +45,6 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.setScheduler
import java.util.UUID
@ExtendWith(InstantExecutorExtension::class)
@ -58,13 +57,8 @@ internal class BookmarkViewModelTest {
private lateinit var viewModel: BookmarkViewModel
private val itemsFromDb: PublishProcessor<List<Page>> =
PublishProcessor.create()
init {
setScheduler(Schedulers.trampoline())
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
}
private val itemsFromDb: MutableStateFlow<List<Page>> =
MutableStateFlow(emptyList())
@BeforeEach
fun init() {
@ -72,8 +66,10 @@ internal class BookmarkViewModelTest {
every { zimReaderContainer.id } returns "id"
every { zimReaderContainer.name } returns "zimName"
every { sharedPreferenceUtil.showBookmarksAllBooks } returns true
every { libkiwixBookMarks.bookmarks() } returns itemsFromDb.distinctUntilChanged()
every { libkiwixBookMarks.pages() } returns libkiwixBookMarks.bookmarks()
every { libkiwixBookMarks.bookmarks() } returns itemsFromDb
every { libkiwixBookMarks.pages() } returns Flowable.fromPublisher(
libkiwixBookMarks.bookmarks().asPublisher()
)
viewModel =
BookmarkViewModel(libkiwixBookMarks, zimReaderContainer, sharedPreferenceUtil).apply {
alertDialogShower = dialogShower