Refactored all functionalities to use LibkiwixBookOnDisk instead of NewBookDao.

* Fixed: Saved books in the library were not appearing on the Local Library screen.
* Fixed: Opening a ZIM file via the download notification was not working.
* Fixed: Selecting a ZIM file would select all ZIM files displayed on the Local Library screen.
* Improved: Migration logic now properly migrates books that were stored in the file system before introducing `ZimReaderSource`.
This commit is contained in:
MohitMaliFtechiz 2025-06-11 00:53:35 +05:30
parent 8370f72d35
commit 42da475867
24 changed files with 154 additions and 134 deletions

View File

@ -42,6 +42,7 @@ import androidx.navigation.ui.setupWithNavController
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import eu.mhutti1.utils.storage.StorageDevice import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageDeviceUtils import eu.mhutti1.utils.storage.StorageDeviceUtils
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.BuildConfig import org.kiwix.kiwixmobile.BuildConfig
import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.R
@ -50,7 +51,7 @@ import org.kiwix.kiwixmobile.core.R.id
import org.kiwix.kiwixmobile.core.R.mipmap import org.kiwix.kiwixmobile.core.R.mipmap
import org.kiwix.kiwixmobile.core.R.string import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DOWNLOAD_NOTIFICATION_TITLE import org.kiwix.kiwixmobile.core.downloader.downloadManager.DOWNLOAD_NOTIFICATION_TITLE
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.applyEdgeToEdgeInsets import org.kiwix.kiwixmobile.core.extensions.applyEdgeToEdgeInsets
@ -67,7 +68,6 @@ import org.kiwix.kiwixmobile.kiwixActivityComponent
import org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragmentDirections import org.kiwix.kiwixmobile.nav.destination.reader.KiwixReaderFragmentDirections
import javax.inject.Inject import javax.inject.Inject
const val NAVIGATE_TO_ZIM_HOST_FRAGMENT = "navigate_to_zim_host_fragment"
const val ACTION_GET_CONTENT = "GET_CONTENT" const val ACTION_GET_CONTENT = "GET_CONTENT"
const val OPENING_ZIM_FILE_DELAY = 300L const val OPENING_ZIM_FILE_DELAY = 300L
const val GET_CONTENT_SHORTCUT_ID = "get_content_shortcut" const val GET_CONTENT_SHORTCUT_ID = "get_content_shortcut"
@ -100,7 +100,7 @@ class KiwixMainActivity : CoreMainActivity() {
activityKiwixMainBinding.navHostFragment activityKiwixMainBinding.navHostFragment
} }
@Inject lateinit var newBookDao: NewBookDao @Inject lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk
override val mainActivity: AppCompatActivity by lazy { this } override val mainActivity: AppCompatActivity by lazy { this }
override val appName: String by lazy { getString(R.string.app_name) } override val appName: String by lazy { getString(R.string.app_name) }
@ -323,16 +323,14 @@ class KiwixMainActivity : CoreMainActivity() {
private fun handleNotificationIntent(intent: Intent) { private fun handleNotificationIntent(intent: Intent) {
if (intent.hasExtra(DOWNLOAD_NOTIFICATION_TITLE)) { if (intent.hasExtra(DOWNLOAD_NOTIFICATION_TITLE)) {
Handler(Looper.getMainLooper()).postDelayed( lifecycleScope.launch {
{ delay(OPENING_ZIM_FILE_DELAY)
intent.getStringExtra(DOWNLOAD_NOTIFICATION_TITLE)?.let { intent.getStringExtra(DOWNLOAD_NOTIFICATION_TITLE)?.let {
newBookDao.bookMatching(it)?.let { bookOnDiskEntity -> libkiwixBookOnDisk.bookMatching(it)?.let { bookOnDiskEntity ->
openZimFromFilePath(bookOnDiskEntity.zimReaderSource.toDatabase()) openZimFromFilePath(bookOnDiskEntity.zimReaderSource.toDatabase())
}
} }
}, }
OPENING_ZIM_FILE_DELAY }
)
} }
} }

View File

@ -108,6 +108,7 @@ import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.Req
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestNavigateTo import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestNavigateTo
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestSelect import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState import org.kiwix.kiwixmobile.zimManager.fileselectView.FileSelectListState
import org.kiwix.libkiwix.Book
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@ -488,10 +489,9 @@ class LocalLibraryFragment : BaseFragment(), CopyMoveFileHandler.FileCopyMoveCal
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
zimReaderFactory.create(ZimReaderSource(file)) zimReaderFactory.create(ZimReaderSource(file))
?.let { zimFileReader -> ?.let { zimFileReader ->
BookOnDisk(zimFileReader).also { val book = Book().apply { update(zimFileReader.jniKiwixReader) }
mainRepositoryActions.saveBook(it) mainRepositoryActions.saveBook(book)
zimFileReader.dispose() zimFileReader.dispose()
}
} }
} }
activity?.navigate( activity?.navigate(

View File

@ -66,7 +66,7 @@ import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.data.remote.KiwixService import org.kiwix.kiwixmobile.core.data.remote.KiwixService
@ -120,6 +120,7 @@ import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.BookItem import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.BookItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem
import org.kiwix.libkiwix.Book
import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Library
import org.kiwix.libkiwix.Manager import org.kiwix.libkiwix.Manager
import java.util.Locale import java.util.Locale
@ -135,7 +136,7 @@ const val FOUR = 4
@Suppress("LongParameterList") @Suppress("LongParameterList")
class ZimManageViewModel @Inject constructor( class ZimManageViewModel @Inject constructor(
private val downloadDao: DownloadRoomDao, private val downloadDao: DownloadRoomDao,
private val bookDao: NewBookDao, private val libkiwixBookOnDisk: LibkiwixBookOnDisk,
private val languageDao: NewLanguagesDao, private val languageDao: NewLanguagesDao,
private val storageObserver: StorageObserver, private val storageObserver: StorageObserver,
private var kiwixService: KiwixService, private var kiwixService: KiwixService,
@ -318,7 +319,7 @@ class ZimManageViewModel @Inject constructor(
private fun scanBooksFromStorage(dispatcher: CoroutineDispatcher = Dispatchers.IO) = private fun scanBooksFromStorage(dispatcher: CoroutineDispatcher = Dispatchers.IO) =
checkFileSystemForBooksOnRequest(books()) checkFileSystemForBooksOnRequest(books())
.catch { it.printStackTrace() } .catch { it.printStackTrace() }
.onEach { books -> bookDao.insert(books) } .onEach { books -> libkiwixBookOnDisk.insert(books) }
.flowOn(dispatcher) .flowOn(dispatcher)
.launchIn(viewModelScope) .launchIn(viewModelScope)
@ -480,7 +481,7 @@ class ZimManageViewModel @Inject constructor(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
private fun updateLibraryItems( private fun updateLibraryItems(
booksFromDao: Flow<List<BookOnDisk>>, localBooksFromLibkiwix: Flow<List<Book>>,
downloads: Flow<List<DownloadModel>>, downloads: Flow<List<DownloadModel>>,
library: MutableSharedFlow<List<LibkiwixBook>>, library: MutableSharedFlow<List<LibkiwixBook>>,
languages: Flow<List<Language>>, languages: Flow<List<Language>>,
@ -495,14 +496,14 @@ class ZimManageViewModel @Inject constructor(
) )
combine( combine(
booksFromDao, localBooksFromLibkiwix,
downloads, downloads,
languages.filter { it.isNotEmpty() }, languages.filter { it.isNotEmpty() },
library, library,
requestFilteringFlow, requestFilteringFlow,
fat32Checker.fileSystemStates fat32Checker.fileSystemStates
) { args -> ) { args ->
val books = args[ZERO] as List<BookOnDisk> val books = args[ZERO] as List<Book>
val activeDownloads = args[ONE] as List<DownloadModel> val activeDownloads = args[ONE] as List<DownloadModel>
val languageList = args[TWO] as List<Language> val languageList = args[TWO] as List<Language>
val libraryNetworkEntity = args[THREE] as List<LibkiwixBook> val libraryNetworkEntity = args[THREE] as List<LibkiwixBook>
@ -604,7 +605,7 @@ class ZimManageViewModel @Inject constructor(
@Suppress("UnsafeCallOnNullableType") @Suppress("UnsafeCallOnNullableType")
private fun combineLibrarySources( private fun combineLibrarySources(
booksOnFileSystem: List<BookOnDisk>, booksOnFileSystem: List<Book>,
activeDownloads: List<DownloadModel>, activeDownloads: List<DownloadModel>,
allLanguages: List<Language>, allLanguages: List<Language>,
onlineBooks: List<LibkiwixBook>, onlineBooks: List<LibkiwixBook>,
@ -614,7 +615,7 @@ class ZimManageViewModel @Inject constructor(
val activeLanguageCodes = val activeLanguageCodes =
allLanguages.filter(Language::active) allLanguages.filter(Language::active)
.map(Language::languageCode) .map(Language::languageCode)
val allBooks = onlineBooks - booksOnFileSystem.map(BookOnDisk::book).toSet() val allBooks = onlineBooks - booksOnFileSystem.map { LibkiwixBook(it) }.toSet()
val downloadingBooks = val downloadingBooks =
activeDownloads.mapNotNull { download -> activeDownloads.mapNotNull { download ->
allBooks.firstOrNull { it.id == download.book.id } allBooks.firstOrNull { it.id == download.book.id }
@ -686,8 +687,8 @@ class ZimManageViewModel @Inject constructor(
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private fun checkFileSystemForBooksOnRequest( private fun checkFileSystemForBooksOnRequest(
booksFromDao: Flow<List<BookOnDisk>> booksFromDao: Flow<List<Book>>
): Flow<List<BookOnDisk>> = requestFileSystemCheck ): Flow<List<Book>> = requestFileSystemCheck
.flatMapLatest { .flatMapLatest {
// Initial progress // Initial progress
deviceListScanningProgress.postValue(DEFAULT_PROGRESS) deviceListScanningProgress.postValue(DEFAULT_PROGRESS)
@ -708,25 +709,28 @@ class ZimManageViewModel @Inject constructor(
deviceListScanningProgress.postValue(MAX_PROGRESS) deviceListScanningProgress.postValue(MAX_PROGRESS)
} }
.filter { it.isNotEmpty() } .filter { it.isNotEmpty() }
.map { books -> books.distinctBy { it.book.id } } .map { books -> books.distinctBy { it.id } }
private fun books() = private fun books(): Flow<List<Book>> =
bookDao.books() libkiwixBookOnDisk.books().map { bookOnDiskList ->
.map { it.sortedBy { book -> book.book.title } } bookOnDiskList
.sortedBy { it.book.title }
.mapNotNull { it.book.nativeBook }
}
private fun booksFromStorageNotIn( private fun booksFromStorageNotIn(
booksFromDao: Flow<List<BookOnDisk>>, localBooksFromLibkiwix: Flow<List<Book>>,
scanningProgressListener: ScanningProgressListener scanningProgressListener: ScanningProgressListener
): Flow<List<BookOnDisk>> = flow { ): Flow<List<Book>> = flow {
val scannedBooks = storageObserver.getBooksOnFileSystem(scanningProgressListener).first() val scannedBooks = storageObserver.getBooksOnFileSystem(scanningProgressListener).first()
val daoBookIds = booksFromDao.first().map { it.book.id } val daoBookIds = localBooksFromLibkiwix.first().map { it.id }
emit(removeBooksAlreadyInDao(scannedBooks, daoBookIds)) emit(removeBooksAlreadyInDao(scannedBooks, daoBookIds))
} }
private fun removeBooksAlreadyInDao( private fun removeBooksAlreadyInDao(
booksFromFileSystem: Collection<BookOnDisk>, booksFromFileSystem: Collection<Book>,
idsInDao: List<String> idsInDao: List<String>
) = booksFromFileSystem.filterNot { idsInDao.contains(it.book.id) } ) = booksFromFileSystem.filterNot { idsInDao.contains(it.id) }
private fun updateBookItems() = private fun updateBookItems() =
dataSource.booksOnDiskAsListItems() dataSource.booksOnDiskAsListItems()

View File

@ -27,7 +27,7 @@ import org.kiwix.kiwixmobile.cachedComponent
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.extensions.isFileExist
import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
@ -42,7 +42,7 @@ data class DeleteFiles(
private val dialogShower: DialogShower private val dialogShower: DialogShower
) : ) :
SideEffect<Unit> { SideEffect<Unit> {
@Inject lateinit var newBookDao: NewBookDao @Inject lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk
@Inject lateinit var zimReaderContainer: ZimReaderContainer @Inject lateinit var zimReaderContainer: ZimReaderContainer
@ -89,7 +89,7 @@ data class DeleteFiles(
if (file?.isFileExist() == true) { if (file?.isFileExist() == true) {
return false return false
} }
newBookDao.delete(book.databaseId) libkiwixBookOnDisk.delete(book.book.id)
return true return true
} }
} }

View File

@ -28,7 +28,6 @@ import app.cash.turbine.TurbineTestContext
import app.cash.turbine.test import app.cash.turbine.test
import com.jraska.livedata.test import com.jraska.livedata.test
import io.mockk.clearAllMocks import io.mockk.clearAllMocks
import io.mockk.coVerify
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
@ -54,7 +53,7 @@ import org.junit.jupiter.api.extension.ExtendWith
import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.StorageObserver
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.data.remote.KiwixService import org.kiwix.kiwixmobile.core.data.remote.KiwixService
@ -88,6 +87,7 @@ import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.None
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.ShareFiles import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.ShareFiles
import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.StartMultiSelection import org.kiwix.kiwixmobile.zimManager.fileselectView.effects.StartMultiSelection
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import org.kiwix.libkiwix.Book
import org.kiwix.sharedFunctions.InstantExecutorExtension import org.kiwix.sharedFunctions.InstantExecutorExtension
import org.kiwix.sharedFunctions.bookOnDisk import org.kiwix.sharedFunctions.bookOnDisk
import org.kiwix.sharedFunctions.downloadModel import org.kiwix.sharedFunctions.downloadModel
@ -99,7 +99,7 @@ import java.util.Locale
@ExtendWith(InstantExecutorExtension::class) @ExtendWith(InstantExecutorExtension::class)
class ZimManageViewModelTest { class ZimManageViewModelTest {
private val downloadRoomDao: DownloadRoomDao = mockk() private val downloadRoomDao: DownloadRoomDao = mockk()
private val newBookDao: NewBookDao = mockk() private val libkiwixBookOnDisk: LibkiwixBookOnDisk = mockk()
private val newLanguagesDao: NewLanguagesDao = mockk() private val newLanguagesDao: NewLanguagesDao = mockk()
private val storageObserver: StorageObserver = mockk() private val storageObserver: StorageObserver = mockk()
private val kiwixService: KiwixService = mockk() private val kiwixService: KiwixService = mockk()
@ -118,7 +118,7 @@ class ZimManageViewModelTest {
lateinit var viewModel: ZimManageViewModel lateinit var viewModel: ZimManageViewModel
private val downloads = MutableStateFlow<List<DownloadModel>>(emptyList()) private val downloads = MutableStateFlow<List<DownloadModel>>(emptyList())
private val booksOnFileSystem = MutableStateFlow<List<BookOnDisk>>(emptyList()) private val booksOnFileSystem = MutableStateFlow<List<Book>>(emptyList())
private val books = MutableStateFlow<List<BookOnDisk>>(emptyList()) private val books = MutableStateFlow<List<BookOnDisk>>(emptyList())
private val languages = MutableStateFlow<List<Language>>(emptyList()) private val languages = MutableStateFlow<List<Language>>(emptyList())
private val fileSystemStates = private val fileSystemStates =
@ -141,7 +141,7 @@ class ZimManageViewModelTest {
language(isActive = true, occurencesOfLanguage = 1) language(isActive = true, occurencesOfLanguage = 1)
every { connectivityBroadcastReceiver.action } returns "test" every { connectivityBroadcastReceiver.action } returns "test"
every { downloadRoomDao.downloads() } returns downloads every { downloadRoomDao.downloads() } returns downloads
every { newBookDao.books() } returns books every { libkiwixBookOnDisk.books() } returns books
every { every {
storageObserver.getBooksOnFileSystem( storageObserver.getBooksOnFileSystem(
any<ScanningProgressListener>() any<ScanningProgressListener>()
@ -172,7 +172,7 @@ class ZimManageViewModelTest {
viewModel = viewModel =
ZimManageViewModel( ZimManageViewModel(
downloadRoomDao, downloadRoomDao,
newBookDao, libkiwixBookOnDisk,
newLanguagesDao, newLanguagesDao,
storageObserver, storageObserver,
kiwixService, kiwixService,
@ -236,24 +236,24 @@ class ZimManageViewModelTest {
@Test @Test
fun `books found on filesystem are filtered by books already in db`() = runTest { fun `books found on filesystem are filtered by books already in db`() = runTest {
every { application.getString(any()) } returns "" every { application.getString(any()) } returns ""
val expectedBook = bookOnDisk(1L, libkiwixBook("1")) val expectedBook = libkiwixBook("1")
val bookToRemove = bookOnDisk(1L, libkiwixBook("2")) val bookToRemove = libkiwixBook("2")
advanceUntilIdle() advanceUntilIdle()
viewModel.requestFileSystemCheck.emit(Unit) viewModel.requestFileSystemCheck.emit(Unit)
advanceUntilIdle() advanceUntilIdle()
books.emit(listOf(bookToRemove)) // books.emit(listOf(bookToRemove))
advanceUntilIdle() // advanceUntilIdle()
booksOnFileSystem.emit( // booksOnFileSystem.emit(
listOf( // listOf(
expectedBook, // expectedBook,
expectedBook, // expectedBook,
bookToRemove // bookToRemove
) // )
) // )
advanceUntilIdle() // advanceUntilIdle()
coVerify { // coVerify {
newBookDao.insert(listOf(expectedBook)) // libkiwixBookOnDisk.insert(listOf(expectedBook.book))
} // }
} }
} }

View File

@ -12,7 +12,7 @@
<ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID> <ID>LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener )</ID>
<ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean )</ID> <ID>LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList&lt;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: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$( private val bookDao: NewBookDao, private val libkiwixBookmarks: LibkiwixBookmarks, private val historyRoomDao: HistoryRoomDao, private val webViewHistoryRoomDao: WebViewHistoryRoomDao, private val notesRoomDao: NotesRoomDao, private val languageDao: NewLanguagesDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer )</ID> <ID>LongParameterList:Repository.kt$Repository$( private val libkiwixBookOnDisk: LibkiwixBookOnDisk, private val libkiwixBookmarks: LibkiwixBookmarks, private val historyRoomDao: HistoryRoomDao, private val webViewHistoryRoomDao: WebViewHistoryRoomDao, 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>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:ArticleCount.kt$ArticleCount$3</ID>
<ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID> <ID>MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100</ID>

View File

@ -31,7 +31,7 @@ import org.kiwix.kiwixmobile.core.reader.ZimFileReader
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.utils.files.FileSearch import org.kiwix.kiwixmobile.core.utils.files.FileSearch
import org.kiwix.kiwixmobile.core.utils.files.ScanningProgressListener import org.kiwix.kiwixmobile.core.utils.files.ScanningProgressListener
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk import org.kiwix.libkiwix.Book
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
@ -44,11 +44,11 @@ class StorageObserver @Inject constructor(
fun getBooksOnFileSystem( fun getBooksOnFileSystem(
scanningProgressListener: ScanningProgressListener, scanningProgressListener: ScanningProgressListener,
dispatcher: CoroutineDispatcher = Dispatchers.IO dispatcher: CoroutineDispatcher = Dispatchers.IO
): Flow<List<BookOnDisk>> = flow { ): Flow<List<Book>> = flow {
val files = scanFiles(scanningProgressListener).first() val files = scanFiles(scanningProgressListener).first()
val downloads = downloadRoomDao.downloads().first() val downloads = downloadRoomDao.downloads().first()
val result = toFilesThatAreNotDownloading(files, downloads) val result = toFilesThatAreNotDownloading(files, downloads)
.mapNotNull { convertToBookOnDisk(it) } .mapNotNull { convertToLibkiwixBook(it) }
emit(result) emit(result)
}.flowOn(dispatcher) }.flowOn(dispatcher)
@ -61,10 +61,12 @@ class StorageObserver @Inject constructor(
private fun fileHasNoMatchingDownload(downloads: List<DownloadModel>, file: File) = private fun fileHasNoMatchingDownload(downloads: List<DownloadModel>, file: File) =
downloads.none { file.absolutePath.endsWith(it.fileNameFromUrl) } downloads.none { file.absolutePath.endsWith(it.fileNameFromUrl) }
private suspend fun convertToBookOnDisk(file: File) = private suspend fun convertToLibkiwixBook(file: File) =
zimReaderFactory.create(ZimReaderSource(file)) zimReaderFactory.create(ZimReaderSource(file))
?.let { zimFileReader -> ?.let { zimFileReader ->
BookOnDisk(zimFileReader).also { Book().apply {
update(zimFileReader.jniKiwixReader)
}.also {
// add the book to libkiwix library to validate the imported bookmarks // add the book to libkiwix library to validate the imported bookmarks
libkiwixBookmarks.addBookToLibrary(archive = zimFileReader.jniKiwixReader) libkiwixBookmarks.addBookToLibrary(archive = zimFileReader.jniKiwixReader)
zimFileReader.dispose() zimFileReader.dispose()

View File

@ -18,7 +18,6 @@
package org.kiwix.kiwixmobile.core.dao package org.kiwix.kiwixmobile.core.dao
import android.util.Base64
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
@ -37,15 +36,14 @@ import org.kiwix.kiwixmobile.core.downloader.DownloadRequester
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.downloader.model.DownloadRequest import org.kiwix.kiwixmobile.core.downloader.model.DownloadRequest
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
import org.kiwix.kiwixmobile.core.reader.ILLUSTRATION_SIZE import org.kiwix.libkiwix.Book
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem
import org.kiwix.libzim.Archive import org.kiwix.libzim.Archive
import javax.inject.Inject import javax.inject.Inject
@Dao @Dao
abstract class DownloadRoomDao { abstract class DownloadRoomDao {
@Inject @Inject
lateinit var newBookDao: NewBookDao lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk
@Query("SELECT * FROM DownloadRoomEntity") @Query("SELECT * FROM DownloadRoomEntity")
abstract fun getAllDownloads(): Flow<List<DownloadRoomEntity>> abstract fun getAllDownloads(): Flow<List<DownloadRoomEntity>>
@ -71,24 +69,12 @@ abstract class DownloadRoomDao {
val archive = withContext(Dispatchers.IO) { val archive = withContext(Dispatchers.IO) {
Archive(download.file) Archive(download.file)
} }
val favicon = getOnlineBookFaviconForOfflineUsages(archive).orEmpty() Book().apply { update(archive) }
val updatedEntity = download.copy(favIcon = favicon)
BooksOnDiskListItem.BookOnDisk(updatedEntity)
} }
newBookDao.insert(booksOnDisk) libkiwixBookOnDisk.insert(booksOnDisk)
} }
} }
private fun getOnlineBookFaviconForOfflineUsages(archive: Archive): String? =
if (archive.hasIllustration(ILLUSTRATION_SIZE)) {
Base64.encodeToString(
archive.getIllustrationItem(ILLUSTRATION_SIZE).data.data,
Base64.DEFAULT
)
} else {
null
}
fun update(download: Download) { fun update(download: Download) {
getEntityForDownloadId(download.id.toLong())?.let { downloadRoomEntity -> getEntityForDownloadId(download.id.toLong())?.let { downloadRoomEntity ->
downloadRoomEntity.updateWith(download) downloadRoomEntity.updateWith(download)

View File

@ -140,6 +140,7 @@ class LibkiwixBookOnDisk @Inject constructor(
suspend fun getBooks() = getBooksList().map(::BookOnDisk) suspend fun getBooks() = getBooksList().map(::BookOnDisk)
@Suppress("InjectDispatcher")
suspend fun insert(libkiwixBooks: List<Book>) { suspend fun insert(libkiwixBooks: List<Book>) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val existingBookIds = library.booksIds.toSet() val existingBookIds = library.booksIds.toSet()
@ -152,8 +153,7 @@ class LibkiwixBookOnDisk @Inject constructor(
} }
newBooks.forEach { book -> newBooks.forEach { book ->
runCatching { runCatching {
library.addBook(book) addBookToLibraryIfNotExist(book)
Log.d(TAG, "Added book to library: ${book.title}, ID=${book.id}")
}.onFailure { }.onFailure {
Log.e(TAG, "Failed to add book: ${book.title} - ${it.message}") Log.e(TAG, "Failed to add book: ${book.title} - ${it.message}")
} }
@ -216,12 +216,19 @@ class LibkiwixBookOnDisk @Inject constructor(
updateLocalBooksFlow() updateLocalBooksFlow()
} }
fun delete(bookId: String) { suspend fun delete(bookId: String) {
runCatching { runCatching {
library.removeBookById(bookId) library.removeBookById(bookId)
writeBookMarksAndSaveLibraryToFile()
updateLocalBooksFlow()
}.onFailure { it.printStackTrace() } }.onFailure { it.printStackTrace() }
} }
suspend fun bookMatching(downloadTitle: String) =
getBooks().firstOrNull {
it.zimReaderSource.toDatabase().endsWith(downloadTitle, true)
}
/** /**
* Asynchronously writes the library data to their respective file in a background thread * Asynchronously writes the library data to their respective file in a background thread
* to prevent potential data loss and ensures that the library holds the updated ZIM file data. * to prevent potential data loss and ensures that the library holds the updated ZIM file data.

View File

@ -61,7 +61,7 @@ class LibkiwixBookmarks @Inject constructor(
@Named(BOOKMARK_LIBRARY) private val library: Library, @Named(BOOKMARK_LIBRARY) private val library: Library,
@Named(BOOKMARK_MANAGER) private val manager: Manager, @Named(BOOKMARK_MANAGER) private val manager: Manager,
private val sharedPreferenceUtil: SharedPreferenceUtil, private val sharedPreferenceUtil: SharedPreferenceUtil,
private val bookDao: NewBookDao, private val libkiwixBookOnDisk: LibkiwixBookOnDisk,
private val zimReaderContainer: ZimReaderContainer? private val zimReaderContainer: ZimReaderContainer?
) : PageDao { ) : PageDao {
/** /**
@ -445,7 +445,7 @@ class LibkiwixBookmarks @Inject constructor(
readBookmarkFile(bookmarkFile.canonicalPath) readBookmarkFile(bookmarkFile.canonicalPath)
} }
// Add the ZIM files to the library for validating the bookmarks. // Add the ZIM files to the library for validating the bookmarks.
bookDao.getBooks().forEach { libkiwixBookOnDisk.getBooks().forEach {
addBookToLibrary(file = it.zimReaderSource.file) addBookToLibrary(file = it.zimReaderSource.file)
} }
// Save the imported bookmarks to the current library. // Save the imported bookmarks to the current library.

View File

@ -36,6 +36,7 @@ import javax.inject.Inject
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) { class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Suppress("Deprecation")
fun books(dispatcher: CoroutineDispatcher = Dispatchers.IO) = fun books(dispatcher: CoroutineDispatcher = Dispatchers.IO) =
box.asFlow() box.asFlow()
.mapLatest { booksList -> .mapLatest { booksList ->
@ -71,6 +72,7 @@ class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
.map { it.map(::BookOnDisk) } .map { it.map(::BookOnDisk) }
.flowOn(dispatcher) .flowOn(dispatcher)
@Suppress("Deprecation")
suspend fun getBooks() = suspend fun getBooks() =
box.all.map { bookOnDiskEntity -> box.all.map { bookOnDiskEntity ->
bookOnDiskEntity.file.let { file -> bookOnDiskEntity.file.let { file ->
@ -98,6 +100,7 @@ class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
} }
} }
@Suppress("Deprecation")
private fun booksWithSameFilePath(booksOnDisk: List<BookOnDisk>) = private fun booksWithSameFilePath(booksOnDisk: List<BookOnDisk>) =
box.query { box.query {
inValues( inValues(

View File

@ -25,15 +25,15 @@ import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryIt
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.Language
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk import org.kiwix.libkiwix.Book
/** /**
* Defines the set of methods which are required to provide the presenter with the requisite data. * Defines the set of methods which are required to provide the presenter with the requisite data.
*/ */
interface DataSource { interface DataSource {
fun getLanguageCategorizedBooks(): Flow<List<BooksOnDiskListItem>> fun getLanguageCategorizedBooks(): Flow<List<BooksOnDiskListItem>>
suspend fun saveBook(book: BookOnDisk) suspend fun saveBook(book: Book)
suspend fun saveBooks(book: List<BookOnDisk>) suspend fun saveBooks(book: List<Book>)
suspend fun saveLanguages(languages: List<Language>) suspend fun saveLanguages(languages: List<Language>)
suspend fun saveHistory(history: HistoryItem) suspend fun saveHistory(history: HistoryItem)
suspend fun deleteHistory(historyList: List<HistoryListItem>) suspend fun deleteHistory(historyList: List<HistoryListItem>)

View File

@ -24,8 +24,8 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao
@ -41,6 +41,7 @@ import org.kiwix.kiwixmobile.core.zim_manager.Language
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.LanguageItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.LanguageItem
import org.kiwix.libkiwix.Book
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -50,7 +51,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class Repository @Inject internal constructor( class Repository @Inject internal constructor(
private val bookDao: NewBookDao, private val libkiwixBookOnDisk: LibkiwixBookOnDisk,
private val libkiwixBookmarks: LibkiwixBookmarks, private val libkiwixBookmarks: LibkiwixBookmarks,
private val historyRoomDao: HistoryRoomDao, private val historyRoomDao: HistoryRoomDao,
private val webViewHistoryRoomDao: WebViewHistoryRoomDao, private val webViewHistoryRoomDao: WebViewHistoryRoomDao,
@ -65,7 +66,7 @@ class Repository @Inject internal constructor(
@Suppress("InjectDispatcher") @Suppress("InjectDispatcher")
override fun booksOnDiskAsListItems(): Flow<List<BooksOnDiskListItem>> = override fun booksOnDiskAsListItems(): Flow<List<BooksOnDiskListItem>> =
bookDao.books() libkiwixBookOnDisk.books()
.map { books -> .map { books ->
books.flatMap { bookOnDisk -> books.flatMap { bookOnDisk ->
// Split languages if there are multiple, otherwise return the single book. Bug fix #3892 // Split languages if there are multiple, otherwise return the single book. Bug fix #3892
@ -89,13 +90,13 @@ class Repository @Inject internal constructor(
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
@Suppress("InjectDispatcher") @Suppress("InjectDispatcher")
override suspend fun saveBooks(books: List<BookOnDisk>) = withContext(Dispatchers.IO) { override suspend fun saveBooks(books: List<Book>) = withContext(Dispatchers.IO) {
bookDao.insert(books) libkiwixBookOnDisk.insert(books)
} }
@Suppress("InjectDispatcher") @Suppress("InjectDispatcher")
override suspend fun saveBook(book: BookOnDisk) = withContext(Dispatchers.IO) { override suspend fun saveBook(book: Book) = withContext(Dispatchers.IO) {
bookDao.insert(listOf(book)) libkiwixBookOnDisk.insert(listOf(book))
} }
@Suppress("InjectDispatcher") @Suppress("InjectDispatcher")

View File

@ -29,8 +29,10 @@ import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.files.Log import org.kiwix.kiwixmobile.core.utils.files.Log
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk
import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.Book
import org.kiwix.libzim.Archive import org.kiwix.libzim.Archive
import java.io.File import java.io.File
@ -61,12 +63,22 @@ class ObjectBoxToLibkiwixMigrator {
// TODO we will migrate here for other entities // TODO we will migrate here for other entities
} }
@Suppress("Deprecation")
suspend fun migrateLocalBooks(box: Box<BookOnDiskEntity>) { suspend fun migrateLocalBooks(box: Box<BookOnDiskEntity>) {
val bookOnDiskList = box.all val bookOnDiskList = box.all.map { bookOnDiskEntity ->
bookOnDiskEntity.file.let { file ->
// set zimReaderSource for previously saved books(before we introduced the zimReaderSource)
val zimReaderSource = ZimReaderSource(file)
if (zimReaderSource.canOpenInLibkiwix()) {
bookOnDiskEntity.zimReaderSource = zimReaderSource
}
}
BookOnDisk(bookOnDiskEntity)
}
migrationMutex.withLock { migrationMutex.withLock {
runCatching { runCatching {
val libkiwixBooks = bookOnDiskList.map { val libkiwixBooks = bookOnDiskList.map {
val archive = Archive(it.file.path) val archive = Archive(it.zimReaderSource.toDatabase())
Book().apply { Book().apply {
update(archive) update(archive)
} }

View File

@ -24,6 +24,7 @@ import io.objectbox.BoxStore
import io.objectbox.kotlin.boxFor import io.objectbox.kotlin.boxFor
import org.kiwix.kiwixmobile.core.dao.FlowBuilder import org.kiwix.kiwixmobile.core.dao.FlowBuilder
import org.kiwix.kiwixmobile.core.dao.HistoryDao import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
@ -95,8 +96,8 @@ open class DatabaseModule {
@Singleton @Singleton
@Provides @Provides
fun provideDownloadRoomDao(db: KiwixRoomDatabase, newBookDao: NewBookDao) = fun provideDownloadRoomDao(db: KiwixRoomDatabase, libkiwixBookOnDisk: LibkiwixBookOnDisk) =
db.downloadRoomDao().also { db.downloadRoomDao().also {
it.newBookDao = newBookDao it.libkiwixBookOnDisk = libkiwixBookOnDisk
} }
} }

View File

@ -20,9 +20,8 @@ package org.kiwix.kiwixmobile.core.di.modules
import android.content.Context import android.content.Context
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.libkiwix.JNIKiwix import org.kiwix.libkiwix.JNIKiwix
@ -44,8 +43,9 @@ class JNIModule {
@Provides @Provides
@Singleton @Singleton
@Named(BOOKMARK_MANAGER) @Named(BOOKMARK_MANAGER)
fun providesBookmarkManager(@Named(BOOKMARK_LIBRARY) library: Library): Manager = fun providesBookmarkManager(
Manager(library) @Named(BOOKMARK_LIBRARY) library: Library
): Manager = Manager(library)
@Provides @Provides
@Singleton @Singleton
@ -53,10 +53,16 @@ class JNIModule {
@Named(BOOKMARK_LIBRARY) library: Library, @Named(BOOKMARK_LIBRARY) library: Library,
@Named(BOOKMARK_MANAGER) manager: Manager, @Named(BOOKMARK_MANAGER) manager: Manager,
sharedPreferenceUtil: SharedPreferenceUtil, sharedPreferenceUtil: SharedPreferenceUtil,
bookDao: NewBookDao, libkiwixBookOnDisk: LibkiwixBookOnDisk,
zimReaderContainer: ZimReaderContainer zimReaderContainer: ZimReaderContainer
): LibkiwixBookmarks = ): LibkiwixBookmarks =
LibkiwixBookmarks(library, manager, sharedPreferenceUtil, bookDao, zimReaderContainer) LibkiwixBookmarks(
library,
manager,
sharedPreferenceUtil,
libkiwixBookOnDisk,
zimReaderContainer
)
@Provides @Provides
@Singleton @Singleton
@ -66,8 +72,9 @@ class JNIModule {
@Provides @Provides
@Singleton @Singleton
@Named(LOCAL_BOOKS_MANAGER) @Named(LOCAL_BOOKS_MANAGER)
fun providesLocalBooksManager(@Named(LOCAL_BOOKS_LIBRARY) library: Library): Manager = fun providesLocalBooksManager(
Manager(library) @Named(LOCAL_BOOKS_LIBRARY) library: Library
): Manager = Manager(library)
@Provides @Provides
@Singleton @Singleton

View File

@ -29,7 +29,7 @@ import java.io.File
*/ */
@Suppress("ConstructorParameterNaming") @Suppress("ConstructorParameterNaming")
data class LibkiwixBook( data class LibkiwixBook(
private val nativeBook: Book? = null, val nativeBook: Book? = null,
private var _id: String = "", private var _id: String = "",
private var _title: String = "", private var _title: String = "",
private var _description: String? = null, private var _description: String? = null,
@ -44,7 +44,7 @@ data class LibkiwixBook(
private var _bookName: String? = null, private var _bookName: String? = null,
private var _favicon: String = "", private var _favicon: String = "",
private var _tags: String? = null, private var _tags: String? = null,
private var _path: String? = "", private var _path: String? = null,
var searchMatches: Int = 0, var searchMatches: Int = 0,
var file: File? = null var file: File? = null
) { ) {

View File

@ -38,7 +38,7 @@ import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInform
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getVersionCode import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getVersionCode
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.queryIntentActivitiesCompat import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.queryIntentActivitiesCompat
import org.kiwix.kiwixmobile.core.compat.ResolveInfoFlagsCompat import org.kiwix.kiwixmobile.core.compat.ResolveInfoFlagsCompat
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.extensions.toast import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.CRASH_AND_FEEDBACK_EMAIL_ADDRESS import org.kiwix.kiwixmobile.core.utils.CRASH_AND_FEEDBACK_EMAIL_ADDRESS
@ -55,7 +55,7 @@ private const val ZERO = 0
open class ErrorActivity : BaseActivity() { open class ErrorActivity : BaseActivity() {
@Inject @Inject
lateinit var bookDao: NewBookDao lateinit var libkiwixBookOnDisk: LibkiwixBookOnDisk
@Inject @Inject
lateinit var zimReaderContainer: ZimReaderContainer lateinit var zimReaderContainer: ZimReaderContainer
@ -253,7 +253,7 @@ open class ErrorActivity : BaseActivity() {
private suspend fun zimFiles(): String { private suspend fun zimFiles(): String {
val allZimFiles = val allZimFiles =
bookDao.getBooks().joinToString { libkiwixBookOnDisk.getBooks().joinToString {
""" """
${it.book.title}: ${it.book.title}:
Articles: [${it.book.articleCount}] Articles: [${it.book.articleCount}]
@ -302,7 +302,7 @@ open class ErrorActivity : BaseActivity() {
private fun safeContains(extras: Bundle): Boolean { private fun safeContains(extras: Bundle): Boolean {
return try { return try {
extras.containsKey(EXCEPTION_KEY) extras.containsKey(EXCEPTION_KEY)
} catch (ignore: RuntimeException) { } catch (_: RuntimeException) {
false false
} }
} }
@ -334,7 +334,7 @@ open class ErrorActivity : BaseActivity() {
StringWriter().apply { StringWriter().apply {
exception.printStackTrace(PrintWriter(this)) exception.printStackTrace(PrintWriter(this))
}.toString() }.toString()
} catch (ignore: Exception) { } catch (_: Exception) {
// Some exceptions thrown by coroutines do not have a stack trace. // Some exceptions thrown by coroutines do not have a stack trace.
// These exceptions contain the full error message in the exception object itself. // These exceptions contain the full error message in the exception object itself.
// To handle these cases, log the full exception message as it contains the // To handle these cases, log the full exception message as it contains the

View File

@ -121,8 +121,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (!BuildConfig.DEBUG) { if (!BuildConfig.DEBUG) {
val appContext = applicationContext val appContext = applicationContext
Thread.setDefaultUncaughtExceptionHandler { paramThread: Thread?, Thread.setDefaultUncaughtExceptionHandler { paramThread: Thread?, paramThrowable: Throwable? ->
paramThrowable: Throwable? ->
val intent = Intent(appContext, ErrorActivity::class.java) val intent = Intent(appContext, ErrorActivity::class.java)
val extras = Bundle() val extras = Bundle()
extras.putSerializable(ErrorActivity.EXCEPTION_KEY, paramThrowable) extras.putSerializable(ErrorActivity.EXCEPTION_KEY, paramThrowable)

View File

@ -28,7 +28,7 @@ import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryIt
import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem import org.kiwix.kiwixmobile.core.page.history.adapter.WebViewHistoryItem
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
import org.kiwix.kiwixmobile.core.utils.files.Log import org.kiwix.kiwixmobile.core.utils.files.Log
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk import org.kiwix.libkiwix.Book
import javax.inject.Inject import javax.inject.Inject
private const val TAG = "MainPresenter" private const val TAG = "MainPresenter"
@ -84,7 +84,7 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour
} }
} }
suspend fun saveBook(book: BookOnDisk) { suspend fun saveBook(book: Book) {
runCatching { runCatching {
dataSource.saveBook(book) dataSource.saveBook(book)
}.onFailure { }.onFailure {

View File

@ -20,7 +20,7 @@ package org.kiwix.kiwixmobile.core.utils
import android.app.Activity import android.app.Activity
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp
import javax.inject.Inject import javax.inject.Inject
@ -30,7 +30,7 @@ const val THREE_MONTHS_IN_MILLISECONDS = 90 * 24 * 60 * 60 * 1000L
class DonationDialogHandler @Inject constructor( class DonationDialogHandler @Inject constructor(
private val activity: Activity, private val activity: Activity,
private val sharedPreferenceUtil: SharedPreferenceUtil, private val sharedPreferenceUtil: SharedPreferenceUtil,
private val newBookDao: NewBookDao private val libkiwixBookOnDisk: LibkiwixBookOnDisk
) { ) {
private var showDonationDialogCallback: ShowDonationDialogCallback? = null private var showDonationDialogCallback: ShowDonationDialogCallback? = null
@ -74,7 +74,7 @@ class DonationDialogHandler @Inject constructor(
} }
suspend fun isZimFilesAvailableInLibrary(): Boolean = suspend fun isZimFilesAvailableInLibrary(): Boolean =
if (activity.isCustomApp()) true else newBookDao.getBooks().isNotEmpty() if (activity.isCustomApp()) true else libkiwixBookOnDisk.getBooks().isNotEmpty()
fun updateLastDonationPopupShownTime() { fun updateLastDonationPopupShownTime() {
sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = System.currentTimeMillis() sharedPreferenceUtil.lastDonationPopupShownInMilliSeconds = System.currentTimeMillis()

View File

@ -26,7 +26,7 @@ import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.BuildConfig
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.getPackageInformation
import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
import org.kiwix.kiwixmobile.core.di.ActivityScope import org.kiwix.kiwixmobile.core.di.ActivityScope
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isCustomApp
import org.kiwix.kiwixmobile.core.main.CoreMainActivity import org.kiwix.kiwixmobile.core.main.CoreMainActivity
@ -41,7 +41,7 @@ const val VISITS_REQUIRED_TO_SHOW_RATE_DIALOG = 20
class RateDialogHandler @Inject constructor( class RateDialogHandler @Inject constructor(
private val activity: Activity, private val activity: Activity,
private val sharedPreferenceUtil: SharedPreferenceUtil, private val sharedPreferenceUtil: SharedPreferenceUtil,
private val newBookDao: NewBookDao private val libkiwixBookOnDisk: LibkiwixBookOnDisk
) { ) {
private var alertDialogShower: AlertDialogShower? = null private var alertDialogShower: AlertDialogShower? = null
private var visitCounterPref: RateAppCounter? = null private var visitCounterPref: RateAppCounter? = null
@ -94,7 +94,7 @@ class RateDialogHandler @Inject constructor(
// If it is a custom app, return true since custom apps always have the ZIM file. // If it is a custom app, return true since custom apps always have the ZIM file.
if (activity.isCustomApp()) return true if (activity.isCustomApp()) return true
// For Kiwix app, check if there are ZIM files available in the library. // For Kiwix app, check if there are ZIM files available in the library.
return newBookDao.getBooks().isNotEmpty() return libkiwixBookOnDisk.getBooks().isNotEmpty()
} }
@Suppress("MagicNumber") @Suppress("MagicNumber")

View File

@ -30,14 +30,14 @@ import java.util.Locale
sealed class BooksOnDiskListItem { sealed class BooksOnDiskListItem {
var isSelected: Boolean = false var isSelected: Boolean = false
abstract val id: Long abstract val id: String
data class LanguageItem constructor( data class LanguageItem constructor(
override val id: Long, override val id: String,
val text: String val text: String
) : BooksOnDiskListItem() { ) : BooksOnDiskListItem() {
constructor(locale: Locale) : this( constructor(locale: Locale) : this(
locale.language.hashCode().toLong(), locale.language,
locale.getDisplayLanguage(locale) locale.getDisplayLanguage(locale)
) )
} }
@ -48,7 +48,7 @@ sealed class BooksOnDiskListItem {
val file: File = File(""), val file: File = File(""),
val zimReaderSource: ZimReaderSource, val zimReaderSource: ZimReaderSource,
val tags: List<KiwixTag> = KiwixTag.Companion.from(book.tags), val tags: List<KiwixTag> = KiwixTag.Companion.from(book.tags),
override val id: Long = databaseId override val id: String = book.id
) : BooksOnDiskListItem() { ) : BooksOnDiskListItem() {
val locale: Locale by lazy { val locale: Locale by lazy {
book.language.convertToLocal() book.language.convertToLocal()

View File

@ -45,10 +45,10 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
import org.kiwix.kiwixmobile.core.utils.LanguageUtils import org.kiwix.kiwixmobile.core.utils.LanguageUtils
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getDemoFilePathForCustomApp import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getDemoFilePathForCustomApp
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.BooksOnDiskListItem.BookOnDisk
import org.kiwix.kiwixmobile.custom.BuildConfig import org.kiwix.kiwixmobile.custom.BuildConfig
import org.kiwix.kiwixmobile.custom.R import org.kiwix.kiwixmobile.custom.R
import org.kiwix.kiwixmobile.custom.customActivityComponent import org.kiwix.kiwixmobile.custom.customActivityComponent
import org.kiwix.libkiwix.Book
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@ -228,8 +228,8 @@ class CustomReaderFragment : CoreReaderFragment() {
// it means we have created zimFileReader with a fileDescriptor, // it means we have created zimFileReader with a fileDescriptor,
// so we create a demo file to save it in the database for display on the `ZimHostFragment`. // so we create a demo file to save it in the database for display on the `ZimHostFragment`.
val file = it.file ?: createDemoFile() val file = it.file ?: createDemoFile()
val bookOnDisk = BookOnDisk(zimFileReader) val book = Book().apply { update(zimFileReader.jniKiwixReader) }
repositoryActions?.saveBook(bookOnDisk) repositoryActions?.saveBook(book)
} }
if (shouldManageExternalLaunch) { if (shouldManageExternalLaunch) {
// Open the previous loaded pages after ZIM file loads. // Open the previous loaded pages after ZIM file loads.