diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt index 7ae7c7a58..24397e991 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/backup/BackupPresenter.kt @@ -4,6 +4,7 @@ import android.os.Bundle import eu.kanade.tachiyomi.data.backup.BackupManager import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -48,13 +49,13 @@ class BackupPresenter : BasePresenter() { * @param file the path where the file will be saved. */ fun createBackup(file: File) { - if (isUnsubscribed(backupSubscription)) { + if (backupSubscription.isNullOrUnsubscribed()) { backupSubscription = getBackupObservable(file) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeFirst( { view, result -> view.onBackupCompleted(file) }, - { view, error -> view.onBackupError(error) }) + BackupFragment::onBackupError) } } @@ -64,13 +65,13 @@ class BackupPresenter : BasePresenter() { * @param stream the input stream of the backup file. */ fun restoreBackup(stream: InputStream) { - if (isUnsubscribed(restoreSubscription)) { + if (restoreSubscription.isNullOrUnsubscribed()) { restoreSubscription = getRestoreObservable(stream) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeFirst( { view, result -> view.onRestoreCompleted() }, - { view, error -> view.onRestoreError(error) }) + BackupFragment::onRestoreError) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/BasePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/BasePresenter.kt index dc91615b3..fbf756a5b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/BasePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/BasePresenter.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.ui.base.presenter import android.content.Context +import nucleus.presenter.RxPresenter import nucleus.view.ViewWithPresenter import rx.Observable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/RxPresenter.java b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/RxPresenter.java deleted file mode 100644 index 370ab64f9..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/RxPresenter.java +++ /dev/null @@ -1,492 +0,0 @@ -package eu.kanade.tachiyomi.ui.base.presenter; - -import android.os.Bundle; -import android.support.annotation.CallSuper; -import android.support.annotation.Nullable; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -import nucleus.presenter.Presenter; -import nucleus.presenter.delivery.DeliverFirst; -import nucleus.presenter.delivery.DeliverLatestCache; -import nucleus.presenter.delivery.DeliverReplay; -import nucleus.presenter.delivery.Delivery; -import rx.Observable; -import rx.Subscription; -import rx.functions.Action1; -import rx.functions.Action2; -import rx.functions.Func0; -import rx.internal.util.SubscriptionList; -import rx.subjects.BehaviorSubject; - -/** - * This is an extension of {@link Presenter} which provides RxJava functionality. - * - * @param a type of view. - */ -public class RxPresenter extends Presenter { - - private static final String REQUESTED_KEY = RxPresenter.class.getName() + "#requested"; - - private final BehaviorSubject views = BehaviorSubject.create(); - private final SubscriptionList subscriptions = new SubscriptionList(); - - private final HashMap> restartables = new HashMap<>(); - private final HashMap restartableSubscriptions = new HashMap<>(); - private final ArrayList requested = new ArrayList<>(); - - /** - * Returns an {@link rx.Observable} that emits the current attached view or null. - * See {@link BehaviorSubject} for more information. - * - * @return an observable that emits the current attached view or null. - */ - public Observable view() { - return views; - } - - /** - * Registers a subscription to automatically unsubscribe it during onDestroy. - * See {@link SubscriptionList#add(Subscription) for details.} - * - * @param subscription a subscription to add. - */ - public void add(Subscription subscription) { - subscriptions.add(subscription); - } - - /** - * Removes and unsubscribes a subscription that has been registered with {@link #add} previously. - * See {@link SubscriptionList#remove(Subscription)} for details. - * - * @param subscription a subscription to remove. - */ - public void remove(Subscription subscription) { - subscriptions.remove(subscription); - } - - /** - * A restartable is any RxJava observable that can be started (subscribed) and - * should be automatically restarted (re-subscribed) after a process restart if - * it was still subscribed at the moment of saving presenter's state. - * - * Registers a factory. Re-subscribes the restartable after the process restart. - * - * @param restartableId id of the restartable - * @param factory factory of the restartable - */ - public void restartable(int restartableId, Func0 factory) { - restartables.put(restartableId, factory); - if (requested.contains(restartableId)) - start(restartableId); - } - - /** - * Starts the given restartable. - * - * @param restartableId id of the restartable - */ - public void start(int restartableId) { - stop(restartableId); - requested.add(restartableId); - restartableSubscriptions.put(restartableId, restartables.get(restartableId).call()); - } - - /** - * Unsubscribes a restartable - * - * @param restartableId id of a restartable. - */ - public void stop(int restartableId) { - requested.remove((Integer) restartableId); - Subscription subscription = restartableSubscriptions.get(restartableId); - if (subscription != null) - subscription.unsubscribe(); - } - - /** - * Checks if a restartable is unsubscribed. - * - * @param restartableId id of the restartable. - * @return true if the subscription is null or unsubscribed, false otherwise. - */ - public boolean isUnsubscribed(int restartableId) { - return isUnsubscribed(restartableSubscriptions.get(restartableId)); - } - - /** - * Checks if a subscription is unsubscribed. - * - * @param subscription the subscription to check. - * @return true if the subscription is null or unsubscribed, false otherwise. - */ - public boolean isUnsubscribed(@Nullable Subscription subscription) { - return subscription == null || subscription.isUnsubscribed(); - } - - /** - * This is a shortcut that can be used instead of combining together - * {@link #restartable(int, Func0)}, - * {@link #deliverFirst()}, - * {@link #split(Action2, Action2)}. - * - * @param restartableId an id of the restartable. - * @param observableFactory a factory that should return an Observable when the restartable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - * @param the type of the observable. - */ - public void restartableFirst(int restartableId, final Func0> observableFactory, - final Action2 onNext, @Nullable final Action2 onError) { - - restartable(restartableId, new Func0() { - @Override - public Subscription call() { - return observableFactory.call() - .compose(RxPresenter.this.deliverFirst()) - .subscribe(split(onNext, onError)); - } - }); - } - - /** - * This is a shortcut for calling {@link #restartableFirst(int, Func0, Action2, Action2)} with the last parameter = null. - */ - public void restartableFirst(int restartableId, final Func0> observableFactory, final Action2 onNext) { - restartableFirst(restartableId, observableFactory, onNext, null); - } - - /** - * This is a shortcut that can be used instead of combining together - * {@link #restartable(int, Func0)}, - * {@link #deliverLatestCache()}, - * {@link #split(Action2, Action2)}. - * - * @param restartableId an id of the restartable. - * @param observableFactory a factory that should return an Observable when the restartable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - * @param the type of the observable. - */ - public void restartableLatestCache(int restartableId, final Func0> observableFactory, - final Action2 onNext, @Nullable final Action2 onError) { - - restartable(restartableId, new Func0() { - @Override - public Subscription call() { - return observableFactory.call() - .compose(RxPresenter.this.deliverLatestCache()) - .subscribe(split(onNext, onError)); - } - }); - } - - /** - * This is a shortcut for calling {@link #restartableLatestCache(int, Func0, Action2, Action2)} with the last parameter = null. - */ - public void restartableLatestCache(int restartableId, final Func0> observableFactory, final Action2 onNext) { - restartableLatestCache(restartableId, observableFactory, onNext, null); - } - - /** - * This is a shortcut that can be used instead of combining together - * {@link #restartable(int, Func0)}, - * {@link #deliverReplay()}, - * {@link #split(Action2, Action2)}. - * - * @param restartableId an id of the restartable. - * @param observableFactory a factory that should return an Observable when the restartable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - * @param the type of the observable. - */ - public void restartableReplay(int restartableId, final Func0> observableFactory, - final Action2 onNext, @Nullable final Action2 onError) { - - restartable(restartableId, new Func0() { - @Override - public Subscription call() { - return observableFactory.call() - .compose(RxPresenter.this.deliverReplay()) - .subscribe(split(onNext, onError)); - } - }); - } - - /** - * This is a shortcut for calling {@link #restartableReplay(int, Func0, Action2, Action2)} with the last parameter = null. - */ - public void restartableReplay(int restartableId, final Func0> observableFactory, final Action2 onNext) { - restartableReplay(restartableId, observableFactory, onNext, null); - } - - /** - * A startable behaves the same as a restartable but it does not resubscribe on process restart - * - * @param startableId an id of the restartable. - * @param observableFactory a factory that should return an Observable when the startable should run. - */ - public void startable(int startableId, final Func0> observableFactory) { - restartables.put(startableId, new Func0() { - @Override - public Subscription call() {return observableFactory.call().subscribe();} - }); - } - - /** - * A startable behaves the same as a restartable but it does not resubscribe on process restart - * - * @param startableId an id of the restartable. - * @param observableFactory a factory that should return an Observable when the startable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - */ - public void startable(int startableId, final Func0> observableFactory, - final Action1 onNext, final Action1 onError) { - - restartables.put(startableId, new Func0() { - @Override - public Subscription call() {return observableFactory.call().subscribe(onNext, onError);} - }); - } - - /** - * A startable behaves the same as a restartable but it does not resubscribe on process restart - * - * @param startableId an id of the restartable. - * @param observableFactory a factory that should return an Observable when the startable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - */ - public void startable(int startableId, final Func0> observableFactory, final Action1 onNext) { - restartables.put(startableId, new Func0() { - @Override - public Subscription call() {return observableFactory.call().subscribe(onNext);} - }); - } - - /** - * This is a shortcut that can be used instead of combining together - * {@link #startable(int, Func0)}, - * {@link #deliverFirst()}, - * {@link #split(Action2, Action2)}. - * - * @param startableId an id of the startable. - * @param observableFactory a factory that should return an Observable when the startable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - * @param the type of the observable. - */ - public void startableFirst(int startableId, final Func0> observableFactory, - final Action2 onNext, @Nullable final Action2 onError) { - - restartables.put(startableId, new Func0() { - @Override - public Subscription call() { - return observableFactory.call() - .compose(RxPresenter.this.deliverFirst()) - .subscribe(split(onNext, onError)); - } - }); - } - - /** - * This is a shortcut for calling {@link #startableFirst(int, Func0, Action2, Action2)} with the last parameter = null. - */ - public void startableFirst(int startableId, final Func0> observableFactory, final Action2 onNext) { - startableFirst(startableId, observableFactory, onNext, null); - } - - /** - * This is a shortcut that can be used instead of combining together - * {@link #startable(int, Func0)}, - * {@link #deliverLatestCache()}, - * {@link #split(Action2, Action2)}. - * - * @param startableId an id of the startable. - * @param observableFactory a factory that should return an Observable when the startable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - * @param the type of the observable. - */ - public void startableLatestCache(int startableId, final Func0> observableFactory, - final Action2 onNext, @Nullable final Action2 onError) { - - restartables.put(startableId, new Func0() { - @Override - public Subscription call() { - return observableFactory.call() - .compose(RxPresenter.this.deliverLatestCache()) - .subscribe(split(onNext, onError)); - } - }); - } - - /** - * This is a shortcut for calling {@link #startableLatestCache(int, Func0, Action2, Action2)} with the last parameter = null. - */ - public void startableLatestCache(int startableId, final Func0> observableFactory, final Action2 onNext) { - startableLatestCache(startableId, observableFactory, onNext, null); - } - - /** - * This is a shortcut that can be used instead of combining together - * {@link #startable(int, Func0)}, - * {@link #deliverReplay()}, - * {@link #split(Action2, Action2)}. - * - * @param startableId an id of the startable. - * @param observableFactory a factory that should return an Observable when the startable should run. - * @param onNext a callback that will be called when received data should be delivered to view. - * @param onError a callback that will be called if the source observable emits onError. - * @param the type of the observable. - */ - public void startableReplay(int startableId, final Func0> observableFactory, - final Action2 onNext, @Nullable final Action2 onError) { - - restartables.put(startableId, new Func0() { - @Override - public Subscription call() { - return observableFactory.call() - .compose(RxPresenter.this.deliverReplay()) - .subscribe(split(onNext, onError)); - } - }); - } - - /** - * This is a shortcut for calling {@link #startableReplay(int, Func0, Action2, Action2)} with the last parameter = null. - */ - public void startableReplay(int startableId, final Func0> observableFactory, final Action2 onNext) { - startableReplay(startableId, observableFactory, onNext, null); - } - - /** - * Returns an {@link rx.Observable.Transformer} that couples views with data that has been emitted by - * the source {@link rx.Observable}. - * - * {@link #deliverLatestCache} keeps the latest onNext value and emits it each time a new view gets attached. - * If a new onNext value appears while a view is attached, it will be delivered immediately. - * - * @param the type of source observable emissions - */ - public DeliverLatestCache deliverLatestCache() { - return new DeliverLatestCache<>(views); - } - - /** - * Returns an {@link rx.Observable.Transformer} that couples views with data that has been emitted by - * the source {@link rx.Observable}. - * - * {@link #deliverFirst} delivers only the first onNext value that has been emitted by the source observable. - * - * @param the type of source observable emissions - */ - public DeliverFirst deliverFirst() { - return new DeliverFirst<>(views); - } - - /** - * Returns an {@link rx.Observable.Transformer} that couples views with data that has been emitted by - * the source {@link rx.Observable}. - * - * {@link #deliverReplay} keeps all onNext values and emits them each time a new view gets attached. - * If a new onNext value appears while a view is attached, it will be delivered immediately. - * - * @param the type of source observable emissions - */ - public DeliverReplay deliverReplay() { - return new DeliverReplay<>(views); - } - - /** - * Returns a method that can be used for manual restartable chain build. It returns an Action1 that splits - * a received {@link Delivery} into two {@link Action2} onNext and onError calls. - * - * @param onNext a method that will be called if the delivery contains an emitted onNext value. - * @param onError a method that will be called if the delivery contains an onError throwable. - * @param a type on onNext value. - * @return an Action1 that splits a received {@link Delivery} into two {@link Action2} onNext and onError calls. - */ - public Action1> split(final Action2 onNext, @Nullable final Action2 onError) { - return new Action1>() { - @Override - public void call(Delivery delivery) { - delivery.split(onNext, onError); - } - }; - } - - /** - * This is a shortcut for calling {@link #split(Action2, Action2)} when the second parameter is null. - */ - public Action1> split(Action2 onNext) { - return split(onNext, null); - } - - /** - * {@inheritDoc} - */ - @CallSuper - @Override - protected void onCreate(Bundle savedState) { - if (savedState != null) - requested.addAll(savedState.getIntegerArrayList(REQUESTED_KEY)); - } - - /** - * {@inheritDoc} - */ - @CallSuper - @Override - protected void onDestroy() { - views.onCompleted(); - subscriptions.unsubscribe(); - for (Map.Entry entry : restartableSubscriptions.entrySet()) - entry.getValue().unsubscribe(); - } - - /** - * {@inheritDoc} - */ - @CallSuper - @Override - protected void onSave(Bundle state) { - for (int i = requested.size() - 1; i >= 0; i--) { - int restartableId = requested.get(i); - Subscription subscription = restartableSubscriptions.get(restartableId); - if (subscription != null && subscription.isUnsubscribed()) - requested.remove(i); - } - state.putIntegerArrayList(REQUESTED_KEY, requested); - } - - /** - * {@inheritDoc} - */ - @CallSuper - @Override - protected void onTakeView(View view) { - views.onNext(view); - } - - /** - * {@inheritDoc} - */ - @CallSuper - @Override - protected void onDropView() { - views.onNext(null); - } - - /** - * Please, use restartableXX and deliverXX methods for pushing data from RxPresenter into View. - */ - @Deprecated - @Nullable - @Override - public View getView() { - return super.getView(); - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 08a8ed626..bf9edbc0a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.info.ChapterCountEvent import eu.kanade.tachiyomi.ui.manga.info.MangaFavoriteEvent import eu.kanade.tachiyomi.util.SharedData +import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import rx.Observable import rx.Subscription import uy.kohesive.injekt.injectLazy @@ -44,10 +45,10 @@ class MangaPresenter : BasePresenter() { } fun setMangaEvent(event: MangaEvent) { - if (isUnsubscribed(mangaSubscription)) { + if (mangaSubscription.isNullOrUnsubscribed()) { manga = event.manga mangaSubscription = Observable.just(manga) - .subscribeLatestCache({ view, manga -> view.onSetManga(manga) }) + .subscribeLatestCache(MangaActivity::onSetManga) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt index cb3303f39..91eb91e5f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersPresenter.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.ui.manga.chapter import android.os.Bundle +import com.jakewharton.rxrelay.PublishRelay import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga @@ -15,11 +16,12 @@ import eu.kanade.tachiyomi.ui.manga.MangaEvent import eu.kanade.tachiyomi.ui.manga.info.ChapterCountEvent import eu.kanade.tachiyomi.ui.manga.info.MangaFavoriteEvent import eu.kanade.tachiyomi.util.SharedData +import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import eu.kanade.tachiyomi.util.syncChaptersWithSource 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.injectLazy @@ -69,8 +71,8 @@ class ChaptersPresenter : BasePresenter() { /** * Subject of list of chapters to allow updating the view without going to DB. */ - val chaptersSubject: PublishSubject> - by lazy { PublishSubject.create>() } + val chaptersRelay: PublishRelay> + by lazy { PublishRelay.create>() } /** * Whether the chapter list has been requested to the source. @@ -78,56 +80,33 @@ class ChaptersPresenter : BasePresenter() { var hasRequested = false private set - companion object { - /** - * Id of the restartable which sends a filtered and ordered list of chapters to the view. - */ - private const val GET_CHAPTERS = 1 + /** + * Subscription to retrieve the new list of chapters from the source. + */ + private var fetchChaptersSubscription: Subscription? = null - /** - * Id of the restartable which requests an updated list of chapters to the source. - */ - private const val FETCH_CHAPTERS = 2 - - /** - * Id of the restartable which listens for download status changes. - */ - private const val CHAPTER_STATUS_CHANGES = 3 - } + /** + * Subscription to observe download status changes. + */ + private var observeDownloadsSubscription: Subscription? = null override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) - startableLatestCache(GET_CHAPTERS, - // On each subject emission, apply filters and sort then update the view. - { chaptersSubject - .flatMap { applyChapterFilters(it) } - .observeOn(AndroidSchedulers.mainThread()) - }, ChaptersFragment::onNextChapters) - - startableFirst(FETCH_CHAPTERS, - { getRemoteChaptersObservable() }, - { view, result -> view.onFetchChaptersDone() }, - ChaptersFragment::onFetchChaptersError) - - startableLatestCache(CHAPTER_STATUS_CHANGES, - { getChapterStatusObservable() }, - ChaptersFragment::onChapterStatusChange, - { view, error -> Timber.e(error) }) - // Find the active manga from the shared data or return. manga = SharedData.get(MangaEvent::class.java)?.manga ?: return + source = sourceManager.get(manga.source)!! Observable.just(manga) .subscribeLatestCache(ChaptersFragment::onNextManga) - // Find the source for this manga. - source = sourceManager.get(manga.source)!! - - // Prepare the publish subject. - start(GET_CHAPTERS) + // Prepare the relay. + chaptersRelay.flatMap { applyChapterFilters(it) } + .observeOn(AndroidSchedulers.mainThread()) + .subscribeLatestCache(ChaptersFragment::onNextChapters, + { view, 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 publish subject. + // changes, and sends the list of chapters to the relay. add(db.getChapters(manga).asRxObservable() .map { chapters -> // Convert every chapter to a model. @@ -141,12 +120,22 @@ class ChaptersPresenter : BasePresenter() { this.chapters = chapters // Listen for download status changes - start(CHAPTER_STATUS_CHANGES) + observeDownloads() // Emit the number of chapters to the info tab. SharedData.get(ChapterCountEvent::class.java)?.emit(chapters.size) } - .subscribe { chaptersSubject.onNext(it) }) + .subscribe { chaptersRelay.call(it) }) + } + + private fun observeDownloads() { + observeDownloadsSubscription?.let { remove(it) } + observeDownloadsSubscription = downloadManager.queue.getStatusObservable() + .observeOn(AndroidSchedulers.mainThread()) + .filter { download -> download.manga.id == manga.id } + .doOnNext { onDownloadStatusChange(it) } + .subscribeLatestCache(ChaptersFragment::onChapterStatusChange, + { view, error -> Timber.e(error) }) } /** @@ -186,34 +175,24 @@ class ChaptersPresenter : BasePresenter() { */ fun fetchChaptersFromSource() { hasRequested = true - start(FETCH_CHAPTERS) + + if (!fetchChaptersSubscription.isNullOrUnsubscribed()) return + fetchChaptersSubscription = Observable.defer { source.fetchChapterList(manga) } + .subscribeOn(Schedulers.io()) + .map { syncChaptersWithSource(db, it, manga, source) } + .observeOn(AndroidSchedulers.mainThread()) + .subscribeFirst({ view, chapters -> + view.onFetchChaptersDone() + }, ChaptersFragment::onFetchChaptersError) } /** * Updates the UI after applying the filters. */ private fun refreshChapters() { - chaptersSubject.onNext(chapters) + chaptersRelay.call(chapters) } - /** - * Returns an observable that updates the chapter list with the latest from the source. - */ - fun getRemoteChaptersObservable(): Observable, List>> = - Observable.defer { source.fetchChapterList(manga) } - .subscribeOn(Schedulers.io()) - .map { syncChaptersWithSource(db, it, manga, source) } - .observeOn(AndroidSchedulers.mainThread()) - - /** - * Returns an observable that listens to download queue status changes. - */ - fun getChapterStatusObservable(): Observable = - downloadManager.queue.getStatusObservable() - .observeOn(AndroidSchedulers.mainThread()) - .filter { download -> download.manga.id == manga.id } - .doOnNext { onDownloadStatusChange(it) } - /** * Applies the view filters to the list of chapters obtained from the database. * @param chapters the list of chapters from the database @@ -224,7 +203,7 @@ class ChaptersPresenter : BasePresenter() { if (onlyUnread()) { observable = observable.filter { !it.read } } - if (onlyRead()) { + else if (onlyRead()) { observable = observable.filter { it.read } } if (onlyDownloaded()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt index 950b8e430..1de6f34e1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoPresenter.kt @@ -9,7 +9,9 @@ import eu.kanade.tachiyomi.data.source.SourceManager import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.manga.MangaEvent import eu.kanade.tachiyomi.util.SharedData +import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import rx.Observable +import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers import uy.kohesive.injekt.injectLazy @@ -49,32 +51,21 @@ class MangaInfoPresenter : BasePresenter() { val coverCache: CoverCache by injectLazy() /** - * The id of the restartable. + * Subscription to send the manga to the view. */ - private val GET_MANGA = 1 + private var viewMangaSubcription: Subscription? = null /** - * The id of the restartable. + * Subscription to update the manga from the source. */ - private val FETCH_MANGA_INFO = 2 + private var fetchMangaSubscription: Subscription? = null override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) - // Notify the view a manga is available or has changed. - startableLatestCache(GET_MANGA, - { Observable.just(manga) }, - { view, manga -> view.onNextManga(manga, source) }) - - // Fetch manga info from source. - startableFirst(FETCH_MANGA_INFO, - { fetchMangaObs() }, - { view, manga -> view.onFetchMangaDone() }, - { view, error -> view.onFetchMangaError() }) - manga = SharedData.get(MangaEvent::class.java)?.manga ?: return source = sourceManager.get(manga.source)!! - refreshManga() + sendMangaToView() // Update chapter count SharedData.get(ChapterCountEvent::class.java)?.observable @@ -88,30 +79,34 @@ class MangaInfoPresenter : BasePresenter() { } /** - * Fetch manga information from source. + * Sends the active manga to the view. */ - fun fetchMangaFromSource() { - if (isUnsubscribed(FETCH_MANGA_INFO)) { - start(FETCH_MANGA_INFO) - } + fun sendMangaToView() { + viewMangaSubcription?.let { remove(it) } + viewMangaSubcription = Observable.just(manga) + .subscribeLatestCache({ view, manga -> view.onNextManga(manga, source) }) } /** * Fetch manga information from source. - * - * @return manga information. */ - private fun fetchMangaObs(): Observable { - return Observable.defer { source.fetchMangaDetails(manga) } - .flatMap { networkManga -> + fun fetchMangaFromSource() { + if (!fetchMangaSubscription.isNullOrUnsubscribed()) return + fetchMangaSubscription = Observable.defer { source.fetchMangaDetails(manga) } + .map { networkManga -> manga.copyFrom(networkManga) manga.initialized = true db.insertManga(manga).executeAsBlocking() - Observable.just(manga) + manga } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .doOnNext { refreshManga() } + .doOnNext { sendMangaToView() } + .subscribeFirst({ view, manga -> + view.onFetchMangaDone() + }, { view, error -> + view.onFetchMangaError() + }) } /** @@ -123,19 +118,14 @@ class MangaInfoPresenter : BasePresenter() { coverCache.deleteFromCache(manga.thumbnail_url) } db.insertManga(manga).executeAsBlocking() - refreshManga() + sendMangaToView() } - private fun setFavorite(favorite:Boolean){ - if (manga.favorite == favorite) + private fun setFavorite(favorite: Boolean) { + if (manga.favorite == favorite) { return + } toggleFavorite() } - /** - * Refresh MangaInfo view. - */ - private fun refreshManga() { - start(GET_MANGA) - } }