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;
+        }
+
     }
 }