diff --git a/rx2/.gitignore b/rx2/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/rx2/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/rx2/build.gradle b/rx2/build.gradle
new file mode 100644
index 00000000..efe6426a
--- /dev/null
+++ b/rx2/build.gradle
@@ -0,0 +1,55 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath "com.novoda:bintray-release:$bintrayVersion"
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'bintray-release'
+apply plugin: 'jacoco-android'
+
+android {
+ compileSdkVersion COMPILE_SDK_VERSION
+ buildToolsVersion BUILD_TOOLS_VERSION
+
+ defaultConfig {
+ minSdkVersion MIN_SDK_VERSION
+ targetSdkVersion TARGET_SDK_VERSION
+ versionCode VERSION_CODE
+ versionName VERSION_NAME
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ debug {
+ // output coverage with ./gradlew clean build createDebugCoverageReport
+ testCoverageEnabled true
+ }
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ compile project(':thirtyinch')
+ compile 'io.reactivex.rxjava2:rxjava:2.0.3'
+ provided "com.android.support:support-annotations:$supportLibraryVersion"
+
+ testCompile "junit:junit:$junitVersion"
+ testCompile "org.mockito:mockito-core:$mockitoVersion"
+}
+
+publish {
+ userOrg = 'passsy'
+ groupId = 'net.grandcentrix.thirtyinch'
+ artifactId = 'thirtyinch-rx'
+ uploadName = 'ThirtyInch'
+ publishVersion = VERSION_NAME
+ //description = ''
+ website = 'https://github.com/grandcentrix/ThirtyInch'
+}
\ No newline at end of file
diff --git a/rx2/src/main/AndroidManifest.xml b/rx2/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..7f593b78
--- /dev/null
+++ b/rx2/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/rx2/src/main/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterDisposableHandler.java b/rx2/src/main/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterDisposableHandler.java
new file mode 100644
index 00000000..ec08524b
--- /dev/null
+++ b/rx2/src/main/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterDisposableHandler.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 grandcentrix GmbH
+ * 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 net.grandcentrix.thirtyinch.rx2;
+
+import net.grandcentrix.thirtyinch.TiLifecycleObserver;
+import net.grandcentrix.thirtyinch.TiPresenter;
+import net.grandcentrix.thirtyinch.TiView;
+
+import android.support.annotation.NonNull;
+
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+
+public class RxTiPresenterDisposableHandler {
+
+ private CompositeDisposable mPresenterDisposables = new CompositeDisposable();
+
+ private CompositeDisposable mUiDisposables;
+
+ public RxTiPresenterDisposableHandler(final TiPresenter presenter) {
+ presenter.addLifecycleObserver(new TiLifecycleObserver() {
+ @Override
+ public void onChange(final TiPresenter.State state,
+ final boolean beforeLifecycleEvent) {
+ if (state == TiPresenter.State.VIEW_DETACHED && beforeLifecycleEvent) {
+ // dispose all UI disposable created in onAttachView(TiView) and added
+ // via manageViewDisposable(Disposable...)
+ if (mUiDisposables != null) {
+ mUiDisposables.dispose();
+ mUiDisposables = null;
+ }
+ }
+
+ if (state == TiPresenter.State.VIEW_ATTACHED && beforeLifecycleEvent) {
+ mUiDisposables = new CompositeDisposable();
+ }
+
+ if (state == TiPresenter.State.DESTROYED && beforeLifecycleEvent) {
+ mPresenterDisposables.dispose();
+ mPresenterDisposables = null;
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Add your disposables here and they will automatically disposed when
+ * {@link TiPresenter#destroy()} gets called
+ *
+ * @throws IllegalStateException when the presenter has reached {@link net.grandcentrix.thirtyinch.TiPresenter.State#DESTROYED}
+ */
+ public void manageDisposable(@NonNull final Disposable... disposables) {
+ if (mPresenterDisposables == null) {
+ throw new IllegalStateException("disposable handling doesn't work"
+ + " when the presenter has reached the DESTROYED state");
+ }
+
+ addDisposables(mPresenterDisposables, disposables);
+ }
+
+ /**
+ * Add your disposables for View events to this method to get them automatically cleaned up
+ * in {@link TiPresenter#detachView()}. typically call this in {@link
+ * TiPresenter#attachView(TiView)} where you dispose to the UI events.
+ *
+ * @throws IllegalStateException when no view is attached
+ */
+ public void manageViewDisposable(@NonNull final Disposable... disposables) {
+ if (mUiDisposables == null) {
+ throw new IllegalStateException("view disposable can't be handled"
+ + " when there is no view");
+ }
+
+ addDisposables(mUiDisposables, disposables);
+ }
+
+ /**
+ * Adds all disposables to the given compositeDisposable if not already disposed
+ */
+ private static void addDisposables(final CompositeDisposable compositeDisposable,
+ final Disposable... disposables) {
+ //noinspection ForLoopReplaceableByForEach
+ for (int i = 0; i < disposables.length; i++) {
+ final Disposable disposable = disposables[i];
+ if (disposable.isDisposed()) {
+ continue;
+ }
+
+ compositeDisposable.add(disposable);
+ }
+ }
+
+}
diff --git a/rx2/src/main/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterUtils.java b/rx2/src/main/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterUtils.java
new file mode 100644
index 00000000..92f76219
--- /dev/null
+++ b/rx2/src/main/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterUtils.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 grandcentrix GmbH
+ * 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 net.grandcentrix.thirtyinch.rx2;
+
+import net.grandcentrix.thirtyinch.Removable;
+import net.grandcentrix.thirtyinch.TiLifecycleObserver;
+import net.grandcentrix.thirtyinch.TiPresenter;
+import net.grandcentrix.thirtyinch.TiView;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.disposables.Disposable;
+
+public class RxTiPresenterUtils {
+
+ /**
+ * Observable of the view state. The View is ready to receive calls after calling {@link
+ * TiPresenter#attachView(TiView)} and before calling {@link TiPresenter#detachView()}.
+ */
+ public static Observable isViewReady(final TiPresenter presenter) {
+ return Observable.create(
+ new ObservableOnSubscribe() {
+ @Override
+ public void subscribe(final ObservableEmitter emitter)
+ throws Exception {
+ if (!emitter.isDisposed()) {
+ emitter.onNext(presenter.getState()
+ == TiPresenter.State.VIEW_ATTACHED);
+ }
+
+ final Removable removable = presenter
+ .addLifecycleObserver(new TiLifecycleObserver() {
+ @Override
+ public void onChange(final TiPresenter.State state,
+ final boolean beforeLifecycleEvent) {
+ if (!emitter.isDisposed()) {
+ emitter.onNext(state ==
+ TiPresenter.State.VIEW_ATTACHED);
+ }
+ }
+ });
+
+ emitter.setDisposable(new Disposable() {
+ @Override
+ public void dispose() {
+ removable.remove();
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return removable.isRemoved();
+ }
+ });
+ }
+ })
+ .distinctUntilChanged();
+ }
+
+}
diff --git a/rx2/src/test/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterDisposableHandlerTest.java b/rx2/src/test/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterDisposableHandlerTest.java
new file mode 100644
index 00000000..6a6be215
--- /dev/null
+++ b/rx2/src/test/java/net/grandcentrix/thirtyinch/rx2/RxTiPresenterDisposableHandlerTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 grandcentrix GmbH
+ * 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 net.grandcentrix.thirtyinch.rx2;
+
+import net.grandcentrix.thirtyinch.TiPresenter;
+import net.grandcentrix.thirtyinch.TiView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import io.reactivex.observers.TestObserver;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+@RunWith(JUnit4.class)
+public class RxTiPresenterDisposableHandlerTest {
+
+ private RxTiPresenterDisposableHandler mDisposableHandler;
+
+ private TiPresenter mPresenter;
+
+ private TiView mView;
+
+ @Before
+ public void setUp() throws Exception {
+ mPresenter = new TiPresenter() {
+ };
+ mDisposableHandler = new RxTiPresenterDisposableHandler(mPresenter);
+ mView = mock(TiView.class);
+ }
+
+ @Test
+ public void testManageDisposable_AfterDestroy_ShouldThrowIllegalState() throws Exception {
+ mPresenter.create();
+ mPresenter.destroy();
+ final TestObserver