Skip to content

Commit 13d5099

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 6e6f23e + fecf2a3 commit 13d5099

File tree

23 files changed

+3298
-29
lines changed

23 files changed

+3298
-29
lines changed

rxjava-contrib/rxjava-android/src/main/java/rx/android/observables/AndroidObservable.java

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,145 @@
1515
*/
1616
package rx.android.observables;
1717

18+
import static org.mockito.Mockito.verify;
19+
20+
import org.junit.Before;
21+
import org.junit.Test;
22+
import org.junit.runner.RunWith;
23+
import org.mockito.Mock;
24+
import org.mockito.MockitoAnnotations;
25+
import org.robolectric.Robolectric;
26+
import org.robolectric.RobolectricTestRunner;
27+
import org.robolectric.annotation.Config;
1828
import rx.Observable;
29+
import rx.Observer;
1930
import rx.operators.OperationObserveFromAndroidComponent;
2031

2132
import android.app.Activity;
2233
import android.app.Fragment;
34+
import android.os.Build;
35+
import android.support.v4.app.FragmentActivity;
36+
2337

2438
public final class AndroidObservable {
2539

40+
private static final boolean USES_SUPPORT_FRAGMENTS;
41+
42+
static {
43+
boolean supportFragmentsAvailable = false;
44+
try {
45+
Class.forName("android.support.v4.app.Fragment");
46+
supportFragmentsAvailable = true;
47+
} catch (ClassNotFoundException e) {
48+
}
49+
USES_SUPPORT_FRAGMENTS = supportFragmentsAvailable;
50+
}
51+
2652
private AndroidObservable() {}
2753

54+
/**
55+
* Transforms a source observable to be attached to the given Activity, in such a way that notifications will always
56+
* arrive on the main UI thread. Currently, this is equivalent to calling <code>observeOn(AndroidSchedulers.mainThread())</code>,
57+
* but this behavior may change in the future, so it is encouraged to use this wrapper instead.
58+
* <p/>
59+
* You must unsubscribe from the returned observable in <code>onDestroy</code> to not leak the given Activity.
60+
* <p/>
61+
* Ex.:
62+
* <pre>
63+
* // in any Activity
64+
* mSubscription = fromActivity(this, Observable.just("value")).subscribe(...);
65+
* // in onDestroy
66+
* mSubscription.unsubscribe();
67+
* </pre>
68+
*
69+
* @param activity the activity in which the source observable will be observed
70+
* @param sourceObservable the observable sequence to observe from the given Activity
71+
* @param <T>
72+
* @return a new observable sequence that will emit notifications on the main UI thread
73+
*/
2874
public static <T> Observable<T> fromActivity(Activity activity, Observable<T> sourceObservable) {
2975
return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, activity);
3076
}
3177

32-
public static <T> Observable<T> fromFragment(Fragment fragment, Observable<T> sourceObservable) {
33-
return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, fragment);
78+
/**
79+
* Transforms a source observable to be attached to the given fragment, in such a way that notifications will always
80+
* arrive on the main UI thread. Moreover, it will be guaranteed that no notifications will be delivered to the
81+
* fragment while it's in detached state (i.e. its host Activity was destroyed.) In other words, during calls
82+
* to onNext, you may assume that fragment.getActivity() will never return null.
83+
* <p/>
84+
* This method accepts both native fragments and support library fragments in its first parameter. It will throw
85+
* for unsupported types.
86+
* <p/>
87+
* You must unsubscribe from the returned observable in <code>onDestroy</code> to not leak the given fragment.
88+
* <p/>
89+
* Ex.:
90+
* <pre>
91+
* // in any Fragment
92+
* mSubscription = fromFragment(this, Observable.just("value")).subscribe(...);
93+
* // in onDestroy
94+
* mSubscription.unsubscribe();
95+
* </pre>
96+
*
97+
* @param fragment the fragment in which the source observable will be observed
98+
* @param sourceObservable the observable sequence to observe from the given fragment
99+
* @param <T>
100+
* @return a new observable sequence that will emit notifications on the main UI thread
101+
*/
102+
public static <T> Observable<T> fromFragment(Object fragment, Observable<T> sourceObservable) {
103+
if (USES_SUPPORT_FRAGMENTS && fragment instanceof android.support.v4.app.Fragment) {
104+
return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, (android.support.v4.app.Fragment) fragment);
105+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && fragment instanceof Fragment) {
106+
return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, (Fragment) fragment);
107+
} else {
108+
throw new IllegalArgumentException("Target fragment is neither a native nor support library Fragment");
109+
}
34110
}
35111

36-
public static <T> Observable<T> fromFragment(android.support.v4.app.Fragment fragment, Observable<T> sourceObservable) {
37-
return OperationObserveFromAndroidComponent.observeFromAndroidComponent(sourceObservable, fragment);
112+
@RunWith(RobolectricTestRunner.class)
113+
@Config(manifest = Config.NONE)
114+
public static final class AndroidObservableTest {
115+
116+
// support library fragments
117+
private FragmentActivity fragmentActivity;
118+
private android.support.v4.app.Fragment supportFragment;
119+
120+
// native fragments
121+
private Activity activity;
122+
private Fragment fragment;
123+
124+
@Mock
125+
private Observer<String> observer;
126+
127+
@Before
128+
public void setup() {
129+
MockitoAnnotations.initMocks(this);
130+
supportFragment = new android.support.v4.app.Fragment();
131+
fragmentActivity = Robolectric.buildActivity(FragmentActivity.class).create().get();
132+
fragmentActivity.getSupportFragmentManager().beginTransaction().add(supportFragment, null).commit();
133+
134+
fragment = new Fragment();
135+
activity = Robolectric.buildActivity(Activity.class).create().get();
136+
activity.getFragmentManager().beginTransaction().add(fragment, null).commit();
137+
}
138+
139+
@Test
140+
public void itSupportsFragmentsFromTheSupportV4Library() {
141+
fromFragment(supportFragment, Observable.just("success")).subscribe(observer);
142+
verify(observer).onNext("success");
143+
verify(observer).onCompleted();
144+
}
145+
146+
@Test
147+
public void itSupportsNativeFragments() {
148+
fromFragment(fragment, Observable.just("success")).subscribe(observer);
149+
verify(observer).onNext("success");
150+
verify(observer).onCompleted();
151+
}
152+
153+
@Test(expected = IllegalArgumentException.class)
154+
public void itThrowsIfObjectPassedIsNotAFragment() {
155+
fromFragment("not a fragment", Observable.never());
156+
}
38157
}
39158

40159
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
apply plugin: 'osgi'
2+
3+
sourceCompatibility = JavaVersion.VERSION_1_6
4+
targetCompatibility = JavaVersion.VERSION_1_6
5+
6+
dependencies {
7+
compile project(':rxjava-core')
8+
testCompile project(":rxjava-core").sourceSets.test.output
9+
provided 'junit:junit-dep:4.10'
10+
provided 'org.mockito:mockito-core:1.8.5'
11+
}
12+
13+
javadoc {
14+
options {
15+
doclet = "org.benjchristensen.doclet.DocletExclude"
16+
docletpath = [rootProject.file('./gradle/doclet-exclude.jar')]
17+
stylesheetFile = rootProject.file('./gradle/javadocStyleSheet.css')
18+
windowTitle = "RxJava Javadoc ${project.version}"
19+
}
20+
options.addStringOption('top').value = '<h2 class="title" style="padding-top:40px">RxJava</h2>'
21+
}
22+
23+
jar {
24+
manifest {
25+
name = 'rxjava-string'
26+
instruction 'Bundle-Vendor', 'Netflix'
27+
instruction 'Bundle-DocURL', 'https://github.com/Netflix/RxJava'
28+
instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*'
29+
}
30+
}

0 commit comments

Comments
 (0)