BookOnDisk Room migration completed

This commit is contained in:
Gouri Panda 2023-01-06 17:00:39 +05:30 committed by Kelson
parent 7bf17a8c4d
commit ee81e91832
11 changed files with 276 additions and 22 deletions

View File

@ -36,7 +36,7 @@ import org.kiwix.kiwixmobile.core.StorageObserver
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.data.remote.KiwixService
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
@ -80,7 +80,7 @@ import javax.inject.Inject
@Suppress("LongParameterList")
class ZimManageViewModel @Inject constructor(
private val downloadDao: FetchDownloadDao,
private val bookDao: NewBookDao,
private val bookRoomDao: NewBookRoomDao,
private val languageRoomDao: LanguageRoomDao,
private val storageObserver: StorageObserver,
private val kiwixService: KiwixService,
@ -439,11 +439,11 @@ class ZimManageViewModel @Inject constructor(
.filter(List<BookOnDisk>::isNotEmpty)
.map { it.distinctBy { bookOnDisk -> bookOnDisk.book.id } }
.subscribe(
bookDao::insert,
bookRoomDao::insert,
Throwable::printStackTrace
)
private fun books() = bookDao.books()
private fun books() = bookRoomDao.books()
.subscribeOn(Schedulers.io())
.map { it.sortedBy { book -> book.book.title } }

View File

@ -23,7 +23,7 @@ import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.cachedComponent
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
@ -36,7 +36,7 @@ data class DeleteFiles(private val booksOnDiskListItems: List<BookOnDisk>) :
SideEffect<Unit> {
@Inject lateinit var dialogShower: DialogShower
@Inject lateinit var newBookDao: NewBookDao
@Inject lateinit var newBookRoomDao: NewBookRoomDao
@Inject lateinit var zimReaderContainer: ZimReaderContainer
override fun invokeWith(activity: AppCompatActivity) {
@ -71,7 +71,7 @@ data class DeleteFiles(private val booksOnDiskListItems: List<BookOnDisk>) :
if (file.exists()) {
return false
}
newBookDao.delete(book.databaseId)
newBookRoomDao.delete(book.databaseId)
return true
}
}

View File

@ -36,7 +36,7 @@ import javax.inject.Inject
class FetchDownloadDao @Inject constructor(
private val box: Box<FetchDownloadEntity>,
private val newBookDao: NewBookDao
private val newBookRoomDao: NewBookRoomDao
) {
fun downloads(): Flowable<List<DownloadModel>> =
@ -51,7 +51,7 @@ class FetchDownloadDao @Inject constructor(
downloadEntities.filter { it.status == COMPLETED }.takeIf { it.isNotEmpty() }?.let {
box.store.callInTx {
box.remove(it)
newBookDao.insert(it.map(::BookOnDisk))
newBookRoomDao.insert(it.map(::BookOnDisk))
}
}
}

View File

@ -0,0 +1,146 @@
/*
* Kiwix Android
* Copyright (c) 2023 Kiwix <android.kiwix.org>
* 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 <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.TypeConverter
import io.objectbox.Box
import io.reactivex.Flowable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskRoomEntity
import org.kiwix.kiwixmobile.core.data.local.entity.Bookmark
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import java.io.File
@Dao
abstract class NewBookRoomDao {
@Query("SELECT * FROM BookOnDiskRoomEntity")
abstract fun getBookAsEntity(): Flowable<List<BookOnDiskRoomEntity>>
@Query("SELECT * FROM BookOnDiskRoomEntity")
abstract fun getBookAsList(): List<BookOnDiskRoomEntity>
fun getBooks(): List<BooksOnDiskListItem.BookOnDisk> =
getBookAsList().map(BooksOnDiskListItem::BookOnDisk)
fun books() = getBookAsEntity()
.doOnNext(::removeBooksThatDoNotExist)
.map { books ->
books.filter {
it.file.exists()
}
}
.map { it.map(BooksOnDiskListItem::BookOnDisk) }
@Delete
abstract fun deleteBooks(books: BookOnDiskRoomEntity)
@Transaction
open fun removeBooksThatDoNotExist(books: List<BookOnDiskRoomEntity>) {
books.filterNot { it.file.exists() }.forEach(::deleteBooks)
}
@Insert
abstract fun insertAsEntity(bookOnDiskRoomEntity: BookOnDiskRoomEntity)
@Transaction
open fun insert(booksOnDisks: List<BooksOnDiskListItem.BookOnDisk>) {
val uniqueBooks = uniqueBooksByFile(booksOnDisks)
removeEntriesWithMatchingIds(uniqueBooks)
uniqueBooks.distinctBy { it.book.id }.map {
val bookOnDiskRoomEntity = BookOnDiskRoomEntity(it)
insertAsEntity(bookOnDiskRoomEntity)
}
}
private fun uniqueBooksByFile(booksOnDisk: List<BooksOnDiskListItem.BookOnDisk>):
List<BooksOnDiskListItem.BookOnDisk> {
val booksWithSameFilePath = booksWithSameFilePath(booksOnDisk)
return booksOnDisk.filter { bookOnDisk: BooksOnDiskListItem.BookOnDisk ->
booksWithSameFilePath?.find { it?.file?.path == bookOnDisk.file.path } == null
}
}
@Query("SELECT * FROM BookOnDiskRoomEntity where file LIKE :filePath")
abstract fun booksWithSameFilePathAsEntity(filePath: String): BookOnDiskRoomEntity?
@Transaction
open fun booksWithSameFilePath(booksOnDisks: List<BooksOnDiskListItem.BookOnDisk>):
List<BooksOnDiskListItem.BookOnDisk?>? {
return booksOnDisks.map { booksOnDisk ->
val path = booksOnDisk.file.path
val entity = booksWithSameFilePathAsEntity(path)
if (entity != null) {
BooksOnDiskListItem.BookOnDisk(entity)
} else {
null
}
}
}
@Query("DELETE FROM BookOnDiskRoomEntity WHERE bookId LIKE :id")
abstract fun removeEntriesWithMatchingId(id: String)
@Transaction
open fun removeEntriesWithMatchingIds(uniqueBooks: List<BooksOnDiskListItem.BookOnDisk>) {
uniqueBooks.forEachIndexed { _, bookOnDisk ->
removeEntriesWithMatchingId(bookOnDisk.id.toString())
}
}
@Query("DELETE FROM BookOnDiskRoomEntity WHERE bookId LIKE :databaseId")
abstract fun delete(databaseId: Long)
fun getFavIconAndZimFile(it: Bookmark): Pair<String?, String?> {
return getBookOnDiskById(it.zimId).let {
return@let it.favIcon to it.file.path
} ?: (null to null)
}
@Query("SELECT * FROM BookOnDiskRoomEntity WHERE bookId LIKE :zimId")
abstract fun getBookOnDiskById(zimId: String): BookOnDiskRoomEntity
@Query("SELECT * FROM BookOnDiskRoomEntity WHERE file LIKE :downloadTitle")
abstract fun bookMatching(downloadTitle: String): BookOnDiskRoomEntity
fun migrationInsert(box: Box<BookOnDiskEntity>) {
val bookOnDiskEntities = box.all
bookOnDiskEntities.forEachIndexed { _, bookOnDiskEntity ->
CoroutineScope(Dispatchers.IO).launch {
val bookOnDisk = BooksOnDiskListItem.BookOnDisk(bookOnDiskEntity)
insertAsEntity(BookOnDiskRoomEntity(bookOnDisk))
}
}
}
}
class StringToFileConverterDao {
@TypeConverter
fun convertToDatabaseValue(entityProperty: File?) = entityProperty?.path ?: ""
@TypeConverter
fun convertToEntityProperty(databaseValue: String?) = File(databaseValue ?: "")
}

View File

@ -0,0 +1,82 @@
/*
* Kiwix Android
* Copyright (c) 2023 Kiwix <android.kiwix.org>
* 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 <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.dao.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
import java.io.File
@Entity
data class BookOnDiskRoomEntity(
@PrimaryKey(autoGenerate = true)
var id: Long = 0,
val file: File = File(""),
val bookId: String,
val title: String,
val description: String?,
val language: String,
val creator: String,
val publisher: String,
val date: String,
val url: String?,
val articleCount: String?,
val mediaCount: String?,
val size: String,
val name: String?,
val favIcon: String,
val tags: String? = null
) {
constructor(bookOnDisk: BooksOnDiskListItem.BookOnDisk) : this(
0,
bookOnDisk.file,
bookOnDisk.book.id,
bookOnDisk.book.title,
bookOnDisk.book.description,
bookOnDisk.book.language,
bookOnDisk.book.creator,
bookOnDisk.book.publisher,
bookOnDisk.book.date,
bookOnDisk.book.url,
bookOnDisk.book.articleCount,
bookOnDisk.book.mediaCount,
bookOnDisk.book.size,
bookOnDisk.book.bookName,
bookOnDisk.book.favicon,
bookOnDisk.book.tags
)
fun toBook() = LibraryNetworkEntity.Book().apply {
id = bookId
title = this@BookOnDiskRoomEntity.title
description = this@BookOnDiskRoomEntity.description
language = this@BookOnDiskRoomEntity.language
creator = this@BookOnDiskRoomEntity.creator
publisher = this@BookOnDiskRoomEntity.publisher
date = this@BookOnDiskRoomEntity.date
url = this@BookOnDiskRoomEntity.url
articleCount = this@BookOnDiskRoomEntity.articleCount
mediaCount = this@BookOnDiskRoomEntity.mediaCount
size = this@BookOnDiskRoomEntity.size
bookName = name
favicon = favIcon
tags = this@BookOnDiskRoomEntity.tags
}
}

View File

@ -24,7 +24,7 @@ import io.reactivex.Scheduler
import io.reactivex.Single
import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchRoomDao
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
@ -52,7 +52,7 @@ import javax.inject.Singleton
class Repository @Inject internal constructor(
@param:IO private val io: Scheduler,
@param:MainThread private val mainThread: Scheduler,
private val bookDao: NewBookDao,
private val bookRoomDao: NewBookRoomDao,
private val bookmarksDao: NewBookmarksDao,
private val historyDao: HistoryDao,
private val notesRoomDao: NotesRoomDao,
@ -67,7 +67,7 @@ class Repository @Inject internal constructor(
.subscribeOn(io)
.observeOn(mainThread)
override fun booksOnDiskAsListItems(): Flowable<List<BooksOnDiskListItem>> = bookDao.books()
override fun booksOnDiskAsListItems(): Flowable<List<BooksOnDiskListItem>> = bookRoomDao.books()
.map { it.sortedBy { bookOnDisk -> bookOnDisk.book.language + bookOnDisk.book.title } }
.map {
HeaderizableList<BooksOnDiskListItem, BookOnDisk, LanguageItem>(it).foldOverAddingHeaders(
@ -78,11 +78,11 @@ class Repository @Inject internal constructor(
.map(MutableList<BooksOnDiskListItem>::toList)
override fun saveBooks(books: List<BookOnDisk>) =
Completable.fromAction { bookDao.insert(books) }
Completable.fromAction { bookRoomDao.insert(books) }
.subscribeOn(io)
override fun saveBook(book: BookOnDisk) =
Completable.fromAction { bookDao.insert(listOf(book)) }
Completable.fromAction { bookRoomDao.insert(listOf(book)) }
.subscribeOn(io)
override fun saveLanguages(languages: List<Language>) =

View File

@ -26,23 +26,30 @@ import androidx.room.TypeConverters
import io.objectbox.BoxStore
import io.objectbox.kotlin.boxFor
import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchRoomDao
import org.kiwix.kiwixmobile.core.dao.NotesRoomDao
import org.kiwix.kiwixmobile.core.dao.StringToFileConverterDao
import org.kiwix.kiwixmobile.core.dao.StringToLocalConverterDao
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.LanguageRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.NotesRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.RecentSearchRoomEntity
@Suppress("UnnecessaryAbstractClass")
@Database(
entities = [RecentSearchRoomEntity::class, NotesRoomEntity::class, LanguageRoomEntity::class],
version = 3
entities = [
RecentSearchRoomEntity::class, NotesRoomEntity::class,
LanguageRoomEntity::class, BookOnDiskRoomEntity::class
],
version = 4
)
@TypeConverters(StringToLocalConverterDao::class)
@TypeConverters(StringToLocalConverterDao::class, StringToFileConverterDao::class)
abstract class KiwixRoomDatabase : RoomDatabase() {
abstract fun newRecentSearchRoomDao(): NewRecentSearchRoomDao
abstract fun noteRoomDao(): NotesRoomDao
abstract fun languageRoomDao(): LanguageRoomDao
abstract fun newBookRoomDao(): NewBookRoomDao
companion object {
private var db: KiwixRoomDatabase? = null
@ -56,6 +63,7 @@ abstract class KiwixRoomDatabase : RoomDatabase() {
it.migrateRecentSearch(boxStore)
it.migrateNote(boxStore)
it.migrateLanguages(boxStore)
it.migrateNewBookDao(boxStore)
}
}
}
@ -76,4 +84,8 @@ abstract class KiwixRoomDatabase : RoomDatabase() {
fun migrateLanguages(boxStore: BoxStore) {
languageRoomDao().migrationToRoomInsert(boxStore.boxFor())
}
fun migrateNewBookDao(boxStore: BoxStore) {
newBookRoomDao().migrationInsert(boxStore.boxFor())
}
}

View File

@ -30,6 +30,7 @@ import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.LanguageRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
@ -95,6 +96,7 @@ interface CoreComponent {
fun newLanguagesDao(): NewLanguagesDao
fun recentSearchDao(): NewRecentSearchDao
fun languageRoomDao(): LanguageRoomDao
fun newBookRoomDao(): NewBookRoomDao
fun recentSearchRoomDao(): NewRecentSearchRoomDao
fun noteRoomDao(): NotesRoomDao
fun newBookmarksDao(): NewBookmarksDao

View File

@ -26,6 +26,7 @@ import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.dao.FlowBuilder
import org.kiwix.kiwixmobile.core.dao.HistoryDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookmarksDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
@ -70,9 +71,9 @@ open class DatabaseModule {
@Provides @Singleton fun providesFetchDownloadDao(
boxStore: BoxStore,
newBookDao: NewBookDao
newBookRoomDao: NewBookRoomDao
): FetchDownloadDao =
FetchDownloadDao(boxStore.boxFor(), newBookDao)
FetchDownloadDao(boxStore.boxFor(), newBookRoomDao)
@Singleton
@Provides
@ -96,4 +97,8 @@ open class DatabaseModule {
@Singleton
@Provides
fun provideLanguageRoomDao(db: KiwixRoomDatabase) = db.languageRoomDao()
@Singleton
@Provides
fun provideNewBookRoomDao(db: KiwixRoomDatabase) = db.newBookRoomDao()
}

View File

@ -25,7 +25,7 @@ import android.os.Process
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewBookRoomDao
import org.kiwix.kiwixmobile.core.databinding.ActivityKiwixErrorBinding
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
@ -42,7 +42,7 @@ private const val ZERO = 0
open class ErrorActivity : BaseActivity() {
@Inject
lateinit var bookDao: NewBookDao
lateinit var bookRoomDao: NewBookRoomDao
@Inject
lateinit var zimReaderContainer: ZimReaderContainer
@ -127,7 +127,7 @@ open class ErrorActivity : BaseActivity() {
""".trimIndent()
private fun zimFiles(): String {
val allZimFiles = bookDao.getBooks().joinToString {
val allZimFiles = bookRoomDao.getBooks().joinToString {
"""
${it.book.title}:
Articles: [${it.book.articleCount}]

View File

@ -19,6 +19,7 @@
package org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskEntity
import org.kiwix.kiwixmobile.core.dao.entities.BookOnDiskRoomEntity
import org.kiwix.kiwixmobile.core.dao.entities.FetchDownloadEntity
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.reader.ZimFileReader
@ -63,6 +64,12 @@ sealed class BooksOnDiskListItem {
file = File(fetchDownloadEntity.file)
)
constructor(bookOnDiskRoomEntity: BookOnDiskRoomEntity) : this(
bookOnDiskRoomEntity.id,
bookOnDiskRoomEntity.toBook(),
bookOnDiskRoomEntity.file
)
constructor(file: File, zimFileReader: ZimFileReader) : this(
book = zimFileReader.toBook(),
file = file