diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt index 245caa4c0..88031499f 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/MangaQueries.kt @@ -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 { diff --git a/app/src/main/java/exh/smartsearch/SmartSearchEngine.kt b/app/src/main/java/eu/kanade/tachiyomi/smartsearch/SmartSearchEngine.kt similarity index 90% rename from app/src/main/java/exh/smartsearch/SmartSearchEngine.kt rename to app/src/main/java/eu/kanade/tachiyomi/smartsearch/SmartSearchEngine.kt index 1bc53bb64..a388a66ed 100644 --- a/app/src/main/java/exh/smartsearch/SmartSearchEngine.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/smartsearch/SmartSearchEngine.kt @@ -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 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourceController.kt index a2632b469..0b84d386b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourceController.kt @@ -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" + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt index 4b77da0e7..0e9ea9c1b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt @@ -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 <-- } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt index 135391dd8..064e0034c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index af5e15dc7..a94fb0adb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -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 = PublishRelay.create() + /** + * Current mangas to move. + */ + private var migratingMangas = mutableSetOf() + /** * 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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 9ff65a535..43a963362 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -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, + 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. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt index 13ccf1320..4095c54cf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt @@ -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(), - FlexibleAdapter.OnItemClickListener, - SourceAdapter.OnSelectClickListener, - SourceAdapter.OnAutoClickListener { +class MigrationController : + NucleusController(), + FlexibleAdapter.OnItemClickListener, + SourceAdapter.OnSelectClickListener, + SourceAdapter.OnAutoClickListener, + MigrationInterface { private var adapter: FlexibleAdapter>? = null @@ -44,15 +42,26 @@ class MigrationController : NucleusController(), } 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(), 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(), 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().skipPreMigration().get(), + router, + listOf(item.manga.id!!) + ) } else if (item is SourceItem) { presenter.setSelectedSource(item.source) } @@ -116,41 +121,34 @@ class MigrationController : NucleusController(), } 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().getFavoriteMangas().asRxSingle().await(Schedulers.io()) - val sourceMangas = manga.asSequence().filter { it.source == item.source.id }.map { it.id!! }.toList() + launchUI { + val manga = Injekt.get().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().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? +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt index 77e5b9392..1c1bc95d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationPresenter.kt @@ -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() { 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, sourceId: Long): List { 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, + 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() + } + } } diff --git a/app/src/main/java/exh/ui/migration/MigrationStatus.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationStatus.kt old mode 100755 new mode 100644 similarity index 88% rename from app/src/main/java/exh/ui/migration/MigrationStatus.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationStatus.kt index 333d85c92..6fab478c4 --- a/app/src/main/java/exh/ui/migration/MigrationStatus.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationStatus.kt @@ -1,4 +1,4 @@ -package exh.ui.migration +package eu.kanade.tachiyomi.ui.migration class MigrationStatus { companion object { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchController.kt index 43e22220b..d9e511435 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SearchController.kt @@ -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) + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt index 2c7cbcb35..e419c6fda 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceAdapter.kt @@ -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>?) { if (this.items !== items) { this.items = items diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceHolder.kt index 96b8620f3..dbddbff0e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceHolder.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/ViewState.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/ViewState.kt index 5fbab9122..0481ed603 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/ViewState.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/ViewState.kt @@ -5,5 +5,6 @@ import eu.kanade.tachiyomi.source.Source data class ViewState( val selectedSource: Source? = null, val mangaForSource: List = emptyList(), - val sourcesWithManga: List = emptyList() + val sourcesWithManga: List = emptyList(), + val isReplacingManga: Boolean = false ) diff --git a/app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationDesignController.kt similarity index 75% rename from app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationDesignController.kt index 69ba8b356..52a0a4017 100644 --- a/app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationDesignController.kt @@ -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 { diff --git a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceAdapter.kt similarity index 77% rename from app/src/main/java/exh/ui/migration/manga/design/MigrationSourceAdapter.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceAdapter.kt index 622f2cb51..9f771f6c5 100644 --- a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceAdapter.kt @@ -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, @@ -21,7 +22,10 @@ class MigrationSourceAdapter( } override fun onRestoreInstanceState(savedInstanceState: Bundle) { - savedInstanceState.getParcelableArrayList(SELECTED_SOURCES_KEY)?.let { + val sourceManager: SourceManager by injectLazy() + savedInstanceState.getParcelableArrayList( + SELECTED_SOURCES_KEY + )?.let { updateDataSet(it.map { MigrationSourceItem.fromParcelable(sourceManager, it) }) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceHolder.kt new file mode 100644 index 000000000..5daff7f88 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceHolder.kt @@ -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() + 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 + } +} diff --git a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceItem.kt similarity index 87% rename from app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceItem.kt index 8f776af32..de66151e4 100644 --- a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/design/MigrationSourceItem.kt @@ -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() { - override fun getLayoutRes() = R.layout.eh_source_item + override fun getLayoutRes() = R.layout.migration_source_item - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): MigrationSourceHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): 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>, + adapter: FlexibleAdapter>, holder: MigrationSourceHolder, position: Int, payloads: List? diff --git a/app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/DeactivatableViewPager.kt similarity index 77% rename from app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/DeactivatableViewPager.kt index 7c37eb4cd..4e4a4da5f 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/DeactivatableViewPager.kt @@ -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) diff --git a/app/src/main/java/exh/ui/migration/manga/process/MigratingManga.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigratingManga.kt similarity index 84% rename from app/src/main/java/exh/ui/migration/manga/process/MigratingManga.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigratingManga.kt index cd88de254..5303ccc09 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/MigratingManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigratingManga.kt @@ -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 } diff --git a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureAdapter.kt similarity index 79% rename from app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureAdapter.kt index 97d703bf4..a3f395522 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureAdapter.kt @@ -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, 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) diff --git a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureConfig.kt similarity index 87% rename from app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureConfig.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureConfig.kt index cc9d41fbe..69279858f 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureConfig.kt @@ -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 diff --git a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureController.kt similarity index 88% rename from app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureController.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureController.kt index e8b9c391a..776b57376 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcedureController.kt @@ -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? = 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) { - 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) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index 61288b643..92458b749 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -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 diff --git a/app/src/main/java/exh/ui/smartsearch/SmartSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/smartsearch/SmartSearchController.kt similarity index 93% rename from app/src/main/java/exh/ui/smartsearch/SmartSearchController.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/smartsearch/SmartSearchController.kt index d0616c600..085cd4264 100644 --- a/app/src/main/java/exh/ui/smartsearch/SmartSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/smartsearch/SmartSearchController.kt @@ -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(), 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 } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/DeferredField.kt b/app/src/main/java/eu/kanade/tachiyomi/util/DeferredField.kt new file mode 100644 index 000000000..90282c9da --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/DeferredField.kt @@ -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 { + + @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 + } +} diff --git a/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt b/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt index b8aec0370..f26cd7179 100644 --- a/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/base/FlatMetadata.kt @@ -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 diff --git a/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt index be71d09d4..7c558c557 100644 --- a/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/base/RaisedSearchMetadata.kt @@ -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 diff --git a/app/src/main/java/exh/search/SearchEngine.kt b/app/src/main/java/exh/search/SearchEngine.kt index 8299d6628..54d4aa50d 100755 --- a/app/src/main/java/exh/search/SearchEngine.kt +++ b/app/src/main/java/exh/search/SearchEngine.kt @@ -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 diff --git a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceHolder.kt b/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceHolder.kt deleted file mode 100644 index 6d9d8e79f..000000000 --- a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceHolder.kt +++ /dev/null @@ -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) : - 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 - } -} diff --git a/app/src/main/res/drawable/baseline_swap_calls_24.xml b/app/src/main/res/drawable/baseline_swap_calls_24.xml new file mode 100644 index 000000000..3dd70fd7b --- /dev/null +++ b/app/src/main/res/drawable/baseline_swap_calls_24.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/eh_migration_design.xml b/app/src/main/res/layout/migration_design_controller.xml similarity index 92% rename from app/src/main/res/layout/eh_migration_design.xml rename to app/src/main/res/layout/migration_design_controller.xml index 425afe082..c295339ba 100644 --- a/app/src/main/res/layout/eh_migration_design.xml +++ b/app/src/main/res/layout/migration_design_controller.xml @@ -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"> @@ -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" /> + app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" + android:focusable="true" /> + app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" + android:focusable="true" /> + app:layout_constraintStart_toEndOf="@+id/prioritize_chapter_count" + android:focusable="true" /> + app:layout_constraintStart_toStartOf="parent" + android:importantForAutofill="no" />