Implemented J2K Auto Source Migration
(cherry picked from commit 8ba75831e6f51f6472d85f813405ede3f679cfd8)
This commit is contained in:
parent
c62d3abbc5
commit
e7b39f29f2
@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.data.database.tables.CategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
import exh.metadata.sql.tables.SearchMetadataTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable
|
||||
|
||||
interface MangaQueries : DbProvider {
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package exh.smartsearch
|
||||
package eu.kanade.tachiyomi.smartsearch
|
||||
|
||||
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.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.ui.smartsearch.SmartSearchPresenter
|
||||
import exh.util.await
|
||||
import eu.kanade.tachiyomi.ui.smartsearch.SmartSearchPresenter
|
||||
import eu.kanade.tachiyomi.util.await
|
||||
import info.debatty.java.stringsimilarity.NormalizedLevenshtein
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -39,7 +39,8 @@ class SmartSearchEngine(
|
||||
"$query ${extraSearchParams.trim()}"
|
||||
} else query
|
||||
|
||||
val searchResults = source.fetchSearchManga(1, builtQuery, FilterList()).toSingle().await(Schedulers.io())
|
||||
val searchResults = source.fetchSearchManga(1, builtQuery, FilterList())
|
||||
.toSingle().await(Schedulers.io())
|
||||
|
||||
searchResults.mangas.map {
|
||||
val cleanedMangaTitle = cleanSmartSearchTitle(it.title)
|
||||
@ -88,11 +89,11 @@ class SmartSearchEngine(
|
||||
// Search first word
|
||||
|
||||
val searchQueries = listOf(
|
||||
listOf(cleanedTitle),
|
||||
splitSortedByLargest.take(2),
|
||||
splitSortedByLargest.take(1),
|
||||
splitCleanedTitle.take(2),
|
||||
splitCleanedTitle.take(1)
|
||||
listOf(cleanedTitle),
|
||||
splitSortedByLargest.take(2),
|
||||
splitSortedByLargest.take(1),
|
||||
splitCleanedTitle.take(2),
|
||||
splitCleanedTitle.take(1)
|
||||
)
|
||||
|
||||
return searchQueries.map {
|
||||
@ -120,10 +121,10 @@ class SmartSearchEngine(
|
||||
|
||||
private fun removeTextInBrackets(text: String, readForward: Boolean): String {
|
||||
val bracketPairs = listOf(
|
||||
'(' to ')',
|
||||
'[' to ']',
|
||||
'<' to '>',
|
||||
'{' to '}'
|
||||
'(' to ')',
|
||||
'[' to ']',
|
||||
'<' to '>',
|
||||
'{' to '}'
|
||||
)
|
||||
var openingBracketPairs = bracketPairs.mapIndexed { index, (opening, _) ->
|
||||
opening to index
|
||||
@ -171,11 +172,11 @@ class SmartSearchEngine(
|
||||
* @return a manga from the database.
|
||||
*/
|
||||
suspend fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
||||
var localManga = db.getManga(sManga.url, sourceId).await()
|
||||
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
|
||||
if (localManga == null) {
|
||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
||||
newManga.copyFrom(sManga)
|
||||
val result = db.insertManga(newManga).await()
|
||||
val result = db.insertManga(newManga).executeAsBlocking()
|
||||
newManga.id = result.insertedId()
|
||||
localManga = newManga
|
||||
}
|
@ -28,6 +28,11 @@ 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.latest.LatestUpdatesController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController
|
||||
import eu.kanade.tachiyomi.ui.smartsearch.SmartSearchController
|
||||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.source.latest.LatestUpdatesController
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -56,16 +61,22 @@ class SourceController :
|
||||
*/
|
||||
private var adapter: SourceAdapter? = null
|
||||
|
||||
// EXH -->
|
||||
private val mode = if (smartSearchConfig == null) Mode.CATALOGUE else Mode.SMART_SEARCH
|
||||
// EXH <--
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
// Enable the option menu
|
||||
setHasOptionsMenu(mode == Mode.CATALOGUE)
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return applicationContext?.getString(R.string.label_sources)
|
||||
}
|
||||
|
||||
returnwhen (mode) {
|
||||
Mode.CATALOGUE -> applicationContext?.getString(R.string.label_sources)
|
||||
Mode.SMART_SEARCH -> "Find in another source"
|
||||
}
|
||||
override fun createPresenter(): SourcePresenter {
|
||||
return SourcePresenter()
|
||||
return SourcePresenter(controllerMode = mode)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +126,16 @@ class SourceController :
|
||||
override fun onItemClick(view: View, position: Int): Boolean {
|
||||
val item = adapter?.getItem(position) as? SourceItem ?: return false
|
||||
val source = item.source
|
||||
openCatalogue(source, BrowseSourceController(source))
|
||||
when (mode) {
|
||||
Mode.CATALOGUE -> {
|
||||
// Open the catalogue view.
|
||||
openCatalogue(source, BrowseSourceController(source))
|
||||
}
|
||||
Mode.SMART_SEARCH -> router.pushController(SmartSearchController(Bundle().apply {
|
||||
putLong(SmartSearchController.ARG_SOURCE_ID, source.id)
|
||||
putParcelable(SmartSearchController.ARG_SMART_SEARCH_CONFIG, smartSearchConfig)
|
||||
}).withFadeTransaction())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -250,4 +270,16 @@ class SourceController :
|
||||
adapter?.addScrollableHeader(LangItem(SourcePresenter.LAST_USED_KEY))
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class SmartSearchConfig(val origTitle: String, val origMangaId: Long) : Parcelable
|
||||
|
||||
enum class Mode {
|
||||
CATALOGUE,
|
||||
SMART_SEARCH
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SMART_SEARCH_CONFIG = "SMART_SEARCH_CONFIG"
|
||||
}
|
||||
}
|
||||
|
@ -614,9 +614,8 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
|
||||
protected companion object {
|
||||
const val SOURCE_ID_KEY = "sourceId"
|
||||
|
||||
const val SEARCH_QUERY_KEY = "searchQuery"
|
||||
// EXH -->
|
||||
const val SMART_SEARCH_CONFIG_KEY = "smartSearchConfig"
|
||||
// EXH <--
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ 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.tables.MangaTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
|
||||
import exh.isLewdSource
|
||||
import exh.metadata.sql.tables.SearchMetadataTable
|
||||
import exh.search.SearchEngine
|
||||
import exh.util.await
|
||||
import exh.util.cancellable
|
||||
|
@ -34,9 +34,17 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
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.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import exh.favorites.FavoritesIntroDialog
|
||||
import exh.favorites.FavoritesSyncStatus
|
||||
import exh.ui.LoaderManager
|
||||
import java.io.IOException
|
||||
import kotlinx.android.synthetic.main.main_activity.tabs
|
||||
import kotlinx.coroutines.flow.filter
|
||||
@ -57,7 +65,8 @@ class LibraryController(
|
||||
TabbedController,
|
||||
ActionMode.Callback,
|
||||
ChangeMangaCategoriesDialog.Listener,
|
||||
DeleteLibraryMangasDialog.Listener {
|
||||
DeleteLibraryMangasDialog.Listener,
|
||||
MigrationInterface {
|
||||
|
||||
/**
|
||||
* Position of the active category.
|
||||
@ -86,6 +95,11 @@ class LibraryController(
|
||||
*/
|
||||
val selectionRelay: PublishRelay<LibrarySelectionEvent> = PublishRelay.create()
|
||||
|
||||
/**
|
||||
* Current mangas to move.
|
||||
*/
|
||||
private var migratingMangas = mutableSetOf<Manga>()
|
||||
|
||||
/**
|
||||
* Relay to notify search query changes.
|
||||
*/
|
||||
@ -468,10 +482,11 @@ class LibraryController(
|
||||
R.id.action_delete -> showDeleteMangaDialog()
|
||||
R.id.action_select_all -> selectAllCategoryManga()
|
||||
R.id.action_select_inverse -> selectInverseCategoryManga()
|
||||
R.id.action_auto_source_migration -> {
|
||||
router.pushController(MigrationDesignController.create(
|
||||
R.id.action_migrate -> {
|
||||
router.pushController(
|
||||
MigrationDesignController.create(
|
||||
selectedMangas.mapNotNull { it.id }
|
||||
).withFadeTransaction())
|
||||
).withFadeTransaction())
|
||||
destroyActionModeIfNeeded()
|
||||
}
|
||||
else -> return false
|
||||
@ -479,6 +494,27 @@ class LibraryController(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean): Manga? {
|
||||
if (manga.id != prevManga.id) {
|
||||
presenter.migrateManga(prevManga, manga, replace = replace)
|
||||
}
|
||||
val nextManga = migratingMangas.firstOrNull() ?: return null
|
||||
migratingMangas.remove(nextManga)
|
||||
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()
|
||||
|
@ -10,10 +10,14 @@ import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
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.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.lang.combineLatest
|
||||
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
|
||||
import java.io.IOException
|
||||
@ -367,6 +371,84 @@ class LibraryPresenter(
|
||||
db.setMangaCategories(mc, mangas)
|
||||
}
|
||||
|
||||
fun migrateManga(prevManga: Manga, manga: Manga, replace: Boolean) {
|
||||
val source = sourceManager.get(manga.source) ?: return
|
||||
|
||||
// state = state.copy(isReplacingManga = true)
|
||||
|
||||
Observable.defer { source.fetchChapterList(manga) }
|
||||
.onErrorReturn { emptyList() }
|
||||
.doOnNext { migrateMangaInternal(source, it, prevManga, manga, replace) }
|
||||
.onErrorReturn { emptyList() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// .doOnUnsubscribe { state = state.copy(isReplacingManga = false) }
|
||||
.subscribe()
|
||||
}
|
||||
|
||||
private fun migrateMangaInternal(
|
||||
source: Source,
|
||||
sourceChapters: List<SChapter>,
|
||||
prevManga: Manga,
|
||||
manga: Manga,
|
||||
replace: Boolean
|
||||
) {
|
||||
|
||||
val flags = preferences.migrateFlags().getOrDefault()
|
||||
val migrateChapters = MigrationFlags.hasChapters(flags)
|
||||
val migrateCategories = MigrationFlags.hasCategories(flags)
|
||||
val migrateTracks = MigrationFlags.hasTracks(flags)
|
||||
|
||||
db.inTransaction {
|
||||
// Update chapters read
|
||||
if (migrateChapters) {
|
||||
try {
|
||||
syncChaptersWithSource(db, sourceChapters, manga, source)
|
||||
} catch (e: Exception) {
|
||||
// Worst case, chapters won't be synced
|
||||
}
|
||||
|
||||
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
|
||||
val maxChapterRead =
|
||||
prevMangaChapters.filter { it.read }.maxBy { it.chapter_number }?.chapter_number
|
||||
if (maxChapterRead != null) {
|
||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||
for (chapter in dbChapters) {
|
||||
if (chapter.isRecognizedNumber && chapter.chapter_number <= maxChapterRead) {
|
||||
chapter.read = true
|
||||
}
|
||||
}
|
||||
db.insertChapters(dbChapters).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
// Update categories
|
||||
if (migrateCategories) {
|
||||
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking()
|
||||
val mangaCategories = categories.map { MangaCategory.create(manga, it) }
|
||||
db.setMangaCategories(mangaCategories, listOf(manga))
|
||||
}
|
||||
// Update track
|
||||
if (migrateTracks) {
|
||||
val tracks = db.getTracks(prevManga).executeAsBlocking()
|
||||
for (track in tracks) {
|
||||
track.id = null
|
||||
track.manga_id = manga.id!!
|
||||
}
|
||||
db.insertTracks(tracks).executeAsBlocking()
|
||||
}
|
||||
// Update favorite status
|
||||
if (replace) {
|
||||
prevManga.favorite = false
|
||||
db.updateMangaFavorite(prevManga).executeAsBlocking()
|
||||
}
|
||||
manga.favorite = true
|
||||
db.updateMangaFavorite(manga).executeAsBlocking()
|
||||
|
||||
// SearchPresenter#networkToLocalManga may have updated the manga title, so ensure db gets updated title
|
||||
db.updateMangaTitle(manga).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cover with local file.
|
||||
*
|
||||
|
@ -1,35 +1,33 @@
|
||||
package eu.kanade.tachiyomi.ui.migration
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
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.ui.base.controller.DialogController
|
||||
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.popControllerWithTag
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import exh.ui.migration.manga.design.MigrationDesignController
|
||||
import eu.kanade.tachiyomi.ui.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.android.synthetic.main.migration_controller.migration_recycler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
SourceAdapter.OnSelectClickListener,
|
||||
SourceAdapter.OnAutoClickListener {
|
||||
class MigrationController :
|
||||
NucleusController<MigrationControllerBinding, MigrationPresenter>(),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
SourceAdapter.OnSelectClickListener,
|
||||
SourceAdapter.OnAutoClickListener,
|
||||
MigrationInterface {
|
||||
|
||||
private var adapter: FlexibleAdapter<IFlexible<*>>? = null
|
||||
|
||||
@ -44,15 +42,26 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
}
|
||||
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
return inflater.inflate(R.layout.migration_controller, container, false)
|
||||
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)
|
||||
migration_recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context)
|
||||
migration_recycler.adapter = adapter
|
||||
binding.migrationRecycler.layoutManager =
|
||||
androidx.recyclerview.widget.LinearLayoutManager(view.context)
|
||||
binding.migrationRecycler.adapter = adapter
|
||||
binding.migrationRecycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
@ -75,29 +84,24 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
|
||||
fun render(state: ViewState) {
|
||||
if (state.selectedSource == null) {
|
||||
title = resources?.getString(R.string.label_migration)
|
||||
title = resources?.getString(R.string.source_migration)
|
||||
if (adapter !is SourceAdapter) {
|
||||
adapter = SourceAdapter(this)
|
||||
binding.migrationRecycler.adapter = adapter
|
||||
}
|
||||
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.migrationRecycler.adapter = adapter
|
||||
}
|
||||
adapter?.updateDataSet(state.mangaForSource)
|
||||
}
|
||||
}
|
||||
|
||||
fun renderIsReplacingManga(state: ViewState) {
|
||||
if (state.isReplacingManga) {
|
||||
if (router.getControllerWithTag(LOADING_DIALOG_TAG) == null) {
|
||||
LoadingController().showDialog(router, LOADING_DIALOG_TAG)
|
||||
}
|
||||
} else {
|
||||
router.popControllerWithTag(LOADING_DIALOG_TAG)
|
||||
adapter?.updateDataSet(state.mangaForSource, true)
|
||||
/*if (switching) launchUI {
|
||||
migration_recycler.alpha = 0f
|
||||
migration_recycler.animate().alpha(1f).setStartDelay(100).setDuration(200).start()
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,10 +109,11 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
val item = adapter?.getItem(position) ?: return false
|
||||
|
||||
if (item is MangaItem) {
|
||||
val controller = SearchController(item.manga)
|
||||
controller.targetController = this
|
||||
|
||||
router.pushController(controller.withFadeTransaction())
|
||||
PreMigrationController.navigateToMigration(
|
||||
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
|
||||
router,
|
||||
listOf(item.manga.id!!)
|
||||
)
|
||||
} else if (item is SourceItem) {
|
||||
presenter.setSelectedSource(item.source)
|
||||
}
|
||||
@ -116,41 +121,34 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
}
|
||||
|
||||
override fun onSelectClick(position: Int) {
|
||||
onItemClick(null, position)
|
||||
onItemClick(view, position)
|
||||
}
|
||||
|
||||
override fun onAutoClick(position: Int) {
|
||||
val item = adapter?.getItem(position) as? SourceItem ?: return
|
||||
|
||||
GlobalScope.launch {
|
||||
val manga = Injekt.get<DatabaseHelper>().getFavoriteMangas().asRxSingle().await(Schedulers.io())
|
||||
val sourceMangas = manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList()
|
||||
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) {
|
||||
router.pushController(MigrationDesignController.create(sourceMangas).withFadeTransaction())
|
||||
PreMigrationController.navigateToMigration(
|
||||
Injekt.get<PreferencesHelper>().skipPreMigration().get(),
|
||||
router,
|
||||
sourceMangas
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun migrateManga(prevManga: Manga, manga: Manga) {
|
||||
presenter.migrateManga(prevManga, manga, replace = true)
|
||||
}
|
||||
|
||||
fun copyManga(prevManga: Manga, manga: Manga) {
|
||||
presenter.migrateManga(prevManga, manga, replace = false)
|
||||
}
|
||||
|
||||
class LoadingController : DialogController() {
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return MaterialDialog.Builder(activity!!)
|
||||
.progress(true, 0)
|
||||
.content(R.string.migrating)
|
||||
.cancelable(false)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOADING_DIALOG_TAG = "LoadingDialog"
|
||||
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?
|
||||
}
|
||||
|
@ -4,11 +4,16 @@ import android.os.Bundle
|
||||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
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.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.util.lang.combineLatest
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.Injekt
|
||||
@ -16,7 +21,8 @@ import uy.kohesive.injekt.api.get
|
||||
|
||||
class MigrationPresenter(
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get()
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
) : BasePresenter<MigrationController>() {
|
||||
|
||||
var state = ViewState()
|
||||
@ -45,8 +51,10 @@ class MigrationPresenter(
|
||||
.doOnNext { state = state.copy(mangaForSource = it) }
|
||||
.subscribe()
|
||||
|
||||
// Render the view when any field changes
|
||||
stateRelay.subscribeLatestCache(MigrationController::render)
|
||||
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) {
|
||||
@ -67,4 +75,78 @@ class MigrationPresenter(
|
||||
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) {
|
||||
val source = sourceManager.get(manga.source) ?: return
|
||||
|
||||
state = state.copy(isReplacingManga = true)
|
||||
|
||||
Observable.defer { source.fetchChapterList(manga) }.onErrorReturn { emptyList() }
|
||||
.doOnNext { migrateMangaInternal(source, it, prevManga, manga, replace) }
|
||||
.onErrorReturn { emptyList() }.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnUnsubscribe { state = state.copy(isReplacingManga = false) }.subscribe()
|
||||
}
|
||||
|
||||
private fun migrateMangaInternal(
|
||||
source: Source,
|
||||
sourceChapters: List<SChapter>,
|
||||
prevManga: Manga,
|
||||
manga: Manga,
|
||||
replace: Boolean
|
||||
) {
|
||||
val flags = preferences.migrateFlags().get()
|
||||
val migrateChapters = MigrationFlags.hasChapters(flags)
|
||||
val migrateCategories = MigrationFlags.hasCategories(flags)
|
||||
val migrateTracks = MigrationFlags.hasTracks(flags)
|
||||
|
||||
db.inTransaction {
|
||||
// Update chapters read
|
||||
if (migrateChapters) {
|
||||
try {
|
||||
syncChaptersWithSource(db, sourceChapters, manga, source)
|
||||
} catch (e: Exception) {
|
||||
// Worst case, chapters won't be synced
|
||||
}
|
||||
|
||||
val prevMangaChapters = db.getChapters(prevManga).executeAsBlocking()
|
||||
val maxChapterRead =
|
||||
prevMangaChapters.filter { it.read }.maxBy { it.chapter_number }?.chapter_number
|
||||
if (maxChapterRead != null) {
|
||||
val dbChapters = db.getChapters(manga).executeAsBlocking()
|
||||
for (chapter in dbChapters) {
|
||||
if (chapter.isRecognizedNumber && chapter.chapter_number <= maxChapterRead) {
|
||||
chapter.read = true
|
||||
}
|
||||
}
|
||||
db.insertChapters(dbChapters).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
// Update categories
|
||||
if (migrateCategories) {
|
||||
val categories = db.getCategoriesForManga(prevManga).executeAsBlocking()
|
||||
val mangaCategories = categories.map { MangaCategory.create(manga, it) }
|
||||
db.setMangaCategories(mangaCategories, listOf(manga))
|
||||
}
|
||||
// Update track
|
||||
if (migrateTracks) {
|
||||
val tracks = db.getTracks(prevManga).executeAsBlocking()
|
||||
for (track in tracks) {
|
||||
track.id = null
|
||||
track.manga_id = manga.id!!
|
||||
}
|
||||
db.insertTracks(tracks).executeAsBlocking()
|
||||
}
|
||||
// Update favorite status
|
||||
if (replace) {
|
||||
prevManga.favorite = false
|
||||
db.updateMangaFavorite(prevManga).executeAsBlocking()
|
||||
}
|
||||
manga.favorite = true
|
||||
db.updateMangaFavorite(manga).executeAsBlocking()
|
||||
|
||||
// SearchPresenter#networkToLocalManga may have updated the manga title, so ensure db gets updated title
|
||||
db.updateMangaTitle(manga).executeAsBlocking()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
app/src/main/java/exh/ui/migration/MigrationStatus.kt → app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationStatus.kt
Executable file → Normal file
2
app/src/main/java/exh/ui/migration/MigrationStatus.kt → app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationStatus.kt
Executable file → Normal file
@ -1,4 +1,4 @@
|
||||
package exh.ui.migration
|
||||
package eu.kanade.tachiyomi.ui.migration
|
||||
|
||||
class MigrationStatus {
|
||||
companion object {
|
@ -2,16 +2,26 @@ package eu.kanade.tachiyomi.ui.migration
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItemsMultiChoice
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchPresenter
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
|
||||
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchPresenter
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.appcompat.QueryTextEvent
|
||||
import reactivecircus.flowbinding.appcompat.queryTextEvents
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SearchController(
|
||||
@ -19,6 +29,23 @@ class SearchController(
|
||||
) : GlobalSearchController(manga?.title) {
|
||||
|
||||
private var newManga: Manga? = null
|
||||
private var progress = 1
|
||||
var totalProgress = 0
|
||||
|
||||
/**
|
||||
* Called when controller is initialized.
|
||||
*/
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
if (totalProgress > 1) {
|
||||
return "($progress/$totalProgress) ${super.getTitle()}"
|
||||
} else {
|
||||
return super.getTitle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun createPresenter(): GlobalSearchPresenter {
|
||||
return SearchPresenter(initialQuery, manga!!)
|
||||
@ -36,21 +63,62 @@ class SearchController(
|
||||
newManga = savedInstanceState.getSerializable(::newManga.name) as? Manga
|
||||
}
|
||||
|
||||
/*override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
if (totalProgress > 1) {
|
||||
val menuItem = menu.add(Menu.NONE, 1, Menu.NONE, R.string.action_skip_manga)
|
||||
menuItem.icon = VectorDrawableCompat.create(resources!!, R.drawable
|
||||
.baseline_skip_next_white_24, null)
|
||||
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||
}
|
||||
}
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
1 -> {
|
||||
newManga = manga
|
||||
migrateManga()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}*/
|
||||
|
||||
fun migrateManga() {
|
||||
val target = targetController as? MigrationInterface ?: return
|
||||
val manga = manga ?: return
|
||||
val newManga = newManga ?: return
|
||||
|
||||
(presenter as? SearchPresenter)?.migrateManga(manga, newManga, true)
|
||||
val nextManga = target.migrateManga(manga, newManga, true)
|
||||
replaceWithNewSearchController(nextManga)
|
||||
}
|
||||
|
||||
fun copyManga() {
|
||||
val target = targetController as? MigrationInterface ?: return
|
||||
val manga = manga ?: return
|
||||
val newManga = newManga ?: return
|
||||
|
||||
(presenter as? SearchPresenter)?.migrateManga(manga, newManga, false)
|
||||
val nextManga = target.migrateManga(manga, newManga, false)
|
||||
replaceWithNewSearchController(nextManga)
|
||||
}
|
||||
|
||||
private fun replaceWithNewSearchController(manga: Manga?) {
|
||||
if (manga != null) {
|
||||
// router.popCurrentController()
|
||||
val searchController = SearchController(manga)
|
||||
searchController.targetController = targetController
|
||||
searchController.progress = progress + 1
|
||||
searchController.totalProgress = totalProgress
|
||||
router.replaceTopController(searchController.withFadeTransaction())
|
||||
} else router.popController(this)
|
||||
}
|
||||
|
||||
override fun onMangaClick(manga: Manga) {
|
||||
if (targetController is MigrationListController) {
|
||||
val migrationListController = targetController as? MigrationListController
|
||||
val sourceManager: SourceManager by injectLazy()
|
||||
val source = sourceManager.get(manga.source) ?: return
|
||||
migrationListController?.useMangaForMigration(manga, source)
|
||||
router.popCurrentController()
|
||||
return
|
||||
}
|
||||
newManga = manga
|
||||
val dialog = MigrationDialog()
|
||||
dialog.targetController = this
|
||||
@ -62,15 +130,6 @@ class SearchController(
|
||||
super.onMangaClick(manga)
|
||||
}
|
||||
|
||||
fun renderIsReplacingManga(isReplacingManga: Boolean) {
|
||||
if (isReplacingManga) {
|
||||
binding.progress.visible()
|
||||
} else {
|
||||
binding.progress.gone()
|
||||
router.popController(this)
|
||||
}
|
||||
}
|
||||
|
||||
class MigrationDialog : DialogController() {
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
@ -81,7 +140,7 @@ class SearchController(
|
||||
val preselected = MigrationFlags.getEnabledFlagsPositions(prefValue)
|
||||
|
||||
return MaterialDialog(activity!!)
|
||||
.message(R.string.migration_dialog_what_to_include)
|
||||
.message(R.string.data_to_include_in_migration)
|
||||
.listItemsMultiChoice(
|
||||
items = MigrationFlags.titles.map { resources?.getString(it) as CharSequence },
|
||||
initialSelection = preselected.toIntArray()
|
||||
@ -99,4 +158,40 @@ class SearchController(
|
||||
.neutralButton(android.R.string.cancel)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds items to the options menu.
|
||||
*
|
||||
* @param menu menu containing options.
|
||||
* @param inflater used to load the menu xml.
|
||||
*/
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
// Inflate menu.
|
||||
inflater.inflate(R.menu.source_browse, menu)
|
||||
|
||||
// Initialize search menu
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
|
||||
searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
searchView.onActionViewExpanded() // Required to show the query in the view
|
||||
searchView.setQuery(presenter.query, false)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
searchView.queryTextEvents()
|
||||
.filter { it is QueryTextEvent.QuerySubmitted }
|
||||
.onEach {
|
||||
presenter.search(it.queryText.toString())
|
||||
searchItem.collapseActionView()
|
||||
setTitle() // Update toolbar title
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
}
|
||||
|
@ -21,20 +21,6 @@ class SourceAdapter(val controller: MigrationController) :
|
||||
setDisplayHeadersAtStartUp(true)
|
||||
}
|
||||
|
||||
// EXH -->
|
||||
/**
|
||||
* Listener for auto item clicks.
|
||||
*/
|
||||
val autoClickListener: OnAutoClickListener? = controller
|
||||
|
||||
/**
|
||||
* Listener which should be called when user clicks select.
|
||||
*/
|
||||
interface OnAutoClickListener {
|
||||
fun onAutoClick(position: Int)
|
||||
}
|
||||
// EXH <--
|
||||
|
||||
/**
|
||||
* Listener for browse item clicks.
|
||||
*/
|
||||
@ -47,6 +33,18 @@ class SourceAdapter(val controller: MigrationController) :
|
||||
fun onSelectClick(position: Int)
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for auto item clicks.
|
||||
*/
|
||||
val autoClickListener: OnAutoClickListener? = controller
|
||||
|
||||
/**
|
||||
* Listener which should be called when user clicks select.
|
||||
*/
|
||||
interface OnAutoClickListener {
|
||||
fun onAutoClick(position: Int)
|
||||
}
|
||||
|
||||
override fun updateDataSet(items: MutableList<IFlexible<*>>?) {
|
||||
if (this.items !== items) {
|
||||
this.items = items
|
||||
|
@ -26,13 +26,13 @@ class SourceHolder(view: View, override val adapter: SourceAdapter) :
|
||||
|
||||
init {
|
||||
source_latest.text = "Auto"
|
||||
source_latest.setOnClickListener {
|
||||
adapter.autoClickListener?.onAutoClick(adapterPosition)
|
||||
}
|
||||
source_browse.setText(R.string.select)
|
||||
source_browse.setOnClickListener {
|
||||
adapter.selectClickListener?.onSelectClick(bindingAdapterPosition)
|
||||
}
|
||||
source_latest.setOnClickListener {
|
||||
adapter.autoClickListener?.onAutoClick(adapterPosition)
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(item: SourceItem) {
|
||||
|
@ -5,5 +5,6 @@ import eu.kanade.tachiyomi.source.Source
|
||||
data class ViewState(
|
||||
val selectedSource: Source? = null,
|
||||
val mangaForSource: List<MangaItem> = emptyList(),
|
||||
val sourcesWithManga: List<SourceItem> = emptyList()
|
||||
val sourcesWithManga: List<SourceItem> = emptyList(),
|
||||
val isReplacingManga: Boolean = false
|
||||
)
|
||||
|
@ -1,43 +1,44 @@
|
||||
package exh.ui.migration.manga.design
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.design
|
||||
|
||||
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.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
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.MigrationProcedureConfig
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureController
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import exh.ui.base.BaseExhController
|
||||
import exh.ui.migration.manga.process.MigrationProcedureConfig
|
||||
import exh.ui.migration.manga.process.MigrationProcedureController
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.begin_migration_btn
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.copy_manga
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.copy_manga_desc
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.extra_search_param
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.extra_search_param_desc
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.extra_search_param_text
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.fuzzy_search
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.mig_categories
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.mig_chapters
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.migration_mode
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.options_group
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.prioritize_chapter_count
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.recycler
|
||||
import kotlinx.android.synthetic.main.eh_migration_design.use_smart_search
|
||||
import kotlinx.android.synthetic.main.migration_design_controller.begin_migration_btn
|
||||
import kotlinx.android.synthetic.main.migration_design_controller.copy_manga
|
||||
import kotlinx.android.synthetic.main.migration_design_controller.copy_manga_desc
|
||||
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 kotlinx.android.synthetic.main.migration_design_controller.recycler
|
||||
import kotlinx.android.synthetic.main.migration_design_controller.use_smart_search
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
// TODO Select all in library
|
||||
class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bundle), FlexibleAdapter.OnItemClickListener {
|
||||
class MigrationDesignController(bundle: Bundle? = null) : BaseController(bundle), FlexibleAdapter
|
||||
.OnItemClickListener {
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
private val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
override val layoutId: Int = R.layout.eh_migration_design
|
||||
|
||||
private var adapter: MigrationSourceAdapter? = null
|
||||
|
||||
private val config: LongArray = args.getLongArray(MANGA_IDS_EXTRA) ?: LongArray(0)
|
||||
@ -46,6 +47,10 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
|
||||
override fun getTitle() = "Select target sources"
|
||||
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
return inflater.inflate(R.layout.migration_design_controller, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
@ -54,7 +59,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
this
|
||||
)
|
||||
adapter = ourAdapter
|
||||
recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context)
|
||||
recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
recycler.setHasFixedSize(true)
|
||||
recycler.adapter = ourAdapter
|
||||
ourAdapter.itemTouchHelperCallback = null // Reset adapter touch adapter to fix drag after rotation
|
||||
@ -100,7 +105,8 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund
|
||||
if (mig_categories.isChecked) flags = flags or MigrationFlags.CATEGORIES
|
||||
if (mig_categories.isChecked) flags = flags or MigrationFlags.TRACK
|
||||
|
||||
router.replaceTopController(MigrationProcedureController.create(
|
||||
router.replaceTopController(
|
||||
MigrationProcedureController.create(
|
||||
MigrationProcedureConfig(
|
||||
config.toList(),
|
||||
ourAdapter.items.filter {
|
@ -1,8 +1,9 @@
|
||||
package exh.ui.migration.manga.design
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.design
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import exh.debug.DebugFunctions.sourceManager
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MigrationSourceAdapter(
|
||||
val items: List<MigrationSourceItem>,
|
||||
@ -21,7 +22,10 @@ class MigrationSourceAdapter(
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
savedInstanceState.getParcelableArrayList<MigrationSourceItem.ParcelableSI>(SELECTED_SOURCES_KEY)?.let {
|
||||
val sourceManager: SourceManager by injectLazy()
|
||||
savedInstanceState.getParcelableArrayList<MigrationSourceItem.ParcelableSI>(
|
||||
SELECTED_SOURCES_KEY
|
||||
)?.let {
|
||||
updateDataSet(it.map { MigrationSourceItem.fromParcelable(sourceManager, it) })
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.design
|
||||
|
||||
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
|
||||
import android.view.View
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.icon
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import kotlinx.android.synthetic.main.migration_source_item.image
|
||||
import kotlinx.android.synthetic.main.migration_source_item.reorder
|
||||
import kotlinx.android.synthetic.main.migration_source_item.title
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MigrationSourceHolder(view: View, val adapter: MigrationSourceAdapter) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
init {
|
||||
setDragHandleView(reorder)
|
||||
}
|
||||
|
||||
fun bind(source: HttpSource, sourceEnabled: Boolean) {
|
||||
val preferences by injectLazy<PreferencesHelper>()
|
||||
val isMultiLanguage = preferences.enabledLanguages().get().size > 1
|
||||
// Set capitalized title.
|
||||
val sourceName = if (isMultiLanguage) source.toString() else source.name.capitalize()
|
||||
title.text = sourceName
|
||||
// Update circle letter image.
|
||||
itemView.post {
|
||||
val icon = source.icon()
|
||||
if (icon != null) {
|
||||
image.setImageDrawable(icon)
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceEnabled) {
|
||||
title.alpha = 1.0f
|
||||
image.alpha = 1.0f
|
||||
title.paintFlags = title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
|
||||
} else {
|
||||
title.alpha = DISABLED_ALPHA
|
||||
image.alpha = DISABLED_ALPHA
|
||||
title.paintFlags = title.paintFlags or STRIKE_THRU_TEXT_FLAG
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an item is released.
|
||||
*
|
||||
* @param position The position of the released item.
|
||||
*/
|
||||
override fun onItemReleased(position: Int) {
|
||||
super.onItemReleased(position)
|
||||
adapter.updateItems()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DISABLED_ALPHA = 0.3f
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
package exh.ui.migration.manga.design
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.design
|
||||
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
@ -11,9 +12,9 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean) : AbstractFlexibleItem<MigrationSourceHolder>() {
|
||||
override fun getLayoutRes() = R.layout.eh_source_item
|
||||
override fun getLayoutRes() = R.layout.migration_source_item
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>): MigrationSourceHolder {
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MigrationSourceHolder {
|
||||
return MigrationSourceHolder(view, adapter as MigrationSourceAdapter)
|
||||
}
|
||||
|
||||
@ -26,7 +27,7 @@ class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean) :
|
||||
* @param payloads List of partial changes.
|
||||
*/
|
||||
override fun bindViewHolder(
|
||||
adapter: FlexibleAdapter<IFlexible<androidx.recyclerview.widget.RecyclerView.ViewHolder>>,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
|
||||
holder: MigrationSourceHolder,
|
||||
position: Int,
|
||||
payloads: List<Any?>?
|
@ -1,10 +1,11 @@
|
||||
package exh.ui.migration.manga.process
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.process
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
|
||||
class DeactivatableViewPager : androidx.viewpager.widget.ViewPager {
|
||||
class DeactivatableViewPager : ViewPager {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
@ -1,11 +1,10 @@
|
||||
package exh.ui.migration.manga.process
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.process
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import exh.util.DeferredField
|
||||
import exh.util.await
|
||||
import eu.kanade.tachiyomi.util.DeferredField
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
@ -27,7 +26,7 @@ class MigratingManga(
|
||||
@Volatile
|
||||
private var manga: Manga? = null
|
||||
suspend fun manga(): Manga? {
|
||||
if (manga == null) manga = db.getManga(mangaId).await()
|
||||
if (manga == null) manga = db.getManga(mangaId).executeAsBlocking()
|
||||
return manga
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
package exh.ui.migration.manga.process
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.process
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.elvishew.xlog.XLog
|
||||
import com.google.gson.Gson
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
@ -21,29 +21,28 @@ import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.inflate
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import exh.MERGED_SOURCE_ID
|
||||
import exh.util.await
|
||||
import java.text.DateFormat
|
||||
import java.text.DecimalFormat
|
||||
import java.util.Date
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.loading_group
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_artist
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_author
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_chapters
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_cover
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_full_title
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_last_chapter
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_last_update
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_source
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_source_label
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.manga_status
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.search_progress
|
||||
import kotlinx.android.synthetic.main.eh_manga_card.view.search_status
|
||||
import kotlinx.android.synthetic.main.eh_migration_process_item.view.accept_migration
|
||||
import kotlinx.android.synthetic.main.eh_migration_process_item.view.eh_manga_card_from
|
||||
import kotlinx.android.synthetic.main.eh_migration_process_item.view.eh_manga_card_to
|
||||
import kotlinx.android.synthetic.main.eh_migration_process_item.view.migrating_frame
|
||||
import kotlinx.android.synthetic.main.eh_migration_process_item.view.skip_migration
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.loading_group
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_artist
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_author
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_chapters
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_cover
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_full_title
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_last_chapter
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_last_update
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_source
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_source_label
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.manga_status
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.search_progress
|
||||
import kotlinx.android.synthetic.main.migration_manga_card.view.search_status
|
||||
import kotlinx.android.synthetic.main.migration_process_item.view.accept_migration
|
||||
import kotlinx.android.synthetic.main.migration_process_item.view.migrating_frame
|
||||
import kotlinx.android.synthetic.main.migration_process_item.view.migration_manga_card_from
|
||||
import kotlinx.android.synthetic.main.migration_process_item.view.migration_manga_card_to
|
||||
import kotlinx.android.synthetic.main.migration_process_item.view.skip_migration
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -58,13 +57,11 @@ class MigrationProcedureAdapter(
|
||||
val controller: MigrationProcedureController,
|
||||
val migratingManga: List<MigratingManga>,
|
||||
override val coroutineContext: CoroutineContext
|
||||
) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope {
|
||||
) : PagerAdapter(), CoroutineScope {
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
private val gson: Gson by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
private val logger = XLog.tag(this::class.simpleName)
|
||||
|
||||
override fun isViewFromObject(p0: View, p1: Any): Boolean {
|
||||
return p0 == p1
|
||||
}
|
||||
@ -73,7 +70,7 @@ class MigrationProcedureAdapter(
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val item = migratingManga[position]
|
||||
val view = container.inflate(R.layout.eh_migration_process_item)
|
||||
val view = container.inflate(R.layout.migration_process_item)
|
||||
container.addView(view)
|
||||
|
||||
view.skip_migration.setOnClickListener {
|
||||
@ -93,7 +90,6 @@ class MigrationProcedureAdapter(
|
||||
}
|
||||
controller.nextMigration()
|
||||
} catch (e: Exception) {
|
||||
logger.e("Migration failure!", e)
|
||||
controller.migrationFailure()
|
||||
}
|
||||
view.migrating_frame.gone()
|
||||
@ -108,13 +104,13 @@ class MigrationProcedureAdapter(
|
||||
return
|
||||
}
|
||||
|
||||
val toMangaObj = db.getManga(manga.searchResult.get() ?: return).await() ?: return
|
||||
val toMangaObj = db.getManga(manga.searchResult.get() ?: return).executeAsBlocking() ?: return
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
migrateMangaInternal(
|
||||
manga.manga() ?: return@withContext,
|
||||
toMangaObj,
|
||||
!controller.config.copy
|
||||
!(controller.config?.copy ?: false)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -124,6 +120,7 @@ class MigrationProcedureAdapter(
|
||||
manga: Manga,
|
||||
replace: Boolean
|
||||
) {
|
||||
val config = controller.config ?: return
|
||||
db.inTransaction {
|
||||
// Update chapters read
|
||||
if (MigrationFlags.hasChapters(controller.config.migrationFlags)) {
|
||||
@ -174,9 +171,9 @@ class MigrationProcedureAdapter(
|
||||
val source = migratingManga.mangaSource()
|
||||
if (manga != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
eh_manga_card_from.loading_group.gone()
|
||||
eh_manga_card_from.attachManga(tag, manga, source)
|
||||
eh_manga_card_from.setOnClickListener {
|
||||
migration_manga_card_from.loading_group.gone()
|
||||
migration_manga_card_from.attachManga(tag, manga, source)
|
||||
migration_manga_card_from.setOnClickListener {
|
||||
controller.router.pushController(MangaController(manga, true).withFadeTransaction())
|
||||
}
|
||||
}
|
||||
@ -184,7 +181,7 @@ class MigrationProcedureAdapter(
|
||||
tag.launch {
|
||||
migratingManga.progress.asFlow().collect { (max, progress) ->
|
||||
withContext(Dispatchers.Main) {
|
||||
eh_manga_card_to.search_progress.let { progressBar ->
|
||||
migration_manga_card_to.search_progress.let { progressBar ->
|
||||
progressBar.max = max
|
||||
progressBar.progress = progress
|
||||
}
|
||||
@ -193,23 +190,23 @@ class MigrationProcedureAdapter(
|
||||
}
|
||||
|
||||
val searchResult = migratingManga.searchResult.get()?.let {
|
||||
db.getManga(it).await()
|
||||
db.getManga(it).executeAsBlocking()
|
||||
}
|
||||
val resultSource = searchResult?.source?.let {
|
||||
sourceManager.get(it)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
if (searchResult != null && resultSource != null) {
|
||||
eh_manga_card_to.loading_group.gone()
|
||||
eh_manga_card_to.attachManga(tag, searchResult, resultSource)
|
||||
eh_manga_card_to.setOnClickListener {
|
||||
migration_manga_card_to.loading_group.gone()
|
||||
migration_manga_card_to.attachManga(tag, searchResult, resultSource)
|
||||
migration_manga_card_to.setOnClickListener {
|
||||
controller.router.pushController(MangaController(searchResult, true).withFadeTransaction())
|
||||
}
|
||||
accept_migration.isEnabled = true
|
||||
accept_migration.alpha = 1.0f
|
||||
} else {
|
||||
eh_manga_card_to.search_progress.gone()
|
||||
eh_manga_card_to.search_status.text = "Found no manga"
|
||||
migration_manga_card_to.search_progress.gone()
|
||||
migration_manga_card_to.search_status.text = "Found no manga"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,7 +261,7 @@ class MigrationProcedureAdapter(
|
||||
else -> R.string.unknown
|
||||
})
|
||||
|
||||
val mangaChapters = db.getChapters(manga).await()
|
||||
val mangaChapters = db.getChapters(manga).executeAsBlocking()
|
||||
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)
|
@ -1,4 +1,4 @@
|
||||
package exh.ui.migration.manga.process
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.process
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
@ -1,22 +1,23 @@
|
||||
package exh.ui.migration.manga.process
|
||||
package eu.kanade.tachiyomi.ui.migration.manga.process
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.elvishew.xlog.XLog
|
||||
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 exh.smartsearch.SmartSearchEngine
|
||||
import exh.ui.base.BaseExhController
|
||||
import exh.util.await
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlinx.android.synthetic.main.eh_migration_process.pager
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.android.synthetic.main.migration_process.pager
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -32,25 +33,28 @@ import rx.schedulers.Schedulers
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
// TODO Will probably implode if activity is fully destroyed
|
||||
class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(bundle), CoroutineScope {
|
||||
override val layoutId = R.layout.eh_migration_process
|
||||
class MigrationProcedureController(bundle: Bundle? = null) : BaseController(bundle), CoroutineScope {
|
||||
|
||||
private var titleText = "Migrate manga"
|
||||
|
||||
private var adapter: MigrationProcedureAdapter? = null
|
||||
|
||||
val config: MigrationProcedureConfig = args.getParcelable(CONFIG_EXTRA)
|
||||
override val coroutineContext: CoroutineContext = Job() + Dispatchers.Default
|
||||
|
||||
val config: MigrationProcedureConfig? = args.getParcelable(CONFIG_EXTRA)
|
||||
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
private val smartSearchEngine = SmartSearchEngine(coroutineContext, config.extraSearchParams)
|
||||
|
||||
private val logger = XLog.tag("MigrationProcedureController")
|
||||
private val smartSearchEngine = SmartSearchEngine(coroutineContext, config?.extraSearchParams)
|
||||
|
||||
private var migrationsJob: Job? = null
|
||||
private var migratingManga: List<MigratingManga>? = null
|
||||
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
return inflater.inflate(R.layout.migration_process, container, false)
|
||||
}
|
||||
|
||||
override fun getTitle(): String {
|
||||
return titleText
|
||||
}
|
||||
@ -58,12 +62,8 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
setTitle()
|
||||
|
||||
activity?.requestedOrientation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
} else {
|
||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
}
|
||||
val config = this.config ?: return
|
||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
|
||||
val newMigratingManga = migratingManga ?: run {
|
||||
val new = config.mangaIds.map {
|
||||
@ -121,7 +121,8 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
}
|
||||
|
||||
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 } ?: return
|
||||
|
||||
for (manga in mangas) {
|
||||
if (!manga.searchResult.initialized && manga.migrationJob.isActive) {
|
||||
@ -147,7 +148,8 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
async {
|
||||
sourceSemaphore.withPermit {
|
||||
try {
|
||||
val searchResult = if (config.enableLenientSearch) {
|
||||
val searchResult = if (config?.enableLenientSearch ==
|
||||
true) {
|
||||
smartSearchEngine.smartSearch(source, mangaObj.title)
|
||||
} else {
|
||||
smartSearchEngine.normalSearch(source, mangaObj.title)
|
||||
@ -168,7 +170,6 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
logger.e("Failed to search in source: ${source.id}!", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
@ -195,7 +196,6 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
logger.e("Failed to search in source: ${source.id}!", e)
|
||||
null
|
||||
}
|
||||
|
||||
@ -220,12 +220,11 @@ class MigrationProcedureController(bundle: Bundle? = null) : BaseExhController(b
|
||||
.await()
|
||||
result.copyFrom(newManga)
|
||||
|
||||
db.insertManga(result).await()
|
||||
db.insertManga(result).executeAsBlocking()
|
||||
} catch (e: CancellationException) {
|
||||
// Ignore cancellations
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
logger.e("Could not load search manga details", e)
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||
import eu.kanade.tachiyomi.ui.migration.MetadataFetchDialog
|
||||
import eu.kanade.tachiyomi.util.preference.defaultValue
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
|
@ -1,4 +1,4 @@
|
||||
package exh.ui.smartsearch
|
||||
package eu.kanade.tachiyomi.ui.smartsearch
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.ui.source.SourceController
|
||||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.android.synthetic.main.eh_smart_search.appbar
|
||||
import kotlinx.android.synthetic.main.smart_search.appbar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
@ -29,10 +29,12 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSea
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
private val source = sourceManager.get(bundle?.getLong(ARG_SOURCE_ID, -1) ?: -1) as? CatalogueSource
|
||||
private val smartSearchConfig: SourceController.SmartSearchConfig? = bundle?.getParcelable(ARG_SMART_SEARCH_CONFIG)
|
||||
private val smartSearchConfig: SourceController.SmartSearchConfig? = bundle?.getParcelable(
|
||||
ARG_SMART_SEARCH_CONFIG
|
||||
)
|
||||
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup) =
|
||||
inflater.inflate(R.layout.eh_smart_search, container, false)!!
|
||||
inflater.inflate(R.layout.smart_search, container, false)!!
|
||||
|
||||
override fun getTitle() = source?.name ?: ""
|
||||
|
@ -1,13 +1,12 @@
|
||||
package exh.ui.smartsearch
|
||||
package eu.kanade.tachiyomi.ui.smartsearch
|
||||
|
||||
import android.os.Bundle
|
||||
import com.elvishew.xlog.XLog
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.smartsearch.SmartSearchEngine
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.ui.source.SourceController
|
||||
import exh.smartsearch.SmartSearchEngine
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -18,7 +17,6 @@ import kotlinx.coroutines.launch
|
||||
|
||||
class SmartSearchPresenter(private val source: CatalogueSource?, private val config: SourceController.SmartSearchConfig?) :
|
||||
BasePresenter<SmartSearchController>(), CoroutineScope {
|
||||
private val logger = XLog.tag("SmartSearchPresenter")
|
||||
|
||||
override val coroutineContext = Job() + Dispatchers.Main
|
||||
|
||||
@ -43,7 +41,6 @@ class SmartSearchPresenter(private val source: CatalogueSource?, private val con
|
||||
if (e is CancellationException) {
|
||||
throw e
|
||||
} else {
|
||||
logger.e("Smart search error", e)
|
||||
SearchResults.Error
|
||||
}
|
||||
}
|
47
app/src/main/java/eu/kanade/tachiyomi/util/DeferredField.kt
Normal file
47
app/src/main/java/eu/kanade/tachiyomi/util/DeferredField.kt
Normal file
@ -0,0 +1,47 @@
|
||||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
/**
|
||||
* Field that can be initialized later. Users can suspend while waiting for the field to initialize.
|
||||
*
|
||||
* @author nulldev
|
||||
*/
|
||||
class DeferredField<T> {
|
||||
|
||||
@Volatile
|
||||
private var content: T? = null
|
||||
|
||||
@Volatile
|
||||
var initialized = false
|
||||
private set
|
||||
|
||||
private val mutex = Mutex(true)
|
||||
|
||||
/**
|
||||
* Initialize the field
|
||||
*/
|
||||
fun initialize(content: T) {
|
||||
// Fast-path new listeners
|
||||
this.content = content
|
||||
initialized = true
|
||||
|
||||
// Notify current listeners
|
||||
mutex.unlock()
|
||||
}
|
||||
|
||||
/**
|
||||
* Will only suspend if !initialized.
|
||||
*/
|
||||
suspend fun get(): T {
|
||||
// Check if field is initialized and return immediately if it is
|
||||
if (initialized) return content as T
|
||||
|
||||
// Wait for field to initialize
|
||||
mutex.withLock {}
|
||||
|
||||
// Field is initialized, return value
|
||||
return content as T
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package exh.metadata.metadata.base
|
||||
|
||||
import com.pushtorefresh.storio.operations.PreparedOperation
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import exh.metadata.sql.models.SearchMetadata
|
||||
import eu.kanade.tachiyomi.data.database.models.SearchMetadata
|
||||
import exh.metadata.sql.models.SearchTag
|
||||
import exh.metadata.sql.models.SearchTitle
|
||||
import kotlin.reflect.KClass
|
||||
|
@ -1,9 +1,9 @@
|
||||
package exh.metadata.metadata.base
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import eu.kanade.tachiyomi.data.database.models.SearchMetadata
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.forEach
|
||||
import exh.metadata.sql.models.SearchMetadata
|
||||
import exh.metadata.sql.models.SearchTag
|
||||
import exh.metadata.sql.models.SearchTitle
|
||||
import exh.plusAssign
|
||||
|
@ -1,6 +1,6 @@
|
||||
package exh.search
|
||||
|
||||
import exh.metadata.sql.tables.SearchMetadataTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.SearchMetadataTable
|
||||
import exh.metadata.sql.tables.SearchTagTable
|
||||
import exh.metadata.sql.tables.SearchTitleTable
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
package exh.ui.migration.manga.design
|
||||
|
||||
import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
|
||||
import android.view.View
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.util.view.getRound
|
||||
import kotlinx.android.synthetic.main.eh_source_item.image
|
||||
import kotlinx.android.synthetic.main.eh_source_item.reorder
|
||||
import kotlinx.android.synthetic.main.eh_source_item.title
|
||||
|
||||
class MigrationSourceHolder(view: View, val adapter: FlexibleAdapter<MigrationSourceItem>) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
init {
|
||||
setDragHandleView(reorder)
|
||||
}
|
||||
|
||||
fun bind(source: HttpSource, sourceEnabled: Boolean) {
|
||||
// Set capitalized title.
|
||||
title.text = source.name.capitalize()
|
||||
|
||||
// Update circle letter image.
|
||||
itemView.post {
|
||||
image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(), false))
|
||||
}
|
||||
|
||||
if (sourceEnabled) {
|
||||
title.alpha = 1.0f
|
||||
image.alpha = 1.0f
|
||||
title.paintFlags = title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
|
||||
} else {
|
||||
title.alpha = DISABLED_ALPHA
|
||||
image.alpha = DISABLED_ALPHA
|
||||
title.paintFlags = title.paintFlags or STRIKE_THRU_TEXT_FLAG
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DISABLED_ALPHA = 0.3f
|
||||
}
|
||||
}
|
10
app/src/main/res/drawable/baseline_swap_calls_24.xml
Normal file
10
app/src/main/res/drawable/baseline_swap_calls_24.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M18,4l-4,4h3v7c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2V8c0,-2.21 -1.79,-4 -4,-4S5,5.79 5,8v7H2l4,4 4,-4H7V8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2v7c0,2.21 1.79,4 4,4s4,-1.79 4,-4V8h3l-4,-4z"/>
|
||||
</vector>
|
@ -16,7 +16,7 @@
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:listitem="@layout/eh_source_item">
|
||||
tools:listitem="@layout/migration_source_item">
|
||||
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="Data to include in migration"
|
||||
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" />
|
||||
@ -45,7 +45,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:checked="true"
|
||||
android:text="@string/categories"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/mig_chapters"
|
||||
@ -56,7 +55,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:checked="true"
|
||||
android:text="@string/track"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/mig_categories"
|
||||
@ -67,9 +65,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="Options"
|
||||
android:text="@string/options"
|
||||
android:textAppearance="@style/TextAppearance.Medium.Body2"
|
||||
app:layout_constraintBottom_toTopOf="@+id/prioritize_chapter_count"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
@ -95,7 +92,8 @@
|
||||
android:clickable="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/fuzzy_search"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" />
|
||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
|
||||
android:focusable="true" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/use_smart_search"
|
||||
@ -114,11 +112,12 @@
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="Use intelligent search algorithm"
|
||||
android:text="@string/use_intelligent_search"
|
||||
android:clickable="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/copy_manga"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" />
|
||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
|
||||
android:focusable="true" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/copy_manga"
|
||||
@ -137,11 +136,12 @@
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="Keep old manga"
|
||||
android:text="@string/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" />
|
||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
|
||||
android:focusable="true" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/extra_search_param"
|
||||
@ -160,11 +160,12 @@
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:text="Include extra search parameter when searching"
|
||||
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" />
|
||||
app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count"
|
||||
android:focusable="true" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/extra_search_param_text"
|
||||
@ -175,11 +176,12 @@
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:ems="10"
|
||||
android:hint="Search parameter (e.g. language:english)"
|
||||
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" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:importantForAutofill="no" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/begin_migration_btn"
|
||||
@ -191,7 +193,7 @@
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="Begin migration"
|
||||
android:text="@string/begin_migration"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
@ -17,7 +17,7 @@
|
||||
android:id="@+id/manga_cover"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:contentDescription="@string/description_cover"
|
||||
app:layout_constraintDimensionRatio="l,2:3"
|
||||
@ -71,7 +71,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
@ -96,7 +96,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
@ -121,7 +121,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
@ -146,7 +146,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintLeft_toRightOf="@+id/manga_chapters_label"
|
||||
@ -169,7 +169,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintLeft_toRightOf="@+id/manga_last_chapter_label"
|
||||
@ -192,7 +192,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintLeft_toRightOf="@+id/manga_last_update_label"
|
||||
@ -215,7 +215,7 @@
|
||||
style="@style/TextAppearance.Regular.Body1.Secondary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:clickable="false"
|
||||
android:textIsSelectable="false"
|
||||
app:layout_constraintLeft_toRightOf="@+id/manga_source_label"
|
@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorPrimary">
|
||||
android:background="?attr/colorPrimary" >
|
||||
|
||||
<exh.ui.migration.manga.process.DeactivatableViewPager
|
||||
<eu.kanade.tachiyomi.ui.migration.manga.process.DeactivatableViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:animateLayoutChanges="true">
|
||||
@ -10,8 +11,8 @@
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
android:id="@+id/eh_manga_card_from"
|
||||
layout="@layout/eh_manga_card"
|
||||
android:id="@+id/migration_manga_card_from"
|
||||
layout="@layout/migration_manga_card"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
@ -33,12 +34,12 @@
|
||||
android:scaleType="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/eh_manga_card_from"
|
||||
app:srcCompat="@drawable/eh_ic_arrow_drop_down_white_100dp" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/migration_manga_card_from"
|
||||
app:srcCompat="@drawable/ic_arrow_down_white_32dp" />
|
||||
|
||||
<include
|
||||
android:id="@+id/eh_manga_card_to"
|
||||
layout="@layout/eh_manga_card"
|
||||
android:id="@+id/migration_manga_card_to"
|
||||
layout="@layout/migration_manga_card"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
@ -55,10 +56,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:drawableStart="@drawable/eh_ic_clear_white_24dp"
|
||||
android:drawableLeft="@drawable/eh_ic_clear_white_24dp"
|
||||
android:drawableStart="@drawable/ic_clear_grey"
|
||||
android:drawablePadding="6dp"
|
||||
android:text="Skip manga"
|
||||
android:textColor="#ffffff"
|
||||
@ -73,8 +72,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:alpha="0.5"
|
||||
android:drawableStart="@drawable/eh_ic_check_white_24dp"
|
||||
android:drawableLeft="@drawable/eh_ic_check_white_24dp"
|
||||
android:drawableStart="@drawable/ic_check_box_24dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:enabled="false"
|
||||
android:text="Migrate manga"
|
8
app/src/main/res/layout/eh_smart_search.xml → app/src/main/res/layout/smart_search.xml
Executable file → Normal file
8
app/src/main/res/layout/eh_smart_search.xml → app/src/main/res/layout/smart_search.xml
Executable file → Normal file
@ -1,6 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true">
|
||||
@ -40,7 +42,7 @@
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="Searching source..."
|
||||
android:textAppearance="@style/TextAppearance.Medium.Title"
|
||||
android:textColor="@color/white" />
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/intercept_progress"
|
||||
@ -48,7 +50,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminateTint="@color/white" />
|
||||
android:indeterminateTint="@android:color/white" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
@ -31,8 +31,10 @@
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_auto_source_migration"
|
||||
android:title="Source migration (automatic)"
|
||||
app:showAsAction="never"/>
|
||||
android:id="@+id/action_migrate"
|
||||
android:icon="@drawable/baseline_swap_calls_24"
|
||||
android:title="@string/label_migration"
|
||||
app:iconTint="?attr/colorOnPrimary"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
||||
|
@ -644,6 +644,12 @@
|
||||
<string name="channel_backup_restore">Backup and restore</string>
|
||||
<string name="channel_backup_restore_progress">Progress</string>
|
||||
<string name="channel_backup_restore_complete">Complete</string>
|
||||
<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>
|
||||
|
||||
<!-- EXH -->
|
||||
<string name="label_login">Login</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user