From 04638535d83e9473a4abb6d47bdd2f86675187fe Mon Sep 17 00:00:00 2001 From: inorichi Date: Thu, 30 Nov 2017 15:37:20 +0100 Subject: [PATCH 01/13] Fix library options menu shown in chapters screen. Resolves #1096 --- .../java/eu/kanade/tachiyomi/ui/library/LibraryController.kt | 3 ++- app/src/main/res/layout/manga_controller.xml | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 fce944223..eeb5fef6e 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 @@ -126,6 +126,7 @@ class LibraryController( init { setHasOptionsMenu(true) + retainViewMode = RetainViewMode.RETAIN_DETACH } override fun getTitle(): String? { @@ -425,7 +426,7 @@ class LibraryController( presenter.onOpenManga() router.pushController(RouterTransaction.with(MangaController(manga)) - .pushChangeHandler(FadeChangeHandler(false)) + .pushChangeHandler(FadeChangeHandler()) .popChangeHandler(FadeChangeHandler())) } diff --git a/app/src/main/res/layout/manga_controller.xml b/app/src/main/res/layout/manga_controller.xml index dcc74b37a..27421c4c5 100644 --- a/app/src/main/res/layout/manga_controller.xml +++ b/app/src/main/res/layout/manga_controller.xml @@ -3,5 +3,4 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/view_pager" android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="?android:attr/colorBackground" /> \ No newline at end of file + android:layout_height="match_parent" /> \ No newline at end of file From 182bf5f2bd06200915755bce37ea56e4ccd04348 Mon Sep 17 00:00:00 2001 From: inorichi Date: Sat, 2 Dec 2017 17:10:31 +0100 Subject: [PATCH 02/13] Add install packages permission. Fixes #1104 --- app/src/main/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 53d83cb31..74fcb5334 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + Date: Sat, 2 Dec 2017 17:29:05 +0100 Subject: [PATCH 03/13] Start downloader after a library update. It should help with some catalogue issues --- .../eu/kanade/tachiyomi/data/download/DownloadManager.kt | 5 +++-- .../java/eu/kanade/tachiyomi/data/download/Downloader.kt | 7 +++++-- .../kanade/tachiyomi/data/library/LibraryUpdateService.kt | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index de74f1e07..17efd8e95 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -85,9 +85,10 @@ class DownloadManager(context: Context) { * * @param manga the manga of the chapters. * @param chapters the list of chapters to enqueue. + * @param autoStart whether to start the downloader after enqueing the chapters. */ - fun downloadChapters(manga: Manga, chapters: List) { - downloader.queueChapters(manga, chapters) + fun downloadChapters(manga: Manga, chapters: List, autoStart: Boolean = true) { + downloader.queueChapters(manga, chapters, autoStart) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index cbcb2045c..88f32553b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -219,8 +219,9 @@ class Downloader(private val context: Context, * * @param manga the manga of the chapters to download. * @param chapters the list of chapters to download. + * @param autoStart whether to start the downloader after enqueing the chapters. */ - fun queueChapters(manga: Manga, chapters: List) = launchUI { + fun queueChapters(manga: Manga, chapters: List, autoStart: Boolean) = launchUI { val source = sourceManager.get(manga.source) as? HttpSource ?: return@launchUI // Called in background thread, the operation can be slow with SAF. @@ -261,7 +262,9 @@ class Downloader(private val context: Context, } // Start downloader if needed - DownloadService.start(this@Downloader.context) + if (autoStart) { + DownloadService.start(this@Downloader.context) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index 50a19ef4d..880764854 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -333,7 +333,9 @@ class LibraryUpdateService( val dbChapters = chapters.map { mangaChapters.find { mangaChapter -> mangaChapter.url == it.url }!! } - downloadManager.downloadChapters(manga, dbChapters) + // We don't want to start downloading while the library is updating, because websites + // may don't like it and they could ban the user. + downloadManager.downloadChapters(manga, dbChapters, false) } /** From 9ba8d88b077d7248e6352a6a63ddb2624c73920a Mon Sep 17 00:00:00 2001 From: inorichi Date: Sat, 2 Dec 2017 20:59:35 +0100 Subject: [PATCH 04/13] Dependency updates --- app/build.gradle | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8a54be230..84e54265a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,7 +106,7 @@ dependencies { implementation 'com.github.inorichi:junrar-android:634c1f5' // Android support library - final support_library_version = '27.0.1' + final support_library_version = '27.0.2' implementation "com.android.support:support-v4:$support_library_version" implementation "com.android.support:appcompat-v7:$support_library_version" implementation "com.android.support:cardview-v7:$support_library_version" @@ -118,7 +118,7 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.0.2' - implementation 'com.android.support:multidex:1.0.1' + implementation 'com.android.support:multidex:1.0.2' // ReactiveX implementation 'io.reactivex:rxandroid:1.2.1' @@ -149,14 +149,14 @@ dependencies { // Disk implementation 'com.jakewharton:disklrucache:2.0.2' - implementation 'com.github.seven332:unifile:1.0.0' + implementation 'com.github.inorichi:unifile:e9ee588' // HTML parser implementation 'org.jsoup:jsoup:1.10.2' // Job scheduling - implementation 'com.evernote:android-job:1.2.0' - implementation 'com.google.android.gms:play-services-gcm:11.6.0' + implementation 'com.evernote:android-job:1.2.1' + implementation 'com.google.android.gms:play-services-gcm:11.6.2' // Changelog implementation 'com.github.gabrielemariotti.changeloglib:changelog:2.1.0' @@ -232,7 +232,7 @@ dependencies { } buildscript { - ext.kotlin_version = '1.1.61' + ext.kotlin_version = '1.2.0' repositories { mavenCentral() } From d690d6e0e36738780466edc228f60c4c3fc055db Mon Sep 17 00:00:00 2001 From: inorichi Date: Sun, 3 Dec 2017 01:03:15 +0100 Subject: [PATCH 05/13] Use synthetic view's new caching method --- app/build.gradle | 3 + .../ui/base/controller/BaseController.kt | 28 ++++-- .../ui/base/controller/ConductorExtensions.kt | 10 ++- .../ui/base/controller/RxController.kt | 2 +- .../ui/catalogue/CatalogueController.kt | 39 ++++---- .../CatalogueSearchController.kt | 22 ++--- .../catalogue/main/CatalogueMainController.kt | 27 +++--- .../ui/category/CategoryController.kt | 28 +++--- .../ui/download/DownloadController.kt | 27 +++--- .../tachiyomi/ui/library/LibraryController.kt | 82 +++++++---------- .../kanade/tachiyomi/ui/main/MainActivity.kt | 21 ++--- .../tachiyomi/ui/manga/MangaController.kt | 22 ++--- .../ui/manga/chapter/ChaptersController.kt | 71 +++++++-------- .../ui/manga/info/MangaInfoController.kt | 90 +++++++++---------- .../ui/manga/track/TrackController.kt | 21 +++-- .../RecentChaptersController.kt | 66 ++++++-------- .../recently_read/RecentlyReadController.kt | 34 +++---- .../ui/setting/SettingsMainController.kt | 7 +- 18 files changed, 274 insertions(+), 326 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 84e54265a..2ebfe6b7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -250,3 +250,6 @@ kotlin { coroutines 'enable' } } +androidExtensions { + experimental = true +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt index 8ab55d9f7..6e9f64f28 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt @@ -6,21 +6,39 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.RestoreViewOnCreateController +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.* -abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle) { +abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle), + LayoutContainer { + + init { + addLifecycleListener(object : LifecycleListener() { + override fun postCreateView(controller: Controller, view: View) { + onViewCreated(view) + } + }) + } + + override val containerView: View? + get() = view override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View { - val view = inflateView(inflater, container) - onViewCreated(view, savedViewState) - return view + return inflateView(inflater, container) + } + + override fun onDestroyView(view: View) { + super.onDestroyView(view) + clearFindViewByIdCache() } abstract fun inflateView(inflater: LayoutInflater, container: ViewGroup): View - open fun onViewCreated(view: View, savedViewState: Bundle?) { } + open fun onViewCreated(view: View) { } override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { if (type.isEnter) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt index 19150e132..492c5a280 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt @@ -5,6 +5,8 @@ import android.os.Build import android.support.v4.content.ContextCompat import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Router +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.FadeChangeHandler fun Router.popControllerWithTag(tag: String): Boolean { val controller = getControllerWithTag(tag) @@ -24,4 +26,10 @@ fun Controller.requestPermissionsSafe(permissions: Array, requestCode: I } } } -} \ No newline at end of file +} + +fun Controller.withFadeTransaction(): RouterTransaction { + return RouterTransaction.with(this) + .pushChangeHandler(FadeChangeHandler()) + .popChangeHandler(FadeChangeHandler()) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt index 80d3b31d4..2b2e1ecc6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt @@ -30,7 +30,7 @@ abstract class RxController(bundle: Bundle? = null) : BaseController(bundle) { } @CallSuper - override fun onViewCreated(view: View, savedViewState: Bundle?) { + override fun onViewCreated(view: View) { if (untilDestroySubscriptions.isUnsubscribed) { untilDestroySubscriptions = CompositeSubscription() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt index 2df28474e..cf6c3d6f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt @@ -7,8 +7,6 @@ import android.support.v4.widget.DrawerLayout import android.support.v7.widget.* import android.view.* import com.afollestad.materialdialogs.MaterialDialog -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import com.f2prateek.rx.preferences.Preference import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents import eu.davidea.flexibleadapter.FlexibleAdapter @@ -21,12 +19,13 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.* import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener -import kotlinx.android.synthetic.main.catalogue_controller.view.* +import kotlinx.android.synthetic.main.catalogue_controller.* import kotlinx.android.synthetic.main.main_activity.* import rx.Observable import rx.Subscription @@ -112,8 +111,8 @@ open class CatalogueController(bundle: Bundle) : return inflater.inflate(R.layout.catalogue_controller, container, false) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) // Initialize adapter, scroll listener and recycler views adapter = FlexibleAdapter(null, this) @@ -121,11 +120,10 @@ open class CatalogueController(bundle: Bundle) : navView?.setFilters(presenter.filterItems) - view.progress?.visible() + progress?.visible() } override fun onDestroyView(view: View) { - super.onDestroyView(view) numColumnsSubscription?.unsubscribe() numColumnsSubscription = null searchViewSubscription?.unsubscribe() @@ -133,6 +131,7 @@ open class CatalogueController(bundle: Bundle) : adapter = null snack = null recycler = null + super.onDestroyView(view) } override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { @@ -172,12 +171,12 @@ open class CatalogueController(bundle: Bundle) : numColumnsSubscription?.unsubscribe() var oldPosition = RecyclerView.NO_POSITION - val oldRecycler = view.catalogue_view?.getChildAt(1) + val oldRecycler = catalogue_view?.getChildAt(1) if (oldRecycler is RecyclerView) { oldPosition = (oldRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() oldRecycler.adapter = null - view.catalogue_view?.removeView(oldRecycler) + catalogue_view?.removeView(oldRecycler) } val recycler = if (presenter.isListMode) { @@ -187,7 +186,7 @@ open class CatalogueController(bundle: Bundle) : addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) } } else { - (view.catalogue_view.inflate(R.layout.catalogue_recycler_autofit) as AutofitRecyclerView).apply { + (catalogue_view.inflate(R.layout.catalogue_recycler_autofit) as AutofitRecyclerView).apply { numColumnsSubscription = getColumnsPreferenceForCurrentOrientation().asObservable() .doOnNext { spanCount = it } .skip(1) @@ -207,7 +206,7 @@ open class CatalogueController(bundle: Bundle) : recycler.setHasFixedSize(true) recycler.adapter = adapter - view.catalogue_view.addView(recycler, 1) + catalogue_view.addView(recycler, 1) if (oldPosition != RecyclerView.NO_POSITION) { recycler.layoutManager.scrollToPosition(oldPosition) @@ -330,7 +329,7 @@ open class CatalogueController(bundle: Bundle) : val message = if (error is NoResultsException) "No results found" else (error.message ?: "") snack?.dismiss() - snack = view?.catalogue_view?.snack(message, Snackbar.LENGTH_INDEFINITE) { + snack = catalogue_view?.snack(message, Snackbar.LENGTH_INDEFINITE) { setAction(R.string.action_retry) { // If not the first page, show bottom progress bar. if (adapter.mainItemCount > 0) { @@ -357,7 +356,6 @@ open class CatalogueController(bundle: Bundle) : * Called by the adapter when scrolled near the bottom. */ override fun onLoadMore(lastPosition: Int, currentPage: Int) { - Timber.e("onLoadMore") if (presenter.hasNextPage()) { presenter.requestNext() } else { @@ -391,7 +389,7 @@ open class CatalogueController(bundle: Bundle) : setupRecycler(view) if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) { // Initialize mangas if going to grid view or if over wifi when going to list view - val mangas = (0..adapter.itemCount-1).mapNotNull { + val mangas = (0 until adapter.itemCount).mapNotNull { (adapter.getItem(it) as? CatalogueItem)?.manga } presenter.initializeMangas(mangas) @@ -433,7 +431,7 @@ open class CatalogueController(bundle: Bundle) : * Shows the progress bar. */ private fun showProgressBar() { - view?.progress?.visible() + progress?.visible() snack?.dismiss() snack = null } @@ -442,7 +440,7 @@ open class CatalogueController(bundle: Bundle) : * Hides active progress bars. */ private fun hideProgressBar() { - view?.progress?.gone() + progress?.gone() } /** @@ -453,9 +451,7 @@ open class CatalogueController(bundle: Bundle) : */ override fun onItemClick(position: Int): Boolean { val item = adapter?.getItem(position) as? CatalogueItem ?: return false - router.pushController(RouterTransaction.with(MangaController(item.manga, true)) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(MangaController(item.manga, true).withFadeTransaction()) return false } @@ -470,10 +466,11 @@ open class CatalogueController(bundle: Bundle) : * @param position the position of the element clicked. */ override fun onItemLongClick(position: Int) { + val activity = activity ?: return val manga = (adapter?.getItem(position) as? CatalogueItem?)?.manga ?: return if (manga.favorite) { - MaterialDialog.Builder(activity!!) - .items(resources?.getString(R.string.remove_from_library)) + MaterialDialog.Builder(activity) + .items(activity.getString(R.string.remove_from_library)) .itemsCallback { _, _, which, _ -> when (which) { 0 -> { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt index dcbb42f4c..dec1dcc8d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt @@ -4,15 +4,14 @@ import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.SearchView import android.view.* -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.manga.MangaController -import kotlinx.android.synthetic.main.catalogue_global_search_controller.view.* +import kotlinx.android.synthetic.main.catalogue_global_search_controller.* /** * This controller shows and manages the different search result in global search. @@ -71,9 +70,7 @@ class CatalogueSearchController(private val initialQuery: String? = null) : */ override fun onMangaClick(manga: Manga) { // Open MangaController. - router.pushController(RouterTransaction.with(MangaController(manga, true)) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(MangaController(manga, true).withFadeTransaction()) } /** @@ -115,18 +112,15 @@ class CatalogueSearchController(private val initialQuery: String? = null) : * Called when the view is created * * @param view view of controller - * @param savedViewState information from previous state. */ - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) adapter = CatalogueSearchAdapter(this) - with(view) { - // Create recycler and set adapter. - recycler.layoutManager = LinearLayoutManager(context) - recycler.adapter = adapter - } + // Create recycler and set adapter. + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.adapter = adapter } override fun onDestroyView(view: View) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt index 76ea5e513..f6aff8ab7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.main -import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.SearchView import android.view.* @@ -16,12 +15,13 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.online.LoginSource import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.catalogue.CatalogueController import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController import eu.kanade.tachiyomi.ui.latest_updates.LatestUpdatesController import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog -import kotlinx.android.synthetic.main.catalogue_main_controller.view.* +import kotlinx.android.synthetic.main.catalogue_main_controller.* import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -89,19 +89,16 @@ class CatalogueMainController : NucleusController(), * Called when the view is created * * @param view view of controller - * @param savedViewState information from previous state. */ - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) adapter = CatalogueMainAdapter(this) - with(view) { - // Create recycler and set adapter. - recycler.layoutManager = LinearLayoutManager(context) - recycler.adapter = adapter - recycler.addItemDecoration(SourceDividerItemDecoration(context)) - } + // Create recycler and set adapter. + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.adapter = adapter + recycler.addItemDecoration(SourceDividerItemDecoration(view.context)) } override fun onDestroyView(view: View) { @@ -165,9 +162,7 @@ class CatalogueMainController : NucleusController(), */ private fun openCatalogue(source: CatalogueSource, controller: CatalogueController) { preferences.lastUsedCatalogueSource().set(source.id) - router.pushController(RouterTransaction.with(controller) - .popChangeHandler(FadeChangeHandler()) - .pushChangeHandler(FadeChangeHandler())) + router.pushController(controller.withFadeTransaction()) } /** @@ -192,9 +187,7 @@ class CatalogueMainController : NucleusController(), .filter { it.isSubmitted } .subscribeUntilDestroy { val query = it.queryText().toString() - router.pushController((RouterTransaction.with(CatalogueSearchController(query))) - .popChangeHandler(FadeChangeHandler()) - .pushChangeHandler(FadeChangeHandler())) + router.pushController(CatalogueSearchController(query).withFadeTransaction()) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt index 87df04333..8f7f055b0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.category -import android.os.Bundle import android.support.design.widget.Snackbar import android.support.v7.app.AppCompatActivity import android.support.v7.view.ActionMode @@ -15,7 +14,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.categories_controller.view.* +import kotlinx.android.synthetic.main.categories_controller.* /** * Controller to manage the categories for the users' library. @@ -70,22 +69,19 @@ class CategoryController : NucleusController(), * Called after view inflation. Used to initialize the view. * * @param view The view of this controller. - * @param savedViewState The saved state of the view. */ - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) - with(view) { - adapter = CategoryAdapter(this@CategoryController) - recycler.layoutManager = LinearLayoutManager(context) - recycler.setHasFixedSize(true) - recycler.adapter = adapter - adapter?.isHandleDragEnabled = true - adapter?.isPermanentDelete = false + adapter = CategoryAdapter(this@CategoryController) + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.setHasFixedSize(true) + recycler.adapter = adapter + adapter?.isHandleDragEnabled = true + adapter?.isPermanentDelete = false - fab.clicks().subscribeUntilDestroy { - CategoryCreateDialog(this@CategoryController).showDialog(router, null) - } + fab.clicks().subscribeUntilDestroy { + CategoryCreateDialog(this@CategoryController).showDialog(router, null) } } @@ -95,12 +91,12 @@ class CategoryController : NucleusController(), * @param view The view of this controller. */ override fun onDestroyView(view: View) { - super.onDestroyView(view) // Manually call callback to delete categories if required undoHelper?.onDeleteConfirmed(Snackbar.Callback.DISMISS_EVENT_MANUAL) undoHelper = null actionMode = null adapter = null + super.onDestroyView(view) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt index af08faffc..9252374f1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.download -import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.view.* import eu.kanade.tachiyomi.R @@ -8,7 +7,7 @@ import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import kotlinx.android.synthetic.main.download_controller.view.* +import kotlinx.android.synthetic.main.download_controller.* import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -52,21 +51,19 @@ class DownloadController : NucleusController() { return resources?.getString(R.string.label_download_queue) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) // Check if download queue is empty and update information accordingly. setInformationView() // Initialize adapter. adapter = DownloadAdapter() - with(view) { - recycler.adapter = adapter + recycler.adapter = adapter - // Set the layout manager for the recycler and fixed size. - recycler.layoutManager = LinearLayoutManager(context) - recycler.setHasFixedSize(true) - } + // Set the layout manager for the recycler and fixed size. + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.setHasFixedSize(true) // Suscribe to changes DownloadService.runningRelay @@ -83,12 +80,12 @@ class DownloadController : NucleusController() { } override fun onDestroyView(view: View) { - super.onDestroyView(view) for (subscription in progressSubscriptions.values) { subscription.unsubscribe() } progressSubscriptions.clear() adapter = null + super.onDestroyView(view) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -232,20 +229,18 @@ class DownloadController : NucleusController() { * @return the holder of the download or null if it's not bound. */ private fun getHolder(download: Download): DownloadHolder? { - val recycler = view?.recycler ?: return null - return recycler.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder + return recycler?.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder } /** * Set information view when queue is empty */ private fun setInformationView() { - val emptyView = view?.empty_view ?: return if (presenter.downloadQueue.isEmpty()) { - emptyView.show(R.drawable.ic_file_download_black_128dp, + empty_view?.show(R.drawable.ic_file_download_black_128dp, R.string.information_no_downloads) } else { - emptyView.hide() + empty_view?.hide() } } 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 eeb5fef6e..2b65b0d23 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 @@ -14,8 +14,6 @@ import android.support.v7.widget.SearchView import android.view.* import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import com.f2prateek.rx.preferences.Preference import com.jakewharton.rxbinding.support.v4.view.pageSelections import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges @@ -30,13 +28,14 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController import eu.kanade.tachiyomi.ui.base.controller.TabbedController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.category.CategoryController import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.inflate import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener -import kotlinx.android.synthetic.main.library_controller.view.* +import kotlinx.android.synthetic.main.library_controller.* import kotlinx.android.synthetic.main.main_activity.* import rx.Subscription import timber.log.Timber @@ -100,14 +99,8 @@ class LibraryController( private set /** - * TabLayout of the categories. + * Adapter of the view pager. */ - private val tabs: TabLayout? - get() = activity?.tabs - - private val drawer: DrawerLayout? - get() = activity?.drawer - private var adapter: LibraryAdapter? = null /** @@ -141,43 +134,41 @@ class LibraryController( return inflater.inflate(R.layout.library_controller, container, false) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) adapter = LibraryAdapter(this) - with(view) { - view_pager.adapter = adapter - view_pager.pageSelections().skip(1).subscribeUntilDestroy { - preferences.lastUsedCategory().set(it) - activeCategory = it - } + view_pager.adapter = adapter + view_pager.pageSelections().skip(1).subscribeUntilDestroy { + preferences.lastUsedCategory().set(it) + activeCategory = it + } - getColumnsPreferenceForCurrentOrientation().asObservable() - .doOnNext { mangaPerRow = it } - .skip(1) - // Set again the adapter to recalculate the covers height - .subscribeUntilDestroy { reattachAdapter() } + getColumnsPreferenceForCurrentOrientation().asObservable() + .doOnNext { mangaPerRow = it } + .skip(1) + // Set again the adapter to recalculate the covers height + .subscribeUntilDestroy { reattachAdapter() } - if (selectedMangas.isNotEmpty()) { - createActionModeIfNeeded() - } + if (selectedMangas.isNotEmpty()) { + createActionModeIfNeeded() } } override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) if (type.isEnter) { - activity?.tabs?.setupWithViewPager(view?.view_pager) + activity?.tabs?.setupWithViewPager(view_pager) presenter.subscribeLibrary() } } override fun onDestroyView(view: View) { - super.onDestroyView(view) adapter = null actionMode = null tabsVisibilitySubscription?.unsubscribe() tabsVisibilitySubscription = null + super.onDestroyView(view) } override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup { @@ -233,14 +224,14 @@ class LibraryController( // Show empty view if needed if (mangaMap.isNotEmpty()) { - view.empty_view.hide() + empty_view.hide() } else { - view.empty_view.show(R.drawable.ic_book_black_128dp, R.string.information_empty_library) + empty_view.show(R.drawable.ic_book_black_128dp, R.string.information_empty_library) } // Get the current active category. val activeCat = if (adapter.categories.isNotEmpty()) - view.view_pager.currentItem + view_pager.currentItem else activeCategory @@ -248,14 +239,14 @@ class LibraryController( adapter.categories = categories // Restore active category. - view.view_pager.setCurrentItem(activeCat, false) + view_pager.setCurrentItem(activeCat, false) tabsVisibilityRelay.call(categories.size > 1) // Delay the scroll position to allow the view to be properly measured. view.post { if (isAttached) { - tabs?.setScrollPosition(view.view_pager.currentItem, 0f, true) + activity?.tabs?.setScrollPosition(view_pager.currentItem, 0f, true) } } @@ -298,14 +289,13 @@ class LibraryController( * Reattaches the adapter to the view pager to recreate fragments */ private fun reattachAdapter() { - val pager = view?.view_pager ?: return val adapter = adapter ?: return - val position = pager.currentItem + val position = view_pager.currentItem adapter.recycle = false - pager.adapter = adapter - pager.currentItem = position + view_pager.adapter = adapter + view_pager.currentItem = position adapter.recycle = true } @@ -331,7 +321,7 @@ class LibraryController( val searchItem = menu.findItem(R.id.action_search) val searchView = searchItem.actionView as SearchView - if (!query.isNullOrEmpty()) { + if (!query.isEmpty()) { searchItem.expandActionView() searchView.setQuery(query, true) searchView.clearFocus() @@ -361,15 +351,13 @@ class LibraryController( override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_filter -> { - navView?.let { drawer?.openDrawer(Gravity.END) } + navView?.let { activity?.drawer?.openDrawer(Gravity.END) } } R.id.action_update_library -> { activity?.let { LibraryUpdateService.start(it) } } R.id.action_edit_categories -> { - router.pushController(RouterTransaction.with(CategoryController()) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(CategoryController().withFadeTransaction()) } else -> return super.onOptionsItemSelected(item) } @@ -425,9 +413,7 @@ class LibraryController( // Notify the presenter a manga is being opened. presenter.onOpenManga() - router.pushController(RouterTransaction.with(MangaController(manga)) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(MangaController(manga).withFadeTransaction()) } /** @@ -462,11 +448,11 @@ class LibraryController( .toTypedArray() ChangeMangaCategoriesDialog(this, mangas, categories, commonCategoriesIndexes) - .showDialog(router, null) + .showDialog(router) } private fun showDeleteMangaDialog() { - DeleteLibraryMangasDialog(this, selectedMangas.toList()).showDialog(router, null) + DeleteLibraryMangasDialog(this, selectedMangas.toList()).showDialog(router) } override fun updateCategoriesForMangas(mangas: List, categories: List) { @@ -481,8 +467,6 @@ class LibraryController( /** * Changes the cover for the selected manga. - * - * @param mangas a list of selected manga. */ private fun changeSelectedCover() { val manga = selectedMangas.firstOrNull() ?: return diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 9815477d1..77b4cc016 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -14,10 +14,7 @@ import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.activity.BaseActivity -import eu.kanade.tachiyomi.ui.base.controller.DialogController -import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController -import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController -import eu.kanade.tachiyomi.ui.base.controller.TabbedController +import eu.kanade.tachiyomi.ui.base.controller.* import eu.kanade.tachiyomi.ui.catalogue.main.CatalogueMainController import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.library.LibraryController @@ -85,14 +82,11 @@ class MainActivity : BaseActivity() { R.id.nav_drawer_recently_read -> setRoot(RecentlyReadController(), id) R.id.nav_drawer_catalogues -> setRoot(CatalogueMainController(), id) R.id.nav_drawer_downloads -> { - router.pushController(RouterTransaction.with(DownloadController()) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(DownloadController().withFadeTransaction()) + } + R.id.nav_drawer_settings -> { + router.pushController(SettingsMainController().withFadeTransaction()) } - R.id.nav_drawer_settings -> - router.pushController(RouterTransaction.with(SettingsMainController()) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) } } drawer.closeDrawer(GravityCompat.START) @@ -189,10 +183,7 @@ class MainActivity : BaseActivity() { } private fun setRoot(controller: Controller, id: Int) { - router.setRoot(RouterTransaction.with(controller) - .popChangeHandler(FadeChangeHandler()) - .pushChangeHandler(FadeChangeHandler()) - .tag(id.toString())) + router.setRoot(controller.withFadeTransaction().tag(id.toString())) } private fun syncActivityViewWithController(to: Controller?, from: Controller? = null) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 562874bee..467848aac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -1,8 +1,6 @@ package eu.kanade.tachiyomi.ui.manga -import android.Manifest.permission.READ_EXTERNAL_STORAGE import android.Manifest.permission.WRITE_EXTERNAL_STORAGE -import android.os.Build import android.os.Bundle import android.support.design.widget.TabLayout import android.support.graphics.drawable.VectorDrawableCompat @@ -32,7 +30,7 @@ import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController import eu.kanade.tachiyomi.ui.manga.track.TrackController import eu.kanade.tachiyomi.util.toast import kotlinx.android.synthetic.main.main_activity.* -import kotlinx.android.synthetic.main.manga_controller.view.* +import kotlinx.android.synthetic.main.manga_controller.* import rx.Subscription import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -81,21 +79,19 @@ class MangaController : RxController, TabbedController { return inflater.inflate(R.layout.manga_controller, container, false) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) if (manga == null || source == null) return requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) - with(view) { - adapter = MangaDetailAdapter() - view_pager.offscreenPageLimit = 3 - view_pager.adapter = adapter + adapter = MangaDetailAdapter() + view_pager.offscreenPageLimit = 3 + view_pager.adapter = adapter - if (!fromCatalogue) - view_pager.currentItem = CHAPTERS_CONTROLLER - } + if (!fromCatalogue) + view_pager.currentItem = CHAPTERS_CONTROLLER } override fun onDestroyView(view: View) { @@ -106,7 +102,7 @@ class MangaController : RxController, TabbedController { override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) if (type.isEnter) { - activity?.tabs?.setupWithViewPager(view?.view_pager) + activity?.tabs?.setupWithViewPager(view_pager) trackingIconSubscription = trackingIconRelay.subscribe { setTrackingIconInternal(it) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt index 630096512..b58073d54 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt @@ -4,7 +4,6 @@ import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.app.Activity import android.content.Intent -import android.os.Bundle import android.support.design.widget.Snackbar import android.support.v7.app.AppCompatActivity import android.support.v7.view.ActionMode @@ -26,7 +25,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.getCoordinates import eu.kanade.tachiyomi.util.snack import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.chapters_controller.view.* +import kotlinx.android.synthetic.main.chapters_controller.* import timber.log.Timber class ChaptersController : NucleusController(), @@ -69,57 +68,55 @@ class ChaptersController : NucleusController(), return inflater.inflate(R.layout.chapters_controller, container, false) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) // Init RecyclerView and adapter adapter = ChaptersAdapter(this, view.context) - with(view) { - recycler.adapter = adapter - recycler.layoutManager = LinearLayoutManager(context) - recycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) - recycler.setHasFixedSize(true) - adapter?.fastScroller = view.fast_scroller + recycler.adapter = adapter + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) + recycler.setHasFixedSize(true) + adapter?.fastScroller = fast_scroller - swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() } + swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() } - fab.clicks().subscribeUntilDestroy { - val item = presenter.getNextUnreadChapter() - if (item != null) { - // Create animation listener - val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator?) { - openChapter(item.chapter, true) - } + fab.clicks().subscribeUntilDestroy { + val item = presenter.getNextUnreadChapter() + if (item != null) { + // Create animation listener + val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator?) { + openChapter(item.chapter, true) } - - // Get coordinates and start animation - val coordinates = fab.getCoordinates() - if (!reveal_view.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) { - openChapter(item.chapter) - } - } else { - context.toast(R.string.no_next_chapter) } + + // Get coordinates and start animation + val coordinates = fab.getCoordinates() + if (!reveal_view.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) { + openChapter(item.chapter) + } + } else { + view.context.toast(R.string.no_next_chapter) } } } override fun onDestroyView(view: View) { - super.onDestroyView(view) adapter = null actionMode = null + super.onDestroyView(view) } override fun onActivityResumed(activity: Activity) { - val view = view ?: return + if (view == null) return // Check if animation view is visible - if (view.reveal_view.visibility == View.VISIBLE) { + if (reveal_view.visibility == View.VISIBLE) { // Show the unReveal effect - val coordinates = view.fab.getCoordinates() - view.reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920) + val coordinates = fab.getCoordinates() + reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920) } super.onActivityResumed(activity) } @@ -213,16 +210,16 @@ class ChaptersController : NucleusController(), } fun fetchChaptersFromSource() { - view?.swipe_refresh?.isRefreshing = true + swipe_refresh?.isRefreshing = true presenter.fetchChaptersFromSource() } fun onFetchChaptersDone() { - view?.swipe_refresh?.isRefreshing = false + swipe_refresh?.isRefreshing = false } fun onFetchChaptersError(error: Throwable) { - view?.swipe_refresh?.isRefreshing = false + swipe_refresh?.isRefreshing = false activity?.toast(error.message) } @@ -231,7 +228,7 @@ class ChaptersController : NucleusController(), } private fun getHolder(chapter: Chapter): ChapterHolder? { - return view?.recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder + return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder } fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) { @@ -365,7 +362,7 @@ class ChaptersController : NucleusController(), destroyActionModeIfNeeded() presenter.downloadChapters(chapters) if (view != null && !presenter.manga.favorite) { - view.recycler?.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) { + recycler?.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) { setAction(R.string.action_add) { presenter.addToLibrary() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index faf0132a3..95a0f8756 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -39,7 +39,7 @@ import eu.kanade.tachiyomi.util.snack import eu.kanade.tachiyomi.util.toast import jp.wasabeef.glide.transformations.CropSquareTransformation import jp.wasabeef.glide.transformations.MaskTransformation -import kotlinx.android.synthetic.main.manga_info_controller.view.* +import kotlinx.android.synthetic.main.manga_info_controller.* import uy.kohesive.injekt.injectLazy import java.text.DecimalFormat @@ -71,17 +71,14 @@ class MangaInfoController : NucleusController(), return inflater.inflate(R.layout.manga_info_controller, container, false) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) - with(view) { - // Set onclickListener to toggle favorite when FAB clicked. - fab_favorite.clicks().subscribeUntilDestroy { onFabClick() } - - // Set SwipeRefresh to refresh manga data. - swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() } - } + // Set onclickListener to toggle favorite when FAB clicked. + fab_favorite.clicks().subscribeUntilDestroy { onFabClick() } + // Set SwipeRefresh to refresh manga data. + swipe_refresh.refreshes().subscribeUntilDestroy { fetchMangaFromSource() } } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -124,50 +121,49 @@ class MangaInfoController : NucleusController(), */ private fun setMangaInfo(manga: Manga, source: Source?) { val view = view ?: return - with(view) { - // Update artist TextView. - manga_artist.text = manga.artist - // Update author TextView. - manga_author.text = manga.author + // Update artist TextView. + manga_artist.text = manga.artist - // If manga source is known update source TextView. - if (source != null) { - manga_source.text = source.toString() - } + // Update author TextView. + manga_author.text = manga.author - // Update genres TextView. - manga_genres.text = manga.genre + // If manga source is known update source TextView. + if (source != null) { + manga_source.text = source.toString() + } - // Update status TextView. - manga_status.setText(when (manga.status) { - SManga.ONGOING -> R.string.ongoing - SManga.COMPLETED -> R.string.completed - SManga.LICENSED -> R.string.licensed - else -> R.string.unknown - }) + // Update genres TextView. + manga_genres.text = manga.genre - // Update description TextView. - manga_summary.text = manga.description + // Update status TextView. + manga_status.setText(when (manga.status) { + SManga.ONGOING -> R.string.ongoing + SManga.COMPLETED -> R.string.completed + SManga.LICENSED -> R.string.licensed + else -> R.string.unknown + }) - // Set the favorite drawable to the correct one. - setFavoriteDrawable(manga.favorite) + // Update description TextView. + manga_summary.text = manga.description - // Set cover if it wasn't already. - if (manga_cover.drawable == null && !manga.thumbnail_url.isNullOrEmpty()) { - GlideApp.with(context) + // Set the favorite drawable to the correct one. + setFavoriteDrawable(manga.favorite) + + // Set cover if it wasn't already. + if (manga_cover.drawable == null && !manga.thumbnail_url.isNullOrEmpty()) { + GlideApp.with(view.context) + .load(manga) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .centerCrop() + .into(manga_cover) + + if (backdrop != null) { + GlideApp.with(view.context) .load(manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() - .into(manga_cover) - - if (backdrop != null) { - GlideApp.with(context) - .load(manga) - .diskCacheStrategy(DiskCacheStrategy.RESOURCE) - .centerCrop() - .into(backdrop) - } + .into(backdrop) } } } @@ -178,7 +174,7 @@ class MangaInfoController : NucleusController(), * @param count number of chapters. */ fun setChapterCount(count: Float) { - view?.manga_chapters?.text = DecimalFormat("#.#").format(count) + manga_chapters?.text = DecimalFormat("#.#").format(count) } /** @@ -243,7 +239,7 @@ class MangaInfoController : NucleusController(), private fun setFavoriteDrawable(isFavorite: Boolean) { // Set the Favorite drawable to the correct one. // Border drawable if false, filled drawable if true. - view?.fab_favorite?.setImageResource(if (isFavorite) + fab_favorite?.setImageResource(if (isFavorite) R.drawable.ic_bookmark_white_24dp else R.drawable.ic_bookmark_border_white_24dp) @@ -279,7 +275,7 @@ class MangaInfoController : NucleusController(), * @param value whether it should be refreshing or not. */ private fun setRefreshing(value: Boolean) { - view?.swipe_refresh?.isRefreshing = value + swipe_refresh?.isRefreshing = value } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt index 363c0fe78..64e7bdd18 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.manga.track -import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View @@ -11,7 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.track_controller.view.* +import kotlinx.android.synthetic.main.track_controller.* class TrackController : NucleusController(), TrackAdapter.OnRowClickListener, @@ -35,8 +34,8 @@ class TrackController : NucleusController(), return inflater.inflate(R.layout.track_controller, container, false) } - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) adapter = TrackAdapter(this) with(view) { @@ -48,14 +47,14 @@ class TrackController : NucleusController(), } override fun onDestroyView(view: View) { - super.onDestroyView(view) adapter = null + super.onDestroyView(view) } fun onNextTrackings(trackings: List) { val atLeastOneLink = trackings.any { it.track != null } adapter?.items = trackings - view?.swipe_refresh?.isEnabled = atLeastOneLink + swipe_refresh?.isEnabled = atLeastOneLink (parentController as? MangaController)?.setTrackingIcon(atLeastOneLink) } @@ -73,11 +72,11 @@ class TrackController : NucleusController(), } fun onRefreshDone() { - view?.swipe_refresh?.isRefreshing = false + swipe_refresh?.isRefreshing = false } fun onRefreshError(error: Throwable) { - view?.swipe_refresh?.isRefreshing = false + swipe_refresh?.isRefreshing = false activity?.toast(error.message) } @@ -109,17 +108,17 @@ class TrackController : NucleusController(), override fun setStatus(item: TrackItem, selection: Int) { presenter.setStatus(item, selection) - view?.swipe_refresh?.isRefreshing = true + swipe_refresh?.isRefreshing = true } override fun setScore(item: TrackItem, score: Int) { presenter.setScore(item, score) - view?.swipe_refresh?.isRefreshing = true + swipe_refresh?.isRefreshing = true } override fun setChaptersRead(item: TrackItem, chaptersRead: Int) { presenter.setLastChapterRead(item, chaptersRead) - view?.swipe_refresh?.isRefreshing = true + swipe_refresh?.isRefreshing = true } private companion object { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt index b8b684481..1b0fef7db 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt @@ -1,13 +1,10 @@ package eu.kanade.tachiyomi.ui.recent_updates -import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.support.v7.view.ActionMode import android.support.v7.widget.DividerItemDecoration import android.support.v7.widget.LinearLayoutManager import android.view.* -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import com.jakewharton.rxbinding.support.v4.widget.refreshes import com.jakewharton.rxbinding.support.v7.widget.scrollStateChanges import eu.davidea.flexibleadapter.FlexibleAdapter @@ -19,10 +16,11 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController 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 eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.recent_chapters_controller.view.* +import kotlinx.android.synthetic.main.recent_chapters_controller.* import timber.log.Timber /** @@ -65,42 +63,39 @@ class RecentChaptersController : NucleusController(), /** * Called when view is created * @param view created view - * @param savedViewState status of saved sate */ - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) - with(view) { - // Init RecyclerView and adapter - val layoutManager = LinearLayoutManager(context) - recycler.layoutManager = layoutManager - recycler.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) - recycler.setHasFixedSize(true) - adapter = RecentChaptersAdapter(this@RecentChaptersController) - recycler.adapter = adapter + // Init RecyclerView and adapter + val layoutManager = LinearLayoutManager(view.context) + recycler.layoutManager = layoutManager + recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) + recycler.setHasFixedSize(true) + adapter = RecentChaptersAdapter(this@RecentChaptersController) + recycler.adapter = adapter - recycler.scrollStateChanges().subscribeUntilDestroy { - // Disable swipe refresh when view is not at the top - val firstPos = layoutManager.findFirstCompletelyVisibleItemPosition() - swipe_refresh.isEnabled = firstPos <= 0 - } - - swipe_refresh.setDistanceToTriggerSync((2 * 64 * resources.displayMetrics.density).toInt()) - swipe_refresh.refreshes().subscribeUntilDestroy { - if (!LibraryUpdateService.isRunning(context)) { - LibraryUpdateService.start(context) - context.toast(R.string.action_update_library) - } - // It can be a very long operation, so we disable swipe refresh and show a toast. - swipe_refresh.isRefreshing = false + recycler.scrollStateChanges().subscribeUntilDestroy { + // Disable swipe refresh when view is not at the top + val firstPos = layoutManager.findFirstCompletelyVisibleItemPosition() + swipe_refresh.isEnabled = firstPos <= 0 + } + + swipe_refresh.setDistanceToTriggerSync((2 * 64 * view.resources.displayMetrics.density).toInt()) + swipe_refresh.refreshes().subscribeUntilDestroy { + if (!LibraryUpdateService.isRunning(view.context)) { + LibraryUpdateService.start(view.context) + view.context.toast(R.string.action_update_library) } + // It can be a very long operation, so we disable swipe refresh and show a toast. + swipe_refresh.isRefreshing = false } } override fun onDestroyView(view: View) { - super.onDestroyView(view) adapter = null actionMode = null + super.onDestroyView(view) } /** @@ -180,11 +175,10 @@ class RecentChaptersController : NucleusController(), } override fun onUpdateEmptyView(size: Int) { - val emptyView = view?.empty_view ?: return if (size > 0) { - emptyView.hide() + empty_view?.hide() } else { - emptyView.show(R.drawable.ic_update_black_128dp, R.string.information_no_recent) + empty_view?.show(R.drawable.ic_update_black_128dp, R.string.information_no_recent) } } @@ -201,7 +195,7 @@ class RecentChaptersController : NucleusController(), * @param download [Download] object containing download progress. */ private fun getHolder(download: Download): RecentChapterHolder? { - return view?.recycler?.findViewHolderForItemId(download.chapter.id!!) as? RecentChapterHolder + return recycler?.findViewHolderForItemId(download.chapter.id!!) as? RecentChapterHolder } /** @@ -260,9 +254,7 @@ class RecentChaptersController : NucleusController(), } fun openManga(chapter: RecentChapterItem) { - router.pushController(RouterTransaction.with(MangaController(chapter.manga)) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(MangaController(chapter.manga).withFadeTransaction()) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt index 5f0f98864..c094c39fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt @@ -1,21 +1,19 @@ package eu.kanade.tachiyomi.ui.recently_read -import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.History import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.recently_read_controller.view.* +import kotlinx.android.synthetic.main.recently_read_controller.* /** * Fragment that shows recently read manga. @@ -51,23 +49,20 @@ class RecentlyReadController : NucleusController(), * Called when view is created * * @param view created view - * @param savedViewState saved state of the view */ - override fun onViewCreated(view: View, savedViewState: Bundle?) { - super.onViewCreated(view, savedViewState) + override fun onViewCreated(view: View) { + super.onViewCreated(view) - with(view) { - // Initialize adapter - recycler.layoutManager = LinearLayoutManager(context) - adapter = RecentlyReadAdapter(this@RecentlyReadController) - recycler.setHasFixedSize(true) - recycler.adapter = adapter - } + // Initialize adapter + recycler.layoutManager = LinearLayoutManager(view.context) + adapter = RecentlyReadAdapter(this@RecentlyReadController) + recycler.setHasFixedSize(true) + recycler.adapter = adapter } override fun onDestroyView(view: View) { - super.onDestroyView(view) adapter = null + super.onDestroyView(view) } /** @@ -80,11 +75,10 @@ class RecentlyReadController : NucleusController(), } override fun onUpdateEmptyView(size: Int) { - val emptyView = view?.empty_view ?: return if (size > 0) { - emptyView.hide() + empty_view.hide() } else { - emptyView.show(R.drawable.ic_glasses_black_128dp, R.string.information_no_recent_manga) + empty_view.show(R.drawable.ic_glasses_black_128dp, R.string.information_no_recent_manga) } } @@ -108,9 +102,7 @@ class RecentlyReadController : NucleusController(), override fun onCoverClick(position: Int) { val manga = adapter?.getItem(position)?.mch?.manga ?: return - router.pushController(RouterTransaction.with(MangaController(manga)) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(MangaController(manga).withFadeTransaction()) } override fun removeHistory(manga: Manga, history: History, all: Boolean) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt index c548bb849..56cfa769d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt @@ -1,9 +1,8 @@ package eu.kanade.tachiyomi.ui.setting import android.support.v7.preference.PreferenceScreen -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.util.getResourceColor class SettingsMainController : SettingsController() { @@ -57,8 +56,6 @@ class SettingsMainController : SettingsController() { } private fun navigateTo(controller: SettingsController) { - router.pushController(RouterTransaction.with(controller) - .pushChangeHandler(FadeChangeHandler()) - .popChangeHandler(FadeChangeHandler())) + router.pushController(controller.withFadeTransaction()) } } \ No newline at end of file From 297fed6aef50b334d2de2867b84b4ca26fa5c387 Mon Sep 17 00:00:00 2001 From: inorichi Date: Sun, 3 Dec 2017 12:58:38 +0100 Subject: [PATCH 06/13] Repackage catalogue to match the UI --- ...ogueMainAdapter.kt => CatalogueAdapter.kt} | 10 +- .../ui/catalogue/CatalogueController.kt | 751 ++++++------------ .../ui/catalogue/CataloguePresenter.kt | 406 ++-------- .../ui/catalogue/{main => }/LangHolder.kt | 40 +- .../ui/catalogue/{main => }/LangItem.kt | 2 +- .../ui/catalogue/NoResultsException.kt | 3 - .../{main => }/SourceDividerItemDecoration.kt | 92 +-- .../ui/catalogue/{main => }/SourceHolder.kt | 4 +- .../ui/catalogue/{main => }/SourceItem.kt | 4 +- .../browse/BrowseCatalogueController.kt | 520 ++++++++++++ .../browse/BrowseCataloguePresenter.kt | 376 +++++++++ .../{ => browse}/CatalogueGridHolder.kt | 2 +- .../catalogue/{ => browse}/CatalogueHolder.kt | 2 +- .../catalogue/{ => browse}/CatalogueItem.kt | 2 +- .../{ => browse}/CatalogueListHolder.kt | 2 +- .../{ => browse}/CatalogueNavigationView.kt | 78 +- .../catalogue/{ => browse}/CataloguePager.kt | 2 +- .../ui/catalogue/browse/NoResultsException.kt | 3 + .../ui/catalogue/{ => browse}/Pager.kt | 2 +- .../ui/catalogue/{ => browse}/ProgressItem.kt | 2 +- .../global_search/CatalogueSearchPresenter.kt | 6 +- .../latest}/LatestUpdatesController.kt | 78 +- .../latest}/LatestUpdatesPager.kt | 4 +- .../latest/LatestUpdatesPresenter.kt | 16 + .../catalogue/main/CatalogueMainController.kt | 231 ------ .../catalogue/main/CatalogueMainPresenter.kt | 104 --- .../latest_updates/LatestUpdatesPresenter.kt | 16 - .../kanade/tachiyomi/ui/main/MainActivity.kt | 5 +- .../res/layout-land/manga_info_controller.xml | 2 +- .../main/res/layout/catalogue_controller.xml | 2 +- app/src/main/res/layout/catalogue_drawer.xml | 2 +- .../main/res/layout/manga_info_controller.xml | 2 +- 32 files changed, 1385 insertions(+), 1386 deletions(-) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{main/CatalogueMainAdapter.kt => CatalogueAdapter.kt} (76%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{main => }/LangHolder.kt (90%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{main => }/LangItem.kt (95%) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/NoResultsException.kt rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{main => }/SourceDividerItemDecoration.kt (94%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{main => }/SourceHolder.kt (96%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{main => }/SourceItem.kt (90%) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/CatalogueGridHolder.kt (97%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/CatalogueHolder.kt (95%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/CatalogueItem.kt (97%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/CatalogueListHolder.kt (97%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/CatalogueNavigationView.kt (93%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/CataloguePager.kt (95%) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/NoResultsException.kt rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/Pager.kt (94%) rename app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/{ => browse}/ProgressItem.kt (96%) rename app/src/main/java/eu/kanade/tachiyomi/ui/{latest_updates => catalogue/latest}/LatestUpdatesController.kt (69%) rename app/src/main/java/eu/kanade/tachiyomi/ui/{latest_updates => catalogue/latest}/LatestUpdatesPager.kt (85%) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPresenter.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainPresenter.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueAdapter.kt similarity index 76% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainAdapter.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueAdapter.kt index d2e15169c..1b1f09d9a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueAdapter.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue.main +package eu.kanade.tachiyomi.ui.catalogue import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible @@ -8,9 +8,9 @@ import eu.kanade.tachiyomi.util.getResourceColor /** * Adapter that holds the catalogue cards. * - * @param controller instance of [CatalogueMainController]. + * @param controller instance of [CatalogueController]. */ -class CatalogueMainAdapter(val controller: CatalogueMainController) : +class CatalogueAdapter(val controller: CatalogueController) : FlexibleAdapter>(null, controller, true) { val cardBackground = controller.activity!!.getResourceColor(R.attr.background_card) @@ -31,7 +31,7 @@ class CatalogueMainAdapter(val controller: CatalogueMainController) : /** * Listener which should be called when user clicks browse. - * Note: Should only be handled by [CatalogueMainController] + * Note: Should only be handled by [CatalogueController] */ interface OnBrowseClickListener { fun onBrowseClick(position: Int) @@ -39,7 +39,7 @@ class CatalogueMainAdapter(val controller: CatalogueMainController) : /** * Listener which should be called when user clicks latest. - * Note: Should only be handled by [CatalogueMainController] + * Note: Should only be handled by [CatalogueController] */ interface OnLatestClickListener { fun onLatestClick(position: Int) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt index cf6c3d6f7..59d0293b4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt @@ -1,520 +1,231 @@ -package eu.kanade.tachiyomi.ui.catalogue - -import android.content.res.Configuration -import android.os.Bundle -import android.support.design.widget.Snackbar -import android.support.v4.widget.DrawerLayout -import android.support.v7.widget.* -import android.view.* -import com.afollestad.materialdialogs.MaterialDialog -import com.f2prateek.rx.preferences.Preference -import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController -import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction -import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog -import eu.kanade.tachiyomi.ui.manga.MangaController -import eu.kanade.tachiyomi.util.* -import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener -import kotlinx.android.synthetic.main.catalogue_controller.* -import kotlinx.android.synthetic.main.main_activity.* -import rx.Observable -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers -import rx.subscriptions.Subscriptions -import timber.log.Timber -import uy.kohesive.injekt.injectLazy -import java.util.concurrent.TimeUnit - -/** - * Controller to manage the catalogues available in the app. - */ -open class CatalogueController(bundle: Bundle) : - NucleusController(bundle), - SecondaryDrawerController, - FlexibleAdapter.OnItemClickListener, - FlexibleAdapter.OnItemLongClickListener, - FlexibleAdapter.EndlessScrollListener, - ChangeMangaCategoriesDialog.Listener { - - constructor(source: CatalogueSource) : this(Bundle().apply { - putLong(SOURCE_ID_KEY, source.id) - }) - - /** - * Preferences helper. - */ - private val preferences: PreferencesHelper by injectLazy() - - /** - * Adapter containing the list of manga from the catalogue. - */ - private var adapter: FlexibleAdapter>? = null - - /** - * Snackbar containing an error message when a request fails. - */ - private var snack: Snackbar? = null - - /** - * Navigation view containing filter items. - */ - private var navView: CatalogueNavigationView? = null - - /** - * Recycler view with the list of results. - */ - private var recycler: RecyclerView? = null - - /** - * Drawer listener to allow swipe only for closing the drawer. - */ - private var drawerListener: DrawerLayout.DrawerListener? = null - - /** - * Subscription for the search view. - */ - private var searchViewSubscription: Subscription? = null - - /** - * Subscription for the number of manga per row. - */ - private var numColumnsSubscription: Subscription? = null - - /** - * Endless loading item. - */ - private var progressItem: ProgressItem? = null - - init { - setHasOptionsMenu(true) - } - - override fun getTitle(): String? { - return presenter.source.name - } - - override fun createPresenter(): CataloguePresenter { - return CataloguePresenter(args.getLong(SOURCE_ID_KEY)) - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.catalogue_controller, container, false) - } - - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - // Initialize adapter, scroll listener and recycler views - adapter = FlexibleAdapter(null, this) - setupRecycler(view) - - navView?.setFilters(presenter.filterItems) - - progress?.visible() - } - - override fun onDestroyView(view: View) { - numColumnsSubscription?.unsubscribe() - numColumnsSubscription = null - searchViewSubscription?.unsubscribe() - searchViewSubscription = null - adapter = null - snack = null - recycler = null - super.onDestroyView(view) - } - - override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { - // Inflate and prepare drawer - val navView = drawer.inflate(R.layout.catalogue_drawer) as CatalogueNavigationView - this.navView = navView - drawerListener = DrawerSwipeCloseListener(drawer, navView).also { - drawer.addDrawerListener(it) - } - navView.setFilters(presenter.filterItems) - - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.END) - - navView.onSearchClicked = { - val allDefault = presenter.sourceFilters == presenter.source.getFilterList() - showProgressBar() - adapter?.clear() - presenter.setSourceFilter(if (allDefault) FilterList() else presenter.sourceFilters) - } - - navView.onResetClicked = { - presenter.appliedFilters = FilterList() - val newFilters = presenter.source.getFilterList() - presenter.sourceFilters = newFilters - navView.setFilters(presenter.filterItems) - } - return navView - } - - override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { - drawerListener?.let { drawer.removeDrawerListener(it) } - drawerListener = null - navView = null - } - - private fun setupRecycler(view: View) { - numColumnsSubscription?.unsubscribe() - - var oldPosition = RecyclerView.NO_POSITION - val oldRecycler = catalogue_view?.getChildAt(1) - if (oldRecycler is RecyclerView) { - oldPosition = (oldRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() - oldRecycler.adapter = null - - catalogue_view?.removeView(oldRecycler) - } - - val recycler = if (presenter.isListMode) { - RecyclerView(view.context).apply { - id = R.id.recycler - layoutManager = LinearLayoutManager(context) - addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) - } - } else { - (catalogue_view.inflate(R.layout.catalogue_recycler_autofit) as AutofitRecyclerView).apply { - numColumnsSubscription = getColumnsPreferenceForCurrentOrientation().asObservable() - .doOnNext { spanCount = it } - .skip(1) - // Set again the adapter to recalculate the covers height - .subscribe { adapter = this@CatalogueController.adapter } - - (layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return when (adapter?.getItemViewType(position)) { - R.layout.catalogue_grid_item, null -> 1 - else -> spanCount - } - } - } - } - } - recycler.setHasFixedSize(true) - recycler.adapter = adapter - - catalogue_view.addView(recycler, 1) - - if (oldPosition != RecyclerView.NO_POSITION) { - recycler.layoutManager.scrollToPosition(oldPosition) - } - this.recycler = recycler - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.catalogue_list, menu) - - // Initialize search menu - menu.findItem(R.id.action_search).apply { - val searchView = actionView as SearchView - - val query = presenter.query - if (!query.isBlank()) { - expandActionView() - searchView.setQuery(query, true) - searchView.clearFocus() - } - - val searchEventsObservable = searchView.queryTextChangeEvents() - .skip(1) - .share() - val writingObservable = searchEventsObservable - .filter { !it.isSubmitted } - .debounce(1250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) - val submitObservable = searchEventsObservable - .filter { it.isSubmitted } - - searchViewSubscription?.unsubscribe() - searchViewSubscription = Observable.merge(writingObservable, submitObservable) - .map { it.queryText().toString() } - .distinctUntilChanged() - .subscribeUntilDestroy { searchWithQuery(it) } - - untilDestroySubscriptions.add( - Subscriptions.create { if (isActionViewExpanded) collapseActionView() }) - } - - // Setup filters button - menu.findItem(R.id.action_set_filter).apply { - icon.mutate() - if (presenter.sourceFilters.isEmpty()) { - isEnabled = false - icon.alpha = 128 - } else { - isEnabled = true - icon.alpha = 255 - } - } - - // Show next display mode - menu.findItem(R.id.action_display_mode).apply { - val icon = if (presenter.isListMode) - R.drawable.ic_view_module_white_24dp - else - R.drawable.ic_view_list_white_24dp - setIcon(icon) - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_display_mode -> swapDisplayMode() - R.id.action_set_filter -> navView?.let { activity?.drawer?.openDrawer(Gravity.END) } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - /** - * Restarts the request with a new query. - * - * @param newQuery the new query. - */ - private fun searchWithQuery(newQuery: String) { - // If text didn't change, do nothing - if (presenter.query == newQuery) - return - - // FIXME dirty fix to restore the toolbar buttons after closing search mode. - if (newQuery == "") { - activity?.invalidateOptionsMenu() - } - - showProgressBar() - adapter?.clear() - - presenter.restartPager(newQuery) - } - - /** - * Called from the presenter when the network request is received. - * - * @param page the current page. - * @param mangas the list of manga of the page. - */ - fun onAddPage(page: Int, mangas: List) { - val adapter = adapter ?: return - hideProgressBar() - if (page == 1) { - adapter.clear() - resetProgressItem() - } - adapter.onLoadMoreComplete(mangas) - } - - /** - * Called from the presenter when the network request fails. - * - * @param error the error received. - */ - fun onAddPageError(error: Throwable) { - Timber.e(error) - val adapter = adapter ?: return - adapter.onLoadMoreComplete(null) - hideProgressBar() - - val message = if (error is NoResultsException) "No results found" else (error.message ?: "") - - snack?.dismiss() - snack = catalogue_view?.snack(message, Snackbar.LENGTH_INDEFINITE) { - setAction(R.string.action_retry) { - // If not the first page, show bottom progress bar. - if (adapter.mainItemCount > 0) { - val item = progressItem ?: return@setAction - adapter.addScrollableFooterWithDelay(item, 0, true) - } else { - showProgressBar() - } - presenter.requestNext() - } - } - } - - /** - * Sets a new progress item and reenables the scroll listener. - */ - private fun resetProgressItem() { - progressItem = ProgressItem() - adapter?.endlessTargetCount = 0 - adapter?.setEndlessScrollListener(this, progressItem!!) - } - - /** - * Called by the adapter when scrolled near the bottom. - */ - override fun onLoadMore(lastPosition: Int, currentPage: Int) { - if (presenter.hasNextPage()) { - presenter.requestNext() - } else { - adapter?.onLoadMoreComplete(null) - adapter?.endlessTargetCount = 1 - } - } - - override fun noMoreLoad(newItemsSize: Int) { - } - - /** - * Called from the presenter when a manga is initialized. - * - * @param manga the manga initialized - */ - fun onMangaInitialized(manga: Manga) { - getHolder(manga)?.setImage(manga) - } - - /** - * Swaps the current display mode. - */ - fun swapDisplayMode() { - val view = view ?: return - val adapter = adapter ?: return - - presenter.swapDisplayMode() - val isListMode = presenter.isListMode - activity?.invalidateOptionsMenu() - setupRecycler(view) - if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) { - // Initialize mangas if going to grid view or if over wifi when going to list view - val mangas = (0 until adapter.itemCount).mapNotNull { - (adapter.getItem(it) as? CatalogueItem)?.manga - } - presenter.initializeMangas(mangas) - } - } - - /** - * Returns a preference for the number of manga per row based on the current orientation. - * - * @return the preference. - */ - fun getColumnsPreferenceForCurrentOrientation(): Preference { - return if (resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT) - preferences.portraitColumns() - else - preferences.landscapeColumns() - } - - /** - * Returns the view holder for the given manga. - * - * @param manga the manga to find. - * @return the holder of the manga or null if it's not bound. - */ - private fun getHolder(manga: Manga): CatalogueHolder? { - val adapter = adapter ?: return null - - adapter.allBoundViewHolders.forEach { holder -> - val item = adapter.getItem(holder.adapterPosition) as? CatalogueItem - if (item != null && item.manga.id!! == manga.id!!) { - return holder as CatalogueHolder - } - } - - return null - } - - /** - * Shows the progress bar. - */ - private fun showProgressBar() { - progress?.visible() - snack?.dismiss() - snack = null - } - - /** - * Hides active progress bars. - */ - private fun hideProgressBar() { - progress?.gone() - } - - /** - * Called when a manga is clicked. - * - * @param position the position of the element clicked. - * @return true if the item should be selected, false otherwise. - */ - override fun onItemClick(position: Int): Boolean { - val item = adapter?.getItem(position) as? CatalogueItem ?: return false - router.pushController(MangaController(item.manga, true).withFadeTransaction()) - - return false - } - - /** - * Called when a manga is long clicked. - * - * Adds the manga to the default category if none is set it shows a list of categories for the user to put the manga - * in, the list consists of the default category plus the user's categories. The default category is preselected on - * new manga, and on already favorited manga the manga's categories are preselected. - * - * @param position the position of the element clicked. - */ - override fun onItemLongClick(position: Int) { - val activity = activity ?: return - val manga = (adapter?.getItem(position) as? CatalogueItem?)?.manga ?: return - if (manga.favorite) { - MaterialDialog.Builder(activity) - .items(activity.getString(R.string.remove_from_library)) - .itemsCallback { _, _, which, _ -> - when (which) { - 0 -> { - presenter.changeMangaFavorite(manga) - adapter?.notifyItemChanged(position) - } - } - }.show() - } else { - presenter.changeMangaFavorite(manga) - adapter?.notifyItemChanged(position) - - val categories = presenter.getCategories() - val defaultCategory = categories.find { it.id == preferences.defaultCategory() } - if (defaultCategory != null) { - presenter.moveMangaToCategory(manga, defaultCategory) - } else if (categories.size <= 1) { // default or the one from the user - presenter.moveMangaToCategory(manga, categories.firstOrNull()) - } else { - val ids = presenter.getMangaCategoryIds(manga) - val preselected = ids.mapNotNull { id -> - categories.indexOfFirst { it.id == id }.takeIf { it != -1 } - }.toTypedArray() - - ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) - .showDialog(router) - } - } - - } - - /** - * Update manga to use selected categories. - * - * @param mangas The list of manga to move to categories. - * @param categories The list of categories where manga will be placed. - */ - override fun updateCategoriesForMangas(mangas: List, categories: List) { - val manga = mangas.firstOrNull() ?: return - presenter.updateMangaCategories(manga, categories) - } - - protected companion object { - const val SOURCE_ID_KEY = "sourceId" - } - -} +package eu.kanade.tachiyomi.ui.catalogue + +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.SearchView +import android.view.* +import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.ControllerChangeType +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.changehandler.FadeChangeHandler +import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.source.online.LoginSource +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction +import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController +import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController +import eu.kanade.tachiyomi.ui.catalogue.latest.LatestUpdatesController +import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController +import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog +import kotlinx.android.synthetic.main.catalogue_main_controller.* +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +/** + * This controller shows and manages the different catalogues enabled by the user. + * This controller should only handle UI actions, IO actions should be done by [CataloguePresenter] + * [SourceLoginDialog.Listener] refreshes the adapter on successful login of catalogues. + * [CatalogueAdapter.OnBrowseClickListener] call function data on browse item click. + * [CatalogueAdapter.OnLatestClickListener] call function data on latest item click + */ +class CatalogueController : NucleusController(), + SourceLoginDialog.Listener, + FlexibleAdapter.OnItemClickListener, + CatalogueAdapter.OnBrowseClickListener, + CatalogueAdapter.OnLatestClickListener { + + /** + * Application preferences. + */ + private val preferences: PreferencesHelper = Injekt.get() + + /** + * Adapter containing sources. + */ + private var adapter : CatalogueAdapter? = null + + /** + * Called when controller is initialized. + */ + init { + // Enable the option menu + setHasOptionsMenu(true) + } + + /** + * Set the title of controller. + * + * @return title. + */ + override fun getTitle(): String? { + return applicationContext?.getString(R.string.label_catalogues) + } + + /** + * Create the [CataloguePresenter] used in controller. + * + * @return instance of [CataloguePresenter] + */ + override fun createPresenter(): CataloguePresenter { + return CataloguePresenter() + } + + /** + * Initiate the view with [R.layout.catalogue_main_controller]. + * + * @param inflater used to load the layout xml. + * @param container containing parent views. + * @return inflated view. + */ + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.catalogue_main_controller, container, false) + } + + /** + * Called when the view is created + * + * @param view view of controller + */ + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + adapter = CatalogueAdapter(this) + + // Create recycler and set adapter. + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.adapter = adapter + recycler.addItemDecoration(SourceDividerItemDecoration(view.context)) + } + + override fun onDestroyView(view: View) { + adapter = null + super.onDestroyView(view) + } + + override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { + super.onChangeStarted(handler, type) + if (!type.isPush && handler is SettingsSourcesFadeChangeHandler) { + presenter.updateSources() + } + } + + /** + * Called when login dialog is closed, refreshes the adapter. + * + * @param source clicked item containing source information. + */ + override fun loginDialogClosed(source: LoginSource) { + if (source.isLogged()) { + adapter?.clear() + presenter.loadSources() + } + } + + /** + * Called when item is clicked + */ + override fun onItemClick(position: Int): Boolean { + val item = adapter?.getItem(position) as? SourceItem ?: return false + val source = item.source + if (source is LoginSource && !source.isLogged()) { + val dialog = SourceLoginDialog(source) + dialog.targetController = this + dialog.showDialog(router) + } else { + // Open the catalogue view. + openCatalogue(source, BrowseCatalogueController(source)) + } + return false + } + + /** + * Called when browse is clicked in [CatalogueAdapter] + */ + override fun onBrowseClick(position: Int) { + onItemClick(position) + } + + /** + * Called when latest is clicked in [CatalogueAdapter] + */ + override fun onLatestClick(position: Int) { + val item = adapter?.getItem(position) as? SourceItem ?: return + openCatalogue(item.source, LatestUpdatesController(item.source)) + } + + /** + * Opens a catalogue with the given controller. + */ + private fun openCatalogue(source: CatalogueSource, controller: BrowseCatalogueController) { + preferences.lastUsedCatalogueSource().set(source.id) + router.pushController(controller.withFadeTransaction()) + } + + /** + * 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.catalogue_main, menu) + + // Initialize search option. + val searchItem = menu.findItem(R.id.action_search) + val searchView = searchItem.actionView as SearchView + + // Change hint to show global search. + searchView.queryHint = applicationContext?.getString(R.string.action_global_search_hint) + + // Create query listener which opens the global search view. + searchView.queryTextChangeEvents() + .filter { it.isSubmitted } + .subscribeUntilDestroy { + val query = it.queryText().toString() + router.pushController(CatalogueSearchController(query).withFadeTransaction()) + } + } + + /** + * Called when an option menu item has been selected by the user. + * + * @param item The selected item. + * @return True if this event has been consumed, false if it has not. + */ + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + // Initialize option to open catalogue settings. + R.id.action_settings -> { + router.pushController((RouterTransaction.with(SettingsSourcesController())) + .popChangeHandler(SettingsSourcesFadeChangeHandler()) + .pushChangeHandler(FadeChangeHandler())) + } + else -> return super.onOptionsItemSelected(item) + } + return true + } + + /** + * Called to update adapter containing sources. + */ + fun setSources(sources: List>) { + adapter?.updateDataSet(sources) + } + + /** + * Called to set the last used catalogue at the top of the view. + */ + fun setLastUsedSource(item: SourceItem?) { + adapter?.removeAllScrollableHeaders() + if (item != null) { + adapter?.addScrollableHeader(item) + } + } + + class SettingsSourcesFadeChangeHandler : FadeChangeHandler() +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt index 62a6977ba..096c5b19e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt @@ -1,376 +1,104 @@ package eu.kanade.tachiyomi.ui.catalogue import android.os.Bundle -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.flexibleadapter.items.ISectionable -import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.database.DatabaseHelper -import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import eu.kanade.tachiyomi.ui.catalogue.filter.* import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers -import rx.schedulers.Schedulers -import rx.subjects.PublishSubject -import timber.log.Timber import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.util.* +import java.util.concurrent.TimeUnit /** - * Presenter of [CatalogueController]. + * Presenter of [CatalogueController] + * Function calls should be done from here. UI calls should be done from the controller. + * + * @param sourceManager manages the different sources. + * @param preferences application preferences. */ -open class CataloguePresenter( - sourceId: Long, - sourceManager: SourceManager = Injekt.get(), - private val db: DatabaseHelper = Injekt.get(), - private val prefs: PreferencesHelper = Injekt.get(), - private val coverCache: CoverCache = Injekt.get() +class CataloguePresenter( + val sourceManager: SourceManager = Injekt.get(), + private val preferences: PreferencesHelper = Injekt.get() ) : BasePresenter() { /** - * Selected source. + * Enabled sources. */ - val source = sourceManager.get(sourceId) as CatalogueSource + var sources = getEnabledSources() /** - * Query from the view. + * Subscription for retrieving enabled sources. */ - var query = "" - private set - - /** - * Modifiable list of filters. - */ - var sourceFilters = FilterList() - set(value) { - field = value - filterItems = value.toItems() - } - - var filterItems: List> = emptyList() - - /** - * List of filters used by the [Pager]. If empty alongside [query], the popular query is used. - */ - var appliedFilters = FilterList() - - /** - * Pager containing a list of manga results. - */ - private lateinit var pager: Pager - - /** - * Subject that initializes a list of manga. - */ - private val mangaDetailSubject = PublishSubject.create>() - - /** - * Whether the view is in list mode or not. - */ - var isListMode: Boolean = false - private set - - /** - * Subscription for the pager. - */ - private var pagerSubscription: Subscription? = null - - /** - * Subscription for one request from the pager. - */ - private var pageSubscription: Subscription? = null - - /** - * Subscription to initialize manga details. - */ - private var initializerSubscription: Subscription? = null + private var sourceSubscription: Subscription? = null override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) - sourceFilters = source.getFilterList() - - if (savedState != null) { - query = savedState.getString(CataloguePresenter::query.name, "") - } - - add(prefs.catalogueAsList().asObservable() - .subscribe { setDisplayMode(it) }) - - restartPager() - } - - override fun onSave(state: Bundle) { - state.putString(CataloguePresenter::query.name, query) - super.onSave(state) + // Load enabled and last used sources + loadSources() + loadLastUsedSource() } /** - * Restarts the pager for the active source with the provided query and filters. - * - * @param query the query. - * @param filters the current state of the filters (for search mode). + * Unsubscribe and create a new subscription to fetch enabled sources. */ - fun restartPager(query: String = this.query, filters: FilterList = this.appliedFilters) { - this.query = query - this.appliedFilters = filters + fun loadSources() { + sourceSubscription?.unsubscribe() - subscribeToMangaInitializer() - - // Create a new pager. - pager = createPager(query, filters) - - val sourceId = source.id - - val catalogueAsList = prefs.catalogueAsList() - - // Prepare the pager. - pagerSubscription?.let { remove(it) } - pagerSubscription = pager.results() - .observeOn(Schedulers.io()) - .map { it.first to it.second.map { networkToLocalManga(it, sourceId) } } - .doOnNext { initializeMangas(it.second) } - .map { it.first to it.second.map { CatalogueItem(it, catalogueAsList) } } - .observeOn(AndroidSchedulers.mainThread()) - .subscribeReplay({ view, (page, mangas) -> - view.onAddPage(page, mangas) - }, { _, error -> - Timber.e(error) - }) - - // Request first page. - requestNext() - } - - /** - * Requests the next page for the active pager. - */ - fun requestNext() { - if (!hasNextPage()) return - - pageSubscription?.let { remove(it) } - pageSubscription = Observable.defer { pager.requestNext() } - .subscribeFirst({ _, _ -> - // Nothing to do when onNext is emitted. - }, CatalogueController::onAddPageError) - } - - /** - * Returns true if the last fetched page has a next page. - */ - fun hasNextPage(): Boolean { - return pager.hasNextPage - } - - /** - * Sets the display mode. - * - * @param asList whether the current mode is in list or not. - */ - private fun setDisplayMode(asList: Boolean) { - isListMode = asList - subscribeToMangaInitializer() - } - - /** - * Subscribes to the initializer of manga details and updates the view if needed. - */ - private fun subscribeToMangaInitializer() { - initializerSubscription?.let { remove(it) } - initializerSubscription = mangaDetailSubject.observeOn(Schedulers.io()) - .flatMap { Observable.from(it) } - .filter { it.thumbnail_url == null && !it.initialized } - .concatMap { getMangaDetailsObservable(it) } - .onBackpressureBuffer() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ manga -> - @Suppress("DEPRECATION") - view?.onMangaInitialized(manga) - }, { error -> - Timber.e(error) - }) - .apply { add(this) } - } - - /** - * Returns a manga from the database for the given manga from network. It creates a new entry - * if the manga is not yet in the database. - * - * @param sManga the manga from the source. - * @return a manga from the database. - */ - private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { - 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).executeAsBlocking() - newManga.id = result.insertedId() - localManga = newManga - } - return localManga - } - - /** - * Initialize a list of manga. - * - * @param mangas the list of manga to initialize. - */ - fun initializeMangas(mangas: List) { - mangaDetailSubject.onNext(mangas) - } - - /** - * Returns an observable of manga that initializes the given manga. - * - * @param manga the manga to initialize. - * @return an observable of the manga to initialize - */ - private fun getMangaDetailsObservable(manga: Manga): Observable { - return source.fetchMangaDetails(manga) - .flatMap { networkManga -> - manga.copyFrom(networkManga) - manga.initialized = true - db.insertManga(manga).executeAsBlocking() - Observable.just(manga) - } - .onErrorResumeNext { Observable.just(manga) } - } - - /** - * Adds or removes a manga from the library. - * - * @param manga the manga to update. - */ - fun changeMangaFavorite(manga: Manga) { - manga.favorite = !manga.favorite - if (!manga.favorite) { - coverCache.deleteFromCache(manga.thumbnail_url) - } - db.insertManga(manga).executeAsBlocking() - } - - /** - * Changes the active display mode. - */ - fun swapDisplayMode() { - prefs.catalogueAsList().set(!isListMode) - } - - /** - * Set the filter states for the current source. - * - * @param filters a list of active filters. - */ - fun setSourceFilter(filters: FilterList) { - restartPager(filters = filters) - } - - open fun createPager(query: String, filters: FilterList): Pager { - return CataloguePager(source, query, filters) - } - - private fun FilterList.toItems(): List> { - return mapNotNull { - when (it) { - is Filter.Header -> HeaderItem(it) - is Filter.Separator -> SeparatorItem(it) - is Filter.CheckBox -> CheckboxItem(it) - is Filter.TriState -> TriStateItem(it) - is Filter.Text -> TextItem(it) - is Filter.Select<*> -> SelectItem(it) - is Filter.Group<*> -> { - val group = GroupItem(it) - val subItems = it.state.mapNotNull { - when (it) { - is Filter.CheckBox -> CheckboxSectionItem(it) - is Filter.TriState -> TriStateSectionItem(it) - is Filter.Text -> TextSectionItem(it) - is Filter.Select<*> -> SelectSectionItem(it) - else -> null - } as? ISectionable<*, *> - } - subItems.forEach { it.header = group } - group.subItems = subItems - group - } - is Filter.Sort -> { - val group = SortGroup(it) - val subItems = it.values.map { - SortItem(it, group) - } - group.subItems = subItems - group - } + val map = TreeMap> { d1, d2 -> + // Catalogues without a lang defined will be placed at the end + when { + d1 == "" && d2 != "" -> 1 + d2 == "" && d1 != "" -> -1 + else -> d1.compareTo(d2) } } - } - - /** - * Get the default, and user categories. - * - * @return List of categories, default plus user categories - */ - fun getCategories(): List { - return db.getCategories().executeAsBlocking() - } - - /** - * Gets the category id's the manga is in, if the manga is not in a category, returns the default id. - * - * @param manga the manga to get categories from. - * @return Array of category ids the manga is in, if none returns default id - */ - fun getMangaCategoryIds(manga: Manga): Array { - val categories = db.getCategoriesForManga(manga).executeAsBlocking() - return categories.mapNotNull { it.id }.toTypedArray() - } - - /** - * Move the given manga to categories. - * - * @param categories the selected categories. - * @param manga the manga to move. - */ - private fun moveMangaToCategories(manga: Manga, categories: List) { - val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) } - db.setMangaCategories(mc, listOf(manga)) - } - - /** - * Move the given manga to the category. - * - * @param category the selected category. - * @param manga the manga to move. - */ - fun moveMangaToCategory(manga: Manga, category: Category?) { - moveMangaToCategories(manga, listOfNotNull(category)) - } - - /** - * Update manga to use selected categories. - * - * @param manga needed to change - * @param selectedCategories selected categories - */ - fun updateMangaCategories(manga: Manga, selectedCategories: List) { - if (!selectedCategories.isEmpty()) { - if (!manga.favorite) - changeMangaFavorite(manga) - - moveMangaToCategories(manga, selectedCategories.filter { it.id != 0 }) - } else { - changeMangaFavorite(manga) + val byLang = sources.groupByTo(map, { it.lang }) + val sourceItems = byLang.flatMap { + val langItem = LangItem(it.key) + it.value.map { source -> SourceItem(source, langItem) } } + + sourceSubscription = Observable.just(sourceItems) + .subscribeLatestCache(CatalogueController::setSources) } + private fun loadLastUsedSource() { + val sharedObs = preferences.lastUsedCatalogueSource().asObservable().share() + + // Emit the first item immediately but delay subsequent emissions by 500ms. + Observable.merge( + sharedObs.take(1), + sharedObs.skip(1).delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())) + .distinctUntilChanged() + .map { (sourceManager.get(it) as? CatalogueSource)?.let { SourceItem(it) } } + .subscribeLatestCache(CatalogueController::setLastUsedSource) + } + + fun updateSources() { + sources = getEnabledSources() + loadSources() + } + + /** + * Returns a list of enabled sources ordered by language and name. + * + * @return list containing enabled sources. + */ + private fun getEnabledSources(): List { + val languages = preferences.enabledLanguages().getOrDefault() + val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault() + + return sourceManager.getCatalogueSources() + .filter { it.lang in languages } + .filterNot { it.id.toString() in hiddenCatalogues } + .sortedBy { "(${it.lang}) ${it.name}" } + + sourceManager.get(LocalSource.ID) as LocalSource + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/LangHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt similarity index 90% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/LangHolder.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt index 02dcea146..ea1fe6f0a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/LangHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt @@ -1,21 +1,21 @@ -package eu.kanade.tachiyomi.ui.catalogue.main - -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.R -import kotlinx.android.synthetic.main.catalogue_main_controller_card.view.* -import java.util.* - -class LangHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter, true) { - - fun bind(item: LangItem) { - itemView.title.text = when { - item.code == "" -> itemView.context.getString(R.string.other_source) - else -> { - val locale = Locale(item.code) - locale.getDisplayName(locale).capitalize() - } - } - } +package eu.kanade.tachiyomi.ui.catalogue + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.viewholders.FlexibleViewHolder +import eu.kanade.tachiyomi.R +import kotlinx.android.synthetic.main.catalogue_main_controller_card.view.* +import java.util.* + +class LangHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter, true) { + + fun bind(item: LangItem) { + itemView.title.text = when { + item.code == "" -> itemView.context.getString(R.string.other_source) + else -> { + val locale = Locale(item.code) + locale.getDisplayName(locale).capitalize() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/LangItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangItem.kt similarity index 95% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/LangItem.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangItem.kt index 815ad7495..dfa6a91d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/LangItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangItem.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue.main +package eu.kanade.tachiyomi.ui.catalogue import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/NoResultsException.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/NoResultsException.kt deleted file mode 100644 index 3ac0dbac8..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/NoResultsException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package eu.kanade.tachiyomi.ui.catalogue - -class NoResultsException : Exception() \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceDividerItemDecoration.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt similarity index 94% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceDividerItemDecoration.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt index bb90f5307..4caa72053 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceDividerItemDecoration.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt @@ -1,47 +1,47 @@ -package eu.kanade.tachiyomi.ui.catalogue.main - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.support.v7.widget.RecyclerView -import android.view.View - -class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoration() { - - private val divider: Drawable - - init { - val a = context.obtainStyledAttributes(ATTRS) - divider = a.getDrawable(0) - a.recycle() - } - - override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { - val left = parent.paddingLeft + SourceHolder.margin - val right = parent.width - parent.paddingRight - SourceHolder.margin - - val childCount = parent.childCount - for (i in 0 until childCount - 1) { - val child = parent.getChildAt(i) - if (parent.getChildViewHolder(child) is SourceHolder && - parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceHolder) { - val params = child.layoutParams as RecyclerView.LayoutParams - val top = child.bottom + params.bottomMargin - val bottom = top + divider.intrinsicHeight - - divider.setBounds(left, top, right, bottom) - divider.draw(c) - } - } - } - - override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, - state: RecyclerView.State) { - outRect.set(0, 0, 0, divider.intrinsicHeight) - } - - companion object { - private val ATTRS = intArrayOf(android.R.attr.listDivider) - } +package eu.kanade.tachiyomi.ui.catalogue + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.support.v7.widget.RecyclerView +import android.view.View + +class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoration() { + + private val divider: Drawable + + init { + val a = context.obtainStyledAttributes(ATTRS) + divider = a.getDrawable(0) + a.recycle() + } + + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + val left = parent.paddingLeft + SourceHolder.margin + val right = parent.width - parent.paddingRight - SourceHolder.margin + + val childCount = parent.childCount + for (i in 0 until childCount - 1) { + val child = parent.getChildAt(i) + if (parent.getChildViewHolder(child) is SourceHolder && + parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceHolder) { + val params = child.layoutParams as RecyclerView.LayoutParams + val top = child.bottom + params.bottomMargin + val bottom = top + divider.intrinsicHeight + + divider.setBounds(left, top, right, bottom) + divider.draw(c) + } + } + } + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, + state: RecyclerView.State) { + outRect.set(0, 0, 0, divider.intrinsicHeight) + } + + companion object { + private val ATTRS = intArrayOf(android.R.attr.listDivider) + } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt similarity index 96% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceHolder.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt index ddc8914b0..e361b7b9c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue.main +package eu.kanade.tachiyomi.ui.catalogue import android.os.Build import android.view.View @@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.util.visible import io.github.mthli.slice.Slice import kotlinx.android.synthetic.main.catalogue_main_controller_card_item.view.* -class SourceHolder(view: View, adapter: CatalogueMainAdapter) : FlexibleViewHolder(view, adapter) { +class SourceHolder(view: View, adapter: CatalogueAdapter) : FlexibleViewHolder(view, adapter) { private val slice = Slice(itemView.card).apply { setColor(adapter.cardBackground) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt similarity index 90% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceItem.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt index 5031b81d9..57c1fdf8a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/SourceItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue.main +package eu.kanade.tachiyomi.ui.catalogue import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter @@ -26,7 +26,7 @@ data class SourceItem(val source: CatalogueSource, val header: LangItem? = null) * Creates a new view holder for this item. */ override fun createViewHolder(view: View, adapter: FlexibleAdapter<*>): SourceHolder { - return SourceHolder(view, adapter as CatalogueMainAdapter) + return SourceHolder(view, adapter as CatalogueAdapter) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt new file mode 100644 index 000000000..e5edc0297 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt @@ -0,0 +1,520 @@ +package eu.kanade.tachiyomi.ui.catalogue.browse + +import android.content.res.Configuration +import android.os.Bundle +import android.support.design.widget.Snackbar +import android.support.v4.widget.DrawerLayout +import android.support.v7.widget.* +import android.view.* +import com.afollestad.materialdialogs.MaterialDialog +import com.f2prateek.rx.preferences.Preference +import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Category +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction +import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog +import eu.kanade.tachiyomi.ui.manga.MangaController +import eu.kanade.tachiyomi.util.* +import eu.kanade.tachiyomi.widget.AutofitRecyclerView +import eu.kanade.tachiyomi.widget.DrawerSwipeCloseListener +import kotlinx.android.synthetic.main.catalogue_controller.* +import kotlinx.android.synthetic.main.main_activity.* +import rx.Observable +import rx.Subscription +import rx.android.schedulers.AndroidSchedulers +import rx.subscriptions.Subscriptions +import timber.log.Timber +import uy.kohesive.injekt.injectLazy +import java.util.concurrent.TimeUnit + +/** + * Controller to manage the catalogues available in the app. + */ +open class BrowseCatalogueController(bundle: Bundle) : + NucleusController(bundle), + SecondaryDrawerController, + FlexibleAdapter.OnItemClickListener, + FlexibleAdapter.OnItemLongClickListener, + FlexibleAdapter.EndlessScrollListener, + ChangeMangaCategoriesDialog.Listener { + + constructor(source: CatalogueSource) : this(Bundle().apply { + putLong(SOURCE_ID_KEY, source.id) + }) + + /** + * Preferences helper. + */ + private val preferences: PreferencesHelper by injectLazy() + + /** + * Adapter containing the list of manga from the catalogue. + */ + private var adapter: FlexibleAdapter>? = null + + /** + * Snackbar containing an error message when a request fails. + */ + private var snack: Snackbar? = null + + /** + * Navigation view containing filter items. + */ + private var navView: CatalogueNavigationView? = null + + /** + * Recycler view with the list of results. + */ + private var recycler: RecyclerView? = null + + /** + * Drawer listener to allow swipe only for closing the drawer. + */ + private var drawerListener: DrawerLayout.DrawerListener? = null + + /** + * Subscription for the search view. + */ + private var searchViewSubscription: Subscription? = null + + /** + * Subscription for the number of manga per row. + */ + private var numColumnsSubscription: Subscription? = null + + /** + * Endless loading item. + */ + private var progressItem: ProgressItem? = null + + init { + setHasOptionsMenu(true) + } + + override fun getTitle(): String? { + return presenter.source.name + } + + override fun createPresenter(): BrowseCataloguePresenter { + return BrowseCataloguePresenter(args.getLong(SOURCE_ID_KEY)) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.catalogue_controller, container, false) + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + // Initialize adapter, scroll listener and recycler views + adapter = FlexibleAdapter(null, this) + setupRecycler(view) + + navView?.setFilters(presenter.filterItems) + + progress?.visible() + } + + override fun onDestroyView(view: View) { + numColumnsSubscription?.unsubscribe() + numColumnsSubscription = null + searchViewSubscription?.unsubscribe() + searchViewSubscription = null + adapter = null + snack = null + recycler = null + super.onDestroyView(view) + } + + override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { + // Inflate and prepare drawer + val navView = drawer.inflate(R.layout.catalogue_drawer) as CatalogueNavigationView + this.navView = navView + drawerListener = DrawerSwipeCloseListener(drawer, navView).also { + drawer.addDrawerListener(it) + } + navView.setFilters(presenter.filterItems) + + drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.END) + + navView.onSearchClicked = { + val allDefault = presenter.sourceFilters == presenter.source.getFilterList() + showProgressBar() + adapter?.clear() + presenter.setSourceFilter(if (allDefault) FilterList() else presenter.sourceFilters) + } + + navView.onResetClicked = { + presenter.appliedFilters = FilterList() + val newFilters = presenter.source.getFilterList() + presenter.sourceFilters = newFilters + navView.setFilters(presenter.filterItems) + } + return navView + } + + override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { + drawerListener?.let { drawer.removeDrawerListener(it) } + drawerListener = null + navView = null + } + + private fun setupRecycler(view: View) { + numColumnsSubscription?.unsubscribe() + + var oldPosition = RecyclerView.NO_POSITION + val oldRecycler = catalogue_view?.getChildAt(1) + if (oldRecycler is RecyclerView) { + oldPosition = (oldRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + oldRecycler.adapter = null + + catalogue_view?.removeView(oldRecycler) + } + + val recycler = if (presenter.isListMode) { + RecyclerView(view.context).apply { + id = R.id.recycler + layoutManager = LinearLayoutManager(context) + addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) + } + } else { + (catalogue_view.inflate(R.layout.catalogue_recycler_autofit) as AutofitRecyclerView).apply { + numColumnsSubscription = getColumnsPreferenceForCurrentOrientation().asObservable() + .doOnNext { spanCount = it } + .skip(1) + // Set again the adapter to recalculate the covers height + .subscribe { adapter = this@BrowseCatalogueController.adapter } + + (layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return when (adapter?.getItemViewType(position)) { + R.layout.catalogue_grid_item, null -> 1 + else -> spanCount + } + } + } + } + } + recycler.setHasFixedSize(true) + recycler.adapter = adapter + + catalogue_view.addView(recycler, 1) + + if (oldPosition != RecyclerView.NO_POSITION) { + recycler.layoutManager.scrollToPosition(oldPosition) + } + this.recycler = recycler + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.catalogue_list, menu) + + // Initialize search menu + menu.findItem(R.id.action_search).apply { + val searchView = actionView as SearchView + + val query = presenter.query + if (!query.isBlank()) { + expandActionView() + searchView.setQuery(query, true) + searchView.clearFocus() + } + + val searchEventsObservable = searchView.queryTextChangeEvents() + .skip(1) + .share() + val writingObservable = searchEventsObservable + .filter { !it.isSubmitted } + .debounce(1250, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) + val submitObservable = searchEventsObservable + .filter { it.isSubmitted } + + searchViewSubscription?.unsubscribe() + searchViewSubscription = Observable.merge(writingObservable, submitObservable) + .map { it.queryText().toString() } + .distinctUntilChanged() + .subscribeUntilDestroy { searchWithQuery(it) } + + untilDestroySubscriptions.add( + Subscriptions.create { if (isActionViewExpanded) collapseActionView() }) + } + + // Setup filters button + menu.findItem(R.id.action_set_filter).apply { + icon.mutate() + if (presenter.sourceFilters.isEmpty()) { + isEnabled = false + icon.alpha = 128 + } else { + isEnabled = true + icon.alpha = 255 + } + } + + // Show next display mode + menu.findItem(R.id.action_display_mode).apply { + val icon = if (presenter.isListMode) + R.drawable.ic_view_module_white_24dp + else + R.drawable.ic_view_list_white_24dp + setIcon(icon) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_display_mode -> swapDisplayMode() + R.id.action_set_filter -> navView?.let { activity?.drawer?.openDrawer(Gravity.END) } + else -> return super.onOptionsItemSelected(item) + } + return true + } + + /** + * Restarts the request with a new query. + * + * @param newQuery the new query. + */ + private fun searchWithQuery(newQuery: String) { + // If text didn't change, do nothing + if (presenter.query == newQuery) + return + + // FIXME dirty fix to restore the toolbar buttons after closing search mode. + if (newQuery == "") { + activity?.invalidateOptionsMenu() + } + + showProgressBar() + adapter?.clear() + + presenter.restartPager(newQuery) + } + + /** + * Called from the presenter when the network request is received. + * + * @param page the current page. + * @param mangas the list of manga of the page. + */ + fun onAddPage(page: Int, mangas: List) { + val adapter = adapter ?: return + hideProgressBar() + if (page == 1) { + adapter.clear() + resetProgressItem() + } + adapter.onLoadMoreComplete(mangas) + } + + /** + * Called from the presenter when the network request fails. + * + * @param error the error received. + */ + fun onAddPageError(error: Throwable) { + Timber.e(error) + val adapter = adapter ?: return + adapter.onLoadMoreComplete(null) + hideProgressBar() + + val message = if (error is NoResultsException) "No results found" else (error.message ?: "") + + snack?.dismiss() + snack = catalogue_view?.snack(message, Snackbar.LENGTH_INDEFINITE) { + setAction(R.string.action_retry) { + // If not the first page, show bottom progress bar. + if (adapter.mainItemCount > 0) { + val item = progressItem ?: return@setAction + adapter.addScrollableFooterWithDelay(item, 0, true) + } else { + showProgressBar() + } + presenter.requestNext() + } + } + } + + /** + * Sets a new progress item and reenables the scroll listener. + */ + private fun resetProgressItem() { + progressItem = ProgressItem() + adapter?.endlessTargetCount = 0 + adapter?.setEndlessScrollListener(this, progressItem!!) + } + + /** + * Called by the adapter when scrolled near the bottom. + */ + override fun onLoadMore(lastPosition: Int, currentPage: Int) { + if (presenter.hasNextPage()) { + presenter.requestNext() + } else { + adapter?.onLoadMoreComplete(null) + adapter?.endlessTargetCount = 1 + } + } + + override fun noMoreLoad(newItemsSize: Int) { + } + + /** + * Called from the presenter when a manga is initialized. + * + * @param manga the manga initialized + */ + fun onMangaInitialized(manga: Manga) { + getHolder(manga)?.setImage(manga) + } + + /** + * Swaps the current display mode. + */ + fun swapDisplayMode() { + val view = view ?: return + val adapter = adapter ?: return + + presenter.swapDisplayMode() + val isListMode = presenter.isListMode + activity?.invalidateOptionsMenu() + setupRecycler(view) + if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) { + // Initialize mangas if going to grid view or if over wifi when going to list view + val mangas = (0 until adapter.itemCount).mapNotNull { + (adapter.getItem(it) as? CatalogueItem)?.manga + } + presenter.initializeMangas(mangas) + } + } + + /** + * Returns a preference for the number of manga per row based on the current orientation. + * + * @return the preference. + */ + fun getColumnsPreferenceForCurrentOrientation(): Preference { + return if (resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT) + preferences.portraitColumns() + else + preferences.landscapeColumns() + } + + /** + * Returns the view holder for the given manga. + * + * @param manga the manga to find. + * @return the holder of the manga or null if it's not bound. + */ + private fun getHolder(manga: Manga): CatalogueHolder? { + val adapter = adapter ?: return null + + adapter.allBoundViewHolders.forEach { holder -> + val item = adapter.getItem(holder.adapterPosition) as? CatalogueItem + if (item != null && item.manga.id!! == manga.id!!) { + return holder as CatalogueHolder + } + } + + return null + } + + /** + * Shows the progress bar. + */ + private fun showProgressBar() { + progress?.visible() + snack?.dismiss() + snack = null + } + + /** + * Hides active progress bars. + */ + private fun hideProgressBar() { + progress?.gone() + } + + /** + * Called when a manga is clicked. + * + * @param position the position of the element clicked. + * @return true if the item should be selected, false otherwise. + */ + override fun onItemClick(position: Int): Boolean { + val item = adapter?.getItem(position) as? CatalogueItem ?: return false + router.pushController(MangaController(item.manga, true).withFadeTransaction()) + + return false + } + + /** + * Called when a manga is long clicked. + * + * Adds the manga to the default category if none is set it shows a list of categories for the user to put the manga + * in, the list consists of the default category plus the user's categories. The default category is preselected on + * new manga, and on already favorited manga the manga's categories are preselected. + * + * @param position the position of the element clicked. + */ + override fun onItemLongClick(position: Int) { + val activity = activity ?: return + val manga = (adapter?.getItem(position) as? CatalogueItem?)?.manga ?: return + if (manga.favorite) { + MaterialDialog.Builder(activity) + .items(activity.getString(R.string.remove_from_library)) + .itemsCallback { _, _, which, _ -> + when (which) { + 0 -> { + presenter.changeMangaFavorite(manga) + adapter?.notifyItemChanged(position) + } + } + }.show() + } else { + presenter.changeMangaFavorite(manga) + adapter?.notifyItemChanged(position) + + val categories = presenter.getCategories() + val defaultCategory = categories.find { it.id == preferences.defaultCategory() } + if (defaultCategory != null) { + presenter.moveMangaToCategory(manga, defaultCategory) + } else if (categories.size <= 1) { // default or the one from the user + presenter.moveMangaToCategory(manga, categories.firstOrNull()) + } else { + val ids = presenter.getMangaCategoryIds(manga) + val preselected = ids.mapNotNull { id -> + categories.indexOfFirst { it.id == id }.takeIf { it != -1 } + }.toTypedArray() + + ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected) + .showDialog(router) + } + } + + } + + /** + * Update manga to use selected categories. + * + * @param mangas The list of manga to move to categories. + * @param categories The list of categories where manga will be placed. + */ + override fun updateCategoriesForMangas(mangas: List, categories: List) { + val manga = mangas.firstOrNull() ?: return + presenter.updateMangaCategories(manga, categories) + } + + protected companion object { + const val SOURCE_ID_KEY = "sourceId" + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt new file mode 100644 index 000000000..6bc440eae --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCataloguePresenter.kt @@ -0,0 +1,376 @@ +package eu.kanade.tachiyomi.ui.catalogue.browse + +import android.os.Bundle +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.flexibleadapter.items.ISectionable +import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.data.database.DatabaseHelper +import eu.kanade.tachiyomi.data.database.models.Category +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.CatalogueSource +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import eu.kanade.tachiyomi.ui.catalogue.filter.* +import rx.Observable +import rx.Subscription +import rx.android.schedulers.AndroidSchedulers +import rx.schedulers.Schedulers +import rx.subjects.PublishSubject +import timber.log.Timber +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +/** + * Presenter of [BrowseCatalogueController]. + */ +open class BrowseCataloguePresenter( + sourceId: Long, + sourceManager: SourceManager = Injekt.get(), + private val db: DatabaseHelper = Injekt.get(), + private val prefs: PreferencesHelper = Injekt.get(), + private val coverCache: CoverCache = Injekt.get() +) : BasePresenter() { + + /** + * Selected source. + */ + val source = sourceManager.get(sourceId) as CatalogueSource + + /** + * Query from the view. + */ + var query = "" + private set + + /** + * Modifiable list of filters. + */ + var sourceFilters = FilterList() + set(value) { + field = value + filterItems = value.toItems() + } + + var filterItems: List> = emptyList() + + /** + * List of filters used by the [Pager]. If empty alongside [query], the popular query is used. + */ + var appliedFilters = FilterList() + + /** + * Pager containing a list of manga results. + */ + private lateinit var pager: Pager + + /** + * Subject that initializes a list of manga. + */ + private val mangaDetailSubject = PublishSubject.create>() + + /** + * Whether the view is in list mode or not. + */ + var isListMode: Boolean = false + private set + + /** + * Subscription for the pager. + */ + private var pagerSubscription: Subscription? = null + + /** + * Subscription for one request from the pager. + */ + private var pageSubscription: Subscription? = null + + /** + * Subscription to initialize manga details. + */ + private var initializerSubscription: Subscription? = null + + override fun onCreate(savedState: Bundle?) { + super.onCreate(savedState) + + sourceFilters = source.getFilterList() + + if (savedState != null) { + query = savedState.getString(::query.name, "") + } + + add(prefs.catalogueAsList().asObservable() + .subscribe { setDisplayMode(it) }) + + restartPager() + } + + override fun onSave(state: Bundle) { + state.putString(::query.name, query) + super.onSave(state) + } + + /** + * Restarts the pager for the active source with the provided query and filters. + * + * @param query the query. + * @param filters the current state of the filters (for search mode). + */ + fun restartPager(query: String = this.query, filters: FilterList = this.appliedFilters) { + this.query = query + this.appliedFilters = filters + + subscribeToMangaInitializer() + + // Create a new pager. + pager = createPager(query, filters) + + val sourceId = source.id + + val catalogueAsList = prefs.catalogueAsList() + + // Prepare the pager. + pagerSubscription?.let { remove(it) } + pagerSubscription = pager.results() + .observeOn(Schedulers.io()) + .map { it.first to it.second.map { networkToLocalManga(it, sourceId) } } + .doOnNext { initializeMangas(it.second) } + .map { it.first to it.second.map { CatalogueItem(it, catalogueAsList) } } + .observeOn(AndroidSchedulers.mainThread()) + .subscribeReplay({ view, (page, mangas) -> + view.onAddPage(page, mangas) + }, { _, error -> + Timber.e(error) + }) + + // Request first page. + requestNext() + } + + /** + * Requests the next page for the active pager. + */ + fun requestNext() { + if (!hasNextPage()) return + + pageSubscription?.let { remove(it) } + pageSubscription = Observable.defer { pager.requestNext() } + .subscribeFirst({ _, _ -> + // Nothing to do when onNext is emitted. + }, BrowseCatalogueController::onAddPageError) + } + + /** + * Returns true if the last fetched page has a next page. + */ + fun hasNextPage(): Boolean { + return pager.hasNextPage + } + + /** + * Sets the display mode. + * + * @param asList whether the current mode is in list or not. + */ + private fun setDisplayMode(asList: Boolean) { + isListMode = asList + subscribeToMangaInitializer() + } + + /** + * Subscribes to the initializer of manga details and updates the view if needed. + */ + private fun subscribeToMangaInitializer() { + initializerSubscription?.let { remove(it) } + initializerSubscription = mangaDetailSubject.observeOn(Schedulers.io()) + .flatMap { Observable.from(it) } + .filter { it.thumbnail_url == null && !it.initialized } + .concatMap { getMangaDetailsObservable(it) } + .onBackpressureBuffer() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ manga -> + @Suppress("DEPRECATION") + view?.onMangaInitialized(manga) + }, { error -> + Timber.e(error) + }) + .apply { add(this) } + } + + /** + * Returns a manga from the database for the given manga from network. It creates a new entry + * if the manga is not yet in the database. + * + * @param sManga the manga from the source. + * @return a manga from the database. + */ + private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga { + 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).executeAsBlocking() + newManga.id = result.insertedId() + localManga = newManga + } + return localManga + } + + /** + * Initialize a list of manga. + * + * @param mangas the list of manga to initialize. + */ + fun initializeMangas(mangas: List) { + mangaDetailSubject.onNext(mangas) + } + + /** + * Returns an observable of manga that initializes the given manga. + * + * @param manga the manga to initialize. + * @return an observable of the manga to initialize + */ + private fun getMangaDetailsObservable(manga: Manga): Observable { + return source.fetchMangaDetails(manga) + .flatMap { networkManga -> + manga.copyFrom(networkManga) + manga.initialized = true + db.insertManga(manga).executeAsBlocking() + Observable.just(manga) + } + .onErrorResumeNext { Observable.just(manga) } + } + + /** + * Adds or removes a manga from the library. + * + * @param manga the manga to update. + */ + fun changeMangaFavorite(manga: Manga) { + manga.favorite = !manga.favorite + if (!manga.favorite) { + coverCache.deleteFromCache(manga.thumbnail_url) + } + db.insertManga(manga).executeAsBlocking() + } + + /** + * Changes the active display mode. + */ + fun swapDisplayMode() { + prefs.catalogueAsList().set(!isListMode) + } + + /** + * Set the filter states for the current source. + * + * @param filters a list of active filters. + */ + fun setSourceFilter(filters: FilterList) { + restartPager(filters = filters) + } + + open fun createPager(query: String, filters: FilterList): Pager { + return CataloguePager(source, query, filters) + } + + private fun FilterList.toItems(): List> { + return mapNotNull { + when (it) { + is Filter.Header -> HeaderItem(it) + is Filter.Separator -> SeparatorItem(it) + is Filter.CheckBox -> CheckboxItem(it) + is Filter.TriState -> TriStateItem(it) + is Filter.Text -> TextItem(it) + is Filter.Select<*> -> SelectItem(it) + is Filter.Group<*> -> { + val group = GroupItem(it) + val subItems = it.state.mapNotNull { + when (it) { + is Filter.CheckBox -> CheckboxSectionItem(it) + is Filter.TriState -> TriStateSectionItem(it) + is Filter.Text -> TextSectionItem(it) + is Filter.Select<*> -> SelectSectionItem(it) + else -> null + } as? ISectionable<*, *> + } + subItems.forEach { it.header = group } + group.subItems = subItems + group + } + is Filter.Sort -> { + val group = SortGroup(it) + val subItems = it.values.map { + SortItem(it, group) + } + group.subItems = subItems + group + } + } + } + } + + /** + * Get the default, and user categories. + * + * @return List of categories, default plus user categories + */ + fun getCategories(): List { + return db.getCategories().executeAsBlocking() + } + + /** + * Gets the category id's the manga is in, if the manga is not in a category, returns the default id. + * + * @param manga the manga to get categories from. + * @return Array of category ids the manga is in, if none returns default id + */ + fun getMangaCategoryIds(manga: Manga): Array { + val categories = db.getCategoriesForManga(manga).executeAsBlocking() + return categories.mapNotNull { it.id }.toTypedArray() + } + + /** + * Move the given manga to categories. + * + * @param categories the selected categories. + * @param manga the manga to move. + */ + private fun moveMangaToCategories(manga: Manga, categories: List) { + val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) } + db.setMangaCategories(mc, listOf(manga)) + } + + /** + * Move the given manga to the category. + * + * @param category the selected category. + * @param manga the manga to move. + */ + fun moveMangaToCategory(manga: Manga, category: Category?) { + moveMangaToCategories(manga, listOfNotNull(category)) + } + + /** + * Update manga to use selected categories. + * + * @param manga needed to change + * @param selectedCategories selected categories + */ + fun updateMangaCategories(manga: Manga, selectedCategories: List) { + if (!selectedCategories.isEmpty()) { + if (!manga.favorite) + changeMangaFavorite(manga) + + moveMangaToCategories(manga, selectedCategories.filter { it.id != 0 }) + } else { + changeMangaFavorite(manga) + } + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt similarity index 97% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt index 3fdba1e2e..6d3d2e746 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import android.view.View import com.bumptech.glide.load.engine.DiskCacheStrategy diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt similarity index 95% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt index 014b7904a..174bb8075 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt similarity index 97% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueItem.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt index 0a3209810..71696565b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import android.view.Gravity import android.view.View diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt similarity index 97% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt index a12ec77d2..928a304d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import android.view.View import com.bumptech.glide.load.engine.DiskCacheStrategy diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt similarity index 93% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt index c55727129..fd2d2685a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt @@ -1,40 +1,40 @@ -package eu.kanade.tachiyomi.ui.catalogue - -import android.content.Context -import android.util.AttributeSet -import android.view.ViewGroup -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.inflate -import eu.kanade.tachiyomi.widget.SimpleNavigationView -import kotlinx.android.synthetic.main.catalogue_drawer_content.view.* - - -class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) - : SimpleNavigationView(context, attrs) { - - val adapter: FlexibleAdapter> = FlexibleAdapter>(null) - .setDisplayHeadersAtStartUp(true) - .setStickyHeaders(true) - - var onSearchClicked = {} - - var onResetClicked = {} - - init { - recycler.adapter = adapter - recycler.setHasFixedSize(true) - val view = inflate(R.layout.catalogue_drawer_content) - ((view as ViewGroup).getChildAt(1) as ViewGroup).addView(recycler) - addView(view) - - search_btn.setOnClickListener { onSearchClicked() } - reset_btn.setOnClickListener { onResetClicked() } - } - - fun setFilters(items: List>) { - adapter.updateDataSet(items) - } - +package eu.kanade.tachiyomi.ui.catalogue.browse + +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.util.inflate +import eu.kanade.tachiyomi.widget.SimpleNavigationView +import kotlinx.android.synthetic.main.catalogue_drawer_content.view.* + + +class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) + : SimpleNavigationView(context, attrs) { + + val adapter: FlexibleAdapter> = FlexibleAdapter>(null) + .setDisplayHeadersAtStartUp(true) + .setStickyHeaders(true) + + var onSearchClicked = {} + + var onResetClicked = {} + + init { + recycler.adapter = adapter + recycler.setHasFixedSize(true) + val view = inflate(R.layout.catalogue_drawer_content) + ((view as ViewGroup).getChildAt(1) as ViewGroup).addView(recycler) + addView(view) + + search_btn.setOnClickListener { onSearchClicked() } + reset_btn.setOnClickListener { onResetClicked() } + } + + fun setFilters(items: List>) { + adapter.updateDataSet(items) + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CataloguePager.kt similarity index 95% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CataloguePager.kt index b95798a7d..a7b563074 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CataloguePager.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.FilterList diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/NoResultsException.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/NoResultsException.kt new file mode 100644 index 000000000..723782f5e --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/NoResultsException.kt @@ -0,0 +1,3 @@ +package eu.kanade.tachiyomi.ui.catalogue.browse + +class NoResultsException : Exception() \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/Pager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/Pager.kt similarity index 94% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/Pager.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/Pager.kt index a0f3d55e2..1383fdfcc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/Pager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/Pager.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import com.jakewharton.rxrelay.PublishRelay import eu.kanade.tachiyomi.source.model.MangasPage diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/ProgressItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt similarity index 96% rename from app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/ProgressItem.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt index 279017a74..0eeeb51d3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/ProgressItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.ui.catalogue +package eu.kanade.tachiyomi.ui.catalogue.browse import android.view.View import android.widget.ProgressBar diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt index ebd5327d6..fac3a5b6c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchPresenter.kt @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.LoginSource import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter +import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCataloguePresenter import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -67,7 +67,7 @@ class CatalogueSearchPresenter( super.onCreate(savedState) // Perform a search with previous or initial state - search(savedState?.getString(CataloguePresenter::query.name) ?: initialQuery.orEmpty()) + search(savedState?.getString(BrowseCataloguePresenter::query.name) ?: initialQuery.orEmpty()) } override fun onDestroy() { @@ -77,7 +77,7 @@ class CatalogueSearchPresenter( } override fun onSave(state: Bundle) { - state.putString(CataloguePresenter::query.name, query) + state.putString(BrowseCataloguePresenter::query.name, query) super.onSave(state) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt similarity index 69% rename from app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesController.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt index 072980607..221a2142e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt @@ -1,39 +1,39 @@ -package eu.kanade.tachiyomi.ui.latest_updates - -import android.os.Bundle -import android.support.v4.widget.DrawerLayout -import android.view.Menu -import android.view.ViewGroup -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.ui.catalogue.CatalogueController -import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter - -/** - * Controller that shows the latest manga from the catalogue. Inherit [CatalogueController]. - */ -class LatestUpdatesController(bundle: Bundle) : CatalogueController(bundle) { - - constructor(source: CatalogueSource) : this(Bundle().apply { - putLong(SOURCE_ID_KEY, source.id) - }) - - override fun createPresenter(): CataloguePresenter { - return LatestUpdatesPresenter(args.getLong(SOURCE_ID_KEY)) - } - - override fun onPrepareOptionsMenu(menu: Menu) { - super.onPrepareOptionsMenu(menu) - menu.findItem(R.id.action_search).isVisible = false - menu.findItem(R.id.action_set_filter).isVisible = false - } - - override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { - return null - } - - override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { - - } - -} +package eu.kanade.tachiyomi.ui.catalogue.latest + +import android.os.Bundle +import android.support.v4.widget.DrawerLayout +import android.view.Menu +import android.view.ViewGroup +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController +import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCataloguePresenter + +/** + * Controller that shows the latest manga from the catalogue. Inherit [BrowseCatalogueController]. + */ +class LatestUpdatesController(bundle: Bundle) : BrowseCatalogueController(bundle) { + + constructor(source: CatalogueSource) : this(Bundle().apply { + putLong(SOURCE_ID_KEY, source.id) + }) + + override fun createPresenter(): BrowseCataloguePresenter { + return LatestUpdatesPresenter(args.getLong(SOURCE_ID_KEY)) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + menu.findItem(R.id.action_search).isVisible = false + menu.findItem(R.id.action_set_filter).isVisible = false + } + + override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { + return null + } + + override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { + + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPager.kt similarity index 85% rename from app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPager.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPager.kt index 7cdcdff66..2e646638b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPager.kt @@ -1,8 +1,8 @@ -package eu.kanade.tachiyomi.ui.latest_updates +package eu.kanade.tachiyomi.ui.catalogue.latest import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.ui.catalogue.Pager +import eu.kanade.tachiyomi.ui.catalogue.browse.Pager import rx.Observable import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPresenter.kt new file mode 100644 index 000000000..a1be55797 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesPresenter.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.ui.catalogue.latest + +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCataloguePresenter +import eu.kanade.tachiyomi.ui.catalogue.browse.Pager + +/** + * Presenter of [LatestUpdatesController]. Inherit BrowseCataloguePresenter. + */ +class LatestUpdatesPresenter(sourceId: Long) : BrowseCataloguePresenter(sourceId) { + + override fun createPager(query: String, filters: FilterList): Pager { + return LatestUpdatesPager(source) + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt deleted file mode 100644 index f6aff8ab7..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainController.kt +++ /dev/null @@ -1,231 +0,0 @@ -package eu.kanade.tachiyomi.ui.catalogue.main - -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.SearchView -import android.view.* -import com.bluelinelabs.conductor.ControllerChangeHandler -import com.bluelinelabs.conductor.ControllerChangeType -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler -import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.source.online.LoginSource -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction -import eu.kanade.tachiyomi.ui.catalogue.CatalogueController -import eu.kanade.tachiyomi.ui.catalogue.global_search.CatalogueSearchController -import eu.kanade.tachiyomi.ui.latest_updates.LatestUpdatesController -import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController -import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog -import kotlinx.android.synthetic.main.catalogue_main_controller.* -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get - -/** - * This controller shows and manages the different catalogues enabled by the user. - * This controller should only handle UI actions, IO actions should be done by [CatalogueMainPresenter] - * [SourceLoginDialog.Listener] refreshes the adapter on successful login of catalogues. - * [CatalogueMainAdapter.OnBrowseClickListener] call function data on browse item click. - * [CatalogueMainAdapter.OnLatestClickListener] call function data on latest item click - */ -class CatalogueMainController : NucleusController(), - SourceLoginDialog.Listener, - FlexibleAdapter.OnItemClickListener, - CatalogueMainAdapter.OnBrowseClickListener, - CatalogueMainAdapter.OnLatestClickListener { - - /** - * Application preferences. - */ - private val preferences: PreferencesHelper = Injekt.get() - - /** - * Adapter containing sources. - */ - private var adapter : CatalogueMainAdapter? = null - - /** - * Called when controller is initialized. - */ - init { - // Enable the option menu - setHasOptionsMenu(true) - } - - /** - * Set the title of controller. - * - * @return title. - */ - override fun getTitle(): String? { - return applicationContext?.getString(R.string.label_catalogues) - } - - /** - * Create the [CatalogueMainPresenter] used in controller. - * - * @return instance of [CatalogueMainPresenter] - */ - override fun createPresenter(): CatalogueMainPresenter { - return CatalogueMainPresenter() - } - - /** - * Initiate the view with [R.layout.catalogue_main_controller]. - * - * @param inflater used to load the layout xml. - * @param container containing parent views. - * @return inflated view. - */ - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.catalogue_main_controller, container, false) - } - - /** - * Called when the view is created - * - * @param view view of controller - */ - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - adapter = CatalogueMainAdapter(this) - - // Create recycler and set adapter. - recycler.layoutManager = LinearLayoutManager(view.context) - recycler.adapter = adapter - recycler.addItemDecoration(SourceDividerItemDecoration(view.context)) - } - - override fun onDestroyView(view: View) { - adapter = null - super.onDestroyView(view) - } - - override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { - super.onChangeStarted(handler, type) - if (!type.isPush && handler is SettingsSourcesFadeChangeHandler) { - presenter.updateSources() - } - } - - /** - * Called when login dialog is closed, refreshes the adapter. - * - * @param source clicked item containing source information. - */ - override fun loginDialogClosed(source: LoginSource) { - if (source.isLogged()) { - adapter?.clear() - presenter.loadSources() - } - } - - /** - * Called when item is clicked - */ - override fun onItemClick(position: Int): Boolean { - val item = adapter?.getItem(position) as? SourceItem ?: return false - val source = item.source - if (source is LoginSource && !source.isLogged()) { - val dialog = SourceLoginDialog(source) - dialog.targetController = this - dialog.showDialog(router) - } else { - // Open the catalogue view. - openCatalogue(source, CatalogueController(source)) - } - return false - } - - /** - * Called when browse is clicked in [CatalogueMainAdapter] - */ - override fun onBrowseClick(position: Int) { - onItemClick(position) - } - - /** - * Called when latest is clicked in [CatalogueMainAdapter] - */ - override fun onLatestClick(position: Int) { - val item = adapter?.getItem(position) as? SourceItem ?: return - openCatalogue(item.source, LatestUpdatesController(item.source)) - } - - /** - * Opens a catalogue with the given controller. - */ - private fun openCatalogue(source: CatalogueSource, controller: CatalogueController) { - preferences.lastUsedCatalogueSource().set(source.id) - router.pushController(controller.withFadeTransaction()) - } - - /** - * 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.catalogue_main, menu) - - // Initialize search option. - val searchItem = menu.findItem(R.id.action_search) - val searchView = searchItem.actionView as SearchView - - // Change hint to show global search. - searchView.queryHint = applicationContext?.getString(R.string.action_global_search_hint) - - // Create query listener which opens the global search view. - searchView.queryTextChangeEvents() - .filter { it.isSubmitted } - .subscribeUntilDestroy { - val query = it.queryText().toString() - router.pushController(CatalogueSearchController(query).withFadeTransaction()) - } - } - - /** - * Called when an option menu item has been selected by the user. - * - * @param item The selected item. - * @return True if this event has been consumed, false if it has not. - */ - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - // Initialize option to open catalogue settings. - R.id.action_settings -> { - router.pushController((RouterTransaction.with(SettingsSourcesController())) - .popChangeHandler(SettingsSourcesFadeChangeHandler()) - .pushChangeHandler(FadeChangeHandler())) - } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - /** - * Called to update adapter containing sources. - */ - fun setSources(sources: List>) { - adapter?.updateDataSet(sources) - } - - /** - * Called to set the last used catalogue at the top of the view. - */ - fun setLastUsedSource(item: SourceItem?) { - adapter?.removeAllScrollableHeaders() - if (item != null) { - adapter?.addScrollableHeader(item) - } - } - - class SettingsSourcesFadeChangeHandler : FadeChangeHandler() -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainPresenter.kt deleted file mode 100644 index a0745a26a..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/main/CatalogueMainPresenter.kt +++ /dev/null @@ -1,104 +0,0 @@ -package eu.kanade.tachiyomi.ui.catalogue.main - -import android.os.Bundle -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.getOrDefault -import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.source.LocalSource -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import rx.Observable -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import java.util.* -import java.util.concurrent.TimeUnit - -/** - * Presenter of [CatalogueMainController] - * Function calls should be done from here. UI calls should be done from the controller. - * - * @param sourceManager manages the different sources. - * @param preferences application preferences. - */ -class CatalogueMainPresenter( - val sourceManager: SourceManager = Injekt.get(), - private val preferences: PreferencesHelper = Injekt.get() -) : BasePresenter() { - - /** - * Enabled sources. - */ - var sources = getEnabledSources() - - /** - * Subscription for retrieving enabled sources. - */ - private var sourceSubscription: Subscription? = null - - override fun onCreate(savedState: Bundle?) { - super.onCreate(savedState) - - // Load enabled and last used sources - loadSources() - loadLastUsedSource() - } - - /** - * Unsubscribe and create a new subscription to fetch enabled sources. - */ - fun loadSources() { - sourceSubscription?.unsubscribe() - - val map = TreeMap> { d1, d2 -> - // Catalogues without a lang defined will be placed at the end - when { - d1 == "" && d2 != "" -> 1 - d2 == "" && d1 != "" -> -1 - else -> d1.compareTo(d2) - } - } - val byLang = sources.groupByTo(map, { it.lang }) - val sourceItems = byLang.flatMap { - val langItem = LangItem(it.key) - it.value.map { source -> SourceItem(source, langItem) } - } - - sourceSubscription = Observable.just(sourceItems) - .subscribeLatestCache(CatalogueMainController::setSources) - } - - private fun loadLastUsedSource() { - val sharedObs = preferences.lastUsedCatalogueSource().asObservable().share() - - // Emit the first item immediately but delay subsequent emissions by 500ms. - Observable.merge( - sharedObs.take(1), - sharedObs.skip(1).delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())) - .distinctUntilChanged() - .map { (sourceManager.get(it) as? CatalogueSource)?.let { SourceItem(it) } } - .subscribeLatestCache(CatalogueMainController::setLastUsedSource) - } - - fun updateSources() { - sources = getEnabledSources() - loadSources() - } - - /** - * Returns a list of enabled sources ordered by language and name. - * - * @return list containing enabled sources. - */ - private fun getEnabledSources(): List { - val languages = preferences.enabledLanguages().getOrDefault() - val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault() - - return sourceManager.getCatalogueSources() - .filter { it.lang in languages } - .filterNot { it.id.toString() in hiddenCatalogues } - .sortedBy { "(${it.lang}) ${it.name}" } + - sourceManager.get(LocalSource.ID) as LocalSource - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt deleted file mode 100644 index 2e0ea07fe..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/latest_updates/LatestUpdatesPresenter.kt +++ /dev/null @@ -1,16 +0,0 @@ -package eu.kanade.tachiyomi.ui.latest_updates - -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter -import eu.kanade.tachiyomi.ui.catalogue.Pager - -/** - * Presenter of [LatestUpdatesController]. Inherit CataloguePresenter. - */ -class LatestUpdatesPresenter(sourceId: Long) : CataloguePresenter(sourceId) { - - override fun createPager(query: String, filters: FilterList): Pager { - return LatestUpdatesPager(source) - } - -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 77b4cc016..53c557948 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -9,13 +9,12 @@ import android.support.v4.widget.DrawerLayout import android.support.v7.graphics.drawable.DrawerArrowDrawable import android.view.ViewGroup import com.bluelinelabs.conductor.* -import com.bluelinelabs.conductor.changehandler.FadeChangeHandler import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.base.controller.* -import eu.kanade.tachiyomi.ui.catalogue.main.CatalogueMainController +import eu.kanade.tachiyomi.ui.catalogue.CatalogueController import eu.kanade.tachiyomi.ui.download.DownloadController import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.manga.MangaController @@ -80,7 +79,7 @@ class MainActivity : BaseActivity() { R.id.nav_drawer_library -> setRoot(LibraryController(), id) R.id.nav_drawer_recent_updates -> setRoot(RecentChaptersController(), id) R.id.nav_drawer_recently_read -> setRoot(RecentlyReadController(), id) - R.id.nav_drawer_catalogues -> setRoot(CatalogueMainController(), id) + R.id.nav_drawer_catalogues -> setRoot(CatalogueController(), id) R.id.nav_drawer_downloads -> { router.pushController(DownloadController().withFadeTransaction()) } diff --git a/app/src/main/res/layout-land/manga_info_controller.xml b/app/src/main/res/layout-land/manga_info_controller.xml index fa49622b5..ad371aaa8 100644 --- a/app/src/main/res/layout-land/manga_info_controller.xml +++ b/app/src/main/res/layout-land/manga_info_controller.xml @@ -3,7 +3,7 @@ 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" - tools:context="eu.kanade.tachiyomi.ui.catalogue.CatalogueController" + tools:context="eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController" android:id="@id/swipe_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> diff --git a/app/src/main/res/layout/catalogue_controller.xml b/app/src/main/res/layout/catalogue_controller.xml index 17ba20e10..929ea2275 100644 --- a/app/src/main/res/layout/catalogue_controller.xml +++ b/app/src/main/res/layout/catalogue_controller.xml @@ -11,7 +11,7 @@ android:fitsSystemWindows="true" android:orientation="vertical" android:id="@+id/catalogue_view" - tools:context="eu.kanade.tachiyomi.ui.catalogue.CatalogueController"> + tools:context="eu.kanade.tachiyomi.ui.catalogue.browse.BrowseCatalogueController"> - From d94dc688301409ea306135766904e19083d8e7a3 Mon Sep 17 00:00:00 2001 From: inorichi Date: Sun, 3 Dec 2017 12:59:51 +0100 Subject: [PATCH 07/13] Fix library not being updated --- .../kanade/tachiyomi/ui/library/LibraryAdapter.kt | 15 +++++++++++++++ .../tachiyomi/ui/library/LibraryCategoryView.kt | 5 ++--- .../tachiyomi/ui/library/LibraryController.kt | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt index 247b113ba..1557a0edd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryAdapter.kt @@ -26,6 +26,8 @@ class LibraryAdapter(private val controller: LibraryController) : RecyclerViewPa } } + private var boundViews = arrayListOf() + /** * Creates a new view for this adapter. * @@ -45,6 +47,7 @@ class LibraryAdapter(private val controller: LibraryController) : RecyclerViewPa */ override fun bindView(view: View, position: Int) { (view as LibraryCategoryView).onBind(categories[position]) + boundViews.add(view) } /** @@ -55,6 +58,7 @@ class LibraryAdapter(private val controller: LibraryController) : RecyclerViewPa */ override fun recycleView(view: View, position: Int) { (view as LibraryCategoryView).onRecycle() + boundViews.remove(view) } /** @@ -85,4 +89,15 @@ class LibraryAdapter(private val controller: LibraryController) : RecyclerViewPa return if (index == -1) POSITION_NONE else index } + /** + * Called when the view of this adapter is being destroyed. + */ + fun onDestroy() { + for (view in boundViews) { + if (view is LibraryCategoryView) { + view.unsubscribe() + } + } + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index dc1419b08..f2969dbce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -124,12 +124,11 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att fun onRecycle() { adapter.setItems(emptyList()) adapter.clearSelection() - subscriptions.clear() + unsubscribe() } - override fun onDetachedFromWindow() { + fun unsubscribe() { subscriptions.clear() - super.onDetachedFromWindow() } /** 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 2b65b0d23..b699069b7 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 @@ -164,6 +164,7 @@ class LibraryController( } override fun onDestroyView(view: View) { + adapter?.onDestroy() adapter = null actionMode = null tabsVisibilitySubscription?.unsubscribe() From 8bcb14c65d039bb9d7254e0876a0a425e07cfbf9 Mon Sep 17 00:00:00 2001 From: inorichi Date: Sun, 3 Dec 2017 17:00:32 +0100 Subject: [PATCH 08/13] Add view caching to view holders --- .../ui/base/holder/BaseFlexibleViewHolder.kt | 15 +++++ .../ui/base/holder/BaseViewHolder.kt | 11 ++++ .../tachiyomi/ui/catalogue/LangHolder.kt | 9 +-- .../tachiyomi/ui/catalogue/SourceHolder.kt | 44 +++++++-------- .../catalogue/browse/CatalogueGridHolder.kt | 10 ++-- .../ui/catalogue/browse/CatalogueHolder.kt | 4 +- .../catalogue/browse/CatalogueListHolder.kt | 10 ++-- .../CatalogueSearchCardHolder.kt | 12 ++-- .../global_search/CatalogueSearchHolder.kt | 55 +++++++++---------- .../tachiyomi/ui/category/CategoryHolder.kt | 18 +++--- .../tachiyomi/ui/download/DownloadHolder.kt | 4 +- .../tachiyomi/ui/library/LibraryGridHolder.kt | 16 +++--- .../tachiyomi/ui/library/LibraryHolder.kt | 4 +- .../tachiyomi/ui/library/LibraryListHolder.kt | 18 +++--- .../ui/manga/chapter/ChapterHolder.kt | 22 ++++---- .../tachiyomi/ui/manga/track/TrackHolder.kt | 20 +++---- .../ui/reader/viewer/webtoon/WebtoonHolder.kt | 24 ++++---- .../ui/recent_updates/RecentChapterHolder.kt | 30 +++++----- .../ui/recently_read/RecentlyReadHolder.kt | 22 ++++---- .../ui/setting/SettingsAboutController.kt | 4 +- .../ui/setting/SettingsSourcesController.kt | 2 - 21 files changed, 183 insertions(+), 171 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseFlexibleViewHolder.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseFlexibleViewHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseFlexibleViewHolder.kt new file mode 100644 index 000000000..3cc6b9c2a --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseFlexibleViewHolder.kt @@ -0,0 +1,15 @@ +package eu.kanade.tachiyomi.ui.base.holder + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.viewholders.FlexibleViewHolder +import kotlinx.android.extensions.LayoutContainer + +abstract class BaseFlexibleViewHolder(view: View, + adapter: FlexibleAdapter<*>, + stickyHeader: Boolean = false) : + FlexibleViewHolder(view, adapter, stickyHeader), LayoutContainer { + + override val containerView: View? + get() = itemView +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt new file mode 100644 index 000000000..c809b7eeb --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.ui.base.holder + +import android.support.v7.widget.RecyclerView +import android.view.View +import kotlinx.android.extensions.LayoutContainer + +abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer { + + override val containerView: View? + get() = itemView +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt index ea1fe6f0a..0c7fad877 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/LangHolder.kt @@ -2,15 +2,16 @@ package eu.kanade.tachiyomi.ui.catalogue import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R -import kotlinx.android.synthetic.main.catalogue_main_controller_card.view.* +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder +import kotlinx.android.synthetic.main.catalogue_main_controller_card.* import java.util.* -class LangHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter, true) { +class LangHolder(view: View, adapter: FlexibleAdapter<*>) : + BaseFlexibleViewHolder(view, adapter, true) { fun bind(item: LangItem) { - itemView.title.text = when { + title.text = when { item.code == "" -> itemView.context.getString(R.string.other_source) else -> { val locale = Locale(item.code) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt index e361b7b9c..5a052d0e9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceHolder.kt @@ -3,53 +3,51 @@ package eu.kanade.tachiyomi.ui.catalogue import android.os.Build import android.view.View import android.view.ViewGroup -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.online.LoginSource +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.dpToPx import eu.kanade.tachiyomi.util.getRound import eu.kanade.tachiyomi.util.gone import eu.kanade.tachiyomi.util.visible import io.github.mthli.slice.Slice -import kotlinx.android.synthetic.main.catalogue_main_controller_card_item.view.* +import kotlinx.android.synthetic.main.catalogue_main_controller_card_item.* -class SourceHolder(view: View, adapter: CatalogueAdapter) : FlexibleViewHolder(view, adapter) { +class SourceHolder(view: View, adapter: CatalogueAdapter) : BaseFlexibleViewHolder(view, adapter) { - private val slice = Slice(itemView.card).apply { + private val slice = Slice(card).apply { setColor(adapter.cardBackground) } init { - itemView.source_browse.setOnClickListener { + source_browse.setOnClickListener { adapter.browseClickListener.onBrowseClick(adapterPosition) } - itemView.source_latest.setOnClickListener { + source_latest.setOnClickListener { adapter.latestClickListener.onLatestClick(adapterPosition) } } fun bind(item: SourceItem) { val source = item.source - with(itemView) { - setCardEdges(item) + setCardEdges(item) - // Set source name - title.text = source.name + // Set source name + title.text = source.name - // Set circle letter image. - post { - image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(),false)) - } + // Set circle letter image. + itemView.post { + image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(),false)) + } - // If source is login, show only login option - if (source is LoginSource && !source.isLogged()) { - source_browse.setText(R.string.login) - source_latest.gone() - } else { - source_browse.setText(R.string.browse) - source_latest.visible() - } + // If source is login, show only login option + if (source is LoginSource && !source.isLogged()) { + source_browse.setText(R.string.login) + source_latest.gone() + } else { + source_browse.setText(R.string.browse) + source_latest.visible() } } @@ -94,7 +92,7 @@ class SourceHolder(view: View, adapter: CatalogueAdapter) : FlexibleViewHolder(v } private fun setMargins(left: Int, top: Int, right: Int, bottom: Int) { - val v = itemView.card + val v = card if (v.layoutParams is ViewGroup.MarginLayoutParams) { val p = v.layoutParams as ViewGroup.MarginLayoutParams p.setMargins(left, top, right, bottom) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt index 6d3d2e746..c2ef105fe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueGridHolder.kt @@ -6,7 +6,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.widget.StateImageViewTarget -import kotlinx.android.synthetic.main.catalogue_grid_item.view.* +import kotlinx.android.synthetic.main.catalogue_grid_item.* /** * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title. @@ -27,16 +27,16 @@ class CatalogueGridHolder(private val view: View, private val adapter: FlexibleA */ override fun onSetValues(manga: Manga) { // Set manga title - view.title.text = manga.title + title.text = manga.title // Set alpha of thumbnail. - view.thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f + thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f setImage(manga) } override fun setImage(manga: Manga) { - GlideApp.with(view.context).clear(view.thumbnail) + GlideApp.with(view.context).clear(thumbnail) if (!manga.thumbnail_url.isNullOrEmpty()) { GlideApp.with(view.context) .load(manga) @@ -44,7 +44,7 @@ class CatalogueGridHolder(private val view: View, private val adapter: FlexibleA .centerCrop() .skipMemoryCache(true) .placeholder(android.R.color.transparent) - .into(StateImageViewTarget(view.thumbnail, view.progress)) + .into(StateImageViewTarget(thumbnail, progress)) } } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt index 174bb8075..9a0ec3916 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueHolder.kt @@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.catalogue.browse import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder /** * Generic class used to hold the displayed data of a manga in the catalogue. @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga * @param adapter the adapter handling this holder. */ abstract class CatalogueHolder(view: View, adapter: FlexibleAdapter<*>) : - FlexibleViewHolder(view, adapter) { + BaseFlexibleViewHolder(view, adapter) { /** * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt index 928a304d9..c0bb0f0ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueListHolder.kt @@ -6,7 +6,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.util.getResourceColor -import kotlinx.android.synthetic.main.catalogue_list_item.view.* +import kotlinx.android.synthetic.main.catalogue_list_item.* /** * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title. @@ -29,14 +29,14 @@ class CatalogueListHolder(private val view: View, adapter: FlexibleAdapter<*>) : * @param manga the manga to bind. */ override fun onSetValues(manga: Manga) { - view.title.text = manga.title - view.title.setTextColor(if (manga.favorite) favoriteColor else unfavoriteColor) + title.text = manga.title + title.setTextColor(if (manga.favorite) favoriteColor else unfavoriteColor) setImage(manga) } override fun setImage(manga: Manga) { - GlideApp.with(view.context).clear(view.thumbnail) + GlideApp.with(view.context).clear(thumbnail) if (!manga.thumbnail_url.isNullOrEmpty()) { GlideApp.with(view.context) .load(manga) @@ -46,7 +46,7 @@ class CatalogueListHolder(private val view: View, adapter: FlexibleAdapter<*>) : .dontAnimate() .skipMemoryCache(true) .placeholder(android.R.color.transparent) - .into(view.thumbnail) + .into(thumbnail) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt index e35a2ceb7..b67e89394 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardHolder.kt @@ -2,14 +2,14 @@ package eu.kanade.tachiyomi.ui.catalogue.global_search import android.view.View import com.bumptech.glide.load.engine.DiskCacheStrategy -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.widget.StateImageViewTarget -import kotlinx.android.synthetic.main.catalogue_global_search_controller_card_item.view.* +import kotlinx.android.synthetic.main.catalogue_global_search_controller_card_item.* class CatalogueSearchCardHolder(view: View, adapter: CatalogueSearchCardAdapter) - : FlexibleViewHolder(view, adapter) { + : BaseFlexibleViewHolder(view, adapter) { init { // Call onMangaClickListener when item is pressed. @@ -22,13 +22,13 @@ class CatalogueSearchCardHolder(view: View, adapter: CatalogueSearchCardAdapter) } fun bind(manga: Manga) { - itemView.tvTitle.text = manga.title + tvTitle.text = manga.title setImage(manga) } fun setImage(manga: Manga) { - GlideApp.with(itemView.context).clear(itemView.itemImage) + GlideApp.with(itemView.context).clear(itemImage) if (!manga.thumbnail_url.isNullOrEmpty()) { GlideApp.with(itemView.context) .load(manga) @@ -36,7 +36,7 @@ class CatalogueSearchCardHolder(view: View, adapter: CatalogueSearchCardAdapter) .centerCrop() .skipMemoryCache(true) .placeholder(android.R.color.transparent) - .into(StateImageViewTarget(itemView.itemImage, itemView.progress)) + .into(StateImageViewTarget(itemImage, progress)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt index 0714c4342..b0c06cbbe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt @@ -2,14 +2,14 @@ package eu.kanade.tachiyomi.ui.catalogue.global_search import android.support.v7.widget.LinearLayoutManager import android.view.View -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.util.gone import eu.kanade.tachiyomi.util.setVectorCompat import eu.kanade.tachiyomi.util.visible -import kotlinx.android.synthetic.main.catalogue_global_search_controller_card.view.* +import kotlinx.android.synthetic.main.catalogue_global_search_controller_card.* /** * Holder that binds the [CatalogueSearchItem] containing catalogue cards. @@ -17,7 +17,8 @@ import kotlinx.android.synthetic.main.catalogue_global_search_controller_card.vi * @param view view of [CatalogueSearchItem] * @param adapter instance of [CatalogueSearchAdapter] */ -class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : FlexibleViewHolder(view, adapter) { +class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : + BaseFlexibleViewHolder(view, adapter) { /** * Adapter containing manga from search results. @@ -27,14 +28,12 @@ class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : F private var lastBoundResults: List? = null init { - with(itemView) { - // Set layout horizontal. - recycler.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - recycler.adapter = mangaAdapter + // Set layout horizontal. + recycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false) + recycler.adapter = mangaAdapter - nothing_found_icon.setVectorCompat(R.drawable.ic_search_black_112dp, - context.getResourceColor(android.R.attr.textColorHint)) - } + nothing_found_icon.setVectorCompat(R.drawable.ic_search_black_112dp, + view.context.getResourceColor(android.R.attr.textColorHint)) } /** @@ -46,28 +45,26 @@ class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : F val source = item.source val results = item.results - with(itemView) { - // Set Title witch country code if available. - title.text = if (!source.lang.isEmpty()) "${source.name} (${source.lang})" else source.name + // Set Title witch country code if available. + title.text = if (!source.lang.isEmpty()) "${source.name} (${source.lang})" else source.name - when { - results == null -> { - progress.visible() - nothing_found.gone() - } - results.isEmpty() -> { - progress.gone() - nothing_found.visible() - } - else -> { - progress.gone() - nothing_found.gone() - } + when { + results == null -> { + progress.visible() + nothing_found.gone() } - if (results !== lastBoundResults) { - mangaAdapter.updateDataSet(results) - lastBoundResults = results + results.isEmpty() -> { + progress.gone() + nothing_found.visible() } + else -> { + progress.gone() + nothing_found.gone() + } + } + if (results !== lastBoundResults) { + mangaAdapter.updateDataSet(results) + lastBoundResults = results } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryHolder.kt index 0dd3c1fa7..b37df4022 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryHolder.kt @@ -1,14 +1,10 @@ package eu.kanade.tachiyomi.ui.category -import android.graphics.Color -import android.graphics.Typeface import android.view.View -import com.amulyakhare.textdrawable.TextDrawable -import com.amulyakhare.textdrawable.util.ColorGenerator -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.data.database.models.Category +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.getRound -import kotlinx.android.synthetic.main.categories_item.view.* +import kotlinx.android.synthetic.main.categories_item.* /** * Holder used to display category items. @@ -16,16 +12,16 @@ import kotlinx.android.synthetic.main.categories_item.view.* * @param view The view used by category items. * @param adapter The adapter containing this holder. */ -class CategoryHolder(view: View, val adapter: CategoryAdapter) : FlexibleViewHolder(view, adapter) { +class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleViewHolder(view, adapter) { init { // Create round letter image onclick to simulate long click - itemView.image.setOnClickListener { + image.setOnClickListener { // Simulate long click on this view to enter selection mode onLongClick(view) } - setDragHandleView(itemView.reorder) + setDragHandleView(reorder) } /** @@ -35,11 +31,11 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : FlexibleViewHol */ fun bind(category: Category) { // Set capitalized title. - itemView.title.text = category.name.capitalize() + title.text = category.name.capitalize() // Update circle letter image. itemView.post { - itemView.image.setImageDrawable(itemView.image.getRound(category.name.take(1).toUpperCase(),false)) + image.setImageDrawable(image.getRound(category.name.take(1).toUpperCase(),false)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt index 39211c01c..fefc804a0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.ui.download -import android.support.v7.widget.RecyclerView import android.view.View import eu.kanade.tachiyomi.data.download.model.Download +import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder import kotlinx.android.synthetic.main.download_item.view.* /** @@ -12,7 +12,7 @@ import kotlinx.android.synthetic.main.download_item.view.* * @param view the inflated view for this holder. * @constructor creates a new download holder. */ -class DownloadHolder(private val view: View) : RecyclerView.ViewHolder(view) { +class DownloadHolder(private val view: View) : BaseViewHolder(view) { private lateinit var download: Download diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt index 429e80e98..2bc68cf3d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt @@ -5,7 +5,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.source.LocalSource -import kotlinx.android.synthetic.main.catalogue_grid_item.view.* +import kotlinx.android.synthetic.main.catalogue_grid_item.* /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -30,30 +30,28 @@ class LibraryGridHolder( */ override fun onSetValues(item: LibraryItem) { // Update the title of the manga. - view.title.text = item.manga.title + title.text = item.manga.title // Update the unread count and its visibility. - with(view.unread_text) { + with(unread_text) { visibility = if (item.manga.unread > 0) View.VISIBLE else View.GONE text = item.manga.unread.toString() } // Update the download count and its visibility. - with(view.download_text) { + with(download_text) { visibility = if (item.downloadCount > 0) View.VISIBLE else View.GONE text = item.downloadCount.toString() } //set local visibility if its local manga - with(view.local_text) { - visibility = if(item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE - } + local_text.visibility = if(item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE // Update the cover. - GlideApp.with(view.context).clear(view.thumbnail) + GlideApp.with(view.context).clear(thumbnail) GlideApp.with(view.context) .load(item.manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() - .into(view.thumbnail) + .into(thumbnail) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt index ff29155c4..41d7f9879 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.viewholders.FlexibleViewHolder +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder /** * Generic class used to hold the displayed data of a manga in the library. @@ -14,7 +14,7 @@ import eu.davidea.viewholders.FlexibleViewHolder abstract class LibraryHolder( view: View, adapter: FlexibleAdapter<*> -) : FlexibleViewHolder(view, adapter) { +) : BaseFlexibleViewHolder(view, adapter) { /** * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt index 37a23fe37..83cc69e25 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -5,7 +5,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.source.LocalSource -import kotlinx.android.synthetic.main.catalogue_list_item.view.* +import kotlinx.android.synthetic.main.catalogue_list_item.* /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -30,38 +30,36 @@ class LibraryListHolder( */ override fun onSetValues(item: LibraryItem) { // Update the title of the manga. - itemView.title.text = item.manga.title + title.text = item.manga.title // Update the unread count and its visibility. - with(itemView.unread_text) { + with(unread_text) { visibility = if (item.manga.unread > 0) View.VISIBLE else View.GONE text = item.manga.unread.toString() } // Update the download count and its visibility. - with(itemView.download_text) { + with(download_text) { visibility = if (item.downloadCount > 0) View.VISIBLE else View.GONE text = "${item.downloadCount}" } //show local text badge if local manga - with(itemView.local_text) { - visibility = if (item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE - } + local_text.visibility = if (item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE // Create thumbnail onclick to simulate long click - itemView.thumbnail.setOnClickListener { + thumbnail.setOnClickListener { // Simulate long click on this view to enter selection mode onLongClick(itemView) } // Update the cover. - GlideApp.with(itemView.context).clear(itemView.thumbnail) + GlideApp.with(itemView.context).clear(thumbnail) GlideApp.with(itemView.context) .load(item.manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() .circleCrop() .dontAnimate() - .into(itemView.thumbnail) + .into(thumbnail) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt index 936763bf7..5be199cae 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterHolder.kt @@ -2,41 +2,41 @@ package eu.kanade.tachiyomi.ui.manga.chapter import android.view.View import android.widget.PopupMenu -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.download.model.Download +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.util.gone import eu.kanade.tachiyomi.util.setVectorCompat -import kotlinx.android.synthetic.main.chapters_item.view.* +import kotlinx.android.synthetic.main.chapters_item.* import java.util.* class ChapterHolder( private val view: View, private val adapter: ChaptersAdapter -) : FlexibleViewHolder(view, adapter) { +) : BaseFlexibleViewHolder(view, adapter) { init { // We need to post a Runnable to show the popup to make sure that the PopupMenu is // correctly positioned. The reason being that the view may change position before the // PopupMenu is shown. - view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } } + chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } } } - fun bind(item: ChapterItem, manga: Manga) = with(view) { + fun bind(item: ChapterItem, manga: Manga) { val chapter = item.chapter chapter_title.text = when (manga.displayMode) { Manga.DISPLAY_NUMBER -> { val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble()) - context.getString(R.string.display_mode_chapter, number) + itemView.context.getString(R.string.display_mode_chapter, number) } else -> chapter.name } // Set the correct drawable for dropdown and update the tint to match theme. - view.chapter_menu.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color)) + chapter_menu.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color)) // Set correct text color chapter_title.setTextColor(if (chapter.read) adapter.readColor else adapter.unreadColor) @@ -53,14 +53,14 @@ class ChapterHolder( chapter_scanlator.text = chapter.scanlator //allow longer titles if there is no scanlator (most sources) if (chapter_scanlator.text.isNullOrBlank()) { - chapter_title.setMaxLines(2) + chapter_title.maxLines = 2 chapter_scanlator.gone() } else { - chapter_title.setMaxLines(1) + chapter_title.maxLines = 1 } chapter_pages.text = if (!chapter.read && chapter.last_page_read > 0) { - context.getString(R.string.chapter_progress, chapter.last_page_read + 1) + itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1) } else { "" } @@ -68,7 +68,7 @@ class ChapterHolder( notifyStatus(item.status) } - fun notifyStatus(status: Int) = with(view.download_text) { + fun notifyStatus(status: Int) = with(download_text) { when (status) { Download.QUEUE -> setText(R.string.chapter_queued) Download.DOWNLOADING -> setText(R.string.chapter_downloading) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt index e5972f296..2996bc929 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackHolder.kt @@ -1,29 +1,29 @@ package eu.kanade.tachiyomi.ui.manga.track import android.annotation.SuppressLint -import android.support.v7.widget.RecyclerView import android.view.View import eu.kanade.tachiyomi.R -import kotlinx.android.synthetic.main.track_item.view.* +import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder +import kotlinx.android.synthetic.main.track_item.* -class TrackHolder(view: View, adapter: TrackAdapter) : RecyclerView.ViewHolder(view) { +class TrackHolder(view: View, adapter: TrackAdapter) : BaseViewHolder(view) { init { val listener = adapter.rowClickListener - view.title_container.setOnClickListener { listener.onTitleClick(adapterPosition) } - view.status_container.setOnClickListener { listener.onStatusClick(adapterPosition) } - view.chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) } - view.score_container.setOnClickListener { listener.onScoreClick(adapterPosition) } + title_container.setOnClickListener { listener.onTitleClick(adapterPosition) } + status_container.setOnClickListener { listener.onStatusClick(adapterPosition) } + chapters_container.setOnClickListener { listener.onChaptersClick(adapterPosition) } + score_container.setOnClickListener { listener.onScoreClick(adapterPosition) } } @SuppressLint("SetTextI18n") @Suppress("DEPRECATION") - fun bind(item: TrackItem) = with(itemView) { + fun bind(item: TrackItem) { val track = item.track track_logo.setImageResource(item.service.getLogo()) logo.setBackgroundColor(item.service.getLogoColor()) if (track != null) { - track_title.setTextAppearance(context, R.style.TextAppearance_Regular_Body1_Secondary) + track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Regular_Body1_Secondary) track_title.setAllCaps(false) track_title.text = track.title track_chapters.text = "${track.last_chapter_read}/" + @@ -31,7 +31,7 @@ class TrackHolder(view: View, adapter: TrackAdapter) : RecyclerView.ViewHolder(v track_status.text = item.service.getStatus(track.status) track_score.text = if (track.score == 0f) "-" else item.service.displayScore(track) } else { - track_title.setTextAppearance(context, R.style.TextAppearance_Medium_Button) + track_title.setTextAppearance(itemView.context, R.style.TextAppearance_Medium_Button) track_title.setText(R.string.action_edit) track_chapters.text = "" track_score.text = "" diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonHolder.kt index 39958bd98..dedccad24 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonHolder.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon -import android.support.v7.widget.RecyclerView import android.view.MotionEvent import android.view.View import android.view.ViewGroup @@ -11,10 +10,11 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.viewer.base.PageDecodeErrorLayout import eu.kanade.tachiyomi.util.inflate -import kotlinx.android.synthetic.main.reader_webtoon_item.view.* +import kotlinx.android.synthetic.main.reader_webtoon_item.* import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -31,7 +31,7 @@ import java.util.concurrent.TimeUnit * @constructor creates a new webtoon holder. */ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) : - RecyclerView.ViewHolder(view) { + BaseViewHolder(view) { /** * Page of a chapter. @@ -54,7 +54,7 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) private var decodeErrorLayout: View? = null init { - with(view.image_view) { + with(image_view) { setMaxTileSize(readerActivity.maxBitmapSize) setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_FIXED) setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE) @@ -78,11 +78,11 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) }) } - view.progress_container.layoutParams = FrameLayout.LayoutParams( + progress_container.layoutParams = FrameLayout.LayoutParams( MATCH_PARENT, webtoonReader.screenHeight) view.setOnTouchListener(adapter.touchListener) - view.retry_button.setOnTouchListener { _, event -> + retry_button.setOnTouchListener { _, event -> if (event.action == MotionEvent.ACTION_UP) { readerActivity.presenter.retryPage(page) } @@ -111,9 +111,9 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) (view as ViewGroup).removeView(it) decodeErrorLayout = null } - view.image_view.recycle() - view.image_view.visibility = View.GONE - view.progress_container.visibility = View.VISIBLE + image_view.recycle() + image_view.visibility = View.GONE + progress_container.visibility = View.VISIBLE } /** @@ -150,7 +150,7 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) .onBackpressureLatest() .observeOn(AndroidSchedulers.mainThread()) .subscribe { progress -> - view.progress_text.text = if (progress > 0) { + progress_text.text = if (progress > 0) { view.context.getString(R.string.download_progress, progress) } else { view.context.getString(R.string.downloading) @@ -279,14 +279,14 @@ class WebtoonHolder(private val view: View, private val adapter: WebtoonAdapter) * Called when the image is decoded and going to be displayed. */ private fun onImageDecoded() { - view.progress_container.visibility = View.GONE + progress_container.visibility = View.GONE } /** * Called when the image fails to decode. */ private fun onImageDecodeError() { - view.progress_container.visibility = View.GONE + progress_container.visibility = View.GONE val page = page ?: return if (decodeErrorLayout != null || !webtoonReader.isAdded) return diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt index 37a72f537..4a5f139d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterHolder.kt @@ -3,13 +3,13 @@ package eu.kanade.tachiyomi.ui.recent_updates import android.view.View import android.widget.PopupMenu import com.bumptech.glide.load.engine.DiskCacheStrategy -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.util.setVectorCompat -import kotlinx.android.synthetic.main.recent_chapters_item.view.* +import kotlinx.android.synthetic.main.recent_chapters_item.* /** * Holder that contains chapter item @@ -22,7 +22,7 @@ import kotlinx.android.synthetic.main.recent_chapters_item.view.* * @constructor creates a new recent chapter holder. */ class RecentChapterHolder(private val view: View, private val adapter: RecentChaptersAdapter) : - FlexibleViewHolder(view, adapter) { + BaseFlexibleViewHolder(view, adapter) { /** * Color of read chapter @@ -43,8 +43,8 @@ class RecentChapterHolder(private val view: View, private val adapter: RecentCha // We need to post a Runnable to show the popup to make sure that the PopupMenu is // correctly positioned. The reason being that the view may change position before the // PopupMenu is shown. - view.chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } } - view.manga_cover.setOnClickListener { + chapter_menu.setOnClickListener { it.post { showPopupMenu(it) } } + manga_cover.setOnClickListener { adapter.coverClickListener.onCoverClick(adapterPosition) } } @@ -58,31 +58,31 @@ class RecentChapterHolder(private val view: View, private val adapter: RecentCha this.item = item // Set chapter title - view.chapter_title.text = item.chapter.name + chapter_title.text = item.chapter.name // Set manga title - view.manga_title.text = item.manga.title + manga_title.text = item.manga.title // Set the correct drawable for dropdown and update the tint to match theme. - view.chapter_menu_icon.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color)) + chapter_menu_icon.setVectorCompat(R.drawable.ic_more_horiz_black_24dp, view.context.getResourceColor(R.attr.icon_color)) // Set cover - GlideApp.with(itemView.context).clear(itemView.manga_cover) + GlideApp.with(itemView.context).clear(manga_cover) if (!item.manga.thumbnail_url.isNullOrEmpty()) { GlideApp.with(itemView.context) .load(item.manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .circleCrop() - .into(itemView.manga_cover) + .into(manga_cover) } // Check if chapter is read and set correct color if (item.chapter.read) { - view.chapter_title.setTextColor(readColor) - view.manga_title.setTextColor(readColor) + chapter_title.setTextColor(readColor) + manga_title.setTextColor(readColor) } else { - view.chapter_title.setTextColor(unreadColor) - view.manga_title.setTextColor(unreadColor) + chapter_title.setTextColor(unreadColor) + manga_title.setTextColor(unreadColor) } // Set chapter status @@ -94,7 +94,7 @@ class RecentChapterHolder(private val view: View, private val adapter: RecentCha * * @param status download status */ - fun notifyStatus(status: Int) = with(view.download_text) { + fun notifyStatus(status: Int) = with(download_text) { when (status) { Download.QUEUE -> setText(R.string.chapter_queued) Download.DOWNLOADING -> setText(R.string.chapter_downloading) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt index ecabfb873..07db9a509 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadHolder.kt @@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.recently_read import android.view.View import com.bumptech.glide.load.engine.DiskCacheStrategy -import eu.davidea.viewholders.FlexibleViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.MangaChapterHistory import eu.kanade.tachiyomi.data.glide.GlideApp -import kotlinx.android.synthetic.main.recently_read_item.view.* +import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder +import kotlinx.android.synthetic.main.recently_read_item.* import java.util.* /** @@ -21,18 +21,18 @@ import java.util.* class RecentlyReadHolder( view: View, val adapter: RecentlyReadAdapter -) : FlexibleViewHolder(view, adapter) { +) : BaseFlexibleViewHolder(view, adapter) { init { - itemView.remove.setOnClickListener { + remove.setOnClickListener { adapter.removeClickListener.onRemoveClick(adapterPosition) } - itemView.resume.setOnClickListener { + resume.setOnClickListener { adapter.resumeClickListener.onResumeClick(adapterPosition) } - itemView.cover.setOnClickListener { + cover.setOnClickListener { adapter.coverClickListener.onCoverClick(adapterPosition) } } @@ -47,24 +47,24 @@ class RecentlyReadHolder( val (manga, chapter, history) = item // Set manga title - itemView.manga_title.text = manga.title + manga_title.text = manga.title // Set source + chapter title val formattedNumber = adapter.decimalFormat.format(chapter.chapter_number.toDouble()) - itemView.manga_source.text = itemView.context.getString(R.string.recent_manga_source) + manga_source.text = itemView.context.getString(R.string.recent_manga_source) .format(adapter.sourceManager.get(manga.source)?.toString(), formattedNumber) // Set last read timestamp title - itemView.last_read.text = adapter.dateFormat.format(Date(history.last_read)) + last_read.text = adapter.dateFormat.format(Date(history.last_read)) // Set cover - GlideApp.with(itemView.context).clear(itemView.cover) + GlideApp.with(itemView.context).clear(cover) if (!manga.thumbnail_url.isNullOrEmpty()) { GlideApp.with(itemView.context) .load(manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() - .into(itemView.cover) + .into(cover) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt index 1a2f6c458..7f391eced 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt @@ -1,6 +1,8 @@ package eu.kanade.tachiyomi.ui.setting import android.app.Dialog +import android.content.Intent +import android.net.Uri import android.os.Bundle import android.support.v7.preference.PreferenceScreen import android.view.View @@ -22,8 +24,6 @@ import java.text.ParseException import java.text.SimpleDateFormat import java.util.* import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys -import android.content.Intent -import android.net.Uri class SettingsAboutController : SettingsController() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt index 25f847b72..c7982d0d8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt @@ -3,8 +3,6 @@ package eu.kanade.tachiyomi.ui.setting import android.graphics.drawable.Drawable import android.support.v7.preference.PreferenceGroup import android.support.v7.preference.PreferenceScreen -import com.bluelinelabs.conductor.ControllerChangeHandler -import com.bluelinelabs.conductor.ControllerChangeType import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.SourceManager From 08baf798aa6bd49ae0f61d84b2a670f36f67bdd7 Mon Sep 17 00:00:00 2001 From: inorichi Date: Mon, 4 Dec 2017 22:22:35 +0100 Subject: [PATCH 09/13] Give view pager unique ids, avoiding subtle bugs --- .../tachiyomi/ui/library/LibraryController.kt | 18 +++++++++--------- .../tachiyomi/ui/manga/MangaController.kt | 8 ++++---- .../ui/reader/viewer/pager/PagerReader.kt | 2 +- app/src/main/res/layout/library_controller.xml | 2 +- app/src/main/res/layout/manga_controller.xml | 2 +- app/src/main/res/values/ids.xml | 2 ++ 6 files changed, 18 insertions(+), 16 deletions(-) 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 b699069b7..806b31b9e 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 @@ -138,8 +138,8 @@ class LibraryController( super.onViewCreated(view) adapter = LibraryAdapter(this) - view_pager.adapter = adapter - view_pager.pageSelections().skip(1).subscribeUntilDestroy { + library_pager.adapter = adapter + library_pager.pageSelections().skip(1).subscribeUntilDestroy { preferences.lastUsedCategory().set(it) activeCategory = it } @@ -158,7 +158,7 @@ class LibraryController( override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) if (type.isEnter) { - activity?.tabs?.setupWithViewPager(view_pager) + activity?.tabs?.setupWithViewPager(library_pager) presenter.subscribeLibrary() } } @@ -232,7 +232,7 @@ class LibraryController( // Get the current active category. val activeCat = if (adapter.categories.isNotEmpty()) - view_pager.currentItem + library_pager.currentItem else activeCategory @@ -240,14 +240,14 @@ class LibraryController( adapter.categories = categories // Restore active category. - view_pager.setCurrentItem(activeCat, false) + library_pager.setCurrentItem(activeCat, false) tabsVisibilityRelay.call(categories.size > 1) // Delay the scroll position to allow the view to be properly measured. view.post { if (isAttached) { - activity?.tabs?.setScrollPosition(view_pager.currentItem, 0f, true) + activity?.tabs?.setScrollPosition(library_pager.currentItem, 0f, true) } } @@ -292,11 +292,11 @@ class LibraryController( private fun reattachAdapter() { val adapter = adapter ?: return - val position = view_pager.currentItem + val position = library_pager.currentItem adapter.recycle = false - view_pager.adapter = adapter - view_pager.currentItem = position + library_pager.adapter = adapter + library_pager.currentItem = position adapter.recycle = true } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 467848aac..ed9636f6b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -87,11 +87,11 @@ class MangaController : RxController, TabbedController { requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) adapter = MangaDetailAdapter() - view_pager.offscreenPageLimit = 3 - view_pager.adapter = adapter + manga_pager.offscreenPageLimit = 3 + manga_pager.adapter = adapter if (!fromCatalogue) - view_pager.currentItem = CHAPTERS_CONTROLLER + manga_pager.currentItem = CHAPTERS_CONTROLLER } override fun onDestroyView(view: View) { @@ -102,7 +102,7 @@ class MangaController : RxController, TabbedController { override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) if (type.isEnter) { - activity?.tabs?.setupWithViewPager(view_pager) + activity?.tabs?.setupWithViewPager(manga_pager) trackingIconSubscription = trackingIconRelay.subscribe { setTrackingIconInternal(it) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt index a5a4e71d4..1bd7b0b80 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt @@ -118,7 +118,7 @@ abstract class PagerReader : BaseReader() { this.pager = pager.apply { setLayoutParams(ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)) setOffscreenPageLimit(1) - setId(R.id.view_pager) + setId(R.id.reader_pager) setOnChapterBoundariesOutListener(object : OnChapterBoundariesOutListener { override fun onFirstPageOutEvent() { readerActivity.requestPreviousChapter() diff --git a/app/src/main/res/layout/library_controller.xml b/app/src/main/res/layout/library_controller.xml index 24e3d9af4..e315b9b92 100644 --- a/app/src/main/res/layout/library_controller.xml +++ b/app/src/main/res/layout/library_controller.xml @@ -8,7 +8,7 @@ + android:id="@+id/library_pager"/> \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 912dfd017..a28b6bb7e 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -3,4 +3,6 @@ + + \ No newline at end of file From e74583640459a4f01b3e2cf7c970278691b72884 Mon Sep 17 00:00:00 2001 From: Bram van de Kerkhof Date: Mon, 4 Dec 2017 22:55:57 +0100 Subject: [PATCH 10/13] Restore tracking on backup (#1097) --- .../tachiyomi/data/backup/BackupManager.kt | 82 ++++++++--------- .../data/backup/BackupRestoreService.kt | 90 +++++++++++++------ app/src/main/res/values-de/strings.xml | 2 +- 3 files changed, 105 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt index 812434abe..33b37b429 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt @@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.* import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.util.syncChaptersWithSource @@ -41,6 +42,11 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { */ internal val sourceManager: SourceManager by injectLazy() + /** + * Tracking manager + */ + internal val trackManager: TrackManager by injectLazy() + /** * Version of parser */ @@ -67,18 +73,16 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { parser = initParser() } - private fun initParser(): Gson { - return when (version) { - 1 -> GsonBuilder().create() - 2 -> GsonBuilder() - .registerTypeAdapter(MangaTypeAdapter.build()) - .registerTypeHierarchyAdapter(ChapterTypeAdapter.build()) - .registerTypeAdapter(CategoryTypeAdapter.build()) - .registerTypeAdapter(HistoryTypeAdapter.build()) - .registerTypeHierarchyAdapter(TrackTypeAdapter.build()) - .create() - else -> throw Exception("Json version unknown") - } + private fun initParser(): Gson = when (version) { + 1 -> GsonBuilder().create() + 2 -> GsonBuilder() + .registerTypeAdapter(MangaTypeAdapter.build()) + .registerTypeHierarchyAdapter(ChapterTypeAdapter.build()) + .registerTypeAdapter(CategoryTypeAdapter.build()) + .registerTypeAdapter(HistoryTypeAdapter.build()) + .registerTypeHierarchyAdapter(TrackTypeAdapter.build()) + .create() + else -> throw Exception("Json version unknown") } /** @@ -300,23 +304,26 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { val trackToUpdate = ArrayList() for (track in tracks) { - var isInDatabase = false - for (dbTrack in dbTracks) { - if (track.sync_id == dbTrack.sync_id) { - // The sync is already in the db, only update its fields - if (track.remote_id != dbTrack.remote_id) { - dbTrack.remote_id = track.remote_id + val service = trackManager.getService(track.sync_id) + if (service != null && service.isLogged) { + var isInDatabase = false + for (dbTrack in dbTracks) { + if (track.sync_id == dbTrack.sync_id) { + // The sync is already in the db, only update its fields + if (track.remote_id != dbTrack.remote_id) { + dbTrack.remote_id = track.remote_id + } + dbTrack.last_chapter_read = Math.max(dbTrack.last_chapter_read, track.last_chapter_read) + isInDatabase = true + trackToUpdate.add(dbTrack) + break } - dbTrack.last_chapter_read = Math.max(dbTrack.last_chapter_read, track.last_chapter_read) - isInDatabase = true - trackToUpdate.add(dbTrack) - break } - } - if (!isInDatabase) { - // Insert new sync. Let the db assign the id - track.id = null - trackToUpdate.add(track) + if (!isInDatabase) { + // Insert new sync. Let the db assign the id + track.id = null + trackToUpdate.add(track) + } } } // Update database @@ -361,32 +368,29 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { * * @return [Manga], null if not found */ - internal fun getMangaFromDatabase(manga: Manga): Manga? { - return databaseHelper.getManga(manga.url, manga.source).executeAsBlocking() - } + internal fun getMangaFromDatabase(manga: Manga): Manga? = + databaseHelper.getManga(manga.url, manga.source).executeAsBlocking() /** * Returns list containing manga from library * * @return [Manga] from library */ - internal fun getFavoriteManga(): List { - return databaseHelper.getFavoriteMangas().executeAsBlocking() - } + internal fun getFavoriteManga(): List = + databaseHelper.getFavoriteMangas().executeAsBlocking() /** * Inserts manga and returns id * * @return id of [Manga], null if not found */ - internal fun insertManga(manga: Manga): Long? { - return databaseHelper.insertManga(manga).executeAsBlocking().insertedId() - } + internal fun insertManga(manga: Manga): Long? = + databaseHelper.insertManga(manga).executeAsBlocking().insertedId() /** * Inserts list of chapters */ - internal fun insertChapters(chapters: List) { + private fun insertChapters(chapters: List) { databaseHelper.updateChaptersBackup(chapters).executeAsBlocking() } @@ -395,7 +399,5 @@ class BackupManager(val context: Context, version: Int = CURRENT_VERSION) { * * @return number of backups selected by user */ - fun numberOfBackups(): Int { - return preferences.numberOfBackups().getOrDefault() - } + fun numberOfBackups(): Int = preferences.numberOfBackups().getOrDefault() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt index 9b3bf8906..5ab30ab5b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt @@ -21,6 +21,7 @@ import eu.kanade.tachiyomi.data.backup.models.Backup.VERSION import eu.kanade.tachiyomi.data.backup.models.DHistory import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.* +import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.util.chop import eu.kanade.tachiyomi.util.isServiceRunning @@ -49,9 +50,8 @@ class BackupRestoreService : Service() { * @param context the application context. * @return true if the service is running, false otherwise. */ - fun isRunning(context: Context): Boolean { - return context.isServiceRunning(BackupRestoreService::class.java) - } + private fun isRunning(context: Context): Boolean = + context.isServiceRunning(BackupRestoreService::class.java) /** * Starts a service to restore a backup from Json @@ -113,7 +113,13 @@ class BackupRestoreService : Service() { */ private val db: DatabaseHelper by injectLazy() - lateinit var executor: ExecutorService + /** + * Tracking manager + */ + internal val trackManager: TrackManager by injectLazy() + + + private lateinit var executor: ExecutorService /** * Method called when the service is created. It injects dependencies and acquire the wake lock. @@ -142,9 +148,7 @@ class BackupRestoreService : Service() { /** * This method needs to be implemented, but it's not used/needed. */ - override fun onBind(intent: Intent): IBinder? { - return null - } + override fun onBind(intent: Intent): IBinder? = null /** * Method called when the service receives an intent. @@ -164,7 +168,7 @@ class BackupRestoreService : Service() { subscription = Observable.using( { db.lowLevel().beginTransaction() }, - { getRestoreObservable(uri).doOnNext{ db.lowLevel().setTransactionSuccessful() } }, + { getRestoreObservable(uri).doOnNext { db.lowLevel().setTransactionSuccessful() } }, { executor.execute { db.lowLevel().endTransaction() } }) .doAfterTerminate { stopSelf(startId) } .subscribeOn(Schedulers.from(executor)) @@ -294,14 +298,14 @@ class BackupRestoreService : Service() { val source = backupManager.sourceManager.get(manga.source) ?: return null val dbManga = backupManager.getMangaFromDatabase(manga) - if (dbManga == null) { + return if (dbManga == null) { // Manga not in database - return mangaFetchObservable(source, manga, chapters, categories, history, tracks) + mangaFetchObservable(source, manga, chapters, categories, history, tracks) } else { // Manga in database // Copy information from manga already in database backupManager.restoreMangaNoFetch(manga, dbManga) // Fetch rest of manga information - return mangaNoFetchObservable(source, manga, chapters, categories, history, tracks) + mangaNoFetchObservable(source, manga, chapters, categories, history, tracks) } } @@ -327,14 +331,12 @@ class BackupRestoreService : Service() { .map { manga } } .doOnNext { - // Restore categories - backupManager.restoreCategoriesForManga(it, categories) - - // Restore history - backupManager.restoreHistoryForManga(history) - - // Restore tracking - backupManager.restoreTrackForManga(it, tracks) + restoreExtraForManga(it, categories, history, tracks) + } + .flatMap { + trackingFetchObservable(it, tracks) + // Convert to the manga that contains new chapters. + .map { manga } } .doOnCompleted { restoreProgress += 1 @@ -356,14 +358,12 @@ class BackupRestoreService : Service() { } } .doOnNext { - // Restore categories - backupManager.restoreCategoriesForManga(it, categories) - - // Restore history - backupManager.restoreHistoryForManga(history) - - // Restore tracking - backupManager.restoreTrackForManga(it, tracks) + restoreExtraForManga(it, categories, history, tracks) + } + .flatMap { manga -> + trackingFetchObservable(manga, tracks) + // Convert to the manga that contains new chapters. + .map { manga } } .doOnCompleted { restoreProgress += 1 @@ -371,6 +371,17 @@ class BackupRestoreService : Service() { } } + private fun restoreExtraForManga(manga: Manga, categories: List, history: List, tracks: List) { + // Restore categories + backupManager.restoreCategoriesForManga(manga, categories) + + // Restore history + backupManager.restoreHistoryForManga(history) + + // Restore tracking + backupManager.restoreTrackForManga(manga, tracks) + } + /** * [Observable] that fetches chapter information * @@ -383,10 +394,33 @@ class BackupRestoreService : Service() { // If there's any error, return empty update and continue. .onErrorReturn { errors.add(Date() to "${manga.title} - ${it.message}") - Pair(emptyList(), emptyList()) + Pair(emptyList(), emptyList()) } } + /** + * [Observable] that refreshes tracking information + * @param manga manga that needs updating. + * @param tracks list containing tracks from restore file. + * @return [Observable] that contains updated track item + */ + private fun trackingFetchObservable(manga: Manga, tracks: List): Observable { + return Observable.from(tracks) + .concatMap { track -> + val service = trackManager.getService(track.sync_id) + if (service != null && service.isLogged) { + service.refresh(track) + .doOnNext { db.insertTrack(it).executeAsBlocking() } + .onErrorReturn { + errors.add(Date() to "${manga.title} - ${it.message}") + track + } + } else { + errors.add(Date() to "${manga.title} - ${service?.name} not logged in") + Observable.empty() + } + } + } /** * Called to update dialog in [BackupConst] diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c614a97d2..a8d4907e1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -201,7 +201,7 @@ Backup wird wiederhergestellt %1$s zur Bibliothek hinzugefügt Quelle nicht gefunden - Backup wird wiederhergestellt %1%s + Backup wird wiederhergestellt %1$s \nQuelle nicht gefunden Backup erstellt Wiederherstellen erfolgreich From f287d313c3f573b65e18b4bbcbfe45e50227e920 Mon Sep 17 00:00:00 2001 From: inorichi Date: Tue, 5 Dec 2017 19:50:39 +0100 Subject: [PATCH 11/13] Release 0.6.6 --- app/build.gradle | 4 ++-- app/src/main/res/raw/changelog_release.xml | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2ebfe6b7f..5a07d36d2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,8 +38,8 @@ android { minSdkVersion 16 targetSdkVersion 26 testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - versionCode 28 - versionName "0.6.5" + versionCode 29 + versionName "0.6.6" buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\"" buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\"" diff --git a/app/src/main/res/raw/changelog_release.xml b/app/src/main/res/raw/changelog_release.xml index 05e14ae52..0a11754be 100644 --- a/app/src/main/res/raw/changelog_release.xml +++ b/app/src/main/res/raw/changelog_release.xml @@ -1,5 +1,17 @@ + + Backups now properly restore tracking information. + + Fixed library view and its overflow menu visible in other screens. + + Fixed updater's notification in Android O. + + Fixed a crash when rotating the screen in the chapters view. + + Improved peformance of the app when using a custom downloads directory. + + Added a download cache for faster navigation. From 8f9737f567a29a32495141b13a7391ca927b088c Mon Sep 17 00:00:00 2001 From: Taumer Date: Tue, 5 Dec 2017 23:21:02 +0300 Subject: [PATCH 12/13] Update regexp for pages from Readmanga/Mintmanga (#1111) --- .../java/eu/kanade/tachiyomi/source/online/russian/Mintmanga.kt | 2 +- .../java/eu/kanade/tachiyomi/source/online/russian/Readmanga.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Mintmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Mintmanga.kt index dbaa49fd7..dd1a765ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Mintmanga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Mintmanga.kt @@ -118,7 +118,7 @@ class Mintmanga : ParsedHttpSource() { val endIndex = html.indexOf("], 0, false);", beginIndex) val trimmedHtml = html.substring(beginIndex, endIndex) - val p = Pattern.compile("'.+?','.+?',\".+?\"") + val p = Pattern.compile("'.*?','.*?',\".*?\"") val m = p.matcher(trimmedHtml) val pages = mutableListOf() diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Readmanga.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Readmanga.kt index 4bc3383d6..296ccc666 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Readmanga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/russian/Readmanga.kt @@ -118,7 +118,7 @@ class Readmanga : ParsedHttpSource() { val endIndex = html.indexOf("], 0, false);", beginIndex) val trimmedHtml = html.substring(beginIndex, endIndex) - val p = Pattern.compile("'.+?','.+?',\".+?\"") + val p = Pattern.compile("'.*?','.*?',\".*?\"") val m = p.matcher(trimmedHtml) val pages = mutableListOf() From 9ae6285eef2db68b348816b67ca7cd371117834e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jozef=20Holl=C3=BD?= Date: Wed, 6 Dec 2017 08:41:37 +0100 Subject: [PATCH 13/13] Change discord invite link in settings (#1112) * Change discord invite link in settings * Change discord link is readme --- README.md | 2 +- .../eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ef48dd243..bd9961298 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ | Build | Download | F-Droid | Contribute | Contact | |-------|----------|---------|------------|---------| -| [![Travis](https://img.shields.io/travis/inorichi/tachiyomi.svg)](https://travis-ci.org/inorichi/tachiyomi) | [![stable release](https://img.shields.io/github/release/inorichi/tachiyomi.svg?maxAge=3600&label=stable)](https://github.com/inorichi/tachiyomi/releases) [![latest dev build](https://img.shields.io/badge/dev-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest) | [![fdroid release](https://img.shields.io/badge/stable-f--droid.org-blue.svg)](https://f-droid.org/repository/browse/?fdid=eu.kanade.tachiyomi) [![fdroid dev](https://img.shields.io/badge/dev-wiki-blue.svg)](//github.com/inorichi/tachiyomi/wiki/FDroid-for-dev-versions) | [![Translation status](http://weblate.j2ghz.com/widgets/tachiyomi/-/svg-badge.svg)](https://github.com/inorichi/tachiyomi/wiki/Translation) | [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/WrBkRk4) | +| [![Travis](https://img.shields.io/travis/inorichi/tachiyomi.svg)](https://travis-ci.org/inorichi/tachiyomi) | [![stable release](https://img.shields.io/github/release/inorichi/tachiyomi.svg?maxAge=3600&label=stable)](https://github.com/inorichi/tachiyomi/releases) [![latest dev build](https://img.shields.io/badge/dev-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest) | [![fdroid release](https://img.shields.io/badge/stable-f--droid.org-blue.svg)](https://f-droid.org/repository/browse/?fdid=eu.kanade.tachiyomi) [![fdroid dev](https://img.shields.io/badge/dev-wiki-blue.svg)](//github.com/inorichi/tachiyomi/wiki/FDroid-for-dev-versions) | [![Translation status](http://weblate.j2ghz.com/widgets/tachiyomi/-/svg-badge.svg)](https://github.com/inorichi/tachiyomi/wiki/Translation) | [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/2dDQBv2) | ### **Contact us on [Discord](https://discord.gg/WrBkRk4)** If you want to open an issue, please read [contributing guidelines](https://github.com/inorichi/tachiyomi/blob/master/.github/CONTRIBUTING.md). Your issue may be closed otherwise. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt index 7f391eced..1c79f9a58 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt @@ -71,7 +71,7 @@ class SettingsAboutController : SettingsController() { } preference { title = "Discord" - val url = "https://discord.gg/WrBkRk4" + val url = "https://discord.gg/2dDQBv2" summary = url onClick { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))