add migrating anime from the browse tab

This commit is contained in:
jmir1 2021-05-21 23:46:56 +02:00
parent 1e0d9ce737
commit bbd4b3766e
9 changed files with 306 additions and 7 deletions

View file

@ -0,0 +1,14 @@
package eu.kanade.tachiyomi.ui.browse.migration.anime
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
class MigrationAnimeAdapter(controller: MigrationAnimeController) :
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
val coverClickListener: OnCoverClickListener = controller
interface OnCoverClickListener {
fun onCoverClick(position: Int)
}
}

View file

@ -0,0 +1,89 @@
package eu.kanade.tachiyomi.ui.browse.migration.anime
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager
import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.databinding.MigrationMangaControllerBinding
import eu.kanade.tachiyomi.ui.anime.AnimeController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.search.AnimeSearchController
class MigrationAnimeController :
NucleusController<MigrationMangaControllerBinding, MigrationAnimePresenter>,
FlexibleAdapter.OnItemClickListener,
MigrationAnimeAdapter.OnCoverClickListener {
private var adapter: MigrationAnimeAdapter? = null
constructor(sourceId: Long, sourceName: String?) : super(
bundleOf(
SOURCE_ID_EXTRA to sourceId,
SOURCE_NAME_EXTRA to sourceName
)
)
@Suppress("unused")
constructor(bundle: Bundle) : this(
bundle.getLong(SOURCE_ID_EXTRA),
bundle.getString(SOURCE_NAME_EXTRA)
)
private val sourceId: Long = args.getLong(SOURCE_ID_EXTRA)
private val sourceName: String? = args.getString(SOURCE_NAME_EXTRA)
override fun getTitle(): String? {
return sourceName
}
override fun createPresenter(): MigrationAnimePresenter {
return MigrationAnimePresenter(sourceId)
}
override fun createBinding(inflater: LayoutInflater) = MigrationMangaControllerBinding.inflate(inflater)
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = MigrationAnimeAdapter(this)
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter
adapter?.fastScroller = binding.fastScroller
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
fun setAnime(anime: List<MigrationAnimeItem>) {
adapter?.updateDataSet(anime)
}
override fun onItemClick(view: View, position: Int): Boolean {
val item = adapter?.getItem(position) as? MigrationAnimeItem ?: return false
val controller = AnimeSearchController(item.anime)
router.pushController(controller.withFadeTransaction())
return false
}
override fun onCoverClick(position: Int) {
val animeItem = adapter?.getItem(position) as? MigrationAnimeItem ?: return
router.pushController(AnimeController(animeItem.anime).withFadeTransaction())
}
companion object {
const val SOURCE_ID_EXTRA = "source_id_extra"
const val SOURCE_NAME_EXTRA = "source_name_extra"
}
}

View file

@ -0,0 +1,34 @@
package eu.kanade.tachiyomi.ui.browse.migration.anime
import android.view.View
import coil.clear
import coil.loadAny
import coil.transform.RoundedCornersTransformation
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.SourceListItemBinding
class MigrationAnimeHolder(
view: View,
private val adapter: MigrationAnimeAdapter
) : FlexibleViewHolder(view, adapter) {
private val binding = SourceListItemBinding.bind(view)
init {
binding.thumbnail.setOnClickListener {
adapter.coverClickListener.onCoverClick(bindingAdapterPosition)
}
}
fun bind(item: MigrationAnimeItem) {
binding.title.text = item.anime.title
// Update the cover.
val radius = itemView.context.resources.getDimension(R.dimen.card_radius)
binding.thumbnail.clear()
binding.thumbnail.loadAny(item.anime.thumbnail_url) {
transformations(RoundedCornersTransformation(radius))
}
}
}

View file

@ -0,0 +1,40 @@
package eu.kanade.tachiyomi.ui.browse.migration.anime
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Anime
class MigrationAnimeItem(val anime: Anime) : AbstractFlexibleItem<MigrationAnimeHolder>() {
override fun getLayoutRes(): Int {
return R.layout.source_list_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MigrationAnimeHolder {
return MigrationAnimeHolder(view, adapter as MigrationAnimeAdapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: MigrationAnimeHolder,
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (other is MigrationAnimeItem) {
return anime.id == other.anime.id
}
return false
}
override fun hashCode(): Int {
return anime.id!!.hashCode()
}
}

View file

@ -0,0 +1,31 @@
package eu.kanade.tachiyomi.ui.browse.migration.anime
import android.os.Bundle
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Anime
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MigrationAnimePresenter(
private val sourceId: Long,
private val db: AnimeDatabaseHelper = Injekt.get()
) : BasePresenter<MigrationAnimeController>() {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
db.getFavoriteAnimes()
.asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
.map { libraryToMigrationItem(it) }
.subscribeLatestCache(MigrationAnimeController::setAnime)
}
private fun libraryToMigrationItem(library: List<Anime>): List<MigrationAnimeItem> {
return library.filter { it.source == sourceId }
.sortedBy { it.title }
.map { MigrationAnimeItem(it) }
}
}

View file

@ -0,0 +1,48 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.AnimeSource
/**
* Item that contains source information.
*
* @param source Instance of [Source] containing source information.
* @param header The header for this item.
*/
data class AnimeSourceItem(val source: AnimeSource, val animeCount: Int) :
AbstractFlexibleItem<SourceHolder>() {
/**
* Returns the layout resource of this item.
*/
override fun getLayoutRes(): Int {
return R.layout.source_main_controller_card_item
}
/**
* Creates a new view holder for this item.
*/
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): SourceHolder {
return SourceHolder(
view,
adapter as SourceAdapter
)
}
/**
* Binds this item to the given view holder.
*/
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: SourceHolder,
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}
}

View file

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.MigrationSourcesControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.anime.MigrationAnimeController
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController
import eu.kanade.tachiyomi.util.system.openInBrowser
@ -66,12 +67,23 @@ class MigrationSourcesController :
adapter?.updateDataSet(sourcesWithManga)
}
fun setAnimeSources(sourcesWithAnime: List<AnimeSourceItem>) {
adapter?.addItems(adapter?.itemCount!!, sourcesWithAnime)
}
override fun onItemClick(view: View, position: Int): Boolean {
if (adapter?.getItem(position) is AnimeSourceItem) {
val item = adapter?.getItem(position) as? AnimeSourceItem ?: return false
val controller = MigrationAnimeController(item.source.id, item.source.name)
parentController!!.router.pushController(controller.withFadeTransaction())
return false
} else {
val item = adapter?.getItem(position) as? SourceItem ?: return false
val controller = MigrationMangaController(item.source.id, item.source.name)
parentController!!.router.pushController(controller.withFadeTransaction())
return false
}
}
companion object {
const val HELP_URL = "https://tachiyomi.org/help/guides/source-migration/"

View file

@ -1,10 +1,11 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.os.Bundle
import eu.kanade.tachiyomi.data.database.AnimeDatabaseHelper
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Anime
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.*
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
@ -12,7 +13,9 @@ import uy.kohesive.injekt.api.get
class MigrationSourcesPresenter(
private val sourceManager: SourceManager = Injekt.get(),
private val db: DatabaseHelper = Injekt.get()
private val animesourceManager: AnimeSourceManager = Injekt.get(),
private val db: DatabaseHelper = Injekt.get(),
private val animedb: AnimeDatabaseHelper = Injekt.get()
) : BasePresenter<MigrationSourcesController>() {
override fun onCreate(savedState: Bundle?) {
@ -23,6 +26,11 @@ class MigrationSourcesPresenter(
.observeOn(AndroidSchedulers.mainThread())
.map { findSourcesWithManga(it) }
.subscribeLatestCache(MigrationSourcesController::setSources)
animedb.getFavoriteAnimes()
.asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
.map { findSourcesWithAnime(it) }
.subscribeLatestCache(MigrationSourcesController::setAnimeSources)
}
private fun findSourcesWithManga(library: List<Manga>): List<SourceItem> {
@ -37,4 +45,16 @@ class MigrationSourcesPresenter(
.sortedBy { it.source.name.toLowerCase() }
.toList()
}
private fun findSourcesWithAnime(library: List<Anime>): List<AnimeSourceItem> {
return library
.groupBy { it.source }
.filterKeys { it != LocalSource.ID }
.map {
val source = animesourceManager.getOrStub(it.key)
AnimeSourceItem(source, it.value.size)
}
.sortedBy { it.source.name.toLowerCase() }
.toList()
}
}

View file

@ -19,6 +19,17 @@ class SourceHolder(view: View, val adapter: SourceAdapter) :
binding.subtitle.isVisible = source.lang != ""
binding.subtitle.text = LocaleHelper.getDisplayName(source.lang)
itemView.post {
binding.image.setImageDrawable(source.icon())
}
}
fun bind(item: AnimeSourceItem) {
val source = item.source
binding.title.text = "${source.name} (${item.animeCount})"
binding.subtitle.isVisible = source.lang != ""
binding.subtitle.text = LocaleHelper.getDisplayName(source.lang)
itemView.post {
binding.image.setImageDrawable(source.icon())
}