/* * Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov) * * This file is part of RoboSwag library. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package ru.touchin.roboswag.components.utils; import android.support.annotation.NonNull; import ru.touchin.roboswag.core.log.Lc; import ru.touchin.roboswag.core.utils.ShouldNotHappenException; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.exceptions.OnErrorThrowable; import rx.functions.Action0; import rx.functions.Action1; import rx.functions.Actions; import rx.subjects.BehaviorSubject; /** * Created by Gavriil Sitnikov on 18/04/16. * Simple implementation of {@link LifecycleBindable}. Could be used to not implement interface but use such object inside. */ public class BaseLifecycleBindable implements LifecycleBindable { @NonNull private final BehaviorSubject isCreatedSubject = BehaviorSubject.create(); @NonNull private final BehaviorSubject isStartedSubject = BehaviorSubject.create(); /** * Call it on parent's onCreate method. */ public void onCreate() { isCreatedSubject.onNext(true); } /** * Call it on parent's onStart method. */ public void onStart() { isStartedSubject.onNext(true); } /** * Call it on parent's onStop method. */ public void onStop() { isStartedSubject.onNext(false); } /** * Call it on parent's onDestroy method. */ public void onDestroy() { isCreatedSubject.onNext(false); } @NonNull @Override public Subscription bind(@NonNull final Observable observable, @NonNull final Action1 onNextAction) { final String codePoint = Lc.getCodePoint(this, 2); return isStartedSubject.switchMap(started -> started ? observable.observeOn(AndroidSchedulers.mainThread()) : Observable.never()) .takeUntil(isCreatedSubject.filter(created -> !created)) .subscribe(onNextAction, throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilStop at " + codePoint, throwable))); } @NonNull @Override public Subscription untilStop(@NonNull final Observable observable) { final String codePoint = Lc.getCodePoint(this, 2); return untilStop(observable, Actions.empty(), throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilStop at " + codePoint, throwable)), Actions.empty()); } @NonNull @Override public Subscription untilStop(@NonNull final Observable observable, @NonNull final Action1 onNextAction) { final String codePoint = Lc.getCodePoint(this, 2); return untilStop(observable, onNextAction, throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilStop at " + codePoint, throwable)), Actions.empty()); } @NonNull @Override public Subscription untilStop(@NonNull final Observable observable, @NonNull final Action1 onNextAction, @NonNull final Action1 onErrorAction) { return untilStop(observable, onNextAction, onErrorAction, Actions.empty()); } @NonNull @Override public Subscription untilStop(@NonNull final Observable observable, @NonNull final Action1 onNextAction, @NonNull final Action1 onErrorAction, @NonNull final Action0 onCompletedAction) { return until(observable, isStartedSubject.map(started -> !started), onNextAction, onErrorAction, onCompletedAction); } @NonNull @Override public Subscription untilDestroy(@NonNull final Observable observable) { final String codePoint = Lc.getCodePoint(this, 2); return untilDestroy(observable, Actions.empty(), throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilDestroy at " + codePoint, throwable)), Actions.empty()); } @NonNull @Override public Subscription untilDestroy(@NonNull final Observable observable, @NonNull final Action1 onNextAction) { final String codePoint = Lc.getCodePoint(this, 2); return untilDestroy(observable, onNextAction, throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilDestroy at " + codePoint, throwable)), Actions.empty()); } @NonNull @Override public Subscription untilDestroy(@NonNull final Observable observable, @NonNull final Action1 onNextAction, @NonNull final Action1 onErrorAction) { return untilDestroy(observable, onNextAction, onErrorAction, Actions.empty()); } @NonNull @Override public Subscription untilDestroy(@NonNull final Observable observable, @NonNull final Action1 onNextAction, @NonNull final Action1 onErrorAction, @NonNull final Action0 onCompletedAction) { return until(observable, isCreatedSubject.map(created -> !created), onNextAction, onErrorAction, onCompletedAction); } @NonNull private Subscription until(@NonNull final Observable observable, @NonNull final Observable conditionSubject, @NonNull final Action1 onNextAction, @NonNull final Action1 onErrorAction, @NonNull final Action0 onCompletedAction) { final Observable actualObservable; if (onNextAction == Actions.empty() && onErrorAction == (Action1) Actions.empty() && onCompletedAction == Actions.empty()) { actualObservable = observable.doOnCompleted(onCompletedAction); } else { actualObservable = observable.observeOn(AndroidSchedulers.mainThread()).doOnCompleted(onCompletedAction); } return isCreatedSubject.first() .switchMap(created -> created ? actualObservable : Observable.empty()) .takeUntil(conditionSubject.filter(condition -> condition)) .subscribe(onNextAction, throwable -> { final boolean isRxError = throwable instanceof OnErrorThrowable; if ((!isRxError && throwable instanceof RuntimeException) || (isRxError && throwable.getCause() instanceof RuntimeException)) { Lc.assertion(throwable); } onErrorAction.call(throwable); }); } }