package com.kickstarter.libs; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Pair; import com.kickstarter.ui.data.ActivityResult; import com.trello.rxlifecycle.ActivityEvent; import rx.Observable; import rx.subjects.PublishSubject; import rx.subscriptions.CompositeSubscription; import timber.log.Timber; public class ActivityViewModel<ViewType extends ActivityLifecycleType> { private final PublishSubject<ViewType> viewChange = PublishSubject.create(); private final Observable<ViewType> view = viewChange.filter(v -> v != null); private final CompositeSubscription subscriptions = new CompositeSubscription(); private final PublishSubject<ActivityResult> activityResult = PublishSubject.create(); private final PublishSubject<Intent> intent = PublishSubject.create(); protected final Koala koala; public ActivityViewModel(final @NonNull Environment environment) { koala = environment.koala(); } /** * Takes activity result data from the activity. */ public void activityResult(final @NonNull ActivityResult activityResult) { this.activityResult.onNext(activityResult); } /** * Takes intent data from the view. */ public void intent(final @NonNull Intent intent) { this.intent.onNext(intent); } @CallSuper protected void onCreate(final @NonNull Context context, final @Nullable Bundle savedInstanceState) { Timber.d("onCreate %s", this.toString()); dropView(); } @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()); subscriptions.clear(); 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 @NonNull Observable<ActivityResult> activityResult() { return activityResult; } protected @NonNull Observable<Intent> intent() { return intent; } /** * 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(v -> v.lifecycle().map(e -> Pair.create(v, e))) .filter(ve -> isFinished(ve.first, ve.second)) ); } /** * Determines from a view and lifecycle event if the view's life is over. */ private boolean isFinished(final @NonNull ViewType view, final @NonNull ActivityEvent event) { if (view instanceof BaseActivity) { return event == ActivityEvent.DESTROY && ((BaseActivity) view).isFinishing(); } return event == ActivityEvent.DESTROY; } }