Rewrite migration, split it up into 2 controllers and reorganize the classes

Everything is under the hood, so on top only the back button was fixed in the selct manga screen
This commit is contained in:
Jobobby04 2020-05-26 17:04:07 -04:00
parent 75bddd5105
commit 04e8f0d77f
32 changed files with 352 additions and 294 deletions

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import java.io.Serializable
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -12,7 +13,7 @@ import uy.kohesive.injekt.api.get
/** /**
* A basic interface for creating a source. It could be an online source, a local source, etc... * A basic interface for creating a source. It could be an online source, a local source, etc...
*/ */
interface Source { interface Source : Serializable {
/** /**
* Id for the source. Must be unique. * Id for the source. Must be unique.

View File

@ -20,7 +20,7 @@ import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.ui.base.controller.RxController import eu.kanade.tachiyomi.ui.base.controller.RxController
import eu.kanade.tachiyomi.ui.base.controller.TabbedController import eu.kanade.tachiyomi.ui.base.controller.TabbedController
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionController import eu.kanade.tachiyomi.ui.browse.extension.ExtensionController
import eu.kanade.tachiyomi.ui.browse.migration.MigrationController import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesController
import eu.kanade.tachiyomi.ui.browse.source.SourceController import eu.kanade.tachiyomi.ui.browse.source.SourceController
import kotlinx.android.synthetic.main.main_activity.tabs import kotlinx.android.synthetic.main.main_activity.tabs
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -126,7 +126,7 @@ class BrowseController :
val controller: Controller = when (position) { val controller: Controller = when (position) {
SOURCES_CONTROLLER -> SourceController() SOURCES_CONTROLLER -> SourceController()
EXTENSIONS_CONTROLLER -> ExtensionController() EXTENSIONS_CONTROLLER -> ExtensionController()
MIGRATION_CONTROLLER -> MigrationController() MIGRATION_CONTROLLER -> MigrationSourcesController()
else -> error("Wrong position $position") else -> error("Wrong position $position")
} }
router.setRoot(RouterTransaction.with(controller)) router.setRoot(RouterTransaction.with(controller))

View File

@ -1,17 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.migration
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
class MangaAdapter(controller: MigrationController) :
FlexibleAdapter<IFlexible<*>>(null, controller) {
private var items: List<IFlexible<*>>? = null
override fun updateDataSet(items: MutableList<IFlexible<*>>?) {
if (this.items !== items) {
this.items = items
super.updateDataSet(items)
}
}
}

View File

@ -1,151 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.migration
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MigrationControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.browse.migration.manga.design.PreMigrationController
import eu.kanade.tachiyomi.util.lang.launchUI
import exh.util.RecyclerWindowInsetsListener
import exh.util.applyWindowInsetsForController
import exh.util.await
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MigrationController :
NucleusController<MigrationControllerBinding, MigrationPresenter>(),
FlexibleAdapter.OnItemClickListener,
SourceAdapter.OnAllClickListener,
MigrationInterface {
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
private var title: String? = null
set(value) {
field = value
setTitle()
}
override fun createPresenter(): MigrationPresenter {
return MigrationPresenter()
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = MigrationControllerBinding.inflate(inflater)
return binding.root
}
fun searchController(manga: Manga): SearchController {
val controller = SearchController(manga)
controller.targetController = this
return controller
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
view.applyWindowInsetsForController()
adapter = FlexibleAdapter(null, this)
binding.recycler.layoutManager =
androidx.recyclerview.widget.LinearLayoutManager(view.context)
binding.recycler.adapter = adapter
binding.recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
override fun getTitle(): String? {
return title
}
override fun handleBack(): Boolean {
return if (presenter.state.selectedSource != null) {
presenter.deselectSource()
true
} else {
super.handleBack()
}
}
fun render(state: ViewState) {
if (state.selectedSource == null) {
title = resources?.getString(R.string.source_migration)
if (adapter !is SourceAdapter) {
adapter = SourceAdapter(this)
binding.recycler.adapter = adapter
adapter?.fastScroller = binding.fastScroller
}
adapter?.updateDataSet(state.sourcesWithManga)
} else {
// val switching = title == resources?.getString(R.string.source_migration)
title = state.selectedSource.toString()
if (adapter !is MangaAdapter) {
adapter = MangaAdapter(this)
binding.recycler.adapter = adapter
adapter?.fastScroller = binding.fastScroller
}
adapter?.updateDataSet(state.mangaForSource, true)
/*if (switching) launchUI {
migration_recycler.alpha = 0f
migration_recycler.animate().alpha(1f).setStartDelay(100).setDuration(200).start()
}*/
}
}
override fun onItemClick(view: View?, position: Int): Boolean {
val item = adapter?.getItem(position) ?: return false
if (item is MangaItem) {
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
parentController!!.router,
listOf(item.manga.id!!)
)
} else if (item is SourceItem) {
presenter.setSelectedSource(item.source)
}
return false
}
override fun onAllClick(position: Int) {
val item = adapter?.getItem(position) as? SourceItem ?: return
launchUI {
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().asRxSingle().await(
Schedulers.io()
)
val sourceMangas =
manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList()
withContext(Dispatchers.Main) {
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
parentController!!.router,
sourceMangas
)
}
}
}
override fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga? {
presenter.migrateManga(prevManga, manga, replace)
return null
}
}
interface MigrationInterface {
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga?
}

View File

@ -6,7 +6,7 @@ import com.afollestad.materialdialogs.MaterialDialog
import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Controller
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.browse.migration.manga.process.MigrationListController import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
class MigrationMangaDialog<T>(bundle: Bundle? = null) : DialogController(bundle) class MigrationMangaDialog<T>(bundle: Bundle? = null) : DialogController(bundle)
where T : Controller { where T : Controller {

View File

@ -1,10 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.migration
import eu.kanade.tachiyomi.source.Source
data class ViewState(
val selectedSource: Source? = null,
val mangaForSource: List<MangaItem> = emptyList(),
val sourcesWithManga: List<SourceItem> = emptyList(),
val isReplacingManga: Boolean = false
)

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.design package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
import android.app.Activity import android.app.Activity
import android.content.res.Configuration import android.content.res.Configuration

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.design package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
import android.os.Bundle import android.os.Bundle
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.design package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
import android.view.View import android.view.View

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.design package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
import android.os.Parcelable import android.os.Parcelable
import android.view.View import android.view.View

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.design package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -20,8 +20,8 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.manga.process.MigrationListController import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
import eu.kanade.tachiyomi.ui.browse.migration.manga.process.MigrationProcedureConfig import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationProcedureConfig
import exh.util.doOnApplyWindowInsets import exh.util.doOnApplyWindowInsets
import exh.util.marginBottom import exh.util.marginBottom
import exh.util.updateLayoutParams import exh.util.updateLayoutParams

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.graphics.Color import android.graphics.Color
@ -27,8 +27,8 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.MigrationMangaDialog import eu.kanade.tachiyomi.ui.browse.migration.MigrationMangaDialog
import eu.kanade.tachiyomi.ui.browse.migration.SearchController import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
@ -349,7 +349,11 @@ class MigrationListController(bundle: Bundle? = null) :
} else { } else {
sources.filter { it.id != manga.source } sources.filter { it.id != manga.source }
} }
val searchController = SearchController(manga, validSources) val searchController =
SearchController(
manga,
validSources
)
searchController.targetController = this@MigrationListController searchController.targetController = this@MigrationListController
router.pushController(searchController.withFadeTransaction()) router.pushController(searchController.withFadeTransaction())
} }

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import android.os.Parcelable import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import android.view.MenuItem import android.view.MenuItem
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import android.view.View import android.view.View
import android.widget.PopupMenu import android.widget.PopupMenu

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga.process package eu.kanade.tachiyomi.ui.browse.migration.advanced.process
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.view.View import android.view.View
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.os.Parcelable
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -7,15 +8,20 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import kotlinx.android.parcel.Parcelize
class MangaItem(val manga: Manga) : AbstractFlexibleItem<MangaHolder>() { @Parcelize
class MangaItem(val manga: Manga) : AbstractFlexibleItem<MangaHolder>(), Parcelable {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.source_list_item return R.layout.source_list_item
} }
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaHolder {
return MangaHolder(view, adapter) return MangaHolder(
view,
adapter
)
} }
override fun bindViewHolder( override fun bindViewHolder(

View File

@ -0,0 +1,95 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MigrationControllerBinding
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.source.SourceDividerItemDecoration
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MigrationMangaController :
NucleusController<MigrationControllerBinding, MigrationMangaPresenter>,
FlexibleAdapter.OnItemClickListener,
MigrationInterface {
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
constructor(source: Source) : super(
Bundle().apply {
putSerializable(SOURCE_EXTRA, source)
}
)
@Suppress("unused")
constructor(bundle: Bundle) : this(bundle.getSerializable(SOURCE_EXTRA) as Source)
private val source: Source = args.getSerializable(SOURCE_EXTRA) as Source
override fun getTitle(): String? {
return source.name
}
override fun createPresenter(): MigrationMangaPresenter {
return MigrationMangaPresenter(
source.id
)
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = MigrationControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
adapter = FlexibleAdapter<IFlexible<*>>(null, this)
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter
binding.recycler.addItemDecoration(SourceDividerItemDecoration(view.context))
adapter?.fastScroller = binding.fastScroller
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
fun setManga(manga: List<MangaItem>) {
adapter?.updateDataSet(manga)
}
override fun onItemClick(view: View, position: Int): Boolean {
val item = adapter?.getItem(position) as? MangaItem
?: return false
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
router,
listOf(item.manga.id!!)
)
return false
}
override fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga? {
presenter.migrateManga(prevManga, manga, replace)
return null
}
companion object {
const val SOURCE_EXTRA = "source_id_extra"
}
}
interface MigrationInterface {
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga?
}

View File

@ -1,37 +1,26 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.os.Bundle import android.os.Bundle
import com.jakewharton.rxrelay.BehaviorRelay
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.combineLatest import exh.debug.DebugFunctions.sourceManager
import rx.Observable import rx.Observable
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class MigrationPresenter( class MigrationMangaPresenter(
private val sourceManager: SourceManager = Injekt.get(), private val sourceId: Long,
private val db: DatabaseHelper = Injekt.get(), private val db: DatabaseHelper = Injekt.get()
private val preferences: PreferencesHelper = Injekt.get() ) : BasePresenter<MigrationMangaController>() {
) : BasePresenter<MigrationController>() {
var state = ViewState()
private set(value) {
field = value
stateRelay.call(value)
}
private val stateRelay = BehaviorRelay.create(state)
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
@ -39,54 +28,24 @@ class MigrationPresenter(
db.getFavoriteMangas() db.getFavoriteMangas()
.asRxObservable() .asRxObservable()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { state = state.copy(sourcesWithManga = findSourcesWithManga(it)) } .map { libraryToMigrationItem(it) }
.combineLatest( .subscribeLatestCache(MigrationMangaController::setManga)
stateRelay.map { it.selectedSource }
.distinctUntilChanged()
) { library, source -> library to source }
.filter { (_, source) -> source != null }
.observeOn(Schedulers.io())
.map { (library, source) -> libraryToMigrationItem(library, source!!.id) }
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { state = state.copy(mangaForSource = it) }
.subscribe()
stateRelay
// Render the view when any field other than isReplacingManga changes
.distinctUntilChanged { t1, t2 -> t1.isReplacingManga != t2.isReplacingManga }
.subscribeLatestCache(MigrationController::render)
} }
fun setSelectedSource(source: Source) { private fun libraryToMigrationItem(library: List<Manga>): List<MangaItem> {
state = state.copy(selectedSource = source, mangaForSource = emptyList()) return library.filter { it.source == sourceId }
} .sortedBy { it.title }
.map { MangaItem(it) }
fun deselectSource() {
state = state.copy(selectedSource = null, mangaForSource = emptyList())
}
private fun findSourcesWithManga(library: List<Manga>): List<SourceItem> {
val header = SelectionHeader()
return library.map { it.source }.toSet()
.mapNotNull { if (it != LocalSource.ID) sourceManager.getOrStub(it) else null }
.sortedBy { it.name }
.map { SourceItem(it, header) }
}
private fun libraryToMigrationItem(library: List<Manga>, sourceId: Long): List<MangaItem> {
return library.filter { it.source == sourceId }.map(::MangaItem)
} }
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) { fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
val source = sourceManager.get(manga.source) ?: return val source = sourceManager.get(manga.source) ?: return
state = state.copy(isReplacingManga = true)
Observable.defer { source.fetchChapterList(manga) }.onErrorReturn { emptyList() } Observable.defer { source.fetchChapterList(manga) }.onErrorReturn { emptyList() }
.doOnNext { migrateMangaInternal(source, it, prevManga, manga, replace) } .doOnNext { migrateMangaInternal(source, it, prevManga, manga, replace) }
.onErrorReturn { emptyList() }.subscribeOn(Schedulers.io()) .onErrorReturn { emptyList() }.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnUnsubscribe { state = state.copy(isReplacingManga = false) }.subscribe() .subscribe()
} }
private fun migrateMangaInternal( private fun migrateMangaInternal(
@ -96,10 +55,19 @@ class MigrationPresenter(
manga: Manga, manga: Manga,
replace: Boolean replace: Boolean
) { ) {
val flags = preferences.migrateFlags().get() val flags = Injekt.get<PreferencesHelper>().migrateFlags().get()
val migrateChapters = MigrationFlags.hasChapters(flags) val migrateChapters =
val migrateCategories = MigrationFlags.hasCategories(flags) MigrationFlags.hasChapters(
val migrateTracks = MigrationFlags.hasTracks(flags) flags
)
val migrateCategories =
MigrationFlags.hasCategories(
flags
)
val migrateTracks =
MigrationFlags.hasTracks(
flags
)
db.inTransaction { db.inTransaction {
// Update chapters read // Update chapters read

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.search
import android.app.Dialog import android.app.Dialog
import android.os.Bundle import android.os.Bundle
@ -15,7 +15,9 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.manga.process.MigrationListController import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListController
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationInterface
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@ -50,7 +52,11 @@ class SearchController(
} }
override fun createPresenter(): GlobalSearchPresenter { override fun createPresenter(): GlobalSearchPresenter {
return SearchPresenter(initialQuery, manga!!, sources = sources) return SearchPresenter(
initialQuery,
manga!!,
sources = sources
)
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
@ -85,7 +91,8 @@ class SearchController(
}*/ }*/
fun migrateManga() { fun migrateManga() {
val target = targetController as? MigrationInterface ?: return val target = targetController as? MigrationInterface
?: return
val manga = manga ?: return val manga = manga ?: return
val newManga = newManga ?: return val newManga = newManga ?: return
@ -94,7 +101,8 @@ class SearchController(
} }
fun copyManga() { fun copyManga() {
val target = targetController as? MigrationInterface ?: return val target = targetController as? MigrationInterface
?: return
val manga = manga ?: return val manga = manga ?: return
val newManga = newManga ?: return val newManga = newManga ?: return
@ -105,7 +113,10 @@ class SearchController(
private fun replaceWithNewSearchController(manga: Manga?) { private fun replaceWithNewSearchController(manga: Manga?) {
if (manga != null) { if (manga != null) {
// router.popCurrentController() // router.popCurrentController()
val searchController = SearchController(manga) val searchController =
SearchController(
manga
)
searchController.targetController = targetController searchController.targetController = targetController
searchController.progress = progress + 1 searchController.progress = progress + 1
searchController.totalProgress = totalProgress searchController.totalProgress = totalProgress
@ -123,7 +134,8 @@ class SearchController(
return return
} }
newManga = manga newManga = manga
val dialog = MigrationDialog() val dialog =
MigrationDialog()
dialog.targetController = this dialog.targetController = this
dialog.showDialog(router) dialog.showDialog(router)
} }
@ -140,7 +152,10 @@ class SearchController(
override fun onCreateDialog(savedViewState: Bundle?): Dialog { override fun onCreateDialog(savedViewState: Bundle?): Dialog {
val prefValue = preferences.migrateFlags().get() val prefValue = preferences.migrateFlags().get()
val preselected = MigrationFlags.getEnabledFlagsPositions(prefValue) val preselected =
MigrationFlags.getEnabledFlagsPositions(
prefValue
)
return MaterialDialog(activity!!) return MaterialDialog(activity!!)
.message(R.string.data_to_include_in_migration) .message(R.string.data_to_include_in_migration)
@ -149,7 +164,10 @@ class SearchController(
{ resources?.getString(it) as CharSequence }, { resources?.getString(it) as CharSequence },
initialSelection = preselected.toIntArray() initialSelection = preselected.toIntArray()
) { _, positions, _ -> ) { _, positions, _ ->
val newValue = MigrationFlags.getFlagsFromPositions(positions.toTypedArray()) val newValue =
MigrationFlags.getFlagsFromPositions(
positions.toTypedArray()
)
preferences.migrateFlags().set(newValue) preferences.migrateFlags().set(newValue)
} }
.positiveButton(R.string.migrate) { .positiveButton(R.string.migrate) {

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.search
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource

View File

@ -0,0 +1,95 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MigrationControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController
import eu.kanade.tachiyomi.ui.browse.source.SourceDividerItemDecoration
import eu.kanade.tachiyomi.util.lang.launchUI
import exh.util.await
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MigrationSourcesController :
NucleusController<MigrationControllerBinding, MigrationSourcesPresenter>(),
FlexibleAdapter.OnItemClickListener,
SourceAdapter.OnAllClickListener {
private var adapter: SourceAdapter? = null
override fun createPresenter(): MigrationSourcesPresenter {
return MigrationSourcesPresenter()
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = MigrationControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
adapter =
SourceAdapter(this)
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter
binding.recycler.addItemDecoration(SourceDividerItemDecoration(view.context))
adapter?.fastScroller = binding.fastScroller
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
fun setSources(sourcesWithManga: List<SourceItem>) {
adapter?.updateDataSet(sourcesWithManga)
}
override fun getTitle(): String? {
return resources?.getString(R.string.source_migration)
}
override fun onItemClick(view: View?, position: Int): Boolean {
val item = adapter?.getItem(position) as? SourceItem
?: return false
val controller =
MigrationMangaController(
item.source
)
parentController!!.router.pushController(controller.withFadeTransaction())
return false
}
override fun onAllClick(position: Int) {
val item = adapter?.getItem(position) as? SourceItem
?: return
launchUI {
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().asRxSingle().await(
Schedulers.io()
)
val sourceMangas =
manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList()
withContext(Dispatchers.Main) {
PreMigrationController.navigateToMigration(
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
parentController!!.router,
sourceMangas
)
}
}
}
}

View File

@ -0,0 +1,49 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.os.Bundle
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.migration.manga.MangaItem
import exh.MERGED_SOURCE_ID
import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MigrationSourcesPresenter(
private val sourceManager: SourceManager = Injekt.get(),
private val db: DatabaseHelper = Injekt.get(),
private val preferences: PreferencesHelper = Injekt.get()
) : BasePresenter<MigrationSourcesController>() {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
db.getFavoriteMangas()
.asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
.map { findSourcesWithManga(it) }
.subscribeLatestCache(MigrationSourcesController::setSources)
}
private fun findSourcesWithManga(library: List<Manga>): List<SourceItem> {
val header =
SelectionHeader()
return library.map { it.source }.toSet()
.mapNotNull { if (it != LocalSource.ID && it != MERGED_SOURCE_ID) sourceManager.getOrStub(it) else null }
.sortedBy { it.name }
.map {
SourceItem(
it,
header
)
}
}
private fun libraryToMigrationItem(library: List<Manga>, sourceId: Long): List<MangaItem> {
return library.filter { it.source == sourceId }.map(::MangaItem)
}
}

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -25,7 +25,10 @@ class SelectionHeader : AbstractHeaderItem<SelectionHeader.Holder>() {
* Creates a new view holder for this item. * Creates a new view holder for this item.
*/ */
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter) return Holder(
view,
adapter
)
} }
/** /**

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.sources
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
@ -8,15 +8,13 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
/** /**
* Adapter that holds the catalogue cards. * Adapter that holds the catalogue cards.
* *
* @param controller instance of [MigrationController]. * @param controller instance of [MigrationSourcesController].
*/ */
class SourceAdapter(val controller: MigrationController) : class SourceAdapter(val controller: MigrationSourcesController) :
FlexibleAdapter<IFlexible<*>>(null, controller, true) { FlexibleAdapter<IFlexible<*>>(null, controller, true) {
val cardBackground = controller.activity!!.getResourceColor(R.attr.colorSurface) val cardBackground = controller.activity!!.getResourceColor(R.attr.colorSurface)
private var items: List<IFlexible<*>>? = null
init { init {
setDisplayHeadersAtStartUp(true) setDisplayHeadersAtStartUp(true)
} }
@ -32,11 +30,4 @@ class SourceAdapter(val controller: MigrationController) :
interface OnAllClickListener { interface OnAllClickListener {
fun onAllClick(position: Int) fun onAllClick(position: Int)
} }
override fun updateDataSet(items: MutableList<IFlexible<*>>?) {
if (this.items !== items) {
this.items = items
super.updateDataSet(items)
}
}
} }

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.View import android.view.View
import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.source.icon

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.migration package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.source.Source
* @param source Instance of [Source] containing source information. * @param source Instance of [Source] containing source information.
* @param header The header for this item. * @param header The header for this item.
*/ */
data class SourceItem(val source: Source, val header: SelectionHeader? = null) : data class SourceItem(val source: Source, val header: SelectionHeader) :
AbstractSectionableItem<SourceHolder, SelectionHeader>(header) { AbstractSectionableItem<SourceHolder, SelectionHeader>(header) {
/** /**
@ -28,7 +28,10 @@ data class SourceItem(val source: Source, val header: SelectionHeader? = null) :
* Creates a new view holder for this item. * Creates a new view holder for this item.
*/ */
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): SourceHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): SourceHolder {
return SourceHolder(view, adapter as SourceAdapter) return SourceHolder(
view,
adapter as SourceAdapter
)
} }
/** /**

View File

@ -34,8 +34,8 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.RootController import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.ui.base.controller.TabbedController import eu.kanade.tachiyomi.ui.base.controller.TabbedController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.MigrationController import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesController
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
@ -429,7 +429,10 @@ class LibraryController(
} }
} }
R.id.action_source_migration -> { R.id.action_source_migration -> {
router.pushController(MigrationController().withFadeTransaction()) router.pushController(
MigrationSourcesController()
.withFadeTransaction()
)
} }
// --> EXH // --> EXH
R.id.action_sync_favorites -> { R.id.action_sync_favorites -> {

View File

@ -32,7 +32,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.source.SourceController import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController

View File

@ -24,7 +24,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.source.SourceController import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController