From 33bd3397e3100e2bfece658e8e54cdeeee892025 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 16 Oct 2024 16:30:05 +0530 Subject: [PATCH] Fixed: Move `canReadFile()` method to IO thread. * Moved the file readability check to the IO thread to prevent ANR. * Refactored the code to accommodate this change. --- .../destination/reader/KiwixReaderFragment.kt | 38 +++++++++++------- .../effects/OpenFileWithNavigation.kt | 26 ++++++++---- buildSrc/src/main/kotlin/Libs.kt | 3 ++ buildSrc/src/main/kotlin/Versions.kt | 2 + core/build.gradle.kts | 1 + core/detekt_baseline.xml | 2 +- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 31 +++++++++----- .../kiwix/kiwixmobile/core/dao/NewBookDao.kt | 27 ++++++++----- .../kiwix/kiwixmobile/core/data/DataSource.kt | 6 ++- .../kiwix/kiwixmobile/core/data/Repository.kt | 40 +++++++++---------- .../kiwixmobile/core/error/ErrorActivity.kt | 12 ++++-- .../core/extensions/FileExtensions.kt | 6 +-- .../core/main/CoreReaderFragment.kt | 29 +++++++++----- .../core/main/MainRepositoryActions.kt | 4 +- .../core/reader/ZimReaderSource.kt | 4 +- .../core/settings/CorePrefsFragment.kt | 7 +++- .../core/utils/DonationDialogHandler.kt | 19 ++++++--- .../core/utils/dialog/RateDialogHandler.kt | 13 ++++-- 18 files changed, 170 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt index b1f2e655e..15a731837 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt @@ -31,7 +31,11 @@ import android.view.View.VISIBLE import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.drawerlayout.widget.DrawerLayout +import androidx.lifecycle.lifecycleScope import com.google.android.material.bottomnavigation.BottomNavigationView +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.cachedComponent import org.kiwix.kiwixmobile.core.R.anim @@ -219,23 +223,27 @@ class KiwixReaderFragment : CoreReaderFragment() { ) { val settings = requireActivity().getSharedPreferences(SharedPreferenceUtil.PREF_KIWIX_MOBILE, 0) val zimReaderSource = fromDatabaseValue(settings.getString(TAG_CURRENT_FILE, null)) - - if (zimReaderSource != null && zimReaderSource.canOpenInLibkiwix()) { - if (zimReaderContainer?.zimReaderSource == null) { - openZimFile(zimReaderSource) - Log.d( - TAG_KIWIX, - "Kiwix normal start, Opened last used zimFile: -> ${zimReaderSource.toDatabase()}" - ) - } else { - zimReaderContainer?.zimFileReader?.let(::setUpBookmarks) + lifecycleScope.launch { + val canOpenInLibkiwix = withContext(Dispatchers.IO) { + zimReaderSource?.canOpenInLibkiwix() } - } else { - getCurrentWebView()?.snack(string.zim_not_opened) - exitBook() // hide the options for zim file to avoid unexpected UI behavior - return // book not found so don't need to restore the tabs for this file + if (zimReaderSource != null && canOpenInLibkiwix == true) { + if (zimReaderContainer?.zimReaderSource == null) { + openZimFile(zimReaderSource) + Log.d( + TAG_KIWIX, + "Kiwix normal start, Opened last used zimFile: -> ${zimReaderSource.toDatabase()}" + ) + } else { + zimReaderContainer?.zimFileReader?.let(::setUpBookmarks) + } + } else { + getCurrentWebView()?.snack(string.zim_not_opened) + exitBook() // hide the options for zim file to avoid unexpected UI behavior + return@launch // book not found so don't need to restore the tabs for this file + } + restoreTabs(zimArticles, zimPositions, currentTab) } - restoreTabs(zimArticles, zimPositions, currentTab) } @Throws(IllegalArgumentException::class) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/fileselectView/effects/OpenFileWithNavigation.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/fileselectView/effects/OpenFileWithNavigation.kt index 43eb6aed8..5ecd169fc 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/fileselectView/effects/OpenFileWithNavigation.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/fileselectView/effects/OpenFileWithNavigation.kt @@ -19,11 +19,16 @@ package org.kiwix.kiwixmobile.zimManager.fileselectView.effects import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem +import org.kiwix.kiwixmobile.main.KiwixMainActivity import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragmentDirections.actionNavigationLibraryToNavigationReader data class OpenFileWithNavigation(private val bookOnDisk: BooksOnDiskListItem.BookOnDisk) : @@ -31,14 +36,19 @@ data class OpenFileWithNavigation(private val bookOnDisk: BooksOnDiskListItem.Bo override fun invokeWith(activity: AppCompatActivity) { val zimReaderSource = bookOnDisk.zimReaderSource - if (!zimReaderSource.canOpenInLibkiwix()) { - activity.toast(R.string.error_file_not_found) - } else { - activity.navigate( - actionNavigationLibraryToNavigationReader().apply { - zimFileUri = zimReaderSource.toDatabase() - } - ) + (activity as KiwixMainActivity).lifecycleScope.launch { + val canOpenInLibkiwix = withContext(Dispatchers.IO) { + zimReaderSource.canOpenInLibkiwix() + } + if (!canOpenInLibkiwix) { + activity.toast(R.string.error_file_not_found) + } else { + activity.navigate( + actionNavigationLibraryToNavigationReader().apply { + zimFileUri = zimReaderSource.toDatabase() + } + ) + } } } } diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index 252ace60b..5633c803b 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -22,6 +22,9 @@ object Libs { "org.jetbrains.kotlinx:kotlinx-coroutines-android:" + Versions.org_jetbrains_kotlinx_kotlinx_coroutines + const val kotlinx_coroutines_rx3: String = + "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:" + Versions.kotlinx_coroutines_rx3 + /** * https://github.com/Kotlin/kotlinx.coroutines */ diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 63b40eeca..aa59c8599 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -16,6 +16,8 @@ object Versions { const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.8.1" + const val kotlinx_coroutines_rx3: String = "1.3.9" + const val androidx_test_espresso: String = "3.5.1" const val tracing: String = "1.1.0" diff --git a/core/build.gradle.kts b/core/build.gradle.kts index aeb3bec79..20216805f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -61,5 +61,6 @@ dependencies { implementation(Libs.webkit) testImplementation(Libs.kotlinx_coroutines_test) implementation(Libs.kotlinx_coroutines_android) + implementation(Libs.kotlinx_coroutines_rx3) implementation(Libs.zxing) } diff --git a/core/detekt_baseline.xml b/core/detekt_baseline.xml index af8150f87..552831001 100644 --- a/core/detekt_baseline.xml +++ b/core/detekt_baseline.xml @@ -12,7 +12,7 @@ LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener ) LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean ) LongParameterList:PageTestHelpers.kt$( bookmarkTitle: String = "bookmarkTitle", isSelected: Boolean = false, id: Long = 2, zimId: String = "zimId", zimName: String = "zimName", zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" ) - LongParameterList:Repository.kt$Repository$( @param:IO private val io: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val bookmarksDao: NewBookmarksDao, private val historyDao: HistoryDao, private val languageDao: NewLanguagesDao, private val recentSearchDao: NewRecentSearchDao, private val zimReaderContainer: ZimReaderContainer ) + LongParameterList:Repository.kt$Repository$( @param:IO private val ioThread: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val libkiwixBookmarks: LibkiwixBookmarks, private val historyRoomDao: HistoryRoomDao, private val notesRoomDao: NotesRoomDao, private val languageDao: NewLanguagesDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer ) LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, nonVideoView: ViewGroup, videoView: ViewGroup, webViewClient: CoreWebViewClient, private val toolbarView: View, private val bottomBarView: View, sharedPreferenceUtil: SharedPreferenceUtil, private val parentNavigationBar: View? = null ) MagicNumber:ArticleCount.kt$ArticleCount$3 MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100 diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index bd909712f..310f9ec29 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -29,10 +29,12 @@ import io.reactivex.subjects.BehaviorSubject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.rx3.rxSingle import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.DarkModeConfig import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp +import org.kiwix.kiwixmobile.core.extensions.deleteFile import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.page.adapter.Page @@ -68,8 +70,13 @@ class LibkiwixBookmarks @Inject constructor( private var bookmarkList: List = arrayListOf() private var libraryBooksList: List = arrayListOf() + @Suppress("CheckResult") private val bookmarkListBehaviour: BehaviorSubject>? by lazy { - BehaviorSubject.createDefault(getBookmarksList()) + BehaviorSubject.create>().also { subject -> + rxSingle { getBookmarksList() } + .subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io()) + .subscribe(subject::onNext, subject::onError) + } } private val bookmarksFolderPath: String by lazy { @@ -113,7 +120,7 @@ class LibkiwixBookmarks @Inject constructor( override fun deletePages(pagesToDelete: List) = deleteBookmarks(pagesToDelete as List) - fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List { + suspend fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List { return zimFileReader?.let { reader -> getBookmarksList() .filter { it.zimId == reader.id } @@ -134,7 +141,7 @@ class LibkiwixBookmarks @Inject constructor( * during data migration, where data is written to the file only once after all bookmarks * have been added to libkiwix to optimize the process. */ - fun saveBookmark( + suspend fun saveBookmark( libkiwixBookmarkItem: LibkiwixBookmarkItem, shouldWriteBookmarkToFile: Boolean = true ) { @@ -241,7 +248,7 @@ class LibkiwixBookmarks @Inject constructor( } @Suppress("ReturnCount") - private fun getBookmarksList(): List { + private suspend fun getBookmarksList(): List { if (!bookmarksChanged && bookmarkList.isNotEmpty()) { // No changes, return the cached data return bookmarkList.distinctBy(LibkiwixBookmarkItem::bookmarkUrl) @@ -290,7 +297,7 @@ class LibkiwixBookmarks @Inject constructor( } @Suppress("NestedBlockDepth") - private fun deleteDuplicateBookmarks() { + private suspend fun deleteDuplicateBookmarks() { bookmarkList.groupBy { it.bookmarkUrl to it.zimReaderSource } .filter { it.value.size > 1 } .forEach { (_, value) -> @@ -319,7 +326,7 @@ class LibkiwixBookmarks @Inject constructor( } } - private fun getZimFileReaderFromBookmark( + private suspend fun getZimFileReaderFromBookmark( bookmarkItem: LibkiwixBookmarkItem, coreApp: CoreApp ): ZimFileReader? { @@ -342,7 +349,7 @@ class LibkiwixBookmarks @Inject constructor( } } - private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = + private suspend fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = getBookmarksList() .any { it.url == libkiwixBookmarkItem.bookmarkUrl && @@ -368,7 +375,11 @@ class LibkiwixBookmarks @Inject constructor( } private fun updateFlowableBookmarkList() { - bookmarkListBehaviour?.onNext(getBookmarksList()) + bookmarkListBehaviour?.let { subject -> + rxSingle { getBookmarksList() } + .subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io()) + .subscribe(subject::onNext, subject::onError) + } } // Export the `bookmark.xml` file to the `Download/org.kiwix/` directory of internal storage. @@ -408,7 +419,7 @@ class LibkiwixBookmarks @Inject constructor( }.first { !it.isFileExist() } } - fun importBookmarks(bookmarkFile: File) { + suspend fun importBookmarks(bookmarkFile: File) { // Create a temporary library manager to import the bookmarks. val tempLibrary = Library() Manager(tempLibrary).apply { @@ -426,7 +437,7 @@ class LibkiwixBookmarks @Inject constructor( sharedPreferenceUtil.context.toast(R.string.bookmark_imported_message) if (bookmarkFile.exists()) { - bookmarkFile.delete() + bookmarkFile.deleteFile() } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewBookDao.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewBookDao.kt index 96fd19907..410848931 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewBookDao.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewBookDao.kt @@ -21,6 +21,7 @@ import io.objectbox.Box import io.objectbox.kotlin.inValues import io.objectbox.kotlin.query import io.objectbox.query.QueryBuilder +import kotlinx.coroutines.rx3.rxSingle import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity_ import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book @@ -31,23 +32,29 @@ import javax.inject.Inject class NewBookDao @Inject constructor(private val box: Box) { fun books() = box.asFlowable() - .map { books -> - books.map { bookOnDiskEntity -> - bookOnDiskEntity.file.let { file -> - // set zimReaderSource for previously saved books + .flatMap { books -> + io.reactivex.rxjava3.core.Flowable.fromIterable(books) + .flatMapSingle { bookOnDiskEntity -> + val file = bookOnDiskEntity.file val zimReaderSource = ZimReaderSource(file) - if (zimReaderSource.canOpenInLibkiwix()) { - bookOnDiskEntity.zimReaderSource = zimReaderSource - } + + rxSingle { zimReaderSource.canOpenInLibkiwix() } + .map { canOpen -> + if (canOpen) { + bookOnDiskEntity.zimReaderSource = zimReaderSource + } + bookOnDiskEntity + } + .onErrorReturn { bookOnDiskEntity } } - bookOnDiskEntity - } + .toList() + .toFlowable() } .doOnNext { removeBooksThatDoNotExist(it.toMutableList()) } .map { books -> books.filter { it.zimReaderSource.exists() } } .map { it.map(::BookOnDisk) } - fun getBooks() = box.all.map { bookOnDiskEntity -> + suspend fun getBooks() = box.all.map { bookOnDiskEntity -> bookOnDiskEntity.file.let { file -> // set zimReaderSource for previously saved books val zimReaderSource = ZimReaderSource(file) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt index 290d07efb..0b897b039 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt @@ -41,9 +41,11 @@ interface DataSource { fun deleteHistory(historyList: List): Completable fun clearHistory(): Completable fun getBookmarks(): Flowable> - fun getCurrentZimBookmarksUrl(): Single> + fun getCurrentZimBookmarksUrl(): io.reactivex.rxjava3.core.Single> + + fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem): + io.reactivex.rxjava3.core.Completable - fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem): Completable fun deleteBookmarks(bookmarks: List): Completable fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable? fun booksOnDiskAsListItems(): Flowable> diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt index bca9ca611..de4c7884d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt @@ -21,7 +21,8 @@ package org.kiwix.kiwixmobile.core.data import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Scheduler -import io.reactivex.Single +import kotlinx.coroutines.rx3.rxCompletable +import kotlinx.coroutines.rx3.rxSingle import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.NewBookDao @@ -47,10 +48,9 @@ import javax.inject.Singleton * A central repository of data which should provide the presenters with the required data. */ -@Suppress("LongParameterList") @Singleton class Repository @Inject internal constructor( - @param:IO private val io: Scheduler, + @param:IO private val ioThread: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, private val libkiwixBookmarks: LibkiwixBookmarks, @@ -64,7 +64,7 @@ class Repository @Inject internal constructor( override fun getLanguageCategorizedBooks() = booksOnDiskAsListItems() .first(emptyList()) - .subscribeOn(io) + .subscribeOn(ioThread) .observeOn(mainThread) override fun booksOnDiskAsListItems(): Flowable> = bookDao.books() @@ -91,60 +91,60 @@ class Repository @Inject internal constructor( override fun saveBooks(books: List) = Completable.fromAction { bookDao.insert(books) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun saveBook(book: BookOnDisk) = Completable.fromAction { bookDao.insert(listOf(book)) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun saveLanguages(languages: List) = Completable.fromAction { languageDao.insert(languages) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun saveHistory(history: HistoryItem) = Completable.fromAction { historyRoomDao.saveHistory(history) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun deleteHistory(historyList: List) = Completable.fromAction { historyRoomDao.deleteHistory(historyList.filterIsInstance(HistoryItem::class.java)) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun clearHistory() = Completable.fromAction { historyRoomDao.deleteAllHistory() recentSearchRoomDao.deleteSearchHistory() - }.subscribeOn(io) + }.subscribeOn(ioThread) override fun getBookmarks() = libkiwixBookmarks.bookmarks() as Flowable> override fun getCurrentZimBookmarksUrl() = - Single.just(libkiwixBookmarks.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader)) - .subscribeOn(io) - .observeOn(mainThread) + rxSingle { + libkiwixBookmarks.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader) + }.subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io()) override fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) = - Completable.fromAction { libkiwixBookmarks.saveBookmark(libkiwixBookmarkItem) } - .subscribeOn(io) + rxCompletable { libkiwixBookmarks.saveBookmark(libkiwixBookmarkItem) } + .subscribeOn(io.reactivex.rxjava3.schedulers.Schedulers.io()) override fun deleteBookmarks(bookmarks: List) = Completable.fromAction { libkiwixBookmarks.deleteBookmarks(bookmarks) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable? = Completable.fromAction { libkiwixBookmarks.deleteBookmark(bookId, bookmarkUrl) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun saveNote(noteListItem: NoteListItem): Completable = Completable.fromAction { notesRoomDao.saveNote(noteListItem) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun deleteNotes(noteList: List) = Completable.fromAction { notesRoomDao.deleteNotes(noteList) } - .subscribeOn(io) + .subscribeOn(ioThread) override fun deleteNote(noteTitle: String): Completable = Completable.fromAction { notesRoomDao.deleteNote(noteTitle) } - .subscribeOn(io) + .subscribeOn(ioThread) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/error/ErrorActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/error/ErrorActivity.kt index f1002a074..80c240459 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/error/ErrorActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/error/ErrorActivity.kt @@ -25,6 +25,8 @@ import android.os.Process import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.core.content.FileProvider +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.CoreApp.Companion.coreComponent import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation @@ -87,7 +89,9 @@ open class ErrorActivity : BaseActivity() { private fun setupReportButton() { activityKiwixErrorBinding?.reportButton?.setOnClickListener { - sendEmailLauncher.launch(Intent.createChooser(emailIntent(), "Send email...")) + lifecycleScope.launch { + sendEmailLauncher.launch(Intent.createChooser(emailIntent(), "Send email...")) + } } } @@ -96,7 +100,7 @@ open class ErrorActivity : BaseActivity() { restartApp() } - private fun emailIntent(): Intent { + private suspend fun emailIntent(): Intent { val emailBody = buildBody() return Intent(Intent.ACTION_SEND).apply { type = "text/plain" @@ -122,7 +126,7 @@ open class ErrorActivity : BaseActivity() { } } - private fun buildBody(): String = """ + private suspend fun buildBody(): String = """ $initialBody ${if (activityKiwixErrorBinding?.allowCrash?.isChecked == true && exception != null) exceptionDetails() else ""} @@ -139,7 +143,7 @@ open class ErrorActivity : BaseActivity() { ${exception?.let(::toStackTraceString)} """.trimIndent() - private fun zimFiles(): String { + private suspend fun zimFiles(): String { val allZimFiles = bookDao.getBooks().joinToString { """ ${it.book.title}: diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/FileExtensions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/FileExtensions.kt index a405c4a30..95c733ded 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/FileExtensions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/extensions/FileExtensions.kt @@ -41,10 +41,6 @@ fun File.totalSpace(): Long = runBlocking { } } -fun File.canReadFile(): Boolean = runBlocking { - withContext(Dispatchers.IO) { - canRead() - } -} +suspend fun File.canReadFile(): Boolean = withContext(Dispatchers.IO) { canRead() } suspend fun File.deleteFile(): Boolean = withContext(Dispatchers.IO) { delete() } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index 6bf336600..d9cf414de 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -80,6 +80,7 @@ import androidx.core.widget.ContentLoadingProgressBar import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.Observer +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -93,6 +94,9 @@ import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.processors.BehaviorProcessor +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.json.JSONArray import org.json.JSONException import org.kiwix.kiwixmobile.core.BuildConfig @@ -1641,16 +1645,21 @@ abstract class CoreReaderFragment : fun openZimFile(zimReaderSource: ZimReaderSource, isCustomApp: Boolean = false) { if (hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE) || isCustomApp) { - if (zimReaderSource.canOpenInLibkiwix()) { - // Show content if there is `Open Library` button showing - // and we are opening the ZIM file - reopenBook() - openAndSetInContainer(zimReaderSource) - updateTitle() - } else { - exitBook() - Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + zimReaderSource.toDatabase()) - requireActivity().toast(R.string.error_file_not_found, Toast.LENGTH_LONG) + lifecycleScope.launch { + val canOpenInLibkiwix = withContext(Dispatchers.IO) { + zimReaderSource.canOpenInLibkiwix() + } + if (canOpenInLibkiwix) { + // Show content if there is `Open Library` button showing + // and we are opening the ZIM file + reopenBook() + openAndSetInContainer(zimReaderSource) + updateTitle() + } else { + exitBook() + Log.w(TAG_KIWIX, "ZIM file doesn't exist at " + zimReaderSource.toDatabase()) + requireActivity().toast(R.string.error_file_not_found, Toast.LENGTH_LONG) + } } } else { this.zimReaderSource = zimReaderSource diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt index 0198c9fb5..5557c2b23 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt @@ -17,13 +17,13 @@ */ package org.kiwix.kiwixmobile.core.main -import org.kiwix.kiwixmobile.core.utils.files.Log import io.reactivex.disposables.Disposable import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.di.ActivityScope import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem +import org.kiwix.kiwixmobile.core.utils.files.Log import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import javax.inject.Inject @@ -32,7 +32,7 @@ private const val TAG = "MainPresenter" @ActivityScope class MainRepositoryActions @Inject constructor(private val dataSource: DataSource) { private var saveHistoryDisposable: Disposable? = null - private var saveBookmarkDisposable: 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 diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderSource.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderSource.kt index f52a80e9f..dc0ff40ad 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderSource.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimReaderSource.kt @@ -62,7 +62,7 @@ class ZimReaderSource( } } - fun canOpenInLibkiwix(): Boolean { + suspend fun canOpenInLibkiwix(): Boolean { return when { file?.canReadFile() == true -> true assetFileDescriptorList?.get(0)?.parcelFileDescriptor?.fd @@ -72,7 +72,7 @@ class ZimReaderSource( } } - fun createArchive(): Archive? { + suspend fun createArchive(): Archive? { if (canOpenInLibkiwix()) { return when { file != null -> Archive(file.canonicalPath) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt index e1f16f5d3..65638c765 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt @@ -40,6 +40,9 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.google.android.material.snackbar.Snackbar import eu.mhutti1.utils.storage.StorageDevice +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.CoreApp.Companion.coreComponent import org.kiwix.kiwixmobile.core.CoreApp.Companion.instance import org.kiwix.kiwixmobile.core.DarkModeConfig @@ -403,7 +406,9 @@ abstract class CorePrefsFragment : createTempFile(contentResolver.openInputStream(uri)).apply { if (isValidXmlFile(this)) { - libkiwixBookmarks?.importBookmarks(this) + CoroutineScope(Dispatchers.IO).launch { + libkiwixBookmarks?.importBookmarks(this@apply) + } } else { activity.toast( resources.getString(R.string.error_invalid_bookmark_file), diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt index 75e0dec31..8a03593f8 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/DonationDialogHandler.kt @@ -19,8 +19,11 @@ package org.kiwix.kiwixmobile.core.utils import android.app.Activity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp +import org.kiwix.kiwixmobile.core.main.CoreMainActivity import javax.inject.Inject const val THREE_DAYS_IN_MILLISECONDS = 3 * 24 * 60 * 60 * 1000L @@ -42,15 +45,19 @@ class DonationDialogHandler @Inject constructor( val currentMilliSeconds = System.currentTimeMillis() val lastPopupMillis = sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds val timeDifference = currentMilliSeconds - lastPopupMillis - if (shouldShowInitialPopup(lastPopupMillis) || timeDifference >= THREE_MONTHS_IN_MILLISECONDS) { - if (isZimFilesAvailableInLibrary() && isTimeToShowDonation(currentMilliSeconds)) { - showDonationDialogCallback?.showDonationDialog() - resetDonateLater() + (activity as CoreMainActivity).lifecycleScope.launch { + if (shouldShowInitialPopup(lastPopupMillis) || + timeDifference >= THREE_MONTHS_IN_MILLISECONDS + ) { + if (isZimFilesAvailableInLibrary() && isTimeToShowDonation(currentMilliSeconds)) { + showDonationDialogCallback?.showDonationDialog() + resetDonateLater() + } } } } - private fun shouldShowInitialPopup(lastPopupMillis: Long): Boolean = + private suspend fun shouldShowInitialPopup(lastPopupMillis: Long): Boolean = lastPopupMillis == 0L && isZimFilesAvailableInLibrary() private fun isTimeToShowDonation(currentMillis: Long): Boolean = @@ -63,7 +70,7 @@ class DonationDialogHandler @Inject constructor( return timeDifference >= THREE_DAYS_IN_MILLISECONDS } - fun isZimFilesAvailableInLibrary(): Boolean = + suspend fun isZimFilesAvailableInLibrary(): Boolean = if (activity.isCustomApp()) true else newBookDao.getBooks().isNotEmpty() fun updateLastDonationPopupShownTime() { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/RateDialogHandler.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/RateDialogHandler.kt index 0f1cf3666..70c68a342 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/RateDialogHandler.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/RateDialogHandler.kt @@ -22,11 +22,14 @@ import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri import androidx.annotation.IdRes +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.di.ActivityScope import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp +import org.kiwix.kiwixmobile.core.main.CoreMainActivity import org.kiwix.kiwixmobile.core.utils.NetworkUtils import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import javax.inject.Inject @@ -67,18 +70,20 @@ class RateDialogHandler @Inject constructor( tempVisitCount = visitCounterPref?.count ?: 0 ++tempVisitCount visitCounterPref?.count = tempVisitCount - if (shouldShowRateDialog() && NetworkUtils.isNetworkAvailable(activity)) { - showRateDialog(iconResId) + (activity as CoreMainActivity).lifecycleScope.launch { + if (shouldShowRateDialog() && NetworkUtils.isNetworkAvailable(activity)) { + showRateDialog(iconResId) + } } } - private fun shouldShowRateDialog(): Boolean { + private suspend fun shouldShowRateDialog(): Boolean { return tempVisitCount >= VISITS_REQUIRED_TO_SHOW_RATE_DIALOG && visitCounterPref?.noThanksState == false && isTwoWeekPassed() && isZimFilesAvailableInLibrary() && !BuildConfig.DEBUG } - private fun isZimFilesAvailableInLibrary(): Boolean { + private suspend fun isZimFilesAvailableInLibrary(): Boolean { // If it is a custom app, return true since custom apps always have the ZIM file. if (activity.isCustomApp()) return true // For Kiwix app, check if there are ZIM files available in the library.