diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationListController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationListController.kt index e599679f0c..b53d14efe5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationListController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationListController.kt @@ -96,7 +96,7 @@ class MigrationListController(bundle: Bundle? = null) : BaseController(bundle), new } - adapter = MigrationProcessAdapter(this, view.context) + adapter = MigrationProcessAdapter(this) recycler.adapter = adapter recycler.layoutManager = LinearLayoutManager(view.context) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessAdapter.kt index 230c29caec..ce1521d9fe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/manga/process/MigrationProcessAdapter.kt @@ -16,8 +16,7 @@ import kotlinx.coroutines.withContext import uy.kohesive.injekt.injectLazy class MigrationProcessAdapter( - val controller: MigrationListController, - context: Context + val controller: MigrationListController ) : FlexibleAdapter(null, controller, true) { 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 9a293663d2..983cc5ab1e 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,35 +1,37 @@ package eu.kanade.tachiyomi.ui.recently_read -import androidx.recyclerview.widget.LinearLayoutManager +import android.app.Activity +import android.os.Bundle import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import android.view.Menu import android.view.MenuInflater import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.appcompat.widget.SearchView -import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges +import androidx.recyclerview.widget.LinearLayoutManager import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.backup.BackupRestoreService 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.BaseController import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.catalogue.browse.ProgressItem import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener +import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.toast -import kotlinx.android.synthetic.main.recently_read_controller.empty_view -import kotlinx.android.synthetic.main.recently_read_controller.recycler +import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener +import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener +import kotlinx.android.synthetic.main.recently_read_controller.* /** * Fragment that shows recently read manga. * Uses R.layout.fragment_recently_read. * UI related actions should be called from here. */ -class RecentlyReadController : NucleusController(), +class RecentlyReadController(bundle: Bundle? = null) : BaseController(bundle), FlexibleAdapter.OnUpdateListener, FlexibleAdapter.EndlessScrollListener, RecentlyReadAdapter.OnRemoveClickListener, @@ -50,16 +52,17 @@ class RecentlyReadController : NucleusController(), * Endless loading item. */ private var progressItem: ProgressItem? = null + private var observeLater:Boolean = false private var query = "" + private var presenter = RecentlyReadPresenter(this) + private var recentItems: MutableList? = null + + override fun getTitle(): String? { return resources?.getString(R.string.label_recent_manga) } - override fun createPresenter(): RecentlyReadPresenter { - return RecentlyReadPresenter() - } - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { return inflater.inflate(R.layout.recently_read_controller, container, false) } @@ -73,38 +76,52 @@ class RecentlyReadController : NucleusController(), super.onViewCreated(view) // Initialize adapter - recycler.layoutManager = LinearLayoutManager(view.context) - adapter = RecentlyReadAdapter(this@RecentlyReadController) - recycler.setHasFixedSize(true) + adapter = RecentlyReadAdapter(this) recycler.adapter = adapter + recycler.layoutManager = LinearLayoutManager(view.context) + recycler.setHasFixedSize(true) recycler.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener) + resetProgressItem() + + if (recentItems != null) + adapter?.updateDataSet(recentItems!!.toList()) + + launchUI { + val manga = presenter.refresh(query) + recentItems = manga.toMutableList() + adapter?.updateDataSet(manga) + } } - override fun onDestroyView(view: View) { - adapter = null - super.onDestroyView(view) + override fun onActivityResumed(activity: Activity) { + super.onActivityResumed(activity) + if (observeLater) { + presenter.observe() + observeLater = false + } } - /** * Populate adapter with chapters * * @param mangaHistory list of manga history */ - fun onNextManga(mangaHistory: List, cleanBatch: Boolean = false) { - if (adapter?.itemCount ?: 0 == 0 || cleanBatch) + fun onNextManga(mangaHistory: List) { + val adapter = adapter ?: return + adapter.updateDataSet(mangaHistory) + adapter.onLoadMoreComplete(null) + if (recentItems == null) resetProgressItem() - if (cleanBatch) adapter?.updateDataSet(mangaHistory) - else adapter?.onLoadMoreComplete(mangaHistory) + recentItems = mangaHistory.toMutableList() } - fun onAddPageError(error: Throwable) { + fun onAddPageError() { adapter?.onLoadMoreComplete(null) adapter?.endlessTargetCount = 1 } override fun onUpdateEmptyView(size: Int) { if (size > 0) { - empty_view.hide() + empty_view?.hide() } else { empty_view.show(R.drawable.ic_glasses_black_128dp, R.string.information_no_recent_manga) } @@ -122,17 +139,17 @@ class RecentlyReadController : NucleusController(), override fun onLoadMore(lastPosition: Int, currentPage: Int) { val view = view ?: return if (BackupRestoreService.isRunning(view.context.applicationContext)) { - onAddPageError(Throwable()) + onAddPageError() return } - val adapter = adapter ?: return - presenter.requestNext(adapter.itemCount, query) + presenter.requestNext(query) } override fun noMoreLoad(newItemsSize: Int) { } override fun onResumeClick(position: Int) { val activity = activity ?: return + observeLater = true val (manga, chapter, _) = (adapter?.getItem(position) as? RecentlyReadItem)?.mch ?: return val nextChapter = presenter.getNextChapter(chapter, manga) @@ -168,17 +185,24 @@ class RecentlyReadController : NucleusController(), inflater.inflate(R.menu.recently_read, menu) val searchItem = menu.findItem(R.id.action_search) val searchView = searchItem.actionView as SearchView - searchView.maxWidth = Int.MAX_VALUE if (query.isNotEmpty()) { searchItem.expandActionView() searchView.setQuery(query, true) searchView.clearFocus() } - searchView.queryTextChanges().filter { router.backstack.lastOrNull()?.controller() == this } - .subscribeUntilDestroy { - query = it.toString() - presenter.updateList(query) + setOnQueryTextChangeListener(searchView) { + if (query != it) { + query = it ?: return@setOnQueryTextChangeListener false + launchUI { + resetProgressItem() + presenter.lastCount = 25 + val manga = presenter.refresh(query) + recentItems = manga.toMutableList() + adapter?.updateDataSet(manga) + } } + true + } // Fixes problem with the overflow icon showing up in lieu of search searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadPresenter.kt index 078c039759..bcf73ba09b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadPresenter.kt @@ -6,6 +6,10 @@ import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.History import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import eu.kanade.tachiyomi.util.system.launchUI +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okhttp3.Dispatcher import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -20,60 +24,47 @@ import java.util.Date * Contains information and data for fragment. * Observable updates should be called from here. */ -class RecentlyReadPresenter : BasePresenter() { +class RecentlyReadPresenter(private val view: RecentlyReadController) { /** * Used to connect to database */ val db: DatabaseHelper by injectLazy() + private var readerSubscription:Subscription? = null var lastCount = 25 var lastSearch = "" - override fun onCreate(savedState: Bundle?) { - super.onCreate(savedState) - - //pageSubscription?.let { remove(it) } - // Used to get a list of recently read manga - updateList() - } - - fun requestNext(offset: Int, search: String = "") { - lastCount = offset + fun requestNext(search: String = "") { + lastCount += 25 lastSearch = search - getRecentMangaObservable((offset), search) - .subscribeLatestCache({ view, mangas -> - view.onNextManga(mangas) - }, RecentlyReadController::onAddPageError) + updateList(search) } /** - * Get recent manga observable + * Get all recent manga up to a point * @return list of history */ - private fun getRecentMangaObservable(offset: Int = 0, search: String = ""): Observable> { + private fun getRecentMangaLimit(search: String = ""): List { // Set date for recent manga val cal = Calendar.getInstance() cal.time = Date() cal.add(Calendar.YEAR, -50) - return db.getRecentManga(cal.time, offset, search).asRxObservable() - .map { recents -> recents.map(::RecentlyReadItem) } - .observeOn(AndroidSchedulers.mainThread()) + return db.getRecentMangaLimit(cal.time, lastCount, search).executeAsBlocking() + .map(::RecentlyReadItem) } - /** - * Get recent manga observable - * @return list of history - */ - private fun getRecentMangaLimitObservable(offset: Int = 0, search: String = ""): Observable> { - // Set date for recent manga + fun observe() { + readerSubscription?.unsubscribe() val cal = Calendar.getInstance() cal.time = Date() cal.add(Calendar.YEAR, -50) - - return db.getRecentMangaLimit(cal.time, lastCount, search).asRxObservable() - .map { recents -> recents.map(::RecentlyReadItem) } - .observeOn(AndroidSchedulers.mainThread()) + readerSubscription = db.getRecentMangaLimit(cal.time, lastCount, "").asRxObservable().map { + val items = it.map(::RecentlyReadItem) + launchUI { + view.onNextManga(items) + } + }.observeOn(Schedulers.io()).skip(1).take(1).subscribe() } /** @@ -86,12 +77,28 @@ class RecentlyReadPresenter : BasePresenter() { updateList() } - fun updateList(search: String? = null) { - lastSearch = search?:lastSearch - getRecentMangaLimitObservable(lastCount, lastSearch).take(1) - .subscribeLatestCache({ view, mangas -> - view.onNextManga(mangas, true) - }, RecentlyReadController::onAddPageError) + suspend fun refresh(search: String? = null): List { + val manga = withContext(Dispatchers.IO) { getRecentMangaLimit(search ?: "") } + checkIfNew(manga.size, search) + lastSearch = search ?: lastSearch + lastCount = manga.size + return manga + } + + private fun updateList(search: String? = null) { + launchUI { + val manga = withContext(Dispatchers.IO) { getRecentMangaLimit(search ?: "") } + checkIfNew(manga.size, search) + lastSearch = search ?: lastSearch + lastCount = manga.size + view.onNextManga(manga) + } + } + + private fun checkIfNew(newCount: Int, newSearch: String?) { + if (lastCount > newCount && newSearch == lastSearch) { + view.onAddPageError() + } } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt index a6d129a325..98645b0281 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt @@ -13,14 +13,12 @@ import android.widget.TextView import androidx.annotation.Px import androidx.annotation.RequiresApi import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.appcompat.widget.SearchView import androidx.core.view.ViewCompat -import com.afollestad.materialdialogs.MaterialDialog -import com.afollestad.materialdialogs.WhichButton -import com.afollestad.materialdialogs.actions.getActionButton -import com.afollestad.materialdialogs.actions.hasActionButton import com.amulyakhare.textdrawable.TextDrawable import eu.kanade.tachiyomi.R import com.amulyakhare.textdrawable.util.ColorGenerator +import com.bluelinelabs.conductor.Controller import com.google.android.material.snackbar.Snackbar import eu.kanade.tachiyomi.util.system.getResourceColor import kotlin.math.min @@ -199,6 +197,22 @@ data class ViewPaddingState( val end: Int ) + +fun Controller.setOnQueryTextChangeListener(searchView: SearchView, f: (text: String?) -> Boolean) { + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextChange(newText: String?): Boolean { + if (router.backstack.lastOrNull()?.controller() == this@setOnQueryTextChangeListener) { + return f(newText) + } + return true + } + + override fun onQueryTextSubmit(query: String?): Boolean { + return true + } + }) +} + @RequiresApi(17) inline fun View.updatePaddingRelative( @Px start: Int = paddingStart, diff --git a/app/src/main/res/layout/recently_read_controller.xml b/app/src/main/res/layout/recently_read_controller.xml index 5f0c27f271..ff1df88842 100644 --- a/app/src/main/res/layout/recently_read_controller.xml +++ b/app/src/main/res/layout/recently_read_controller.xml @@ -1,26 +1,27 @@ + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/frame_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:visibility="gone" />