Revert "Temporarily include nucleus in the project"
This reverts commit 447dfd1e3c
.
This commit is contained in:
parent
9213fc6999
commit
a9bfeb058b
19 changed files with 13 additions and 1288 deletions
|
@ -141,6 +141,9 @@ dependencies {
|
||||||
compile "com.pushtorefresh.storio:sqlite-annotations:$STORIO_VERSION"
|
compile "com.pushtorefresh.storio:sqlite-annotations:$STORIO_VERSION"
|
||||||
kapt "com.pushtorefresh.storio:sqlite-annotations-processor:$STORIO_VERSION"
|
kapt "com.pushtorefresh.storio:sqlite-annotations-processor:$STORIO_VERSION"
|
||||||
|
|
||||||
|
// Model View Presenter
|
||||||
|
compile 'info.android15.nucleus:nucleus:2.0.5'
|
||||||
|
|
||||||
// Dependency injection
|
// Dependency injection
|
||||||
compile "com.google.dagger:dagger:$DAGGER_VERSION"
|
compile "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||||
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
|
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
|
||||||
|
|
|
@ -87,9 +87,8 @@ public abstract class BaseRxActivity<P extends Presenter> extends BaseActivity i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onPause() {
|
||||||
super.onDestroy();
|
super.onPause();
|
||||||
presenterDelegate.onDropView();
|
presenterDelegate.onPause(isFinishing());
|
||||||
presenterDelegate.onDestroy(!isChangingConfigurations());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.kanade.tachiyomi.ui.base.fragment;
|
package eu.kanade.tachiyomi.ui.base.fragment;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.App;
|
import eu.kanade.tachiyomi.App;
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||||
|
@ -84,14 +85,13 @@ public abstract class BaseRxFragment<P extends Presenter> extends BaseFragment i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onPause() {
|
||||||
super.onDestroyView();
|
super.onPause();
|
||||||
presenterDelegate.onDropView();
|
presenterDelegate.onPause(getActivity().isFinishing() || isRemoving(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static boolean isRemoving(Fragment fragment) {
|
||||||
public void onDestroy() {
|
Fragment parent = fragment.getParentFragment();
|
||||||
super.onDestroy();
|
return fragment.isRemoving() || (parent != null && isRemoving(parent));
|
||||||
presenterDelegate.onDestroy(!getActivity().isChangingConfigurations());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package nucleus.factory;
|
|
||||||
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
public interface PresenterFactory<P extends Presenter> {
|
|
||||||
P createPresenter();
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
package nucleus.factory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the singleton where all presenters are stored.
|
|
||||||
*/
|
|
||||||
public enum PresenterStorage {
|
|
||||||
|
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
private HashMap<String, Presenter> idToPresenter = new HashMap<>();
|
|
||||||
private HashMap<Presenter, String> presenterToId = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a presenter to the storage
|
|
||||||
*
|
|
||||||
* @param presenter a presenter to add
|
|
||||||
*/
|
|
||||||
public void add(final Presenter presenter) {
|
|
||||||
String id = presenter.getClass().getSimpleName() + "/" + System.nanoTime() + "/" + (int)(Math.random() * Integer.MAX_VALUE);
|
|
||||||
idToPresenter.put(id, presenter);
|
|
||||||
presenterToId.put(presenter, id);
|
|
||||||
presenter.addOnDestroyListener(new Presenter.OnDestroyListener() {
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
idToPresenter.remove(presenterToId.remove(presenter));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a presenter by id or null if such presenter does not exist.
|
|
||||||
*
|
|
||||||
* @param id id of a presenter that has been received by calling {@link #getId(Presenter)}
|
|
||||||
* @param <P> a type of presenter
|
|
||||||
* @return a presenter or null
|
|
||||||
*/
|
|
||||||
public <P> P getPresenter(String id) {
|
|
||||||
//noinspection unchecked
|
|
||||||
return (P)idToPresenter.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns id of a given presenter.
|
|
||||||
*
|
|
||||||
* @param presenter a presenter to get id for.
|
|
||||||
* @return if of the presenter.
|
|
||||||
*/
|
|
||||||
public String getId(Presenter presenter) {
|
|
||||||
return presenterToId.get(presenter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all presenters from the storage.
|
|
||||||
* Use this method for testing purposes only.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
idToPresenter.clear();
|
|
||||||
presenterToId.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package nucleus.factory;
|
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents a {@link PresenterFactory} that creates a presenter using {@link Class#newInstance()} method.
|
|
||||||
*
|
|
||||||
* @param <P> the type of the presenter.
|
|
||||||
*/
|
|
||||||
public class ReflectionPresenterFactory<P extends Presenter> implements PresenterFactory<P> {
|
|
||||||
|
|
||||||
private Class<P> presenterClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method returns a {@link ReflectionPresenterFactory} instance if a given view class has
|
|
||||||
* a {@link RequiresPresenter} annotation, or null otherwise.
|
|
||||||
*
|
|
||||||
* @param viewClass a class of the view
|
|
||||||
* @param <P> a type of the presenter
|
|
||||||
* @return a {@link ReflectionPresenterFactory} instance that is supposed to create a presenter from {@link RequiresPresenter} annotation.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static <P extends Presenter> ReflectionPresenterFactory<P> fromViewClass(Class<?> viewClass) {
|
|
||||||
RequiresPresenter annotation = viewClass.getAnnotation(RequiresPresenter.class);
|
|
||||||
//noinspection unchecked
|
|
||||||
Class<P> presenterClass = annotation == null ? null : (Class<P>)annotation.value();
|
|
||||||
return presenterClass == null ? null : new ReflectionPresenterFactory<>(presenterClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReflectionPresenterFactory(Class<P> presenterClass) {
|
|
||||||
this.presenterClass = presenterClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public P createPresenter() {
|
|
||||||
try {
|
|
||||||
return presenterClass.newInstance();
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package nucleus.factory;
|
|
||||||
|
|
||||||
import java.lang.annotation.Inherited;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
@Inherited
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface RequiresPresenter {
|
|
||||||
Class<? extends Presenter> value();
|
|
||||||
}
|
|
|
@ -1,164 +0,0 @@
|
||||||
package nucleus.presenter;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.Fragment;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a base class for all presenters. Subclasses can override
|
|
||||||
* {@link #onCreate}, {@link #onDestroy}, {@link #onSave},
|
|
||||||
* {@link #onTakeView}, {@link #onDropView}.
|
|
||||||
* <p/>
|
|
||||||
* {@link Presenter.OnDestroyListener} can also be used by external classes
|
|
||||||
* to be notified about the need of freeing resources.
|
|
||||||
*
|
|
||||||
* @param <View> a type of view to return with {@link #getView()}.
|
|
||||||
*/
|
|
||||||
public class Presenter<View> {
|
|
||||||
|
|
||||||
@Nullable private View view;
|
|
||||||
private CopyOnWriteArrayList<OnDestroyListener> onDestroyListeners = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called after presenter construction.
|
|
||||||
*
|
|
||||||
* This method is intended for overriding.
|
|
||||||
*
|
|
||||||
* @param savedState If the presenter is being re-instantiated after a process restart then this Bundle
|
|
||||||
* contains the data it supplied in {@link #onSave}.
|
|
||||||
*/
|
|
||||||
protected void onCreate(@Nullable Bundle savedState) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is being called when a user leaves view.
|
|
||||||
*
|
|
||||||
* This method is intended for overriding.
|
|
||||||
*/
|
|
||||||
protected void onDestroy() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A returned state is the state that will be passed to {@link #onCreate} for a new presenter instance after a process restart.
|
|
||||||
*
|
|
||||||
* This method is intended for overriding.
|
|
||||||
*
|
|
||||||
* @param state a non-null bundle which should be used to put presenter's state into.
|
|
||||||
*/
|
|
||||||
protected void onSave(Bundle state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is being called when a view gets attached to it.
|
|
||||||
* Normally this happens during {@link Activity#onResume()}, {@link android.app.Fragment#onResume()}
|
|
||||||
* and {@link android.view.View#onAttachedToWindow()}.
|
|
||||||
*
|
|
||||||
* This method is intended for overriding.
|
|
||||||
*
|
|
||||||
* @param view a view that should be taken
|
|
||||||
*/
|
|
||||||
protected void onTakeView(View view) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is being called when a view gets detached from the presenter.
|
|
||||||
* Normally this happens during {@link Activity#onPause()} ()}, {@link Fragment#onPause()} ()}
|
|
||||||
* and {@link android.view.View#onDetachedFromWindow()}.
|
|
||||||
*
|
|
||||||
* This method is intended for overriding.
|
|
||||||
*/
|
|
||||||
protected void onDropView() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A callback to be invoked when a presenter is about to be destroyed.
|
|
||||||
*/
|
|
||||||
public interface OnDestroyListener {
|
|
||||||
/**
|
|
||||||
* Called before {@link Presenter#onDestroy()}.
|
|
||||||
*/
|
|
||||||
void onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener observing {@link #onDestroy}.
|
|
||||||
*
|
|
||||||
* @param listener a listener to add.
|
|
||||||
*/
|
|
||||||
public void addOnDestroyListener(OnDestroyListener listener) {
|
|
||||||
onDestroyListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removed a listener observing {@link #onDestroy}.
|
|
||||||
*
|
|
||||||
* @param listener a listener to remove.
|
|
||||||
*/
|
|
||||||
public void removeOnDestroyListener(OnDestroyListener listener) {
|
|
||||||
onDestroyListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current view attached to the presenter or null.
|
|
||||||
*
|
|
||||||
* View is normally available between
|
|
||||||
* {@link Activity#onResume()} and {@link Activity#onPause()},
|
|
||||||
* {@link Fragment#onResume()} and {@link Fragment#onPause()},
|
|
||||||
* {@link android.view.View#onAttachedToWindow()} and {@link android.view.View#onDetachedFromWindow()}.
|
|
||||||
*
|
|
||||||
* Calls outside of these ranges will return null.
|
|
||||||
* Notice here that {@link Activity#onActivityResult(int, int, Intent)} is called *before* {@link Activity#onResume()}
|
|
||||||
* so you can't use this method as a callback.
|
|
||||||
*
|
|
||||||
* @return a current attached view.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public View getView() {
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the presenter.
|
|
||||||
*/
|
|
||||||
public void create(Bundle bundle) {
|
|
||||||
onCreate(bundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the presenter, calling all {@link Presenter.OnDestroyListener} callbacks.
|
|
||||||
*/
|
|
||||||
public void destroy() {
|
|
||||||
for (OnDestroyListener listener : onDestroyListeners)
|
|
||||||
listener.onDestroy();
|
|
||||||
onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the presenter.
|
|
||||||
*/
|
|
||||||
public void save(Bundle state) {
|
|
||||||
onSave(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attaches a view to the presenter.
|
|
||||||
*
|
|
||||||
* @param view a view to attach.
|
|
||||||
*/
|
|
||||||
public void takeView(View view) {
|
|
||||||
this.view = view;
|
|
||||||
onTakeView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detaches the presenter from a view.
|
|
||||||
*/
|
|
||||||
public void dropView() {
|
|
||||||
onDropView();
|
|
||||||
this.view = null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,342 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package nucleus.presenter.delivery;
|
|
||||||
|
|
||||||
import rx.Notification;
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.functions.Func1;
|
|
||||||
|
|
||||||
public class DeliverFirst<View, T> implements Observable.Transformer<T, Delivery<View, T>> {
|
|
||||||
|
|
||||||
private final Observable<View> view;
|
|
||||||
|
|
||||||
public DeliverFirst(Observable<View> view) {
|
|
||||||
this.view = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Observable<Delivery<View, T>> call(Observable<T> observable) {
|
|
||||||
return observable.materialize()
|
|
||||||
.take(1)
|
|
||||||
.switchMap(new Func1<Notification<T>, Observable<? extends Delivery<View, T>>>() {
|
|
||||||
@Override
|
|
||||||
public Observable<? extends Delivery<View, T>> call(final Notification<T> notification) {
|
|
||||||
return view.map(new Func1<View, Delivery<View, T>>() {
|
|
||||||
@Override
|
|
||||||
public Delivery<View, T> call(View view) {
|
|
||||||
return view == null ? null : new Delivery<>(view, notification);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(new Func1<Delivery<View, T>, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean call(Delivery<View, T> delivery) {
|
|
||||||
return delivery != null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.take(1);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package nucleus.presenter.delivery;
|
|
||||||
|
|
||||||
import rx.Notification;
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.functions.Func1;
|
|
||||||
import rx.functions.Func2;
|
|
||||||
|
|
||||||
public class DeliverLatestCache<View, T> implements Observable.Transformer<T, Delivery<View, T>> {
|
|
||||||
|
|
||||||
private final Observable<View> view;
|
|
||||||
|
|
||||||
public DeliverLatestCache(Observable<View> view) {
|
|
||||||
this.view = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Observable<Delivery<View, T>> call(Observable<T> observable) {
|
|
||||||
return Observable
|
|
||||||
.combineLatest(
|
|
||||||
view,
|
|
||||||
observable
|
|
||||||
.materialize()
|
|
||||||
.filter(new Func1<Notification<T>, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean call(Notification<T> notification) {
|
|
||||||
return !notification.isOnCompleted();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new Func2<View, Notification<T>, Delivery<View, T>>() {
|
|
||||||
@Override
|
|
||||||
public Delivery<View, T> call(View view, Notification<T> notification) {
|
|
||||||
return view == null ? null : new Delivery<>(view, notification);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter(new Func1<Delivery<View, T>, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean call(Delivery<View, T> delivery) {
|
|
||||||
return delivery != null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
package nucleus.presenter.delivery;
|
|
||||||
|
|
||||||
import rx.Notification;
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.Subscription;
|
|
||||||
import rx.functions.Action0;
|
|
||||||
import rx.functions.Func1;
|
|
||||||
import rx.subjects.ReplaySubject;
|
|
||||||
|
|
||||||
public class DeliverReplay<View, T> implements Observable.Transformer<T, Delivery<View, T>> {
|
|
||||||
|
|
||||||
private final Observable<View> view;
|
|
||||||
|
|
||||||
public DeliverReplay(Observable<View> view) {
|
|
||||||
this.view = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Observable<Delivery<View, T>> call(Observable<T> observable) {
|
|
||||||
final ReplaySubject<Notification<T>> subject = ReplaySubject.create();
|
|
||||||
final Subscription subscription = observable
|
|
||||||
.materialize()
|
|
||||||
.filter(new Func1<Notification<T>, Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean call(Notification<T> notification) {
|
|
||||||
return !notification.isOnCompleted();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.subscribe(subject);
|
|
||||||
return view
|
|
||||||
.switchMap(new Func1<View, Observable<Delivery<View, T>>>() {
|
|
||||||
@Override
|
|
||||||
public Observable<Delivery<View, T>> call(final View view) {
|
|
||||||
return view == null ? Observable.<Delivery<View, T>>never() : subject
|
|
||||||
.map(new Func1<Notification<T>, Delivery<View, T>>() {
|
|
||||||
@Override
|
|
||||||
public Delivery<View, T> call(Notification<T> notification) {
|
|
||||||
return new Delivery<>(view, notification);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.doOnUnsubscribe(new Action0() {
|
|
||||||
@Override
|
|
||||||
public void call() {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package nucleus.presenter.delivery;
|
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import rx.Notification;
|
|
||||||
import rx.functions.Action2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that represents a couple of View and Data.
|
|
||||||
*
|
|
||||||
* @param <View>
|
|
||||||
* @param <T>
|
|
||||||
*/
|
|
||||||
public final class Delivery<View, T> {
|
|
||||||
|
|
||||||
private final View view;
|
|
||||||
private final Notification<T> notification;
|
|
||||||
|
|
||||||
public Delivery(View view, Notification<T> notification) {
|
|
||||||
this.view = view;
|
|
||||||
this.notification = notification;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void split(Action2<View, T> onNext, @Nullable Action2<View, Throwable> onError) {
|
|
||||||
if (notification.getKind() == Notification.Kind.OnNext)
|
|
||||||
onNext.call(view, notification.getValue());
|
|
||||||
else if (onError != null && notification.getKind() == Notification.Kind.OnError)
|
|
||||||
onError.call(view, notification.getThrowable());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
|
|
||||||
Delivery<?, ?> delivery = (Delivery<?, ?>)o;
|
|
||||||
|
|
||||||
if (view != null ? !view.equals(delivery.view) : delivery.view != null) return false;
|
|
||||||
return !(notification != null ? !notification.equals(delivery.notification) : delivery.notification != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = view != null ? view.hashCode() : 0;
|
|
||||||
result = 31 * result + (notification != null ? notification.hashCode() : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Delivery{" +
|
|
||||||
"view=" + view +
|
|
||||||
", notification=" + notification +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package nucleus.view;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import nucleus.factory.PresenterFactory;
|
|
||||||
import nucleus.factory.ReflectionPresenterFactory;
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is an example of how an activity could controls it's presenter.
|
|
||||||
* You can inherit from this class or copy/paste this class's code to
|
|
||||||
* create your own view implementation.
|
|
||||||
*
|
|
||||||
* @param <P> a type of presenter to return with {@link #getPresenter}.
|
|
||||||
*/
|
|
||||||
public abstract class NucleusActivity<P extends Presenter> extends Activity implements ViewWithPresenter<P> {
|
|
||||||
|
|
||||||
private static final String PRESENTER_STATE_KEY = "presenter_state";
|
|
||||||
|
|
||||||
private PresenterLifecycleDelegate<P> presenterDelegate =
|
|
||||||
new PresenterLifecycleDelegate<>(ReflectionPresenterFactory.<P>fromViewClass(getClass()));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current presenter factory.
|
|
||||||
*/
|
|
||||||
public PresenterFactory<P> getPresenterFactory() {
|
|
||||||
return presenterDelegate.getPresenterFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a presenter factory.
|
|
||||||
* Call this method before onCreate/onFinishInflate to override default {@link ReflectionPresenterFactory} presenter factory.
|
|
||||||
* Use this method for presenter dependency injection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setPresenterFactory(PresenterFactory<P> presenterFactory) {
|
|
||||||
presenterDelegate.setPresenterFactory(presenterFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current attached presenter.
|
|
||||||
* This method is guaranteed to return a non-null value between
|
|
||||||
* onResume/onPause and onAttachedToWindow/onDetachedFromWindow calls
|
|
||||||
* if the presenter factory returns a non-null value.
|
|
||||||
*
|
|
||||||
* @return a currently attached presenter or null.
|
|
||||||
*/
|
|
||||||
public P getPresenter() {
|
|
||||||
return presenterDelegate.getPresenter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
if (savedInstanceState != null)
|
|
||||||
presenterDelegate.onRestoreInstanceState(savedInstanceState.getBundle(PRESENTER_STATE_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putBundle(PRESENTER_STATE_KEY, presenterDelegate.onSaveInstanceState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
presenterDelegate.onResume(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
presenterDelegate.onDropView();
|
|
||||||
presenterDelegate.onDestroy(!isChangingConfigurations());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
package nucleus.view;
|
|
||||||
|
|
||||||
import android.app.Fragment;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import nucleus.factory.PresenterFactory;
|
|
||||||
import nucleus.factory.ReflectionPresenterFactory;
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This view is an example of how a view should control it's presenter.
|
|
||||||
* You can inherit from this class or copy/paste this class's code to
|
|
||||||
* create your own view implementation.
|
|
||||||
*
|
|
||||||
* @param <P> a type of presenter to return with {@link #getPresenter}.
|
|
||||||
*/
|
|
||||||
public abstract class NucleusFragment<P extends Presenter> extends Fragment implements ViewWithPresenter<P> {
|
|
||||||
|
|
||||||
private static final String PRESENTER_STATE_KEY = "presenter_state";
|
|
||||||
private PresenterLifecycleDelegate<P> presenterDelegate =
|
|
||||||
new PresenterLifecycleDelegate<>(ReflectionPresenterFactory.<P>fromViewClass(getClass()));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current presenter factory.
|
|
||||||
*/
|
|
||||||
public PresenterFactory<P> getPresenterFactory() {
|
|
||||||
return presenterDelegate.getPresenterFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a presenter factory.
|
|
||||||
* Call this method before onCreate/onFinishInflate to override default {@link ReflectionPresenterFactory} presenter factory.
|
|
||||||
* Use this method for presenter dependency injection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setPresenterFactory(PresenterFactory<P> presenterFactory) {
|
|
||||||
presenterDelegate.setPresenterFactory(presenterFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current attached presenter.
|
|
||||||
* This method is guaranteed to return a non-null value between
|
|
||||||
* onResume/onPause and onAttachedToWindow/onDetachedFromWindow calls
|
|
||||||
* if the presenter factory returns a non-null value.
|
|
||||||
*
|
|
||||||
* @return a currently attached presenter or null.
|
|
||||||
*/
|
|
||||||
public P getPresenter() {
|
|
||||||
return presenterDelegate.getPresenter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle bundle) {
|
|
||||||
super.onCreate(bundle);
|
|
||||||
if (bundle != null)
|
|
||||||
presenterDelegate.onRestoreInstanceState(bundle.getBundle(PRESENTER_STATE_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle bundle) {
|
|
||||||
super.onSaveInstanceState(bundle);
|
|
||||||
bundle.putBundle(PRESENTER_STATE_KEY, presenterDelegate.onSaveInstanceState());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
presenterDelegate.onResume(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
presenterDelegate.onDropView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
presenterDelegate.onDestroy(!getActivity().isChangingConfigurations());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
package nucleus.view;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.ContextWrapper;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
import nucleus.factory.PresenterFactory;
|
|
||||||
import nucleus.factory.ReflectionPresenterFactory;
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This view is an example of how a view should control it's presenter.
|
|
||||||
* You can inherit from this class or copy/paste this class's code to
|
|
||||||
* create your own view implementation.
|
|
||||||
*
|
|
||||||
* @param <P> a type of presenter to return with {@link #getPresenter}.
|
|
||||||
*/
|
|
||||||
public class NucleusLayout<P extends Presenter> extends FrameLayout implements ViewWithPresenter<P> {
|
|
||||||
|
|
||||||
private static final String PARENT_STATE_KEY = "parent_state";
|
|
||||||
private static final String PRESENTER_STATE_KEY = "presenter_state";
|
|
||||||
|
|
||||||
private PresenterLifecycleDelegate<P> presenterDelegate =
|
|
||||||
new PresenterLifecycleDelegate<>(ReflectionPresenterFactory.<P>fromViewClass(getClass()));
|
|
||||||
|
|
||||||
public NucleusLayout(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NucleusLayout(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NucleusLayout(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current presenter factory.
|
|
||||||
*/
|
|
||||||
public PresenterFactory<P> getPresenterFactory() {
|
|
||||||
return presenterDelegate.getPresenterFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a presenter factory.
|
|
||||||
* Call this method before onCreate/onFinishInflate to override default {@link ReflectionPresenterFactory} presenter factory.
|
|
||||||
* Use this method for presenter dependency injection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setPresenterFactory(PresenterFactory<P> presenterFactory) {
|
|
||||||
presenterDelegate.setPresenterFactory(presenterFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current attached presenter.
|
|
||||||
* This method is guaranteed to return a non-null value between
|
|
||||||
* onResume/onPause and onAttachedToWindow/onDetachedFromWindow calls
|
|
||||||
* if the presenter factory returns a non-null value.
|
|
||||||
*
|
|
||||||
* @return a currently attached presenter or null.
|
|
||||||
*/
|
|
||||||
public P getPresenter() {
|
|
||||||
return presenterDelegate.getPresenter();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the unwrapped activity of the view or throws an exception.
|
|
||||||
*
|
|
||||||
* @return an unwrapped activity
|
|
||||||
*/
|
|
||||||
public Activity getActivity() {
|
|
||||||
Context context = getContext();
|
|
||||||
while (!(context instanceof Activity) && context instanceof ContextWrapper)
|
|
||||||
context = ((ContextWrapper) context).getBaseContext();
|
|
||||||
if (!(context instanceof Activity))
|
|
||||||
throw new IllegalStateException("Expected an activity context, got " + context.getClass().getSimpleName());
|
|
||||||
return (Activity) context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Parcelable onSaveInstanceState() {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putBundle(PRESENTER_STATE_KEY, presenterDelegate.onSaveInstanceState());
|
|
||||||
bundle.putParcelable(PARENT_STATE_KEY, super.onSaveInstanceState());
|
|
||||||
return bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onRestoreInstanceState(Parcelable state) {
|
|
||||||
Bundle bundle = (Bundle) state;
|
|
||||||
super.onRestoreInstanceState(bundle.getParcelable(PARENT_STATE_KEY));
|
|
||||||
presenterDelegate.onRestoreInstanceState(bundle.getBundle(PRESENTER_STATE_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow();
|
|
||||||
if (!isInEditMode())
|
|
||||||
presenterDelegate.onResume(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow();
|
|
||||||
presenterDelegate.onDropView();
|
|
||||||
presenterDelegate.onDestroy(!getActivity().isChangingConfigurations());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package nucleus.view;
|
|
||||||
|
|
||||||
import android.os.Parcel;
|
|
||||||
|
|
||||||
class ParcelFn {
|
|
||||||
|
|
||||||
private static final ClassLoader CLASS_LOADER = ParcelFn.class.getClassLoader();
|
|
||||||
|
|
||||||
static <T> T unmarshall(byte[] array) {
|
|
||||||
Parcel parcel = Parcel.obtain();
|
|
||||||
parcel.unmarshall(array, 0, array.length);
|
|
||||||
parcel.setDataPosition(0);
|
|
||||||
Object value = parcel.readValue(CLASS_LOADER);
|
|
||||||
parcel.recycle();
|
|
||||||
return (T)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static byte[] marshall(Object o) {
|
|
||||||
Parcel parcel = Parcel.obtain();
|
|
||||||
parcel.writeValue(o);
|
|
||||||
byte[] result = parcel.marshall();
|
|
||||||
parcel.recycle();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,127 +0,0 @@
|
||||||
package nucleus.view;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import nucleus.factory.PresenterFactory;
|
|
||||||
import nucleus.factory.PresenterStorage;
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class adopts a View lifecycle to the Presenter`s lifecycle.
|
|
||||||
*
|
|
||||||
* @param <P> a type of the presenter.
|
|
||||||
*/
|
|
||||||
public final class PresenterLifecycleDelegate<P extends Presenter> {
|
|
||||||
|
|
||||||
private static final String PRESENTER_KEY = "presenter";
|
|
||||||
private static final String PRESENTER_ID_KEY = "presenter_id";
|
|
||||||
|
|
||||||
@Nullable private PresenterFactory<P> presenterFactory;
|
|
||||||
@Nullable private P presenter;
|
|
||||||
@Nullable private Bundle bundle;
|
|
||||||
|
|
||||||
private boolean presenterHasView;
|
|
||||||
|
|
||||||
public PresenterLifecycleDelegate(@Nullable PresenterFactory<P> presenterFactory) {
|
|
||||||
this.presenterFactory = presenterFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link ViewWithPresenter#getPresenterFactory()}
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public PresenterFactory<P> getPresenterFactory() {
|
|
||||||
return presenterFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link ViewWithPresenter#setPresenterFactory(PresenterFactory)}
|
|
||||||
*/
|
|
||||||
public void setPresenterFactory(@Nullable PresenterFactory<P> presenterFactory) {
|
|
||||||
if (presenter != null)
|
|
||||||
throw new IllegalArgumentException("setPresenterFactory() should be called before onResume()");
|
|
||||||
this.presenterFactory = presenterFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link ViewWithPresenter#getPresenter()}
|
|
||||||
*/
|
|
||||||
public P getPresenter() {
|
|
||||||
if (presenterFactory != null) {
|
|
||||||
if (presenter == null && bundle != null)
|
|
||||||
presenter = PresenterStorage.INSTANCE.getPresenter(bundle.getString(PRESENTER_ID_KEY));
|
|
||||||
|
|
||||||
if (presenter == null) {
|
|
||||||
presenter = presenterFactory.createPresenter();
|
|
||||||
PresenterStorage.INSTANCE.add(presenter);
|
|
||||||
presenter.create(bundle == null ? null : bundle.getBundle(PRESENTER_KEY));
|
|
||||||
}
|
|
||||||
bundle = null;
|
|
||||||
}
|
|
||||||
return presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link android.app.Activity#onSaveInstanceState(Bundle)}, {@link android.app.Fragment#onSaveInstanceState(Bundle)}, {@link android.view.View#onSaveInstanceState()}.
|
|
||||||
*/
|
|
||||||
public Bundle onSaveInstanceState() {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
getPresenter();
|
|
||||||
if (presenter != null) {
|
|
||||||
Bundle presenterBundle = new Bundle();
|
|
||||||
presenter.save(presenterBundle);
|
|
||||||
bundle.putBundle(PRESENTER_KEY, presenterBundle);
|
|
||||||
bundle.putString(PRESENTER_ID_KEY, PresenterStorage.INSTANCE.getId(presenter));
|
|
||||||
}
|
|
||||||
return bundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link android.app.Activity#onCreate(Bundle)}, {@link android.app.Fragment#onCreate(Bundle)}, {@link android.view.View#onRestoreInstanceState(Parcelable)}.
|
|
||||||
*/
|
|
||||||
public void onRestoreInstanceState(Bundle presenterState) {
|
|
||||||
if (presenter != null)
|
|
||||||
throw new IllegalArgumentException("onRestoreInstanceState() should be called before onResume()");
|
|
||||||
this.bundle = ParcelFn.unmarshall(ParcelFn.marshall(presenterState));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link android.app.Activity#onResume()},
|
|
||||||
* {@link android.app.Fragment#onResume()},
|
|
||||||
* {@link android.view.View#onAttachedToWindow()}
|
|
||||||
*/
|
|
||||||
public void onResume(Object view) {
|
|
||||||
getPresenter();
|
|
||||||
if (presenter != null && !presenterHasView) {
|
|
||||||
//noinspection unchecked
|
|
||||||
presenter.takeView(view);
|
|
||||||
presenterHasView = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link android.app.Activity#onDestroy()},
|
|
||||||
* {@link android.app.Fragment#onDestroyView()},
|
|
||||||
* {@link android.view.View#onDetachedFromWindow()}
|
|
||||||
*/
|
|
||||||
public void onDropView() {
|
|
||||||
if (presenter != null && presenterHasView) {
|
|
||||||
presenter.dropView();
|
|
||||||
presenterHasView = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link android.app.Activity#onDestroy()},
|
|
||||||
* {@link android.app.Fragment#onDestroy()},
|
|
||||||
* {@link android.view.View#onDetachedFromWindow()}
|
|
||||||
*/
|
|
||||||
public void onDestroy(boolean isFinal) {
|
|
||||||
if (presenter != null && isFinal) {
|
|
||||||
presenter.destroy();
|
|
||||||
presenter = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package nucleus.view;
|
|
||||||
|
|
||||||
import nucleus.factory.PresenterFactory;
|
|
||||||
import nucleus.factory.ReflectionPresenterFactory;
|
|
||||||
import nucleus.presenter.Presenter;
|
|
||||||
|
|
||||||
public interface ViewWithPresenter<P extends Presenter> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current presenter factory.
|
|
||||||
*/
|
|
||||||
PresenterFactory<P> getPresenterFactory();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a presenter factory.
|
|
||||||
* Call this method before onCreate/onFinishInflate to override default {@link ReflectionPresenterFactory} presenter factory.
|
|
||||||
* Use this method for presenter dependency injection.
|
|
||||||
*/
|
|
||||||
void setPresenterFactory(PresenterFactory<P> presenterFactory);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a current attached presenter.
|
|
||||||
* This method is guaranteed to return a non-null value between
|
|
||||||
* onResume/onPause and onAttachedToWindow/onDetachedFromWindow calls
|
|
||||||
* if the presenter factory returns a non-null value.
|
|
||||||
*
|
|
||||||
* @return a currently attached presenter or null.
|
|
||||||
*/
|
|
||||||
P getPresenter();
|
|
||||||
}
|
|
Reference in a new issue