diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/BaseDao.java b/app/src/main/java/org/kiwix/kiwixmobile/database/BaseDao.java index 12ce206f0..11ff16c3e 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/BaseDao.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/BaseDao.java @@ -39,8 +39,6 @@ abstract class BaseDao { } }); updates - .debounce(10, TimeUnit.MILLISECONDS) - .onBackpressureDrop() .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .subscribe(unit -> { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/BookDao.java b/app/src/main/java/org/kiwix/kiwixmobile/database/BookDao.java index 0ece6dd5d..6f4231df5 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/BookDao.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/BookDao.java @@ -19,7 +19,6 @@ package org.kiwix.kiwixmobile.database; import com.yahoo.squidb.data.SquidCursor; import com.yahoo.squidb.sql.Query; -import com.yahoo.squidb.sql.Table; import com.yahoo.squidb.sql.TableStatement; import io.reactivex.Flowable; import io.reactivex.processors.BehaviorProcessor; @@ -27,7 +26,6 @@ import java.io.File; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import org.jetbrains.annotations.NotNull; import org.kiwix.kiwixmobile.database.entity.BookDatabaseEntity; import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book; @@ -91,32 +89,39 @@ public class BookDao extends BaseDao { } public List getBooks() { - SquidCursor bookCursor = kiwixDatabase.query( - BookDatabaseEntity.class, - Query.select()); + kiwixDatabase.beginTransaction(); ArrayList books = new ArrayList<>(); - while (bookCursor.moveToNext()) { - Book book = new Book(); - setBookDetails(book, bookCursor); - if (!hasParts(book.file)) { - if (book.file.exists()) { - books.add(book); - } else { - kiwixDatabase.deleteWhere(BookDatabaseEntity.class, BookDatabaseEntity.URL.eq(book.file)); + try(SquidCursor bookCursor = kiwixDatabase.query( + BookDatabaseEntity.class, + Query.select())) { + while (bookCursor.moveToNext()) { + Book book = new Book(); + setBookDetails(book, bookCursor); + if (!hasParts(book.file)) { + if (book.file.exists()) { + books.add(book); + } else { + kiwixDatabase.deleteWhere(BookDatabaseEntity.class, + BookDatabaseEntity.URL.eq(book.file)); + } } } } - bookCursor.close(); + kiwixDatabase.setTransactionSuccessful(); + kiwixDatabase.endTransaction(); return books; } public void saveBooks(List books) { + kiwixDatabase.beginTransaction(); for (Book book : books) { if (book != null) { BookDatabaseEntity bookDatabaseEntity = new BookDatabaseEntity(); setBookDatabaseEntity(book, bookDatabaseEntity); } } + kiwixDatabase.setTransactionSuccessful(); + kiwixDatabase.endTransaction(); } public void deleteBook(String id) { diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java b/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java index 01aa6bceb..f020cdcf1 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/DownloadDao.java @@ -49,16 +49,14 @@ public class DownloadDao extends BaseDao{ } public void insert(final DownloadModel downloadModel) { - if (doesNotAlreadyExist(downloadModel)) { kiwixDatabase.persistWithOnConflict(databaseEntity(downloadModel), TableStatement.ConflictAlgorithm.REPLACE); - } } - private boolean doesNotAlreadyExist(DownloadModel downloadModel) { + public boolean doesNotAlreadyExist(LibraryNetworkEntity.Book book) { return kiwixDatabase.count( DownloadDatabaseEntity.class, - DownloadDatabaseEntity.BOOK_ID.eq(downloadModel.getBook().getId()) + DownloadDatabaseEntity.BOOK_ID.eq(book.getId()) ) == 0; } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java b/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java index b63e3bcf2..4020991b0 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java +++ b/app/src/main/java/org/kiwix/kiwixmobile/database/NetworkLanguageDao.java @@ -37,12 +37,12 @@ public class NetworkLanguageDao extends BaseDao { @Inject public NetworkLanguageDao(KiwixDatabase kiwikDatabase) { - super(kiwikDatabase,NetworkLanguageDatabaseEntity.TABLE); + super(kiwikDatabase, NetworkLanguageDatabaseEntity.TABLE); } @Override protected void onUpdateToTable() { - allLanguageProcessor.onNext(fetchAllLanguages()); + allLanguageProcessor.onNext(fetchAllLanguages()); } public Flowable> allLanguages() { @@ -75,10 +75,13 @@ public class NetworkLanguageDao extends BaseDao { } public void saveFilteredLanguages(List languages) { + kiwixDatabase.beginTransaction(); kiwixDatabase.deleteAll(NetworkLanguageDatabaseEntity.class); Collections.sort(languages, (language, t1) -> language.getLanguage().compareTo(t1.getLanguage())); - for (Language language : languages) { + + for (int i = 0; i < languages.size(); i++) { + Language language = languages.get(i); NetworkLanguageDatabaseEntity networkLanguageDatabaseEntity = new NetworkLanguageDatabaseEntity(); networkLanguageDatabaseEntity.setLanguageISO3(language.getLanguageCode()); @@ -86,5 +89,7 @@ public class NetworkLanguageDao extends BaseDao { networkLanguageDatabaseEntity.setNumberOfOccurences(language.getOccurencesOfLanguage()); kiwixDatabase.persist(networkLanguageDatabaseEntity); } + kiwixDatabase.setTransactionSuccessful(); + kiwixDatabase.endTransaction(); } } \ No newline at end of file diff --git a/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloaderImpl.kt b/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloaderImpl.kt index 623a22c0c..165fac5ca 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloaderImpl.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/downloader/DownloaderImpl.kt @@ -36,15 +36,18 @@ class DownloaderImpl @Inject constructor( override fun download(book: LibraryNetworkEntity.Book) { kiwixService.getMetaLinks(book.url) .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) .take(1) .subscribe( { - val downloadId = downloadRequester.enqueue( - DownloadRequest(it, book) - ) - downloadDao.insert( - DownloadModel(downloadId, book) - ) + if(downloadDao.doesNotAlreadyExist(book)){ + val downloadId = downloadRequester.enqueue( + DownloadRequest(it, book) + ) + downloadDao.insert( + DownloadModel(downloadId, book) + ) + } }, Throwable::printStackTrace ) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt index 3752797ff..88dc9ddd7 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/ZimManageViewModel.kt @@ -101,6 +101,7 @@ class ZimManageViewModel @Inject constructor( return arrayOf( updateDownloadItems(downloadStatuses), removeCompletedDownloadsFromDb(downloadStatuses), + removeNonExistingDownloadsFromDb(downloadStatuses, downloads), updateBookItems(booksFromDao), checkFileSystemForBooksOnRequest(booksFromDao), updateLibraryItems(booksFromDao, downloads, library), @@ -110,6 +111,46 @@ class ZimManageViewModel @Inject constructor( ) } + private fun removeNonExistingDownloadsFromDb( + downloadStatuses: Flowable>, + downloads: Flowable> + ) = downloadStatuses + .withLatestFrom( + downloads, + BiFunction(this::combineToDownloadsWithoutStatuses) + ) + .buffer(3, SECONDS) + .map(this::downloadIdsWithNoStatusesOverBufferPeriod) + .subscribe( + { + downloadDao.delete(*it.toTypedArray()) + }, + Throwable::printStackTrace + ) + + private fun downloadIdsWithNoStatusesOverBufferPeriod(it: List>) = + it.flatten() + .fold(mutableMapOf(), + { acc, id -> acc.increment(id) }) + .filter { (_, value) -> value == it.size } + .map { (key, _) -> key } + + private fun combineToDownloadsWithoutStatuses( + statuses: List, + downloads: List + ): MutableList { + val downloadIdsWithStatuses = statuses.map { it.downloadId } + return downloads.fold( + mutableListOf(), + { acc, downloadModel -> + if (!downloadIdsWithStatuses.contains(downloadModel.downloadId)) { + acc.add(downloadModel.downloadId) + } + acc + } + ) + } + private fun updateLanguageItemsForDialog() = requestLanguagesDialog .withLatestFrom(languageDao.allLanguages(), BiFunction, List> { _, languages -> languages }) @@ -144,13 +185,11 @@ class ZimManageViewModel @Inject constructor( ) = Flowable.combineLatest( booksFromDao, downloads, - languageDao.allLanguages() - .debounce(100, MILLISECONDS) - .filter { it.isNotEmpty() }, + languageDao.allLanguages().filter { it.isNotEmpty() }, library, requestFiltering .doOnNext { libraryListIsRefreshing.postValue(true) } - .debounce(1, SECONDS) + .debounce(500, MILLISECONDS) .observeOn(Schedulers.io()), Function5(this::combineLibrarySources) ) @@ -178,22 +217,26 @@ class ZimManageViewModel @Inject constructor( private fun combineToLanguageList( booksFromNetwork: List, allLanguages: List - ): List { - val networkLanguageCounts = booksFromNetwork.mapNotNull { it.language } + ) = when { + booksFromNetwork.isEmpty() && allLanguages.isEmpty() -> defaultLanguage() + booksFromNetwork.isEmpty() && allLanguages.isNotEmpty() -> emptyList() + booksFromNetwork.isNotEmpty() && allLanguages.isEmpty() -> + fromLocalesWithNetworkMatchesSetActiveBy( + networkLanguageCounts(booksFromNetwork), defaultLanguage() + ) + booksFromNetwork.isNotEmpty() && allLanguages.isNotEmpty() -> + fromLocalesWithNetworkMatchesSetActiveBy( + networkLanguageCounts(booksFromNetwork), allLanguages + ) + else -> throw RuntimeException("Impossible state") + } + + private fun networkLanguageCounts(booksFromNetwork: List) = + booksFromNetwork.mapNotNull { it.language } .fold( mutableMapOf(), { acc, language -> acc.increment(language) } ) - return when { - booksFromNetwork.isEmpty() && allLanguages.isEmpty() -> defaultLanguage() - booksFromNetwork.isEmpty() && allLanguages.isNotEmpty() -> allLanguages - booksFromNetwork.isNotEmpty() && allLanguages.isEmpty() -> - fromLocalesWithNetworkMatchesSetActiveBy(networkLanguageCounts, defaultLanguage()) - booksFromNetwork.isNotEmpty() && allLanguages.isNotEmpty() -> - fromLocalesWithNetworkMatchesSetActiveBy(networkLanguageCounts, allLanguages) - else -> throw RuntimeException("Impossible state") - } - } private fun MutableMap.increment(key: K) = apply { set(key, getOrElse(key, { 0 }) + 1) } @@ -201,18 +244,16 @@ class ZimManageViewModel @Inject constructor( private fun fromLocalesWithNetworkMatchesSetActiveBy( networkLanguageCounts: MutableMap, listToActivateBy: List - ): List { - return Locale.getISOLanguages() - .map { Locale(it) } - .filter { networkLanguageCounts.containsKey(it.isO3Language) } - .map { locale -> - Language( - locale.isO3Language, - languageIsActive(listToActivateBy, locale), - networkLanguageCounts.getOrElse(locale.isO3Language, { 0 }) - ) - } - } + ) = Locale.getISOLanguages() + .map { Locale(it) } + .filter { networkLanguageCounts.containsKey(it.isO3Language) } + .map { locale -> + Language( + locale.isO3Language, + languageIsActive(listToActivateBy, locale), + networkLanguageCounts.getOrElse(locale.isO3Language, { 0 }) + ) + } private fun defaultLanguage() = listOf( diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt index 2f74007de..66fb46ce1 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/fileselect_view/BooksViewHolder.kt @@ -2,7 +2,6 @@ package org.kiwix.kiwixmobile.zim_manager.fileselect_view import android.support.v7.widget.RecyclerView.ViewHolder import android.view.View -import butterknife.internal.DebouncingOnClickListener import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.library_item.creator import kotlinx.android.synthetic.main.library_item.date @@ -20,7 +19,7 @@ import org.kiwix.kiwixmobile.extensions.setTextAndVisibility import org.kiwix.kiwixmobile.library.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.utils.BookUtils import org.kiwix.kiwixmobile.utils.NetworkUtils -import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.MegaByte +import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.KiloByte class BooksViewHolder( override val containerView: View, @@ -37,7 +36,7 @@ class BooksViewHolder( creator.setTextAndVisibility(book.creator) publisher.setTextAndVisibility(book.publisher) date.setTextAndVisibility(book.date) - size.setTextAndVisibility(MegaByte(book.size).humanReadable) + size.setTextAndVisibility(KiloByte(book.size).humanReadable) language.text = bookUtils.getLanguage(book.getLanguage()) fileName.text = NetworkUtils.parseURL( KiwixApplication.getInstance(), book.file.path diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt index 186eae5ff..fabc6ea9f 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/LibraryFragment.kt @@ -213,11 +213,11 @@ class LibraryFragment : BaseFragment() { arguments = Bundle().apply { putString( StorageSelectDialog.STORAGE_DIALOG_INTERNAL, - getString(string.internal_storage) + this@LibraryFragment.getString(string.internal_storage) ) putString( StorageSelectDialog.STORAGE_DIALOG_EXTERNAL, - getString(string.external_storage) + this@LibraryFragment.getString(string.external_storage) ) putInt(StorageSelectDialog.STORAGE_DIALOG_THEME, StyleUtils.dialogStyle()) } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/MegaByte.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/KiloByte.kt similarity index 79% rename from app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/MegaByte.kt rename to app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/KiloByte.kt index df84bddb0..bd157b3f0 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/MegaByte.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/KiloByte.kt @@ -2,9 +2,9 @@ package org.kiwix.kiwixmobile.zim_manager.library_view.adapter import java.text.DecimalFormat -inline class MegaByte(val megabyteString: String?) { +inline class KiloByte(val kilobyteString: String?) { val humanReadable - get() = megabyteString?.toLongOrNull()?.let { + get() = kilobyteString?.toLongOrNull()?.let { val units = arrayOf("KB", "MB", "GB", "TB") val conversion = (Math.log10(it.toDouble()) / Math.log10(1024.0)).toInt() (DecimalFormat("#,##0.#") diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt index b94d630b1..921c7f4b0 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zim_manager/library_view/adapter/LibraryViewHolder.kt @@ -2,7 +2,6 @@ package org.kiwix.kiwixmobile.zim_manager.library_view.adapter import android.support.v7.widget.RecyclerView import android.view.View -import butterknife.internal.DebouncingOnClickListener import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.library_divider.divider_text import kotlinx.android.synthetic.main.library_item.creator @@ -40,7 +39,7 @@ sealed class LibraryViewHolder(override val containerView: creator.setTextAndVisibility(item.book.creator) publisher.setTextAndVisibility(item.book.publisher) date.setTextAndVisibility(item.book.date) - size.setTextAndVisibility(MegaByte(item.book.size).humanReadable) + size.setTextAndVisibility(KiloByte(item.book.size).humanReadable) language.text = bookUtils.getLanguage(item.book.getLanguage()) fileName.text = NetworkUtils.parseURL( KiwixApplication.getInstance(), item.book.file?.path ?: ""