diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt index 7621f64d8b..246625abae 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Manga.kt @@ -18,6 +18,8 @@ interface Manga : SManga { var unread: Int + var downloadTotal: Int + var category: Int fun setChapterOrder(order: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt index 401d99a058..64777b0471 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/MangaImpl.kt @@ -34,6 +34,8 @@ class MangaImpl : Manga { @Transient override var unread: Int = 0 + @Transient override var downloadTotal: Int = 0 + @Transient override var category: Int = 0 override fun equals(other: Any?): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 2ee14b3bbd..c2eb585893 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -105,6 +105,8 @@ object PreferenceKeys { const val defaultCategory = "default_category" + const val downloadBadge = "pref_display_download_badge" + fun sourceUsername(sourceId: Long) = "pref_source_username_$sourceId" fun sourcePassword(sourceId: Long) = "pref_source_password_$sourceId" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 8e0f401a04..00bfc6661c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -141,6 +141,8 @@ class PreferencesHelper(val context: Context) { fun libraryAsList() = rxPrefs.getBoolean(Keys.libraryAsList, false) + fun downloadBadge() = rxPrefs.getBoolean(Keys.downloadBadge, false) + fun filterDownloaded() = rxPrefs.getBoolean(Keys.filterDownloaded, false) fun filterUnread() = rxPrefs.getBoolean(Keys.filterUnread, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 7b694cfbea..0842514071 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -196,6 +196,7 @@ class LibraryController( is LibraryNavigationView.FilterGroup -> onFilterChanged() is LibraryNavigationView.SortGroup -> onSortChanged() is LibraryNavigationView.DisplayGroup -> reattachAdapter() + is LibraryNavigationView.BadgeGroup -> onDownloadBadgeChanged() } } @@ -285,6 +286,10 @@ class LibraryController( (activity as? AppCompatActivity)?.supportInvalidateOptionsMenu() } + private fun onDownloadBadgeChanged(){ + presenter.requestDownloadBadgesUpdate() + } + /** * Called when the sorting mode is changed. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt index b13616c8d0..9b614ea37e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt @@ -5,7 +5,12 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.source.LocalSource import kotlinx.android.synthetic.main.catalogue_grid_item.view.* +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -19,7 +24,9 @@ import kotlinx.android.synthetic.main.catalogue_grid_item.view.* class LibraryGridHolder( private val view: View, private val adapter: FlexibleAdapter<*> + ) : LibraryHolder(view, adapter) { + private val preferences: PreferencesHelper = Injekt.get() /** * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this @@ -36,6 +43,15 @@ class LibraryGridHolder( visibility = if (manga.unread > 0) View.VISIBLE else View.GONE text = manga.unread.toString() } + // Update the download count and its visibility. + with(view.download_text) { + visibility = if (manga.downloadTotal > 0 && preferences.downloadBadge().getOrDefault()) View.VISIBLE else View.GONE + text = manga.downloadTotal.toString() + } + //set local visibility if its local manga + with(view.local_text){ + visibility = if(manga.source == LocalSource.ID) View.VISIBLE else View.GONE + } // Update the cover. GlideApp.with(view.context).clear(view.thumbnail) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt index 5895b5acb8..a3b60e9322 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -5,7 +5,12 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.source.LocalSource import kotlinx.android.synthetic.main.catalogue_list_item.view.* +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -21,6 +26,7 @@ class LibraryListHolder( private val view: View, private val adapter: FlexibleAdapter<*> ) : LibraryHolder(view, adapter) { + private val preferences: PreferencesHelper = Injekt.get() /** * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this @@ -37,6 +43,15 @@ class LibraryListHolder( visibility = if (manga.unread > 0) View.VISIBLE else View.GONE text = manga.unread.toString() } + // Update the download count and its visibility. + with(itemView.download_text) { + visibility = if (manga.downloadTotal > 0 && preferences.downloadBadge().getOrDefault()) View.VISIBLE else View.GONE + text = manga.downloadTotal.toString() + } + //show local text badge if local manga + with(itemView.local_text) { + visibility = if (manga.source == LocalSource.ID) View.VISIBLE else View.GONE + } // Create thumbnail onclick to simulate long click itemView.thumbnail.setOnClickListener { @@ -55,4 +70,4 @@ class LibraryListHolder( .into(itemView.thumbnail) } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt index 7f90523c69..e79503a7d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt @@ -25,7 +25,7 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A /** * List of groups shown in the view. */ - private val groups = listOf(FilterGroup(), SortGroup(), DisplayGroup()) + private val groups = listOf(FilterGroup(), SortGroup(), DisplayGroup(), BadgeGroup()) /** * Adapter instance. @@ -166,6 +166,23 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A } + inner class BadgeGroup : Group { + private val downloadBadge = Item.CheckboxGroup(R.string.action_display_download_badge, this) + override val header = null + override val footer= null + override val items = listOf(downloadBadge) + override fun initModels() { + downloadBadge.checked = preferences.downloadBadge().getOrDefault() + } + + override fun onItemClicked(item: Item) { + item as Item.CheckboxGroup + item.checked = !item.checked + preferences.downloadBadge().set((item.checked)) + adapter.notifyItemChanged(item) + } + } + /** * Display group, to show the library as a list or a grid. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 30edf6fed4..cb0a6d3231 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -53,6 +53,11 @@ class LibraryPresenter( */ private val filterTriggerRelay = BehaviorRelay.create(Unit) + /** + * Relay used to apply the UI update to the last emission of the library. + */ + private val downloadTriggerRelay = BehaviorRelay.create(Unit) + /** * Relay used to apply the selected sorting method to the last emission of the library. */ @@ -76,6 +81,8 @@ class LibraryPresenter( librarySubscription = getLibraryObservable() .combineLatest(filterTriggerRelay.observeOn(Schedulers.io()), { lib, _ -> Pair(lib.first, applyFilters(lib.second)) }) + .combineLatest(downloadTriggerRelay.observeOn(Schedulers.io()), + { lib, _ -> Pair(lib.first, addDownloadTotal(lib.second)) }) .combineLatest(sortTriggerRelay.observeOn(Schedulers.io()), { lib, _ -> Pair(lib.first, applySort(lib.second)) }) .map { Pair(it.first, it.second.mapValues { it.value.map(::LibraryItem) }) } @@ -141,6 +148,48 @@ class LibraryPresenter( return map.mapValues { entry -> entry.value.filter(filterFn) } } + /** + * Adds Downloaded chapter count to manga + * + * @param map the map to filter. + */ + private fun addDownloadTotal(map: Map>): Map> { + // Cached list of downloaded manga directories given a source id. + if (preferences.downloadBadge().getOrDefault()) { + val mangaDirsForSource = mutableMapOf>() + + // Cached list of downloaded chapter directories for a manga. + val chapterDirectories = mutableMapOf() + + for ((key, mangaList) in map) { + for (manga in mangaList) { + manga.downloadTotal = getDownloadedCountFromDirectory(manga, mangaDirsForSource, chapterDirectories) + } + } + } + return map; + } + + //Get count of downloaded chapters for a manga + fun getDownloadedCountFromDirectory(manga: Manga, mangaDirsForSource: MutableMap>, chapterDirectories: MutableMap): Int { + val source = sourceManager.get(manga.source) ?: return 0; + // Get the directories for the source of the manga. + val dirsForSource = mangaDirsForSource.getOrPut(source.id) { + val sourceDir = downloadManager.findSourceDir(source) + sourceDir?.listFiles()?.associateBy { it.name }.orEmpty() + } + val mangaDirName = downloadManager.getMangaDirName(manga) + val mangaDir = dirsForSource[mangaDirName] ?: return 0 + + chapterDirectories.getOrPut(manga.id!!) { + if (mangaDir.listFiles()?.isNotEmpty() ?: false) { + return mangaDir.listFiles()!!.size + } + return 0; + } + return 0; + } + /** * Applies library sorting to the given map of manga. * @@ -236,6 +285,13 @@ class LibraryPresenter( filterTriggerRelay.call(Unit) } + /** + * Requests the library to have download badges added. + */ + fun requestDownloadBadgesUpdate() { + downloadTriggerRelay.call(Unit) + } + /** * Requests the library to be sorted. */ diff --git a/app/src/main/res/layout/catalogue_grid_item.xml b/app/src/main/res/layout/catalogue_grid_item.xml index e475733d46..4bde878c41 100644 --- a/app/src/main/res/layout/catalogue_grid_item.xml +++ b/app/src/main/res/layout/catalogue_grid_item.xml @@ -29,17 +29,63 @@ android:layout_gravity="bottom" android:background="@drawable/gradient_shape"/> - + tools:layout_editor_absoluteY="7dp" + tools:layout_editor_absoluteX="7dp"> + + + + - - - - - + + + android:ellipsize="end" + android:maxLines="1" + tools:text="Manga title" + android:layout_marginStart="8dp" + app:layout_constraintTop_toTopOf="parent" + android:layout_marginTop="8dp" + app:layout_constraintBottom_toBottomOf="parent" + android:layout_marginBottom="8dp" + app:layout_constraintLeft_toRightOf="@+id/thumbnail" + android:layout_marginLeft="8dp" + app:layout_constraintRight_toLeftOf="@+id/local_text" + android:layout_marginRight="8dp" + app:layout_constraintVertical_bias="0.523" + app:layout_constraintHorizontal_bias="0.007"/> + tools:text="130" + tools:visibility="visible" + app:layout_constraintTop_toTopOf="parent" + android:layout_marginTop="8dp" + app:layout_constraintBottom_toBottomOf="parent" + android:layout_marginBottom="8dp" + android:layout_marginRight="8dp" + app:layout_constraintRight_toRightOf="parent"/> + android:visibility="gone" + tools:text="122" + tools:visibility="visible" + android:layout_marginEnd="8dp" + app:layout_constraintRight_toLeftOf="@+id/unread_text" + app:layout_constraintTop_toTopOf="parent" + android:layout_marginTop="8dp" + app:layout_constraintBottom_toBottomOf="parent" + android:layout_marginBottom="8dp"/> + - + - \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index e4a43c3b54..5390a66640 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -78,4 +78,6 @@ #F44336 + #009688 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b43fad331..1f13c76d6e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -21,6 +21,7 @@ Selected: %1$d Backup + Settings Filter @@ -74,6 +75,7 @@ Display Grid List + Download Badges Set filter Cancel Sort @@ -270,6 +272,7 @@ Title or author… Updating category + Local Are you sure you want to remove selected manga? Also delete downloaded chapters