mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
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.
This commit is contained in:
parent
5ceb3ceccb
commit
33bd3397e3
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
<ID>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 )</ID>
|
||||
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID>
|
||||
<ID>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" )</ID>
|
||||
<ID>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 )</ID>
|
||||
<ID>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 )</ID>
|
||||
<ID>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 )</ID>
|
||||
<ID>MagicNumber:ArticleCount.kt$ArticleCount$3</ID>
|
||||
<ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID>
|
||||
|
@ -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<LibkiwixBookmarkItem> = arrayListOf()
|
||||
private var libraryBooksList: List<String> = arrayListOf()
|
||||
|
||||
@Suppress("CheckResult")
|
||||
private val bookmarkListBehaviour: BehaviorSubject<List<LibkiwixBookmarkItem>>? by lazy {
|
||||
BehaviorSubject.createDefault(getBookmarksList())
|
||||
BehaviorSubject.create<List<LibkiwixBookmarkItem>>().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<Page>) =
|
||||
deleteBookmarks(pagesToDelete as List<LibkiwixBookmarkItem>)
|
||||
|
||||
fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List<String> {
|
||||
suspend fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List<String> {
|
||||
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<LibkiwixBookmarkItem> {
|
||||
private suspend fun getBookmarksList(): List<LibkiwixBookmarkItem> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<BookOnDiskEntity>) {
|
||||
|
||||
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)
|
||||
|
@ -41,9 +41,11 @@ interface DataSource {
|
||||
fun deleteHistory(historyList: List<HistoryListItem>): Completable
|
||||
fun clearHistory(): Completable
|
||||
fun getBookmarks(): Flowable<List<LibkiwixBookmarkItem>>
|
||||
fun getCurrentZimBookmarksUrl(): Single<List<String>>
|
||||
fun getCurrentZimBookmarksUrl(): io.reactivex.rxjava3.core.Single<List<String>>
|
||||
|
||||
fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem):
|
||||
io.reactivex.rxjava3.core.Completable
|
||||
|
||||
fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem): Completable
|
||||
fun deleteBookmarks(bookmarks: List<LibkiwixBookmarkItem>): Completable
|
||||
fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable?
|
||||
fun booksOnDiskAsListItems(): Flowable<List<BooksOnDiskListItem>>
|
||||
|
@ -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<List<BooksOnDiskListItem>> = bookDao.books()
|
||||
@ -91,60 +91,60 @@ class Repository @Inject internal constructor(
|
||||
|
||||
override fun saveBooks(books: List<BookOnDisk>) =
|
||||
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<Language>) =
|
||||
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<HistoryListItem>) =
|
||||
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<List<LibkiwixBookmarkItem>>
|
||||
|
||||
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<LibkiwixBookmarkItem>) =
|
||||
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<NoteListItem>) =
|
||||
Completable.fromAction { notesRoomDao.deleteNotes(noteList) }
|
||||
.subscribeOn(io)
|
||||
.subscribeOn(ioThread)
|
||||
|
||||
override fun deleteNote(noteTitle: String): Completable =
|
||||
Completable.fromAction { notesRoomDao.deleteNote(noteTitle) }
|
||||
.subscribeOn(io)
|
||||
.subscribeOn(ioThread)
|
||||
}
|
||||
|
@ -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}:
|
||||
|
@ -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() }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user