package com.kickstarter.libs;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.kickstarter.libs.utils.ObjectUtils;
import com.trello.rxlifecycle.FragmentEvent;
import rx.Observable;
import rx.subjects.PublishSubject;
import timber.log.Timber;
/**
* A view model bound to the lifecycle and arguments of a Fragment, from ATTACH to DETACH.
*/
public class FragmentViewModel<ViewType extends FragmentLifecycleType> {
private final PublishSubject<ViewType> viewChange = PublishSubject.create();
private final Observable<ViewType> view = viewChange.filter(ObjectUtils::isNotNull);
private final PublishSubject<Bundle> arguments = PublishSubject.create();
protected final Koala koala;
public FragmentViewModel(final @NonNull Environment environment) {
koala = environment.koala();
}
@CallSuper
protected void onCreate(final @NonNull Context context, final @Nullable Bundle savedInstanceState) {
Timber.d("onCreate %s", this.toString());
dropView();
}
/**
* Takes bundle arguments from the view.
*/
public void arguments(final @Nullable Bundle bundle) {
this.arguments.onNext(bundle);
}
protected @NonNull Observable<Bundle> arguments() {
return arguments;
}
@CallSuper
protected void onResume(final @NonNull ViewType view) {
Timber.d("onResume %s", this.toString());
onTakeView(view);
}
@CallSuper
protected void onPause() {
Timber.d("onPause %s", this.toString());
dropView();
}
@CallSuper
protected void onDestroy() {
Timber.d("onDestroy %s", this.toString());
dropView();
}
@CallSuper
protected void onDetach() {
Timber.d("onDetach %s", this.toString());
viewChange.onCompleted();
}
private void onTakeView(final @NonNull ViewType view) {
Timber.d("onTakeView %s %s", this.toString(), view.toString());
viewChange.onNext(view);
}
private void dropView() {
Timber.d("dropView %s", this.toString());
viewChange.onNext(null);
}
protected final @NonNull Observable<ViewType> view() {
return view;
}
/**
* By composing this transformer with an observable you guarantee that every observable in your view model
* will be properly completed when the view model completes.
*
* It is required that *every* observable in a view model do `.compose(bindToLifecycle())` before calling
* `subscribe`.
*/
public @NonNull <T> Observable.Transformer<T, T> bindToLifecycle() {
return source -> source.takeUntil(
view.switchMap(FragmentLifecycleType::lifecycle)
.filter(FragmentEvent.DETACH::equals)
);
}
}