Mitigate most of the lag problems, should fix all crashes(inspired by J2k code)

Add tracking button the way J2K has it, my own implementation
This commit is contained in:
Jobobby04 2020-05-16 17:53:02 -04:00
parent e6f5ea172a
commit 4a64bb250d
5 changed files with 257 additions and 255 deletions

View File

@ -22,8 +22,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson import com.google.gson.Gson
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.SelectableAdapter import eu.davidea.flexibleadapter.SelectableAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@ -35,6 +33,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.MangaAllInOneControllerBinding import eu.kanade.tachiyomi.databinding.MangaAllInOneControllerBinding
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
@ -56,6 +55,7 @@ import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersAdapter
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter
import eu.kanade.tachiyomi.ui.manga.chapter.DeleteChaptersDialog import eu.kanade.tachiyomi.ui.manga.chapter.DeleteChaptersDialog
import eu.kanade.tachiyomi.ui.manga.chapter.DownloadCustomChaptersDialog import eu.kanade.tachiyomi.ui.manga.chapter.DownloadCustomChaptersDialog
import eu.kanade.tachiyomi.ui.manga.track.TrackController
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.recent.history.HistoryController import eu.kanade.tachiyomi.ui.recent.history.HistoryController
@ -178,12 +178,8 @@ class MangaAllInOneController :
private var initialLoad: Boolean = true private var initialLoad: Boolean = true
// EXH -->
private var lastMangaThumbnail: String? = null
// EXH --> // EXH -->
val smartSearchConfig: SourceController.SmartSearchConfig? = args.getParcelable(SMART_SEARCH_CONFIG_EXTRA) val smartSearchConfig: SourceController.SmartSearchConfig? = args.getParcelable(SMART_SEARCH_CONFIG_EXTRA)
// EXH <--
override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main
@ -192,12 +188,6 @@ class MangaAllInOneController :
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
// EXH <-- // EXH <--
val lastUpdateRelay: BehaviorRelay<Date> = BehaviorRelay.create()
val chapterCountRelay: BehaviorRelay<Float> = BehaviorRelay.create()
val mangaFavoriteRelay: PublishRelay<Boolean> = PublishRelay.create()
val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false) val fromSource = args.getBoolean(FROM_SOURCE_EXTRA, false)
var update = args.getBoolean(UPDATE_EXTRA, false) var update = args.getBoolean(UPDATE_EXTRA, false)
@ -212,8 +202,7 @@ class MangaAllInOneController :
override fun createPresenter(): MangaAllInOnePresenter { override fun createPresenter(): MangaAllInOnePresenter {
return MangaAllInOnePresenter( return MangaAllInOnePresenter(
manga!!, source!!, this, manga!!, source!!, smartSearchConfig
chapterCountRelay, lastUpdateRelay, mangaFavoriteRelay, smartSearchConfig
) )
} }
@ -232,6 +221,26 @@ class MangaAllInOneController :
.onEach { onFavoriteClick() } .onEach { onFavoriteClick() }
.launchIn(scope) .launchIn(scope)
if ((Injekt.get<TrackManager>().hasLoggedServices()) && presenter.manga.favorite) {
binding.btnTracking.visible()
}
scope.launch(Dispatchers.IO) {
if (Injekt.get<DatabaseHelper>().getTracks(presenter.manga).executeAsBlocking().any {
val status = Injekt.get<TrackManager>().getService(it.sync_id)?.getStatus(it.status)
status != null
}
) {
withContext(Dispatchers.Main) {
binding.btnTracking.icon = resources!!.getDrawable(R.drawable.ic_cloud_white_24dp, null)
}
}
}
binding.btnTracking.clicks()
.onEach { openTracking() }
.launchIn(scope)
if (presenter.manga.favorite && presenter.getCategories().isNotEmpty()) { if (presenter.manga.favorite && presenter.getCategories().isNotEmpty()) {
binding.btnCategories.visible() binding.btnCategories.visible()
} }
@ -440,6 +449,12 @@ class MangaAllInOneController :
} }
// AZ <-- // AZ <--
private fun openTracking() {
router?.pushController(
TrackController(fromAllInOne = true, manga = manga).withFadeTransaction()
)
}
/** /**
* Check if manga is initialized. * Check if manga is initialized.
* If true update view with manga information, * If true update view with manga information,
@ -448,10 +463,10 @@ class MangaAllInOneController :
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the manga. * @param source the source of the manga.
*/ */
fun onNextManga(manga: Manga, source: Source) { fun onNextManga(manga: Manga, source: Source, chapters: List<ChapterItem>) {
if (manga.initialized) { if (manga.initialized) {
// Update view. // Update view.
setMangaInfo(manga, source) setMangaInfo(manga, source, chapters)
} else { } else {
// Initialize manga. // Initialize manga.
fetchMangaFromSource() fetchMangaFromSource()
@ -464,7 +479,7 @@ class MangaAllInOneController :
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the manga. * @param source the source of the manga.
*/ */
private fun setMangaInfo(manga: Manga, source: Source?) { private fun setMangaInfo(manga: Manga, source: Source?, chapters: List<ChapterItem>) {
val view = view ?: return val view = view ?: return
// update full title TextView. // update full title TextView.
@ -579,6 +594,35 @@ class MangaAllInOneController :
initialLoad = false initialLoad = false
} }
} }
if (update ||
// Auto-update old format galleries
(
(presenter.manga.source == EH_SOURCE_ID || presenter.manga.source == EXH_SOURCE_ID) &&
chapters.size == 1 && chapters.first().date_upload == 0L
)
) {
update = false
fetchMangaFromSource()
}
val adapter = adapter ?: return
adapter.updateDataSet(chapters)
if (selectedItems.isNotEmpty()) {
adapter.clearSelection() // we need to start from a clean state, index may have changed
createActionModeIfNeeded()
selectedItems.forEach { item ->
val position = adapter.indexOf(item)
if (position != -1 && !adapter.isSelected(position)) {
adapter.toggleSelection(position)
}
}
actionMode?.invalidate()
}
}
fun setTracking() {
binding.btnTracking.icon = resources!!.getDrawable(R.drawable.ic_cloud_white_24dp, null)
} }
private fun hideMangaInfo() { private fun hideMangaInfo() {
@ -721,14 +765,7 @@ class MangaAllInOneController :
presenter.fetchMangaFromSource(manualFetch) presenter.fetchMangaFromSource(manualFetch)
} }
/**
* Update swipe refresh to stop showing refresh in progress spinner.
*/
fun onFetchMangaDone() { fun onFetchMangaDone() {
fetchChaptersFromSource()
}
fun onFetchChaptersDone() {
setRefreshing(false) setRefreshing(false)
} }
@ -745,7 +782,7 @@ class MangaAllInOneController :
* *
* @param value whether it should be refreshing or not. * @param value whether it should be refreshing or not.
*/ */
private fun setRefreshing(value: Boolean) { fun setRefreshing(value: Boolean) {
binding.swipeRefresh.isRefreshing = value binding.swipeRefresh.isRefreshing = value
} }
@ -999,56 +1036,6 @@ class MangaAllInOneController :
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
fun onNextChapters(chapters: List<ChapterItem>) {
// If the list is empty, fetch chapters from source if the conditions are met
// We use presenter chapters instead because they are always unfiltered
if (presenter.chapters.isEmpty()) {
initialFetchChapters()
}
if (update ||
// Auto-update old format galleries
(
(presenter.manga.source == EH_SOURCE_ID || presenter.manga.source == EXH_SOURCE_ID) &&
chapters.size == 1 && chapters.first().date_upload == 0L
)
) {
update = false
fetchChaptersFromSource()
}
val adapter = adapter ?: return
adapter.updateDataSet(chapters)
if (selectedItems.isNotEmpty()) {
adapter.clearSelection() // we need to start from a clean state, index may have changed
createActionModeIfNeeded()
selectedItems.forEach { item ->
val position = adapter.indexOf(item)
if (position != -1 && !adapter.isSelected(position)) {
adapter.toggleSelection(position)
}
}
actionMode?.invalidate()
}
}
private fun initialFetchChapters() {
// Only fetch if this view is from the catalog and it hasn't requested previously
if (fromSource && !presenter.hasRequested) {
fetchChaptersFromSource()
}
}
private fun fetchChaptersFromSource() {
presenter.fetchChaptersFromSource()
}
fun onFetchChaptersError(error: Throwable) {
onFetchChaptersDone()
activity?.toast(error.message)
}
fun onChapterStatusChange(download: Download) { fun onChapterStatusChange(download: Download) {
getHolder(download.chapter)?.notifyStatus(download.status) getHolder(download.chapter)?.notifyStatus(download.status)
} }

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.manga
import android.os.Bundle import android.os.Bundle
import com.google.gson.Gson import com.google.gson.Gson
import com.jakewharton.rxrelay.BehaviorRelay import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Category
@ -15,23 +14,27 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.SourceController import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.debug.DebugToggles import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper import exh.eh.EHentaiUpdateHelper
import exh.isEhBasedSource
import exh.util.await import exh.util.await
import java.util.Date import java.util.Date
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -48,11 +51,9 @@ import uy.kohesive.injekt.injectLazy
* Observable updates should be called from here. * Observable updates should be called from here.
*/ */
class MangaAllInOnePresenter( class MangaAllInOnePresenter(
val controller: MangaAllInOneController,
val manga: Manga, val manga: Manga,
val source: Source, val source: Source,
private val chapterCountRelay: BehaviorRelay<Float>,
private val lastUpdateRelay: BehaviorRelay<Date>,
private val mangaFavoriteRelay: PublishRelay<Boolean>,
val smartSearchConfig: SourceController.SmartSearchConfig?, val smartSearchConfig: SourceController.SmartSearchConfig?,
private val db: DatabaseHelper = Injekt.get(), private val db: DatabaseHelper = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
@ -67,11 +68,7 @@ class MangaAllInOnePresenter(
var chapters: List<ChapterItem> = emptyList() var chapters: List<ChapterItem> = emptyList()
private set private set
/** private val scope = CoroutineScope(Job() + Dispatchers.Default)
* Subject of list of chapters to allow updating the view without going to DB.
*/
val chaptersRelay: PublishRelay<List<ChapterItem>>
by lazy { PublishRelay.create<List<ChapterItem>>() }
/** /**
* Whether the chapter list has been requested to the source. * Whether the chapter list has been requested to the source.
@ -79,11 +76,6 @@ class MangaAllInOnePresenter(
var hasRequested = false var hasRequested = false
private set private set
/**
* Subscription to retrieve the new list of chapters from the source.
*/
private var fetchChaptersSubscription: Subscription? = null
/** /**
* Subscription to observe download status changes. * Subscription to observe download status changes.
*/ */
@ -95,67 +87,31 @@ class MangaAllInOnePresenter(
val redirectUserRelay = BehaviorRelay.create<ChaptersPresenter.EXHRedirect>() val redirectUserRelay = BehaviorRelay.create<ChaptersPresenter.EXHRedirect>()
// EXH <-- // EXH <--
/**
* Subscription to send the manga to the view.
*/
private var viewMangaSubscription: Subscription? = null
/**
* Subscription to update the manga from the source.
*/
private var fetchMangaSubscription: Subscription? = null
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
getMangaObservable() if (manga.isLocal()) {
.subscribeLatestCache({ view, manga -> view.onNextManga(manga, source) }) controller.setRefreshing(true)
fetchMangaFromSource()
// Update chapter count } else if (!manga.initialized) {
chapterCountRelay.observeOn(AndroidSchedulers.mainThread()) controller.setRefreshing(true)
.subscribeLatestCache(MangaAllInOneController::setChapterCount) fetchMangaFromSource()
} else {
// Prepare the relay. updateManga()
chaptersRelay.flatMap { applyChapterFilters(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(MangaAllInOneController::onNextChapters) { _, error -> Timber.e(error) }
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
// changes, and sends the list of chapters to the relay.
add(
db.getChapters(manga).asRxObservable()
.map { chapters ->
// Convert every chapter to a model.
chapters.map { it.toModel() }
} }
.doOnNext { chapters ->
// Find downloaded chapters
setDownloadedChapters(chapters)
// Store the last emission
this.chapters = chapters
// Listen for download status changes // Listen for download status changes
observeDownloads() observeDownloads()
}
// Emit the number of chapters to the info tab. private fun updateChapters() {
chapterCountRelay.call( val chapters = db.getChapters(manga).executeAsBlocking().map { it.toModel() }
chapters.maxBy { it.chapter_number }?.chapter_number
?: 0f // Find downloaded chapters
) setDownloadedChapters(chapters)
// Emit the upload date of the most recent chapter
lastUpdateRelay.call(
Date(
chapters.maxBy { it.date_upload }?.date_upload
?: 0
)
)
// EXH --> // EXH -->
if (chapters.isNotEmpty() && if (chapters.isNotEmpty() && (source.isEhBasedSource()) && DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled) {
(source.id == EXH_SOURCE_ID || source.id == EH_SOURCE_ID) &&
DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled
) {
// Check for gallery in library and accept manga with lowest id // Check for gallery in library and accept manga with lowest id
// Find chapters sharing same root // Find chapters sharing same root
add( add(
@ -179,46 +135,88 @@ class MangaAllInOnePresenter(
) )
} }
// EXH <-- // EXH <--
this.chapters = applyChapterFilters(chapters)
} }
.subscribe { chaptersRelay.call(it) }
private fun updateChapterInfo() {
scope.launch(Dispatchers.IO) {
val lastUpdateDate = Date(
chapters.maxBy { it.date_upload }?.date_upload ?: 0
) )
// Update favorite status val chapterCount = chapters.maxBy { it.chapter_number }?.chapter_number ?: 0f
mangaFavoriteRelay.observeOn(AndroidSchedulers.mainThread())
.subscribe { setFavorite(it) }
.apply { add(this) }
withContext(Dispatchers.Main) {
// set chapter count
controller.setChapterCount(chapterCount)
// update last update date // update last update date
lastUpdateRelay.observeOn(AndroidSchedulers.mainThread()) controller.setLastUpdateDate(lastUpdateDate)
.subscribeLatestCache(MangaAllInOneController::setLastUpdateDate) }
}
} }
private fun getMangaObservable(): Observable<Manga> { private fun updateManga() {
return db.getManga(manga.url, manga.source).asRxObservable() scope.launch(Dispatchers.IO) {
.observeOn(AndroidSchedulers.mainThread()) val manga = db.getManga(manga.url, manga.source).executeAsBlocking()!!
updateChapters()
updateChapterInfo()
withContext(Dispatchers.Main) {
controller.onNextManga(
manga, source, chapters
)
}
}
} }
/** /**
* Fetch manga information from source. * Fetch manga information from source.
*/ */
fun fetchMangaFromSource(manualFetch: Boolean = false) { fun fetchMangaFromSource(manualFetch: Boolean = false, FetchManga: Boolean = true, FetchChapters: Boolean = true) {
if (!fetchMangaSubscription.isNullOrUnsubscribed()) return hasRequested = true
fetchMangaSubscription = Observable.defer { source.fetchMangaDetails(manga) }
.map { networkManga -> scope.launch(Dispatchers.IO) {
if (FetchManga) {
val networkManga = try {
source.fetchMangaDetails(manga).toBlocking().single()
} catch (e: Exception) {
withContext(Dispatchers.Main) { controller.onFetchMangaError(e) }
return@launch
}
if (networkManga != null) {
manga.prepUpdateCover(coverCache, networkManga, manualFetch) manga.prepUpdateCover(coverCache, networkManga, manualFetch)
manga.copyFrom(networkManga) manga.copyFrom(networkManga)
manga.initialized = true manga.initialized = true
db.insertManga(manga).executeAsBlocking() db.insertManga(manga).executeAsBlocking()
manga
} }
.subscribeOn(Schedulers.io()) }
.observeOn(AndroidSchedulers.mainThread()) var chapters: List<SChapter> = listOf()
.subscribeFirst( if (FetchChapters) {
{ view, _ -> try {
view.onFetchMangaDone() chapters = source.fetchChapterList(manga).toBlocking().single()
}, } catch (e: Exception) {
MangaAllInOneController::onFetchMangaError withContext(Dispatchers.Main) { controller.onFetchMangaError(e) }
) return@launch
}
}
try {
if (FetchChapters) {
syncChaptersWithSource(db, chapters, manga, source)
updateChapters()
updateChapterInfo()
}
withContext(Dispatchers.Main) {
controller.onNextManga(this@MangaAllInOnePresenter.manga, this@MangaAllInOnePresenter.source, this@MangaAllInOnePresenter.chapters)
controller.onFetchMangaDone()
}
} catch (e: java.lang.Exception) {
withContext(Dispatchers.Main) {
controller.onFetchMangaError(e)
}
}
}
} }
/** /**
@ -404,49 +402,23 @@ class MangaAllInOnePresenter(
} }
} }
/**
* Requests an updated list of chapters from the source.
*/
fun fetchChaptersFromSource() {
hasRequested = true
if (!fetchChaptersSubscription.isNullOrUnsubscribed()) return
fetchChaptersSubscription = Observable.defer { source.fetchChapterList(manga) }
.subscribeOn(Schedulers.io())
.map { syncChaptersWithSource(db, it, manga, source) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onFetchChaptersDone()
},
MangaAllInOneController::onFetchChaptersError
)
}
/**
* Updates the UI after applying the filters.
*/
private fun refreshChapters() {
chaptersRelay.call(chapters)
}
/** /**
* Applies the view filters to the list of chapters obtained from the database. * Applies the view filters to the list of chapters obtained from the database.
* @param chapters the list of chapters from the database * @param chapters the list of chapters from the database
* @return an observable of the list of chapters filtered and sorted. * @return an observable of the list of chapters filtered and sorted.
*/ */
private fun applyChapterFilters(chapters: List<ChapterItem>): Observable<List<ChapterItem>> { private fun applyChapterFilters(chapterList: List<ChapterItem>): List<ChapterItem> {
var observable = Observable.from(chapters).subscribeOn(Schedulers.io()) var chapters = chapterList
if (onlyUnread()) { if (onlyUnread()) {
observable = observable.filter { !it.read } chapters = chapters.filter { !it.read }
} else if (onlyRead()) { } else if (onlyRead()) {
observable = observable.filter { it.read } chapters = chapters.filter { it.read }
} }
if (onlyDownloaded()) { if (onlyDownloaded()) {
observable = observable.filter { it.isDownloaded || it.manga.source == LocalSource.ID } chapters = chapters.filter { it.isDownloaded || it.manga.source == LocalSource.ID }
} }
if (onlyBookmarked()) { if (onlyBookmarked()) {
observable = observable.filter { it.bookmark } chapters = chapters.filter { it.bookmark }
} }
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) { Manga.SORTING_SOURCE -> when (sortDescending()) {
@ -457,9 +429,10 @@ class MangaAllInOnePresenter(
true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) } true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) }
false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) } false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
} }
else -> throw NotImplementedError("Unimplemented sorting method") else -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
} }
return observable.toSortedList(sortFunction) chapters = chapters.sortedWith(Comparator(sortFunction))
return chapters
} }
/** /**
@ -478,7 +451,7 @@ class MangaAllInOnePresenter(
// Force UI update if downloaded filter active and download finished. // Force UI update if downloaded filter active and download finished.
if (onlyDownloaded() && download.status == Download.DOWNLOADED) { if (onlyDownloaded() && download.status == Download.DOWNLOADED) {
refreshChapters() updateChapters()
} }
} }
@ -541,7 +514,7 @@ class MangaAllInOnePresenter(
fun deleteChapters(chapters: List<ChapterItem>) { fun deleteChapters(chapters: List<ChapterItem>) {
Observable.just(chapters) Observable.just(chapters)
.doOnNext { deleteChaptersInternal(chapters) } .doOnNext { deleteChaptersInternal(chapters) }
.doOnNext { if (onlyDownloaded()) refreshChapters() } .doOnNext { if (onlyDownloaded()) updateChapters() }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeFirst( .subscribeFirst(
@ -570,7 +543,7 @@ class MangaAllInOnePresenter(
fun revertSortOrder() { fun revertSortOrder() {
manga.setChapterOrder(if (sortDescending()) Manga.SORT_ASC else Manga.SORT_DESC) manga.setChapterOrder(if (sortDescending()) Manga.SORT_ASC else Manga.SORT_DESC)
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**
@ -580,7 +553,7 @@ class MangaAllInOnePresenter(
fun setUnreadFilter(onlyUnread: Boolean) { fun setUnreadFilter(onlyUnread: Boolean) {
manga.readFilter = if (onlyUnread) Manga.SHOW_UNREAD else Manga.SHOW_ALL manga.readFilter = if (onlyUnread) Manga.SHOW_UNREAD else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**
@ -590,7 +563,7 @@ class MangaAllInOnePresenter(
fun setReadFilter(onlyRead: Boolean) { fun setReadFilter(onlyRead: Boolean) {
manga.readFilter = if (onlyRead) Manga.SHOW_READ else Manga.SHOW_ALL manga.readFilter = if (onlyRead) Manga.SHOW_READ else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**
@ -600,7 +573,7 @@ class MangaAllInOnePresenter(
fun setDownloadedFilter(onlyDownloaded: Boolean) { fun setDownloadedFilter(onlyDownloaded: Boolean) {
manga.downloadedFilter = if (onlyDownloaded) Manga.SHOW_DOWNLOADED else Manga.SHOW_ALL manga.downloadedFilter = if (onlyDownloaded) Manga.SHOW_DOWNLOADED else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**
@ -610,7 +583,7 @@ class MangaAllInOnePresenter(
fun setBookmarkedFilter(onlyBookmarked: Boolean) { fun setBookmarkedFilter(onlyBookmarked: Boolean) {
manga.bookmarkedFilter = if (onlyBookmarked) Manga.SHOW_BOOKMARKED else Manga.SHOW_ALL manga.bookmarkedFilter = if (onlyBookmarked) Manga.SHOW_BOOKMARKED else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**
@ -621,14 +594,14 @@ class MangaAllInOnePresenter(
manga.downloadedFilter = Manga.SHOW_ALL manga.downloadedFilter = Manga.SHOW_ALL
manga.bookmarkedFilter = Manga.SHOW_ALL manga.bookmarkedFilter = Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**
* Adds manga to library * Adds manga to library
*/ */
fun addToLibrary() { fun addToLibrary() {
mangaFavoriteRelay.call(true) setFavorite(true)
} }
/** /**
@ -647,7 +620,7 @@ class MangaAllInOnePresenter(
fun setSorting(sort: Int) { fun setSorting(sort: Int) {
manga.sorting = sort manga.sorting = sort
db.updateFlags(manga).executeAsBlocking() db.updateFlags(manga).executeAsBlocking()
refreshChapters() updateChapters()
} }
/** /**

View File

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.TrackControllerBinding import eu.kanade.tachiyomi.databinding.TrackControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
@ -17,7 +18,7 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.swiperefreshlayout.refreshes import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber import timber.log.Timber
class TrackController : class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = null) :
NucleusController<TrackControllerBinding, TrackPresenter>(), NucleusController<TrackControllerBinding, TrackPresenter>(),
TrackAdapter.OnClickListener, TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener, SetTrackStatusDialog.Listener,
@ -34,7 +35,13 @@ class TrackController :
} }
override fun createPresenter(): TrackPresenter { override fun createPresenter(): TrackPresenter {
return TrackPresenter((parentController as MangaController).manga!!) return (
if (fromAllInOne && manga != null) {
TrackPresenter(manga)
} else {
TrackPresenter((parentController as MangaController).manga!!)
}
)
} }
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@ -63,8 +70,10 @@ class TrackController :
val atLeastOneLink = trackings.any { it.track != null } val atLeastOneLink = trackings.any { it.track != null }
adapter?.items = trackings adapter?.items = trackings
binding.swipeRefresh.isEnabled = atLeastOneLink binding.swipeRefresh.isEnabled = atLeastOneLink
if (!fromAllInOne) {
(parentController as? MangaController)?.setTrackingIcon(atLeastOneLink) (parentController as? MangaController)?.setTrackingIcon(atLeastOneLink)
} }
}
fun onSearchResults(results: List<TrackSearch>) { fun onSearchResults(results: List<TrackSearch>) {
getSearchDialog()?.onSearchResults(results) getSearchDialog()?.onSearchResults(results)

View File

@ -204,16 +204,25 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_source_label" /> app:layout_constraintStart_toEndOf="@+id/manga_source_label" />
<HorizontalScrollView
android:id="@+id/actions_bar_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_source" >
<LinearLayout <LinearLayout
android:id="@+id/actions_bar" android:id="@+id/actions_bar"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingTop="8dp" android:paddingTop="8dp"
android:paddingBottom="8dp" android:paddingBottom="8dp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_source"> app:layout_constraintTop_toBottomOf="parent">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite" android:id="@+id/btn_favorite"
@ -223,6 +232,17 @@
android:text="@string/add_to_library" android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" /> app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/track"
android:visibility="gone"
tools:visibility="visible"
app:icon="@drawable/ic_cloud_off_24dp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories" android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless" style="@style/Theme.Widget.Button.Icon.Textless"
@ -280,6 +300,8 @@
</LinearLayout> </LinearLayout>
</HorizontalScrollView>
<TextView <TextView
android:id="@+id/manga_summary_label" android:id="@+id/manga_summary_label"
style="@style/TextAppearance.Regular.SubHeading" style="@style/TextAppearance.Regular.SubHeading"
@ -290,7 +312,7 @@
android:textIsSelectable="false" android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/actions_bar" /> app:layout_constraintTop_toBottomOf="@+id/actions_bar_scroll_view" />
<TextView <TextView
android:id="@+id/manga_summary" android:id="@+id/manga_summary"

View File

@ -243,7 +243,7 @@
<LinearLayout <LinearLayout
android:id="@+id/actions_bar" android:id="@+id/actions_bar"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingStart="16dp" android:paddingStart="16dp"
@ -259,6 +259,17 @@
android:text="@string/add_to_library" android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" /> app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/track"
android:visibility="gone"
tools:visibility="visible"
app:icon="@drawable/ic_cloud_off_24dp" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories" android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless" style="@style/Theme.Widget.Button.Icon.Textless"