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 25cda71f4b..1b25c11911 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 @@ -1,7 +1,5 @@ package eu.kanade.tachiyomi.ui.library -import android.app.Activity -import android.content.Intent import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater @@ -37,7 +35,6 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight import eu.kanade.tachiyomi.ui.manga.MangaController -import eu.kanade.tachiyomi.util.hasCustomCover import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.gone @@ -52,7 +49,6 @@ import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.appcompat.queryTextChanges import reactivecircus.flowbinding.viewpager.pageSelections import rx.Subscription -import timber.log.Timber import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -64,7 +60,6 @@ class LibraryController( RootController, TabbedController, ActionMode.Callback, - ChangeMangaCoverDialog.Listener, ChangeMangaCategoriesDialog.Listener, DeleteLibraryMangasDialog.Listener { @@ -88,8 +83,6 @@ class LibraryController( */ val selectedMangas = mutableSetOf() - private var selectedCoverManga: Manga? = null - /** * Relay to notify the UI of selection updates. */ @@ -468,7 +461,6 @@ class LibraryController( } else { mode.title = count.toString() - binding.actionToolbar.findItem(R.id.action_edit_cover)?.isVisible = count == 1 binding.actionToolbar.findItem(R.id.action_download_unread)?.isVisible = selectedMangas.any { it.source != LocalSource.ID } } return false @@ -480,7 +472,6 @@ class LibraryController( private fun onActionItemClicked(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_edit_cover -> handleChangeCover() R.id.action_move_to_category -> showChangeMangaCategoriesDialog() R.id.action_download_unread -> downloadUnreadChapters() R.id.action_delete -> showDeleteMangaDialog() @@ -540,23 +531,6 @@ class LibraryController( } } - private fun handleChangeCover() { - val manga = selectedMangas.firstOrNull() ?: return - - if (manga.hasCustomCover(coverCache)) { - showEditCoverDialog(manga) - } else { - openMangaCoverPicker(manga) - } - } - - /** - * Edit custom cover for selected manga. - */ - private fun showEditCoverDialog(manga: Manga) { - ChangeMangaCoverDialog(this, manga).showDialog(router) - } - /** * Move the selected manga to a list of categories. */ @@ -586,31 +560,6 @@ class LibraryController( DeleteLibraryMangasDialog(this, selectedMangas.toList()).showDialog(router) } - override fun openMangaCoverPicker(manga: Manga) { - selectedCoverManga = manga - - if (manga.favorite) { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.type = "image/*" - startActivityForResult( - Intent.createChooser( - intent, - resources?.getString(R.string.file_select_cover) - ), - REQUEST_IMAGE_OPEN - ) - } else { - activity?.toast(R.string.notification_first_add_to_library) - } - - destroyActionModeIfNeeded() - } - - override fun deleteMangaCover(manga: Manga) { - presenter.deleteCustomCover(manga) - destroyActionModeIfNeeded() - } - override fun updateCategoriesForMangas(mangas: List, categories: List) { presenter.moveMangasToCategories(categories, mangas) destroyActionModeIfNeeded() @@ -632,32 +581,4 @@ class LibraryController( selectInverseRelay.call(it) } } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode == REQUEST_IMAGE_OPEN) { - val dataUri = data?.data - if (dataUri == null || resultCode != Activity.RESULT_OK) return - val activity = activity ?: return - val manga = selectedCoverManga ?: return - - selectedCoverManga = null - presenter.editCover(manga, activity, dataUri) - } - } - - fun onSetCoverSuccess() { - activity?.toast(R.string.cover_updated) - } - - fun onSetCoverError(error: Throwable) { - activity?.toast(R.string.notification_cover_update_failed) - Timber.e(error) - } - - private companion object { - /** - * Key to change the cover of a manga in [onActivityResult]. - */ - const val REQUEST_IMAGE_OPEN = 101 - } } 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 9b1290ffc1..fd1d280f80 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 @@ -1,7 +1,5 @@ package eu.kanade.tachiyomi.ui.library -import android.content.Context -import android.net.Uri import android.os.Bundle import com.jakewharton.rxrelay.BehaviorRelay import eu.kanade.tachiyomi.data.cache.CoverCache @@ -11,7 +9,6 @@ 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.LocalSource import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource @@ -21,7 +18,6 @@ import eu.kanade.tachiyomi.util.lang.combineLatest import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.removeCovers -import eu.kanade.tachiyomi.util.updateCoverLastModified import java.util.Collections import java.util.Comparator import rx.Observable @@ -374,46 +370,4 @@ class LibraryPresenter( db.setMangaCategories(mc, mangas) } - - /** - * Update cover with local file. - * - * @param manga the manga edited. - * @param context Context. - * @param data uri of the cover resource. - */ - fun editCover(manga: Manga, context: Context, data: Uri) { - Observable - .fromCallable { - context.contentResolver.openInputStream(data)?.use { - if (manga.isLocal()) { - LocalSource.updateCover(context, manga, it) - manga.updateCoverLastModified(db) - } else if (manga.favorite) { - coverCache.setCustomCoverToCache(manga, it) - manga.updateCoverLastModified(db) - } - } - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeFirst( - { view, _ -> view.onSetCoverSuccess() }, - { view, e -> view.onSetCoverError(e) } - ) - } - - fun deleteCustomCover(manga: Manga) { - Observable - .fromCallable { - coverCache.deleteCustomCover(manga) - manga.updateCoverLastModified(db) - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeFirst( - { view, _ -> view.onSetCoverSuccess() }, - { view, e -> view.onSetCoverError(e) } - ) - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index d9b0af010c..bcd78118f6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -26,6 +26,7 @@ import com.google.android.material.snackbar.Snackbar import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.SelectableAdapter import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Chapter @@ -44,6 +45,7 @@ import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog +import eu.kanade.tachiyomi.ui.library.ChangeMangaCoverDialog import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight @@ -59,6 +61,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.recent.history.HistoryController import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController import eu.kanade.tachiyomi.ui.webview.WebViewActivity +import eu.kanade.tachiyomi.util.hasCustomCover import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.getCoordinates @@ -81,6 +84,7 @@ class MangaController : ActionMode.Callback, FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemLongClickListener, + ChangeMangaCoverDialog.Listener, ChangeMangaCategoriesDialog.Listener, DownloadCustomChaptersDialog.Listener, DeleteChaptersDialog.Listener { @@ -113,6 +117,7 @@ class MangaController : private val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false) private val preferences: PreferencesHelper by injectLazy() + private val coverCache: CoverCache by injectLazy() private var mangaInfoAdapter: MangaInfoHeaderAdapter? = null private var chaptersHeaderAdapter: MangaChaptersHeaderAdapter? = null @@ -310,7 +315,8 @@ class MangaController : // Hide download options for local manga menu.findItem(R.id.download_group).isVisible = !isLocalSource - // Hide migrate option for non-library manga + // Hide edit cover and migrate options for non-library manga + menu.findItem(R.id.action_edit_cover).isVisible = presenter.manga.favorite menu.findItem(R.id.action_migrate).isVisible = presenter.manga.favorite } @@ -371,6 +377,7 @@ class MangaController : activity?.invalidateOptionsMenu() } + R.id.action_edit_cover -> handleChangeCover() R.id.action_migrate -> migrateManga() } return super.onOptionsItemSelected(item) @@ -582,22 +589,78 @@ class MangaController : } } - // Manga info - end + private fun handleChangeCover() { + val manga = manga ?: return + if (manga.hasCustomCover(coverCache)) { + showEditCoverDialog(manga) + } else { + openMangaCoverPicker(manga) + } + } - // Chapters list - start + /** + * Edit custom cover for selected manga. + */ + private fun showEditCoverDialog(manga: Manga) { + ChangeMangaCoverDialog(this, manga).showDialog(router) + } + + override fun openMangaCoverPicker(manga: Manga) { + if (manga.favorite) { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "image/*" + startActivityForResult( + Intent.createChooser( + intent, + resources?.getString(R.string.file_select_cover) + ), + REQUEST_IMAGE_OPEN + ) + } else { + activity?.toast(R.string.notification_first_add_to_library) + } + + destroyActionModeIfNeeded() + } + + override fun deleteMangaCover(manga: Manga) { + presenter.deleteCustomCover(manga) + mangaInfoAdapter?.notifyDataSetChanged() + destroyActionModeIfNeeded() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == REQUEST_IMAGE_OPEN) { + val dataUri = data?.data + if (dataUri == null || resultCode != Activity.RESULT_OK) return + val activity = activity ?: return + presenter.editCover(manga!!, activity, dataUri) + } + } + + fun onSetCoverSuccess() { + mangaInfoAdapter?.notifyDataSetChanged() + activity?.toast(R.string.cover_updated) + } + + fun onSetCoverError(error: Throwable) { + activity?.toast(R.string.notification_cover_update_failed) + Timber.e(error) + } /** * Initiates source migration for the specific manga. */ private fun migrateManga() { - val controller = - SearchController( - presenter.manga - ) + val controller = SearchController(presenter.manga) controller.targetController = this router.pushController(controller.withFadeTransaction()) } + // Manga info - end + + // Chapters list - start + fun onNextChapters(chapters: List) { // If the list is empty and it hasn't requested previously, fetch chapters from source // We use presenter chapters instead because they are always unfiltered @@ -943,5 +1006,10 @@ class MangaController : companion object { const val FROM_SOURCE_EXTRA = "from_source" const val MANGA_EXTRA = "manga" + + /** + * Key to change the cover of a manga in [onActivityResult]. + */ + const val REQUEST_IMAGE_OPEN = 101 } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index fd0e173389..0bc875b080 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -1,5 +1,7 @@ package eu.kanade.tachiyomi.ui.manga +import android.content.Context +import android.net.Uri import android.os.Bundle import com.jakewharton.rxrelay.PublishRelay import eu.kanade.tachiyomi.data.cache.CoverCache @@ -11,6 +13,7 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem @@ -21,6 +24,7 @@ import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.shouldDownloadNewChapters +import eu.kanade.tachiyomi.util.updateCoverLastModified import java.util.Date import rx.Observable import rx.Subscription @@ -217,6 +221,48 @@ class MangaPresenter( moveMangaToCategories(manga, listOfNotNull(category)) } + /** + * Update cover with local file. + * + * @param manga the manga edited. + * @param context Context. + * @param data uri of the cover resource. + */ + fun editCover(manga: Manga, context: Context, data: Uri) { + Observable + .fromCallable { + context.contentResolver.openInputStream(data)?.use { + if (manga.isLocal()) { + LocalSource.updateCover(context, manga, it) + manga.updateCoverLastModified(db) + } else if (manga.favorite) { + coverCache.setCustomCoverToCache(manga, it) + manga.updateCoverLastModified(db) + } + } + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeFirst( + { view, _ -> view.onSetCoverSuccess() }, + { view, e -> view.onSetCoverError(e) } + ) + } + + fun deleteCustomCover(manga: Manga) { + Observable + .fromCallable { + coverCache.deleteCustomCover(manga) + manga.updateCoverLastModified(db) + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribeFirst( + { view, _ -> view.onSetCoverSuccess() }, + { view, e -> view.onSetCoverError(e) } + ) + } + // Manga info - end // Chapters list - start diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index 6a3d7eeae8..844736a1f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -12,6 +12,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.data.glide.MangaThumbnail import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.databinding.MangaInfoHeaderBinding @@ -49,6 +50,7 @@ class MangaInfoHeaderAdapter( private lateinit var binding: MangaInfoHeaderBinding private var initialLoad: Boolean = true + private var currentMangaThumbnail: MangaThumbnail? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) @@ -237,9 +239,10 @@ class MangaInfoHeaderAdapter( // Set the favorite drawable to the correct one. setFavoriteButtonState(manga.favorite) - // Set cover if it wasn't already. - if (binding.mangaCover.drawable == null) { - val mangaThumbnail = manga.toMangaThumbnail() + // Set cover if changed. + val mangaThumbnail = manga.toMangaThumbnail() + if (mangaThumbnail != currentMangaThumbnail) { + currentMangaThumbnail = mangaThumbnail listOf(binding.mangaCover, binding.backdrop) .forEach { GlideApp.with(view.context) diff --git a/app/src/main/res/menu/chapters.xml b/app/src/main/res/menu/chapters.xml index e356b755c7..7b7ab3203f 100644 --- a/app/src/main/res/menu/chapters.xml +++ b/app/src/main/res/menu/chapters.xml @@ -97,6 +97,11 @@ + + - -