diff --git a/build.gradle b/build.gradle
index 9e4257360..74b6711ac 100644
--- a/build.gradle
+++ b/build.gradle
@@ -58,6 +58,7 @@ ext.libraries = [
// Core libraries
supportAnnotations : 'com.android.support:support-annotations:' + supportLibsVersion,
+ rxJava : 'io.reactivex:rxjava:1.2.1',
rxJava2 : 'io.reactivex.rxjava2:rxjava:2.1.6',
rxAndroid2 : 'io.reactivex.rxjava2:rxandroid:2.0.1',
kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version",
@@ -73,6 +74,9 @@ ext.libraries = [
storIOContentResolverAnnotationsProcessor: project(':storio-content-resolver-annotations-processor'),
storIOTestCommon : project(':storio-test-common'),
+ // StorIO old versions
+ storIOSQLite1 : 'com.pushtorefresh.storio:sqlite:1.13.0',
+
// Libraries for tests and sample app
junit : 'junit:junit:4.12',
assertJ : 'org.assertj:assertj-core:3.9.0', // http://joel-costigliola.github.io/assertj/assertj-core-news.html
diff --git a/settings.gradle b/settings.gradle
index e95c02190..b7f591eea 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -12,6 +12,8 @@ include ':storio-sqlite-annotations-processor-test'
include ':storio-content-resolver-annotations-processor'
include ':storio-content-resolver-annotations-processor-test'
+include ':storio-sqlite-interop1to3'
+
include ':storio-test-without-rxjava'
include ':storio-basic-sample-app'
diff --git a/storio-sqlite-interop1to3/build.gradle b/storio-sqlite-interop1to3/build.gradle
new file mode 100644
index 000000000..1b2f572c7
--- /dev/null
+++ b/storio-sqlite-interop1to3/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion rootProject.ext.compileSdkVersion
+
+ defaultConfig {
+ versionName VERSION_NAME
+ versionCode Integer.parseInt(VERSION_CODE)
+ minSdkVersion rootProject.ext.minSdkVersion
+ }
+
+ packagingOptions {
+ exclude 'LICENSE.txt' // multiple libs have this file -> cause build error
+ }
+}
+
+dependencies {
+ implementation libraries.storIOSQLite1
+ implementation libraries.storIOSQLite
+
+ compileOnly libraries.rxJava
+ compileOnly libraries.rxJava2
+ testImplementation libraries.rxJava
+ testImplementation libraries.rxJava2
+
+ testImplementation libraries.junit
+ testImplementation libraries.kotlinStdLib
+ testImplementation libraries.mockitoKotlin
+ testImplementation libraries.assertJ
+}
+
+apply from: '../gradle/checkstyle.gradle'
+apply from: '../gradle/jacoco-android.gradle'
+apply from: '../gradle/publish-android-lib.gradle'
diff --git a/storio-sqlite-interop1to3/gradle.properties b/storio-sqlite-interop1to3/gradle.properties
new file mode 100644
index 000000000..259c49c41
--- /dev/null
+++ b/storio-sqlite-interop1to3/gradle.properties
@@ -0,0 +1,3 @@
+POM_NAME=storio-sqlite-interop1to3
+POM_ARTIFACT_ID=storio-sqlite-interop1to3
+POM_PACKAGING=jar
diff --git a/storio-sqlite-interop1to3/src/main/AndroidManifest.xml b/storio-sqlite-interop1to3/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..ed40771d8
--- /dev/null
+++ b/storio-sqlite-interop1to3/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/storio-sqlite-interop1to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3.java b/storio-sqlite-interop1to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3.java
new file mode 100644
index 000000000..3d5c18c52
--- /dev/null
+++ b/storio-sqlite-interop1to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3.java
@@ -0,0 +1,80 @@
+package com.pushtorefresh.storio3.sqlite.interop1to3;
+
+import android.support.annotation.NonNull;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import io.reactivex.BackpressureStrategy;
+import io.reactivex.functions.Consumer;
+import rx.functions.Action1;
+
+public class StorIOSQLite1To3 {
+
+ private final Set forwardedChanges1 = Collections.newSetFromMap(
+ new ConcurrentHashMap()
+ );
+
+ private final Set forwardedChanges3 = Collections.newSetFromMap(
+ new ConcurrentHashMap()
+ );
+
+ public void forwardNotifications(
+ @NonNull final com.pushtorefresh.storio.sqlite.StorIOSQLite sqlite1,
+ @NonNull final com.pushtorefresh.storio3.sqlite.StorIOSQLite sqlite3
+ ) {
+ com.pushtorefresh.storio.internal.Environment.throwExceptionIfRxJavaIsNotAvailable(
+ "forwardNotifications() requires rxJava2"
+ );
+ com.pushtorefresh.storio3.internal.Environment.throwExceptionIfRxJava2IsNotAvailable(
+ "forwardNotifications() requires rxJava2"
+ );
+
+ sqlite1.observeChanges()
+ .subscribe(new Action1() {
+ @Override
+ public void call(@NonNull com.pushtorefresh.storio.sqlite.Changes changes1) {
+ if (!forwardedChanges1.remove(changes1)) { // Check to prevent cyclic forwarding.
+ final com.pushtorefresh.storio3.sqlite.Changes changes3ToForward
+ = convertToV3(changes1);
+ forwardedChanges3.add(changes3ToForward);
+ sqlite3.lowLevel().notifyAboutChanges(changes3ToForward);
+ }
+ }
+ });
+
+ sqlite3.observeChanges(BackpressureStrategy.BUFFER)
+ .subscribe(new Consumer() {
+ @Override
+ public void accept(com.pushtorefresh.storio3.sqlite.Changes changes3) throws Exception {
+ if (!forwardedChanges3.remove(changes3)) { // Check to prevent cyclic forwarding.
+ final com.pushtorefresh.storio.sqlite.Changes changes1ToForward
+ = convertToV1(changes3);
+ forwardedChanges1.add(changes1ToForward);
+ sqlite1.lowLevel().notifyAboutChanges(changes1ToForward);
+ }
+ }
+ });
+ }
+
+ @NonNull
+ private com.pushtorefresh.storio.sqlite.Changes convertToV1(
+ @NonNull com.pushtorefresh.storio3.sqlite.Changes changes3
+ ) {
+ return com.pushtorefresh.storio.sqlite.Changes.newInstance(
+ changes3.affectedTables(),
+ changes3.affectedTags()
+ );
+ }
+
+ @NonNull
+ private com.pushtorefresh.storio3.sqlite.Changes convertToV3(
+ @NonNull com.pushtorefresh.storio.sqlite.Changes changes1
+ ) {
+ return com.pushtorefresh.storio3.sqlite.Changes.newInstance(
+ changes1.affectedTables(),
+ changes1.affectedTags()
+ );
+ }
+}
diff --git a/storio-sqlite-interop1to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3Test.kt b/storio-sqlite-interop1to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3Test.kt
new file mode 100644
index 000000000..5cac98a3e
--- /dev/null
+++ b/storio-sqlite-interop1to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3Test.kt
@@ -0,0 +1,114 @@
+package com.pushtorefresh.storio3.sqlite.interop1to3
+
+import com.nhaarman.mockito_kotlin.mock
+import io.reactivex.BackpressureStrategy
+import org.junit.Before
+import org.junit.Test
+import com.pushtorefresh.storio.sqlite.Changes as Changes1
+import com.pushtorefresh.storio.sqlite.StorIOSQLite as StorIOSQLite1
+import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite as DefaultStorIOSQLite1
+import com.pushtorefresh.storio3.sqlite.Changes as Changes3
+import com.pushtorefresh.storio3.sqlite.StorIOSQLite as StorIOSQLite3
+import com.pushtorefresh.storio3.sqlite.impl.DefaultStorIOSQLite as DefaultStorIOSQLite3
+import io.reactivex.subscribers.TestSubscriber as TestSubscriberRx2
+import rx.observers.TestSubscriber as TestSubscriberRx1
+
+class StorIOSQLite1To3Test {
+
+ val observer1 = TestSubscriberRx1.create()
+
+ val observer3 = TestSubscriberRx2.create()
+
+ lateinit var storIOSQLite1: StorIOSQLite1
+
+ lateinit var storIOSQLite3: StorIOSQLite3
+
+ lateinit var changes1WithTags: Changes1
+
+ lateinit var changes1WithoutTags: Changes1
+
+ lateinit var changes3WithTags: Changes3
+
+ lateinit var changes3WithoutTags: Changes3
+
+ @Before
+ fun `before each test`() {
+ storIOSQLite1 = DefaultStorIOSQLite1.builder()
+ .sqliteOpenHelper(mock())
+ .defaultScheduler(null)
+ .build()
+ storIOSQLite3 = DefaultStorIOSQLite3.builder()
+ .sqliteOpenHelper(mock())
+ .defaultRxScheduler(null)
+ .build()
+ val interop = StorIOSQLite1To3()
+ interop.forwardNotifications(storIOSQLite1, storIOSQLite3)
+
+ storIOSQLite1.observeChanges().subscribe(observer1)
+ storIOSQLite3.observeChanges(BackpressureStrategy.BUFFER).subscribe(observer3)
+
+ val tables = HashSet()
+ tables.add("table1")
+ tables.add("table2")
+
+ val tags = HashSet()
+ tags.add("tag1")
+ tags.add("tag2")
+
+ changes1WithTags = Changes1.newInstance(tables, tags)
+ changes3WithTags = Changes3.newInstance(tables, tags)
+
+ changes1WithoutTags = Changes1.newInstance(tables, null as Collection?)
+ changes3WithoutTags = Changes3.newInstance(tables, null as Collection?)
+ }
+
+ @Test
+ fun `forwardNotifications should forward from 1 to 3 with tags`() {
+ storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithTags)
+ observer1.assertValue(changes1WithTags)
+ observer3.assertValue(changes3WithTags)
+ }
+
+ @Test
+ fun `forwardNotifications should forward from 1 to 3 without tags`() {
+ storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithoutTags)
+ observer1.assertValue(changes1WithoutTags)
+ observer3.assertValue(changes3WithoutTags)
+ }
+
+ @Test
+ fun `forwardNotifications should forward from 1 to 3 multiple`() {
+ storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithTags)
+ observer1.assertValue(changes1WithTags)
+ observer3.assertValue(changes3WithTags)
+
+ storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithTags)
+ observer1.assertValues(changes1WithTags, changes1WithTags)
+ observer3.assertValues(changes3WithTags, changes3WithTags)
+ }
+
+ @Test
+ fun `forwardNotifications should forward from 3 to 1 with tags`() {
+ storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags)
+ observer1.assertValue(changes1WithTags)
+ observer3.assertValue(changes3WithTags)
+ }
+
+ @Test
+ fun `forwardNotifications should forward from 3 to 1 without tags`() {
+ storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithoutTags)
+ observer1.assertValue(changes1WithoutTags)
+ observer3.assertValue(changes3WithoutTags)
+ }
+
+ @Test
+ fun `forwardNotifications should forward from 3 to 1 with multiple`() {
+ storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags)
+ observer1.assertValue(changes1WithTags)
+ observer3.assertValue(changes3WithTags)
+
+ storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags)
+ observer1.assertValues(changes1WithTags, changes1WithTags)
+ observer3.assertValues(changes3WithTags, changes3WithTags)
+ }
+}