Part 2 of Auto-Migration Done

(cherry picked from commit c4321e3adfff1bdfdcd8ba209dd20549348be217)
This commit is contained in:
jobobby04 2020-04-16 23:45:05 -04:00 committed by Jobobby04
parent f1472d4f8b
commit a6f0e7f9b9
16 changed files with 481 additions and 331 deletions

View File

@ -260,6 +260,14 @@ class PreferencesHelper(val context: Context) {
fun skipPreMigration() = flowPrefs.getBoolean(Keys.skipPreMigration, false)
fun migrationSources() = rxPrefs.getString("migrate_sources", "")
fun smartMigration() = rxPrefs.getBoolean("smart_migrate", false)
fun useSourceWithMost() = rxPrefs.getBoolean("use_source_with_most", false)
fun skipPreMigration() = rxPrefs.getBoolean(Keys.skipPreMigration, false)
fun upgradeFilters() {
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()

View File

@ -11,6 +11,8 @@ import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
@ -36,8 +38,9 @@ import eu.kanade.tachiyomi.ui.main.offsetFabAppbarHeight
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.migration.MigrationController
import eu.kanade.tachiyomi.ui.migration.MigrationInterface
import eu.kanade.tachiyomi.ui.migration.SearchController
import eu.kanade.tachiyomi.ui.migration.manga.design.MigrationDesignController
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.inflate
@ -484,9 +487,15 @@ class LibraryController(
R.id.action_select_inverse -> selectInverseCategoryManga()
R.id.action_migrate -> {
router.pushController(
MigrationDesignController.create(
selectedMangas.mapNotNull { it.id }
).withFadeTransaction())
if (preferences.skipPreMigration().getOrDefault()) {
MigrationListController.create(
MigrationProcedureConfig(
selectedMangas.mapNotNull { it.id }, null)
)
} else {
MigrationDesignController.create(selectedMangas.mapNotNull { it.id })
}
.withFadeTransaction())
destroyActionModeIfNeeded()
}
else -> return false
@ -503,18 +512,6 @@ class LibraryController(
return nextManga
}
private fun startMangaMigration() {
migratingMangas.clear()
migratingMangas.addAll(selectedMangas)
destroyActionModeIfNeeded()
val manga = migratingMangas.firstOrNull() ?: return
val searchController = SearchController(manga)
searchController.totalProgress = migratingMangas.size
searchController.targetController = this
router.pushController(searchController.withFadeTransaction())
migratingMangas.remove(manga)
}
override fun onDestroyActionMode(mode: ActionMode?) {
// Clear all the manga selections and notify child views.
selectedMangas.clear()
@ -788,3 +785,15 @@ class LibraryController(
const val REQUEST_IMAGE_OPEN = 101
}
}
object HeightTopWindowInsetsListener : View.OnApplyWindowInsetsListener {
override fun onApplyWindowInsets(v: View, insets: WindowInsets): WindowInsets {
val topInset = insets.systemWindowInsetTop
v.setPadding(0, topInset, 0, 0)
if (v.layoutParams.height != topInset) {
v.layoutParams.height = topInset
v.requestLayout()
}
return insets
}
}

View File

@ -0,0 +1,137 @@
package eu.kanade.tachiyomi.ui.migration.manga.design
import android.app.Activity
import android.content.res.Configuration
import android.os.Bundle
import android.widget.CompoundButton
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.Toast
import com.bluelinelabs.conductor.Controller
import com.f2prateek.rx.preferences.Preference
import com.google.android.material.bottomsheet.BottomSheetDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.migration_bottom_sheet.*
import kotlinx.android.synthetic.main.migration_bottom_sheet.extra_search_param
import kotlinx.android.synthetic.main.migration_bottom_sheet.extra_search_param_text
import kotlinx.android.synthetic.main.migration_bottom_sheet.mig_categories
import kotlinx.android.synthetic.main.migration_bottom_sheet.mig_chapters
import kotlinx.android.synthetic.main.migration_bottom_sheet.mig_tracking
import kotlinx.android.synthetic.main.migration_bottom_sheet.use_smart_search
import uy.kohesive.injekt.injectLazy
class MigrationBottomSheetDialog(
activity: Activity,
theme: Int,
private val listener:
StartMigrationListener
) :
BottomSheetDialog(activity,
theme) {
/**
* Preferences helper.
*/
private val preferences by injectLazy<PreferencesHelper>()
init {
// Use activity theme for this layout
val view = activity.layoutInflater.inflate(R.layout.migration_bottom_sheet, null)
// val scroll = NestedScrollView(context)
// scroll.addView(view)
setContentView(view)
if (activity.resources.configuration?.orientation == Configuration.ORIENTATION_LANDSCAPE)
sourceGroup.orientation = LinearLayout.HORIZONTAL
window?.setBackgroundDrawable(null)
}
/**
* Called when the sheet is created. It initializes the listeners and values of the preferences.
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initPreferences()
fab.setOnClickListener {
preferences.skipPreMigration().set(skip_step.isChecked)
listener.startMigration(
if (use_smart_search.isChecked && extra_search_param_text.text.isNotBlank())
extra_search_param_text.text.toString() else null)
dismiss()
}
}
/**
* Init general reader preferences.
*/
private fun initPreferences() {
val flags = preferences.migrateFlags().getOrDefault()
mig_chapters.isChecked = MigrationFlags.hasChapters(flags)
mig_categories.isChecked = MigrationFlags.hasCategories(flags)
mig_tracking.isChecked = MigrationFlags.hasTracks(flags)
mig_chapters.setOnCheckedChangeListener { _, _ -> setFlags() }
mig_categories.setOnCheckedChangeListener { _, _ -> setFlags() }
mig_tracking.setOnCheckedChangeListener { _, _ -> setFlags() }
use_smart_search.bindToPreference(preferences.smartMigration())
extra_search_param_text.gone()
extra_search_param.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
extra_search_param_text.visible()
} else {
extra_search_param_text.gone()
}
}
sourceGroup.bindToPreference(preferences.useSourceWithMost())
skip_step.isChecked = preferences.skipPreMigration().getOrDefault()
skip_step.setOnCheckedChangeListener { _, isChecked ->
if (isChecked)
(listener as? Controller)?.activity?.toast(R.string.pre_migration_skip_toast,
Toast.LENGTH_LONG)
}
}
private fun setFlags() {
var flags = 0
if (mig_chapters.isChecked) flags = flags or MigrationFlags.CHAPTERS
if (mig_categories.isChecked) flags = flags or MigrationFlags.CATEGORIES
if (mig_categories.isChecked) flags = flags or MigrationFlags.TRACK
preferences.migrateFlags().set(flags)
}
/**
* Binds a checkbox or switch view with a boolean preference.
*/
private fun CompoundButton.bindToPreference(pref: Preference<Boolean>) {
isChecked = pref.getOrDefault()
setOnCheckedChangeListener { _, isChecked -> pref.set(isChecked) }
}
/**
* Binds a radio group with a boolean preference.
*/
private fun RadioGroup.bindToPreference(pref: Preference<Boolean>) {
(getChildAt(pref.getOrDefault().toInt()) as RadioButton).isChecked = true
setOnCheckedChangeListener { _, value ->
val index = indexOfChild(findViewById(value))
pref.set(index == 1)
}
}
private fun Boolean.toInt() = if (this) 1 else 0
}
interface StartMigrationListener {
fun startMigration(extraParam: String?)
}

View File

@ -4,7 +4,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
@ -13,27 +15,18 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visible
import kotlinx.android.synthetic.main.migration_design_controller.begin_migration_btn
import kotlinx.android.synthetic.main.migration_design_controller.extra_search_param
import kotlinx.android.synthetic.main.migration_design_controller.extra_search_param_desc
import kotlinx.android.synthetic.main.migration_design_controller.extra_search_param_text
import kotlinx.android.synthetic.main.migration_design_controller.fuzzy_search
import kotlinx.android.synthetic.main.migration_design_controller.mig_categories
import kotlinx.android.synthetic.main.migration_design_controller.mig_chapters
import kotlinx.android.synthetic.main.migration_design_controller.migration_mode
import kotlinx.android.synthetic.main.migration_design_controller.options_group
import kotlinx.android.synthetic.main.migration_design_controller.prioritize_chapter_count
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
import eu.kanade.tachiyomi.util.view.marginBottom
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import kotlinx.android.synthetic.main.migration_design_controller.fab
import kotlinx.android.synthetic.main.migration_design_controller.recycler
import kotlinx.android.synthetic.main.migration_design_controller.use_smart_search
import uy.kohesive.injekt.injectLazy
class MigrationDesignController(bundle: Bundle? = null) : BaseController(bundle), FlexibleAdapter
.OnItemClickListener {
.OnItemClickListener, StartMigrationListener {
private val sourceManager: SourceManager by injectLazy()
private val prefs: PreferencesHelper by injectLazy()
@ -53,7 +46,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseController(bundle)
super.onViewCreated(view)
val ourAdapter = adapter ?: MigrationSourceAdapter(
getEnabledSources().map { MigrationSourceItem(it, true) },
getEnabledSources().map { MigrationSourceItem(it, isEnabled(it.id.toString())) },
this
)
adapter = ourAdapter
@ -63,83 +56,42 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseController(bundle)
ourAdapter.itemTouchHelperCallback = null // Reset adapter touch adapter to fix drag after rotation
ourAdapter.isHandleDragEnabled = true
migration_mode.setOnClickListener {
prioritize_chapter_count.toggle()
}
val fabBaseMarginBottom = fab?.marginBottom ?: 0
recycler.doOnApplyWindowInsets { v, insets, padding ->
fuzzy_search.setOnClickListener {
use_smart_search.toggle()
}
extra_search_param_desc.setOnClickListener {
extra_search_param.toggle()
}
prioritize_chapter_count.setOnCheckedChangeListener { _, b ->
updatePrioritizeChapterCount(b)
}
extra_search_param.setOnCheckedChangeListener { _, b ->
updateOptionsState()
}
updatePrioritizeChapterCount(prioritize_chapter_count.isChecked)
updateOptionsState()
begin_migration_btn.setOnClickListener {
if (!showingOptions) {
showingOptions = true
updateOptionsState()
return@setOnClickListener
fab?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = fabBaseMarginBottom + insets.systemWindowInsetBottom
}
// offset the recycler by the fab's inset + some inset on top
v.updatePaddingRelative(bottom = padding.bottom + (fab?.marginBottom ?: 0) +
fabBaseMarginBottom + (fab?.height ?: 0))
}
var flags = 0
if (mig_chapters.isChecked) flags = flags or MigrationFlags.CHAPTERS
if (mig_categories.isChecked) flags = flags or MigrationFlags.CATEGORIES
if (mig_categories.isChecked) flags = flags or MigrationFlags.TRACK
fab.setOnClickListener {
val dialog = MigrationBottomSheetDialog(activity!!, R.style.SheetDialog, this)
dialog.show()
val bottomSheet =
dialog.findViewById<FrameLayout>(com.google.android.material.R.id
.design_bottom_sheet)
val behavior: BottomSheetBehavior<*> = BottomSheetBehavior.from(bottomSheet!!)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.skipCollapsed = true
}
}
router.replaceTopController(
MigrationListController.create(
MigrationProcedureConfig(
config.toList(),
ourAdapter.items.filter {
it.sourceEnabled
}.map { it.source.id },
useSourceWithMostChapters = prioritize_chapter_count.isChecked,
enableLenientSearch = use_smart_search.isChecked,
migrationFlags = flags,
extraSearchParams = if (extra_search_param.isChecked && extra_search_param_text.text.isNotBlank()) {
extra_search_param_text.text.toString()
} else null
)
override fun startMigration(extraParam: String?) {
val listOfSources = adapter?.items?.filter {
it.sourceEnabled
}?.joinToString("/") { it.source.id.toString() }
prefs.migrationSources().set(listOfSources)
router.replaceTopController(
MigrationListController.create(
MigrationProcedureConfig(
config.toList(),
extraSearchParams = extraParam
)
).withFadeTransaction())
}
}
fun updateOptionsState() {
if (showingOptions) {
begin_migration_btn.text = "Begin migration"
options_group.visible()
if (extra_search_param.isChecked) {
extra_search_param_text.visible()
} else {
extra_search_param_text.gone()
}
} else {
begin_migration_btn.text = "Next step"
options_group.gone()
extra_search_param_text.gone()
}
}
override fun handleBack(): Boolean {
if (showingOptions) {
showingOptions = false
updateOptionsState()
return true
}
return super.handleBack()
}
override fun onSaveInstanceState(outState: Bundle) {
@ -153,14 +105,6 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseController(bundle)
adapter?.onRestoreInstanceState(savedInstanceState)
}
private fun updatePrioritizeChapterCount(migrationMode: Boolean) {
migration_mode.text = if (migrationMode) {
"Currently using the source with the most chapters and the above list to break ties (slow with many sources or smart search)"
} else {
"Currently using the first source in the list that has the manga"
}
}
override fun onItemClick(view: View, position: Int): Boolean {
adapter?.getItem(position)?.let {
it.sourceEnabled = !it.sourceEnabled
@ -176,13 +120,25 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseController(bundle)
*/
private fun getEnabledSources(): List<HttpSource> {
val languages = prefs.enabledLanguages().getOrDefault()
val hiddenCatalogues = prefs.hiddenCatalogues().getOrDefault()
val sourcesSaved = prefs.migrationSources().getOrDefault().split("/")
var sources = sourceManager.getCatalogueSources()
.filterIsInstance<HttpSource>()
.filter { it.lang in languages }
.sortedBy { "(${it.lang}) ${it.name}" }
sources =
sources.filter { isEnabled(it.id.toString()) }.sortedBy { sourcesSaved.indexOf(it.id
.toString())
} +
sources.filterNot { isEnabled(it.id.toString()) }
return sourceManager.getVisibleCatalogueSources()
.filterIsInstance<HttpSource>()
.filter { it.lang in languages }
.filterNot { it.id.toString() in hiddenCatalogues }
.sortedBy { "(${it.lang}) ${it.name}" }
return sources
}
fun isEnabled(id: String): Boolean {
val sourcesSaved = prefs.migrationSources().getOrDefault()
val hiddenCatalogues = prefs.hiddenCatalogues().getOrDefault()
return if (sourcesSaved.isEmpty()) id !in hiddenCatalogues
else sourcesSaved.split("/").contains(id)
}
companion object {

View File

@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import uy.kohesive.injekt.injectLazy
class MigrationSourceAdapter(
val items: List<MigrationSourceItem>,
var items: List<MigrationSourceItem>,
val controller: MigrationDesignController
) : FlexibleAdapter<MigrationSourceItem>(
items,
@ -32,6 +32,10 @@ class MigrationSourceAdapter(
super.onRestoreInstanceState(savedInstanceState)
}
fun updateItems() {
items = currentItems
}
companion object {
private const val SELECTED_SOURCES_KEY = "selected_sources"
}

View File

@ -16,6 +16,8 @@ import com.afollestad.materialdialogs.MaterialDialog
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.data.preference.getOrDefault
import eu.kanade.tachiyomi.smartsearch.SmartSearchEngine
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
@ -62,6 +64,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
val config: MigrationProcedureConfig? = args.getParcelable(CONFIG_EXTRA)
private val db: DatabaseHelper by injectLazy()
private val preferences: PreferencesHelper by injectLazy()
private val sourceManager: SourceManager by injectLazy()
private val smartSearchEngine = SmartSearchEngine(coroutineContext, config?.extraSearchParams)
@ -95,11 +98,8 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
recycler.adapter = adapter
recycler.layoutManager = LinearLayoutManager(view.context)
// recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration
// .VERTICAL))
recycler.setHasFixedSize(true)
recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
// recycler.isEnabled = false
adapter?.updateDataSet(newMigratingManga.map { it.toModal() })
@ -110,21 +110,6 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
}
}
/*fun nextMigration() {
adapter?.let { adapter ->
if(pager.currentItem >= adapter.count - 1) {
applicationContext?.toast("All migrations complete!")
router.popCurrentController()
} else {
adapter.migratingManga[pager.currentItem].migrationJob.cancel()
pager.setCurrentItem(pager.currentItem + 1, true)
launch(Dispatchers.Main) {
updateTitle()
}
}
}
}*/
fun migrationFailure() {
activity?.let {
MaterialDialog.Builder(it)
@ -136,8 +121,13 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
}
suspend fun runMigrations(mangas: List<MigratingManga>) {
val sources = config?.targetSourceIds?.mapNotNull { sourceManager.get(it) as? CatalogueSource } ?: return
val useSourceWithMost = preferences.useSourceWithMost().getOrDefault()
val useSmartSearch = preferences.smartMigration().getOrDefault()
val sources = preferences.migrationSources().getOrDefault().split("/").mapNotNull {
val value = it.toLongOrNull() ?: return
sourceManager.get(value) as? CatalogueSource }
if (config == null) return
for (manga in mangas) {
if (!manga.searchResult.initialized && manga.migrationJob.isActive) {
val mangaObj = manga.manga()
@ -154,7 +144,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
val validSources = sources.filter {
it.id != mangaSource.id
}
if (config.useSourceWithMostChapters) {
if (useSourceWithMost) {
val sourceSemaphore = Semaphore(3)
val processedSources = AtomicInteger()
@ -162,7 +152,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
async {
sourceSemaphore.withPermit {
try {
val searchResult = if (config.enableLenientSearch) {
val searchResult = if (useSmartSearch) {
smartSearchEngine.smartSearch(source, mangaObj.title)
} else {
smartSearchEngine.normalSearch(source, mangaObj.title)
@ -192,7 +182,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle),
} else {
validSources.forEachIndexed { index, source ->
val searchResult = try {
val searchResult = if (config.enableLenientSearch) {
val searchResult = if (useSmartSearch) {
smartSearchEngine.smartSearch(source, mangaObj.title)
} else {
smartSearchEngine.normalSearch(source, mangaObj.title)

View File

@ -8,7 +8,6 @@ import com.google.gson.Gson
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.database.models.MangaCategory
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
@ -16,7 +15,6 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.inflate
import eu.kanade.tachiyomi.util.view.visible
@ -123,7 +121,7 @@ class MigrationProcedureAdapter(
val config = controller.config ?: return
// db.inTransaction {
// Update chapters read
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {
/* if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
val maxChapterRead = prevMangaChapters.filter { it.read }
.maxBy { it.chapter_number }?.chapter_number
@ -162,7 +160,7 @@ class MigrationProcedureAdapter(
// SearchPresenter#networkToLocalManga may have updated the manga title, so ensure db gets updated title
db.updateMangaTitle(manga).executeAsBlocking()
// }
//}*/
}
fun View.setupView(tag: ViewTag, migratingManga: MigratingManga) {

View File

@ -6,9 +6,5 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
data class MigrationProcedureConfig(
var mangaIds: List<Long>,
val targetSourceIds: List<Long>,
val useSourceWithMostChapters: Boolean,
val enableLenientSearch: Boolean,
val migrationFlags: Int,
val extraSearchParams: String?
) : Parcelable

View File

@ -9,27 +9,16 @@ import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.smartsearch.SmartSearchEngine
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.util.await
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.system.toast
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.CoroutineContext
import kotlinx.android.synthetic.main.migration_process.pager
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
// TODO Will probably implode if activity is fully destroyed
@ -121,7 +110,7 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseController(bund
}
suspend fun runMigrations(mangas: List<MigratingManga>) {
val sources = config?.targetSourceIds?.mapNotNull { sourceManager.get(it) as?
/* val sources = config?.targetSourceIds?.mapNotNull { sourceManager.get(it) as?
CatalogueSource } ?: return
for (manga in mangas) {
@ -229,7 +218,7 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseController(bund
manga.searchResult.initialize(result?.id)
}
}
}*/
}
override fun onDestroy() {

View File

@ -6,6 +6,8 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
import eu.kanade.tachiyomi.util.lang.launchUI
import kotlinx.coroutines.Dispatchers
@ -21,6 +23,7 @@ class MigrationProcessAdapter(
private val db: DatabaseHelper by injectLazy()
var items: List<MigrationProcessItem> = emptyList()
val preferences: PreferencesHelper by injectLazy()
val menuItemListener: MigrationProcessInterface = controller
@ -46,7 +49,7 @@ class MigrationProcessAdapter(
.searchResult.content != null })
fun mangasSkipped() = (items.count { (!it.manga.searchResult.initialized || it.manga
.searchResult.content == null) && !it.manga.migrationJob.isActive })
.searchResult.content == null) })
suspend fun performMigrations(copy: Boolean) {
withContext(Dispatchers.IO) {
@ -95,9 +98,9 @@ class MigrationProcessAdapter(
replace: Boolean
) {
if (controller.config == null) return
// db.inTransaction {
val flags = preferences.migrateFlags().getOrDefault()
// Update chapters read
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {
if (MigrationFlags.hasChapters(flags)) {
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
val maxChapterRead = prevMangaChapters.filter { it.read }
.maxBy { it.chapter_number }?.chapter_number
@ -112,13 +115,13 @@ class MigrationProcessAdapter(
}
}
// Update categories
if (MigrationFlags.hasCategories(controller.config.migrationFlags)) {
if (MigrationFlags.hasCategories(flags)) {
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking()
val mangaCategories = categories.map { MangaCategory.create(manga, it) }
db.setMangaCategories(mangaCategories, listOf(manga))
}
// Update track
if (MigrationFlags.hasTracks(controller.config.migrationFlags)) {
if (MigrationFlags.hasTracks(flags)) {
val tracks = db.getTracks(prevManga).executeAsBlocking()
for (track in tracks) {
track.id = null

View File

@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.util.preference.onChange
import eu.kanade.tachiyomi.util.preference.onClick
import eu.kanade.tachiyomi.util.preference.preference
import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.LocaleHelper
@ -191,6 +192,16 @@ class SettingsGeneralController : SettingsController() {
}
}
if (preferences.skipPreMigration().getOrDefault() || preferences.migrationSources()
.getOrDefault().isNotEmpty()) {
switchPreference {
key = Keys.skipPreMigration
titleRes = R.string.pref_skip_pre_migration
summaryRes = R.string.pref_skip_pre_migration_summary
defaultValue = false
}
}
// --> EXH
switchPreference {
key = Keys.eh_expandFilters

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="?attr/colorSurface" />
<corners android:radius="10dip" />
</shape>

View File

@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="@drawable/dialog_rounded_background"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<TextView
android:id="@+id/data_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/data_to_include_in_migration"
android:textAppearance="@style/TextAppearance.Medium.Body2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<CheckBox
android:id="@+id/mig_chapters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:checked="true"
android:text="@string/chapters"
app:layout_constraintStart_toStartOf="@+id/data_label"
app:layout_constraintTop_toBottomOf="@+id/data_label" />
<CheckBox
android:id="@+id/mig_categories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:checked="true"
android:text="@string/categories"
app:layout_constraintBottom_toBottomOf="@+id/mig_chapters"
app:layout_constraintStart_toEndOf="@+id/mig_chapters"
app:layout_constraintTop_toTopOf="@+id/mig_chapters" />
<CheckBox
android:id="@+id/mig_tracking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:checked="true"
android:text="@string/track"
app:layout_constraintBottom_toBottomOf="@+id/mig_categories"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/mig_categories"
app:layout_constraintTop_toTopOf="@+id/mig_categories" />
<TextView
android:id="@+id/options_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/options"
android:textAppearance="@style/TextAppearance.Medium.Body2"
app:layout_constraintStart_toStartOf="@+id/mig_chapters"
app:layout_constraintTop_toBottomOf="@+id/mig_chapters" />
<RadioGroup
android:id="@+id/sourceGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/options_label"
app:layout_constraintTop_toBottomOf="@+id/options_label">
<RadioButton
android:id="@+id/radioButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="0dp"
android:paddingEnd="8dp"
android:text="@string/use_first_source" />
<RadioButton
android:id="@+id/radioButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/use_most_chapters" />
</RadioGroup>
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/use_smart_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginTop="16dp"
android:text="@string/use_intelligent_search"
app:layout_constraintEnd_toEndOf="@+id/sourceGroup"
app:layout_constraintStart_toStartOf="@+id/sourceGroup"
app:layout_constraintTop_toBottomOf="@+id/sourceGroup" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/extra_search_param"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/include_extra_search_parameter"
app:layout_constraintEnd_toEndOf="@+id/use_smart_search"
app:layout_constraintStart_toStartOf="@+id/use_smart_search"
app:layout_constraintTop_toBottomOf="@+id/use_smart_search" />
<EditText
android:id="@+id/extra_search_param_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:ems="10"
android:hint="@string/search_parameter"
android:importantForAutofill="no"
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/extra_search_param"
app:layout_constraintTop_toBottomOf="@+id/extra_search_param" />
<Switch
android:id="@+id/skip_step"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@string/skip_this_step_next_time"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/extra_search_param"
app:layout_constraintStart_toStartOf="@+id/extra_search_param"
app:layout_constraintTop_toBottomOf="@+id/extra_search_param_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:src="@drawable/ic_arrow_forward_24dp"
app:layout_anchor="@id/constraintLayout"
app:layout_anchorGravity="bottom|end"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
@ -10,174 +11,24 @@
android:id="@+id/recycler"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/textView2"
android:clipToPadding="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="@layout/migration_source_item">
</androidx.recyclerview.widget.RecyclerView>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/data_to_include_in_migration"
android:textAppearance="@style/TextAppearance.Medium.Body2"
app:layout_constraintBottom_toTopOf="@+id/mig_chapters"
app:layout_constraintStart_toStartOf="@+id/textView" />
<CheckBox
android:id="@+id/mig_chapters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:checked="true"
android:text="@string/chapters"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintStart_toStartOf="@+id/textView2" />
<CheckBox
android:id="@+id/mig_categories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:checked="true"
android:text="@string/categories"
app:layout_constraintBottom_toBottomOf="@+id/mig_chapters"
app:layout_constraintStart_toEndOf="@+id/mig_chapters" />
<CheckBox
android:id="@+id/mig_tracking"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:checked="true"
android:text="@string/track"
app:layout_constraintBottom_toBottomOf="@+id/mig_categories"
app:layout_constraintStart_toEndOf="@+id/mig_categories" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:text="@string/options"
android:textAppearance="@style/TextAppearance.Medium.Body2"
app:layout_constraintBottom_toTopOf="@+id/prioritize_chapter_count"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/prioritize_chapter_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="@+id/textView"
app:layout_constraintTop_toTopOf="@+id/migration_mode" />
<TextView
android:id="@+id/migration_mode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:gravity="start|center_vertical"
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
style="@style/Theme.Widget.FAB"
android:clickable="true"
app:layout_constraintBottom_toTopOf="@+id/fuzzy_search"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
android:focusable="true" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/use_smart_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/textView"
app:layout_constraintTop_toTopOf="@+id/fuzzy_search" />
<TextView
android:id="@+id/fuzzy_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:gravity="start|center_vertical"
android:text="@string/use_intelligent_search"
android:clickable="true"
app:layout_constraintBottom_toTopOf="@+id/extra_search_param"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
android:focusable="true" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/extra_search_param"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/textView"
app:layout_constraintTop_toTopOf="@+id/extra_search_param_desc" />
<TextView
android:id="@+id/extra_search_param_desc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:gravity="start|center_vertical"
android:text="@string/include_extra_search_parameter"
android:clickable="true"
app:layout_constraintBottom_toTopOf="@+id/extra_search_param_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
android:focusable="true" />
<EditText
android:id="@+id/extra_search_param_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:ems="10"
android:hint="@string/search_parameter"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/begin_migration_btn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:importantForAutofill="no" />
<Button
android:id="@+id/begin_migration_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:text="@string/begin_migration"
android:focusable="true"
android:src="@drawable/ic_arrow_forward_24dp"
app:layout_anchor="@id/recycler"
app:layout_anchorGravity="bottom|right|end"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="@+id/options_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="migration_mode,use_smart_search,fuzzy_search,action_copy_manga,extra_search_param_desc,mig_tracking,textView,mig_chapters,copy_manga_desc,textView2,prioritize_chapter_count,mig_categories,extra_search_param" />
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -192,7 +192,9 @@
<string name="wifi">Wi-Fi</string>
<string name="charging">Charging</string>
<string name="pref_update_only_non_completed">Only update ongoing manga</string>
<string name="pref_skip_pre_migration">Skip pre-migration</string>
<string name="pref_skip_pre_migration_summary">Use last saved pre-migration preferences
and sources to mass migrate</string>
<string name="pref_category_library_categories">Categories</string>
<string name="default_category">Default category</string>
<string name="default_category_summary">Always ask</string>
@ -657,10 +659,12 @@
<string name="data_to_include_in_migration">Data to include in migration</string>
<string name="search_parameter">Search parameter (e.g. language:english)</string>
<string name="include_extra_search_parameter">Include extra search parameter when searching</string>
<string name="keep_old_manga">Keep old manga</string>
<string name="use_intelligent_search">Use intelligent search algorithm</string>
<string name="begin_migration">Begin migration</string>
<string name="use_intelligent_search">Search title + keywords of title</string>
<string name="migrating_to">migrating to</string>
<string name="use_most_chapters">Use source with the most chapters (slower)</string>
<string name="use_first_source">Use first source with alternative</string>
<string name="skip_this_step_next_time">Skip this step next time</string>
<string name="pre_migration_skip_toast">To show this screen again, go to Settings -> General.</string>
<!-- EXH -->
<string name="label_login">Login</string>

View File

@ -309,4 +309,25 @@
<style name="FilePickerAlertDialogTheme" parent="Theme.MaterialComponents.Light.Dialog.Alert" />
<style name="reader_settings_popup_animation">
<item name="android:windowEnterAnimation">@anim/enter_from_right</item>
<item name="android:windowExitAnimation">@anim/exit_to_right</item>
</style>
<style name="reader_brightness_popup_animation">
<item name="android:windowEnterAnimation">@anim/enter_from_left</item>
<item name="android:windowExitAnimation">@anim/exit_to_left</item>
</style>
<style name="SheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
<!--<item name="android:windowCloseOnTouchOutside">false</item>-->
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:colorBackground"> @android:color/transparent</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:backgroundDimAmount">0.3</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
</style>
</resources>