mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-08 06:42:21 -04:00
Merge pull request #3937 from CalebKL/task/anr-fix-zimfile-reader
Task/anr fix zimfile reader
This commit is contained in:
commit
8bd23938bc
@ -159,12 +159,25 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
|||||||
override fun openHomeScreen() {
|
override fun openHomeScreen() {
|
||||||
Handler(Looper.getMainLooper()).postDelayed({
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
if (webViewList.size == 0) {
|
if (webViewList.size == 0) {
|
||||||
hideTabSwitcher()
|
hideTabSwitcher(false)
|
||||||
}
|
}
|
||||||
}, HIDE_TAB_SWITCHER_DELAY)
|
}, HIDE_TAB_SWITCHER_DELAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hideTabSwitcher() {
|
/**
|
||||||
|
* Hides the tab switcher and optionally closes the ZIM book based on the `shouldCloseZimBook` parameter.
|
||||||
|
*
|
||||||
|
* @param shouldCloseZimBook If `true`, the ZIM book will be closed, and the `ZimFileReader` will be set to `null`.
|
||||||
|
* If `false`, it skips setting the `ZimFileReader` to `null`. This is particularly useful when restoring tabs,
|
||||||
|
* as setting the `ZimFileReader` to `null` would require re-creating it, which is a resource-intensive operation,
|
||||||
|
* especially for large ZIM files.
|
||||||
|
*
|
||||||
|
* Refer to the following methods for more details:
|
||||||
|
* @See exitBook
|
||||||
|
* @see closeTab
|
||||||
|
* @see closeAllTabs
|
||||||
|
*/
|
||||||
|
override fun hideTabSwitcher(shouldCloseZimBook: Boolean) {
|
||||||
actionBar?.let { actionBar ->
|
actionBar?.let { actionBar ->
|
||||||
actionBar.setDisplayShowTitleEnabled(true)
|
actionBar.setDisplayShowTitleEnabled(true)
|
||||||
toolbar?.let { activity?.setupDrawerToggle(it, true) }
|
toolbar?.let { activity?.setupDrawerToggle(it, true) }
|
||||||
@ -181,7 +194,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
|||||||
}
|
}
|
||||||
mainMenu?.showWebViewOptions(true)
|
mainMenu?.showWebViewOptions(true)
|
||||||
if (webViewList.isEmpty()) {
|
if (webViewList.isEmpty()) {
|
||||||
exitBook()
|
exitBook(shouldCloseZimBook)
|
||||||
} else {
|
} else {
|
||||||
// Reset the top margin of web views to 0 to remove any previously set margin
|
// Reset the top margin of web views to 0 to remove any previously set margin
|
||||||
// This ensures that the web views are displayed without any additional
|
// This ensures that the web views are displayed without any additional
|
||||||
|
@ -19,9 +19,12 @@
|
|||||||
package org.kiwix.kiwixmobile.core
|
package org.kiwix.kiwixmobile.core
|
||||||
|
|
||||||
import io.reactivex.Flowable
|
import io.reactivex.Flowable
|
||||||
|
import io.reactivex.Single
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.BiFunction
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
||||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
||||||
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
|
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
|
||||||
@ -45,7 +48,17 @@ class StorageObserver @Inject constructor(
|
|||||||
): Flowable<List<BookOnDisk>> {
|
): Flowable<List<BookOnDisk>> {
|
||||||
return scanFiles(scanningProgressListener)
|
return scanFiles(scanningProgressListener)
|
||||||
.withLatestFrom(downloadRoomDao.downloads(), BiFunction(::toFilesThatAreNotDownloading))
|
.withLatestFrom(downloadRoomDao.downloads(), BiFunction(::toFilesThatAreNotDownloading))
|
||||||
.map { it.mapNotNull(::convertToBookOnDisk) }
|
.flatMapSingle { files ->
|
||||||
|
Single.create { emitter ->
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
emitter.onSuccess(files.mapNotNull { convertToBookOnDisk(it) })
|
||||||
|
} catch (ignore: Exception) {
|
||||||
|
emitter.onError(ignore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scanFiles(scanningProgressListener: ScanningProgressListener) =
|
private fun scanFiles(scanningProgressListener: ScanningProgressListener) =
|
||||||
@ -57,7 +70,7 @@ class StorageObserver @Inject constructor(
|
|||||||
private fun fileHasNoMatchingDownload(downloads: List<DownloadModel>, file: File) =
|
private fun fileHasNoMatchingDownload(downloads: List<DownloadModel>, file: File) =
|
||||||
downloads.firstOrNull { file.absolutePath.endsWith(it.fileNameFromUrl) } == null
|
downloads.firstOrNull { file.absolutePath.endsWith(it.fileNameFromUrl) } == null
|
||||||
|
|
||||||
private fun convertToBookOnDisk(file: File) = runBlocking {
|
private suspend fun convertToBookOnDisk(file: File) =
|
||||||
zimReaderFactory.create(ZimReaderSource(file))
|
zimReaderFactory.create(ZimReaderSource(file))
|
||||||
?.let { zimFileReader ->
|
?.let { zimFileReader ->
|
||||||
BookOnDisk(zimFileReader).also {
|
BookOnDisk(zimFileReader).also {
|
||||||
@ -67,4 +80,3 @@ class StorageObserver @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -129,10 +129,10 @@ class LibkiwixBookmarks @Inject constructor(
|
|||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader): Flowable<List<String>> =
|
fun bookmarkUrlsForCurrentBook(zimId: String): Flowable<List<String>> =
|
||||||
flowableBookmarkList()
|
flowableBookmarkList()
|
||||||
.map { bookmarksList ->
|
.map { bookmarksList ->
|
||||||
bookmarksList.filter { it.zimId == zimFileReader.id }
|
bookmarksList.filter { it.zimId == zimId }
|
||||||
.map(LibkiwixBookmarkItem::bookmarkUrl)
|
.map(LibkiwixBookmarkItem::bookmarkUrl)
|
||||||
}
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
|
@ -860,7 +860,13 @@ abstract class CoreReaderFragment :
|
|||||||
view?.startAnimation(AnimationUtils.loadAnimation(view.context, anim))
|
view?.startAnimation(AnimationUtils.loadAnimation(view.context, anim))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun hideTabSwitcher() {
|
/**
|
||||||
|
* @param shouldCloseZimBook A flag to indicate whether the ZIM book should be closed.
|
||||||
|
* - Default is `true`, which ensures normal behavior for most scenarios.
|
||||||
|
* - If `false`, the ZIM book is not closed. This is useful in cases where the user restores tabs,
|
||||||
|
* as closing the ZIM book would require reloading the ZIM file, which can be a resource-intensive operation.
|
||||||
|
*/
|
||||||
|
protected open fun hideTabSwitcher(shouldCloseZimBook: Boolean = true) {
|
||||||
actionBar?.apply {
|
actionBar?.apply {
|
||||||
setDisplayShowTitleEnabled(true)
|
setDisplayShowTitleEnabled(true)
|
||||||
}
|
}
|
||||||
@ -1388,7 +1394,17 @@ abstract class CoreReaderFragment :
|
|||||||
.setAction(R.string.undo) { undoButton ->
|
.setAction(R.string.undo) { undoButton ->
|
||||||
undoButton.isEnabled = false
|
undoButton.isEnabled = false
|
||||||
restoreDeletedTab(index)
|
restoreDeletedTab(index)
|
||||||
}.show()
|
}.addCallback(object : Snackbar.Callback() {
|
||||||
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
|
super.onDismissed(transientBottomBar, event)
|
||||||
|
// If the undo button is not clicked and no tabs are left, exit the book and
|
||||||
|
// clean up resources.
|
||||||
|
if (event != DISMISS_EVENT_ACTION && webViewList.isEmpty()) {
|
||||||
|
closeZimBook()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
openHomeScreen()
|
openHomeScreen()
|
||||||
}
|
}
|
||||||
@ -1399,19 +1415,23 @@ abstract class CoreReaderFragment :
|
|||||||
mainMenu?.showBookSpecificMenuItems()
|
mainMenu?.showBookSpecificMenuItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun exitBook() {
|
protected fun exitBook(shouldCloseZimBook: Boolean = true) {
|
||||||
showNoBookOpenViews()
|
showNoBookOpenViews()
|
||||||
bottomToolbar?.visibility = View.GONE
|
bottomToolbar?.visibility = View.GONE
|
||||||
actionBar?.title = getString(R.string.reader)
|
actionBar?.title = getString(R.string.reader)
|
||||||
contentFrame?.visibility = View.GONE
|
contentFrame?.visibility = View.GONE
|
||||||
hideProgressBar()
|
hideProgressBar()
|
||||||
mainMenu?.hideBookSpecificMenuItems()
|
mainMenu?.hideBookSpecificMenuItems()
|
||||||
|
if (shouldCloseZimBook) {
|
||||||
closeZimBook()
|
closeZimBook()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun closeZimBook() {
|
fun closeZimBook() {
|
||||||
|
lifecycleScope.launch {
|
||||||
zimReaderContainer?.setZimReaderSource(null)
|
zimReaderContainer?.setZimReaderSource(null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected fun showProgressBarWithProgress(progress: Int) {
|
protected fun showProgressBarWithProgress(progress: Int) {
|
||||||
progressBar?.apply {
|
progressBar?.apply {
|
||||||
@ -1443,7 +1463,6 @@ abstract class CoreReaderFragment :
|
|||||||
LinearLayout.LayoutParams.MATCH_PARENT
|
LinearLayout.LayoutParams.MATCH_PARENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
zimReaderContainer?.setZimReaderSource(tempZimSourceForUndo)
|
|
||||||
webViewList.add(index, it)
|
webViewList.add(index, it)
|
||||||
tabsAdapter?.notifyDataSetChanged()
|
tabsAdapter?.notifyDataSetChanged()
|
||||||
snackBarRoot?.let { root ->
|
snackBarRoot?.let { root ->
|
||||||
@ -1800,7 +1819,7 @@ abstract class CoreReaderFragment :
|
|||||||
protected fun setUpBookmarks(zimFileReader: ZimFileReader) {
|
protected fun setUpBookmarks(zimFileReader: ZimFileReader) {
|
||||||
safeDispose()
|
safeDispose()
|
||||||
bookmarkingDisposable = Flowable.combineLatest(
|
bookmarkingDisposable = Flowable.combineLatest(
|
||||||
libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReader),
|
libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReader.id),
|
||||||
webUrlsProcessor,
|
webUrlsProcessor,
|
||||||
List<String?>::contains
|
List<String?>::contains
|
||||||
)
|
)
|
||||||
@ -1876,7 +1895,16 @@ abstract class CoreReaderFragment :
|
|||||||
setIsCloseAllTabButtonClickable(true)
|
setIsCloseAllTabButtonClickable(true)
|
||||||
restoreDeletedTabs()
|
restoreDeletedTabs()
|
||||||
}
|
}
|
||||||
}.show()
|
}.addCallback(object : Snackbar.Callback() {
|
||||||
|
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||||
|
super.onDismissed(transientBottomBar, event)
|
||||||
|
// If the undo button is not clicked and no tabs are left, exit the book and
|
||||||
|
// clean up resources.
|
||||||
|
if (event != DISMISS_EVENT_ACTION && webViewList.isEmpty()) {
|
||||||
|
closeZimBook()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1886,7 +1914,6 @@ abstract class CoreReaderFragment :
|
|||||||
|
|
||||||
private fun restoreDeletedTabs() {
|
private fun restoreDeletedTabs() {
|
||||||
if (tempWebViewListForUndo.isNotEmpty()) {
|
if (tempWebViewListForUndo.isNotEmpty()) {
|
||||||
zimReaderContainer?.setZimReaderSource(tempZimSourceForUndo)
|
|
||||||
webViewList.addAll(tempWebViewListForUndo)
|
webViewList.addAll(tempWebViewListForUndo)
|
||||||
tabsAdapter?.notifyDataSetChanged()
|
tabsAdapter?.notifyDataSetChanged()
|
||||||
snackBarRoot?.let { root ->
|
snackBarRoot?.let { root ->
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
package org.kiwix.kiwixmobile.core.reader
|
package org.kiwix.kiwixmobile.core.reader
|
||||||
|
|
||||||
import android.webkit.WebResourceResponse
|
import android.webkit.WebResourceResponse
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Factory
|
import org.kiwix.kiwixmobile.core.reader.ZimFileReader.Factory
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -32,11 +34,11 @@ class ZimReaderContainer @Inject constructor(private val zimFileReaderFactory: F
|
|||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setZimReaderSource(zimReaderSource: ZimReaderSource?) {
|
suspend fun setZimReaderSource(zimReaderSource: ZimReaderSource?) {
|
||||||
if (zimReaderSource == zimFileReader?.zimReaderSource) {
|
if (zimReaderSource == zimFileReader?.zimReaderSource) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
zimFileReader = runBlocking {
|
zimFileReader = withContext(Dispatchers.IO) {
|
||||||
if (zimReaderSource?.exists() == true && zimReaderSource.canOpenInLibkiwix())
|
if (zimReaderSource?.exists() == true && zimReaderSource.canOpenInLibkiwix())
|
||||||
zimFileReaderFactory.create(zimReaderSource)
|
zimFileReaderFactory.create(zimReaderSource)
|
||||||
else null
|
else null
|
||||||
|
@ -42,6 +42,7 @@ import org.kiwix.sharedFunctions.bookOnDisk
|
|||||||
import org.kiwix.sharedFunctions.resetSchedulers
|
import org.kiwix.sharedFunctions.resetSchedulers
|
||||||
import org.kiwix.sharedFunctions.setScheduler
|
import org.kiwix.sharedFunctions.setScheduler
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class StorageObserverTest {
|
class StorageObserverTest {
|
||||||
|
|
||||||
@ -106,6 +107,7 @@ class StorageObserverTest {
|
|||||||
.also {
|
.also {
|
||||||
downloads.offer(listOf(downloadModel))
|
downloads.offer(listOf(downloadModel))
|
||||||
files.offer(listOf(file))
|
files.offer(listOf(file))
|
||||||
|
it.awaitDone(2, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun withFiltering() {
|
private fun withFiltering() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user