Skip to content

Commit 4cb3e40

Browse files
Merge pull request #1545 from ronshapiro/master
Make Android ViewObservable.input observe TextView instead of String
2 parents fa49293 + 82bc2de commit 4cb3e40

File tree

4 files changed

+330
-0
lines changed

4 files changed

+330
-0
lines changed

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

+14
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,32 @@
1818
import rx.Observable;
1919
import rx.operators.OperatorCompoundButtonInput;
2020
import rx.operators.OperatorEditTextInput;
21+
import rx.operators.OperatorTextViewInput;
2122
import rx.operators.OperatorViewClick;
2223

2324
import android.view.View;
2425
import android.widget.CompoundButton;
2526
import android.widget.EditText;
27+
import android.widget.TextView;
2628

2729
public class ViewObservable {
2830

2931
public static <T extends View> Observable<T> clicks(final T view, final boolean emitInitialValue) {
3032
return Observable.create(new OperatorViewClick<T>(view, emitInitialValue));
3133
}
3234

35+
public static <T extends TextView> Observable<T> text(final T input) {
36+
return text(input, false);
37+
}
38+
39+
public static <T extends TextView> Observable<T> text(final T input, final boolean emitInitialValue) {
40+
return Observable.create(new OperatorTextViewInput<T>(input, emitInitialValue));
41+
}
42+
43+
/**
44+
* @deprecated Use #text(android.widget.TextView, boolean)} (and map the values) instead.
45+
*/
46+
@Deprecated
3347
public static Observable<String> input(final EditText input, final boolean emitInitialValue) {
3448
return Observable.create(new OperatorEditTextInput(input, emitInitialValue));
3549
}

rxjava-contrib/rxjava-android/src/main/java/rx/operators/OperatorEditTextInput.java

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
import android.text.TextWatcher;
2626
import android.widget.EditText;
2727

28+
/**
29+
* @deprecated Use {@link rx.operators.OperatorTextViewInput} instead.
30+
*/
31+
@Deprecated
2832
public class OperatorEditTextInput implements Observable.OnSubscribe<String> {
2933
private final EditText input;
3034
private final boolean emitInitialValue;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Copyright 2014 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package rx.operators;
17+
18+
import rx.Observable;
19+
import rx.Subscriber;
20+
import rx.Subscription;
21+
import rx.android.observables.Assertions;
22+
import rx.android.subscriptions.AndroidSubscriptions;
23+
import rx.functions.Action0;
24+
import android.text.Editable;
25+
import android.text.TextWatcher;
26+
import android.widget.TextView;
27+
28+
public class OperatorTextViewInput<T extends TextView> implements Observable.OnSubscribe<T> {
29+
private final T input;
30+
private final boolean emitInitialValue;
31+
32+
public OperatorTextViewInput(final T input, final boolean emitInitialValue) {
33+
this.input = input;
34+
this.emitInitialValue = emitInitialValue;
35+
}
36+
37+
@Override
38+
public void call(final Subscriber<? super T> observer) {
39+
Assertions.assertUiThread();
40+
final TextWatcher watcher = new SimpleTextWatcher() {
41+
@Override
42+
public void afterTextChanged(final Editable editable) {
43+
observer.onNext(input);
44+
}
45+
};
46+
47+
final Subscription subscription = AndroidSubscriptions.unsubscribeInUiThread(new Action0() {
48+
@Override
49+
public void call() {
50+
input.removeTextChangedListener(watcher);
51+
}
52+
});
53+
54+
if (emitInitialValue) {
55+
observer.onNext(input);
56+
}
57+
58+
input.addTextChangedListener(watcher);
59+
observer.add(subscription);
60+
}
61+
62+
private static class SimpleTextWatcher implements TextWatcher {
63+
@Override
64+
public void beforeTextChanged(final CharSequence sequence, final int start, final int count, final int after) {
65+
// nothing to do
66+
}
67+
68+
@Override
69+
public void onTextChanged(final CharSequence sequence, final int start, final int before, final int count) {
70+
// nothing to do
71+
}
72+
73+
@Override
74+
public void afterTextChanged(final Editable editable) {
75+
// nothing to do
76+
}
77+
}
78+
}
79+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
/**
2+
* Copyright 2014 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package rx.android.operators;
17+
18+
import static org.mockito.Matchers.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import android.app.Activity;
22+
import android.widget.EditText;
23+
import android.widget.TextView;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.mockito.InOrder;
27+
import org.robolectric.Robolectric;
28+
import org.robolectric.RobolectricTestRunner;
29+
import rx.Observable;
30+
import rx.Observer;
31+
import rx.Subscription;
32+
import rx.android.observables.ViewObservable;
33+
import rx.functions.Func1;
34+
import rx.observers.TestObserver;
35+
36+
@RunWith(RobolectricTestRunner.class)
37+
public class OperatorTextViewInputTest {
38+
39+
private static TextView createTextView(final String value) {
40+
final Activity activity = Robolectric.buildActivity(Activity.class).create().get();
41+
final TextView text = new TextView(activity);
42+
43+
if (value != null) {
44+
text.setText(value);
45+
}
46+
47+
return text;
48+
}
49+
50+
private static EditText createEditText(final String value) {
51+
final Activity activity = Robolectric.buildActivity(Activity.class).create().get();
52+
final EditText text = new EditText(activity);
53+
54+
if (value != null) {
55+
text.setText(value);
56+
}
57+
58+
return text;
59+
}
60+
61+
@Test
62+
@SuppressWarnings("unchecked")
63+
public void testOverloadedMethodDefaultsWithoutInitialValue() {
64+
final TextView input = createTextView("initial");
65+
final Observable<TextView> observable = ViewObservable.text(input);
66+
final Observer<TextView> observer = mock(Observer.class);
67+
final Subscription subscription = observable.subscribe(new TestObserver<TextView>(observer));
68+
69+
final InOrder inOrder = inOrder(observer);
70+
71+
inOrder.verify(observer, never()).onNext(any(TextView.class));
72+
73+
input.setText("1");
74+
inOrder.verify(observer, times(1)).onNext(input);
75+
76+
input.setText("2");
77+
inOrder.verify(observer, times(1)).onNext(input);
78+
79+
input.setText("3");
80+
inOrder.verify(observer, times(1)).onNext(input);
81+
82+
subscription.unsubscribe();
83+
input.setText("4");
84+
inOrder.verify(observer, never()).onNext(any(TextView.class));
85+
86+
inOrder.verify(observer, never()).onError(any(Throwable.class));
87+
inOrder.verify(observer, never()).onCompleted();
88+
}
89+
90+
@Test
91+
@SuppressWarnings("unchecked")
92+
public void testWithoutInitialValue() {
93+
final TextView input = createTextView("initial");
94+
final Observable<TextView> observable = ViewObservable.text(input, false);
95+
final Observer<TextView> observer = mock(Observer.class);
96+
final Subscription subscription = observable.subscribe(new TestObserver<TextView>(observer));
97+
98+
final InOrder inOrder = inOrder(observer);
99+
100+
inOrder.verify(observer, never()).onNext(any(TextView.class));
101+
102+
input.setText("1");
103+
inOrder.verify(observer, times(1)).onNext(input);
104+
105+
input.setText("2");
106+
inOrder.verify(observer, times(1)).onNext(input);
107+
108+
input.setText("3");
109+
inOrder.verify(observer, times(1)).onNext(input);
110+
111+
subscription.unsubscribe();
112+
input.setText("4");
113+
inOrder.verify(observer, never()).onNext(any(TextView.class));
114+
115+
inOrder.verify(observer, never()).onError(any(Throwable.class));
116+
inOrder.verify(observer, never()).onCompleted();
117+
}
118+
119+
@Test
120+
@SuppressWarnings("unchecked")
121+
public void testWithInitialValue() {
122+
final TextView input = createTextView("initial");
123+
final Observable<TextView> observable = ViewObservable.text(input, true);
124+
final Observer<TextView> observer = mock(Observer.class);
125+
final Subscription subscription = observable.subscribe(new TestObserver<TextView>(observer));
126+
127+
final InOrder inOrder = inOrder(observer);
128+
129+
inOrder.verify(observer, times(1)).onNext(input);
130+
131+
input.setText("one");
132+
inOrder.verify(observer, times(1)).onNext(input);
133+
134+
input.setText("two");
135+
inOrder.verify(observer, times(1)).onNext(input);
136+
137+
input.setText("three");
138+
inOrder.verify(observer, times(1)).onNext(input);
139+
140+
subscription.unsubscribe();
141+
input.setText("four");
142+
inOrder.verify(observer, never()).onNext(any(TextView.class));
143+
144+
inOrder.verify(observer, never()).onError(any(Throwable.class));
145+
inOrder.verify(observer, never()).onCompleted();
146+
}
147+
148+
@Test
149+
@SuppressWarnings("unchecked")
150+
public void testMultipleSubscriptions() {
151+
final TextView input = createTextView("initial");
152+
final Observable<TextView> observable = ViewObservable.text(input, false);
153+
154+
final Observer<TextView> observer1 = mock(Observer.class);
155+
final Observer<TextView> observer2 = mock(Observer.class);
156+
157+
final Subscription subscription1 = observable.subscribe(new TestObserver<TextView>(observer1));
158+
final Subscription subscription2 = observable.subscribe(new TestObserver<TextView>(observer2));
159+
160+
final InOrder inOrder1 = inOrder(observer1);
161+
final InOrder inOrder2 = inOrder(observer2);
162+
163+
input.setText("1");
164+
inOrder1.verify(observer1, times(1)).onNext(input);
165+
inOrder2.verify(observer2, times(1)).onNext(input);
166+
167+
input.setText("2");
168+
inOrder1.verify(observer1, times(1)).onNext(input);
169+
inOrder2.verify(observer2, times(1)).onNext(input);
170+
subscription1.unsubscribe();
171+
172+
input.setText("3");
173+
inOrder1.verify(observer1, never()).onNext(any(TextView.class));
174+
inOrder2.verify(observer2, times(1)).onNext(input);
175+
subscription2.unsubscribe();
176+
177+
input.setText("4");
178+
inOrder1.verify(observer1, never()).onNext(any(TextView.class));
179+
inOrder2.verify(observer2, never()).onNext(any(TextView.class));
180+
181+
inOrder1.verify(observer1, never()).onError(any(Throwable.class));
182+
inOrder2.verify(observer2, never()).onError(any(Throwable.class));
183+
184+
inOrder1.verify(observer1, never()).onCompleted();
185+
inOrder2.verify(observer2, never()).onCompleted();
186+
}
187+
188+
@Test
189+
@SuppressWarnings("unchecked")
190+
public void testTextViewSubclass() {
191+
final EditText input = createEditText("initial");
192+
final Observable<EditText> observable = ViewObservable.text(input, false);
193+
final Observer<EditText> observer = mock(Observer.class);
194+
observable.subscribe(new TestObserver<EditText>(observer));
195+
196+
final InOrder inOrder = inOrder(observer);
197+
198+
inOrder.verify(observer, never()).onNext(any(EditText.class));
199+
200+
input.setText("1");
201+
inOrder.verify(observer, times(1)).onNext(input);
202+
}
203+
204+
@Test
205+
@SuppressWarnings("unchecked")
206+
public void testLegacyStringObservableCompatibility() {
207+
final EditText input = createEditText("initial");
208+
final Observable<String> observable = ViewObservable.text(input, false)
209+
.map(new Func1<EditText, String>() {
210+
211+
@Override
212+
public String call(EditText view) {
213+
return view.getText().toString();
214+
}
215+
});
216+
final Observer<String> observer = mock(Observer.class);
217+
observable.subscribe(new TestObserver<String>(observer));
218+
219+
final InOrder inOrder = inOrder(observer);
220+
221+
inOrder.verify(observer, never()).onNext(anyString());
222+
223+
input.setText("1");
224+
inOrder.verify(observer, times(1)).onNext("1");
225+
226+
input.setText("2");
227+
inOrder.verify(observer, times(1)).onNext("2");
228+
229+
input.setText("3");
230+
inOrder.verify(observer, times(1)).onNext("3");
231+
}
232+
233+
}

0 commit comments

Comments
 (0)