From e0849fa183753a922676ddfbfc2bdb5df5e28b95 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 2 Jul 2025 01:02:33 +0530 Subject: [PATCH] Fixed: Library language list is now retrieved dynamically from `libkiwix`. * Previously, we used `Locale.getISOLanguages()` to display the language list. However, this did not include all the languages in which books are available. Now, we use the `OPDS` stream via libkiwix to fetch online books, and utilize a method in libkiwix that provides the list of available book languages. This dynamically retrieved list is now shown in the `LanguageFragment`, allowing users to select a language and download books accordingly. * The `LanguageFragment` previously relied on `ObjectBox` for storing and managing selected languages. This has now been migrated to use `Room` database instead. * A migration strategy has been added to move previously selected languages from ObjectBox to Room. * Corresponding test cases for migration have been added, and existing tests have been refactored to reflect these changes. * Since we now rely on `libkiwix` to retrieve the language list, the `ZimManageViewModelTest` initially failed due to native code dependencies (JNI) not loading in unit test environments. To address this, we refactored the code and introduced a `JNIModule` class in the app module (where `OnlineLibraryManager` resides). We now inject the `Library` object, allowing us to pass a mocked instance to `ZimManageViewModel` for proper unit testing. --- app/detekt_baseline.xml | 2 +- .../ObjectBoxToRoomMigratorTest.kt | 63 +++++++++++++++++-- .../di/components/KiwixComponent.kt | 5 +- .../kiwix/kiwixmobile/di/modules/JNIModule.kt | 52 +++++++++++++++ .../language/viewmodel/LanguageViewModel.kt | 8 +-- .../viewmodel/SaveLanguagesAndFinish.kt | 6 +- .../zimManager/OnlineLibraryManager.kt | 17 ++++- .../zimManager/ZimManageViewModel.kt | 49 ++++++++------- .../viewmodel/LanguageViewModelTest.kt | 10 +-- .../viewmodel/SaveLanguagesAndFinishTest.kt | 4 +- .../zimManager/ZimManageViewModelTest.kt | 22 ++++--- core/detekt_baseline.xml | 2 +- .../kiwixmobile/core/dao/LanguageRoomDao.kt | 50 +++++++++++++++ .../core/dao/entities/LanguageRoomEntity.kt | 56 +++++++++++++++++ .../core/data/KiwixRoomDatabase.kt | 32 ++++++++-- .../kiwix/kiwixmobile/core/data/Repository.kt | 6 +- .../data/remote/ObjectBoxToRoomMigrator.kt | 12 ++++ .../core/di/components/CoreComponent.kt | 2 + .../core/di/modules/DatabaseModule.kt | 4 ++ .../core/utils/SharedPreferenceUtil.kt | 7 +++ 20 files changed, 348 insertions(+), 61 deletions(-) create mode 100644 app/src/main/java/org/kiwix/kiwixmobile/di/modules/JNIModule.kt create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/dao/LanguageRoomDao.kt create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageRoomEntity.kt diff --git a/app/detekt_baseline.xml b/app/detekt_baseline.xml index afafc1a17..70ed398dd 100644 --- a/app/detekt_baseline.xml +++ b/app/detekt_baseline.xml @@ -5,7 +5,7 @@ EmptyFunctionBlock:None.kt$None${ } EmptyFunctionBlock:SimplePageChangeListener.kt$SimplePageChangeListener${ } LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List<BookOnDisk>, activeDownloads: List<DownloadModel>, allLanguages: List<Language>, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState ) - LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: DownloadRoomDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private var kiwixService: KiwixService, val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil ) + LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: DownloadRoomDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private var kiwixService: KiwixService, val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil, private val onlineLibraryManager: OnlineLibraryManager ) MagicNumber:LibraryListItem.kt$LibraryListItem.LibraryDownloadItem$1000L MagicNumber:PeerGroupHandshake.kt$PeerGroupHandshake$15000 MagicNumber:ShareFiles.kt$ShareFiles$24 diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt index 6f1a350f9..b579fab32 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToRoomMigratorTest.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.runBlocking import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.core.IsEqual.equalTo import org.junit.After +import org.junit.Assert import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue @@ -44,6 +45,7 @@ import org.junit.runner.RunWith import org.kiwix.kiwixmobile.KiwixRoomDatabaseTest.Companion.getHistoryItem import org.kiwix.kiwixmobile.KiwixRoomDatabaseTest.Companion.getNoteListItem import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity +import org.kiwix.kiwixmobile.core.dao.entities.LanguageEntity import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase @@ -52,6 +54,7 @@ import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem import org.kiwix.kiwixmobile.core.utils.LanguageUtils import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.main.KiwixMainActivity import org.kiwix.kiwixmobile.testutils.TestUtils @@ -212,10 +215,13 @@ class ObjectBoxToRoomMigratorTest { private suspend fun clearRoomAndBoxStoreDatabases(box: Box) { // delete history for testing other edge cases - kiwixRoomDatabase.recentSearchRoomDao().deleteSearchHistory() - kiwixRoomDatabase.historyRoomDao().deleteAllHistory() - kiwixRoomDatabase.notesRoomDao() - .deletePages(kiwixRoomDatabase.notesRoomDao().notes().first()) + with(kiwixRoomDatabase) { + recentSearchRoomDao().deleteSearchHistory() + historyRoomDao().deleteAllHistory() + notesRoomDao() + .deletePages(kiwixRoomDatabase.notesRoomDao().notes().first()) + languageRoomDao().deleteAllLanguages() + } box.removeAll() } @@ -470,4 +476,53 @@ class ObjectBoxToRoomMigratorTest { migrationTime < migrationMaxTime ) } + + @Test + fun migrateLanguages_shouldInsertDataIntoRoomDatabase() = + runBlocking { + val box = boxStore.boxFor(LanguageEntity::class.java) + // clear both databases for history to test more edge cases + clearRoomAndBoxStoreDatabases(box) + + val language = Language("eng", true, 10) + val language1 = Language("fr", false, 1) + + // insert into object box + box.put(LanguageEntity(language)) + // migrate data into room database + objectBoxToRoomMigrator.migrateLanguages(box) + // check if data successfully migrated to room + var actualDataAfterMigration = kiwixRoomDatabase.languageRoomDao().languages().first() + assertEquals(actualDataAfterMigration.size, 1) + assertEquals(actualDataAfterMigration[0].language, language.language) + assertEquals(actualDataAfterMigration[0].occurencesOfLanguage, language.occurencesOfLanguage) + assertEquals(actualDataAfterMigration[0].active, language.active) + + // clear both databases to test more edge cases + clearRoomAndBoxStoreDatabases(box) + + // Migrate data from empty ObjectBox database + objectBoxToRoomMigrator.migrateLanguages(box) + val actualData = kiwixRoomDatabase.languageRoomDao().languages().first() + assertTrue(actualData.isEmpty()) + + // Test if data successfully migrated to Room and existing data is deleted. + kiwixRoomDatabase.languageRoomDao().insert(listOf(language)) + box.put(LanguageEntity(language1)) + objectBoxToRoomMigrator.migrateLanguages(box) + + actualDataAfterMigration = kiwixRoomDatabase.languageRoomDao().languages().first() + assertEquals(1, actualDataAfterMigration.size) + val existingItem = + actualDataAfterMigration.find { + it.active == language.active && it.language == language.language + } + Assert.assertNull(existingItem) + val newItem = + actualDataAfterMigration.find { + it.active == language1.active && it.language == language1.language + } + assertNotNull(newItem) + clearRoomAndBoxStoreDatabases(box) + } } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/di/components/KiwixComponent.kt b/app/src/main/java/org/kiwix/kiwixmobile/di/components/KiwixComponent.kt index 1178a6d1d..01587bde3 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/di/components/KiwixComponent.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/di/components/KiwixComponent.kt @@ -22,17 +22,20 @@ import dagger.Component import org.kiwix.kiwixmobile.core.di.components.CoreComponent import org.kiwix.kiwixmobile.di.KiwixScope import org.kiwix.kiwixmobile.di.components.ServiceComponent.Builder +import org.kiwix.kiwixmobile.di.modules.JNIModule import org.kiwix.kiwixmobile.di.modules.KiwixModule import org.kiwix.kiwixmobile.di.modules.KiwixViewModelModule import org.kiwix.kiwixmobile.storage.StorageSelectDialog +import org.kiwix.kiwixmobile.zimManager.OnlineLibraryManager @KiwixScope @Component( dependencies = [CoreComponent::class], - modules = [KiwixViewModelModule::class, KiwixModule::class] + modules = [KiwixViewModelModule::class, KiwixModule::class, JNIModule::class] ) interface KiwixComponent { fun activityComponentBuilder(): KiwixActivityComponent.Builder fun serviceComponent(): Builder fun inject(storageSelectDialog: StorageSelectDialog) + fun providesOnlineLibraryManager(): OnlineLibraryManager } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/di/modules/JNIModule.kt b/app/src/main/java/org/kiwix/kiwixmobile/di/modules/JNIModule.kt new file mode 100644 index 000000000..6785167cd --- /dev/null +++ b/app/src/main/java/org/kiwix/kiwixmobile/di/modules/JNIModule.kt @@ -0,0 +1,52 @@ +/* + * Kiwix Android + * Copyright (c) 2025 Kiwix + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package org.kiwix.kiwixmobile.di.modules + +import dagger.Module +import dagger.Provides +import org.kiwix.kiwixmobile.di.KiwixScope +import org.kiwix.kiwixmobile.zimManager.OnlineLibraryManager +import org.kiwix.libkiwix.Library +import org.kiwix.libkiwix.Manager +import javax.inject.Named + +@Module +class JNIModule { + @Provides + @Named(ONLINE_BOOKS_LIBRARY) + @KiwixScope + fun provideOnlineBooksLibrary(): Library = Library() + + @Provides + @Named(ONLINE_BOOKS_MANAGER) + @KiwixScope + fun providesOnlineBooksManager( + @Named(ONLINE_BOOKS_LIBRARY) library: Library + ): Manager = Manager(library) + + @Provides + @KiwixScope + fun provideOnlineLibraryManager( + @Named(ONLINE_BOOKS_LIBRARY) library: Library, + @Named(ONLINE_BOOKS_MANAGER) manager: Manager, + ) = OnlineLibraryManager(library, manager) +} + +const val ONLINE_BOOKS_LIBRARY = "onlineBookLibrary" +const val ONLINE_BOOKS_MANAGER = "onlineBookManager" diff --git a/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModel.kt index 434dcb871..45d4e0570 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModel.kt @@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import org.kiwix.kiwixmobile.core.base.SideEffect -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.language.composables.LanguageListItem.LanguageItem import org.kiwix.kiwixmobile.language.viewmodel.Action.Filter import org.kiwix.kiwixmobile.language.viewmodel.Action.SaveAll @@ -41,7 +41,7 @@ import org.kiwix.kiwixmobile.language.viewmodel.State.Saving import javax.inject.Inject class LanguageViewModel @Inject constructor( - private val languageDao: NewLanguagesDao + private val languageRoomDao: LanguageRoomDao ) : ViewModel() { val state = MutableStateFlow(Loading) val actions = MutableSharedFlow(extraBufferCapacity = Int.MAX_VALUE) @@ -63,7 +63,7 @@ class LanguageViewModel @Inject constructor( .launchIn(viewModelScope) private fun observeLanguages() = - languageDao.languages() + languageRoomDao.languages() .filter { it.isNotEmpty() } .onEach { languages -> actions.tryEmit(UpdateLanguages(languages)) } .launchIn(viewModelScope) @@ -112,7 +112,7 @@ class LanguageViewModel @Inject constructor( effects.tryEmit( SaveLanguagesAndFinish( currentState.items, - languageDao, + languageRoomDao, viewModelScope ) ) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinish.kt b/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinish.kt index 9b5bc48a6..c262cbe29 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinish.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinish.kt @@ -23,20 +23,20 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.kiwix.kiwixmobile.core.base.SideEffect -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.zim_manager.Language @Suppress("InjectDispatcher") data class SaveLanguagesAndFinish( private val languages: List, - private val languageDao: NewLanguagesDao, + private val languageRoomDao: LanguageRoomDao, private val lifecycleScope: CoroutineScope ) : SideEffect { override fun invokeWith(activity: AppCompatActivity) { lifecycleScope.launch { runCatching { withContext(Dispatchers.IO) { - languageDao.insert(languages) + languageRoomDao.insert(languages) } activity.onBackPressedDispatcher.onBackPressed() }.onFailure { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/OnlineLibraryManager.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/OnlineLibraryManager.kt index 241d494a8..247491af5 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/OnlineLibraryManager.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/OnlineLibraryManager.kt @@ -19,10 +19,17 @@ package org.kiwix.kiwixmobile.zimManager import org.kiwix.kiwixmobile.core.entity.LibkiwixBook +import org.kiwix.kiwixmobile.di.modules.ONLINE_BOOKS_LIBRARY +import org.kiwix.kiwixmobile.di.modules.ONLINE_BOOKS_MANAGER import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager +import javax.inject.Inject +import javax.inject.Named -class OnlineLibraryManager(private val library: Library, private val manager: Manager) { +class OnlineLibraryManager @Inject constructor( + @Named(ONLINE_BOOKS_LIBRARY) private val library: Library, + @Named(ONLINE_BOOKS_MANAGER) private val manager: Manager, +) { suspend fun parseOPDSStream(content: String?, urlHost: String): Boolean = runCatching { manager.readOpds(content, urlHost) @@ -40,4 +47,12 @@ class OnlineLibraryManager(private val library: Library, private val manager: Ma }.onFailure { it.printStackTrace() } return onlineBooksList } + + suspend fun getOnlineBooksLanguage(): List { + return runCatching { + library.booksLanguages.distinct() + }.onFailure { + it.printStackTrace() + }.getOrDefault(emptyList()) + } } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt index 66e777f37..2b6c20635 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModel.kt @@ -66,8 +66,8 @@ import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.remote.KiwixService import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_NETWORK_PATH @@ -89,6 +89,7 @@ import org.kiwix.kiwixmobile.core.ui.components.ONE import org.kiwix.kiwixmobile.core.ui.components.TWO import org.kiwix.kiwixmobile.core.utils.BookUtils import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.kiwixmobile.core.utils.TAG_KIWIX import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower import org.kiwix.kiwixmobile.core.utils.files.Log import org.kiwix.kiwixmobile.core.utils.files.ScanningProgressListener @@ -121,8 +122,6 @@ import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.Book import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem import org.kiwix.libkiwix.Book -import org.kiwix.libkiwix.Library -import org.kiwix.libkiwix.Manager import java.util.Locale import java.util.concurrent.TimeUnit.SECONDS import javax.inject.Inject @@ -137,7 +136,7 @@ const val FOUR = 4 class ZimManageViewModel @Inject constructor( private val downloadDao: DownloadRoomDao, private val libkiwixBookOnDisk: LibkiwixBookOnDisk, - private val languageDao: NewLanguagesDao, + private val languageRoomDao: LanguageRoomDao, private val storageObserver: StorageObserver, private var kiwixService: KiwixService, val context: Application, @@ -148,6 +147,7 @@ class ZimManageViewModel @Inject constructor( private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil, + private val onlineLibraryManager: OnlineLibraryManager ) : ViewModel() { sealed class FileSelectActions { data class RequestNavigateTo(val bookOnDisk: BookOnDisk) : FileSelectActions() @@ -160,12 +160,6 @@ class ZimManageViewModel @Inject constructor( object UserClickedDownloadBooksButton : FileSelectActions() } - private val library by lazy { Library() } - private val manager by lazy { Manager(library) } - - private val onlineLibraryManager by lazy { - OnlineLibraryManager(library, manager) - } private var isUnitTestCase: Boolean = false val sideEffects: MutableSharedFlow> = MutableSharedFlow() private val _libraryItems = MutableStateFlow>(emptyList()) @@ -294,7 +288,7 @@ class ZimManageViewModel @Inject constructor( private fun observeCoroutineFlows(dispatcher: CoroutineDispatcher = Dispatchers.IO) { val downloads = downloadDao.downloads() val booksFromDao = books() - val languages = languageDao.languages() + val languages = languageRoomDao.languages() coroutineJobs.apply { add(scanBooksFromStorage(dispatcher)) add(updateBookItems()) @@ -541,11 +535,11 @@ class ZimManageViewModel @Inject constructor( .filter { it.isNotEmpty() } .distinctUntilChanged() .catch { it.printStackTrace() } - .onEach { languageDao.insert(it) } + .onEach { languageRoomDao.insert(it) } .flowOn(dispatcher) .launchIn(viewModelScope) - private fun combineToLanguageList( + private suspend fun combineToLanguageList( booksFromNetwork: List, allLanguages: List ) = when { @@ -579,19 +573,28 @@ class ZimManageViewModel @Inject constructor( private fun MutableMap.increment(key: K) = apply { set(key, getOrElse(key) { 0 } + 1) } - private fun fromLocalesWithNetworkMatchesSetActiveBy( + private suspend fun fromLocalesWithNetworkMatchesSetActiveBy( networkLanguageCounts: MutableMap, listToActivateBy: List - ) = Locale.getISOLanguages() - .map { it.convertToLocal() } - .filter { networkLanguageCounts.containsKey(it.isO3Language) } - .map { locale -> - Language( - locale.isO3Language, - languageIsActive(listToActivateBy, locale), - networkLanguageCounts.getOrElse(locale.isO3Language) { 0 } - ) + ) = onlineLibraryManager.getOnlineBooksLanguage() + .mapNotNull { code -> + runCatching { + val locale = code.convertToLocal() + val o3 = locale.isO3Language + if (networkLanguageCounts.containsKey(o3)) { + Language( + o3, + languageIsActive(listToActivateBy, locale), + networkLanguageCounts.getOrElse(o3) { 0 } + ) + } else { + null + } + }.onFailure { + Log.w(TAG_KIWIX, "Unsupported locale code: $code", it) + }.getOrNull() } + .distinctBy { it.language } private fun defaultLanguage() = listOf( diff --git a/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModelTest.kt b/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModelTest.kt index aa374e60e..aa13e4f60 100644 --- a/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModelTest.kt +++ b/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/LanguageViewModelTest.kt @@ -28,7 +28,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.language.composables.LanguageListItem import org.kiwix.kiwixmobile.language.viewmodel.Action.Filter @@ -46,7 +46,7 @@ fun languageItem(language: Language = language()) = @ExtendWith(InstantExecutorExtension::class) class LanguageViewModelTest { - private val newLanguagesDao: NewLanguagesDao = mockk() + private val languageRoomDao: LanguageRoomDao = mockk() private lateinit var languageViewModel: LanguageViewModel private lateinit var languages: MutableStateFlow> @@ -54,9 +54,9 @@ class LanguageViewModelTest { fun init() { clearAllMocks() languages = MutableStateFlow(emptyList()) - every { newLanguagesDao.languages() } returns languages + every { languageRoomDao.languages() } returns languages languageViewModel = - LanguageViewModel(newLanguagesDao) + LanguageViewModel(languageRoomDao) } @Test @@ -183,7 +183,7 @@ class LanguageViewModelTest { assertThat(awaitItem()).isEqualTo( SaveLanguagesAndFinish( languages, - newLanguagesDao, + languageRoomDao, languageViewModel.viewModelScope ) ) diff --git a/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinishTest.kt b/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinishTest.kt index 1681780a8..3f7384216 100644 --- a/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinishTest.kt +++ b/app/src/test/java/org/kiwix/kiwixmobile/language/viewmodel/SaveLanguagesAndFinishTest.kt @@ -27,13 +27,13 @@ import io.mockk.verify import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.Test -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.zim_manager.Language class SaveLanguagesAndFinishTest { @Test fun `invoke saves and finishes`() = runTest { - val languageDao = mockk() + val languageDao = mockk() val activity = mockk() val lifeCycleScope = TestScope(testScheduler) val onBackPressedDispatcher = mockk() diff --git a/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt b/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt index deb7cc3fd..a65b0230b 100644 --- a/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt +++ b/app/src/test/java/org/kiwix/kiwixmobile/zimManager/ZimManageViewModelTest.kt @@ -28,6 +28,7 @@ import app.cash.turbine.TurbineTestContext import app.cash.turbine.test import com.jraska.livedata.test import io.mockk.clearAllMocks +import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.mockk @@ -53,8 +54,8 @@ import org.junit.jupiter.api.extension.ExtendWith import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.remote.KiwixService import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel @@ -103,7 +104,7 @@ import kotlin.time.toDuration class ZimManageViewModelTest { private val downloadRoomDao: DownloadRoomDao = mockk() private val libkiwixBookOnDisk: LibkiwixBookOnDisk = mockk() - private val newLanguagesDao: NewLanguagesDao = mockk() + private val languageRoomDao: LanguageRoomDao = mockk() private val storageObserver: StorageObserver = mockk() private val kiwixService: KiwixService = mockk() private val application: Application = mockk() @@ -129,6 +130,7 @@ class ZimManageViewModelTest { private val networkStates = MutableStateFlow(NetworkState.NOT_CONNECTED) private val booksOnDiskListItems = MutableStateFlow>(emptyList()) private val testDispatcher = StandardTestDispatcher() + private val onlineLibraryManager = mockk() @AfterAll fun teardown() { @@ -150,7 +152,7 @@ class ZimManageViewModelTest { any() ) } returns booksOnFileSystem - every { newLanguagesDao.languages() } returns languages + every { languageRoomDao.languages() } returns languages every { fat32Checker.fileSystemStates } returns fileSystemStates every { connectivityBroadcastReceiver.networkStates } returns networkStates if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -176,7 +178,7 @@ class ZimManageViewModelTest { ZimManageViewModel( downloadRoomDao, libkiwixBookOnDisk, - newLanguagesDao, + languageRoomDao, storageObserver, kiwixService, application, @@ -186,7 +188,8 @@ class ZimManageViewModelTest { defaultLanguageProvider, dataSource, connectivityManager, - sharedPreferenceUtil + sharedPreferenceUtil, + onlineLibraryManager ).apply { setIsUnitTestCase() setAlertDialogShower(alertDialogShower) @@ -279,7 +282,7 @@ class ZimManageViewModelTest { expectedLanguage ) advanceUntilIdle() - verify { newLanguagesDao.insert(listOf(expectedLanguage)) } + verify { languageRoomDao.insert(listOf(expectedLanguage)) } } @Test @@ -298,7 +301,7 @@ class ZimManageViewModelTest { ), language(isActive = true, occurencesOfLanguage = 1) ) - verify { newLanguagesDao.insert(any()) } + verify { languageRoomDao.insert(any()) } } @Test @@ -323,7 +326,7 @@ class ZimManageViewModelTest { defaultLanguage ) verify { - newLanguagesDao.insert( + languageRoomDao.insert( listOf( defaultLanguage.copy(occurencesOfLanguage = 2), Language( @@ -362,7 +365,7 @@ class ZimManageViewModelTest { ) advanceUntilIdle() verify(timeout = MOCKK_TIMEOUT_FOR_VERIFICATION) { - newLanguagesDao.insert( + languageRoomDao.insert( listOf( dbLanguage.copy(occurencesOfLanguage = 2), Language( @@ -386,6 +389,7 @@ class ZimManageViewModelTest { every { application.getString(any()) } returns "" every { application.getString(any(), any()) } returns "" every { defaultLanguageProvider.provide() } returns defaultLanguage + coEvery { onlineLibraryManager.getOnlineBooksLanguage() } returns networkBooks.map { it.language } viewModel.networkLibrary.emit(networkBooks) advanceUntilIdle() languages.value = dbBooks diff --git a/core/detekt_baseline.xml b/core/detekt_baseline.xml index d2e3edb2c..9d16a483a 100644 --- a/core/detekt_baseline.xml +++ b/core/detekt_baseline.xml @@ -12,7 +12,7 @@ LongParameterList:MainMenu.kt$MainMenu$( private val activity: Activity, zimFileReader: ZimFileReader?, menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, disableReadAloud: Boolean = false, disableTabs: Boolean = false, private val menuClickListener: MenuClickListener ) LongParameterList:MainMenu.kt$MainMenu.Factory$( menu: Menu, webViews: MutableList<KiwixWebView>, urlIsValid: Boolean, menuClickListener: MenuClickListener, disableReadAloud: Boolean, disableTabs: Boolean ) LongParameterList:PageTestHelpers.kt$( bookmarkTitle: String = "bookmarkTitle", isSelected: Boolean = false, id: Long = 2, zimId: String = "zimId", zimName: String = "zimName", zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" ) - LongParameterList:Repository.kt$Repository$( 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 ) + 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 languageRoomDao: LanguageRoomDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer ) LongParameterList:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$( context: Context, callback: WebViewCallback, attrs: AttributeSet, videoView: ViewGroup?, webViewClient: CoreWebViewClient, sharedPreferenceUtil: SharedPreferenceUtil ) MagicNumber:ArticleCount.kt$ArticleCount$3 MagicNumber:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$100 diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LanguageRoomDao.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LanguageRoomDao.kt new file mode 100644 index 000000000..9f22ec821 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LanguageRoomDao.kt @@ -0,0 +1,50 @@ +/* + * Kiwix Android + * Copyright (c) 2025 Kiwix + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package org.kiwix.kiwixmobile.core.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import org.kiwix.kiwixmobile.core.dao.entities.LanguageRoomEntity +import org.kiwix.kiwixmobile.core.zim_manager.Language + +@Dao +abstract class LanguageRoomDao { + @Query("SELECT * FROM LanguageRoomEntity") + abstract fun languageAsEntity(): Flow> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + abstract fun saveLanguages(languageRoomEntityList: List) + + @Query("DELETE FROM LanguageRoomEntity") + abstract fun deleteAllLanguages() + + fun languages(): Flow> = + languageAsEntity().map { it.map(LanguageRoomEntity::toLanguageModel) } + + @Transaction + open fun insert(languages: List) { + deleteAllLanguages() + saveLanguages(languages.map(::LanguageRoomEntity)) + } +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageRoomEntity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageRoomEntity.kt new file mode 100644 index 000000000..fb9fc33b6 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/entities/LanguageRoomEntity.kt @@ -0,0 +1,56 @@ +/* + * Kiwix Android + * Copyright (c) 2025 Kiwix + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package org.kiwix.kiwixmobile.core.dao.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.convertToLocal +import org.kiwix.kiwixmobile.core.zim_manager.Language +import java.util.Locale + +@Entity +data class LanguageRoomEntity( + @PrimaryKey(autoGenerate = true) var id: Long = 0, + @TypeConverters(StringToLocalRoomConverter::class) + var locale: Locale = Locale.ENGLISH, + var active: Boolean = false, + var occurencesOfLanguage: Int = 0 +) { + constructor(language: Language) : this( + 0, + language.languageCode.convertToLocal(), + language.active, + language.occurencesOfLanguage + ) + + fun toLanguageModel() = + Language(locale, active, occurencesOfLanguage, id) +} + +class StringToLocalRoomConverter { + @TypeConverter + fun convertToDatabaseValue(entityProperty: Locale?): String = + entityProperty?.isO3Language ?: Locale.ENGLISH.isO3Language + + @TypeConverter + fun convertToEntityProperty(databaseValue: String?): Locale = + databaseValue?.convertToLocal() ?: Locale.ENGLISH +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/KiwixRoomDatabase.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/KiwixRoomDatabase.kt index adb930319..6865ce354 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/KiwixRoomDatabase.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/KiwixRoomDatabase.kt @@ -28,14 +28,17 @@ import androidx.sqlite.db.SupportSQLiteDatabase import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao import org.kiwix.kiwixmobile.core.dao.HistoryRoomDaoCoverts +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.dao.NotesRoomDao import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao import org.kiwix.kiwixmobile.core.dao.entities.BundleRoomConverter import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity import org.kiwix.kiwixmobile.core.dao.entities.HistoryRoomEntity +import org.kiwix.kiwixmobile.core.dao.entities.LanguageRoomEntity import org.kiwix.kiwixmobile.core.dao.entities.NotesRoomEntity import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity +import org.kiwix.kiwixmobile.core.dao.entities.StringToLocalRoomConverter import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity import org.kiwix.kiwixmobile.core.dao.entities.ZimSourceRoomConverter @@ -46,15 +49,17 @@ import org.kiwix.kiwixmobile.core.dao.entities.ZimSourceRoomConverter HistoryRoomEntity::class, NotesRoomEntity::class, DownloadRoomEntity::class, - WebViewHistoryEntity::class + WebViewHistoryEntity::class, + LanguageRoomEntity::class ], - version = 8, + version = 9, exportSchema = false ) @TypeConverters( HistoryRoomDaoCoverts::class, ZimSourceRoomConverter::class, - BundleRoomConverter::class + BundleRoomConverter::class, + StringToLocalRoomConverter::class ) abstract class KiwixRoomDatabase : RoomDatabase() { abstract fun recentSearchRoomDao(): RecentSearchRoomDao @@ -62,6 +67,7 @@ abstract class KiwixRoomDatabase : RoomDatabase() { abstract fun notesRoomDao(): NotesRoomDao abstract fun downloadRoomDao(): DownloadRoomDao abstract fun webViewHistoryRoomDao(): WebViewHistoryRoomDao + abstract fun languageRoomDao(): LanguageRoomDao companion object { private var db: KiwixRoomDatabase? = null @@ -78,7 +84,8 @@ abstract class KiwixRoomDatabase : RoomDatabase() { MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, - MIGRATION_7_8 + MIGRATION_7_8, + MIGRATION_8_9 ) .build().also { db = it } } @@ -305,6 +312,23 @@ abstract class KiwixRoomDatabase : RoomDatabase() { } } + @Suppress("MagicNumber") + private val MIGRATION_8_9 = + object : Migration(8, 9) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `LanguageRoomEntity` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `locale` TEXT NOT NULL DEFAULT 'eng', + `active` INTEGER NOT NULL DEFAULT 0, + `occurencesOfLanguage` INTEGER NOT NULL DEFAULT 0 + ) + """.trimIndent() + ) + } + } + fun destroyInstance() { db = null } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt index 63d069de7..21662400b 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt @@ -24,9 +24,9 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks -import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NotesRoomDao import org.kiwix.kiwixmobile.core.dao.RecentSearchRoomDao import org.kiwix.kiwixmobile.core.dao.WebViewHistoryRoomDao @@ -56,7 +56,7 @@ class Repository @Inject internal constructor( private val historyRoomDao: HistoryRoomDao, private val webViewHistoryRoomDao: WebViewHistoryRoomDao, private val notesRoomDao: NotesRoomDao, - private val languageDao: NewLanguagesDao, + private val languageRoomDao: LanguageRoomDao, private val recentSearchRoomDao: RecentSearchRoomDao, private val zimReaderContainer: ZimReaderContainer ) : DataSource { @@ -102,7 +102,7 @@ class Repository @Inject internal constructor( @Suppress("InjectDispatcher") override suspend fun saveLanguages(languages: List) = withContext(Dispatchers.IO) { - languageDao.insert(languages) + languageRoomDao.insert(languages) } @Suppress("InjectDispatcher") diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToRoomMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToRoomMigrator.kt index 6b31f04b0..f6009f202 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToRoomMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToRoomMigrator.kt @@ -23,6 +23,7 @@ import io.objectbox.BoxStore import io.objectbox.kotlin.boxFor import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity +import org.kiwix.kiwixmobile.core.dao.entities.LanguageEntity import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchEntity import org.kiwix.kiwixmobile.core.data.KiwixRoomDatabase @@ -49,6 +50,9 @@ class ObjectBoxToRoomMigrator { if (!sharedPreferenceUtil.prefIsNotesMigrated) { migrateNotes(boxStore.boxFor()) } + if (!sharedPreferenceUtil.prefLanguageListMigrated) { + migrateLanguages(boxStore.boxFor()) + } // TODO we will migrate here for other entities } @@ -87,4 +91,12 @@ class ObjectBoxToRoomMigrator { } sharedPreferenceUtil.putPrefNotesMigrated(true) } + + suspend fun migrateLanguages(box: Box) { + kiwixRoomDatabase.languageRoomDao() + .insert( + box.all.map { it.toLanguageModel() } + ) + sharedPreferenceUtil.putPrefLanguageListMigrated(true) + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt index 4dee3f60d..30e5e5cfe 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/components/CoreComponent.kt @@ -30,6 +30,7 @@ import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao import org.kiwix.kiwixmobile.core.dao.HistoryDao import org.kiwix.kiwixmobile.core.dao.HistoryRoomDao +import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.NewBookDao @@ -107,6 +108,7 @@ interface CoreComponent { fun historyRoomDao(): HistoryRoomDao fun webViewHistoryRoomDao(): WebViewHistoryRoomDao fun noteRoomDao(): NotesRoomDao + fun languageRoomDao(): LanguageRoomDao fun objectBoxToRoomMigrator(): ObjectBoxToRoomMigrator fun context(): Context fun downloader(): Downloader diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt index 0a121bd96..e30a4f7d1 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt @@ -100,4 +100,8 @@ open class DatabaseModule { db.downloadRoomDao().also { it.libkiwixBookOnDisk = libkiwixBookOnDisk } + + @Singleton + @Provides + fun provideLanguageRoomDao(db: KiwixRoomDatabase) = db.languageRoomDao() } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt index 5d3f503c3..b7d4a2db7 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt @@ -117,6 +117,9 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { val prefIsBookOnDiskMigrated: Boolean get() = sharedPreferences.getBoolean(PREF_BOOK_ON_DISK_MIGRATED, false) + val prefLanguageListMigrated: Boolean + get() = sharedPreferences.getBoolean(PREF_LANGUAGE_LIST_MIGRATED, false) + val prefStorage: String get() { val storage = sharedPreferences.getString(PREF_STORAGE, null) @@ -169,6 +172,9 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { fun putPrefBookOnDiskMigrated(isMigrated: Boolean) = sharedPreferences.edit { putBoolean(PREF_BOOK_ON_DISK_MIGRATED, isMigrated) } + fun putPrefLanguageListMigrated(isMigrated: Boolean) = + sharedPreferences.edit { putBoolean(PREF_LANGUAGE_LIST_MIGRATED, isMigrated) } + fun putPrefLanguage(language: String) = sharedPreferences.edit { putString(PREF_LANG, language) } @@ -352,6 +358,7 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { const val PREF_NOTES_MIGRATED = "pref_notes_migrated" const val PREF_APP_DIRECTORY_TO_PUBLIC_MIGRATED = "pref_app_directory_to_public_migrated" const val PREF_BOOK_ON_DISK_MIGRATED = "pref_book_on_disk_migrated" + const val PREF_LANGUAGE_LIST_MIGRATED = "pref_language_list_migrated" const val PREF_SHOW_COPY_MOVE_STORAGE_SELECTION_DIALOG = "pref_show_copy_move_storage_dialog" private const val PREF_LATER_CLICKED_MILLIS = "pref_later_clicked_millis" const val PREF_LAST_DONATION_POPUP_SHOWN_IN_MILLISECONDS =