diff --git a/autodispose-rxlifecycle/build.gradle b/autodispose-rxlifecycle/build.gradle
new file mode 100644
index 000000000..56c49924d
--- /dev/null
+++ b/autodispose-rxlifecycle/build.gradle
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017. Uber Technologies
+ *
+ * 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.
+ */
+
+buildscript {
+ repositories {
+ jcenter()
+ maven { url deps.build.repositories.plugins }
+ }
+ dependencies {
+ classpath deps.build.gradlePlugins.apt
+ classpath deps.build.gradlePlugins.errorProne
+ }
+}
+
+apply plugin: 'java-library'
+apply plugin: 'net.ltgt.apt'
+apply plugin: 'net.ltgt.errorprone'
+
+sourceCompatibility = "1.7"
+targetCompatibility = "1.7"
+
+test {
+ testLogging.showStandardStreams = true
+}
+
+dependencies {
+ apt deps.build.nullAway
+ testApt deps.build.nullAway
+
+ compile project(':autodispose')
+ compile deps.misc.rxlifecycle
+ compileOnly deps.misc.errorProneAnnotations
+ compileOnly deps.misc.javaxExtras
+
+ errorprone deps.build.checkerFramework
+ errorprone deps.build.errorProne
+
+ testCompile project(':test-utils')
+}
+
+tasks.withType(JavaCompile) {
+ options.compilerArgs += ["-Xep:NullAway:ERROR", "-XepOpt:NullAway:AnnotatedPackages=com.uber"]
+}
+
+apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
diff --git a/autodispose-rxlifecycle/gradle.properties b/autodispose-rxlifecycle/gradle.properties
new file mode 100755
index 000000000..d1f035f8d
--- /dev/null
+++ b/autodispose-rxlifecycle/gradle.properties
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2017. Uber Technologies
+#
+# 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.
+#
+
+POM_NAME=AutoDispose (RxLifecycle Interop)
+POM_ARTIFACT_ID=autodispose-rxlifecycle
+POM_PACKAGING=jar
diff --git a/autodispose-rxlifecycle/src/main/java/com/ubercab/autodispose/rxlifecycle/RxLifecycleInterop.java b/autodispose-rxlifecycle/src/main/java/com/ubercab/autodispose/rxlifecycle/RxLifecycleInterop.java
new file mode 100644
index 000000000..a11185687
--- /dev/null
+++ b/autodispose-rxlifecycle/src/main/java/com/ubercab/autodispose/rxlifecycle/RxLifecycleInterop.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017. Uber Technologies
+ *
+ * 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 com.ubercab.autodispose.rxlifecycle;
+
+import com.trello.rxlifecycle2.LifecycleProvider;
+import com.trello.rxlifecycle2.OutsideLifecycleException;
+import com.uber.autodispose.AutoDispose.ScopeHandler;
+import com.uber.autodispose.LifecycleEndedException;
+import com.uber.autodispose.ScopeProvider;
+import io.reactivex.Maybe;
+
+/**
+ * Interop for RxLifecycle. It provides static utility methods to convert {@link
+ * LifecycleProvider} to {@link ScopeProvider}.
+ *
+ *
There are several static utility converter
+ * methods such as {@link #from(LifecycleProvider)} for {@link
+ * LifecycleProvider#bindToLifecycle()} and {@link #from(LifecycleProvider, Object)} for
+ * {@link LifecycleProvider#bindUntilEvent(Object)}.
+ *
+ *
+ * Note: RxLifecycle treats the {@link OutsideLifecycleException}
+ * as normal terminal event. There is no mapping to {@link LifecycleEndedException} and in such
+ * cases the stream is normally disposed.
+ */
+public final class RxLifecycleInterop {
+
+ private RxLifecycleInterop() {
+ throw new AssertionError("No Instances");
+ }
+
+ private static final Object DEFAULT_THROWAWAY_OBJECT = new Object();
+
+ /**
+ * Converter for transforming {@link LifecycleProvider} to {@link ScopeProvider}.
+ * It disposes the source when the next reasonable event occurs.
+ *
+ * Example usage:
+ *
+ * Observable.just(1)
+ * .to(RxLifecycleInterop.from(lifecycleProvider))
+ * .subscribe(...)
+ *
+ *
+ * @param the lifecycle event.
+ * @param provider the {@link LifecycleProvider} for RxLifecycle.
+ * @return a {@link ScopeHandler} to create AutoDisposing transformation
+ */
+ public static ScopeProvider from(final LifecycleProvider provider) {
+ return new ScopeProvider() {
+ @Override public Maybe> requestScope() {
+ return provider.lifecycle()
+ .compose(provider.bindToLifecycle())
+ .ignoreElements()
+ .toMaybe()
+ .defaultIfEmpty(DEFAULT_THROWAWAY_OBJECT);
+ }
+ };
+ }
+
+ /**
+ * Converter for transforming {@link LifecycleProvider} to {@link ScopeProvider}.
+ * It disposes the source when a specific event occurs.
+ *
+ * Example usage:
+ *
+ * Observable.just(1)
+ * .to(RxLifecycleInterop.from(lifecycleProvider, event))
+ * .subscribe(...)
+ *
+ *
+ * @param the lifecycle event.
+ * @param provider the {@link LifecycleProvider} for RxLifecycle.
+ * @param event the event at which the source is disposed.
+ * @return a {@link ScopeHandler} to create AutoDisposing transformation
+ */
+ public static ScopeProvider from(final LifecycleProvider provider, final E event) {
+ return new ScopeProvider() {
+ @Override public Maybe> requestScope() {
+ return provider.lifecycle()
+ .compose(provider.bindUntilEvent(event))
+ .ignoreElements()
+ .toMaybe()
+ .defaultIfEmpty(DEFAULT_THROWAWAY_OBJECT);
+ }
+ };
+ }
+}
diff --git a/autodispose-rxlifecycle/src/main/java/com/ubercab/autodispose/rxlifecycle/package-info.java b/autodispose-rxlifecycle/src/main/java/com/ubercab/autodispose/rxlifecycle/package-info.java
new file mode 100644
index 000000000..6a9760a72
--- /dev/null
+++ b/autodispose-rxlifecycle/src/main/java/com/ubercab/autodispose/rxlifecycle/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2017. Uber Technologies
+ *
+ * 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.
+ */
+
+/**
+ * AutoDispose extensions for interop with RxLifecycle. This namely supports
+ * {@link com.trello.rxlifecycle2.LifecycleProvider}.
+ */
+@com.uber.javaxextras.FieldsMethodsAndParametersAreNonNullByDefault
+package com.ubercab.autodispose.rxlifecycle;
+
diff --git a/autodispose-rxlifecycle/src/test/java/com/ubercab/autodispose/rxlifecycle/RxLifecycleInteropTest.java b/autodispose-rxlifecycle/src/test/java/com/ubercab/autodispose/rxlifecycle/RxLifecycleInteropTest.java
new file mode 100644
index 000000000..cf6101a86
--- /dev/null
+++ b/autodispose-rxlifecycle/src/test/java/com/ubercab/autodispose/rxlifecycle/RxLifecycleInteropTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017. Uber Technologies
+ *
+ * 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 com.ubercab.autodispose.rxlifecycle;
+
+import com.uber.autodispose.AutoDispose;
+import com.uber.autodispose.test.RecordingObserver;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.observers.TestObserver;
+import io.reactivex.subjects.PublishSubject;
+import org.junit.Test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+public class RxLifecycleInteropTest {
+
+ private static final RecordingObserver.Logger LOGGER = new RecordingObserver.Logger() {
+ @Override public void log(String message) {
+ System.out.println(RxLifecycleInteropTest.class.getSimpleName() + ": " + message);
+ }
+ };
+
+ private TestLifecycleProvider lifecycleProvider = new TestLifecycleProvider();
+
+ @Test
+ public void bindLifecycle_normalTermination_completeTheStream() {
+ lifecycleProvider.emitCreate();
+ TestObserver o = new TestObserver<>();
+ PublishSubject source = PublishSubject.create();
+ Disposable d = source.to(AutoDispose.with(
+ RxLifecycleInterop.from(lifecycleProvider)).forObservable())
+ .subscribeWith(o);
+ o.assertSubscribed();
+
+ assertThat(source.hasObservers()).isTrue();
+
+ source.onNext(1);
+ o.assertValue(1);
+
+ source.onNext(2);
+ source.onComplete();
+ o.assertValues(1, 2);
+ o.assertComplete();
+ assertThat(d.isDisposed()).isFalse(); // Because it completed normally, was not disposed.
+ assertThat(source.hasObservers()).isFalse();
+ }
+
+ @Test
+ public void bindLifecycle_normalTermination_unsubscribe() {
+ lifecycleProvider.emitCreate();
+ RecordingObserver o = new RecordingObserver<>(LOGGER);
+ PublishSubject source = PublishSubject.create();
+ source.to(AutoDispose.with(
+ RxLifecycleInterop.from(lifecycleProvider)).forObservable())
+ .subscribe(o);
+ o.takeSubscribe();
+
+ assertThat(source.hasObservers()).isTrue();
+
+ source.onNext(1);
+ assertThat(o.takeNext()).isEqualTo(1);
+
+ lifecycleProvider.emitDestroy();
+ source.onNext(2);
+ o.assertNoMoreEvents();
+ assertThat(source.hasObservers()).isFalse();
+ }
+
+ @Test
+ public void bindLifecycle_outsideLifecycleBound_unsubscribe() {
+ lifecycleProvider.emitCreate();
+ RecordingObserver o = new RecordingObserver<>(LOGGER);
+ PublishSubject source = PublishSubject.create();
+ lifecycleProvider.emitDestroy();
+ source
+ .to(AutoDispose.with(
+ RxLifecycleInterop.from(lifecycleProvider)).forObservable())
+ .subscribe(o);
+
+ o.takeSubscribe();
+
+ source.onNext(2);
+ o.assertNoMoreEvents();
+ assertThat(
+ source.hasObservers()).isFalse(); // Because RxLifecycle
+ // treats OutsideLifecycleException as terminal event.
+ }
+
+ @Test
+ public void bindUntilEvent_normalTermination_completeTheStream() {
+ lifecycleProvider.emitCreate();
+ TestObserver o = new TestObserver<>();
+ PublishSubject source = PublishSubject.create();
+ Disposable d = source.to(AutoDispose.with(RxLifecycleInterop.from(
+ lifecycleProvider,
+ TestLifecycleProvider.Event.DESTROY)).forObservable())
+ .subscribeWith(o);
+ o.assertSubscribed();
+
+ assertThat(source.hasObservers()).isTrue();
+
+ source.onNext(1);
+ o.assertValue(1);
+
+ source.onNext(2);
+ source.onComplete();
+ o.assertValues(1, 2);
+ o.assertComplete();
+ assertThat(d.isDisposed()).isFalse(); // Because it completed normally, was not disposed.
+ assertThat(source.hasObservers()).isFalse();
+ }
+
+ @Test
+ public void bindUntilEvent_interruptedTermination_unsubscribe() {
+ lifecycleProvider.emitCreate();
+ RecordingObserver o = new RecordingObserver<>(LOGGER);
+ PublishSubject source = PublishSubject.create();
+ source.to(AutoDispose.with(RxLifecycleInterop.from(lifecycleProvider,
+ TestLifecycleProvider.Event.DESTROY)).forObservable())
+ .subscribe(o);
+ o.takeSubscribe();
+
+ assertThat(source.hasObservers()).isTrue();
+
+ source.onNext(1);
+ assertThat(o.takeNext()).isEqualTo(1);
+
+ lifecycleProvider.emitDestroy();
+ source.onNext(2);
+ o.assertNoMoreEvents();
+ assertThat(source.hasObservers()).isFalse();
+ }
+}
diff --git a/autodispose-rxlifecycle/src/test/java/com/ubercab/autodispose/rxlifecycle/TestLifecycleProvider.java b/autodispose-rxlifecycle/src/test/java/com/ubercab/autodispose/rxlifecycle/TestLifecycleProvider.java
new file mode 100644
index 000000000..7cac2cb8c
--- /dev/null
+++ b/autodispose-rxlifecycle/src/test/java/com/ubercab/autodispose/rxlifecycle/TestLifecycleProvider.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017. Uber Technologies
+ *
+ * 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 com.ubercab.autodispose.rxlifecycle;
+
+import com.trello.rxlifecycle2.LifecycleProvider;
+import com.trello.rxlifecycle2.LifecycleTransformer;
+import com.trello.rxlifecycle2.OutsideLifecycleException;
+import com.trello.rxlifecycle2.RxLifecycle;
+import io.reactivex.Observable;
+import io.reactivex.functions.Function;
+import io.reactivex.subjects.BehaviorSubject;
+
+final class TestLifecycleProvider implements LifecycleProvider {
+
+ private static final Function CORRESPONDING_EVENTS = new Function() {
+ @Override public Event apply(Event event)
+ throws Exception {
+ switch (event) {
+ case CREATE:
+ return Event.DESTROY;
+ default:
+ throw new OutsideLifecycleException("Lifecycle ended");
+ }
+ }
+ };
+
+ private final BehaviorSubject lifecycle = BehaviorSubject.create();
+
+ @Override public Observable lifecycle() {
+ return lifecycle.hide();
+ }
+
+ @Override
+ public LifecycleTransformer bindUntilEvent(Event event) {
+ return RxLifecycle.bindUntilEvent(lifecycle, event);
+ }
+
+ @Override public LifecycleTransformer bindToLifecycle() {
+ return RxLifecycle.bind(lifecycle, CORRESPONDING_EVENTS);
+ }
+
+ void emitCreate() {
+ lifecycle.onNext(Event.CREATE);
+ }
+
+ void emitDestroy() {
+ lifecycle.onNext(Event.DESTROY);
+ }
+
+ enum Event {
+ CREATE,
+ DESTROY
+ }
+}
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 7f013ae71..c6bec0e6d 100755
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -52,7 +52,8 @@ def kotlin = [
def misc = [
errorProneAnnotations: "com.google.errorprone:error_prone_annotations:${versions.errorProne}",
javaxExtras: "com.uber.javaxextras:javax-extras:0.1.0",
- jsr305: 'com.google.code.findbugs:jsr305:3.0.2'
+ jsr305: 'com.google.code.findbugs:jsr305:3.0.2',
+ rxlifecycle: 'com.trello.rxlifecycle2:rxlifecycle:2.2.0'
]
def rx = [
diff --git a/settings.gradle b/settings.gradle
index b2f57264d..a74b395c8 100755
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,5 +23,7 @@ include ':android:autodispose-android-archcomponents-test'
include ':android:autodispose-android-archcomponents-test-kotlin'
include ':autodispose'
include ':autodispose-kotlin'
+include 'autodispose-rxlifecycle'
include ':sample'
include ':test-utils'
+