From a7e349b1b2a921d3f40869568cc13b798d172429 Mon Sep 17 00:00:00 2001 From: Jay Date: Tue, 29 Oct 2019 19:22:54 -0700 Subject: [PATCH] More use of Snackbars No longer double confirming to see if it's ok to remove manga in library and catalouge, snackbar has an undo button --- .../kanade/tachiyomi/data/cache/CoverCache.kt | 28 +++++++++- .../data/download/DownloadManager.kt | 20 +++++++ .../data/download/DownloadPendingDeleter.kt | 16 ++++++ .../tachiyomi/data/download/Downloader.kt | 20 +++++++ .../browse/BrowseCatalogueController.kt | 56 ++++++++++--------- .../browse/BrowseCataloguePresenter.kt | 5 +- .../tachiyomi/ui/library/LibraryController.kt | 20 +++++-- .../tachiyomi/ui/library/LibraryPresenter.kt | 30 ++++++---- .../ui/manga/info/MangaInfoController.kt | 16 ++---- .../ui/manga/info/MangaInfoPresenter.kt | 19 +------ .../kanade/tachiyomi/util/ViewExtensions.kt | 2 +- app/src/main/res/drawable/bg_snackbar.xml | 4 +- app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 1 + 14 files changed, 162 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt index 4586f8c182..15d0f9cc04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/CoverCache.kt @@ -1,7 +1,11 @@ package eu.kanade.tachiyomi.data.cache import android.content.Context +import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.util.DiskUtil +import eu.kanade.tachiyomi.util.launchUI +import kotlinx.coroutines.cancel +import kotlinx.coroutines.delay import java.io.File import java.io.IOException import java.io.InputStream @@ -60,8 +64,30 @@ class CoverCache(private val context: Context) { return false // Remove file. - val file = getCoverFile(thumbnailUrl!!) + val file = getCoverFile(thumbnailUrl) return file.exists() && file.delete() } + /** + * Delete the cover file from the cache. + * + * @param thumbnailUrl the thumbnail url. + * @return status of deletion. + */ + fun deleteFromCache(manga: Manga, delayBy:Long) { + val thumbnailUrl = manga.thumbnail_url + // Check if url is empty. + if (thumbnailUrl.isNullOrEmpty()) return + launchUI { + if (delayBy > 0) { + delay(delayBy) + if (manga.favorite) cancel() + } + // Remove file. + val file = getCoverFile(thumbnailUrl) + if (file.exists()) + file.delete() + } + } + } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 4e23599d0c..8214818373 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -9,6 +9,9 @@ import eu.kanade.tachiyomi.data.download.model.DownloadQueue import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.util.launchNow +import eu.kanade.tachiyomi.util.launchUI +import kotlinx.coroutines.delay import rx.Observable import uy.kohesive.injekt.injectLazy @@ -181,11 +184,28 @@ class DownloadManager(context: Context) { * @param source the source of the manga. */ fun deleteManga(manga: Manga, source: Source) { + downloader.clearQueue(manga, true) queue.remove(manga) provider.findMangaDir(manga, source)?.delete() cache.removeManga(manga) } + + /** + * Deletes the directory of a downloaded manga. + * + * @param manga the manga to delete. + * @param source the source of the manga. + */ + fun deleteManga(manga: Manga, source: Source, delayBy: Long) { + launchUI { + delay(delayBy) + if (!manga.favorite) { + deleteManga(manga, source) + } + } + } + /** * Adds a list of chapters to be deleted later. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt index 894b9e493d..a7ce329a8a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadPendingDeleter.kt @@ -90,6 +90,22 @@ class DownloadPendingDeleter(context: Context) { } } + /** + * Returns the list of chapters to be deleted grouped by its manga. + * + * Note: the returned list of manga and chapters only contain basic information needed by the + * downloader, so don't use them for anything else. + */ + @Synchronized + fun getPendingChapters(manga: Manga): List? { + val entries = decodeAll() + prefs.edit().clear().apply() + lastAddedEntry = null + + val entry = entries.find { it.manga.id == manga.id } + return entry?.chapters?.map { it.toModel() } + } + /** * Decodes all the chapters from preferences. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 5c232fb64f..a68d60c1f5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -157,6 +157,26 @@ class Downloader( notifier.dismiss() } + /** + * Removes everything from the queue for a certain manga + * + * @param isNotification value that determines if status is set (needed for view updates) + */ + fun clearQueue(manga: Manga, isNotification: Boolean = false) { + //Needed to update the chapter view + if (isNotification) { + queue + .filter { it.status == Download.QUEUE && it.manga.id == manga.id } + .forEach { it.status = Download.NOT_DOWNLOADED } + } + queue.remove(manga) + if (queue.isEmpty()) { + DownloadService.stop(context) + stop() + } + notifier.dismiss() + } + /** * Prepares the subscriptions to start downloading. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt index fe268a890b..81e4538d18 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt @@ -503,38 +503,40 @@ open class BrowseCatalogueController(bundle: Bundle) : override fun onItemLongClick(position: Int) { val activity = activity ?: return val manga = (adapter?.getItem(position) as? CatalogueItem?)?.manga ?: return + snack?.dismiss() if (manga.favorite) { - MaterialDialog.Builder(activity) - .items(activity.getString(R.string.remove_from_library)) - .itemsCallback { _, _, which, _ -> - when (which) { - 0 -> { - presenter.changeMangaFavorite(manga) - adapter?.notifyItemChanged(position) - activity?.toast(activity?.getString(R.string.manga_removed_library)) - } - } - }.show() - } else { presenter.changeMangaFavorite(manga) adapter?.notifyItemChanged(position) + snack = + catalogue_view?.snack(activity.getString(R.string.manga_removed_library), 5000) { + setAction(R.string.action_undo) { + if (!manga.favorite) addManga(manga, position) + } + } + } else { + addManga(manga, position) + snack = + catalogue_view?.snack(activity.getString(R.string.manga_added_library), Snackbar.LENGTH_SHORT) + } + } - val categories = presenter.getCategories() - val defaultCategory = categories.find { it.id == preferences.defaultCategory() } - if (defaultCategory != null) { - presenter.moveMangaToCategory(manga, defaultCategory) - } else if (categories.size <= 1) { // default or the one from the user - presenter.moveMangaToCategory(manga, categories.firstOrNull()) - } else { - val ids = presenter.getMangaCategoryIds(manga) - val preselected = ids.mapNotNull { id -> - categories.indexOfFirst { it.id == id }.takeIf { it != -1 } - }.toTypedArray() + private fun addManga(manga: Manga, position: Int) { + presenter.changeMangaFavorite(manga) + adapter?.notifyItemChanged(position) - ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) - .showDialog(router) - } - activity?.toast(activity?.getString(R.string.manga_added_library)) + val categories = presenter.getCategories() + val defaultCategory = categories.find { it.id == preferences.defaultCategory() } + if (defaultCategory != null) { + presenter.moveMangaToCategory(manga, defaultCategory) + } else if (categories.size <= 1) { // default or the one from the user + presenter.moveMangaToCategory(manga, categories.firstOrNull()) + } else { + val ids = presenter.getMangaCategoryIds(manga) + val preselected = ids.mapNotNull { id -> + categories.indexOfFirst { it.id == id }.takeIf { it != -1 } + }.toTypedArray() + + ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected).showDialog(router) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt index 6bc440eae6..017b1ba496 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaCategory +import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.SourceManager @@ -254,7 +255,9 @@ open class BrowseCataloguePresenter( fun changeMangaFavorite(manga: Manga) { manga.favorite = !manga.favorite if (!manga.favorite) { - coverCache.deleteFromCache(manga.thumbnail_url) + coverCache.deleteFromCache(manga, 5000) + val downloadManager: DownloadManager = Injekt.get() + downloadManager.deleteManga(manga,source,5000) } db.insertManga(manga).executeAsBlocking() } 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 29a43b90ac..81f65ba012 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 @@ -8,11 +8,11 @@ import android.net.Uri import android.os.Bundle import com.google.android.material.tabs.TabLayout import androidx.core.graphics.drawable.DrawableCompat -import androidx.drawerlayout.widget.DrawerLayout import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView import android.view.* +import androidx.core.view.GravityCompat import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.f2prateek.rx.preferences.Preference @@ -177,7 +177,8 @@ class LibraryController( override fun createSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout): ViewGroup { val view = drawer.inflate(R.layout.library_drawer) as LibraryNavigationView navView = view - drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.END) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED, + GravityCompat.END) navView?.onGroupClicked = { group -> when (group) { @@ -365,7 +366,7 @@ class LibraryController( override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_filter -> { - navView?.let { activity?.drawer?.openDrawer(Gravity.END) } + navView?.let { activity?.drawer?.openDrawer(GravityCompat.END) } } R.id.action_update_library -> { activity?.let { LibraryUpdateService.start(it) } @@ -413,7 +414,7 @@ class LibraryController( destroyActionModeIfNeeded() } R.id.action_move_to_category -> showChangeMangaCategoriesDialog() - R.id.action_delete -> showDeleteMangaDialog() + R.id.action_delete -> deleteMangasFromLibrary() else -> return false } return true @@ -470,8 +471,15 @@ class LibraryController( .showDialog(router) } - private fun showDeleteMangaDialog() { - DeleteLibraryMangasDialog(this, selectedMangas.toList()).showDialog(router) + private fun deleteMangasFromLibrary() { + val mangas = selectedMangas.toList() + presenter.removeMangaFromLibrary(mangas, true) + destroyActionModeIfNeeded() + view?.snack(activity?.getString(R.string.remove_from_library) ?: "", 5000) { + setAction(R.string.action_undo) { + presenter.addMangas(mangas) + } + } } override fun updateCategoriesForMangas(mangas: List, categories: List) { 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 17ac0cba58..1c1b6fd182 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 @@ -222,16 +222,13 @@ class LibraryPresenter( * @return an observable of the categories and its manga. */ private fun getLibraryObservable(): Observable { - return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable(), - { dbCategories, libraryManga -> - val categories = if (libraryManga.containsKey(0)) - arrayListOf(Category.createDefault()) + dbCategories - else - dbCategories + return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable()) { dbCategories, libraryManga -> + val categories = if (libraryManga.containsKey(0)) arrayListOf(Category.createDefault()) + dbCategories + else dbCategories - this.categories = categories - Library(categories, libraryManga) - }) + this.categories = categories + Library(categories, libraryManga) + } } /** @@ -316,11 +313,11 @@ class LibraryPresenter( Observable.fromCallable { mangaToDelete.forEach { manga -> - coverCache.deleteFromCache(manga.thumbnail_url) + coverCache.deleteFromCache(manga, 5000) if (deleteChapters) { val source = sourceManager.get(manga.source) as? HttpSource if (source != null) { - downloadManager.deleteManga(manga, source) + downloadManager.deleteManga(manga, source, 5000) } } } @@ -329,6 +326,17 @@ class LibraryPresenter( .subscribe() } + fun addMangas(mangas: List) { + val mangaToAdd = mangas.distinctBy { it.id } + mangaToAdd.forEach { it.favorite = true } + + Observable.fromCallable { db.insertMangas(mangaToAdd).executeAsBlocking() } + .onErrorResumeNext { Observable.empty() } + .subscribeOn(Schedulers.io()) + .subscribe() + mangaToAdd.forEach { db.insertManga(it).executeAsBlocking() } + } + /** * Move the given list of manga to categories. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index 76948bc69e..efc78efea8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -421,24 +421,18 @@ class MangaInfoController : NucleusController(), private fun showAddedSnack() { val view = container snack?.dismiss() - snack = view?.snack(view.context.getString(R.string.manga_added_library), Snackbar - .LENGTH_SHORT) + snack = view?.snack(view.context.getString(R.string.manga_added_library), Snackbar.LENGTH_SHORT) } private fun showRemovedSnack() { val view = container - val hasDownloads = presenter.hasDownloads() snack?.dismiss() if (view != null) { - val message = view.context.getString(R.string.manga_removed_library) + - (if (hasDownloads) "\n" + view.context.getString(R.string - .delete_downloads_for_manga) else "") - snack = view.snack(message, (if (hasDownloads) Snackbar.LENGTH_INDEFINITE - else Snackbar.LENGTH_SHORT)) { - if (hasDownloads) setAction(R.string.action_delete) { - presenter.deleteDownloads() - } + snack = view.snack(view.context.getString(R.string.manga_removed_library), 5000) { + setAction(R.string.action_undo) { + presenter.setFavorite(true) } + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt index 5083498cea..5e389d2e31 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt @@ -101,34 +101,21 @@ class MangaInfoPresenter( fun toggleFavorite(): Boolean { manga.favorite = !manga.favorite if (!manga.favorite) { - coverCache.deleteFromCache(manga.thumbnail_url) + coverCache.deleteFromCache(manga, 5000) + downloadManager.deleteManga(manga, source, 5000) } db.insertManga(manga).executeAsBlocking() sendMangaToView() return manga.favorite } - private fun setFavorite(favorite: Boolean) { + fun setFavorite(favorite: Boolean) { if (manga.favorite == favorite) { return } toggleFavorite() } - /** - * Returns true if the manga has any downloads. - */ - fun hasDownloads(): Boolean { - return downloadManager.getDownloadCount(manga) > 0 - } - - /** - * Deletes all the downloads for the manga. - */ - fun deleteDownloads() { - downloadManager.deleteManga(manga, source) - } - /** * Get the default, and user categories. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt index 06b2917a21..7f5def81db 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt @@ -40,7 +40,7 @@ fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG, f: (Snackbar Unit)? = null): Snackbar { val snack = Snackbar.make(this, message, length) val textView: TextView = snack.view.findViewById(com.google.android.material.R.id.snackbar_text) - textView.setTextColor(Color.WHITE) + textView.setTextColor(context.getResourceColor(android.R.attr.textColorPrimaryInverse)) when { Build.VERSION.SDK_INT >= 23 -> snack.config(context, rootWindowInsets.systemWindowInsetBottom) else -> snack.config(context) diff --git a/app/src/main/res/drawable/bg_snackbar.xml b/app/src/main/res/drawable/bg_snackbar.xml index 63ea0348c8..ab37d237f8 100644 --- a/app/src/main/res/drawable/bg_snackbar.xml +++ b/app/src/main/res/drawable/bg_snackbar.xml @@ -1,6 +1,6 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index e08f4af761..493da64e2e 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -3,5 +3,6 @@ @color/md_white_1000_12 @color/colorAccentDark #B3000000 + #FFFFFF @color/colorDarkPrimary \ 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 c1f38c8fb6..36f7d3e52e 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -6,6 +6,7 @@ @color/md_black_1000_12 @color/colorPrimary #FFFFFF + #323232 #212121 #212121