123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- package nucleus.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.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 <View> a type of view.
- */
- public class RxPresenter<View> extends Presenter<View> {
- private static final String REQUESTED_KEY = RxPresenter.class.getName() + "#requested";
- private final BehaviorSubject<View> views = BehaviorSubject.create();
- private final SubscriptionList subscriptions = new SubscriptionList();
- private final HashMap<Integer, Func0<Subscription>> restartables = new HashMap<>();
- private final HashMap<Integer, Subscription> restartableSubscriptions = new HashMap<>();
- private final ArrayList<Integer> 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> 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<Subscription> 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) {
- Subscription subscription = restartableSubscriptions.get(restartableId);
- 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 <T> the type of the observable.
- */
- public <T> void restartableFirst(int restartableId, final Func0<Observable<T>> observableFactory,
- final Action2<View, T> onNext, @Nullable final Action2<View, Throwable> onError) {
- restartable(restartableId, new Func0<Subscription>() {
- @Override
- public Subscription call() {
- return observableFactory.call()
- .compose(RxPresenter.this.<T>deliverFirst())
- .subscribe(split(onNext, onError));
- }
- });
- }
- /**
- * This is a shortcut for calling {@link #restartableFirst(int, Func0, Action2, Action2)} with the last parameter = null.
- */
- public <T> void restartableFirst(int restartableId, final Func0<Observable<T>> observableFactory, final Action2<View, T> 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 <T> the type of the observable.
- */
- public <T> void restartableLatestCache(int restartableId, final Func0<Observable<T>> observableFactory,
- final Action2<View, T> onNext, @Nullable final Action2<View, Throwable> onError) {
- restartable(restartableId, new Func0<Subscription>() {
- @Override
- public Subscription call() {
- return observableFactory.call()
- .compose(RxPresenter.this.<T>deliverLatestCache())
- .subscribe(split(onNext, onError));
- }
- });
- }
- /**
- * This is a shortcut for calling {@link #restartableLatestCache(int, Func0, Action2, Action2)} with the last parameter = null.
- */
- public <T> void restartableLatestCache(int restartableId, final Func0<Observable<T>> observableFactory, final Action2<View, T> 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 <T> the type of the observable.
- */
- public <T> void restartableReplay(int restartableId, final Func0<Observable<T>> observableFactory,
- final Action2<View, T> onNext, @Nullable final Action2<View, Throwable> onError) {
- restartable(restartableId, new Func0<Subscription>() {
- @Override
- public Subscription call() {
- return observableFactory.call()
- .compose(RxPresenter.this.<T>deliverReplay())
- .subscribe(split(onNext, onError));
- }
- });
- }
- /**
- * This is a shortcut for calling {@link #restartableReplay(int, Func0, Action2, Action2)} with the last parameter = null.
- */
- public <T> void restartableReplay(int restartableId, final Func0<Observable<T>> observableFactory, final Action2<View, T> onNext) {
- restartableReplay(restartableId, 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 <T> the type of source observable emissions
- */
- public <T> DeliverLatestCache<View, T> 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 <T> the type of source observable emissions
- */
- public <T> DeliverFirst<View, T> 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 <T> the type of source observable emissions
- */
- public <T> DeliverReplay<View, T> 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 <T> a type on onNext value.
- * @return an Action1 that splits a received {@link Delivery} into two {@link Action2} onNext and onError calls.
- */
- public <T> Action1<Delivery<View, T>> split(final Action2<View, T> onNext, @Nullable final Action2<View, Throwable> onError) {
- return new Action1<Delivery<View, T>>() {
- @Override
- public void call(Delivery<View, T> delivery) {
- delivery.split(onNext, onError);
- }
- };
- }
- /**
- * This is a shortcut for calling {@link #split(Action2, Action2)} when the second parameter is null.
- */
- public <T> Action1<Delivery<View, T>> split(Action2<View, T> 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<Integer, Subscription> 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();
- }
- }
|