ShadowTextView.append updates the selectionStart and selectionEnd
Added getPreviousKeyCode and getPreviousKeyEvent to ShadowTextView
Move support for selectionStart and selectionEnd from ShadowEditText to ShadowTextView
diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEditText.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEditText.java
index 41f63f1..50cf455 100644
--- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEditText.java
+++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowEditText.java
@@ -15,8 +15,6 @@
public class ShadowEditText extends ShadowTextView {
private int maxLength = Integer.MAX_VALUE;
- private int selectionStart = 0;
- private int selectionEnd = 0;
public ShadowEditText() {
focusable = true;
@@ -48,24 +46,15 @@
return (Editable) text;
}
+ @Override
@Implementation
public void setSelection(int index) {
- setSelection(index, index);
+ super.setSelection(index);
}
+ @Override
@Implementation
public void setSelection(int start, int end) {
- selectionStart = start;
- selectionEnd = end;
- }
-
- @Implementation
- public int getSelectionStart() {
- return selectionStart;
- }
-
- @Implementation
- public int getSelectionEnd() {
- return selectionEnd;
+ super.setSelection(start, end);
}
}
\ No newline at end of file
diff --git a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java
index 8425337..d5bb48d 100644
--- a/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java
+++ b/src/main/java/com/xtremelabs/robolectric/shadows/ShadowTextView.java
@@ -41,8 +41,12 @@
private int textAppearanceId;
private TransformationMethod transformationMethod;
private int inputType;
+ protected int selectionStart = 0;
+ protected int selectionEnd = 0;
private List<TextWatcher> watchers = new ArrayList<TextWatcher>();
+ private List<Integer> previousKeyCodes = new ArrayList<Integer>();
+ private List<KeyEvent> previousKeyEvents = new ArrayList<KeyEvent>();
@Override
public void applyAttributes() {
@@ -71,6 +75,8 @@
@Implementation
public final void append(CharSequence text) {
+ boolean isSelectStartAtEnd = selectionStart == this.text.length();
+ boolean isSelectEndAtEnd = selectionEnd == this.text.length();
CharSequence oldValue = this.text;
StringBuffer sb = new StringBuffer(this.text);
sb.append(text);
@@ -78,9 +84,15 @@
sendBeforeTextChanged(sb.toString());
this.text = sb.toString();
+ if (isSelectStartAtEnd) {
+ selectionStart = this.text.length();
+ }
+ if (isSelectEndAtEnd) {
+ selectionEnd = this.text.length();
+ }
+
sendOnTextChanged(oldValue);
sendAfterTextChanged();
-
}
@Implementation
@@ -247,6 +259,8 @@
@Implementation
public boolean onKeyDown(int keyCode, KeyEvent event) {
+ previousKeyCodes.add(keyCode);
+ previousKeyEvents.add(event);
if (onKeyListener != null) {
return onKeyListener.onKey(realView, keyCode, event);
} else {
@@ -256,6 +270,8 @@
@Implementation
public boolean onKeyUp(int keyCode, KeyEvent event) {
+ previousKeyCodes.add(keyCode);
+ previousKeyEvents.add(event);
if (onKeyListener != null) {
return onKeyListener.onKey(realView, keyCode, event);
} else {
@@ -263,6 +279,14 @@
}
}
+ public int getPreviousKeyCode(int index) {
+ return previousKeyCodes.get(index);
+ }
+
+ public KeyEvent getPreviousKeyEvent(int index) {
+ return previousKeyEvents.get(index);
+ }
+
@Implementation
public int getGravity() {
return gravity;
@@ -426,6 +450,25 @@
return new TextPaint();
}
+ public void setSelection(int index) {
+ setSelection(index, index);
+ }
+
+ public void setSelection(int start, int end) {
+ selectionStart = start;
+ selectionEnd = end;
+ }
+
+ @Implementation
+ public int getSelectionStart() {
+ return selectionStart;
+ }
+
+ @Implementation
+ public int getSelectionEnd() {
+ return selectionEnd;
+ }
+
/**
* @return the list of currently registered watchers/listeners
*/
diff --git a/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java b/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java
index 5a6e667..12aab82 100644
--- a/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java
+++ b/src/test/java/com/xtremelabs/robolectric/shadows/TextViewTest.java
@@ -35,9 +35,9 @@
@RunWith(WithTestDefaultsRunner.class)
public class TextViewTest {
- private static final String INITIAL_TEXT = "initial text";
- private static final String NEW_TEXT = "new text";
- private TextView textView;
+ private static final String INITIAL_TEXT = "initial text";
+ private static final String NEW_TEXT = "new text";
+ private TextView textView;
@Before
public void setUp() throws Exception {
@@ -138,130 +138,130 @@
}
@Test
- public void shouldNotHaveTransformationMethodByDefault(){
+ public void shouldNotHaveTransformationMethodByDefault() {
ShadowTextView view = new ShadowTextView();
assertThat(view.getTransformationMethod(), is(CoreMatchers.<Object>nullValue()));
}
@Test
- public void shouldAllowSettingATransformationMethod(){
+ public void shouldAllowSettingATransformationMethod() {
ShadowTextView view = new ShadowTextView();
view.setTransformationMethod(new ShadowPasswordTransformationMethod());
assertEquals(view.getTransformationMethod().getClass(), ShadowPasswordTransformationMethod.class);
}
-
+
@Test
public void testGetInputType() throws Exception {
assertThat(textView.getInputType(), not(equalTo(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)));
textView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
assertThat(textView.getInputType(), equalTo(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD));
}
-
- @Test
- public void givenATextViewWithATextWatcherAdded_WhenSettingTextWithTextResourceId_ShouldNotifyTextWatcher() {
- MockTextWatcher mockTextWatcher = new MockTextWatcher();
- textView.addTextChangedListener(mockTextWatcher);
- textView.setText(R.string.hello);
-
- assertEachTextWatcherEventWasInvoked(mockTextWatcher);
+ @Test
+ public void givenATextViewWithATextWatcherAdded_WhenSettingTextWithTextResourceId_ShouldNotifyTextWatcher() {
+ MockTextWatcher mockTextWatcher = new MockTextWatcher();
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.setText(R.string.hello);
+
+ assertEachTextWatcherEventWasInvoked(mockTextWatcher);
}
-
- @Test
- public void givenATextViewWithATextWatcherAdded_WhenSettingTextWithCharSequence_ShouldNotifyTextWatcher() {
- MockTextWatcher mockTextWatcher = new MockTextWatcher();
- textView.addTextChangedListener(mockTextWatcher);
- textView.setText("text");
-
- assertEachTextWatcherEventWasInvoked(mockTextWatcher);
- }
-
@Test
- public void givenATextViewWithMultipleTextWatchersAdded_WhenSettingText_ShouldNotifyEachTextWatcher() {
- List<MockTextWatcher> mockTextWatchers = anyNumberOfTextWatchers();
- for (MockTextWatcher textWatcher : mockTextWatchers) {
- textView.addTextChangedListener(textWatcher);
+ public void givenATextViewWithATextWatcherAdded_WhenSettingTextWithCharSequence_ShouldNotifyTextWatcher() {
+ MockTextWatcher mockTextWatcher = new MockTextWatcher();
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.setText("text");
+
+ assertEachTextWatcherEventWasInvoked(mockTextWatcher);
+ }
+
+ @Test
+ public void givenATextViewWithMultipleTextWatchersAdded_WhenSettingText_ShouldNotifyEachTextWatcher() {
+ List<MockTextWatcher> mockTextWatchers = anyNumberOfTextWatchers();
+ for (MockTextWatcher textWatcher : mockTextWatchers) {
+ textView.addTextChangedListener(textWatcher);
}
-
- textView.setText("text");
-
- for (MockTextWatcher textWatcher : mockTextWatchers) {
- assertEachTextWatcherEventWasInvoked(textWatcher);
+
+ textView.setText("text");
+
+ for (MockTextWatcher textWatcher : mockTextWatchers) {
+ assertEachTextWatcherEventWasInvoked(textWatcher);
}
- }
-
+ }
+
@Test
- public void whenSettingText_ShouldFireBeforeTextChangedWithCorrectArguments() {
- textView.setText(INITIAL_TEXT);
- TextWatcher mockTextWatcher = mock(TextWatcher.class);
- textView.addTextChangedListener(mockTextWatcher);
-
- textView.setText(NEW_TEXT);
-
- verify(mockTextWatcher).beforeTextChanged(INITIAL_TEXT, 0, INITIAL_TEXT.length(), NEW_TEXT.length());
- }
-
+ public void whenSettingText_ShouldFireBeforeTextChangedWithCorrectArguments() {
+ textView.setText(INITIAL_TEXT);
+ TextWatcher mockTextWatcher = mock(TextWatcher.class);
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.setText(NEW_TEXT);
+
+ verify(mockTextWatcher).beforeTextChanged(INITIAL_TEXT, 0, INITIAL_TEXT.length(), NEW_TEXT.length());
+ }
+
@Test
- public void whenSettingText_ShouldFireOnTextChangedWithCorrectArguments() {
- textView.setText(INITIAL_TEXT);
- TextWatcher mockTextWatcher = mock(TextWatcher.class);
- textView.addTextChangedListener(mockTextWatcher);
-
- textView.setText(NEW_TEXT);
-
- verify(mockTextWatcher).onTextChanged(NEW_TEXT, 0, INITIAL_TEXT.length(), NEW_TEXT.length());
- }
-
+ public void whenSettingText_ShouldFireOnTextChangedWithCorrectArguments() {
+ textView.setText(INITIAL_TEXT);
+ TextWatcher mockTextWatcher = mock(TextWatcher.class);
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.setText(NEW_TEXT);
+
+ verify(mockTextWatcher).onTextChanged(NEW_TEXT, 0, INITIAL_TEXT.length(), NEW_TEXT.length());
+ }
+
@Test
- public void whenSettingText_ShouldFireAfterTextChangedWithCorrectArgument() {
- MockTextWatcher mockTextWatcher = new MockTextWatcher();
- textView.addTextChangedListener(mockTextWatcher);
-
- textView.setText(NEW_TEXT);
-
- assertThat(mockTextWatcher.afterTextChangeArgument.toString(), equalTo(NEW_TEXT));
- }
-
+ public void whenSettingText_ShouldFireAfterTextChangedWithCorrectArgument() {
+ MockTextWatcher mockTextWatcher = new MockTextWatcher();
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.setText(NEW_TEXT);
+
+ assertThat(mockTextWatcher.afterTextChangeArgument.toString(), equalTo(NEW_TEXT));
+ }
+
@Test
public void whenAppendingText_ShouldAppendNewTextAfterOldOne() {
- textView.setText(INITIAL_TEXT);
- textView.append(NEW_TEXT);
-
- assertEquals(INITIAL_TEXT + NEW_TEXT, textView.getText());
+ textView.setText(INITIAL_TEXT);
+ textView.append(NEW_TEXT);
+
+ assertEquals(INITIAL_TEXT + NEW_TEXT, textView.getText());
}
-
+
@Test
public void whenAppendingText_ShouldFireBeforeTextChangedWithCorrectArguments() {
- textView.setText(INITIAL_TEXT);
- TextWatcher mockTextWatcher = mock(TextWatcher.class);
- textView.addTextChangedListener(mockTextWatcher);
-
- textView.append(NEW_TEXT);
-
- verify(mockTextWatcher).beforeTextChanged(INITIAL_TEXT, 0, INITIAL_TEXT.length(), INITIAL_TEXT.length() + NEW_TEXT.length());
+ textView.setText(INITIAL_TEXT);
+ TextWatcher mockTextWatcher = mock(TextWatcher.class);
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.append(NEW_TEXT);
+
+ verify(mockTextWatcher).beforeTextChanged(INITIAL_TEXT, 0, INITIAL_TEXT.length(), INITIAL_TEXT.length() + NEW_TEXT.length());
}
-
+
@Test
public void whenAppendingText_ShouldFireOnTextChangedWithCorrectArguments() {
- textView.setText(INITIAL_TEXT);
- TextWatcher mockTextWatcher = mock(TextWatcher.class);
- textView.addTextChangedListener(mockTextWatcher);
-
- textView.append(NEW_TEXT);
-
- verify(mockTextWatcher).onTextChanged(INITIAL_TEXT + NEW_TEXT, 0, INITIAL_TEXT.length(), INITIAL_TEXT.length() + NEW_TEXT.length());
+ textView.setText(INITIAL_TEXT);
+ TextWatcher mockTextWatcher = mock(TextWatcher.class);
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.append(NEW_TEXT);
+
+ verify(mockTextWatcher).onTextChanged(INITIAL_TEXT + NEW_TEXT, 0, INITIAL_TEXT.length(), INITIAL_TEXT.length() + NEW_TEXT.length());
}
-
+
@Test
public void whenAppendingText_ShouldFireAfterTextChangedWithCorrectArgument() {
- textView.setText(INITIAL_TEXT);
- MockTextWatcher mockTextWatcher = new MockTextWatcher();
- textView.addTextChangedListener(mockTextWatcher);
-
- textView.append(NEW_TEXT);
-
- assertThat(mockTextWatcher.afterTextChangeArgument.toString(), equalTo(INITIAL_TEXT + NEW_TEXT));
+ textView.setText(INITIAL_TEXT);
+ MockTextWatcher mockTextWatcher = new MockTextWatcher();
+ textView.addTextChangedListener(mockTextWatcher);
+
+ textView.append(NEW_TEXT);
+
+ assertThat(mockTextWatcher.afterTextChangeArgument.toString(), equalTo(INITIAL_TEXT + NEW_TEXT));
}
@Test
@@ -279,24 +279,47 @@
assertThat(textView.getPaint().measureText("12345"), equalTo(5f));
}
+ @Test
+ public void append_whenSelectionIsAtTheEnd_shouldKeepSelectionAtTheEnd() throws Exception {
+ textView.setText("1");
+ shadowOf(textView).setSelection(0, 0);
+ textView.append("2");
+ assertEquals(0, textView.getSelectionEnd());
+ assertEquals(0, textView.getSelectionStart());
+
+ shadowOf(textView).setSelection(2, 2);
+ textView.append("3");
+ assertEquals(3, textView.getSelectionEnd());
+ assertEquals(3, textView.getSelectionStart());
+ }
+
+ @Test
+ public void append_whenSelectionReachesToEnd_shouldExtendSelectionToTheEnd() throws Exception {
+ textView.setText("12");
+ shadowOf(textView).setSelection(0, 2);
+ textView.append("3");
+ assertEquals(3, textView.getSelectionEnd());
+ assertEquals(0, textView.getSelectionStart());
+ }
+
private List<MockTextWatcher> anyNumberOfTextWatchers() {
- List<MockTextWatcher> mockTextWatchers = new ArrayList<MockTextWatcher>();
- int numberBetweenOneAndTen = new Random().nextInt(10) + 1;
- for (int i = 0; i < numberBetweenOneAndTen; i++) {
- mockTextWatchers.add(new MockTextWatcher());
+ List<MockTextWatcher> mockTextWatchers = new ArrayList<MockTextWatcher>();
+ int numberBetweenOneAndTen = new Random().nextInt(10) + 1;
+ for (int i = 0; i < numberBetweenOneAndTen; i++) {
+ mockTextWatchers.add(new MockTextWatcher());
}
- return mockTextWatchers;
- }
+ return mockTextWatchers;
+ }
- private void assertEachTextWatcherEventWasInvoked(MockTextWatcher mockTextWatcher) {
- assertTrue("Expected each TextWatcher event to have been invoked once", mockTextWatcher.methodsCalled.size() == 3);
-
- assertThat(mockTextWatcher.methodsCalled.get(0), equalTo("beforeTextChanged"));
- assertThat(mockTextWatcher.methodsCalled.get(1), equalTo("onTextChanged"));
- assertThat(mockTextWatcher.methodsCalled.get(2), equalTo("afterTextChanged"));
- }
+ private void assertEachTextWatcherEventWasInvoked(MockTextWatcher mockTextWatcher) {
+ assertTrue("Expected each TextWatcher event to have been invoked once", mockTextWatcher.methodsCalled.size() == 3);
- private List<String> urlStringsFrom(URLSpan[] urlSpans) {
+ assertThat(mockTextWatcher.methodsCalled.get(0), equalTo("beforeTextChanged"));
+ assertThat(mockTextWatcher.methodsCalled.get(1), equalTo("onTextChanged"));
+ assertThat(mockTextWatcher.methodsCalled.get(2), equalTo("afterTextChanged"));
+ }
+
+ private List<String> urlStringsFrom(URLSpan[] urlSpans) {
List<String> urls = new ArrayList<String>();
for (URLSpan urlSpan : urlSpans) {
urls.add(urlSpan.getURL());
@@ -315,27 +338,27 @@
return false;
}
}
-
+
private static class MockTextWatcher implements TextWatcher {
- List<String> methodsCalled = new ArrayList<String>();
- Editable afterTextChangeArgument;
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- methodsCalled.add("beforeTextChanged");
- }
+ List<String> methodsCalled = new ArrayList<String>();
+ Editable afterTextChangeArgument;
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- methodsCalled.add("onTextChanged");
- }
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ methodsCalled.add("beforeTextChanged");
+ }
- @Override
- public void afterTextChanged(Editable s) {
- methodsCalled.add("afterTextChanged");
- afterTextChangeArgument = s;
- }
-
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ methodsCalled.add("onTextChanged");
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ methodsCalled.add("afterTextChanged");
+ afterTextChangeArgument = s;
+ }
+
}
}