diff --git a/.gitignore b/.gitignore index 97b9a6b5..0a81078b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,6 @@ # Compiled source # ################### -*.com *.class -*.dll -*.exe -*.o -*.so # Packages # ############ @@ -15,7 +10,6 @@ *.dmg *.gz *.iso -*.jar *.rar *.tar *.zip @@ -66,10 +60,6 @@ bin/ /.nb-gradle/profiles/private/ .nb-gradle-properties -# Scala build -*.cache -/.nb-gradle/private/ - # Android local.properties diff --git a/.travis.yml b/.travis.yml index 96a28656..ebeb9fbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,10 @@ -language: java +language: android jdk: - oraclejdk7 + +android: + components: + - android-20 + +script: "./gradlew build" diff --git a/build.gradle b/build.gradle index 24a0d3e0..6b4eac8a 100644 --- a/build.gradle +++ b/build.gradle @@ -5,27 +5,3 @@ buildscript { classpath 'com.netflix.nebula:gradle-extra-configurations-plugin:1.12.+' } } - -apply plugin: 'rxjava-project' -apply plugin: 'provided-base' - -dependencies { - compile 'io.reactivex:rxjava:1.0.+' - provided 'com.google.android:android:4.0.1.2' - provided 'com.google.android:support-v4:r7' - - // testing - testCompile 'junit:junit-dep:4.10' - testCompile 'org.mockito:mockito-core:1.8.5' - testCompile('org.robolectric:robolectric:2.3') { - exclude group: 'com.android.support' - } -} - -test { - testLogging { - exceptionFormat "full" - events "started" - displayGranularity 2 - } -} diff --git a/gradle.properties b/gradle.properties index 085b9610..9389f786 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ -version=1.0.0-RC1-SNAPSHOT +rxJavaVersion=1.0.0-rc.9 +version=0.22.0 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9a1b8c12..61e8d106 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Thu Sep 18 17:51:34 CEST 2014 +#Wed Oct 22 12:19:29 CEST 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 00000000..6f3f3d0c --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,31 @@ +buildscript { + repositories { jcenter() } + dependencies { + classpath 'com.netflix.nebula:gradle-rxjava-project-plugin:1.12.+' + classpath 'com.netflix.nebula:gradle-extra-configurations-plugin:1.12.+' + } +} + +apply plugin: 'rxjava-project' +apply plugin: 'provided-base' + +dependencies { + compile "io.reactivex:rxjava:$rxJavaVersion" + provided 'com.google.android:android:4.0.1.2' + provided 'com.google.android:support-v4:r7' + + // testing + testCompile 'junit:junit-dep:4.10' + testCompile 'org.mockito:mockito-core:1.8.5' + testCompile('org.robolectric:robolectric:2.3') { + exclude group: 'com.android.support' + } +} + +test { + testLogging { + exceptionFormat "full" + events "started" + displayGranularity 2 + } +} diff --git a/src/main/java/rx/android/concurrency/AndroidSchedulers.java b/library/src/main/java/rx/android/concurrency/AndroidSchedulers.java similarity index 100% rename from src/main/java/rx/android/concurrency/AndroidSchedulers.java rename to library/src/main/java/rx/android/concurrency/AndroidSchedulers.java diff --git a/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java b/library/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java similarity index 100% rename from src/main/java/rx/android/concurrency/HandlerThreadScheduler.java rename to library/src/main/java/rx/android/concurrency/HandlerThreadScheduler.java diff --git a/src/main/java/rx/android/events/OnCheckedChangeEvent.java b/library/src/main/java/rx/android/events/OnCheckedChangeEvent.java similarity index 100% rename from src/main/java/rx/android/events/OnCheckedChangeEvent.java rename to library/src/main/java/rx/android/events/OnCheckedChangeEvent.java diff --git a/src/main/java/rx/android/events/OnClickEvent.java b/library/src/main/java/rx/android/events/OnClickEvent.java similarity index 100% rename from src/main/java/rx/android/events/OnClickEvent.java rename to library/src/main/java/rx/android/events/OnClickEvent.java diff --git a/src/main/java/rx/android/events/OnItemClickEvent.java b/library/src/main/java/rx/android/events/OnItemClickEvent.java similarity index 100% rename from src/main/java/rx/android/events/OnItemClickEvent.java rename to library/src/main/java/rx/android/events/OnItemClickEvent.java diff --git a/src/main/java/rx/android/events/OnTextChangeEvent.java b/library/src/main/java/rx/android/events/OnTextChangeEvent.java similarity index 100% rename from src/main/java/rx/android/events/OnTextChangeEvent.java rename to library/src/main/java/rx/android/events/OnTextChangeEvent.java diff --git a/src/main/java/rx/functions/ViewAction1.java b/library/src/main/java/rx/android/functions/ViewAction1.java similarity index 100% rename from src/main/java/rx/functions/ViewAction1.java rename to library/src/main/java/rx/android/functions/ViewAction1.java index 57670d5a..7c9c76fc 100644 --- a/src/main/java/rx/functions/ViewAction1.java +++ b/library/src/main/java/rx/android/functions/ViewAction1.java @@ -15,10 +15,10 @@ import android.view.View; -import rx.functions.Action1; - import java.lang.ref.WeakReference; +import rx.functions.Action1; + /** * An {@link Action1} implementation specific for {@link View}s. * diff --git a/src/main/java/rx/functions/ViewActionSetActivated.java b/library/src/main/java/rx/android/functions/ViewActionSetActivated.java similarity index 100% rename from src/main/java/rx/functions/ViewActionSetActivated.java rename to library/src/main/java/rx/android/functions/ViewActionSetActivated.java diff --git a/src/main/java/rx/functions/ViewActionSetClickable.java b/library/src/main/java/rx/android/functions/ViewActionSetClickable.java similarity index 100% rename from src/main/java/rx/functions/ViewActionSetClickable.java rename to library/src/main/java/rx/android/functions/ViewActionSetClickable.java diff --git a/src/main/java/rx/functions/ViewActionSetEnabled.java b/library/src/main/java/rx/android/functions/ViewActionSetEnabled.java similarity index 100% rename from src/main/java/rx/functions/ViewActionSetEnabled.java rename to library/src/main/java/rx/android/functions/ViewActionSetEnabled.java diff --git a/src/main/java/rx/functions/ViewActionSetFocusable.java b/library/src/main/java/rx/android/functions/ViewActionSetFocusable.java similarity index 100% rename from src/main/java/rx/functions/ViewActionSetFocusable.java rename to library/src/main/java/rx/android/functions/ViewActionSetFocusable.java diff --git a/src/main/java/rx/functions/ViewActionSetSelected.java b/library/src/main/java/rx/android/functions/ViewActionSetSelected.java similarity index 100% rename from src/main/java/rx/functions/ViewActionSetSelected.java rename to library/src/main/java/rx/android/functions/ViewActionSetSelected.java diff --git a/src/main/java/rx/functions/ViewActionSetVisibility.java b/library/src/main/java/rx/android/functions/ViewActionSetVisibility.java similarity index 100% rename from src/main/java/rx/functions/ViewActionSetVisibility.java rename to library/src/main/java/rx/android/functions/ViewActionSetVisibility.java diff --git a/src/main/java/rx/functions/ViewActions.java b/library/src/main/java/rx/android/functions/ViewActions.java similarity index 100% rename from src/main/java/rx/functions/ViewActions.java rename to library/src/main/java/rx/android/functions/ViewActions.java diff --git a/src/main/java/rx/android/observables/AndroidObservable.java b/library/src/main/java/rx/android/observables/AndroidObservable.java similarity index 100% rename from src/main/java/rx/android/observables/AndroidObservable.java rename to library/src/main/java/rx/android/observables/AndroidObservable.java diff --git a/src/main/java/rx/android/observables/Assertions.java b/library/src/main/java/rx/android/observables/Assertions.java similarity index 100% rename from src/main/java/rx/android/observables/Assertions.java rename to library/src/main/java/rx/android/observables/Assertions.java diff --git a/src/main/java/rx/android/observables/ViewObservable.java b/library/src/main/java/rx/android/observables/ViewObservable.java similarity index 100% rename from src/main/java/rx/android/observables/ViewObservable.java rename to library/src/main/java/rx/android/observables/ViewObservable.java diff --git a/src/main/java/rx/android/operators/OperatorAdapterViewOnItemClick.java b/library/src/main/java/rx/android/operators/OperatorAdapterViewOnItemClick.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorAdapterViewOnItemClick.java rename to library/src/main/java/rx/android/operators/OperatorAdapterViewOnItemClick.java diff --git a/src/main/java/rx/android/operators/OperatorBroadcastRegister.java b/library/src/main/java/rx/android/operators/OperatorBroadcastRegister.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorBroadcastRegister.java rename to library/src/main/java/rx/android/operators/OperatorBroadcastRegister.java diff --git a/src/main/java/rx/android/operators/OperatorCompoundButtonInput.java b/library/src/main/java/rx/android/operators/OperatorCompoundButtonInput.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorCompoundButtonInput.java rename to library/src/main/java/rx/android/operators/OperatorCompoundButtonInput.java diff --git a/src/main/java/rx/android/operators/OperatorConditionalBinding.java b/library/src/main/java/rx/android/operators/OperatorConditionalBinding.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorConditionalBinding.java rename to library/src/main/java/rx/android/operators/OperatorConditionalBinding.java diff --git a/src/main/java/rx/android/operators/OperatorLocalBroadcastRegister.java b/library/src/main/java/rx/android/operators/OperatorLocalBroadcastRegister.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorLocalBroadcastRegister.java rename to library/src/main/java/rx/android/operators/OperatorLocalBroadcastRegister.java diff --git a/src/main/java/rx/android/operators/OperatorSharedPreferenceChange.java b/library/src/main/java/rx/android/operators/OperatorSharedPreferenceChange.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorSharedPreferenceChange.java rename to library/src/main/java/rx/android/operators/OperatorSharedPreferenceChange.java diff --git a/src/main/java/rx/android/operators/OperatorTextViewInput.java b/library/src/main/java/rx/android/operators/OperatorTextViewInput.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorTextViewInput.java rename to library/src/main/java/rx/android/operators/OperatorTextViewInput.java diff --git a/src/main/java/rx/android/operators/OperatorViewClick.java b/library/src/main/java/rx/android/operators/OperatorViewClick.java similarity index 100% rename from src/main/java/rx/android/operators/OperatorViewClick.java rename to library/src/main/java/rx/android/operators/OperatorViewClick.java diff --git a/src/main/java/rx/android/schedulers/AndroidSchedulers.java b/library/src/main/java/rx/android/schedulers/AndroidSchedulers.java similarity index 100% rename from src/main/java/rx/android/schedulers/AndroidSchedulers.java rename to library/src/main/java/rx/android/schedulers/AndroidSchedulers.java diff --git a/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java b/library/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java similarity index 100% rename from src/main/java/rx/android/schedulers/HandlerThreadScheduler.java rename to library/src/main/java/rx/android/schedulers/HandlerThreadScheduler.java diff --git a/src/main/java/rx/android/subscriptions/AndroidSubscriptions.java b/library/src/main/java/rx/android/subscriptions/AndroidSubscriptions.java similarity index 100% rename from src/main/java/rx/android/subscriptions/AndroidSubscriptions.java rename to library/src/main/java/rx/android/subscriptions/AndroidSubscriptions.java diff --git a/src/test/java/rx/TestUtil.java b/library/src/test/java/rx/android/TestUtil.java similarity index 100% rename from src/test/java/rx/TestUtil.java rename to library/src/test/java/rx/android/TestUtil.java diff --git a/src/test/java/rx/functions/ViewAction1Test.java b/library/src/test/java/rx/android/functions/ViewAction1Test.java similarity index 98% rename from src/test/java/rx/functions/ViewAction1Test.java rename to library/src/test/java/rx/android/functions/ViewAction1Test.java index 7c03d01f..8e9ce81f 100644 --- a/src/test/java/rx/functions/ViewAction1Test.java +++ b/library/src/test/java/rx/android/functions/ViewAction1Test.java @@ -17,7 +17,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import rx.subjects.PublishSubject; diff --git a/src/test/java/rx/functions/ViewActionSetActivatedTest.java b/library/src/test/java/rx/android/functions/ViewActionSetActivatedTest.java similarity index 97% rename from src/test/java/rx/functions/ViewActionSetActivatedTest.java rename to library/src/test/java/rx/android/functions/ViewActionSetActivatedTest.java index ce7b0383..265b9576 100644 --- a/src/test/java/rx/functions/ViewActionSetActivatedTest.java +++ b/library/src/test/java/rx/android/functions/ViewActionSetActivatedTest.java @@ -17,7 +17,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import rx.subjects.PublishSubject; diff --git a/src/test/java/rx/functions/ViewActionSetClickableTest.java b/library/src/test/java/rx/android/functions/ViewActionSetClickableTest.java similarity index 97% rename from src/test/java/rx/functions/ViewActionSetClickableTest.java rename to library/src/test/java/rx/android/functions/ViewActionSetClickableTest.java index fdd12556..d3de8452 100644 --- a/src/test/java/rx/functions/ViewActionSetClickableTest.java +++ b/library/src/test/java/rx/android/functions/ViewActionSetClickableTest.java @@ -17,7 +17,6 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import rx.subjects.PublishSubject; diff --git a/src/test/java/rx/functions/ViewActionSetEnabledTest.java b/library/src/test/java/rx/android/functions/ViewActionSetEnabledTest.java similarity index 100% rename from src/test/java/rx/functions/ViewActionSetEnabledTest.java rename to library/src/test/java/rx/android/functions/ViewActionSetEnabledTest.java diff --git a/src/test/java/rx/functions/ViewActionSetFocusableTest.java b/library/src/test/java/rx/android/functions/ViewActionSetFocusableTest.java similarity index 100% rename from src/test/java/rx/functions/ViewActionSetFocusableTest.java rename to library/src/test/java/rx/android/functions/ViewActionSetFocusableTest.java diff --git a/src/test/java/rx/functions/ViewActionSetSelectedTest.java b/library/src/test/java/rx/android/functions/ViewActionSetSelectedTest.java similarity index 100% rename from src/test/java/rx/functions/ViewActionSetSelectedTest.java rename to library/src/test/java/rx/android/functions/ViewActionSetSelectedTest.java diff --git a/src/test/java/rx/functions/ViewActionSetVisibilityTest.java b/library/src/test/java/rx/android/functions/ViewActionSetVisibilityTest.java similarity index 100% rename from src/test/java/rx/functions/ViewActionSetVisibilityTest.java rename to library/src/test/java/rx/android/functions/ViewActionSetVisibilityTest.java diff --git a/src/test/java/rx/android/observables/AndroidObservableTest.java b/library/src/test/java/rx/android/observables/AndroidObservableTest.java similarity index 100% rename from src/test/java/rx/android/observables/AndroidObservableTest.java rename to library/src/test/java/rx/android/observables/AndroidObservableTest.java diff --git a/src/test/java/rx/android/operators/OperatorAdapterViewOnItemClickTest.java b/library/src/test/java/rx/android/operators/OperatorAdapterViewOnItemClickTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorAdapterViewOnItemClickTest.java rename to library/src/test/java/rx/android/operators/OperatorAdapterViewOnItemClickTest.java diff --git a/src/test/java/rx/android/operators/OperatorBroadcastRegisterTest.java b/library/src/test/java/rx/android/operators/OperatorBroadcastRegisterTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorBroadcastRegisterTest.java rename to library/src/test/java/rx/android/operators/OperatorBroadcastRegisterTest.java diff --git a/src/test/java/rx/android/operators/OperatorCompoundButtonInputTest.java b/library/src/test/java/rx/android/operators/OperatorCompoundButtonInputTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorCompoundButtonInputTest.java rename to library/src/test/java/rx/android/operators/OperatorCompoundButtonInputTest.java diff --git a/src/test/java/rx/android/operators/OperatorConditionalBindingTest.java b/library/src/test/java/rx/android/operators/OperatorConditionalBindingTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorConditionalBindingTest.java rename to library/src/test/java/rx/android/operators/OperatorConditionalBindingTest.java diff --git a/src/test/java/rx/android/operators/OperatorLocalBroadcastRegisterTest.java b/library/src/test/java/rx/android/operators/OperatorLocalBroadcastRegisterTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorLocalBroadcastRegisterTest.java rename to library/src/test/java/rx/android/operators/OperatorLocalBroadcastRegisterTest.java diff --git a/src/test/java/rx/android/operators/OperatorSharedPreferencesChangeTest.java b/library/src/test/java/rx/android/operators/OperatorSharedPreferencesChangeTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorSharedPreferencesChangeTest.java rename to library/src/test/java/rx/android/operators/OperatorSharedPreferencesChangeTest.java diff --git a/src/test/java/rx/android/operators/OperatorTextViewInputTest.java b/library/src/test/java/rx/android/operators/OperatorTextViewInputTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorTextViewInputTest.java rename to library/src/test/java/rx/android/operators/OperatorTextViewInputTest.java diff --git a/src/test/java/rx/android/operators/OperatorViewClickTest.java b/library/src/test/java/rx/android/operators/OperatorViewClickTest.java similarity index 100% rename from src/test/java/rx/android/operators/OperatorViewClickTest.java rename to library/src/test/java/rx/android/operators/OperatorViewClickTest.java diff --git a/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java b/library/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java similarity index 98% rename from src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java rename to library/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java index 9f0021d4..6184454f 100644 --- a/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java +++ b/library/src/test/java/rx/android/schedulers/HandlerThreadSchedulerTest.java @@ -13,15 +13,7 @@ */ package rx.android.schedulers; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.concurrent.TimeUnit; +import android.os.Handler; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,14 +23,21 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; +import java.util.concurrent.TimeUnit; + import rx.Observable; import rx.Scheduler; import rx.Scheduler.Worker; import rx.Subscriber; import rx.Subscription; import rx.functions.Action0; -import rx.functions.Action1; -import android.os.Handler; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) @Config(manifest=Config.NONE) diff --git a/samples-build-wrapper/build.gradle b/samples-build-wrapper/build.gradle new file mode 100644 index 00000000..1e0885f6 --- /dev/null +++ b/samples-build-wrapper/build.gradle @@ -0,0 +1,14 @@ +apply plugin: 'java' + +tasks.assemble.doLast { + def androidHome = System.getenv("ANDROID_HOME") + if (androidHome) { + println "Found ANDROID_HOME at $androidHome" + } else { + throw new Exception("ANDROID_HOME not found") + } + project.exec { + workingDir '../samples' + commandLine "./gradlew", "clean", "packageDebug" + } +} diff --git a/samples/app/build.gradle b/samples/app/build.gradle new file mode 100644 index 00000000..a4da6275 --- /dev/null +++ b/samples/app/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 20 + buildToolsVersion "20" + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 20 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + runProguard false + } + } +} + +// make sure we always compile against the latest version of RxAndroid +def rootProjectProperties = new Properties() +file("../../gradle.properties").withReader { reader -> + rootProjectProperties.load(reader) + properties.putAll(rootProjectProperties) +} + +dependencies { + def version = rootProjectProperties.get("version") + compile "io.reactivex:rxandroid:$version" +} diff --git a/samples/app/src/main/AndroidManifest.xml b/samples/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..018e199e --- /dev/null +++ b/samples/app/src/main/AndroidManifest.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/app/src/main/java/rx/android/samples/ListFragmentActivity.java b/samples/app/src/main/java/rx/android/samples/ListFragmentActivity.java new file mode 100644 index 00000000..0a3d549d --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/ListFragmentActivity.java @@ -0,0 +1,75 @@ +package rx.android.samples; + +import android.app.Activity; +import android.app.ListFragment; +import android.os.Bundle; +import android.widget.ArrayAdapter; + +import rx.Observable; +import rx.Subscriber; + +import static rx.android.schedulers.AndroidSchedulers.mainThread; + +/** + * Problem: + * You have an asynchronous sequence that emits items to be displayed in a list. You want the data + * to survive rotation changes. + *

+ * Solution: + * Combine {@link android.app.Fragment#setRetainInstance(boolean)} in a ListFragment with + * {@link rx.android.schedulers.AndroidSchedulers#mainThread()} and an {@link rx.Observable.Operator} + * that binds to the list adapter. + */ +public class ListFragmentActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setTitle("Lists"); + setContentView(R.layout.list_fragment_activity); + } + + @SuppressWarnings("ConstantConditions") + public static class RetainedListFragment extends ListFragment { + + private ArrayAdapter adapter; + + public RetainedListFragment() { + setRetainInstance(true); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1); + setListAdapter(adapter); + SampleObservables.numberStrings(1, 20, 250) + .observeOn(mainThread()) + .lift(new BindAdapter()) + .subscribe(); + } + + private final class BindAdapter implements Observable.Operator { + @Override + public Subscriber call(Subscriber subscriber) { + return new Subscriber() { + @Override + public void onCompleted() { + adapter.notifyDataSetChanged(); + } + + @Override + public void onError(Throwable throwable) { + + } + + @Override + public void onNext(String strings) { + adapter.add(strings); + } + }; + } + } + } +} diff --git a/samples/app/src/main/java/rx/android/samples/ListenInOutActivity.java b/samples/app/src/main/java/rx/android/samples/ListenInOutActivity.java new file mode 100644 index 00000000..6d4432af --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/ListenInOutActivity.java @@ -0,0 +1,87 @@ +package rx.android.samples; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.ToggleButton; + +import rx.Observable; +import rx.Observer; +import rx.Subscription; +import rx.observables.ConnectableObservable; + +import static rx.android.observables.AndroidObservable.bindActivity; + +/** + * Activity that binds to a counting sequence and is able to listen in and out to that + * sequence by pressing a toggle button. The button disables itself once the sequence + * finishes. + */ +public class ListenInOutActivity extends Activity implements Observer { + + private Observable source; + private Subscription subscription; + private TextView textView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.listen_in_out_activity); + + textView = (TextView) findViewById(android.R.id.text1); + + // in a production app, you would use dependency injection, fragments, or other + // means to preserve the observable, but this will suffice here + source = (Observable) getLastNonConfigurationInstance(); + if (source == null) { + source = SampleObservables.numberStrings(1, 100, 200).publish(); + ((ConnectableObservable) source).connect(); + } + + subscribeToSequence(); + } + + private void subscribeToSequence() { + subscription = bindActivity(this, source).subscribe(this); + } + + @Override + public Object onRetainNonConfigurationInstance() { + return source; + } + + @Override + protected void onDestroy() { + subscription.unsubscribe(); + super.onDestroy(); + } + + @Override + public void onCompleted() { + TextView button = (TextView) findViewById(R.id.toggle_button); + button.setText("Completed"); + button.setEnabled(false); + } + + @Override + public void onError(Throwable e) { + e.printStackTrace(); + Toast.makeText(this, "Error: " + e, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onNext(String s) { + textView.setText(s); + } + + public void onSequenceToggleClicked(View view) { + if (((ToggleButton) view).isChecked()) { + subscription.unsubscribe(); + } else { + subscribeToSequence(); + } + } +} diff --git a/samples/app/src/main/java/rx/android/samples/ListeningFragmentActivity.java b/samples/app/src/main/java/rx/android/samples/ListeningFragmentActivity.java new file mode 100644 index 00000000..00a6e971 --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/ListeningFragmentActivity.java @@ -0,0 +1,96 @@ +package rx.android.samples; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import rx.Subscriber; +import rx.Subscription; +import rx.android.observables.AndroidObservable; +import rx.observables.ConnectableObservable; +import rx.subscriptions.Subscriptions; + + +/** + * Problem: + * You have a background sequence which keeps emitting items (either a limited or unlimited number) + * and your UI component should be able to "listen in" to the sequence, i.e. it's okay to miss + * in-flight items when going e.g. through a screen rotation or being otherwise detached from the + * screen for a limited period of time. (Another example is a "page out" in a fragment ViewPager.) + *

+ * This is useful if you need behavior that mimics event buses. Think of a publishing + * Observable as a channel or queue on an event bus. + *

+ * Solution: + * Combine {@link android.app.Fragment#setRetainInstance(boolean)} with + * {@link rx.android.schedulers.AndroidSchedulers#mainThread()} and {@link rx.Observable#publish()} + */ +public class ListeningFragmentActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.listening_fragment_activity); + } + + @SuppressWarnings("ConstantConditions") + public static class ListeningFragment extends Fragment { + + private ConnectableObservable strings; + private Subscription subscription = Subscriptions.empty(); + + public ListeningFragment() { + setRetainInstance(true); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + strings = SampleObservables.numberStrings(1, 50, 250).publish(); + strings.connect(); // trigger the sequence + } + + @Override + public void onDestroyView() { + subscription.unsubscribe(); // stop listening + super.onDestroyView(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.retained_fragment, container, false); + } + + @Override + public void onViewCreated(final View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + final TextView textView = (TextView) view.findViewById(android.R.id.text1); + + // re-connect to sequence + subscription = AndroidObservable.bindFragment(this, strings).subscribe(new Subscriber() { + + @Override + public void onCompleted() { + Toast.makeText(getActivity(), "Done!", Toast.LENGTH_SHORT).show(); + } + + @Override + public void onError(Throwable throwable) { + + } + + @Override + public void onNext(String s) { + textView.setText(s); + } + }); + } + } +} diff --git a/samples/app/src/main/java/rx/android/samples/RetainedFragmentActivity.java b/samples/app/src/main/java/rx/android/samples/RetainedFragmentActivity.java new file mode 100644 index 00000000..c161f511 --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/RetainedFragmentActivity.java @@ -0,0 +1,107 @@ +package rx.android.samples; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.TextView; + +import org.json.JSONException; +import org.json.JSONObject; + +import rx.Observable; +import rx.Subscription; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.subscriptions.Subscriptions; + +import static rx.android.observables.AndroidObservable.bindFragment; + +/** + * Problem: + * You have a data source (where that data is potentially expensive to obtain), and you want to + * emit this data into a fragment. However, you want to gracefully deal with rotation changes and + * not lose any data already emitted. + *

+ * Solution: + * Combine {@link android.app.Fragment#setRetainInstance(boolean)} with + * {@link rx.android.schedulers.AndroidSchedulers#mainThread()} and {@link rx.Observable#cache()} + */ +public class RetainedFragmentActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setTitle("Fake API call"); + setContentView(R.layout.retained_fragment_activity); + } + + @SuppressWarnings("ConstantConditions") + public static class RetainedFragment extends Fragment { + + // in a production app, you don't want to have JSON parser code in your fragment, + // but we'll simplify a little here + private static final Func1 PARSE_JSON = new Func1() { + @Override + public String call(String json) { + try { + JSONObject jsonObject = new JSONObject(json); + return String.valueOf(jsonObject.getInt("result")); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + }; + + private Observable strings; + private Subscription subscription = Subscriptions.empty(); + + public RetainedFragment() { + setRetainInstance(true); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // simulate fetching a JSON document with a latency of 2 seconds + // in retained fragments, it's sufficient to bind the fragment in onCreate, since + // Android takes care of detaching the Activity for us, and holding a reference for + // the duration of the observable does not harm. + strings = bindFragment(this, SampleObservables.fakeApiCall(2000).map(PARSE_JSON).cache()); + } + + @Override + public void onDestroyView() { + subscription.unsubscribe(); + super.onDestroyView(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + getActivity().setProgressBarIndeterminateVisibility(true); + return inflater.inflate(R.layout.retained_fragment, container, false); + } + + @Override + public void onViewCreated(final View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + final TextView textView = (TextView) view.findViewById(android.R.id.text1); + + // (re-)subscribe to the sequence, which either emits the cached result or simply re- + // attaches the subscriber to wait for it to arrive + subscription = strings.subscribe(new Action1() { + @Override + public void call(String result) { + textView.setText(result); + getActivity().setProgressBarIndeterminateVisibility(false); + } + }); + } + } +} diff --git a/samples/app/src/main/java/rx/android/samples/SampleObservables.java b/samples/app/src/main/java/rx/android/samples/SampleObservables.java new file mode 100644 index 00000000..d4cbf315 --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/SampleObservables.java @@ -0,0 +1,42 @@ +package rx.android.samples; + +import android.os.SystemClock; + +import rx.Observable; +import rx.Subscriber; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +public class SampleObservables { + + /** + * Emits numbers as strings, where these numbers a generated on a background thread. + */ + public static Observable numberStrings(int from, int to, final long delay) { + return Observable.range(from, to).map(new Func1() { + @Override + public String call(Integer integer) { + return integer.toString(); + } + }).doOnNext(new Action1() { + @Override + public void call(String s) { + SystemClock.sleep(delay); + } + }).subscribeOn(Schedulers.newThread()); + } + + public static Observable fakeApiCall(final long delay) { + return Observable.create(new Observable.OnSubscribe() { + @Override + public void call(Subscriber subscriber) { + // simulate I/O latency + SystemClock.sleep(delay); + final String fakeJson = "{\"result\": 42}"; + subscriber.onNext(fakeJson); + subscriber.onCompleted(); + } + }).subscribeOn(Schedulers.io()); + } +} diff --git a/samples/app/src/main/java/rx/android/samples/SamplesApplication.java b/samples/app/src/main/java/rx/android/samples/SamplesApplication.java new file mode 100644 index 00000000..d90ba339 --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/SamplesApplication.java @@ -0,0 +1,13 @@ +package rx.android.samples; + +import android.app.Application; +import android.os.StrictMode; + +public class SamplesApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + StrictMode.enableDefaults(); + } +} diff --git a/samples/app/src/main/java/rx/android/samples/UIBindingActivity.java b/samples/app/src/main/java/rx/android/samples/UIBindingActivity.java new file mode 100644 index 00000000..3c34494c --- /dev/null +++ b/samples/app/src/main/java/rx/android/samples/UIBindingActivity.java @@ -0,0 +1,150 @@ +package rx.android.samples; + +import android.app.Activity; +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.Button; +import android.widget.TextView; +import org.json.JSONException; +import org.json.JSONObject; +import rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.functions.Func1; +import rx.subscriptions.Subscriptions; + +/** + * Problem: + * You have a data source (where that data is potentially expensive to obtain), and you want to + * emit this data into a fragment. However, you want to gracefully deal with rotation changes and + * not lose any data already emitted. + *

+ * You also want your UI to update accordingly to the data being emitted. + * + * @author zsiegel (zsiegel87@gmail.com) + */ +public class UIBindingActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setTitle("UIBinding"); + setContentView(R.layout.ui_binding_activity); + } + + @SuppressWarnings("ConstantConditions") + public static class RetainedBindingFragment extends Fragment { + + private Button startButton; + + private Observable request; + private Subscription requestSubscription = Subscriptions.empty(); + + // in a production app, you don't want to have JSON parser code in your fragment, + // but we'll simplify a little here + private static final Func1 PARSE_JSON = new Func1() { + @Override + public String call(String json) { + try { + JSONObject jsonObject = new JSONObject(json); + return String.valueOf(jsonObject.getInt("result")); + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + }; + + public RetainedBindingFragment() { + setRetainInstance(true); + } + + /** + * We un-subscribe whenever we are paused + */ + @Override + public void onPause() { + requestSubscription.unsubscribe(); + super.onPause(); + } + + /** + * We re-subscribe whenever we are resumed + */ + @Override + public void onResume() { + super.onResume(); + subscribe(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.retained_fragment_v2, container, false); + } + + @Override + public void onViewCreated(final View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + final TextView textView = (TextView) getView().findViewById(android.R.id.text1); + + startButton = (Button) view.findViewById(R.id.button); + startButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + textView.setText(""); + start(); + } + }); + } + + private void start() { + + request = SampleObservables + .fakeApiCall(5000) + .map(PARSE_JSON) + .observeOn(AndroidSchedulers.mainThread()) + .cache(); + + subscribe(); + } + + /** + * We subscribe/re-subscribe here + */ + private void subscribe() { + if (request != null) { + + final TextView textView = (TextView) getView().findViewById(android.R.id.text1); + + requestSubscription = request.map(new Func1() { + + //Consume the data that comes back and then signal that we are done + @Override + public Boolean call(String s) { + textView.setText(s); + return true; + } + }).startWith(false) //Before we receive data our request is not complete + .subscribe(new Action1() { + + //We update the UI based on the state of the request + @Override + public void call(Boolean completed) { + setRequestInProgress(completed); + } + }); + } + } + + private void setRequestInProgress(boolean completed) { + getActivity().setProgressBarIndeterminateVisibility(!completed); + startButton.setEnabled(completed); + } + } +} diff --git a/samples/app/src/main/res/drawable-hdpi/ic_launcher.png b/samples/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 00000000..96a442e5 Binary files /dev/null and b/samples/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/samples/app/src/main/res/drawable-mdpi/ic_launcher.png b/samples/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 00000000..359047df Binary files /dev/null and b/samples/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/samples/app/src/main/res/drawable-xhdpi/ic_launcher.png b/samples/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 00000000..71c6d760 Binary files /dev/null and b/samples/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/samples/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/samples/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..4df18946 Binary files /dev/null and b/samples/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/samples/app/src/main/res/layout/list_fragment.xml b/samples/app/src/main/res/layout/list_fragment.xml new file mode 100644 index 00000000..be671b87 --- /dev/null +++ b/samples/app/src/main/res/layout/list_fragment.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/samples/app/src/main/res/layout/list_fragment_activity.xml b/samples/app/src/main/res/layout/list_fragment_activity.xml new file mode 100644 index 00000000..dcf41594 --- /dev/null +++ b/samples/app/src/main/res/layout/list_fragment_activity.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/samples/app/src/main/res/layout/listen_in_out_activity.xml b/samples/app/src/main/res/layout/listen_in_out_activity.xml new file mode 100644 index 00000000..94c1fd86 --- /dev/null +++ b/samples/app/src/main/res/layout/listen_in_out_activity.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/samples/app/src/main/res/layout/listening_fragment_activity.xml b/samples/app/src/main/res/layout/listening_fragment_activity.xml new file mode 100644 index 00000000..8b267945 --- /dev/null +++ b/samples/app/src/main/res/layout/listening_fragment_activity.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/samples/app/src/main/res/layout/retained_fragment.xml b/samples/app/src/main/res/layout/retained_fragment.xml new file mode 100644 index 00000000..0fc012b4 --- /dev/null +++ b/samples/app/src/main/res/layout/retained_fragment.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/samples/app/src/main/res/layout/retained_fragment_activity.xml b/samples/app/src/main/res/layout/retained_fragment_activity.xml new file mode 100644 index 00000000..2ac313cc --- /dev/null +++ b/samples/app/src/main/res/layout/retained_fragment_activity.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/samples/app/src/main/res/layout/retained_fragment_v2.xml b/samples/app/src/main/res/layout/retained_fragment_v2.xml new file mode 100644 index 00000000..aa5e2792 --- /dev/null +++ b/samples/app/src/main/res/layout/retained_fragment_v2.xml @@ -0,0 +1,19 @@ + + + + + + +