From 4fe6d11596172361a3cef98aa3c332cd3428e81b Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 25 Aug 2023 18:42:00 +0530 Subject: [PATCH 01/38] Created ObjectBox to libkiwix migrator for Bookmarks. --- .../remote/ObjectBoxToLibkiwixMigrator.kt | 61 +++++++++++++++++++ .../core/di/components/CoreComponent.kt | 3 + .../kiwixmobile/core/main/CoreMainActivity.kt | 8 +++ .../core/utils/SharedPreferenceUtil.kt | 7 +++ 4 files changed, 79 insertions(+) create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt new file mode 100644 index 0000000000..d3651b917b --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -0,0 +1,61 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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.data.remote + +import io.objectbox.Box +import io.objectbox.BoxStore +import io.objectbox.kotlin.boxFor +import io.objectbox.kotlin.query +import io.objectbox.query.QueryBuilder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.kiwix.kiwixmobile.core.CoreApp +import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity +import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import javax.inject.Inject + +class ObjectBoxToLibkiwixMigrator { + @Inject lateinit var boxStore: BoxStore + @Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil + + fun migrateBookmarksToLibkiwix() { + CoreApp.coreComponent.inject(this) + migrateBookMarks(boxStore.boxFor()) + // TODO we will migrate here for other entities + } + + fun migrateBookMarks(box: Box) { + val bookMarksList = box.all + bookMarksList.forEachIndexed { _, bookmarkEntity -> + CoroutineScope(Dispatchers.IO).launch { + // removing the single entity from the object box after migration. + box.query { + equal( + BookmarkEntity_.bookmarkUrl, + bookmarkEntity.bookmarkUrl, + QueryBuilder.StringOrder.CASE_INSENSITIVE + ) + }.remove() + } + } + sharedPreferenceUtil.putPrefBookMarkMigrated(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 c207a7d8d1..a7077be32f 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 @@ -37,6 +37,7 @@ import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao import org.kiwix.kiwixmobile.core.data.DataModule import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.data.remote.KiwixService +import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator import org.kiwix.kiwixmobile.core.di.modules.ApplicationModule import org.kiwix.kiwixmobile.core.di.modules.CoreViewModelModule import org.kiwix.kiwixmobile.core.di.modules.JNIModule @@ -93,6 +94,7 @@ interface CoreComponent { fun newBookmarksDao(): NewBookmarksDao fun connectivityManager(): ConnectivityManager fun wifiManager(): WifiManager + fun objectBoxToLibkiwixMigrator(): ObjectBoxToLibkiwixMigrator fun context(): Context fun downloader(): Downloader fun notificationManager(): NotificationManager @@ -104,6 +106,7 @@ interface CoreComponent { fun inject(errorActivity: ErrorActivity) fun inject(searchFragment: SearchFragment) + fun inject(objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator) fun inject(settingsFragment: CoreSettingsFragment) fun coreServiceComponent(): CoreServiceComponent.Builder diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt index ad4f9f1b24..a937bff6bf 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt @@ -43,6 +43,7 @@ import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions +import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator import org.kiwix.kiwixmobile.core.di.components.CoreActivityComponent import org.kiwix.kiwixmobile.core.error.ErrorActivity import org.kiwix.kiwixmobile.core.extensions.browserIntent @@ -85,6 +86,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { abstract val topLevelDestinations: Set abstract val navHostContainer: FragmentContainerView abstract val mainActivity: AppCompatActivity + @Inject lateinit var objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator override fun onCreate(savedInstanceState: Bundle?) { setTheme(R.style.KiwixTheme) @@ -104,7 +106,13 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { exitProcess(KIWIX_INTERNAL_ERROR) } } +<<<<<<< HEAD setMainActivityToCoreApp() +======= + if (!sharedPreferenceUtil.prefIsBookmarksMigrated) { + objectBoxToLibkiwixMigrator.migrateBookmarksToLibkiwix() + } +>>>>>>> 371eb5c2d (Created ObjectBox to libkiwix migrator for Bookmarks) } @Suppress("DEPRECATION") 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 2122336890..9089be233f 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 @@ -90,6 +90,9 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { val prefDeviceDefaultLanguage: String get() = sharedPreferences.getString(PREF_DEVICE_DEFAULT_LANG, "") ?: "" + val prefIsBookmarksMigrated: Boolean + get() = sharedPreferences.getBoolean(PREF_BOOKMARKS_MIGRATED, false) + val prefStorage: String get() { val storage = sharedPreferences.getString(PREF_STORAGE, null) @@ -115,6 +118,9 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { fun getPrefStorageTitle(defaultTitle: String): String = sharedPreferences.getString(PREF_STORAGE_TITLE, defaultTitle) ?: defaultTitle + fun putPrefBookMarkMigrated(isMigrated: Boolean) = + sharedPreferences.edit { putBoolean(PREF_BOOKMARKS_MIGRATED, isMigrated) } + fun putPrefLanguage(language: String) = sharedPreferences.edit { putString(PREF_LANG, language) } @@ -273,5 +279,6 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { const val PREF_SHOW_MANAGE_PERMISSION_DIALOG_ON_REFRESH = "pref_show_manage_external_files" const val IS_PLAY_STORE_BUILD = "is_play_store_build" const val PREF_PLAY_STORE_RESTRICTION = "pref_play_store_restriction" + const val PREF_BOOKMARKS_MIGRATED = "pref_bookmarks_migrated" } } From da3241bea0d86dadb0dc0c69b2220a56bd20af3e Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 29 Aug 2023 11:33:11 +0530 Subject: [PATCH 02/38] Created LibkiwixBooks to store and retrieve bookmarks --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 38 +++++++++++++++++++ .../kiwix/kiwixmobile/core/data/Repository.kt | 3 +- .../core/di/components/CoreComponent.kt | 2 + .../kiwixmobile/core/di/modules/JNIModule.kt | 5 +++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt new file mode 100644 index 0000000000..cc4a684a50 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -0,0 +1,38 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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 io.objectbox.kotlin.query +import io.reactivex.Flowable +import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ +import org.kiwix.kiwixmobile.core.page.adapter.Page +import org.kiwix.libkiwix.Manager +import javax.inject.Inject + +class LibkiwixBookmarks @Inject constructor(val manager: Manager) : PageDao { + fun bookmarks(): Flowable> = box.asFlowable( + box.query { + order(BookmarkEntity_.bookmarkTitle) + } + ).map { it.map(org.kiwix.kiwixmobile.core.page.bookmark.adapter::BookmarkItem) } + override fun pages(): Flowable> = bookmarks() + + override fun deletePages(pagesToDelete: List) { + } +} 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 2aaf489cb1..6a93709e89 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 @@ -23,6 +23,7 @@ import io.reactivex.Flowable import io.reactivex.Scheduler import io.reactivex.Single import org.kiwix.kiwixmobile.core.dao.HistoryDao +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao @@ -53,7 +54,7 @@ class Repository @Inject internal constructor( @param:IO private val io: Scheduler, @param:MainThread private val mainThread: Scheduler, private val bookDao: NewBookDao, - private val bookmarksDao: NewBookmarksDao, + private val libkiwixBookmarks: LibkiwixBookmarks, private val historyDao: HistoryDao, private val notesDao: NewNoteDao, private val languageDao: NewLanguagesDao, 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 a7077be32f..8b550a9caf 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 @@ -29,6 +29,7 @@ import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao import org.kiwix.kiwixmobile.core.dao.HistoryDao +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.NewBookDao import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao @@ -95,6 +96,7 @@ interface CoreComponent { fun connectivityManager(): ConnectivityManager fun wifiManager(): WifiManager fun objectBoxToLibkiwixMigrator(): ObjectBoxToLibkiwixMigrator + fun libkiwixBookmarks(): LibkiwixBookmarks fun context(): Context fun downloader(): Downloader fun notificationManager(): NotificationManager diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt index ba1001d12f..4bc313db1b 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt @@ -20,11 +20,16 @@ package org.kiwix.kiwixmobile.core.di.modules import android.content.Context import dagger.Module import dagger.Provides +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.libkiwix.JNIKiwix +import org.kiwix.libkiwix.Manager import javax.inject.Singleton @Module class JNIModule { @Provides @Singleton fun providesJNIKiwix(context: Context): JNIKiwix = JNIKiwix(context) + + @Provides @Singleton + fun providesLibkiwixBookmarks(manager: Manager): LibkiwixBookmarks = LibkiwixBookmarks(manager) } From 98556246e5b10ed8761ef3ea81f285a86417d2af Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 30 Aug 2023 18:32:53 +0530 Subject: [PATCH 03/38] Storing Bookmarks via libkiwix instead of Objectbox --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 37 ++++++++++++- .../kiwix/kiwixmobile/core/data/DataSource.kt | 3 +- .../kiwix/kiwixmobile/core/data/Repository.kt | 14 ++--- .../kiwixmobile/core/di/modules/JNIModule.kt | 19 ++++++- .../bookmark/adapter/LibkiwixBookarkItem.kt | 53 +++++++++++++++++++ .../core/utils/SharedPreferenceUtil.kt | 2 +- 6 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index cc4a684a50..0dd19a6247 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -21,18 +21,51 @@ package org.kiwix.kiwixmobile.core.dao import io.objectbox.kotlin.query import io.reactivex.Flowable import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ +import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.libkiwix.Bookmark +import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager +import java.io.File import javax.inject.Inject -class LibkiwixBookmarks @Inject constructor(val manager: Manager) : PageDao { - fun bookmarks(): Flowable> = box.asFlowable( +class LibkiwixBookmarks @Inject constructor( + val library: Library, + val manager: Manager, + val sharedPreferenceUtil: SharedPreferenceUtil +) : PageDao { + + private val bookmarksPath: String by lazy { + sharedPreferenceUtil.getPublicDirectoryPath(sharedPreferenceUtil.defaultStorage()) + "/kiwix/Bookmarks.txt" + } + + private val bookMarksFile: File by lazy { File(bookmarksPath) } + + init { + if (!File(bookmarksPath).isFileExist()) File(bookmarksPath).createNewFile() + } + + fun bookmarks(): Flowable> { + manager.readBookmarkFile(bookmarksPath) + val bookMarksArray: Array? = library.getBookmarks(true) + return bookMarksArray?.let { + it.map(::BookmarkItem) + } ?: emptyList() + } + box.asFlowable( box.query { order(BookmarkEntity_.bookmarkTitle) } ).map { it.map(org.kiwix.kiwixmobile.core.page.bookmark.adapter::BookmarkItem) } + override fun pages(): Flowable> = bookmarks() override fun deletePages(pagesToDelete: List) { } + + fun saveBookmark(bookmark: Bookmark) { + + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt index e9d4b24e83..aa0694e0d9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt @@ -27,6 +27,7 @@ import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk +import org.kiwix.libkiwix.Bookmark /** * Defines the set of methods which are required to provide the presenter with the requisite data. @@ -43,7 +44,7 @@ interface DataSource { fun getBookmarks(): Flowable> fun getCurrentZimBookmarksUrl(): Single> - fun saveBookmark(bookmark: BookmarkItem): Completable + fun saveBookmark(bookmark: Bookmark): Completable fun deleteBookmarks(bookmarks: List): Completable fun deleteBookmark(bookmarkUrl: String): Completable? fun booksOnDiskAsListItems(): Flowable> diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/Repository.kt index 6a93709e89..b678816582 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 @@ -25,7 +25,6 @@ import io.reactivex.Single import org.kiwix.kiwixmobile.core.dao.HistoryDao import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.NewBookDao -import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao import org.kiwix.kiwixmobile.core.dao.NewNoteDao import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao @@ -41,6 +40,7 @@ import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem +import org.kiwix.libkiwix.Bookmark import javax.inject.Inject import javax.inject.Singleton @@ -105,23 +105,23 @@ class Repository @Inject internal constructor( recentSearchDao.deleteSearchHistory() } - override fun getBookmarks() = bookmarksDao.bookmarks() as Flowable> + override fun getBookmarks() = libkiwixBookmarks.bookmarks() as Flowable> override fun getCurrentZimBookmarksUrl() = - Single.just(bookmarksDao.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader)) + Single.just(libkiwixBookmarks.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader)) .subscribeOn(io) .observeOn(mainThread) - override fun saveBookmark(bookmark: BookmarkItem) = - Completable.fromAction { bookmarksDao.saveBookmark(bookmark) } + override fun saveBookmark(bookmark: Bookmark) = + Completable.fromAction { libkiwixBookmarks.saveBookmark(bookmark) } .subscribeOn(io) override fun deleteBookmarks(bookmarks: List) = - Completable.fromAction { bookmarksDao.deleteBookmarks(bookmarks) } + Completable.fromAction { libkiwixBookmarks.deleteBookmarks(bookmarks) } .subscribeOn(io) override fun deleteBookmark(bookmarkUrl: String): Completable? = - Completable.fromAction { bookmarksDao.deleteBookmark(bookmarkUrl) } + Completable.fromAction { libkiwixBookmarks.deleteBookmark(bookmarkUrl) } .subscribeOn(io) override fun saveNote(noteListItem: NoteListItem): Completable = diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt index 4bc313db1b..f4b1a7ebeb 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/JNIModule.kt @@ -21,7 +21,9 @@ import android.content.Context import dagger.Module import dagger.Provides import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.libkiwix.JNIKiwix +import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager import javax.inject.Singleton @@ -30,6 +32,19 @@ class JNIModule { @Provides @Singleton fun providesJNIKiwix(context: Context): JNIKiwix = JNIKiwix(context) - @Provides @Singleton - fun providesLibkiwixBookmarks(manager: Manager): LibkiwixBookmarks = LibkiwixBookmarks(manager) + @Provides + @Singleton + fun provideLibrary(): Library = Library() + + @Provides + @Singleton + fun providesManager(library: Library): Manager = Manager(library) + + @Provides + @Singleton + fun providesLibkiwixBookmarks( + library: Library, + manager: Manager, + sharedPreferenceUtil: SharedPreferenceUtil + ): LibkiwixBookmarks = LibkiwixBookmarks(library, manager, sharedPreferenceUtil) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt new file mode 100644 index 0000000000..5eebd309e3 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt @@ -0,0 +1,53 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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.page.bookmark.adapter + +import org.kiwix.kiwixmobile.core.page.adapter.Page +import org.kiwix.kiwixmobile.core.reader.ZimFileReader +import org.kiwix.libkiwix.Bookmark + +data class LibkiwixBookarkItem( + override val id: Long = 0L, + override val zimFilePath: String?, + override val zimId: String, + val zimName: String, + val bookMarkUrl: String, + override val url: String = bookMarkUrl, + override val title: String, + override var isSelected: Boolean = false, + override val favicon: String? +) : Page { + constructor(libkiwixBookmark: Bookmark) : this( + zimId = libkiwixBookmark.bookId, + libkiwixBookmark.url + ) + + constructor( + title: String, + url: String, + zimFileReader: ZimFileReader + ) : this( + zimId = zimFileReader.id, + zimName = zimFileReader.name, + zimFilePath = zimFileReader.zimFile.canonicalPath, + bookmarkUrl = url, + title = title, + favicon = zimFileReader.favicon + ) +} 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 9089be233f..88d0dab07c 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 @@ -111,7 +111,7 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { val storagePosition: Int get() = sharedPreferences.getInt(STORAGE_POSITION, 0) - private fun defaultStorage(): String = + fun defaultStorage(): String = getExternalFilesDirs(context, null)[0]?.path ?: context.filesDir.path // a workaround for emulators From 45531afc806a77e8c6fe8fce68fbb3dcdabaaa4b Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 1 Sep 2023 19:12:45 +0530 Subject: [PATCH 04/38] Created saved deleted feature and implemented the functionality, created flowables to perform bookmarks operation on background thread for better user experience. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 76 +++++++++++++++---- .../kiwixmobile/core/dao/NewLanguagesDao.kt | 18 +++++ .../kiwix/kiwixmobile/core/data/DataSource.kt | 8 +- .../kiwix/kiwixmobile/core/data/Repository.kt | 12 +-- .../core/main/CoreReaderFragment.kt | 24 +++--- .../core/main/MainRepositoryActions.kt | 8 +- ...BookarkItem.kt => LibkiwixBookmarkItem.kt} | 42 +++++----- .../kiwixmobile/core/reader/ZimFileReader.kt | 2 +- 8 files changed, 134 insertions(+), 56 deletions(-) rename core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/{LibkiwixBookarkItem.kt => LibkiwixBookmarkItem.kt} (59%) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 0dd19a6247..b8864bb9f5 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -19,12 +19,16 @@ package org.kiwix.kiwixmobile.core.dao import io.objectbox.kotlin.query +import io.objectbox.query.QueryBuilder import io.reactivex.Flowable import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem +import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.Bookmark import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager @@ -37,35 +41,77 @@ class LibkiwixBookmarks @Inject constructor( val sharedPreferenceUtil: SharedPreferenceUtil ) : PageDao { - private val bookmarksPath: String by lazy { - sharedPreferenceUtil.getPublicDirectoryPath(sharedPreferenceUtil.defaultStorage()) + "/kiwix/Bookmarks.txt" + private val bookmarksFolderPath: String by lazy { + sharedPreferenceUtil.getPublicDirectoryPath(sharedPreferenceUtil.defaultStorage()) + "/kiwix/Bookmarks/" } - private val bookMarksFile: File by lazy { File(bookmarksPath) } + private val bookMarksFile: File by lazy { File(bookmarksFolderPath) } init { - if (!File(bookmarksPath).isFileExist()) File(bookmarksPath).createNewFile() + if (!File(bookmarksFolderPath).isFileExist()) File(bookmarksFolderPath).mkdir() + manager.readBookmarkFile(bookmarksFolderPath) } fun bookmarks(): Flowable> { - manager.readBookmarkFile(bookmarksPath) val bookMarksArray: Array? = library.getBookmarks(true) - return bookMarksArray?.let { - it.map(::BookmarkItem) - } ?: emptyList() - } - box.asFlowable( - box.query { - order(BookmarkEntity_.bookmarkTitle) + return bookMarksArray?.let { + it.map(::BookmarkItem) } - ).map { it.map(org.kiwix.kiwixmobile.core.page.bookmark.adapter::BookmarkItem) } + } override fun pages(): Flowable> = bookmarks() - override fun deletePages(pagesToDelete: List) { + override fun deletePages(pagesToDelete: List) = + deleteBookmarks(pagesToDelete as List) + + fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?) = box.query { + equal( + BookmarkEntity_.zimId, zimFileReader?.id ?: "", + QueryBuilder.StringOrder.CASE_INSENSITIVE + ) + .or() + .equal( + BookmarkEntity_.zimName, zimFileReader?.name ?: "", + QueryBuilder.StringOrder.CASE_INSENSITIVE + ) + order(BookmarkEntity_.bookmarkTitle) + }.property(BookmarkEntity_.bookmarkUrl) + .findStrings() + .toList() + .distinct() + + fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader): Flowable> { + val book = Book().apply { + update(zimFileReader.jniKiwixReader) + } + library.addBook(book) + val bookMarksList: Flowable> = arrayListOf() + library.getBookmarks(true) + .map { + } + return bookMarksList + } + + fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { + library.addBook(libkiwixBookmarkItem.libKiwixBook) + val bookmark = Bookmark().apply { + bookId = libkiwixBookmarkItem.zimId + title = libkiwixBookmarkItem.title + url = libkiwixBookmarkItem.url + bookTitle = libkiwixBookmarkItem.libKiwixBook?.title ?: libkiwixBookmarkItem.zimId + } + library.addBookmark(bookmark).also { + // if the book name is not found then takes zim id as file name + val bookMarkFileName = libkiwixBookmarkItem.libKiwixBook?.name ?: libkiwixBookmarkItem.id + library.writeBookmarksToFile("$bookmarksFolderPath/$bookMarkFileName") + } } - fun saveBookmark(bookmark: Bookmark) { + fun deleteBookmarks(bookmarks: List) { + bookmarks.map { deleteBookmark(it.zimId, it.bookmarkUrl) } + } + fun deleteBookmark(bookId: String, bookmarkUrl: String) { + library.removeBookmark(bookId, bookmarkUrl) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt index 0ea78ceecb..5e029226be 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt @@ -23,8 +23,10 @@ import io.objectbox.query.Query import io.objectbox.rx.RxQuery import io.reactivex.BackpressureStrategy import io.reactivex.BackpressureStrategy.LATEST +import io.reactivex.Flowable import org.kiwix.kiwixmobile.core.dao.entities.LanguageEntity import org.kiwix.kiwixmobile.core.zim_manager.Language +import org.kiwix.libkiwix.Library import javax.inject.Inject import javax.inject.Singleton @@ -46,3 +48,19 @@ internal fun Box.asFlowable( backpressureStrategy: BackpressureStrategy = LATEST ) = RxQuery.observable(query).toFlowable(backpressureStrategy) + +fun Library.asFlowable( + queryFunction: () -> List, + backpressureStrategy: BackpressureStrategy = BackpressureStrategy.LATEST +): Flowable> { + return Flowable.create({ emitter -> + val subscription = this.subscribe { + val results = queryFunction() + emitter.onNext(results) + } + + emitter.setCancellable { + subscription.dispose() + } + }, backpressureStrategy) +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt index aa0694e0d9..0d19c3fe45 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt @@ -21,13 +21,13 @@ import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Single import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk -import org.kiwix.libkiwix.Bookmark /** * Defines the set of methods which are required to provide the presenter with the requisite data. @@ -44,9 +44,9 @@ interface DataSource { fun getBookmarks(): Flowable> fun getCurrentZimBookmarksUrl(): Single> - fun saveBookmark(bookmark: Bookmark): Completable - fun deleteBookmarks(bookmarks: List): Completable - fun deleteBookmark(bookmarkUrl: String): Completable? + fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem): Completable + fun deleteBookmarks(bookmarks: List): Completable + fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable? fun booksOnDiskAsListItems(): Flowable> fun saveNote(noteListItem: NoteListItem): Completable 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 b678816582..3688eeb13e 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 @@ -32,6 +32,7 @@ import org.kiwix.kiwixmobile.core.di.qualifiers.IO import org.kiwix.kiwixmobile.core.di.qualifiers.MainThread import org.kiwix.kiwixmobile.core.extensions.HeaderizableList import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem @@ -40,7 +41,6 @@ import org.kiwix.kiwixmobile.core.zim_manager.Language import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.LanguageItem -import org.kiwix.libkiwix.Bookmark import javax.inject.Inject import javax.inject.Singleton @@ -112,16 +112,16 @@ class Repository @Inject internal constructor( .subscribeOn(io) .observeOn(mainThread) - override fun saveBookmark(bookmark: Bookmark) = - Completable.fromAction { libkiwixBookmarks.saveBookmark(bookmark) } + override fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) = + Completable.fromAction { libkiwixBookmarks.saveBookmark(libkiwixBookmarkItem) } .subscribeOn(io) - override fun deleteBookmarks(bookmarks: List) = + override fun deleteBookmarks(bookmarks: List) = Completable.fromAction { libkiwixBookmarks.deleteBookmarks(bookmarks) } .subscribeOn(io) - override fun deleteBookmark(bookmarkUrl: String): Completable? = - Completable.fromAction { libkiwixBookmarks.deleteBookmark(bookmarkUrl) } + override fun deleteBookmark(bookId: String, bookmarkUrl: String): Completable? = + Completable.fromAction { libkiwixBookmarks.deleteBookmark(bookId, bookmarkUrl) } .subscribeOn(io) override fun saveNote(noteListItem: NoteListItem): Completable = diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index 8a1827735e..3a65c947bc 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -123,7 +123,7 @@ import org.kiwix.kiwixmobile.core.main.MainMenu.MenuClickListener import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.TableClickListener import org.kiwix.kiwixmobile.core.navigateToAppSettings -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.NavigationHistoryClickListener import org.kiwix.kiwixmobile.core.page.history.NavigationHistoryDialog import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem @@ -159,6 +159,7 @@ import org.kiwix.kiwixmobile.core.utils.files.FileUtils.deleteCachedFiles import org.kiwix.kiwixmobile.core.utils.files.FileUtils.readFile import org.kiwix.kiwixmobile.core.utils.titleToUrl import org.kiwix.kiwixmobile.core.utils.urlSuffixToParsableUrl +import org.kiwix.libkiwix.Book import java.io.File import java.io.IOException import java.text.SimpleDateFormat @@ -340,6 +341,7 @@ abstract class CoreReaderFragment : private var navigationHistoryList: MutableList = ArrayList() private var isReadSelection = false private var isReadAloudServiceRunning = false + private var libKiwixBook: Book? = null private var storagePermissionForNotesLauncher: ActivityResultLauncher? = registerForActivityResult( @@ -1111,6 +1113,7 @@ abstract class CoreReaderFragment : unRegisterReadAloudService() storagePermissionForNotesLauncher?.unregister() storagePermissionForNotesLauncher = null + libKiwixBook = null } private fun updateTableOfContents() { @@ -1648,14 +1651,17 @@ abstract class CoreReaderFragment : @OnClick(R2.id.bottom_toolbar_bookmark) fun toggleBookmark() { getCurrentWebView()?.url?.let { articleUrl -> - if (isBookmarked) { - repositoryActions?.deleteBookmark(articleUrl) - snackBarRoot?.snack(R.string.bookmark_removed) - } else { - zimReaderContainer?.zimFileReader?.let { zimFileReader -> + zimReaderContainer?.zimFileReader?.let { zimFileReader -> + libKiwixBook = Book().apply { + update(zimFileReader.jniKiwixReader) + } + if (isBookmarked) { + repositoryActions?.deleteBookmark(libKiwixBook!!.id, articleUrl) + snackBarRoot?.snack(R.string.bookmark_removed) + } else { getCurrentWebView()?.title?.let { repositoryActions?.saveBookmark( - BookmarkItem(it, articleUrl, zimFileReader) + LibkiwixBookmarkItem(it, articleUrl, zimFileReader, libKiwixBook!!) ) snackBarRoot?.snack( stringId = R.string.bookmark_added, @@ -1666,9 +1672,9 @@ abstract class CoreReaderFragment : } ) } - } ?: kotlin.run { - requireActivity().toast(R.string.unable_to_add_to_bookmarks, Toast.LENGTH_SHORT) } + } ?: kotlin.run { + requireActivity().toast(R.string.unable_to_add_to_bookmarks, Toast.LENGTH_SHORT) } } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt index 6acee4c889..9490de80a4 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainRepositoryActions.kt @@ -21,7 +21,7 @@ import android.util.Log import io.reactivex.disposables.Disposable import org.kiwix.kiwixmobile.core.data.DataSource import org.kiwix.kiwixmobile.core.di.ActivityScope -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk @@ -42,13 +42,13 @@ class MainRepositoryActions @Inject constructor(private val dataSource: DataSour .subscribe({}, { e -> Log.e(TAG, "Unable to save history", e) }) } - fun saveBookmark(bookmark: BookmarkItem) { + fun saveBookmark(bookmark: LibkiwixBookmarkItem) { saveBookmarkDisposable = dataSource.saveBookmark(bookmark) .subscribe({}, { e -> Log.e(TAG, "Unable to save bookmark", e) }) } - fun deleteBookmark(bookmarkUrl: String) { - dataSource.deleteBookmark(bookmarkUrl) + fun deleteBookmark(bookId: String, bookmarkUrl: String) { + dataSource.deleteBookmark(bookId, bookmarkUrl) ?.subscribe({}, { e -> Log.e(TAG, "Unable to delete bookmark", e) }) ?: Log.e(TAG, "Unable to delete bookmark") } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt similarity index 59% rename from core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt rename to core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt index 5eebd309e3..f5e2088631 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt @@ -20,34 +20,42 @@ package org.kiwix.kiwixmobile.core.page.bookmark.adapter import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.reader.ZimFileReader -import org.kiwix.libkiwix.Bookmark +import org.kiwix.libkiwix.Book -data class LibkiwixBookarkItem( - override val id: Long = 0L, - override val zimFilePath: String?, +data class LibkiwixBookmarkItem( + val databaseId: Long = 0L, override val zimId: String, val zimName: String, - val bookMarkUrl: String, - override val url: String = bookMarkUrl, + override val zimFilePath: String?, + val bookmarkUrl: String, override val title: String, + override val favicon: String?, override var isSelected: Boolean = false, - override val favicon: String? + override val url: String = bookmarkUrl, + override val id: Long = databaseId, + val libKiwixBook: Book?, ) : Page { - constructor(libkiwixBookmark: Bookmark) : this( + /*constructor(libkiwixBookmark: Bookmark, libkiwixBook: Book) : this( + id = 0L, zimId = libkiwixBookmark.bookId, - libkiwixBookmark.url - ) + zimFilePath = libkiwixBook.url, + zimName = libkiwixBookmark.bookTitle, + bookMarkUrl = libkiwixBookmark.url, + title = libkiwixBookmark.title, + )*/ constructor( title: String, - url: String, - zimFileReader: ZimFileReader + articleUrl: String, + zimFileReader: ZimFileReader, + libKiwixBook: Book ) : this( - zimId = zimFileReader.id, - zimName = zimFileReader.name, - zimFilePath = zimFileReader.zimFile.canonicalPath, - bookmarkUrl = url, + zimFilePath = libKiwixBook.path, + zimId = libKiwixBook.id, + zimName = libKiwixBook.name, + bookmarkUrl = articleUrl, title = title, - favicon = zimFileReader.favicon + favicon = zimFileReader.favicon, + libKiwixBook = libKiwixBook ) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt index 2707a5034a..9b74218bb5 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/reader/ZimFileReader.kt @@ -56,7 +56,7 @@ class ZimFileReader constructor( val zimFile: File?, val assetFileDescriptor: AssetFileDescriptor? = null, val assetDescriptorFilePath: String? = null, - private val jniKiwixReader: Archive, + val jniKiwixReader: Archive, private val nightModeConfig: NightModeConfig, private val searcher: SuggestionSearcher = SuggestionSearcher(jniKiwixReader) ) { From 627851a1277d19de0d9a4520a9c166df28d333de Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 4 Sep 2023 17:04:15 +0530 Subject: [PATCH 05/38] Fixed ObjectBoxToLibkiwixMigrator implementation not found --- .../kiwix/kiwixmobile/core/di/modules/ApplicationModule.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ApplicationModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ApplicationModule.kt index ca8e3cba7d..af9980272b 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ApplicationModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ApplicationModule.kt @@ -30,6 +30,7 @@ import io.reactivex.Scheduler import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.kiwix.kiwixmobile.core.NightModeConfig +import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator import org.kiwix.kiwixmobile.core.di.qualifiers.Computation import org.kiwix.kiwixmobile.core.di.qualifiers.IO import org.kiwix.kiwixmobile.core.di.qualifiers.MainThread @@ -66,6 +67,10 @@ class ApplicationModule { @Singleton internal fun provideBookUtils(): BookUtils = BookUtils() + @Provides + @Singleton + fun provideObjectBoxToLibkiwixMigrator() = ObjectBoxToLibkiwixMigrator() + @IO @Provides fun provideIoThread(): Scheduler = Schedulers.io() From de900fda6f22b076d944f13bc4318710b3e45195 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 4 Sep 2023 17:07:19 +0530 Subject: [PATCH 06/38] Implemented Save/Delete functionality with libkiwix. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 81 +++++++++++-------- .../kiwixmobile/core/dao/NewLanguagesDao.kt | 18 ----- .../remote/ObjectBoxToLibkiwixMigrator.kt | 14 ++-- .../core/main/CoreReaderFragment.kt | 5 ++ .../bookmark/adapter/LibkiwixBookmarkItem.kt | 19 ++--- .../bookmark/viewmodel/BookmarkViewModel.kt | 6 +- 6 files changed, 72 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index b8864bb9f5..c03a061b5d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -18,13 +18,11 @@ package org.kiwix.kiwixmobile.core.dao -import io.objectbox.kotlin.query -import io.objectbox.query.QueryBuilder +import io.reactivex.BackpressureStrategy import io.reactivex.Flowable -import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_ +import io.reactivex.schedulers.Schedulers import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil @@ -45,11 +43,17 @@ class LibkiwixBookmarks @Inject constructor( sharedPreferenceUtil.getPublicDirectoryPath(sharedPreferenceUtil.defaultStorage()) + "/kiwix/Bookmarks/" } - private val bookMarksFile: File by lazy { File(bookmarksFolderPath) } + private val bookmarkFile: File by lazy { + File("$bookmarksFolderPath/bookmark.txt") + } init { + // Check if bookmark folder exist if not then create the folder first. if (!File(bookmarksFolderPath).isFileExist()) File(bookmarksFolderPath).mkdir() - manager.readBookmarkFile(bookmarksFolderPath) + // Check if bookmark file exist if not then create the file to save the bookmarks. + if (!bookmarkFile.isFileExist()) bookmarkFile.createNewFile() + // set up manager to read the bookmarks from this file + manager.readBookmarkFile(bookmarkFile.canonicalPath) } fun bookmarks(): Flowable> { @@ -64,36 +68,37 @@ class LibkiwixBookmarks @Inject constructor( override fun deletePages(pagesToDelete: List) = deleteBookmarks(pagesToDelete as List) - fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?) = box.query { - equal( - BookmarkEntity_.zimId, zimFileReader?.id ?: "", - QueryBuilder.StringOrder.CASE_INSENSITIVE - ) - .or() - .equal( - BookmarkEntity_.zimName, zimFileReader?.name ?: "", - QueryBuilder.StringOrder.CASE_INSENSITIVE - ) - order(BookmarkEntity_.bookmarkTitle) - }.property(BookmarkEntity_.bookmarkUrl) - .findStrings() - .toList() - .distinct() + fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List { + return zimFileReader?.let { reader -> + library + .getBookmarks(true) + .map { it.url } + } ?: emptyList() + } fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader): Flowable> { - val book = Book().apply { - update(zimFileReader.jniKiwixReader) - } - library.addBook(book) - val bookMarksList: Flowable> = arrayListOf() - library.getBookmarks(true) - .map { + return Flowable.create({ emitter -> + // Create a Book object and add it to the library + val book = Book().apply { + update(zimFileReader.jniKiwixReader) } - return bookMarksList + addBookToLibrary(book) + + // Retrieve bookmarks from the library + val bookmarks = library.getBookmarks(true) + + // Extract URLs from bookmarks + val urls = bookmarks.map { it.url } + + // Emit the list of URLs + emitter.onNext(urls) + emitter.onComplete() + }, BackpressureStrategy.LATEST) + .subscribeOn(Schedulers.io()) } fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { - library.addBook(libkiwixBookmarkItem.libKiwixBook) + addBookToLibrary(libkiwixBookmarkItem.libKiwixBook) val bookmark = Bookmark().apply { bookId = libkiwixBookmarkItem.zimId title = libkiwixBookmarkItem.title @@ -101,17 +106,25 @@ class LibkiwixBookmarks @Inject constructor( bookTitle = libkiwixBookmarkItem.libKiwixBook?.title ?: libkiwixBookmarkItem.zimId } library.addBookmark(bookmark).also { - // if the book name is not found then takes zim id as file name - val bookMarkFileName = libkiwixBookmarkItem.libKiwixBook?.name ?: libkiwixBookmarkItem.id - library.writeBookmarksToFile("$bookmarksFolderPath/$bookMarkFileName") + writeBookMarksToFile() } } + private fun addBookToLibrary(libKiwixBook: Book?) { + library.addBook(libKiwixBook) + } + fun deleteBookmarks(bookmarks: List) { bookmarks.map { deleteBookmark(it.zimId, it.bookmarkUrl) } } fun deleteBookmark(bookId: String, bookmarkUrl: String) { - library.removeBookmark(bookId, bookmarkUrl) + library.removeBookmark(bookId, bookmarkUrl).also { + writeBookMarksToFile() + } + } + + private fun writeBookMarksToFile() { + library.writeBookmarksToFile(bookmarkFile.canonicalPath) } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt index 5e029226be..0ea78ceecb 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/NewLanguagesDao.kt @@ -23,10 +23,8 @@ import io.objectbox.query.Query import io.objectbox.rx.RxQuery import io.reactivex.BackpressureStrategy import io.reactivex.BackpressureStrategy.LATEST -import io.reactivex.Flowable import org.kiwix.kiwixmobile.core.dao.entities.LanguageEntity import org.kiwix.kiwixmobile.core.zim_manager.Language -import org.kiwix.libkiwix.Library import javax.inject.Inject import javax.inject.Singleton @@ -48,19 +46,3 @@ internal fun Box.asFlowable( backpressureStrategy: BackpressureStrategy = LATEST ) = RxQuery.observable(query).toFlowable(backpressureStrategy) - -fun Library.asFlowable( - queryFunction: () -> List, - backpressureStrategy: BackpressureStrategy = BackpressureStrategy.LATEST -): Flowable> { - return Flowable.create({ emitter -> - val subscription = this.subscribe { - val results = queryFunction() - emitter.onNext(results) - } - - emitter.setCancellable { - subscription.dispose() - } - }, backpressureStrategy) -} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index d3651b917b..5ca0d811ad 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -47,13 +47,13 @@ class ObjectBoxToLibkiwixMigrator { bookMarksList.forEachIndexed { _, bookmarkEntity -> CoroutineScope(Dispatchers.IO).launch { // removing the single entity from the object box after migration. - box.query { - equal( - BookmarkEntity_.bookmarkUrl, - bookmarkEntity.bookmarkUrl, - QueryBuilder.StringOrder.CASE_INSENSITIVE - ) - }.remove() + // box.query { + // equal( + // BookmarkEntity_.bookmarkUrl, + // bookmarkEntity.bookmarkUrl, + // QueryBuilder.StringOrder.CASE_INSENSITIVE + // ) + // }.remove() } } sharedPreferenceUtil.putPrefBookMarkMigrated(true) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index 3a65c947bc..b751c42968 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -107,6 +107,7 @@ import org.kiwix.kiwixmobile.core.R2 import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.base.BaseFragment import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission @@ -249,6 +250,10 @@ abstract class CoreReaderFragment : @Inject var newBookmarksDao: NewBookmarksDao? = null + @JvmField + @Inject + var libkiwixBookmarks: LibkiwixBookmarks? = null + @JvmField @Inject var alertDialogShower: DialogShower? = null diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt index f5e2088631..34cb275e51 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt @@ -21,6 +21,7 @@ package org.kiwix.kiwixmobile.core.page.bookmark.adapter import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.libkiwix.Book +import org.kiwix.libkiwix.Bookmark data class LibkiwixBookmarkItem( val databaseId: Long = 0L, @@ -35,14 +36,14 @@ data class LibkiwixBookmarkItem( override val id: Long = databaseId, val libKiwixBook: Book?, ) : Page { - /*constructor(libkiwixBookmark: Bookmark, libkiwixBook: Book) : this( - id = 0L, - zimId = libkiwixBookmark.bookId, - zimFilePath = libkiwixBook.url, - zimName = libkiwixBookmark.bookTitle, - bookMarkUrl = libkiwixBookmark.url, - title = libkiwixBookmark.title, - )*/ + // constructor(libkiwixBookmark: Bookmark, libkiwixBook: Book) : this( + // id = 0L, + // zimId = libkiwixBookmark.bookId, + // zimFilePath = libkiwixBook.url, + // zimName = libkiwixBookmark.bookTitle, + // bookMarkUrl = libkiwixBookmark.url, + // title = libkiwixBookmark.title, + // ) constructor( title: String, @@ -50,7 +51,7 @@ data class LibkiwixBookmarkItem( zimFileReader: ZimFileReader, libKiwixBook: Book ) : this( - zimFilePath = libKiwixBook.path, + zimFilePath = zimFileReader.zimFile.canonicalPath, zimId = libKiwixBook.id, zimName = libKiwixBook.name, bookmarkUrl = articleUrl, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt index ebdee2849b..8e1fe6f128 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt @@ -18,7 +18,7 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel -import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.ShowDeleteBookmarksDialog import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.UpdateAllBookmarksPreference @@ -29,10 +29,10 @@ import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import javax.inject.Inject class BookmarkViewModel @Inject constructor( - bookmarksDao: NewBookmarksDao, + libkiwixBookmarks: LibkiwixBookmarks, zimReaderContainer: ZimReaderContainer, sharedPrefs: SharedPreferenceUtil -) : PageViewModel(bookmarksDao, sharedPrefs, zimReaderContainer) { +) : PageViewModel(libkiwixBookmarks, sharedPrefs, zimReaderContainer) { override fun initialState(): BookmarkState = BookmarkState(emptyList(), sharedPreferenceUtil.showBookmarksAllBooks, zimReaderContainer.id) From 88e30e19fdf5742f4c78260c1645b8f3f4be7914 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 4 Sep 2023 18:16:26 +0530 Subject: [PATCH 07/38] Refactored the code to display saved bookmarks in the bookmarks screen, improved the delete functionality in the bookmark screen, and also refactored the add/delete bookmark functionality in the reader fragment. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 9 ++++++--- .../kiwix/kiwixmobile/core/data/DataSource.kt | 2 +- .../kiwix/kiwixmobile/core/data/Repository.kt | 4 ++-- .../bookmark/adapter/LibkiwixBookmarkItem.kt | 17 +++++++++-------- .../page/bookmark/viewmodel/BookmarkState.kt | 8 +++++--- .../bookmark/viewmodel/BookmarkViewModel.kt | 15 +++++++++++---- .../effects/ShowDeleteBookmarksDialog.kt | 3 ++- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index c03a061b5d..c083982c18 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -58,9 +58,12 @@ class LibkiwixBookmarks @Inject constructor( fun bookmarks(): Flowable> { val bookMarksArray: Array? = library.getBookmarks(true) - return bookMarksArray?.let { - it.map(::BookmarkItem) - } + val bookmarkList = bookMarksArray?.toList() ?: emptyList() + + return Flowable.fromIterable(bookmarkList) + .map(::LibkiwixBookmarkItem) + .toList() + .toFlowable() as Flowable> } override fun pages(): Flowable> = bookmarks() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt index 0d19c3fe45..159eab7845 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt @@ -41,7 +41,7 @@ interface DataSource { fun saveHistory(history: HistoryItem): Completable fun deleteHistory(historyList: List): Completable fun clearHistory(): Completable - fun getBookmarks(): Flowable> + fun getBookmarks(): Flowable> fun getCurrentZimBookmarksUrl(): Single> fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem): Completable 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 3688eeb13e..179d6ed6ee 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 @@ -31,7 +31,6 @@ import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao import org.kiwix.kiwixmobile.core.di.qualifiers.IO import org.kiwix.kiwixmobile.core.di.qualifiers.MainThread import org.kiwix.kiwixmobile.core.extensions.HeaderizableList -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem @@ -105,7 +104,8 @@ class Repository @Inject internal constructor( recentSearchDao.deleteSearchHistory() } - override fun getBookmarks() = libkiwixBookmarks.bookmarks() as Flowable> + override fun getBookmarks() = + libkiwixBookmarks.bookmarks() as Flowable> override fun getCurrentZimBookmarksUrl() = Single.just(libkiwixBookmarks.getCurrentZimBookmarksUrl(zimReaderContainer.zimFileReader)) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt index 34cb275e51..6d70163779 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt @@ -36,14 +36,15 @@ data class LibkiwixBookmarkItem( override val id: Long = databaseId, val libKiwixBook: Book?, ) : Page { - // constructor(libkiwixBookmark: Bookmark, libkiwixBook: Book) : this( - // id = 0L, - // zimId = libkiwixBookmark.bookId, - // zimFilePath = libkiwixBook.url, - // zimName = libkiwixBookmark.bookTitle, - // bookMarkUrl = libkiwixBookmark.url, - // title = libkiwixBookmark.title, - // ) + constructor(libkiwixBookmark: Bookmark) : this( + zimId = libkiwixBookmark.bookId, + zimName = libkiwixBookmark.bookTitle, + zimFilePath = libkiwixBookmark.url, + bookmarkUrl = libkiwixBookmark.url, + title = libkiwixBookmark.title, + favicon = null, + libKiwixBook = null + ) constructor( title: String, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt index 810e6f9906..989b26d42b 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt @@ -18,18 +18,20 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.page.adapter.PageRelated import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.viewmodel.PageState data class BookmarkState( - override val pageItems: List, + override val pageItems: List, override val showAll: Boolean, override val currentZimId: String?, override val searchTerm: String = "" -) : PageState() { +) : PageState() { override val visiblePageItems: List = filteredPageItems - override fun copyWithNewItems(newItems: List): PageState = + override fun copyWithNewItems(newItems: List): PageState = copy(pageItems = newItems) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt index 8e1fe6f128..a955b7b4cb 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModel.kt @@ -19,7 +19,7 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.ShowDeleteBookmarksDialog import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.UpdateAllBookmarksPreference import org.kiwix.kiwixmobile.core.page.viewmodel.Action @@ -32,7 +32,11 @@ class BookmarkViewModel @Inject constructor( libkiwixBookmarks: LibkiwixBookmarks, zimReaderContainer: ZimReaderContainer, sharedPrefs: SharedPreferenceUtil -) : PageViewModel(libkiwixBookmarks, sharedPrefs, zimReaderContainer) { +) : PageViewModel( + libkiwixBookmarks, + sharedPrefs, + zimReaderContainer +) { override fun initialState(): BookmarkState = BookmarkState(emptyList(), sharedPreferenceUtil.showBookmarksAllBooks, zimReaderContainer.id) @@ -47,7 +51,7 @@ class BookmarkViewModel @Inject constructor( state: BookmarkState, action: Action.UpdatePages ): BookmarkState = - state.copy(pageItems = action.pages.filterIsInstance()) + state.copy(pageItems = action.pages.filterIsInstance()) override fun offerUpdateToShowAllToggle( action: Action.UserClickedShowAllToggle, @@ -63,6 +67,9 @@ class BookmarkViewModel @Inject constructor( override fun createDeletePageDialogEffect(state: BookmarkState) = ShowDeleteBookmarksDialog(effects, state, pageDao) - override fun copyWithNewItems(state: BookmarkState, newItems: List): BookmarkState = + override fun copyWithNewItems( + state: BookmarkState, + newItems: List + ): BookmarkState = state.copy(pageItems = newItems) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt index 68591834d4..c8599ec9e8 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt @@ -24,6 +24,7 @@ import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.dao.PageDao import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.viewmodel.PageState import org.kiwix.kiwixmobile.core.page.viewmodel.effects.DeletePageItems import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower @@ -33,7 +34,7 @@ import javax.inject.Inject data class ShowDeleteBookmarksDialog( private val effects: PublishProcessor>, - private val state: PageState, + private val state: PageState, private val pageDao: PageDao ) : SideEffect { @Inject lateinit var dialogShower: DialogShower From d68c890e8f51ec7905e28f182667a5b9f587476b Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 4 Sep 2023 19:27:01 +0530 Subject: [PATCH 08/38] Save bookmark when it does not exist in the file since bookmarks is now written in a file with libkiwix --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index c083982c18..a62822c51f 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -56,15 +56,11 @@ class LibkiwixBookmarks @Inject constructor( manager.readBookmarkFile(bookmarkFile.canonicalPath) } - fun bookmarks(): Flowable> { - val bookMarksArray: Array? = library.getBookmarks(true) - val bookmarkList = bookMarksArray?.toList() ?: emptyList() - - return Flowable.fromIterable(bookmarkList) + fun bookmarks(): Flowable> = + Flowable.fromIterable(getBookmarksList()) .map(::LibkiwixBookmarkItem) .toList() .toFlowable() as Flowable> - } override fun pages(): Flowable> = bookmarks() @@ -86,12 +82,7 @@ class LibkiwixBookmarks @Inject constructor( update(zimFileReader.jniKiwixReader) } addBookToLibrary(book) - - // Retrieve bookmarks from the library - val bookmarks = library.getBookmarks(true) - - // Extract URLs from bookmarks - val urls = bookmarks.map { it.url } + val urls = getBookmarksList().map { it.url } // Emit the list of URLs emitter.onNext(urls) @@ -101,15 +92,17 @@ class LibkiwixBookmarks @Inject constructor( } fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { - addBookToLibrary(libkiwixBookmarkItem.libKiwixBook) - val bookmark = Bookmark().apply { - bookId = libkiwixBookmarkItem.zimId - title = libkiwixBookmarkItem.title - url = libkiwixBookmarkItem.url - bookTitle = libkiwixBookmarkItem.libKiwixBook?.title ?: libkiwixBookmarkItem.zimId - } - library.addBookmark(bookmark).also { - writeBookMarksToFile() + if (!isBookMarkExist(libkiwixBookmarkItem)) { + addBookToLibrary(libkiwixBookmarkItem.libKiwixBook) + val bookmark = Bookmark().apply { + bookId = libkiwixBookmarkItem.zimId + title = libkiwixBookmarkItem.title + url = libkiwixBookmarkItem.url + bookTitle = libkiwixBookmarkItem.libKiwixBook?.title ?: libkiwixBookmarkItem.zimId + } + library.addBookmark(bookmark).also { + writeBookMarksToFile() + } } } @@ -130,4 +123,11 @@ class LibkiwixBookmarks @Inject constructor( private fun writeBookMarksToFile() { library.writeBookmarksToFile(bookmarkFile.canonicalPath) } + + private fun getBookmarksList() = + library.getBookmarks(true)?.toList() ?: emptyList() + + private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = + getBookmarksList() + .any { it.url == libkiwixBookmarkItem.bookmarkUrl && it.bookId == libkiwixBookmarkItem.zimId } } From c3848496311ec8622678d6d2e43b7c5b91f49d36 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 4 Sep 2023 19:34:36 +0530 Subject: [PATCH 09/38] Improved url loading for current book --- .../java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index a62822c51f..f32f58f664 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -71,6 +71,7 @@ class LibkiwixBookmarks @Inject constructor( return zimFileReader?.let { reader -> library .getBookmarks(true) + .filter { it.bookId == reader.id } .map { it.url } } ?: emptyList() } @@ -82,7 +83,9 @@ class LibkiwixBookmarks @Inject constructor( update(zimFileReader.jniKiwixReader) } addBookToLibrary(book) - val urls = getBookmarksList().map { it.url } + val urls = getBookmarksList() + .filter { it.bookId == zimFileReader.id } + .map { it.url } // Emit the list of URLs emitter.onNext(urls) From 7a375e415bf9320bc360c6786f0bc012de6ea3c0 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 5 Sep 2023 15:48:56 +0530 Subject: [PATCH 10/38] Fixed bookmark not redable from file if there is no book added to the library --- .../java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 5 ++--- .../core/page/bookmark/viewmodel/BookmarkState.kt | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index f32f58f664..8de54c9408 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -69,8 +69,7 @@ class LibkiwixBookmarks @Inject constructor( fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List { return zimFileReader?.let { reader -> - library - .getBookmarks(true) + getBookmarksList() .filter { it.bookId == reader.id } .map { it.url } } ?: emptyList() @@ -128,7 +127,7 @@ class LibkiwixBookmarks @Inject constructor( } private fun getBookmarksList() = - library.getBookmarks(true)?.toList() ?: emptyList() + library.getBookmarks(false)?.toList() ?: emptyList() private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = getBookmarksList() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt index 989b26d42b..0bf9f095e8 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt @@ -18,9 +18,7 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel -import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.page.adapter.PageRelated -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.viewmodel.PageState From 9d69aebb9bbd79a8aa8dd34e88c690f14a84fdef Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 5 Sep 2023 16:57:26 +0530 Subject: [PATCH 11/38] Improved saving bookmark, add book to the libkiwix library if not already added. --- .../kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 10 ++++++---- .../kiwix/kiwixmobile/core/main/CoreReaderFragment.kt | 6 +----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 8de54c9408..c82f550410 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -81,7 +81,7 @@ class LibkiwixBookmarks @Inject constructor( val book = Book().apply { update(zimFileReader.jniKiwixReader) } - addBookToLibrary(book) + addBookToLibraryIfNotExist(book) val urls = getBookmarksList() .filter { it.bookId == zimFileReader.id } .map { it.url } @@ -95,7 +95,7 @@ class LibkiwixBookmarks @Inject constructor( fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { if (!isBookMarkExist(libkiwixBookmarkItem)) { - addBookToLibrary(libkiwixBookmarkItem.libKiwixBook) + addBookToLibraryIfNotExist(libkiwixBookmarkItem.libKiwixBook) val bookmark = Bookmark().apply { bookId = libkiwixBookmarkItem.zimId title = libkiwixBookmarkItem.title @@ -108,8 +108,10 @@ class LibkiwixBookmarks @Inject constructor( } } - private fun addBookToLibrary(libKiwixBook: Book?) { - library.addBook(libKiwixBook) + private fun addBookToLibraryIfNotExist(libKiwixBook: Book?) { + if (!library.booksIds.any { it == libKiwixBook?.id }) { + library.addBook(libKiwixBook) + } } fun deleteBookmarks(bookmarks: List) { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index b751c42968..b7654f1938 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -246,10 +246,6 @@ abstract class CoreReaderFragment : @Inject var menuFactory: MainMenu.Factory? = null - @JvmField - @Inject - var newBookmarksDao: NewBookmarksDao? = null - @JvmField @Inject var libkiwixBookmarks: LibkiwixBookmarks? = null @@ -1542,7 +1538,7 @@ abstract class CoreReaderFragment : protected fun setUpBookmarks(zimFileReader: ZimFileReader) { safeDispose() bookmarkingDisposable = Flowable.combineLatest( - newBookmarksDao?.bookmarkUrlsForCurrentBook(zimFileReader), + libkiwixBookmarks?.bookmarkUrlsForCurrentBook(zimFileReader), webUrlsProcessor, List::contains ) From 0c750fcaf034ad04783958bb7aa8ec8f53a1c748 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 5 Sep 2023 18:18:23 +0530 Subject: [PATCH 12/38] Fixed all bookmarks automatically selected if we select only one bookmark --- .../kiwixmobile/core/page/viewmodel/PageState.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt index 01841b9ca2..89e495ac05 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt @@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.core.page.viewmodel import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.adapter.PageRelated +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem abstract class PageState { abstract val pageItems: List @@ -36,9 +37,18 @@ abstract class PageState { fun getItemsAfterToggleSelectionOfItem(page: Page): List { return pageItems.map { - if (it.id == page.id) it.apply { - isSelected = !isSelected - } else it + // check if the current item is `LibkiwixBookmarkItem` because we have not saving + // the bookmarks in database so it does not have any unique value so to get the + // selected items we check for uri since url is unique for every bookmark. + if (it is LibkiwixBookmarkItem) { + if (it.url == page.url) it.apply { + isSelected = !isSelected + } else it + } else { + if (it.id == page.id) it.apply { + isSelected = !isSelected + } else it + } } } From e85b7176d302722b778f6a86c5111c0d85022d66 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 5 Sep 2023 18:28:17 +0530 Subject: [PATCH 13/38] Migrating bookmarks from objectbox to libkiwix --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 6 ++++-- .../data/remote/ObjectBoxToLibkiwixMigrator.kt | 18 +++++++++++------- .../bookmark/adapter/LibkiwixBookmarkItem.kt | 13 +++++++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index c82f550410..e8f731b82d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -109,8 +109,10 @@ class LibkiwixBookmarks @Inject constructor( } private fun addBookToLibraryIfNotExist(libKiwixBook: Book?) { - if (!library.booksIds.any { it == libKiwixBook?.id }) { - library.addBook(libKiwixBook) + libKiwixBook?.let { book -> + if (!library.booksIds.any { it == book.id }) { + library.addBook(libKiwixBook) + } } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index 5ca0d811ad..bc09fc4a98 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -27,14 +27,17 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.CoreApp +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks 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.utils.SharedPreferenceUtil import javax.inject.Inject class ObjectBoxToLibkiwixMigrator { @Inject lateinit var boxStore: BoxStore @Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil + @Inject lateinit var libkiwixBookmarks: LibkiwixBookmarks fun migrateBookmarksToLibkiwix() { CoreApp.coreComponent.inject(this) @@ -46,14 +49,15 @@ class ObjectBoxToLibkiwixMigrator { val bookMarksList = box.all bookMarksList.forEachIndexed { _, bookmarkEntity -> CoroutineScope(Dispatchers.IO).launch { + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity)) // removing the single entity from the object box after migration. - // box.query { - // equal( - // BookmarkEntity_.bookmarkUrl, - // bookmarkEntity.bookmarkUrl, - // QueryBuilder.StringOrder.CASE_INSENSITIVE - // ) - // }.remove() + box.query { + equal( + BookmarkEntity_.bookmarkUrl, + bookmarkEntity.bookmarkUrl, + QueryBuilder.StringOrder.CASE_INSENSITIVE + ) + }.remove() } } sharedPreferenceUtil.putPrefBookMarkMigrated(true) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt index 6d70163779..a0e038e0fd 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.page.bookmark.adapter +import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.libkiwix.Book @@ -60,4 +61,16 @@ data class LibkiwixBookmarkItem( favicon = zimFileReader.favicon, libKiwixBook = libKiwixBook ) + + constructor( + bookmarkEntity: BookmarkEntity + ) : this( + zimId = bookmarkEntity.zimId, + zimFilePath = bookmarkEntity.zimFilePath, + zimName = bookmarkEntity.zimName, + bookmarkUrl = bookmarkEntity.bookmarkUrl, + title = bookmarkEntity.bookmarkTitle, + favicon = bookmarkEntity.favicon, + libKiwixBook = null + ) } From b2c1c1ed5dfe7ba68f78b410295339442ab8ec9e Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 5 Sep 2023 18:49:04 +0530 Subject: [PATCH 14/38] Improved seleted items method for avoid duplicacy --- .../kiwixmobile/core/page/viewmodel/PageState.kt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt index 89e495ac05..05decb8746 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/viewmodel/PageState.kt @@ -39,16 +39,12 @@ abstract class PageState { return pageItems.map { // check if the current item is `LibkiwixBookmarkItem` because we have not saving // the bookmarks in database so it does not have any unique value so to get the - // selected items we check for uri since url is unique for every bookmark. - if (it is LibkiwixBookmarkItem) { - if (it.url == page.url) it.apply { - isSelected = !isSelected - } else it - } else { - if (it.id == page.id) it.apply { - isSelected = !isSelected - } else it - } + // selected items we check for url since url is unique for every bookmark. + val currentItemIdentifier = if (it is LibkiwixBookmarkItem) it.url else it.id + val pageIdentifier = if (it is LibkiwixBookmarkItem) page.url else page.id + if (currentItemIdentifier == pageIdentifier) it.apply { + isSelected = !isSelected + } else it } } From 7c2320722581ce0462e26e5568654570194f9ac9 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 5 Sep 2023 19:03:53 +0530 Subject: [PATCH 15/38] Fixed detekt errors --- .../org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 4 +++- .../java/org/kiwix/kiwixmobile/core/data/DataSource.kt | 1 - .../org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt | 8 +++----- .../core/page/bookmark/viewmodel/BookmarkState.kt | 4 ++-- .../viewmodel/effects/ShowDeleteBookmarksDialog.kt | 1 - 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index e8f731b82d..f3b8e68d5e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -40,7 +40,9 @@ class LibkiwixBookmarks @Inject constructor( ) : PageDao { private val bookmarksFolderPath: String by lazy { - sharedPreferenceUtil.getPublicDirectoryPath(sharedPreferenceUtil.defaultStorage()) + "/kiwix/Bookmarks/" + sharedPreferenceUtil.getPublicDirectoryPath( + sharedPreferenceUtil.defaultStorage() + ) + "/kiwix/Bookmarks/" } private val bookmarkFile: File by lazy { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt index 159eab7845..3ff5bfc576 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/DataSource.kt @@ -20,7 +20,6 @@ package org.kiwix.kiwixmobile.core.data import io.reactivex.Completable import io.reactivex.Flowable import io.reactivex.Single -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index b7654f1938..ec02a67dec 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -342,7 +342,6 @@ abstract class CoreReaderFragment : private var navigationHistoryList: MutableList = ArrayList() private var isReadSelection = false private var isReadAloudServiceRunning = false - private var libKiwixBook: Book? = null private var storagePermissionForNotesLauncher: ActivityResultLauncher? = registerForActivityResult( @@ -1114,7 +1113,6 @@ abstract class CoreReaderFragment : unRegisterReadAloudService() storagePermissionForNotesLauncher?.unregister() storagePermissionForNotesLauncher = null - libKiwixBook = null } private fun updateTableOfContents() { @@ -1653,16 +1651,16 @@ abstract class CoreReaderFragment : fun toggleBookmark() { getCurrentWebView()?.url?.let { articleUrl -> zimReaderContainer?.zimFileReader?.let { zimFileReader -> - libKiwixBook = Book().apply { + val libKiwixBook = Book().apply { update(zimFileReader.jniKiwixReader) } if (isBookmarked) { - repositoryActions?.deleteBookmark(libKiwixBook!!.id, articleUrl) + repositoryActions?.deleteBookmark(libKiwixBook.id, articleUrl) snackBarRoot?.snack(R.string.bookmark_removed) } else { getCurrentWebView()?.title?.let { repositoryActions?.saveBookmark( - LibkiwixBookmarkItem(it, articleUrl, zimFileReader, libKiwixBook!!) + LibkiwixBookmarkItem(it, articleUrl, zimFileReader, libKiwixBook) ) snackBarRoot?.snack( stringId = R.string.bookmark_added, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt index 0bf9f095e8..aea012598e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkState.kt @@ -30,6 +30,6 @@ data class BookmarkState( ) : PageState() { override val visiblePageItems: List = filteredPageItems - override fun copyWithNewItems(newItems: List): PageState = - copy(pageItems = newItems) + override fun copyWithNewItems(newItems: List): + PageState = copy(pageItems = newItems) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt index c8599ec9e8..22ba4ad65c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/effects/ShowDeleteBookmarksDialog.kt @@ -23,7 +23,6 @@ import io.reactivex.processors.PublishProcessor import org.kiwix.kiwixmobile.core.base.SideEffect import org.kiwix.kiwixmobile.core.dao.PageDao import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.viewmodel.PageState import org.kiwix.kiwixmobile.core.page.viewmodel.effects.DeletePageItems From 19085d69bb95222456a1c3a6f8afd755eac4e811 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 6 Sep 2023 16:53:48 +0530 Subject: [PATCH 16/38] Fixed after `delete/save` bookmark list is not updating. * We introduced a Flowable list in LibkiwixBookmarks to observe live changes on the UI. Previously, the UI was not updating after deleting bookmarks on the BookmarkScreen, and toggling bookmarks in the reader screen was also not reflecting the changes. To address this, we created a Flowable list and are now observing it whenever bookmarks are added or removed in the library. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index f3b8e68d5e..be6fcc749c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -19,8 +19,10 @@ package org.kiwix.kiwixmobile.core.dao import io.reactivex.BackpressureStrategy +import io.reactivex.BackpressureStrategy.LATEST import io.reactivex.Flowable import io.reactivex.schedulers.Schedulers +import io.reactivex.subjects.BehaviorSubject import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem @@ -39,6 +41,10 @@ class LibkiwixBookmarks @Inject constructor( val sharedPreferenceUtil: SharedPreferenceUtil ) : PageDao { + private val bookmarkListBehaviour: BehaviorSubject>? by lazy { + BehaviorSubject.createDefault(getBookmarksList()) + } + private val bookmarksFolderPath: String by lazy { sharedPreferenceUtil.getPublicDirectoryPath( sharedPreferenceUtil.defaultStorage() @@ -59,10 +65,8 @@ class LibkiwixBookmarks @Inject constructor( } fun bookmarks(): Flowable> = - Flowable.fromIterable(getBookmarksList()) - .map(::LibkiwixBookmarkItem) - .toList() - .toFlowable() as Flowable> + flowableBookmarkList() + .map { it.map(::LibkiwixBookmarkItem) } override fun pages(): Flowable> = bookmarks() @@ -77,23 +81,18 @@ class LibkiwixBookmarks @Inject constructor( } ?: emptyList() } - fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader): Flowable> { - return Flowable.create({ emitter -> - // Create a Book object and add it to the library - val book = Book().apply { - update(zimFileReader.jniKiwixReader) + fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader): Flowable> = + flowableBookmarkList() + .map { bookmarksList -> + bookmarksList.filter { it.bookId == zimFileReader.id } + .map(Bookmark::getUrl) + } + .subscribeOn(Schedulers.io()).also { + val book = Book().apply { + update(zimFileReader.jniKiwixReader) + } + addBookToLibraryIfNotExist(book) } - addBookToLibraryIfNotExist(book) - val urls = getBookmarksList() - .filter { it.bookId == zimFileReader.id } - .map { it.url } - - // Emit the list of URLs - emitter.onNext(urls) - emitter.onComplete() - }, BackpressureStrategy.LATEST) - .subscribeOn(Schedulers.io()) - } fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { if (!isBookMarkExist(libkiwixBookmarkItem)) { @@ -106,6 +105,7 @@ class LibkiwixBookmarks @Inject constructor( } library.addBookmark(bookmark).also { writeBookMarksToFile() + updateFlowableBookmarkList() } } } @@ -125,6 +125,7 @@ class LibkiwixBookmarks @Inject constructor( fun deleteBookmark(bookId: String, bookmarkUrl: String) { library.removeBookmark(bookId, bookmarkUrl).also { writeBookMarksToFile() + updateFlowableBookmarkList() } } @@ -138,4 +139,26 @@ class LibkiwixBookmarks @Inject constructor( private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = getBookmarksList() .any { it.url == libkiwixBookmarkItem.bookmarkUrl && it.bookId == libkiwixBookmarkItem.zimId } + + private fun flowableBookmarkList( + backpressureStrategy: BackpressureStrategy = LATEST + ): Flowable> { + return Flowable.create({ emitter -> + val disposable = bookmarkListBehaviour?.subscribe( + { list -> + if (!emitter.isCancelled) { + emitter.onNext(list.toList()) + } + }, + emitter::onError, + emitter::onComplete + ) + + emitter.setDisposable(disposable) + }, backpressureStrategy) + } + + private fun updateFlowableBookmarkList() { + bookmarkListBehaviour?.onNext(getBookmarksList()) + } } From 429502af193fa2c2ab3671b3cc3c687ebba53ec5 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 6 Sep 2023 18:09:15 +0530 Subject: [PATCH 17/38] Refactored unit coverage test cases for testing bookmarks with `LibkiwixBookmarks`. --- .../core/dao/LibkiwixBookmarkTest.kt | 42 +++++++++++++++ .../core/libkiwix_wrapper/BookmarkWrapper.kt | 54 +++++++++++++++++++ .../core/libkiwix_wrapper/LibraryWrapper.kt | 28 ++++++++++ .../core/libkiwix_wrapper/ManagerWrapper.kt | 23 ++++++++ .../kiwixmobile/core/page/PageTestHelpers.kt | 25 ++++----- .../viewmodel/BookmarkViewModelTest.kt | 12 ++--- 6 files changed, 166 insertions(+), 18 deletions(-) create mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt create mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt create mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt create mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt new file mode 100644 index 0000000000..ac09ed95c5 --- /dev/null +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt @@ -0,0 +1,42 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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 io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.Test +import org.kiwix.kiwixmobile.core.libkiwix_wrapper.BookmarkWrapper +import org.kiwix.kiwixmobile.core.libkiwix_wrapper.LibraryWrapper +import org.kiwix.kiwixmobile.core.libkiwix_wrapper.ManagerWrapper +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil + +internal class LibkiwixBookmarkTest { + private val library: LibraryWrapper = mockk(relaxed = true) + private val manager = ManagerWrapper(library) + private val sharedPreferenceUtil: SharedPreferenceUtil = mockk(relaxed = true) + private val libkiwixBookmarks = LibkiwixBookmarks(library, manager, sharedPreferenceUtil) + + @Test + fun saveBookmark() { + val bookmark: BookmarkWrapper = mockk(relaxed = true) + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmark)) + verify { library.addBookmark(bookmark) } + } +} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt new file mode 100644 index 0000000000..ecdd9fcdda --- /dev/null +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt @@ -0,0 +1,54 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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.libkiwix_wrapper + +import org.kiwix.libkiwix.Bookmark + +class BookmarkWrapper : Bookmark() { + override fun getBookId(): String = super.getBookId() + override fun getDate(): String = super.getDate() + override fun getBookTitle(): String = super.getBookTitle() + override fun getLanguage(): String = super.getLanguage() + override fun getTitle(): String = super.getTitle() + override fun getUrl(): String = super.getUrl() + + override fun setBookId(bookId: String?) { + super.setBookId(bookId) + } + + override fun setBookTitle(bookTitle: String?) { + super.setBookTitle(bookTitle) + } + + override fun setDate(Date: String?) { + super.setDate(Date) + } + + override fun setLanguage(language: String?) { + super.setLanguage(language) + } + + override fun setUrl(url: String?) { + super.setUrl(url) + } + + override fun setTitle(title: String?) { + super.setTitle(title) + } +} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt new file mode 100644 index 0000000000..789d0de838 --- /dev/null +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt @@ -0,0 +1,28 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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.libkiwix_wrapper + +import org.kiwix.libkiwix.Bookmark +import org.kiwix.libkiwix.Library + +class LibraryWrapper : Library() { + + override fun addBookmark(bookmark: Bookmark?) = + super.addBookmark(bookmark) +} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt new file mode 100644 index 0000000000..55933d785e --- /dev/null +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt @@ -0,0 +1,23 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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.libkiwix_wrapper + +import org.kiwix.libkiwix.Manager + +class ManagerWrapper(library: LibraryWrapper) : Manager(library) diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/page/PageTestHelpers.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/page/PageTestHelpers.kt index 26784f24dc..4c0e3c0875 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/page/PageTestHelpers.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/page/PageTestHelpers.kt @@ -19,7 +19,7 @@ package org.kiwix.kiwixmobile.core.page import org.kiwix.kiwixmobile.core.page.adapter.Page -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.BookmarkItem +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.BookmarkState import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem import org.kiwix.kiwixmobile.core.page.history.viewmodel.HistoryState @@ -79,21 +79,22 @@ fun bookmark( zimFilePath: String = "zimFilePath", bookmarkUrl: String = "bookmarkUrl", favicon: String = "favicon" -): BookmarkItem { - return BookmarkItem( - id, - zimId, - zimName, - zimFilePath, - bookmarkUrl, - bookmarkTitle, - favicon, - isSelected +): LibkiwixBookmarkItem { + return LibkiwixBookmarkItem( + id = id, + zimId = zimId, + zimName = zimName, + zimFilePath = zimFilePath, + bookmarkUrl = bookmarkUrl, + title = bookmarkTitle, + isSelected = isSelected, + favicon = favicon, + libKiwixBook = null ) } fun bookmarkState( - bookmarks: List = emptyList(), + bookmarks: List = emptyList(), showAll: Boolean = true, zimId: String = "id", searchTerm: String = "" diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModelTest.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModelTest.kt index 3e555c0bff..0c320ddf03 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModelTest.kt +++ b/core/src/test/java/org/kiwix/kiwixmobile/core/page/bookmark/viewmodel/BookmarkViewModelTest.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.NewBookmarksDao +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.bookmark import org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects.ShowDeleteBookmarksDialog @@ -44,7 +44,7 @@ import org.kiwix.sharedFunctions.setScheduler @ExtendWith(InstantExecutorExtension::class) internal class BookmarkViewModelTest { - private val bookmarksDao: NewBookmarksDao = mockk() + private val libkiwixBookMarks: LibkiwixBookmarks = mockk() private val zimReaderContainer: ZimReaderContainer = mockk() private val sharedPreferenceUtil: SharedPreferenceUtil = mockk() @@ -64,9 +64,9 @@ internal class BookmarkViewModelTest { every { zimReaderContainer.id } returns "id" every { zimReaderContainer.name } returns "zimName" every { sharedPreferenceUtil.showBookmarksAllBooks } returns true - every { bookmarksDao.bookmarks() } returns itemsFromDb.distinctUntilChanged() - every { bookmarksDao.pages() } returns bookmarksDao.bookmarks() - viewModel = BookmarkViewModel(bookmarksDao, zimReaderContainer, sharedPreferenceUtil) + every { libkiwixBookMarks.bookmarks() } returns itemsFromDb.distinctUntilChanged() + every { libkiwixBookMarks.pages() } returns libkiwixBookMarks.bookmarks() + viewModel = BookmarkViewModel(libkiwixBookMarks, zimReaderContainer, sharedPreferenceUtil) } @Test @@ -132,7 +132,7 @@ internal class BookmarkViewModelTest { assertThat( viewModel.createDeletePageDialogEffect(bookmarkState()) ).isEqualTo( - ShowDeleteBookmarksDialog(viewModel.effects, bookmarkState(), bookmarksDao) + ShowDeleteBookmarksDialog(viewModel.effects, bookmarkState(), libkiwixBookMarks) ) } From 3d7a4af7d3bc57b55cd3245e2cf5b39f58fbbec3 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Wed, 6 Sep 2023 18:53:01 +0530 Subject: [PATCH 18/38] Fixed application crash while running application on the emulators. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index be6fcc749c..b20b20dd73 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.dao +import android.os.Build import io.reactivex.BackpressureStrategy import io.reactivex.BackpressureStrategy.LATEST import io.reactivex.Flowable @@ -46,9 +47,16 @@ class LibkiwixBookmarks @Inject constructor( } private val bookmarksFolderPath: String by lazy { - sharedPreferenceUtil.getPublicDirectoryPath( - sharedPreferenceUtil.defaultStorage() - ) + "/kiwix/Bookmarks/" + if (Build.DEVICE.contains("generic")) { + // Workaround for emulators: Emulators have limited memory and + // restrictions on creating folders, so we will use the default + // path for saving the bookmark file. + sharedPreferenceUtil.context.filesDir.path + } else { + sharedPreferenceUtil.getPublicDirectoryPath( + sharedPreferenceUtil.defaultStorage() + ) + "/kiwix/Bookmarks/" + } } private val bookmarkFile: File by lazy { From 07e40ee3acdf87122988cfb7978445f7d5a633e8 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Thu, 7 Sep 2023 15:33:04 +0530 Subject: [PATCH 19/38] Improved LibkiwixBookTest and added new ObjectBoxToLibkiwixMigratorTest cases --- .../ObjectBoxToLibkiwixMigratorTest.kt | 153 ++++++++++++++++++ .../page/bookmarks}/LibkiwixBookmarkTest.kt | 17 +- 2 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt rename {core/src/test/java/org/kiwix/kiwixmobile/core/dao => app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks}/LibkiwixBookmarkTest.kt (74%) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt new file mode 100644 index 0000000000..175fa1766b --- /dev/null +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -0,0 +1,153 @@ +/* + * Kiwix Android + * Copyright (c) 2023 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 + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.mockk.mockk +import io.objectbox.Box +import io.objectbox.BoxStore +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.jupiter.api.Test +import org.junit.runner.RunWith +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks +import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity +import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator +import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil + +@RunWith(AndroidJUnit4::class) +class ObjectBoxToLibkiwixMigratorTest { + private var boxStore: BoxStore = mockk() + private var libkiwixBookmarks: LibkiwixBookmarks = mockk(relaxed = true) + private lateinit var objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator + + @Test + fun migrateBookmarkTest(): Unit = runBlocking { + val sharedPreferenceUtil: SharedPreferenceUtil = mockk(relaxed = true) + objectBoxToLibkiwixMigrator = ObjectBoxToLibkiwixMigrator() + objectBoxToLibkiwixMigrator.sharedPreferenceUtil = sharedPreferenceUtil + objectBoxToLibkiwixMigrator.boxStore = boxStore + objectBoxToLibkiwixMigrator.libkiwixBookmarks = libkiwixBookmarks + val box = boxStore.boxFor(BookmarkEntity::class.java) + val expectedZimName = "Alpine_Linux" + val expectedZimId = "8812214350305159407L" + val expectedZimFilePath = "data/Android/kiwix/alpine_linux_2022.zim" + val expectedTitle = "Installing" + val expectedBookmarkUrl = "https://alpine_linux/InstallingPage" + val expectedFavicon = "" + val bookmarkEntity = BookmarkEntity( + 0, + expectedZimId, + expectedZimName, + expectedZimFilePath, + expectedBookmarkUrl, + expectedTitle, + expectedFavicon + ) + box.put(bookmarkEntity) + // migrate data into room database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + // check if data successfully migrated to room + val actual = libkiwixBookmarks.bookmarks().blockingFirst() + assertEquals(actual.size, 1) + assertEquals(actual[0].zimFilePath, expectedZimFilePath) + assertEquals(actual[0].zimId, expectedZimId) + assertEquals(actual[0].favicon, expectedFavicon) + assertEquals(actual[0].title, expectedTitle) + assertEquals(actual[0].url, expectedBookmarkUrl) + + // clear both databases for recent searches to test more edge cases + clearBookmarks(box, libkiwixBookmarks) + // Migrate data from empty ObjectBox database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + val actualData = libkiwixBookmarks.bookmarks().blockingFirst() + assertTrue(actualData.isEmpty()) + + // Test if data successfully migrated to Room and existing data is preserved + val existingTitle = "Home Page" + val existingBookmarkUrl = "https://alpine_linux/HomePage" + val secondBookmarkEntity = BookmarkEntity( + 0, + expectedZimId, + expectedZimName, + expectedZimFilePath, + existingBookmarkUrl, + existingTitle, + expectedFavicon + ) + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(secondBookmarkEntity)) + box.put(bookmarkEntity) + // Migrate data into Room database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + val actualDataAfterMigration = libkiwixBookmarks.bookmarks().blockingFirst() + assertEquals(2, actual.size) + val existingItem = + actualDataAfterMigration.find { + it.url == existingBookmarkUrl && it.title == existingTitle + } + assertNotNull(existingItem) + val newItem = + actualDataAfterMigration.find { + it.url == expectedBookmarkUrl && it.title == expectedTitle + } + assertNotNull(newItem) + + clearBookmarks(box, libkiwixBookmarks) + + // Test large data migration for recent searches + val numEntities = 10000 + // Insert a large number of recent search entities into ObjectBox + for (i in 1..numEntities) { + val bookMarkUrl = "https://alpine_linux/search_$i" + val title = "title_$i" + val bookmarkEntity1 = BookmarkEntity( + 0, + expectedZimId, + expectedZimName, + expectedZimFilePath, + bookMarkUrl, + title, + expectedFavicon + ) + box.put(bookmarkEntity1) + } + val startTime = System.currentTimeMillis() + // Migrate data into Room database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + val endTime = System.currentTimeMillis() + val migrationTime = endTime - startTime + // Check if data successfully migrated to Room + val actualDataAfterLargeMigration = + libkiwixBookmarks.bookmarks().blockingFirst() + assertEquals(numEntities, actualDataAfterLargeMigration.size) + // Assert that the migration completes within a reasonable time frame + assertTrue("Migration took too long: $migrationTime ms", migrationTime < 5000) + } + + private fun clearBookmarks(box: Box, libkiwixBookmark: LibkiwixBookmarks) { + // delete bookmarks for testing other edge cases + libkiwixBookmark.deleteBookmarks( + libkiwixBookmark.bookmarks().blockingFirst() as List + ) + box.removeAll() + } +} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt similarity index 74% rename from core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt rename to app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt index ac09ed95c5..d2b24db37c 100644 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt @@ -16,26 +16,27 @@ * */ -package org.kiwix.kiwixmobile.core.dao +package org.kiwix.kiwixmobile.page.bookmarks import io.mockk.mockk import io.mockk.verify import org.junit.jupiter.api.Test -import org.kiwix.kiwixmobile.core.libkiwix_wrapper.BookmarkWrapper -import org.kiwix.kiwixmobile.core.libkiwix_wrapper.LibraryWrapper -import org.kiwix.kiwixmobile.core.libkiwix_wrapper.ManagerWrapper +import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.libkiwix.Bookmark +import org.kiwix.libkiwix.Library +import org.kiwix.libkiwix.Manager internal class LibkiwixBookmarkTest { - private val library: LibraryWrapper = mockk(relaxed = true) - private val manager = ManagerWrapper(library) + private val library: Library = mockk(relaxed = true) + private val manager = Manager(library) private val sharedPreferenceUtil: SharedPreferenceUtil = mockk(relaxed = true) private val libkiwixBookmarks = LibkiwixBookmarks(library, manager, sharedPreferenceUtil) @Test - fun saveBookmark() { - val bookmark: BookmarkWrapper = mockk(relaxed = true) + internal fun saveBookmark() { + val bookmark: Bookmark = mockk(relaxed = true) libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmark)) verify { library.addBookmark(bookmark) } } From 94a529cdf9e0bc9360965358bbf1ceb71f41d957 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Sat, 16 Sep 2023 19:29:18 +0530 Subject: [PATCH 20/38] Introducing the `writeFile` method of the Library class allows us to save the `library`, which contains information about books, their file paths, and favicons, into a file. * We now save this library information into a file named `library.txt` and subsequently read from it to retrieve file paths and favicons. * The test cases have been refactored to accommodate this new functionality. * The `ObjectBoxToLibkiwiMigrator` code has also been enhanced. With this change, we now save books in the library for their favicon and zimFilePath, resulting in a refactor and improvement of this class's functionality and its associated test cases. * The process of writing bookmarks and library data to file has been enhanced. Now, this is performed asynchronously in a background thread to mitigate potential data loss. * Additionally, several other improvements have been made throughout the codebase. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 4 +- .../page/bookmarks/LibkiwixBookmarkTest.kt | 2 +- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 85 ++++++++++++++++--- .../remote/ObjectBoxToLibkiwixMigrator.kt | 8 +- .../bookmark/adapter/LibkiwixBookmarkItem.kt | 15 ++-- 5 files changed, 92 insertions(+), 22 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index 175fa1766b..02edac1fcc 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -33,6 +33,7 @@ import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.libkiwix.Book @RunWith(AndroidJUnit4::class) class ObjectBoxToLibkiwixMigratorTest { @@ -94,7 +95,8 @@ class ObjectBoxToLibkiwixMigratorTest { existingTitle, expectedFavicon ) - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(secondBookmarkEntity)) + val libkiwixBook: Book = mockk(relaxed = true) + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(secondBookmarkEntity, libkiwixBook)) box.put(bookmarkEntity) // Migrate data into Room database objectBoxToLibkiwixMigrator.migrateBookMarks(box) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt index d2b24db37c..efd778dfb6 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt @@ -37,7 +37,7 @@ internal class LibkiwixBookmarkTest { @Test internal fun saveBookmark() { val bookmark: Bookmark = mockk(relaxed = true) - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmark)) + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmark, null, null)) verify { library.addBookmark(bookmark) } } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index b20b20dd73..54e3ea0a9c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -19,20 +19,26 @@ package org.kiwix.kiwixmobile.core.dao import android.os.Build +import android.util.Base64 import io.reactivex.BackpressureStrategy import io.reactivex.BackpressureStrategy.LATEST import io.reactivex.Flowable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.BehaviorSubject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem +import org.kiwix.kiwixmobile.core.reader.ILLUSTRATION_SIZE import org.kiwix.kiwixmobile.core.reader.ZimFileReader import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.Bookmark import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager +import org.kiwix.libzim.Archive import java.io.File import javax.inject.Inject @@ -42,7 +48,7 @@ class LibkiwixBookmarks @Inject constructor( val sharedPreferenceUtil: SharedPreferenceUtil ) : PageDao { - private val bookmarkListBehaviour: BehaviorSubject>? by lazy { + private val bookmarkListBehaviour: BehaviorSubject>? by lazy { BehaviorSubject.createDefault(getBookmarksList()) } @@ -63,9 +69,17 @@ class LibkiwixBookmarks @Inject constructor( File("$bookmarksFolderPath/bookmark.txt") } + private val libraryFile: File by lazy { + File("$bookmarksFolderPath/library.txt") + } + init { // Check if bookmark folder exist if not then create the folder first. if (!File(bookmarksFolderPath).isFileExist()) File(bookmarksFolderPath).mkdir() + // Check if library file exist if not then create the file to save the library with book information. + if (!libraryFile.isFileExist()) libraryFile.createNewFile() + // set up manager to read the library from this file + manager.readFile(libraryFile.canonicalPath) // Check if bookmark file exist if not then create the file to save the bookmarks. if (!bookmarkFile.isFileExist()) bookmarkFile.createNewFile() // set up manager to read the bookmarks from this file @@ -74,7 +88,7 @@ class LibkiwixBookmarks @Inject constructor( fun bookmarks(): Flowable> = flowableBookmarkList() - .map { it.map(::LibkiwixBookmarkItem) } + .map { it } override fun pages(): Flowable> = bookmarks() @@ -84,16 +98,16 @@ class LibkiwixBookmarks @Inject constructor( fun getCurrentZimBookmarksUrl(zimFileReader: ZimFileReader?): List { return zimFileReader?.let { reader -> getBookmarksList() - .filter { it.bookId == reader.id } - .map { it.url } + .filter { it.zimId == reader.id } + .map(LibkiwixBookmarkItem::bookmarkUrl) } ?: emptyList() } fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader): Flowable> = flowableBookmarkList() .map { bookmarksList -> - bookmarksList.filter { it.bookId == zimFileReader.id } - .map(Bookmark::getUrl) + bookmarksList.filter { it.zimId == zimFileReader.id } + .map(LibkiwixBookmarkItem::bookmarkUrl) } .subscribeOn(Schedulers.io()).also { val book = Book().apply { @@ -112,7 +126,7 @@ class LibkiwixBookmarks @Inject constructor( bookTitle = libkiwixBookmarkItem.libKiwixBook?.title ?: libkiwixBookmarkItem.zimId } library.addBookmark(bookmark).also { - writeBookMarksToFile() + writeBookMarksAndSaveLibraryToFile() updateFlowableBookmarkList() } } @@ -132,25 +146,68 @@ class LibkiwixBookmarks @Inject constructor( fun deleteBookmark(bookId: String, bookmarkUrl: String) { library.removeBookmark(bookId, bookmarkUrl).also { - writeBookMarksToFile() + writeBookMarksAndSaveLibraryToFile() updateFlowableBookmarkList() } } - private fun writeBookMarksToFile() { - library.writeBookmarksToFile(bookmarkFile.canonicalPath) + /** + * Asynchronously writes the library and bookmarks data to their respective files in a background thread + * to prevent potential data loss and ensures that the library holds the updated ZIM file paths and favicons. + */ + private fun writeBookMarksAndSaveLibraryToFile() { + CoroutineScope(Dispatchers.IO).launch { + // Save the library, which contains ZIM file paths and favicons, to a file. + library.writeToFile(libraryFile.canonicalPath) + + // Save the bookmarks data to a separate file. + library.writeBookmarksToFile(bookmarkFile.canonicalPath) + } } - private fun getBookmarksList() = - library.getBookmarks(false)?.toList() ?: emptyList() + private fun getBookmarksList(): List { + // Retrieve the list of bookmarks from the library, or return an empty list if it's null. + val bookmarkList = library.getBookmarks(false)?.toList() ?: return emptyList() + + // Create a list to store LibkiwixBookmarkItem objects. + return bookmarkList.mapNotNull { bookmark -> + // Check if the library contains the book associated with the bookmark. + val book = if (library.booksIds.contains(bookmark.bookId)) { + library.getBookById(bookmark.bookId) + } else { + null + } + + // Create an Archive object for the book's path, if it exists. + val archive: Archive? = book?.let { Archive(it.path) } + + // Check if the Archive has an illustration of the specified size and encode it to Base64. + val favicon = archive?.takeIf { it.hasIllustration(ILLUSTRATION_SIZE) }?.let { + Base64.encodeToString(it.getIllustrationItem(ILLUSTRATION_SIZE).data.data, Base64.DEFAULT) + } + + // Create a LibkiwixBookmarkItem object with bookmark, favicon, and book path. + val libkiwixBookmarkItem = LibkiwixBookmarkItem( + bookmark, + favicon, + book?.path + ) + + // Dispose of the Archive object to release resources. + archive?.dispose() + + // Return the LibkiwixBookmarkItem, filtering out null results. + return@mapNotNull libkiwixBookmarkItem + } + } private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = getBookmarksList() - .any { it.url == libkiwixBookmarkItem.bookmarkUrl && it.bookId == libkiwixBookmarkItem.zimId } + .any { it.url == libkiwixBookmarkItem.bookmarkUrl && it.zimId == libkiwixBookmarkItem.zimId } private fun flowableBookmarkList( backpressureStrategy: BackpressureStrategy = LATEST - ): Flowable> { + ): Flowable> { return Flowable.create({ emitter -> val disposable = bookmarkListBehaviour?.subscribe( { list -> diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index bc09fc4a98..a8884ad44a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -32,6 +32,8 @@ 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.utils.SharedPreferenceUtil +import org.kiwix.libkiwix.Book +import org.kiwix.libzim.Archive import javax.inject.Inject class ObjectBoxToLibkiwixMigrator { @@ -49,7 +51,11 @@ class ObjectBoxToLibkiwixMigrator { val bookMarksList = box.all bookMarksList.forEachIndexed { _, bookmarkEntity -> CoroutineScope(Dispatchers.IO).launch { - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity)) + // for saving book to library, otherwise it does not save the favicon and zimFilePath in library. + val libkiwixBook = Book().apply { + update(Archive(bookmarkEntity.zimFilePath)) + } + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook)) // removing the single entity from the object box after migration. box.query { equal( diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt index a0e038e0fd..b6ab5b6f34 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt @@ -37,13 +37,17 @@ data class LibkiwixBookmarkItem( override val id: Long = databaseId, val libKiwixBook: Book?, ) : Page { - constructor(libkiwixBookmark: Bookmark) : this( + constructor( + libkiwixBookmark: Bookmark, + favicon: String?, + zimFilePath: String? + ) : this( zimId = libkiwixBookmark.bookId, zimName = libkiwixBookmark.bookTitle, - zimFilePath = libkiwixBookmark.url, + zimFilePath = zimFilePath, bookmarkUrl = libkiwixBookmark.url, title = libkiwixBookmark.title, - favicon = null, + favicon = favicon, libKiwixBook = null ) @@ -63,7 +67,8 @@ data class LibkiwixBookmarkItem( ) constructor( - bookmarkEntity: BookmarkEntity + bookmarkEntity: BookmarkEntity, + libkiwixBook: Book ) : this( zimId = bookmarkEntity.zimId, zimFilePath = bookmarkEntity.zimFilePath, @@ -71,6 +76,6 @@ data class LibkiwixBookmarkItem( bookmarkUrl = bookmarkEntity.bookmarkUrl, title = bookmarkEntity.bookmarkTitle, favicon = bookmarkEntity.favicon, - libKiwixBook = null + libKiwixBook = libkiwixBook ) } From a78c4562f22739d45fe00b85e9f48f4d88a0fa68 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Mon, 18 Sep 2023 19:40:20 +0530 Subject: [PATCH 21/38] Removed the unnecessary wrapper classes of libkiwix from the codebase since we don't need these classes as saving/deleting functionality will be tested in java-libkiwix. * Added instrumentation test case for testing the UI part with libkiwix bookmark functionality. --- .../page/bookmarks/BookmarksRobot.kt | 52 ++++++++++ .../page/bookmarks/LibkiwixBookmarkTest.kt | 99 +++++++++++++++---- .../core/libkiwix_wrapper/BookmarkWrapper.kt | 54 ---------- .../core/libkiwix_wrapper/LibraryWrapper.kt | 28 ------ .../core/libkiwix_wrapper/ManagerWrapper.kt | 23 ----- 5 files changed, 134 insertions(+), 122 deletions(-) delete mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt delete mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt delete mode 100644 core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/BookmarksRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/BookmarksRobot.kt index df59cd09a1..19584fd70c 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/BookmarksRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/BookmarksRobot.kt @@ -18,18 +18,29 @@ package org.kiwix.kiwixmobile.page.bookmarks +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions +import androidx.test.espresso.matcher.ViewMatchers.withText import applyWithViewHierarchyPrinting import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed +import com.adevinta.android.barista.interaction.BaristaSleepInteractions import org.kiwix.kiwixmobile.BaseRobot import org.kiwix.kiwixmobile.Findable.StringId.ContentDesc import org.kiwix.kiwixmobile.Findable.StringId.TextId +import org.kiwix.kiwixmobile.Findable.Text +import org.kiwix.kiwixmobile.Findable.ViewId import org.kiwix.kiwixmobile.R +import org.kiwix.kiwixmobile.testutils.TestUtils +import java.util.concurrent.TimeUnit fun bookmarks(func: BookmarksRobot.() -> Unit) = BookmarksRobot().applyWithViewHierarchyPrinting(func) class BookmarksRobot : BaseRobot() { + private var retryCountForBookmarkAddedButton = 5 + fun assertBookMarksDisplayed() { assertDisplayed(R.string.bookmarks_from_current_book) } @@ -41,4 +52,45 @@ class BookmarksRobot : BaseRobot() { fun assertDeleteBookmarksDialogDisplayed() { isVisible(TextId(R.string.delete_bookmarks)) } + + fun clickOnSaveBookmarkImage() { + pauseForBetterTestPerformance() + clickOn(ViewId(R.id.bottom_toolbar_bookmark)) + } + + fun longClickOnSaveBookmarkImage() { + // wait for disappearing the snack-bar after removing the bookmark + BaristaSleepInteractions.sleep(5L, TimeUnit.SECONDS) + longClickOn(ViewId(R.id.bottom_toolbar_bookmark)) + } + + fun clickOnOpenSavedBookmarkButton() { + try { + onView(withText("OPEN")).perform(click()) + } catch (runtimeException: RuntimeException) { + if (retryCountForBookmarkAddedButton > 0) { + retryCountForBookmarkAddedButton-- + clickOnOpenSavedBookmarkButton() + } else { + throw RuntimeException( + "Unable to save the bookmark, original exception is" + + " ${runtimeException.localizedMessage}" + ) + } + } + } + + fun assertBookmarkSaved() { + pauseForBetterTestPerformance() + isVisible(Text("Test Zim")) + } + + fun assertBookmarkRemoved() { + pauseForBetterTestPerformance() + onView(withText("Test Zim")).check(ViewAssertions.doesNotExist()) + } + + private fun pauseForBetterTestPerformance() { + BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS_FOR_SEARCH_TEST.toLong()) + } } diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt index efd778dfb6..27a411b5dd 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt @@ -18,26 +18,91 @@ package org.kiwix.kiwixmobile.page.bookmarks -import io.mockk.mockk -import io.mockk.verify -import org.junit.jupiter.api.Test -import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks -import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem +import androidx.core.content.edit +import androidx.core.net.toUri +import androidx.lifecycle.Lifecycle +import androidx.preference.PreferenceManager +import androidx.test.core.app.ActivityScenario +import androidx.test.internal.runner.junit4.statement.UiThreadStatement +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.kiwix.kiwixmobile.BaseActivityTest +import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil -import org.kiwix.libkiwix.Bookmark -import org.kiwix.libkiwix.Library -import org.kiwix.libkiwix.Manager +import org.kiwix.kiwixmobile.main.KiwixMainActivity +import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragmentDirections +import org.kiwix.kiwixmobile.search.SearchFragmentTest +import org.kiwix.kiwixmobile.testutils.RetryRule +import org.kiwix.kiwixmobile.testutils.TestUtils +import java.io.File +import java.io.FileOutputStream +import java.io.OutputStream -internal class LibkiwixBookmarkTest { - private val library: Library = mockk(relaxed = true) - private val manager = Manager(library) - private val sharedPreferenceUtil: SharedPreferenceUtil = mockk(relaxed = true) - private val libkiwixBookmarks = LibkiwixBookmarks(library, manager, sharedPreferenceUtil) +class LibkiwixBookmarkTest : BaseActivityTest() { + + @Rule + @JvmField + var retryRule = RetryRule() + + private lateinit var kiwixMainActivity: KiwixMainActivity + + @Before + override fun waitForIdle() { + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply { + if (TestUtils.isSystemUINotRespondingDialogVisible(this)) { + TestUtils.closeSystemDialogs(context) + } + waitForIdle() + } + PreferenceManager.getDefaultSharedPreferences(context).edit { + putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false) + putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) + putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) + } + activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { + moveToState(Lifecycle.State.RESUMED) + } + } @Test - internal fun saveBookmark() { - val bookmark: Bookmark = mockk(relaxed = true) - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmark, null, null)) - verify { library.addBookmark(bookmark) } + fun testBookmarks() { + activityScenario.onActivity { + kiwixMainActivity = it + kiwixMainActivity.navigate(R.id.libraryFragment) + } + val loadFileStream = + SearchFragmentTest::class.java.classLoader.getResourceAsStream("testzim.zim") + val zimFile = File(context.cacheDir, "testzim.zim") + if (zimFile.exists()) zimFile.delete() + zimFile.createNewFile() + loadFileStream.use { inputStream -> + val outputStream: OutputStream = FileOutputStream(zimFile) + outputStream.use { it -> + val buffer = ByteArray(inputStream.available()) + var length: Int + while (inputStream.read(buffer).also { length = it } > 0) { + it.write(buffer, 0, length) + } + } + } + + UiThreadStatement.runOnUiThread { + kiwixMainActivity.navigate( + LocalLibraryFragmentDirections.actionNavigationLibraryToNavigationReader() + .apply { zimFileUri = zimFile.toUri().toString() } + ) + } + bookmarks { + clickOnSaveBookmarkImage() + clickOnOpenSavedBookmarkButton() + assertBookmarkSaved() + pressBack() + clickOnSaveBookmarkImage() + longClickOnSaveBookmarkImage() + assertBookmarkRemoved() + } } } diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt deleted file mode 100644 index ecdd9fcdda..0000000000 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/BookmarkWrapper.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2023 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.libkiwix_wrapper - -import org.kiwix.libkiwix.Bookmark - -class BookmarkWrapper : Bookmark() { - override fun getBookId(): String = super.getBookId() - override fun getDate(): String = super.getDate() - override fun getBookTitle(): String = super.getBookTitle() - override fun getLanguage(): String = super.getLanguage() - override fun getTitle(): String = super.getTitle() - override fun getUrl(): String = super.getUrl() - - override fun setBookId(bookId: String?) { - super.setBookId(bookId) - } - - override fun setBookTitle(bookTitle: String?) { - super.setBookTitle(bookTitle) - } - - override fun setDate(Date: String?) { - super.setDate(Date) - } - - override fun setLanguage(language: String?) { - super.setLanguage(language) - } - - override fun setUrl(url: String?) { - super.setUrl(url) - } - - override fun setTitle(title: String?) { - super.setTitle(title) - } -} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt deleted file mode 100644 index 789d0de838..0000000000 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/LibraryWrapper.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2023 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.libkiwix_wrapper - -import org.kiwix.libkiwix.Bookmark -import org.kiwix.libkiwix.Library - -class LibraryWrapper : Library() { - - override fun addBookmark(bookmark: Bookmark?) = - super.addBookmark(bookmark) -} diff --git a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt b/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt deleted file mode 100644 index 55933d785e..0000000000 --- a/core/src/test/java/org/kiwix/kiwixmobile/core/libkiwix_wrapper/ManagerWrapper.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2023 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.libkiwix_wrapper - -import org.kiwix.libkiwix.Manager - -class ManagerWrapper(library: LibraryWrapper) : Manager(library) From a3af901ef69ed6309c2d5c5bfc95a72276444868 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Thu, 28 Sep 2023 17:50:10 +0530 Subject: [PATCH 22/38] Resolved bookmark saving issue, which causes the bug when we try to retrieve the saved bookmarks. * Enhanced the `isBookMarkExist` method, addressing a bug that prevented the addition of new bookmarks for the same file. The method has been refactored for improved functionality. * In the debug version, added informative logs to provide developers with insights into the bookmark-saving functionality. --- core/detekt_baseline.xml | 1 + .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 52 +++++++++++++++++-- .../remote/ObjectBoxToLibkiwixMigrator.kt | 18 +++---- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/core/detekt_baseline.xml b/core/detekt_baseline.xml index d3886eedb9..9d11100032 100644 --- a/core/detekt_baseline.xml +++ b/core/detekt_baseline.xml @@ -68,6 +68,7 @@ ReturnCount:ToolbarScrollingKiwixWebView.kt$ToolbarScrollingKiwixWebView$@SuppressLint("ClickableViewAccessibility") override fun onTouchEvent(event: MotionEvent): Boolean TooGenericExceptionCaught:CompatFindActionModeCallback.kt$CompatFindActionModeCallback$exception: Exception TooGenericExceptionCaught:JNIInitialiser.kt$JNIInitialiser$e: Exception + TooGenericExceptionCaught:LibkiwixBookmarks.kt$LibkiwixBookmarks$exception: Exception TooGenericExceptionCaught:OnSwipeTouchListener.kt$OnSwipeTouchListener.GestureListener$exception: Exception TooGenericExceptionCaught:ZimFileReader.kt$ZimFileReader$exception: Exception TooGenericExceptionThrown:AdapterDelegateManager.kt$AdapterDelegateManager$throw RuntimeException("No delegate registered for $item") diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 54e3ea0a9c..b65fbde58c 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.core.dao import android.os.Build import android.util.Base64 +import android.util.Log import io.reactivex.BackpressureStrategy import io.reactivex.BackpressureStrategy.LATEST import io.reactivex.Flowable @@ -28,6 +29,7 @@ import io.reactivex.subjects.BehaviorSubject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem @@ -44,7 +46,7 @@ import javax.inject.Inject class LibkiwixBookmarks @Inject constructor( val library: Library, - val manager: Manager, + manager: Manager, val sharedPreferenceUtil: SharedPreferenceUtil ) : PageDao { @@ -135,7 +137,17 @@ class LibkiwixBookmarks @Inject constructor( private fun addBookToLibraryIfNotExist(libKiwixBook: Book?) { libKiwixBook?.let { book -> if (!library.booksIds.any { it == book.id }) { - library.addBook(libKiwixBook) + library.addBook(libKiwixBook).also { + if (BuildConfig.DEBUG) { + Log.d( + TAG, + "Added Book to Library:\n" + + "ZIM File Path: ${book.path}\n" + + "Book ID: ${book.id}\n" + + "Book Title: ${book.title}" + ) + } + } } } } @@ -175,17 +187,40 @@ class LibkiwixBookmarks @Inject constructor( val book = if (library.booksIds.contains(bookmark.bookId)) { library.getBookById(bookmark.bookId) } else { + if (BuildConfig.DEBUG) { + Log.d( + TAG, + "Library does not contain the book for this bookmark:\n" + + "Book Title: ${bookmark.bookTitle}\n" + + "Bookmark URL: ${bookmark.url}" + ) + } null } // Create an Archive object for the book's path, if it exists. - val archive: Archive? = book?.let { Archive(it.path) } + val archive: Archive? = book?.run { + try { + Archive(this.path) + } catch (exception: Exception) { + // to handle if zim file not found + // TODO should we delete bookmark if zim file not found? + // deleteBookmark(book.id, bookmark.url) + if (BuildConfig.DEBUG) { + Log.e( + TAG, + "Failed to create an archive for path: ${book.path}\n" + + "Exception: $exception" + ) + } + null + } + } // Check if the Archive has an illustration of the specified size and encode it to Base64. val favicon = archive?.takeIf { it.hasIllustration(ILLUSTRATION_SIZE) }?.let { Base64.encodeToString(it.getIllustrationItem(ILLUSTRATION_SIZE).data.data, Base64.DEFAULT) } - // Create a LibkiwixBookmarkItem object with bookmark, favicon, and book path. val libkiwixBookmarkItem = LibkiwixBookmarkItem( bookmark, @@ -203,7 +238,10 @@ class LibkiwixBookmarks @Inject constructor( private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = getBookmarksList() - .any { it.url == libkiwixBookmarkItem.bookmarkUrl && it.zimId == libkiwixBookmarkItem.zimId } + .any { + it.url == libkiwixBookmarkItem.bookmarkUrl && + it.zimFilePath == libkiwixBookmarkItem.zimFilePath + } private fun flowableBookmarkList( backpressureStrategy: BackpressureStrategy = LATEST @@ -226,4 +264,8 @@ class LibkiwixBookmarks @Inject constructor( private fun updateFlowableBookmarkList() { bookmarkListBehaviour?.onNext(getBookmarksList()) } + + companion object { + const val TAG = "LibkiwixBookmark" + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index a8884ad44a..036891feb8 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -21,15 +21,12 @@ package org.kiwix.kiwixmobile.core.data.remote import io.objectbox.Box import io.objectbox.BoxStore import io.objectbox.kotlin.boxFor -import io.objectbox.kotlin.query -import io.objectbox.query.QueryBuilder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks 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.utils.SharedPreferenceUtil import org.kiwix.libkiwix.Book @@ -56,14 +53,15 @@ class ObjectBoxToLibkiwixMigrator { update(Archive(bookmarkEntity.zimFilePath)) } libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook)) + // TODO should we remove data from objectBox? // removing the single entity from the object box after migration. - box.query { - equal( - BookmarkEntity_.bookmarkUrl, - bookmarkEntity.bookmarkUrl, - QueryBuilder.StringOrder.CASE_INSENSITIVE - ) - }.remove() + // box.query { + // equal( + // BookmarkEntity_.bookmarkUrl, + // bookmarkEntity.bookmarkUrl, + // QueryBuilder.StringOrder.CASE_INSENSITIVE + // ) + // }.remove() } } sharedPreferenceUtil.putPrefBookMarkMigrated(true) From 1f3cfd67394af1f88cf96a6a17d828d2ff4c4658 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 3 Nov 2023 17:01:43 +0530 Subject: [PATCH 23/38] Fixed, compilation error --- .../java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt index a937bff6bf..8a8d6c9ae9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt @@ -106,13 +106,11 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { exitProcess(KIWIX_INTERNAL_ERROR) } } -<<<<<<< HEAD + setMainActivityToCoreApp() -======= if (!sharedPreferenceUtil.prefIsBookmarksMigrated) { objectBoxToLibkiwixMigrator.migrateBookmarksToLibkiwix() } ->>>>>>> 371eb5c2d (Created ObjectBox to libkiwix migrator for Bookmarks) } @Suppress("DEPRECATION") From 6c5afa9413c3249adac9829820e8aadb8b4cf673 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 3 Nov 2023 18:35:46 +0530 Subject: [PATCH 24/38] The writing of the library to a file when retrieving bookmarks for the current book has been removed because adding the book to the library is unnecessary, as we have not saved any bookmarks yet. * We now write bookmarks and the library on the main thread instead of saving them in the background thread to prevent any data loss. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index b65fbde58c..0a1a61365a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -26,9 +26,6 @@ import io.reactivex.BackpressureStrategy.LATEST import io.reactivex.Flowable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.BehaviorSubject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page @@ -111,12 +108,7 @@ class LibkiwixBookmarks @Inject constructor( bookmarksList.filter { it.zimId == zimFileReader.id } .map(LibkiwixBookmarkItem::bookmarkUrl) } - .subscribeOn(Schedulers.io()).also { - val book = Book().apply { - update(zimFileReader.jniKiwixReader) - } - addBookToLibraryIfNotExist(book) - } + .subscribeOn(Schedulers.io()) fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { if (!isBookMarkExist(libkiwixBookmarkItem)) { @@ -168,13 +160,11 @@ class LibkiwixBookmarks @Inject constructor( * to prevent potential data loss and ensures that the library holds the updated ZIM file paths and favicons. */ private fun writeBookMarksAndSaveLibraryToFile() { - CoroutineScope(Dispatchers.IO).launch { - // Save the library, which contains ZIM file paths and favicons, to a file. - library.writeToFile(libraryFile.canonicalPath) + // Save the library, which contains ZIM file paths and favicons, to a file. + library.writeToFile(libraryFile.canonicalPath) - // Save the bookmarks data to a separate file. - library.writeBookmarksToFile(bookmarkFile.canonicalPath) - } + // Save the bookmarks data to a separate file. + library.writeBookmarksToFile(bookmarkFile.canonicalPath) } private fun getBookmarksList(): List { From aaa9cc368cd07652ba4f7707dc6071f17f1c317a Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 7 Nov 2023 18:46:31 +0530 Subject: [PATCH 25/38] Added logs while reading reading the bookmarks/library data, and added logs before writing the library in file. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 0a1a61365a..d368e59df9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -65,11 +65,11 @@ class LibkiwixBookmarks @Inject constructor( } private val bookmarkFile: File by lazy { - File("$bookmarksFolderPath/bookmark.txt") + File("$bookmarksFolderPath/bookmark.xml") } private val libraryFile: File by lazy { - File("$bookmarksFolderPath/library.txt") + File("$bookmarksFolderPath/library.xml") } init { @@ -83,6 +83,21 @@ class LibkiwixBookmarks @Inject constructor( if (!bookmarkFile.isFileExist()) bookmarkFile.createNewFile() // set up manager to read the bookmarks from this file manager.readBookmarkFile(bookmarkFile.canonicalPath) + + library.booksIds + .asSequence() + .map(library::getBookById) + .forEach { + Log.e( + TAG, + "readLibraryFromFile: " + + "reading books from a file via the manager.readFile() method current books size" + + " current books size in the library is = ${library.booksIds.size}\n" + + "book path = ${it.path}\n" + + "book title = ${it.title}\n" + + "book id = ${it.name}\n", + ) + } } fun bookmarks(): Flowable> = @@ -160,6 +175,20 @@ class LibkiwixBookmarks @Inject constructor( * to prevent potential data loss and ensures that the library holds the updated ZIM file paths and favicons. */ private fun writeBookMarksAndSaveLibraryToFile() { + library.booksIds + .asSequence() + .map(library::getBookById) + .forEach { + Log.e( + TAG, + "writeBookMarksAndSaveLibraryToFile:" + + " Trying to write a file with the library.writeFile() method." + + " current books size in the library is = ${library.booksIds.size}\n" + + "book path = ${it.path}\n" + + "book title = ${it.title}\n" + + "book id = ${it.name}\n", + ) + } // Save the library, which contains ZIM file paths and favicons, to a file. library.writeToFile(libraryFile.canonicalPath) From d821d5a07b9ea23b60e69edcfdc10c1462bf1e81 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Tue, 7 Nov 2023 19:57:27 +0530 Subject: [PATCH 26/38] Fixed path and improved the logs --- .../org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index d368e59df9..667f7a0165 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -60,7 +60,7 @@ class LibkiwixBookmarks @Inject constructor( } else { sharedPreferenceUtil.getPublicDirectoryPath( sharedPreferenceUtil.defaultStorage() - ) + "/kiwix/Bookmarks/" + ) + "/Kiwix/Bookmarks/" } } @@ -95,7 +95,7 @@ class LibkiwixBookmarks @Inject constructor( " current books size in the library is = ${library.booksIds.size}\n" + "book path = ${it.path}\n" + "book title = ${it.title}\n" + - "book id = ${it.name}\n", + "book id = ${it.id}\n", ) } } @@ -186,7 +186,7 @@ class LibkiwixBookmarks @Inject constructor( " current books size in the library is = ${library.booksIds.size}\n" + "book path = ${it.path}\n" + "book title = ${it.title}\n" + - "book id = ${it.name}\n", + "book id = ${it.id}\n", ) } // Save the library, which contains ZIM file paths and favicons, to a file. From 0d48e23da51c14300066240ae2674a3c86eb135e Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 10 Nov 2023 12:26:13 +0530 Subject: [PATCH 27/38] Instead of creating an archive object for every book and retrieving the favicon from it, we are now speeding up the process by directly taking the favicon from the book, as the issue at https://github.com/kiwix/java-libkiwix/issues/73 has been resolved. --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 667f7a0165..a06f50dd2a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -37,7 +37,6 @@ import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.Bookmark import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager -import org.kiwix.libzim.Archive import java.io.File import javax.inject.Inject @@ -217,41 +216,17 @@ class LibkiwixBookmarks @Inject constructor( null } - // Create an Archive object for the book's path, if it exists. - val archive: Archive? = book?.run { - try { - Archive(this.path) - } catch (exception: Exception) { - // to handle if zim file not found - // TODO should we delete bookmark if zim file not found? - // deleteBookmark(book.id, bookmark.url) - if (BuildConfig.DEBUG) { - Log.e( - TAG, - "Failed to create an archive for path: ${book.path}\n" + - "Exception: $exception" - ) - } - null - } + // Check if the book has an illustration of the specified size and encode it to Base64. + val favicon = book?.getIllustration(ILLUSTRATION_SIZE)?.data?.let { + Base64.encodeToString(it, Base64.DEFAULT) } - // Check if the Archive has an illustration of the specified size and encode it to Base64. - val favicon = archive?.takeIf { it.hasIllustration(ILLUSTRATION_SIZE) }?.let { - Base64.encodeToString(it.getIllustrationItem(ILLUSTRATION_SIZE).data.data, Base64.DEFAULT) - } - // Create a LibkiwixBookmarkItem object with bookmark, favicon, and book path. - val libkiwixBookmarkItem = LibkiwixBookmarkItem( + // Return the LibkiwixBookmarkItem, filtering out null results. + return@mapNotNull LibkiwixBookmarkItem( bookmark, favicon, book?.path ) - - // Dispose of the Archive object to release resources. - archive?.dispose() - - // Return the LibkiwixBookmarkItem, filtering out null results. - return@mapNotNull libkiwixBookmarkItem } } From ecc0d4b484ad0681145a8affad1ffb5c41594f1f Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 10 Nov 2023 12:28:53 +0530 Subject: [PATCH 28/38] Removed unnecessary comments --- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index a06f50dd2a..a6ccf4bdba 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -82,21 +82,6 @@ class LibkiwixBookmarks @Inject constructor( if (!bookmarkFile.isFileExist()) bookmarkFile.createNewFile() // set up manager to read the bookmarks from this file manager.readBookmarkFile(bookmarkFile.canonicalPath) - - library.booksIds - .asSequence() - .map(library::getBookById) - .forEach { - Log.e( - TAG, - "readLibraryFromFile: " + - "reading books from a file via the manager.readFile() method current books size" + - " current books size in the library is = ${library.booksIds.size}\n" + - "book path = ${it.path}\n" + - "book title = ${it.title}\n" + - "book id = ${it.id}\n", - ) - } } fun bookmarks(): Flowable> = @@ -174,20 +159,6 @@ class LibkiwixBookmarks @Inject constructor( * to prevent potential data loss and ensures that the library holds the updated ZIM file paths and favicons. */ private fun writeBookMarksAndSaveLibraryToFile() { - library.booksIds - .asSequence() - .map(library::getBookById) - .forEach { - Log.e( - TAG, - "writeBookMarksAndSaveLibraryToFile:" + - " Trying to write a file with the library.writeFile() method." + - " current books size in the library is = ${library.booksIds.size}\n" + - "book path = ${it.path}\n" + - "book title = ${it.title}\n" + - "book id = ${it.id}\n", - ) - } // Save the library, which contains ZIM file paths and favicons, to a file. library.writeToFile(libraryFile.canonicalPath) From 8666675acc89d6e2c1c0305f3ed8491d63c981ba Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 10 Nov 2023 12:53:32 +0530 Subject: [PATCH 29/38] Storing bookmarks/library inside our app-specific directory --- .../java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index a6ccf4bdba..253bd13687 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -57,9 +57,7 @@ class LibkiwixBookmarks @Inject constructor( // path for saving the bookmark file. sharedPreferenceUtil.context.filesDir.path } else { - sharedPreferenceUtil.getPublicDirectoryPath( - sharedPreferenceUtil.defaultStorage() - ) + "/Kiwix/Bookmarks/" + "${sharedPreferenceUtil.defaultStorage()}/Bookmarks/" } } From 60966b9a52cb73471c2d132eb0411415f0dfb386 Mon Sep 17 00:00:00 2001 From: MohitMali Date: Thu, 16 Nov 2023 12:54:56 +0530 Subject: [PATCH 30/38] Getting zimFilePath efficiently for `LibkiwixBookmarkItem`. * Removed unused imports. --- .../java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt | 1 - .../core/page/bookmark/adapter/LibkiwixBookmarkItem.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt index ec02a67dec..4a7b38af44 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreReaderFragment.kt @@ -108,7 +108,6 @@ import org.kiwix.kiwixmobile.core.StorageObserver import org.kiwix.kiwixmobile.core.base.BaseFragment import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks -import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt index b6ab5b6f34..e04b042280 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/page/bookmark/adapter/LibkiwixBookmarkItem.kt @@ -57,7 +57,7 @@ data class LibkiwixBookmarkItem( zimFileReader: ZimFileReader, libKiwixBook: Book ) : this( - zimFilePath = zimFileReader.zimFile.canonicalPath, + zimFilePath = zimFileReader.zimFile?.canonicalPath, zimId = libKiwixBook.id, zimName = libKiwixBook.name, bookmarkUrl = articleUrl, From e866dd29b50da46698a98f24c7867057ced37902 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Thu, 4 Jan 2024 18:49:26 +0530 Subject: [PATCH 31/38] Writing bookmarks and library data to a file in the background to avoid impacting application performance. --- .../kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 253bd13687..50bfe90830 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -26,6 +26,9 @@ import io.reactivex.BackpressureStrategy.LATEST import io.reactivex.Flowable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.BehaviorSubject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.extensions.isFileExist import org.kiwix.kiwixmobile.core.page.adapter.Page @@ -157,11 +160,13 @@ class LibkiwixBookmarks @Inject constructor( * to prevent potential data loss and ensures that the library holds the updated ZIM file paths and favicons. */ private fun writeBookMarksAndSaveLibraryToFile() { - // Save the library, which contains ZIM file paths and favicons, to a file. - library.writeToFile(libraryFile.canonicalPath) + CoroutineScope(Dispatchers.IO).launch { + // Save the library, which contains ZIM file paths and favicons, to a file. + library.writeToFile(libraryFile.canonicalPath) - // Save the bookmarks data to a separate file. - library.writeBookmarksToFile(bookmarkFile.canonicalPath) + // Save the bookmarks data to a separate file. + library.writeBookmarksToFile(bookmarkFile.canonicalPath) + } } private fun getBookmarksList(): List { From f45e3216554dab28a010a50b3fb68b88f86dec8a Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Fri, 5 Jan 2024 19:31:49 +0530 Subject: [PATCH 32/38] Refactored the test cases. * Improved the instrumentation test cases. * Improved the migration test case. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 129 ++++++++++++++---- .../page/bookmarks/LibkiwixBookmarkTest.kt | 13 ++ 2 files changed, 114 insertions(+), 28 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index 02edac1fcc..9d7254fd91 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -1,6 +1,6 @@ /* * Kiwix Android - * Copyright (c) 2023 Kiwix + * Copyright (c) 2024 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 @@ -18,40 +18,105 @@ package org.kiwix.kiwixmobile +import androidx.core.content.edit +import androidx.lifecycle.Lifecycle +import androidx.preference.PreferenceManager +import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.mockk.mockk +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.adevinta.android.barista.interaction.BaristaSleepInteractions import io.objectbox.Box import io.objectbox.BoxStore import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertTrue -import org.junit.jupiter.api.Test +import org.junit.Before +import org.junit.Test +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.runner.RunWith import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator +import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil +import org.kiwix.kiwixmobile.main.KiwixMainActivity +import org.kiwix.kiwixmobile.testutils.TestUtils import org.kiwix.libkiwix.Book +import org.kiwix.libkiwix.Library +import org.kiwix.libkiwix.Manager +import java.io.File +import java.io.FileOutputStream +import java.io.OutputStream @RunWith(AndroidJUnit4::class) -class ObjectBoxToLibkiwixMigratorTest { - private var boxStore: BoxStore = mockk() - private var libkiwixBookmarks: LibkiwixBookmarks = mockk(relaxed = true) - private lateinit var objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator +class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { + private val objectBoxToLibkiwixMigrator = ObjectBoxToLibkiwixMigrator() + + // take the existing boxStore object + private val boxStore: BoxStore? = DatabaseModule.boxStore + + // @Rule + // @JvmField + // var retryRule = RetryRule() + + @Before + override fun waitForIdle() { + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply { + if (TestUtils.isSystemUINotRespondingDialogVisible(this)) { + TestUtils.closeSystemDialogs(context) + } + waitForIdle() + } + PreferenceManager.getDefaultSharedPreferences(context).edit { + putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false) + putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) + putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) + putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) + } + activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { + moveToState(Lifecycle.State.RESUMED) + } + } @Test fun migrateBookmarkTest(): Unit = runBlocking { - val sharedPreferenceUtil: SharedPreferenceUtil = mockk(relaxed = true) - objectBoxToLibkiwixMigrator = ObjectBoxToLibkiwixMigrator() - objectBoxToLibkiwixMigrator.sharedPreferenceUtil = sharedPreferenceUtil - objectBoxToLibkiwixMigrator.boxStore = boxStore - objectBoxToLibkiwixMigrator.libkiwixBookmarks = libkiwixBookmarks + if (boxStore == null) { + throw RuntimeException( + "BoxStore is not available for testing," + + " check is your application running" + ) + } val box = boxStore.boxFor(BookmarkEntity::class.java) + val library = Library() + val manager = Manager(library) + val sharedPreferenceUtil = SharedPreferenceUtil(context) + objectBoxToLibkiwixMigrator.libkiwixBookmarks = + LibkiwixBookmarks(library, manager, sharedPreferenceUtil) + objectBoxToLibkiwixMigrator.sharedPreferenceUtil = SharedPreferenceUtil(context) + + // add a file in fileSystem because we need to actual file path for making object of Archive. + val loadFileStream = + ObjectBoxToLibkiwixMigratorTest::class.java.classLoader.getResourceAsStream("testzim.zim") + val zimFile = File(context.cacheDir, "testzim.zim") + if (zimFile.exists()) zimFile.delete() + zimFile.createNewFile() + loadFileStream.use { inputStream -> + val outputStream: OutputStream = FileOutputStream(zimFile) + outputStream.use { it -> + val buffer = ByteArray(inputStream.available()) + var length: Int + while (inputStream.read(buffer).also { length = it } > 0) { + it.write(buffer, 0, length) + } + } + } + // clear the data before running the test case + clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) val expectedZimName = "Alpine_Linux" - val expectedZimId = "8812214350305159407L" - val expectedZimFilePath = "data/Android/kiwix/alpine_linux_2022.zim" + val expectedZimId = "60094d1e-1c9a-a60b-2011-4fb02f8db6c3" + val expectedZimFilePath = zimFile.canonicalPath val expectedTitle = "Installing" val expectedBookmarkUrl = "https://alpine_linux/InstallingPage" val expectedFavicon = "" @@ -67,20 +132,20 @@ class ObjectBoxToLibkiwixMigratorTest { box.put(bookmarkEntity) // migrate data into room database objectBoxToLibkiwixMigrator.migrateBookMarks(box) + BaristaSleepInteractions.sleep(2000L) // check if data successfully migrated to room - val actual = libkiwixBookmarks.bookmarks().blockingFirst() + val actual = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() assertEquals(actual.size, 1) assertEquals(actual[0].zimFilePath, expectedZimFilePath) assertEquals(actual[0].zimId, expectedZimId) - assertEquals(actual[0].favicon, expectedFavicon) assertEquals(actual[0].title, expectedTitle) assertEquals(actual[0].url, expectedBookmarkUrl) // clear both databases for recent searches to test more edge cases - clearBookmarks(box, libkiwixBookmarks) + clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) // Migrate data from empty ObjectBox database objectBoxToLibkiwixMigrator.migrateBookMarks(box) - val actualData = libkiwixBookmarks.bookmarks().blockingFirst() + val actualData = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() assertTrue(actualData.isEmpty()) // Test if data successfully migrated to Room and existing data is preserved @@ -95,13 +160,21 @@ class ObjectBoxToLibkiwixMigratorTest { existingTitle, expectedFavicon ) - val libkiwixBook: Book = mockk(relaxed = true) - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(secondBookmarkEntity, libkiwixBook)) + val libkiwixBook = Book() + objectBoxToLibkiwixMigrator.libkiwixBookmarks.saveBookmark( + LibkiwixBookmarkItem( + secondBookmarkEntity, + libkiwixBook + ) + ) + BaristaSleepInteractions.sleep(2000L) box.put(bookmarkEntity) // Migrate data into Room database objectBoxToLibkiwixMigrator.migrateBookMarks(box) - val actualDataAfterMigration = libkiwixBookmarks.bookmarks().blockingFirst() - assertEquals(2, actual.size) + BaristaSleepInteractions.sleep(2000L) + val actualDataAfterMigration = + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() + assertEquals(1, actual.size) val existingItem = actualDataAfterMigration.find { it.url == existingBookmarkUrl && it.title == existingTitle @@ -113,7 +186,7 @@ class ObjectBoxToLibkiwixMigratorTest { } assertNotNull(newItem) - clearBookmarks(box, libkiwixBookmarks) + clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) // Test large data migration for recent searches val numEntities = 10000 @@ -139,10 +212,10 @@ class ObjectBoxToLibkiwixMigratorTest { val migrationTime = endTime - startTime // Check if data successfully migrated to Room val actualDataAfterLargeMigration = - libkiwixBookmarks.bookmarks().blockingFirst() + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() assertEquals(numEntities, actualDataAfterLargeMigration.size) // Assert that the migration completes within a reasonable time frame - assertTrue("Migration took too long: $migrationTime ms", migrationTime < 5000) + assertTrue(migrationTime < 5000) } private fun clearBookmarks(box: Box, libkiwixBookmark: LibkiwixBookmarks) { diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt index 27a411b5dd..11b45bdc78 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt @@ -96,13 +96,26 @@ class LibkiwixBookmarkTest : BaseActivityTest() { ) } bookmarks { + // Test saving bookmark clickOnSaveBookmarkImage() clickOnOpenSavedBookmarkButton() assertBookmarkSaved() pressBack() + // Test removing bookmark clickOnSaveBookmarkImage() longClickOnSaveBookmarkImage() assertBookmarkRemoved() + pressBack() + // Save the bookmark to test whether it remains saved after the application restarts or not. + clickOnSaveBookmarkImage() + } + } + + @Test + fun testBookmarkRemainsSavedOrNot() { + bookmarks { + longClickOnSaveBookmarkImage() + assertBookmarkSaved() } } } From efd8caf244456409280d5daee786372e2e25d54f Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 9 Jan 2024 11:40:41 +0530 Subject: [PATCH 33/38] Enhanced the migration process to handle exceptions more effectively. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 2 +- .../remote/ObjectBoxToLibkiwixMigrator.kt | 39 ++++++++++++------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index 9d7254fd91..7669903bad 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -174,7 +174,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { BaristaSleepInteractions.sleep(2000L) val actualDataAfterMigration = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() - assertEquals(1, actual.size) + assertEquals(2, actualDataAfterMigration.size) val existingItem = actualDataAfterMigration.find { it.url == existingBookmarkUrl && it.title == existingTitle diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index 036891feb8..b094108772 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.core.data.remote +import android.util.Log import io.objectbox.Box import io.objectbox.BoxStore import io.objectbox.kotlin.boxFor @@ -48,20 +49,32 @@ class ObjectBoxToLibkiwixMigrator { val bookMarksList = box.all bookMarksList.forEachIndexed { _, bookmarkEntity -> CoroutineScope(Dispatchers.IO).launch { - // for saving book to library, otherwise it does not save the favicon and zimFilePath in library. - val libkiwixBook = Book().apply { - update(Archive(bookmarkEntity.zimFilePath)) + // moving this to handle the exceptions thrown by the libkiwix if any occur, + // like if path is not validate due to user move the ZIM file to another location etc. + try { + // for saving book to library, otherwise it does not save the + // favicon and zimFilePath in library. + val libkiwixBook = Book().apply { + update(Archive(bookmarkEntity.zimFilePath)) + } + libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook)) + // TODO should we remove data from objectBox? + // removing the single entity from the object box after migration. + // box.query { + // equal( + // BookmarkEntity_.bookmarkUrl, + // bookmarkEntity.bookmarkUrl, + // QueryBuilder.StringOrder.CASE_INSENSITIVE + // ) + // }.remove() + } catch (ignore: Exception) { + Log.e( + "MIGRATING_BOOKMARKS", + "there is an error while migrating the bookmark for\n" + + " ZIM file = ${bookmarkEntity.zimFilePath} \n" + + "Bookmark Title = ${bookmarkEntity.bookmarkTitle}" + ) } - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook)) - // TODO should we remove data from objectBox? - // removing the single entity from the object box after migration. - // box.query { - // equal( - // BookmarkEntity_.bookmarkUrl, - // bookmarkEntity.bookmarkUrl, - // QueryBuilder.StringOrder.CASE_INSENSITIVE - // ) - // }.remove() } } sharedPreferenceUtil.putPrefBookMarkMigrated(true) From 99cf75bf599a92802eb61cf9a490041bc7b2b44a Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 10 Jan 2024 19:01:20 +0530 Subject: [PATCH 34/38] Improved the large data migration. * Refined test cases to thoroughly assess the migration process. * Enhanced the migration logic to effectively manage large datasets during migration. * Optimized the Bookmark functionality to minimize unnecessary data loading on libkiwix. Retrieving data from libkiwix is now performed only when there is a change in the bookmark, reducing redundant data fetches. Otherwise, the previously loaded data is returned to minimize unnecessary resource access. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 230 +++++++++++++----- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 55 ++++- .../remote/ObjectBoxToLibkiwixMigrator.kt | 20 +- .../kiwixmobile/core/main/CoreMainActivity.kt | 8 +- 4 files changed, 232 insertions(+), 81 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index 7669903bad..f4fa1d9d2d 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -22,19 +22,26 @@ import androidx.core.content.edit import androidx.lifecycle.Lifecycle import androidx.preference.PreferenceManager import androidx.test.core.app.ActivityScenario -import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.espresso.IdlingPolicies +import androidx.test.espresso.IdlingRegistry import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice -import com.adevinta.android.barista.interaction.BaristaSleepInteractions import io.objectbox.Box import io.objectbox.BoxStore +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.junit.After import org.junit.Before +import org.junit.BeforeClass +import org.junit.Rule import org.junit.Test import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.runner.RunWith +import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator @@ -42,24 +49,45 @@ import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.main.KiwixMainActivity +import org.kiwix.kiwixmobile.testutils.RetryRule import org.kiwix.kiwixmobile.testutils.TestUtils +import org.kiwix.kiwixmobile.utils.KiwixIdlingResource import org.kiwix.libkiwix.Book import org.kiwix.libkiwix.Library import org.kiwix.libkiwix.Manager import java.io.File import java.io.FileOutputStream import java.io.OutputStream +import java.util.concurrent.TimeUnit -@RunWith(AndroidJUnit4::class) class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { private val objectBoxToLibkiwixMigrator = ObjectBoxToLibkiwixMigrator() // take the existing boxStore object private val boxStore: BoxStore? = DatabaseModule.boxStore + private lateinit var zimFile: File + private lateinit var box: Box + private val expectedZimName = "Alpine_Linux" + private val expectedZimId = "60094d1e-1c9a-a60b-2011-4fb02f8db6c3" + private val expectedZimFilePath: String by lazy { zimFile.canonicalPath } + private val expectedTitle = "Installing" + private val expectedBookmarkUrl = "https://alpine_linux/InstallingPage" + private val expectedFavicon = "" + private val bookmarkEntity: BookmarkEntity by lazy { + BookmarkEntity( + 0, + expectedZimId, + expectedZimName, + expectedZimFilePath, + expectedBookmarkUrl, + expectedTitle, + expectedFavicon + ) + } - // @Rule - // @JvmField - // var retryRule = RetryRule() + @Rule + @JvmField + var retryRule = RetryRule() @Before override fun waitForIdle() { @@ -78,17 +106,18 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) } + CoreApp.coreComponent.inject(objectBoxToLibkiwixMigrator) + setUpObjectBoxAndData() } - @Test - fun migrateBookmarkTest(): Unit = runBlocking { + private fun setUpObjectBoxAndData() { if (boxStore == null) { throw RuntimeException( "BoxStore is not available for testing," + " check is your application running" ) } - val box = boxStore.boxFor(BookmarkEntity::class.java) + box = boxStore.boxFor(BookmarkEntity::class.java) val library = Library() val manager = Manager(library) val sharedPreferenceUtil = SharedPreferenceUtil(context) @@ -99,7 +128,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { // add a file in fileSystem because we need to actual file path for making object of Archive. val loadFileStream = ObjectBoxToLibkiwixMigratorTest::class.java.classLoader.getResourceAsStream("testzim.zim") - val zimFile = File(context.cacheDir, "testzim.zim") + zimFile = File(context.cacheDir, "testzim.zim") if (zimFile.exists()) zimFile.delete() zimFile.createNewFile() loadFileStream.use { inputStream -> @@ -112,8 +141,13 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { } } } + // clear the data before running the test case clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) + } + + @Test + fun testSingleDataMigration(): Unit = runBlocking { val expectedZimName = "Alpine_Linux" val expectedZimId = "60094d1e-1c9a-a60b-2011-4fb02f8db6c3" val expectedZimFilePath = zimFile.canonicalPath @@ -130,24 +164,53 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { expectedFavicon ) box.put(bookmarkEntity) - // migrate data into room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - BaristaSleepInteractions.sleep(2000L) + withContext(Dispatchers.IO) { + // migrate data into room database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + } // check if data successfully migrated to room - val actual = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() - assertEquals(actual.size, 1) - assertEquals(actual[0].zimFilePath, expectedZimFilePath) - assertEquals(actual[0].zimId, expectedZimId) - assertEquals(actual[0].title, expectedTitle) - assertEquals(actual[0].url, expectedBookmarkUrl) - - // clear both databases for recent searches to test more edge cases - clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) - // Migrate data from empty ObjectBox database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - val actualData = objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() - assertTrue(actualData.isEmpty()) + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { actualDataAfterMigration -> + assertEquals(1, actualDataAfterMigration.size) + assertEquals(actualDataAfterMigration[0].zimFilePath, expectedZimFilePath) + assertEquals(actualDataAfterMigration[0].zimId, expectedZimId) + assertEquals(actualDataAfterMigration[0].title, expectedTitle) + assertEquals(actualDataAfterMigration[0].url, expectedBookmarkUrl) + }, + { + throw RuntimeException( + "Exception occurred during migration. Original Exception ${it.printStackTrace()}" + ) + } + ) + } + + @Test + fun testMigrationWithEmptyData(): Unit = runBlocking { + withContext(Dispatchers.IO) { + // Migrate data from empty ObjectBox database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + } + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { actualDataAfterMigration -> + assertTrue(actualDataAfterMigration.isEmpty()) + }, + { + throw RuntimeException( + "Exception occurred during migration. Original Exception ${it.printStackTrace()}" + ) + } + ) + } + @Test + fun testMigrationWithExistingData(): Unit = runBlocking { // Test if data successfully migrated to Room and existing data is preserved val existingTitle = "Home Page" val existingBookmarkUrl = "https://alpine_linux/HomePage" @@ -161,40 +224,53 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { expectedFavicon ) val libkiwixBook = Book() - objectBoxToLibkiwixMigrator.libkiwixBookmarks.saveBookmark( - LibkiwixBookmarkItem( - secondBookmarkEntity, - libkiwixBook + withContext(Dispatchers.IO) { + objectBoxToLibkiwixMigrator.libkiwixBookmarks.saveBookmark( + LibkiwixBookmarkItem( + secondBookmarkEntity, + libkiwixBook + ) ) - ) - BaristaSleepInteractions.sleep(2000L) - box.put(bookmarkEntity) - // Migrate data into Room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - BaristaSleepInteractions.sleep(2000L) - val actualDataAfterMigration = - objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() - assertEquals(2, actualDataAfterMigration.size) - val existingItem = - actualDataAfterMigration.find { - it.url == existingBookmarkUrl && it.title == existingTitle - } - assertNotNull(existingItem) - val newItem = - actualDataAfterMigration.find { - it.url == expectedBookmarkUrl && it.title == expectedTitle - } - assertNotNull(newItem) - - clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) + box.put(bookmarkEntity) + } + withContext(Dispatchers.IO) { + // Migrate data into Room database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) + } + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { actualDataAfterMigration -> + assertEquals(2, actualDataAfterMigration.size) + val existingItem = + actualDataAfterMigration.find { + it.url == existingBookmarkUrl && it.title == existingTitle + } + assertNotNull(existingItem) + val newItem = + actualDataAfterMigration.find { + it.url == expectedBookmarkUrl && it.title == expectedTitle + } + assertNotNull(newItem) + }, + { + throw RuntimeException( + "Exception occurred during migration. Original Exception ${it.printStackTrace()}" + ) + } + ) + } + @Test + fun testLargeDataMigration(): Unit = runBlocking { // Test large data migration for recent searches val numEntities = 10000 // Insert a large number of recent search entities into ObjectBox for (i in 1..numEntities) { val bookMarkUrl = "https://alpine_linux/search_$i" val title = "title_$i" - val bookmarkEntity1 = BookmarkEntity( + val bookmarkEntity = BookmarkEntity( 0, expectedZimId, expectedZimName, @@ -203,19 +279,26 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { title, expectedFavicon ) - box.put(bookmarkEntity1) + box.put(bookmarkEntity) + } + withContext(Dispatchers.IO) { + // Migrate data into Room database + objectBoxToLibkiwixMigrator.migrateBookMarks(box) } - val startTime = System.currentTimeMillis() - // Migrate data into Room database - objectBoxToLibkiwixMigrator.migrateBookMarks(box) - val endTime = System.currentTimeMillis() - val migrationTime = endTime - startTime // Check if data successfully migrated to Room - val actualDataAfterLargeMigration = - objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks().blockingFirst() - assertEquals(numEntities, actualDataAfterLargeMigration.size) - // Assert that the migration completes within a reasonable time frame - assertTrue(migrationTime < 5000) + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { actualDataAfterMigration -> + assertEquals(numEntities, actualDataAfterMigration.size) + }, + { + throw RuntimeException( + "Exception occurred during migration. Original Exception ${it.printStackTrace()}" + ) + } + ) } private fun clearBookmarks(box: Box, libkiwixBookmark: LibkiwixBookmarks) { @@ -225,4 +308,23 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) box.removeAll() } + + @After + fun finish() { + IdlingRegistry.getInstance().unregister(KiwixIdlingResource.getInstance()) + PreferenceManager.getDefaultSharedPreferences(context).edit { + putBoolean(SharedPreferenceUtil.PREF_IS_TEST, false) + putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, true) + } + } + + companion object { + + @BeforeClass + fun beforeClass() { + IdlingPolicies.setMasterPolicyTimeout(180, TimeUnit.SECONDS) + IdlingPolicies.setIdlingResourceTimeout(180, TimeUnit.SECONDS) + IdlingRegistry.getInstance().register(KiwixIdlingResource.getInstance()) + } + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 50bfe90830..6052453196 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -49,6 +49,14 @@ class LibkiwixBookmarks @Inject constructor( val sharedPreferenceUtil: SharedPreferenceUtil ) : PageDao { + /** + * Request new data from Libkiwix when changes occur inside it; otherwise, + * return the previous data to avoid unnecessary data load on Libkiwix. + */ + private var bookmarksChanged: Boolean = false + private var bookmarkList: List = arrayListOf() + private var libraryBooksList: List = arrayListOf() + private val bookmarkListBehaviour: BehaviorSubject>? by lazy { BehaviorSubject.createDefault(getBookmarksList()) } @@ -110,7 +118,15 @@ class LibkiwixBookmarks @Inject constructor( } .subscribeOn(Schedulers.io()) - fun saveBookmark(libkiwixBookmarkItem: LibkiwixBookmarkItem) { + /** + * Saves bookmarks in libkiwix. The use of `shouldWriteBookmarkToFile` is primarily + * during data migration, where data is written to the file only once after all bookmarks + * have been added to libkiwix to optimize the process. + */ + fun saveBookmark( + libkiwixBookmarkItem: LibkiwixBookmarkItem, + shouldWriteBookmarkToFile: Boolean = true + ) { if (!isBookMarkExist(libkiwixBookmarkItem)) { addBookToLibraryIfNotExist(libkiwixBookmarkItem.libKiwixBook) val bookmark = Bookmark().apply { @@ -120,16 +136,24 @@ class LibkiwixBookmarks @Inject constructor( bookTitle = libkiwixBookmarkItem.libKiwixBook?.title ?: libkiwixBookmarkItem.zimId } library.addBookmark(bookmark).also { - writeBookMarksAndSaveLibraryToFile() - updateFlowableBookmarkList() + if (shouldWriteBookmarkToFile) { + writeBookMarksAndSaveLibraryToFile() + updateFlowableBookmarkList() + } } } } private fun addBookToLibraryIfNotExist(libKiwixBook: Book?) { libKiwixBook?.let { book -> - if (!library.booksIds.any { it == book.id }) { + if (libraryBooksList.isEmpty()) { + // store booksIds in a list to avoid multiple data call on libkiwix + libraryBooksList = library.booksIds.toList() + } + if (!libraryBooksList.any { it == book.id }) { library.addBook(libKiwixBook).also { + // now library has changed so update our library list. + libraryBooksList = library.booksIds.toList() if (BuildConfig.DEBUG) { Log.d( TAG, @@ -145,7 +169,11 @@ class LibkiwixBookmarks @Inject constructor( } fun deleteBookmarks(bookmarks: List) { - bookmarks.map { deleteBookmark(it.zimId, it.bookmarkUrl) } + bookmarks.map { library.removeBookmark(it.zimId, it.bookmarkUrl) } + .also { + writeBookMarksAndSaveLibraryToFile() + updateFlowableBookmarkList() + } } fun deleteBookmark(bookId: String, bookmarkUrl: String) { @@ -167,14 +195,21 @@ class LibkiwixBookmarks @Inject constructor( // Save the bookmarks data to a separate file. library.writeBookmarksToFile(bookmarkFile.canonicalPath) } + // set the bookmark change to true so that libkiwix will return the new data. + bookmarksChanged = true } + @Suppress("ReturnCount") private fun getBookmarksList(): List { + if (!bookmarksChanged && bookmarkList.isNotEmpty()) { + // No changes, return the cached data + return bookmarkList + } // Retrieve the list of bookmarks from the library, or return an empty list if it's null. - val bookmarkList = library.getBookmarks(false)?.toList() ?: return emptyList() + val bookmarkArray = library.getBookmarks(false)?.toList() ?: return bookmarkList // Create a list to store LibkiwixBookmarkItem objects. - return bookmarkList.mapNotNull { bookmark -> + bookmarkList = bookmarkArray.mapNotNull { bookmark -> // Check if the library contains the book associated with the bookmark. val book = if (library.booksIds.contains(bookmark.bookId)) { library.getBookById(bookmark.bookId) @@ -200,8 +235,12 @@ class LibkiwixBookmarks @Inject constructor( bookmark, favicon, book?.path - ) + ).also { + // set the bookmark change to false to avoid reloading the data from libkiwix + bookmarksChanged = false + } } + return bookmarkList } private fun isBookMarkExist(libkiwixBookmarkItem: LibkiwixBookmarkItem): Boolean = diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index b094108772..b6ed41dd77 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -22,9 +22,8 @@ import android.util.Log import io.objectbox.Box import io.objectbox.BoxStore import io.objectbox.kotlin.boxFor -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity @@ -38,17 +37,19 @@ class ObjectBoxToLibkiwixMigrator { @Inject lateinit var boxStore: BoxStore @Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil @Inject lateinit var libkiwixBookmarks: LibkiwixBookmarks + private val migrationMutex = Mutex() - fun migrateBookmarksToLibkiwix() { + suspend fun migrateBookmarksToLibkiwix() { CoreApp.coreComponent.inject(this) migrateBookMarks(boxStore.boxFor()) // TODO we will migrate here for other entities } - fun migrateBookMarks(box: Box) { + suspend fun migrateBookMarks(box: Box) { val bookMarksList = box.all - bookMarksList.forEachIndexed { _, bookmarkEntity -> - CoroutineScope(Dispatchers.IO).launch { + // run migration with mutex to do the migration one by one. + migrationMutex.withLock { + bookMarksList.forEachIndexed { index, bookmarkEntity -> // moving this to handle the exceptions thrown by the libkiwix if any occur, // like if path is not validate due to user move the ZIM file to another location etc. try { @@ -57,7 +58,10 @@ class ObjectBoxToLibkiwixMigrator { val libkiwixBook = Book().apply { update(Archive(bookmarkEntity.zimFilePath)) } - libkiwixBookmarks.saveBookmark(LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook)) + libkiwixBookmarks.saveBookmark( + LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook), + shouldWriteBookmarkToFile = index == bookMarksList.size - 1 + ) // TODO should we remove data from objectBox? // removing the single entity from the object box after migration. // box.query { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt index 8a8d6c9ae9..f6ca82ad7e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/CoreMainActivity.kt @@ -38,6 +38,9 @@ import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.navigation.NavDirections import com.google.android.material.navigation.NavigationView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.kiwix.kiwixmobile.core.BuildConfig import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.R @@ -109,7 +112,10 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider { setMainActivityToCoreApp() if (!sharedPreferenceUtil.prefIsBookmarksMigrated) { - objectBoxToLibkiwixMigrator.migrateBookmarksToLibkiwix() + // run the migration on background thread to avoid any UI related issues. + CoroutineScope(Dispatchers.IO).launch { + objectBoxToLibkiwixMigrator.migrateBookmarksToLibkiwix() + } } } From 50f92beeb1b048d8e42c97bb6fb9a1e4cb0b357f Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Thu, 11 Jan 2024 17:26:04 +0530 Subject: [PATCH 35/38] Fixed the failing LibkiwixBookmarkTest. * Enhanced the process of adding books to the library to prevent unnecessary data loading from libkiwix. * Released the memory occupied by bookmarks and archives to resolve potential issues when running migrations on lower-end devices. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 19 +++++++++++++++---- .../page/bookmarks/LibkiwixBookmarkTest.kt | 3 +-- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 16 +++++++++++----- .../remote/ObjectBoxToLibkiwixMigrator.kt | 7 +++++-- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index f4fa1d9d2d..eb902ca873 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -38,6 +38,7 @@ import org.junit.Before import org.junit.BeforeClass import org.junit.Rule import org.junit.Test +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue @@ -105,6 +106,9 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { moveToState(Lifecycle.State.RESUMED) + onActivity { + it.navigate(R.id.libraryFragment) + } } CoreApp.coreComponent.inject(objectBoxToLibkiwixMigrator) setUpObjectBoxAndData() @@ -143,7 +147,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { } // clear the data before running the test case - clearBookmarks(box, objectBoxToLibkiwixMigrator.libkiwixBookmarks) + clearBookmarks() } @Test @@ -301,10 +305,11 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) } - private fun clearBookmarks(box: Box, libkiwixBookmark: LibkiwixBookmarks) { + private fun clearBookmarks() { // delete bookmarks for testing other edge cases - libkiwixBookmark.deleteBookmarks( - libkiwixBookmark.bookmarks().blockingFirst() as List + objectBoxToLibkiwixMigrator.libkiwixBookmarks.deleteBookmarks( + objectBoxToLibkiwixMigrator.libkiwixBookmarks.bookmarks() + .blockingFirst() as List ) box.removeAll() } @@ -318,6 +323,12 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { } } + @AfterAll + fun deleteBookmarks() { + // Clear the bookmarks list from device to not affect the other test cases. + clearBookmarks() + } + companion object { @BeforeClass diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt index 11b45bdc78..2b57d2a0d7 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/bookmarks/LibkiwixBookmarkTest.kt @@ -34,7 +34,6 @@ import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.main.KiwixMainActivity import org.kiwix.kiwixmobile.nav.destination.library.LocalLibraryFragmentDirections -import org.kiwix.kiwixmobile.search.SearchFragmentTest import org.kiwix.kiwixmobile.testutils.RetryRule import org.kiwix.kiwixmobile.testutils.TestUtils import java.io.File @@ -74,7 +73,7 @@ class LibkiwixBookmarkTest : BaseActivityTest() { kiwixMainActivity.navigate(R.id.libraryFragment) } val loadFileStream = - SearchFragmentTest::class.java.classLoader.getResourceAsStream("testzim.zim") + LibkiwixBookmarkTest::class.java.classLoader.getResourceAsStream("testzim.zim") val zimFile = File(context.cacheDir, "testzim.zim") if (zimFile.exists()) zimFile.delete() zimFile.createNewFile() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 6052453196..1fb9db3b44 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -140,17 +140,15 @@ class LibkiwixBookmarks @Inject constructor( writeBookMarksAndSaveLibraryToFile() updateFlowableBookmarkList() } + // dispose the bookmark + bookmark.dispose() } } } private fun addBookToLibraryIfNotExist(libKiwixBook: Book?) { libKiwixBook?.let { book -> - if (libraryBooksList.isEmpty()) { - // store booksIds in a list to avoid multiple data call on libkiwix - libraryBooksList = library.booksIds.toList() - } - if (!libraryBooksList.any { it == book.id }) { + if (!isBookAlreadyExistInLibrary(book.id)) { library.addBook(libKiwixBook).also { // now library has changed so update our library list. libraryBooksList = library.booksIds.toList() @@ -168,6 +166,14 @@ class LibkiwixBookmarks @Inject constructor( } } + private fun isBookAlreadyExistInLibrary(bookId: String): Boolean { + if (libraryBooksList.isEmpty()) { + // store booksIds in a list to avoid multiple data call on libkiwix + libraryBooksList = library.booksIds.toList() + } + return libraryBooksList.any { it == bookId } + } + fun deleteBookmarks(bookmarks: List) { bookmarks.map { library.removeBookmark(it.zimId, it.bookmarkUrl) } .also { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt index b6ed41dd77..9f92a47c4a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/data/remote/ObjectBoxToLibkiwixMigrator.kt @@ -55,13 +55,15 @@ class ObjectBoxToLibkiwixMigrator { try { // for saving book to library, otherwise it does not save the // favicon and zimFilePath in library. + val archive = Archive(bookmarkEntity.zimFilePath) val libkiwixBook = Book().apply { - update(Archive(bookmarkEntity.zimFilePath)) + update(archive) } libkiwixBookmarks.saveBookmark( LibkiwixBookmarkItem(bookmarkEntity, libkiwixBook), shouldWriteBookmarkToFile = index == bookMarksList.size - 1 ) + archive.dispose() // TODO should we remove data from objectBox? // removing the single entity from the object box after migration. // box.query { @@ -76,7 +78,8 @@ class ObjectBoxToLibkiwixMigrator { "MIGRATING_BOOKMARKS", "there is an error while migrating the bookmark for\n" + " ZIM file = ${bookmarkEntity.zimFilePath} \n" + - "Bookmark Title = ${bookmarkEntity.bookmarkTitle}" + "Bookmark Title = ${bookmarkEntity.bookmarkTitle} \n" + + "Original exception is = $ignore" ) } } From ed2a9e0ec97da23f0100623d0ad6fcfdc591756e Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Mon, 15 Jan 2024 23:57:31 +0530 Subject: [PATCH 36/38] Fixed the failing `LibkiwixBookmarkTest` on Android 33 and 30. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index eb902ca873..802229af2a 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -38,12 +38,10 @@ import org.junit.Before import org.junit.BeforeClass import org.junit.Rule import org.junit.Test -import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertTrue import org.kiwix.kiwixmobile.core.CoreApp -import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator import org.kiwix.kiwixmobile.core.di.modules.DatabaseModule @@ -54,8 +52,6 @@ import org.kiwix.kiwixmobile.testutils.RetryRule import org.kiwix.kiwixmobile.testutils.TestUtils import org.kiwix.kiwixmobile.utils.KiwixIdlingResource import org.kiwix.libkiwix.Book -import org.kiwix.libkiwix.Library -import org.kiwix.libkiwix.Manager import java.io.File import java.io.FileOutputStream import java.io.OutputStream @@ -100,8 +96,8 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { } PreferenceManager.getDefaultSharedPreferences(context).edit { putBoolean(SharedPreferenceUtil.PREF_SHOW_INTRO, false) - putBoolean(SharedPreferenceUtil.PREF_WIFI_ONLY, false) putBoolean(SharedPreferenceUtil.PREF_IS_TEST, true) + putBoolean(SharedPreferenceUtil.IS_PLAY_STORE_BUILD, true) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, false) } activityScenario = ActivityScenario.launch(KiwixMainActivity::class.java).apply { @@ -122,12 +118,6 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) } box = boxStore.boxFor(BookmarkEntity::class.java) - val library = Library() - val manager = Manager(library) - val sharedPreferenceUtil = SharedPreferenceUtil(context) - objectBoxToLibkiwixMigrator.libkiwixBookmarks = - LibkiwixBookmarks(library, manager, sharedPreferenceUtil) - objectBoxToLibkiwixMigrator.sharedPreferenceUtil = SharedPreferenceUtil(context) // add a file in fileSystem because we need to actual file path for making object of Archive. val loadFileStream = @@ -269,7 +259,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { @Test fun testLargeDataMigration(): Unit = runBlocking { // Test large data migration for recent searches - val numEntities = 10000 + val numEntities = 1000 // Insert a large number of recent search entities into ObjectBox for (i in 1..numEntities) { val bookMarkUrl = "https://alpine_linux/search_$i" @@ -296,8 +286,12 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { .subscribe( { actualDataAfterMigration -> assertEquals(numEntities, actualDataAfterMigration.size) + // Clear the bookmarks list from device to not affect the other test cases. + clearBookmarks() }, { + // Clear the bookmarks list from device to not affect the other test cases. + clearBookmarks() throw RuntimeException( "Exception occurred during migration. Original Exception ${it.printStackTrace()}" ) @@ -318,17 +312,10 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { fun finish() { IdlingRegistry.getInstance().unregister(KiwixIdlingResource.getInstance()) PreferenceManager.getDefaultSharedPreferences(context).edit { - putBoolean(SharedPreferenceUtil.PREF_IS_TEST, false) putBoolean(SharedPreferenceUtil.PREF_PLAY_STORE_RESTRICTION, true) } } - @AfterAll - fun deleteBookmarks() { - // Clear the bookmarks list from device to not affect the other test cases. - clearBookmarks() - } - companion object { @BeforeClass From 1460e0d4e59ba2b152bc41c9def5534f634b5f8b Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 16 Jan 2024 15:12:38 +0530 Subject: [PATCH 37/38] Fixed Migration test was failing on the API level 24. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 43 ++++++++++++------- .../kiwixmobile/core/dao/LibkiwixBookmarks.kt | 2 +- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index 802229af2a..f22381d8d2 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile +import android.os.Build import androidx.core.content.edit import androidx.lifecycle.Lifecycle import androidx.preference.PreferenceManager @@ -259,22 +260,22 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { @Test fun testLargeDataMigration(): Unit = runBlocking { // Test large data migration for recent searches - val numEntities = 1000 + val numEntities = getSafeMigrationDataCount() // Insert a large number of recent search entities into ObjectBox - for (i in 1..numEntities) { - val bookMarkUrl = "https://alpine_linux/search_$i" - val title = "title_$i" - val bookmarkEntity = BookmarkEntity( - 0, - expectedZimId, - expectedZimName, - expectedZimFilePath, - bookMarkUrl, - title, - expectedFavicon - ) - box.put(bookmarkEntity) - } + (1..numEntities) + .asSequence() + .map { + BookmarkEntity( + 0, + expectedZimId, + expectedZimName, + expectedZimFilePath, + "https://alpine_linux/search_$it", + "title_$it", + expectedFavicon + ) + } + .forEach(box::put) withContext(Dispatchers.IO) { // Migrate data into Room database objectBoxToLibkiwixMigrator.migrateBookMarks(box) @@ -299,6 +300,18 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) } + /** + * Retrieves the count of migration data, limiting the process on Android API level 24 due to its + * constrained `local reference table` size, which has a limit of 512 entries. The limitation is + * imposed because libkiwix returns all bookmarks at once, and there is no available method to + * retrieve a subset of bookmarks from the library. + * + * On Android versions newer than Nougat (API level 24), the count is set to 10,000. On older versions, + * it's set to 500 to avoid potential `local reference table` overflow issues. + */ + private fun getSafeMigrationDataCount(): Int = + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) 10000 else 500 + private fun clearBookmarks() { // delete bookmarks for testing other edge cases objectBoxToLibkiwixMigrator.libkiwixBookmarks.deleteBookmarks( diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt index 1fb9db3b44..b01dde8626 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/dao/LibkiwixBookmarks.kt @@ -217,7 +217,7 @@ class LibkiwixBookmarks @Inject constructor( // Create a list to store LibkiwixBookmarkItem objects. bookmarkList = bookmarkArray.mapNotNull { bookmark -> // Check if the library contains the book associated with the bookmark. - val book = if (library.booksIds.contains(bookmark.bookId)) { + val book = if (isBookAlreadyExistInLibrary(bookmark.bookId)) { library.getBookById(bookmark.bookId) } else { if (BuildConfig.DEBUG) { From 81994805b7ca3e5110597967dea6737cb894904b Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 6 Feb 2024 14:07:53 +0530 Subject: [PATCH 38/38] Upgraded java-libkiwix to 2.0.0 * Re-enabled test case for migrating bookmarks more then 512 to test it properly on API level 24. --- .../ObjectBoxToLibkiwixMigratorTest.kt | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt index f22381d8d2..b3e06717a0 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/ObjectBoxToLibkiwixMigratorTest.kt @@ -18,7 +18,6 @@ package org.kiwix.kiwixmobile -import android.os.Build import androidx.core.content.edit import androidx.lifecycle.Lifecycle import androidx.preference.PreferenceManager @@ -260,7 +259,7 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { @Test fun testLargeDataMigration(): Unit = runBlocking { // Test large data migration for recent searches - val numEntities = getSafeMigrationDataCount() + val numEntities = 10000 // Insert a large number of recent search entities into ObjectBox (1..numEntities) .asSequence() @@ -300,18 +299,6 @@ class ObjectBoxToLibkiwixMigratorTest : BaseActivityTest() { ) } - /** - * Retrieves the count of migration data, limiting the process on Android API level 24 due to its - * constrained `local reference table` size, which has a limit of 512 entries. The limitation is - * imposed because libkiwix returns all bookmarks at once, and there is no available method to - * retrieve a subset of bookmarks from the library. - * - * On Android versions newer than Nougat (API level 24), the count is set to 10,000. On older versions, - * it's set to 500 to avoid potential `local reference table` overflow issues. - */ - private fun getSafeMigrationDataCount(): Int = - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) 10000 else 500 - private fun clearBookmarks() { // delete bookmarks for testing other edge cases objectBoxToLibkiwixMigrator.libkiwixBookmarks.deleteBookmarks(