Finish auto-migration feature
This commit is contained in:
parent
9cc24a3be3
commit
4f2985469c
@ -165,6 +165,16 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
|||||||
|
|
||||||
subscriptions += controller.selectionRelay
|
subscriptions += controller.selectionRelay
|
||||||
.subscribe { onSelectionChanged(it) }
|
.subscribe { onSelectionChanged(it) }
|
||||||
|
|
||||||
|
subscriptions += controller.selectAllRelay
|
||||||
|
.subscribe {
|
||||||
|
if (it == category.id) {
|
||||||
|
adapter.currentItems.forEach { item ->
|
||||||
|
controller.setSelection(item.manga, true)
|
||||||
|
}
|
||||||
|
controller.invalidateActionMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRecycle() {
|
fun onRecycle() {
|
||||||
|
@ -99,6 +99,8 @@ class LibraryController(
|
|||||||
*/
|
*/
|
||||||
val libraryMangaRelay: BehaviorRelay<LibraryMangaEvent> = BehaviorRelay.create()
|
val libraryMangaRelay: BehaviorRelay<LibraryMangaEvent> = BehaviorRelay.create()
|
||||||
|
|
||||||
|
val selectAllRelay: PublishRelay<Int> = PublishRelay.create()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of manga per row in grid mode.
|
* Number of manga per row in grid mode.
|
||||||
*/
|
*/
|
||||||
@ -436,6 +438,9 @@ class LibraryController(
|
|||||||
}
|
}
|
||||||
R.id.action_move_to_category -> showChangeMangaCategoriesDialog()
|
R.id.action_move_to_category -> showChangeMangaCategoriesDialog()
|
||||||
R.id.action_delete -> showDeleteMangaDialog()
|
R.id.action_delete -> showDeleteMangaDialog()
|
||||||
|
R.id.action_select_all -> {
|
||||||
|
selectAllRelay.call(activeCategory)
|
||||||
|
}
|
||||||
R.id.action_auto_source_migration -> {
|
R.id.action_auto_source_migration -> {
|
||||||
router.pushController(MigrationDesignController.create(
|
router.pushController(MigrationDesignController.create(
|
||||||
selectedMangas.mapNotNull { it.id }
|
selectedMangas.mapNotNull { it.id }
|
||||||
|
@ -13,7 +13,8 @@ import rx.schedulers.Schedulers
|
|||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class SmartSearchEngine(parentContext: CoroutineContext): CoroutineScope {
|
class SmartSearchEngine(parentContext: CoroutineContext,
|
||||||
|
val extraSearchParams: String? = null): CoroutineScope {
|
||||||
override val coroutineContext: CoroutineContext = parentContext + Job() + Dispatchers.Default
|
override val coroutineContext: CoroutineContext = parentContext + Job() + Dispatchers.Default
|
||||||
|
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val db: DatabaseHelper by injectLazy()
|
||||||
@ -26,7 +27,11 @@ class SmartSearchEngine(parentContext: CoroutineContext): CoroutineScope {
|
|||||||
val eligibleManga = supervisorScope {
|
val eligibleManga = supervisorScope {
|
||||||
queries.map { query ->
|
queries.map { query ->
|
||||||
async(Dispatchers.Default) {
|
async(Dispatchers.Default) {
|
||||||
val searchResults = source.fetchSearchManga(1, query, FilterList()).toSingle().await(Schedulers.io())
|
val builtQuery = if(extraSearchParams != null) {
|
||||||
|
"$query ${extraSearchParams.trim()}"
|
||||||
|
} else title
|
||||||
|
|
||||||
|
val searchResults = source.fetchSearchManga(1, builtQuery, FilterList()).toSingle().await(Schedulers.io())
|
||||||
|
|
||||||
searchResults.mangas.map {
|
searchResults.mangas.map {
|
||||||
val cleanedMangaTitle = cleanSmartSearchTitle(it.title)
|
val cleanedMangaTitle = cleanSmartSearchTitle(it.title)
|
||||||
@ -44,7 +49,10 @@ class SmartSearchEngine(parentContext: CoroutineContext): CoroutineScope {
|
|||||||
|
|
||||||
suspend fun normalSearch(source: CatalogueSource, title: String): SManga? {
|
suspend fun normalSearch(source: CatalogueSource, title: String): SManga? {
|
||||||
val eligibleManga = supervisorScope {
|
val eligibleManga = supervisorScope {
|
||||||
val searchResults = source.fetchSearchManga(1, title, FilterList()).toSingle().await(Schedulers.io())
|
val searchQuery = if(extraSearchParams != null) {
|
||||||
|
"$title ${extraSearchParams.trim()}"
|
||||||
|
} else title
|
||||||
|
val searchResults = source.fetchSearchManga(1, searchQuery, FilterList()).toSingle().await(Schedulers.io())
|
||||||
|
|
||||||
searchResults.mangas.map {
|
searchResults.mangas.map {
|
||||||
val normalizedDistance = NormalizedLevenshtein().similarity(title, it.title)
|
val normalizedDistance = NormalizedLevenshtein().similarity(title, it.title)
|
||||||
@ -80,7 +88,7 @@ class SmartSearchEngine(parentContext: CoroutineContext): CoroutineScope {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return searchQueries.map {
|
return searchQueries.map {
|
||||||
it.joinToString().trim()
|
it.joinToString(" ").trim()
|
||||||
}.distinct()
|
}.distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,8 +175,8 @@ class SmartSearchEngine(parentContext: CoroutineContext): CoroutineScope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MIN_SMART_ELIGIBLE_THRESHOLD = 0.7
|
const val MIN_SMART_ELIGIBLE_THRESHOLD = 0.4
|
||||||
const val MIN_NORMAL_ELIGIBLE_THRESHOLD = 0.5
|
const val MIN_NORMAL_ELIGIBLE_THRESHOLD = 0.4
|
||||||
|
|
||||||
private val titleRegex = Regex("[^a-zA-Z0-9- ]")
|
private val titleRegex = Regex("[^a-zA-Z0-9- ]")
|
||||||
private val consecutiveSpacesRegex = Regex(" +")
|
private val consecutiveSpacesRegex = Regex(" +")
|
||||||
|
@ -11,13 +11,14 @@ 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.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||||
|
import eu.kanade.tachiyomi.util.gone
|
||||||
|
import eu.kanade.tachiyomi.util.visible
|
||||||
import exh.ui.base.BaseExhController
|
import exh.ui.base.BaseExhController
|
||||||
import exh.ui.migration.manga.process.MigrationProcedureConfig
|
import exh.ui.migration.manga.process.MigrationProcedureConfig
|
||||||
import exh.ui.migration.manga.process.MigrationProcedureController
|
import exh.ui.migration.manga.process.MigrationProcedureController
|
||||||
import kotlinx.android.synthetic.main.eh_migration_design.*
|
import kotlinx.android.synthetic.main.eh_migration_design.*
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
// TODO Handle config changes
|
|
||||||
// TODO Select all in library
|
// TODO Select all in library
|
||||||
class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bundle), FlexibleAdapter.OnItemClickListener {
|
class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bundle), FlexibleAdapter.OnItemClickListener {
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
@ -29,6 +30,8 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
|||||||
|
|
||||||
private val config: LongArray = args.getLongArray(MANGA_IDS_EXTRA) ?: LongArray(0)
|
private val config: LongArray = args.getLongArray(MANGA_IDS_EXTRA) ?: LongArray(0)
|
||||||
|
|
||||||
|
private var showingOptions = false
|
||||||
|
|
||||||
override fun getTitle() = "Select target sources"
|
override fun getTitle() = "Select target sources"
|
||||||
|
|
||||||
override fun onViewCreated(view: View) {
|
override fun onViewCreated(view: View) {
|
||||||
@ -53,13 +56,33 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
|||||||
use_smart_search.toggle()
|
use_smart_search.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copy_manga_desc.setOnClickListener {
|
||||||
|
copy_manga.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_search_param_desc.setOnClickListener {
|
||||||
|
extra_search_param.toggle()
|
||||||
|
}
|
||||||
|
|
||||||
prioritize_chapter_count.setOnCheckedChangeListener { _, b ->
|
prioritize_chapter_count.setOnCheckedChangeListener { _, b ->
|
||||||
updatePrioritizeChapterCount(b)
|
updatePrioritizeChapterCount(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extra_search_param.setOnCheckedChangeListener { _, b ->
|
||||||
|
updateOptionsState()
|
||||||
|
}
|
||||||
|
|
||||||
updatePrioritizeChapterCount(prioritize_chapter_count.isChecked)
|
updatePrioritizeChapterCount(prioritize_chapter_count.isChecked)
|
||||||
|
|
||||||
|
updateOptionsState()
|
||||||
|
|
||||||
begin_migration_btn.setOnClickListener {
|
begin_migration_btn.setOnClickListener {
|
||||||
|
if(!showingOptions) {
|
||||||
|
showingOptions = true
|
||||||
|
updateOptionsState()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
var flags = 0
|
var flags = 0
|
||||||
if(mig_chapters.isChecked) flags = flags or MigrationFlags.CHAPTERS
|
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.CATEGORIES
|
||||||
@ -73,12 +96,41 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
|||||||
}.map { it.source.id },
|
}.map { it.source.id },
|
||||||
useSourceWithMostChapters = prioritize_chapter_count.isChecked,
|
useSourceWithMostChapters = prioritize_chapter_count.isChecked,
|
||||||
enableLenientSearch = use_smart_search.isChecked,
|
enableLenientSearch = use_smart_search.isChecked,
|
||||||
migrationFlags = flags
|
migrationFlags = flags,
|
||||||
|
copy = copy_manga.isChecked,
|
||||||
|
extraSearchParams = if(extra_search_param.isChecked && extra_search_param_text.text.isNotBlank()) {
|
||||||
|
extra_search_param_text.text.toString()
|
||||||
|
} else null
|
||||||
)
|
)
|
||||||
).withFadeTransaction())
|
).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) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
adapter?.onSaveInstanceState(outState)
|
adapter?.onSaveInstanceState(outState)
|
||||||
@ -92,9 +144,9 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
|||||||
|
|
||||||
private fun updatePrioritizeChapterCount(migrationMode: Boolean) {
|
private fun updatePrioritizeChapterCount(migrationMode: Boolean) {
|
||||||
migration_mode.text = if(migrationMode) {
|
migration_mode.text = if(migrationMode) {
|
||||||
"Use the source with the most chapters and use the above list to break ties (slow with many sources or smart search)"
|
"Currently using the source with the most chapters and the above list to break ties (slow with many sources or smart search)"
|
||||||
} else {
|
} else {
|
||||||
"Use the first source in the list that has the manga"
|
"Currently using the first source in the list that has the manga"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,7 @@ import eu.kanade.tachiyomi.source.SourceManager
|
|||||||
import exh.util.DeferredField
|
import exh.util.DeferredField
|
||||||
import exh.util.await
|
import exh.util.await
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.BroadcastChannel
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
|
||||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
||||||
import kotlinx.coroutines.channels.ReceiveChannel
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class MigratingManga(private val db: DatabaseHelper,
|
class MigratingManga(private val db: DatabaseHelper,
|
||||||
|
@ -4,28 +4,22 @@ import android.support.v4.view.PagerAdapter
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.elvishew.xlog.XLog
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
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.glide.GlideApp
|
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.all.MergedSource
|
import eu.kanade.tachiyomi.source.online.all.MergedSource
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController
|
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||||
import eu.kanade.tachiyomi.util.gone
|
import eu.kanade.tachiyomi.util.*
|
||||||
import eu.kanade.tachiyomi.util.inflate
|
|
||||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
|
||||||
import eu.kanade.tachiyomi.util.visible
|
|
||||||
import exh.MERGED_SOURCE_ID
|
import exh.MERGED_SOURCE_ID
|
||||||
import exh.debug.DebugFunctions.sourceManager
|
|
||||||
import exh.util.await
|
import exh.util.await
|
||||||
import kotlinx.android.synthetic.main.eh_manga_card.view.*
|
import kotlinx.android.synthetic.main.eh_manga_card.view.*
|
||||||
import kotlinx.android.synthetic.main.eh_migration_process_item.view.*
|
import kotlinx.android.synthetic.main.eh_migration_process_item.view.*
|
||||||
@ -33,6 +27,9 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.util.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
||||||
@ -42,6 +39,8 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
|||||||
private val gson: Gson by injectLazy()
|
private val gson: Gson by injectLazy()
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
|
|
||||||
|
private val logger = XLog.tag(this::class.simpleName)
|
||||||
|
|
||||||
override fun isViewFromObject(p0: View, p1: Any): Boolean {
|
override fun isViewFromObject(p0: View, p1: Any): Boolean {
|
||||||
return p0 == p1
|
return p0 == p1
|
||||||
}
|
}
|
||||||
@ -57,34 +56,51 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
|||||||
controller.nextMigration()
|
controller.nextMigration()
|
||||||
}
|
}
|
||||||
|
|
||||||
view.accept_migration.setOnClickListener {
|
|
||||||
view.migrating_frame.visible()
|
|
||||||
}
|
|
||||||
|
|
||||||
val viewTag = ViewTag(coroutineContext)
|
val viewTag = ViewTag(coroutineContext)
|
||||||
view.tag = viewTag
|
view.tag = viewTag
|
||||||
view.setupView(viewTag, item)
|
view.setupView(viewTag, item)
|
||||||
|
|
||||||
|
view.accept_migration.setOnClickListener {
|
||||||
|
viewTag.launch(Dispatchers.Main) {
|
||||||
|
view.migrating_frame.visible()
|
||||||
|
try {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
performMigration(item)
|
||||||
|
}
|
||||||
|
controller.nextMigration()
|
||||||
|
} catch(e: Exception) {
|
||||||
|
logger.e("Migration failure!", e)
|
||||||
|
controller.migrationFailure()
|
||||||
|
}
|
||||||
|
view.migrating_frame.gone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performMigration() {
|
suspend fun performMigration(manga: MigratingManga) {
|
||||||
|
if(!manga.searchResult.initialized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val toMangaObj = db.getManga(manga.searchResult.get() ?: return).await() ?: return
|
||||||
|
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
migrateMangaInternal(
|
||||||
|
manga.manga() ?: return@withContext,
|
||||||
|
toMangaObj,
|
||||||
|
!controller.config.copy
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private fun migrateMangaInternal(source: Source,
|
|
||||||
sourceChapters: List<SChapter>,
|
private fun migrateMangaInternal(prevManga: Manga,
|
||||||
prevManga: Manga,
|
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
replace: Boolean) {
|
replace: Boolean) {
|
||||||
db.inTransaction {
|
db.inTransaction {
|
||||||
// Update chapters read
|
// Update chapters read
|
||||||
if (migrateChapters) {
|
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {
|
||||||
try {
|
|
||||||
syncChaptersWithSource(db, sourceChapters, manga, source)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Worst case, chapters won't be synced
|
|
||||||
}
|
|
||||||
|
|
||||||
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
|
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
|
||||||
val maxChapterRead = prevMangaChapters.filter { it.read }
|
val maxChapterRead = prevMangaChapters.filter { it.read }
|
||||||
.maxBy { it.chapter_number }?.chapter_number
|
.maxBy { it.chapter_number }?.chapter_number
|
||||||
@ -99,13 +115,13 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update categories
|
// Update categories
|
||||||
if (migrateCategories) {
|
if (MigrationFlags.hasCategories(controller.config.migrationFlags)) {
|
||||||
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking()
|
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking()
|
||||||
val mangaCategories = categories.map { MangaCategory.create(manga, it) }
|
val mangaCategories = categories.map { MangaCategory.create(manga, it) }
|
||||||
db.setMangaCategories(mangaCategories, listOf(manga))
|
db.setMangaCategories(mangaCategories, listOf(manga))
|
||||||
}
|
}
|
||||||
// Update track
|
// Update track
|
||||||
if (migrateTracks) {
|
if (MigrationFlags.hasTracks(controller.config.migrationFlags)) {
|
||||||
val tracks = db.getTracks(prevManga).executeAsBlocking()
|
val tracks = db.getTracks(prevManga).executeAsBlocking()
|
||||||
for (track in tracks) {
|
for (track in tracks) {
|
||||||
track.id = null
|
track.id = null
|
||||||
@ -174,7 +190,7 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun View.attachManga(tag: ViewTag, manga: Manga, source: Source) {
|
suspend fun View.attachManga(tag: ViewTag, manga: Manga, source: Source) {
|
||||||
// TODO Duplicated in MangaInfoController
|
// TODO Duplicated in MangaInfoController
|
||||||
|
|
||||||
GlideApp.with(context)
|
GlideApp.with(context)
|
||||||
@ -221,6 +237,23 @@ class MigrationProcedureAdapter(val controller: MigrationProcedureController,
|
|||||||
SManga.LICENSED -> R.string.licensed
|
SManga.LICENSED -> R.string.licensed
|
||||||
else -> R.string.unknown
|
else -> R.string.unknown
|
||||||
})
|
})
|
||||||
|
|
||||||
|
val mangaChapters = db.getChapters(manga).await()
|
||||||
|
manga_chapters.text = mangaChapters.size.toString()
|
||||||
|
val latestChapter = mangaChapters.maxBy { it.chapter_number }?.chapter_number ?: -1f
|
||||||
|
val lastUpdate = Date(mangaChapters.maxBy { it.date_upload }?.date_upload ?: 0)
|
||||||
|
|
||||||
|
if (latestChapter > 0f) {
|
||||||
|
manga_last_chapter.text = DecimalFormat("#.#").format(count)
|
||||||
|
} else {
|
||||||
|
manga_last_chapter.setText(R.string.unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastUpdate.time != 0L) {
|
||||||
|
manga_last_update.text = DateFormat.getDateInstance(DateFormat.SHORT).format(lastUpdate)
|
||||||
|
} else {
|
||||||
|
manga_last_update.setText(R.string.unknown)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||||
|
@ -9,5 +9,7 @@ data class MigrationProcedureConfig(
|
|||||||
val targetSourceIds: List<Long>,
|
val targetSourceIds: List<Long>,
|
||||||
val useSourceWithMostChapters: Boolean,
|
val useSourceWithMostChapters: Boolean,
|
||||||
val enableLenientSearch: Boolean,
|
val enableLenientSearch: Boolean,
|
||||||
val migrationFlags: Int
|
val migrationFlags: Int,
|
||||||
|
val copy: Boolean,
|
||||||
|
val extraSearchParams: String?
|
||||||
): Parcelable
|
): Parcelable
|
@ -4,12 +4,13 @@ import android.content.pm.ActivityInfo
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.elvishew.xlog.XLog
|
import com.elvishew.xlog.XLog
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
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.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
|
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
||||||
import eu.kanade.tachiyomi.util.toast
|
import eu.kanade.tachiyomi.util.toast
|
||||||
import exh.smartsearch.SmartSearchEngine
|
import exh.smartsearch.SmartSearchEngine
|
||||||
import exh.ui.base.BaseExhController
|
import exh.ui.base.BaseExhController
|
||||||
@ -22,6 +23,7 @@ import rx.schedulers.Schedulers
|
|||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
// TODO Will probably implode if activity is fully destroyed
|
||||||
class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(bundle), CoroutineScope {
|
class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(bundle), CoroutineScope {
|
||||||
override val layoutId = R.layout.eh_migration_process
|
override val layoutId = R.layout.eh_migration_process
|
||||||
|
|
||||||
@ -29,12 +31,12 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
|||||||
|
|
||||||
private var adapter: MigrationProcedureAdapter? = null
|
private var adapter: MigrationProcedureAdapter? = null
|
||||||
|
|
||||||
private val config: MigrationProcedureConfig = args.getParcelable(CONFIG_EXTRA)
|
val config: MigrationProcedureConfig = args.getParcelable(CONFIG_EXTRA)
|
||||||
|
|
||||||
private val db: DatabaseHelper by injectLazy()
|
private val db: DatabaseHelper by injectLazy()
|
||||||
private val sourceManager: SourceManager by injectLazy()
|
private val sourceManager: SourceManager by injectLazy()
|
||||||
|
|
||||||
private val smartSearchEngine = SmartSearchEngine(coroutineContext)
|
private val smartSearchEngine = SmartSearchEngine(coroutineContext, config.extraSearchParams)
|
||||||
|
|
||||||
private val logger = XLog.tag("MigrationProcedureController")
|
private val logger = XLog.tag("MigrationProcedureController")
|
||||||
|
|
||||||
@ -74,11 +76,15 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTitle()
|
pager.post {
|
||||||
|
// pager.currentItem doesn't appear to be valid if we don't do this in a post
|
||||||
|
updateTitle()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTitle() {
|
fun updateTitle() {
|
||||||
titleText = "Migrate manga (${pager.currentItem + 1}/${adapter?.count ?: 0})"
|
titleText = "Migrate manga (${pager.currentItem + 1}/${adapter?.count ?: 0})"
|
||||||
|
setTitle()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextMigration() {
|
fun nextMigration() {
|
||||||
@ -88,14 +94,23 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
|||||||
router.popCurrentController()
|
router.popCurrentController()
|
||||||
} else {
|
} else {
|
||||||
pager.setCurrentItem(pager.currentItem + 1, true)
|
pager.setCurrentItem(pager.currentItem + 1, true)
|
||||||
updateTitle()
|
|
||||||
launch(Dispatchers.Main) {
|
launch(Dispatchers.Main) {
|
||||||
setTitle()
|
updateTitle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun migrationFailure() {
|
||||||
|
activity?.let {
|
||||||
|
MaterialDialog.Builder(it)
|
||||||
|
.title("Migration failure")
|
||||||
|
.content("An unknown error occured while migrating this manga!")
|
||||||
|
.positiveText("Ok")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun runMigrations(mangas: List<MigratingManga>) {
|
suspend fun runMigrations(mangas: List<MigratingManga>) {
|
||||||
val sources = config.targetSourceIds.mapNotNull { sourceManager.get(it) as? CatalogueSource }
|
val sources = config.targetSourceIds.mapNotNull { sourceManager.get(it) as? CatalogueSource }
|
||||||
|
|
||||||
@ -123,21 +138,22 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
|||||||
async {
|
async {
|
||||||
sourceSemaphore.withPermit {
|
sourceSemaphore.withPermit {
|
||||||
try {
|
try {
|
||||||
supervisorScope {
|
val searchResult = if (config.enableLenientSearch) {
|
||||||
val searchResult = if (config.enableLenientSearch) {
|
smartSearchEngine.smartSearch(source, mangaObj.title)
|
||||||
smartSearchEngine.smartSearch(source, mangaObj.title)
|
} else {
|
||||||
} else {
|
smartSearchEngine.normalSearch(source, mangaObj.title)
|
||||||
smartSearchEngine.normalSearch(source, mangaObj.title)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(searchResult != null) {
|
if(searchResult != null) {
|
||||||
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
|
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
|
||||||
val chapters = source.fetchChapterList(localManga).toSingle().await(Schedulers.io())
|
val chapters = source.fetchChapterList(localManga).toSingle().await(Schedulers.io())
|
||||||
manga.progress.send(validSources.size to processedSources.incrementAndGet())
|
withContext(Dispatchers.IO) {
|
||||||
localManga to chapters.size
|
syncChaptersWithSource(db, chapters, localManga, source)
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
manga.progress.send(validSources.size to processedSources.incrementAndGet())
|
||||||
|
localManga to chapters.size
|
||||||
|
} else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
logger.e("Failed to search in source: ${source.id}!", e)
|
logger.e("Failed to search in source: ${source.id}!", e)
|
||||||
@ -149,17 +165,20 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
|||||||
} else {
|
} else {
|
||||||
validSources.forEachIndexed { index, source ->
|
validSources.forEachIndexed { index, source ->
|
||||||
val searchResult = try {
|
val searchResult = try {
|
||||||
supervisorScope {
|
val searchResult = if (config.enableLenientSearch) {
|
||||||
val searchResult = if (config.enableLenientSearch) {
|
smartSearchEngine.smartSearch(source, mangaObj.title)
|
||||||
smartSearchEngine.smartSearch(source, mangaObj.title)
|
} else {
|
||||||
} else {
|
smartSearchEngine.normalSearch(source, mangaObj.title)
|
||||||
smartSearchEngine.normalSearch(source, mangaObj.title)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchResult != null) {
|
|
||||||
smartSearchEngine.networkToLocalManga(searchResult, source.id)
|
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (searchResult != null) {
|
||||||
|
val localManga = smartSearchEngine.networkToLocalManga(searchResult, source.id)
|
||||||
|
val chapters = source.fetchChapterList(localManga).toSingle().await(Schedulers.io())
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
syncChaptersWithSource(db, chapters, localManga, source)
|
||||||
|
}
|
||||||
|
localManga
|
||||||
|
} else null
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
logger.e("Failed to search in source: ${source.id}!", e)
|
logger.e("Failed to search in source: ${source.id}!", e)
|
||||||
null
|
null
|
||||||
@ -180,15 +199,13 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
|||||||
|
|
||||||
if(result != null && result.thumbnail_url == null) {
|
if(result != null && result.thumbnail_url == null) {
|
||||||
try {
|
try {
|
||||||
supervisorScope {
|
val newManga = sourceManager.getOrStub(result.source)
|
||||||
val newManga = sourceManager.getOrStub(result.source)
|
.fetchMangaDetails(result)
|
||||||
.fetchMangaDetails(result)
|
.toSingle()
|
||||||
.toSingle()
|
.await()
|
||||||
.await()
|
result.copyFrom(newManga)
|
||||||
result.copyFrom(newManga)
|
|
||||||
|
|
||||||
db.insertManga(result).await()
|
db.insertManga(result).await()
|
||||||
}
|
|
||||||
} catch(e: Exception) {
|
} catch(e: Exception) {
|
||||||
logger.e("Could not load search manga details", e)
|
logger.e("Could not load search manga details", e)
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,6 @@ class DeferredField<T> {
|
|||||||
mutex.withLock {}
|
mutex.withLock {}
|
||||||
|
|
||||||
// Field is initialized, return value
|
// Field is initialized, return value
|
||||||
return content!!
|
return content as T
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,11 +19,10 @@
|
|||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:contentDescription="@string/description_cover"
|
android:contentDescription="@string/description_cover"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintDimensionRatio="l,2:3"
|
||||||
app:layout_constraintDimensionRatio="h,3:2"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toLeftOf="@+id/card_scroll_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintWidth_min="100dp"
|
app:layout_constraintWidth_min="100dp"
|
||||||
tools:background="@color/material_grey_700" />
|
tools:background="@color/material_grey_700" />
|
||||||
@ -31,10 +30,11 @@
|
|||||||
<android.support.constraint.ConstraintLayout
|
<android.support.constraint.ConstraintLayout
|
||||||
android:id="@+id/card_scroll_content"
|
android:id="@+id/card_scroll_content"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/manga_cover"
|
android:paddingBottom="16dp"
|
||||||
|
app:layout_constraintHorizontal_weight="2"
|
||||||
app:layout_constraintLeft_toRightOf="@+id/manga_cover"
|
app:layout_constraintLeft_toRightOf="@+id/manga_cover"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/manga_cover">
|
app:layout_constraintTop_toTopOf="@+id/manga_cover">
|
||||||
@ -130,6 +130,75 @@
|
|||||||
app:layout_constraintLeft_toRightOf="@+id/manga_status_label"
|
app:layout_constraintLeft_toRightOf="@+id/manga_status_label"
|
||||||
app:layout_constraintRight_toRightOf="parent" />
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_chapters_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:text="@string/manga_info_chapters_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_chapters"
|
||||||
|
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/manga_chapters_label"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_last_chapter_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:text="@string/manga_info_last_chapter_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_last_chapter"
|
||||||
|
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/manga_last_chapter_label"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_last_update_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clickable="false"
|
||||||
|
android:text="@string/manga_info_latest_data_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_last_chapter_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_last_update"
|
||||||
|
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:clickable="false"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/manga_last_update_label"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_last_chapter_label" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/manga_source_label"
|
android:id="@+id/manga_source_label"
|
||||||
style="@style/TextAppearance.Medium.Body2"
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
@ -139,7 +208,7 @@
|
|||||||
android:text="@string/manga_info_source_label"
|
android:text="@string/manga_info_source_label"
|
||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
|
app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/manga_source"
|
android:id="@+id/manga_source"
|
||||||
@ -151,7 +220,7 @@
|
|||||||
android:textIsSelectable="false"
|
android:textIsSelectable="false"
|
||||||
app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
|
app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
|
app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label" />
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:text="@string/chapters"
|
|
||||||
android:checked="true"
|
android:checked="true"
|
||||||
|
android:text="@string/chapters"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/textView"
|
app:layout_constraintBottom_toTopOf="@+id/textView"
|
||||||
app:layout_constraintStart_toStartOf="@+id/textView2" />
|
app:layout_constraintStart_toStartOf="@+id/textView2" />
|
||||||
|
|
||||||
@ -46,8 +46,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:text="@string/categories"
|
|
||||||
android:checked="true"
|
android:checked="true"
|
||||||
|
android:text="@string/categories"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/mig_chapters"
|
app:layout_constraintBottom_toBottomOf="@+id/mig_chapters"
|
||||||
app:layout_constraintStart_toEndOf="@+id/mig_chapters" />
|
app:layout_constraintStart_toEndOf="@+id/mig_chapters" />
|
||||||
|
|
||||||
@ -57,8 +57,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
android:text="@string/track"
|
|
||||||
android:checked="true"
|
android:checked="true"
|
||||||
|
android:text="@string/track"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/mig_categories"
|
app:layout_constraintBottom_toBottomOf="@+id/mig_categories"
|
||||||
app:layout_constraintStart_toEndOf="@+id/mig_categories" />
|
app:layout_constraintStart_toEndOf="@+id/mig_categories" />
|
||||||
|
|
||||||
@ -92,6 +92,7 @@
|
|||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
|
android:clickable="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/fuzzy_search"
|
app:layout_constraintBottom_toTopOf="@+id/fuzzy_search"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" />
|
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" />
|
||||||
@ -100,7 +101,6 @@
|
|||||||
android:id="@+id/use_smart_search"
|
android:id="@+id/use_smart_search"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/textView"
|
app:layout_constraintStart_toStartOf="@+id/textView"
|
||||||
app:layout_constraintTop_toTopOf="@+id/fuzzy_search" />
|
app:layout_constraintTop_toTopOf="@+id/fuzzy_search" />
|
||||||
|
|
||||||
@ -114,11 +114,73 @@
|
|||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:gravity="start|center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
android:text="Use smart search algorithm"
|
android:text="Use intelligent search algorithm"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/begin_migration_btn"
|
android:clickable="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/copy_manga"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" />
|
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.SwitchCompat
|
||||||
|
android:id="@+id/copy_manga"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/textView"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/copy_manga_desc" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/copy_manga_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="Keep old manga"
|
||||||
|
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.support.v7.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="Include extra search parameter when searching"
|
||||||
|
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" />
|
||||||
|
|
||||||
|
<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="Search parameter (e.g. language:english)"
|
||||||
|
android:inputType="textPersonName"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/begin_migration_btn"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/begin_migration_btn"
|
android:id="@+id/begin_migration_btn"
|
||||||
style="@style/Theme.Widget.Button.Colored"
|
style="@style/Theme.Widget.Button.Colored"
|
||||||
@ -134,4 +196,10 @@
|
|||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<android.support.constraint.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,copy_manga,extra_search_param_desc,mig_tracking,textView,mig_chapters,copy_manga_desc,textView2,prioritize_chapter_count,mig_categories,extra_search_param" />
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</android.support.constraint.ConstraintLayout>
|
@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:animateLayoutChanges="true">
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<android.support.constraint.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -18,6 +18,11 @@
|
|||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item android:id="@+id/action_select_all"
|
||||||
|
android:title="@string/action_select_all"
|
||||||
|
android:icon="@drawable/ic_select_all_white_24dp"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_auto_source_migration"
|
android:id="@+id/action_auto_source_migration"
|
||||||
android:title="Source migration (automatic)"
|
android:title="Source migration (automatic)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user