Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      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 
     17 package android.text.method.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertNotNull;
     22 import static org.junit.Assert.assertNull;
     23 import static org.junit.Assert.assertSame;
     24 import static org.junit.Assert.assertTrue;
     25 
     26 import android.app.Activity;
     27 import android.app.Instrumentation;
     28 import android.os.SystemClock;
     29 import android.support.test.InstrumentationRegistry;
     30 import android.support.test.annotation.UiThreadTest;
     31 import android.support.test.filters.MediumTest;
     32 import android.support.test.rule.ActivityTestRule;
     33 import android.support.test.runner.AndroidJUnit4;
     34 import android.text.Editable;
     35 import android.text.Selection;
     36 import android.text.Spannable;
     37 import android.text.SpannableString;
     38 import android.text.method.ArrowKeyMovementMethod;
     39 import android.text.method.MetaKeyKeyListener;
     40 import android.text.method.MovementMethod;
     41 import android.util.TypedValue;
     42 import android.view.KeyEvent;
     43 import android.view.MotionEvent;
     44 import android.view.View;
     45 import android.view.WindowManager;
     46 import android.widget.TextView;
     47 import android.widget.TextView.BufferType;
     48 
     49 import com.android.compatibility.common.util.PollingCheck;
     50 
     51 import org.junit.Before;
     52 import org.junit.Rule;
     53 import org.junit.Test;
     54 import org.junit.runner.RunWith;
     55 
     56 /**
     57  * Test {@link ArrowKeyMovementMethod}. The class is an implementation of interface
     58  * {@link MovementMethod}. The typical usage of {@link MovementMethod} is tested in
     59  * {@link android.widget.cts.TextViewTest} and this test case is only focused on the
     60  * implementation of the methods.
     61  *
     62  * @see android.widget.cts.TextViewTest
     63  */
     64 @MediumTest
     65 @RunWith(AndroidJUnit4.class)
     66 public class ArrowKeyMovementMethodTest {
     67     private static final String THREE_LINES_TEXT = "first line\nsecond line\nlast line";
     68     private static final int END_OF_ALL_TEXT = THREE_LINES_TEXT.length();
     69     private static final int END_OF_1ST_LINE = THREE_LINES_TEXT.indexOf('\n');
     70     private static final int START_OF_2ND_LINE = END_OF_1ST_LINE + 1;
     71     private static final int END_OF_2ND_LINE = THREE_LINES_TEXT.indexOf('\n', START_OF_2ND_LINE);
     72     private static final int START_OF_3RD_LINE = END_OF_2ND_LINE + 1;
     73     private static final int SPACE_IN_2ND_LINE = THREE_LINES_TEXT.indexOf(' ', START_OF_2ND_LINE);
     74 
     75     private Instrumentation mInstrumentation;
     76     private TextView mTextView;
     77     private ArrowKeyMovementMethod mArrowKeyMovementMethod;
     78     private Editable mEditable;
     79     private MyMetaKeyKeyListener mMetaListener;
     80 
     81     @Rule
     82     public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class);
     83 
     84     @Before
     85     public void setup() throws Throwable {
     86         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     87         mMetaListener = new MyMetaKeyKeyListener();
     88         mArrowKeyMovementMethod = new ArrowKeyMovementMethod();
     89 
     90         mActivityRule.runOnUiThread(() -> {;
     91             initTextViewWithNullLayout();
     92 
     93             Activity activity = mActivityRule.getActivity();
     94             activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
     95                     WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
     96 
     97             activity.setContentView(mTextView);
     98             mTextView.setFocusable(true);
     99             mTextView.requestFocus();
    100         });
    101         PollingCheck.waitFor(() -> mTextView.isFocused() && (mTextView.getLayout() != null));
    102     }
    103 
    104     @Test
    105     public void testConstructor() {
    106         new ArrowKeyMovementMethod();
    107     }
    108 
    109     @Test
    110     public void testCanSelectArbitrarily() {
    111         assertTrue(new ArrowKeyMovementMethod().canSelectArbitrarily());
    112     }
    113 
    114     @Test
    115     public void testGetInstance() {
    116         MovementMethod method0 = ArrowKeyMovementMethod.getInstance();
    117         assertNotNull(method0);
    118 
    119         MovementMethod method1 = ArrowKeyMovementMethod.getInstance();
    120         assertNotNull(method1);
    121         assertSame(method0, method1);
    122     }
    123 
    124     @Test
    125     public void testOnTakeFocus() throws Throwable {
    126         /*
    127          * The following assertions depend on whether the TextView has a layout.
    128          * The text view will not get layout in setContent method but in other
    129          * handler's function. Assertion which is following the setContent will
    130          * not get the expecting result. It have to wait all the handlers'
    131          * operations on the UiTread to finish. So all these cases are divided
    132          * into several steps, setting the content at first, waiting the layout,
    133          * and checking the assertion at last.
    134          */
    135         verifySelection(-1);
    136         mActivityRule.runOnUiThread(() -> {
    137             Selection.removeSelection(mEditable);
    138             mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
    139         });
    140         mInstrumentation.waitForIdleSync();
    141         verifySelection(END_OF_ALL_TEXT);
    142 
    143         mActivityRule.runOnUiThread(() -> {
    144             Selection.removeSelection(mEditable);
    145             mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
    146         });
    147         mInstrumentation.waitForIdleSync();
    148         verifySelection(END_OF_ALL_TEXT);
    149 
    150         mActivityRule.runOnUiThread(mTextView::setSingleLine);
    151         // wait until the textView gets layout
    152         mInstrumentation.waitForIdleSync();
    153         assertNotNull(mTextView.getLayout());
    154         assertEquals(1, mTextView.getLayout().getLineCount());
    155 
    156         mActivityRule.runOnUiThread(() -> {
    157             Selection.removeSelection(mEditable);
    158             mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
    159         });
    160         verifySelection(END_OF_ALL_TEXT);
    161 
    162         mActivityRule.runOnUiThread(() -> {
    163             Selection.removeSelection(mEditable);
    164             mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
    165         });
    166         verifySelection(END_OF_ALL_TEXT);
    167     }
    168 
    169     @UiThreadTest
    170     @Test
    171     public void testOnTakeFocusWithNullLayout() {
    172         initTextViewWithNullLayout();
    173         verifySelectEndOfContent();
    174     }
    175 
    176     @UiThreadTest
    177     @Test(expected=NullPointerException.class)
    178     public void testOnTakeFocusNullView() {
    179         // Should throw NullPointerException when param textView is null
    180         mArrowKeyMovementMethod.onTakeFocus(null, mEditable, View.FOCUS_DOWN);
    181     }
    182 
    183     @UiThreadTest
    184     @Test(expected=NullPointerException.class)
    185     public void testOnTakeFocusNullSpannable() {
    186         initTextViewWithNullLayout();
    187         // Should throw NullPointerException when param spannable is null
    188         mArrowKeyMovementMethod.onTakeFocus(mTextView, null, View.FOCUS_DOWN);
    189     }
    190 
    191     @UiThreadTest
    192     @Test
    193     public void testOnKeyDownWithKeyCodeUp() {
    194         // shift+alt tests
    195         final KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    196                 KeyEvent.KEYCODE_DPAD_UP, 0, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
    197 
    198         // first line
    199         // second |line
    200         // last line
    201         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    202         pressBothShiftAlt();
    203         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    204                 KeyEvent.KEYCODE_DPAD_UP, shiftAltEvent));
    205         // |first line
    206         // second |line
    207         // last line
    208         verifySelection(SPACE_IN_2ND_LINE, 0);
    209 
    210         // shift tests
    211         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP, 0,
    212                 KeyEvent.META_SHIFT_ON);
    213 
    214         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    215         pressShift();
    216         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    217                 KeyEvent.KEYCODE_DPAD_UP, shiftEvent));
    218         // first lin|e
    219         // second |line
    220         // last line
    221         assertEquals(SPACE_IN_2ND_LINE, Selection.getSelectionStart(mEditable));
    222         int correspondingIn1stLine = Selection.getSelectionEnd(mEditable);
    223         assertTrue(correspondingIn1stLine >= 0);
    224         assertTrue(correspondingIn1stLine <= END_OF_1ST_LINE);
    225 
    226         pressShift();
    227         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    228                 KeyEvent.KEYCODE_DPAD_UP, shiftEvent));
    229         // |first line
    230         // second |line
    231         // last line
    232         verifySelection(SPACE_IN_2ND_LINE, 0);
    233 
    234         // alt tests
    235         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP, 0,
    236                 KeyEvent.META_ALT_ON);
    237 
    238         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    239         pressAlt();
    240         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    241                 KeyEvent.KEYCODE_DPAD_UP, altEvent));
    242         // |first line
    243         // second line
    244         // last line
    245         verifySelection(0);
    246 
    247         // no-meta tests
    248         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP,
    249                 0, 0);
    250 
    251         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    252         MetaKeyKeyListener.resetMetaState(mEditable);
    253         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    254                 KeyEvent.KEYCODE_DPAD_UP, noMetaEvent));
    255         // first lin|e
    256         // second line
    257         // last line
    258         verifySelection(correspondingIn1stLine);
    259 
    260         // Move to beginning of first line (behavior changed in L)
    261         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    262                 KeyEvent.KEYCODE_DPAD_UP, noMetaEvent));
    263         // |first line
    264         // second line
    265         // last line
    266         verifySelection(0);
    267 
    268         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    269                 KeyEvent.KEYCODE_DPAD_UP, noMetaEvent));
    270         // first lin|e
    271         // second line
    272         // last line
    273         verifySelection(0);
    274     }
    275 
    276     @UiThreadTest
    277     @Test
    278     public void testOnKeyDownWithKeyCodeDown() {
    279         // shift+alt tests
    280         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    281                 KeyEvent.KEYCODE_DPAD_DOWN, 0, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
    282 
    283         // first line
    284         // second |line
    285         // last line
    286         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    287         pressBothShiftAlt();
    288         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    289                 KeyEvent.KEYCODE_DPAD_DOWN, shiftAltEvent));
    290         // first line
    291         // second |line
    292         // last line|
    293         verifySelection(SPACE_IN_2ND_LINE, END_OF_ALL_TEXT);
    294 
    295         // shift tests
    296         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN,
    297                 0, KeyEvent.META_SHIFT_ON);
    298 
    299         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    300         pressShift();
    301         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    302                 KeyEvent.KEYCODE_DPAD_DOWN, shiftEvent));
    303         // first line
    304         // second |line
    305         // last lin|e
    306         assertEquals(SPACE_IN_2ND_LINE, Selection.getSelectionStart(mEditable));
    307         int correspondingIn3rdLine = Selection.getSelectionEnd(mEditable);
    308         assertTrue(correspondingIn3rdLine >= START_OF_3RD_LINE);
    309         assertTrue(correspondingIn3rdLine <= END_OF_ALL_TEXT);
    310 
    311         pressShift();
    312         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    313                 KeyEvent.KEYCODE_DPAD_DOWN, shiftEvent));
    314         // first line
    315         // second |line
    316         // last line|
    317         verifySelection(SPACE_IN_2ND_LINE, END_OF_ALL_TEXT);
    318 
    319         // alt tests
    320         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, 0,
    321                 KeyEvent.META_ALT_ON);
    322 
    323         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    324         pressAlt();
    325         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    326                 KeyEvent.KEYCODE_DPAD_DOWN, altEvent));
    327         // first line
    328         // second line
    329         // last line|
    330         verifySelection(END_OF_ALL_TEXT);
    331 
    332         // no-meta tests
    333         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN,
    334                 0, 0);
    335 
    336         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    337         MetaKeyKeyListener.resetMetaState(mEditable);
    338         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    339                 KeyEvent.KEYCODE_DPAD_DOWN, noMetaEvent));
    340         // first line
    341         // second line
    342         // last lin|e
    343         verifySelection(correspondingIn3rdLine);
    344 
    345         // move to end of last line (behavior changed in L)
    346         Selection.setSelection(mEditable, END_OF_ALL_TEXT - 1);
    347         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    348                 KeyEvent.KEYCODE_DPAD_DOWN, noMetaEvent));
    349         // first line
    350         // second line
    351         // last line|
    352         verifySelection(END_OF_ALL_TEXT);
    353 
    354         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    355                 KeyEvent.KEYCODE_DPAD_DOWN, noMetaEvent));
    356         // first line
    357         // second line
    358         // last line|
    359         verifySelection(END_OF_ALL_TEXT);
    360     }
    361 
    362     @UiThreadTest
    363     @Test
    364     public void testOnKeyDownWithKeyCodeLeft() {
    365         // shift+alt tests
    366         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    367                 KeyEvent.KEYCODE_DPAD_LEFT, 0, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
    368 
    369         // first line
    370         // second |line
    371         // last line
    372         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    373         pressBothShiftAlt();
    374         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    375                 KeyEvent.KEYCODE_DPAD_LEFT, shiftAltEvent));
    376         // first line
    377         // |second |line
    378         // last line
    379         verifySelection(SPACE_IN_2ND_LINE, START_OF_2ND_LINE);
    380 
    381         pressBothShiftAlt();
    382         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    383                 KeyEvent.KEYCODE_DPAD_LEFT, shiftAltEvent));
    384         // first line
    385         // |second |line
    386         // last line
    387         verifySelection(SPACE_IN_2ND_LINE, START_OF_2ND_LINE);
    388 
    389         // shift tests
    390         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
    391                 0, KeyEvent.META_SHIFT_ON);
    392 
    393         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    394         pressShift();
    395         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    396                 KeyEvent.KEYCODE_DPAD_LEFT, shiftEvent));
    397         // first line
    398         // second| |line
    399         // last line
    400         verifySelection(SPACE_IN_2ND_LINE, SPACE_IN_2ND_LINE - 1);
    401 
    402         pressShift();
    403         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    404                 KeyEvent.KEYCODE_DPAD_LEFT, shiftEvent));
    405         // first line
    406         // secon|d |line
    407         // last line
    408         verifySelection(SPACE_IN_2ND_LINE, SPACE_IN_2ND_LINE - 2);
    409 
    410         // alt tests
    411         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0,
    412                 KeyEvent.META_ALT_ON);
    413 
    414         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    415         pressAlt();
    416         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    417                 KeyEvent.KEYCODE_DPAD_LEFT, altEvent));
    418         // first line
    419         // |second line
    420         // last line
    421         verifySelection(START_OF_2ND_LINE);
    422 
    423         pressAlt();
    424         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    425                 KeyEvent.KEYCODE_DPAD_LEFT, altEvent));
    426         // first line
    427         // |second line
    428         // last line
    429         verifySelection(START_OF_2ND_LINE);
    430 
    431         // no-meta tests
    432         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT,
    433                 0, 0);
    434 
    435         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    436         MetaKeyKeyListener.resetMetaState(mEditable);
    437         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    438                 KeyEvent.KEYCODE_DPAD_LEFT, noMetaEvent));
    439         // first line
    440         // second| line
    441         // last line
    442         verifySelection(SPACE_IN_2ND_LINE - 1);
    443 
    444         Selection.setSelection(mEditable, START_OF_2ND_LINE);
    445         // first line
    446         // |second line
    447         // last line
    448         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    449                 KeyEvent.KEYCODE_DPAD_LEFT, noMetaEvent));
    450         // first line|
    451         // second line
    452         // last line
    453         verifySelection(END_OF_1ST_LINE);
    454     }
    455 
    456     @UiThreadTest
    457     @Test
    458     public void testOnKeyDownWithKeyCodeRight() {
    459         // shift+alt tests
    460         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    461                 KeyEvent.KEYCODE_DPAD_RIGHT, 0, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
    462 
    463         // first line
    464         // second |line
    465         // last line
    466         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    467         pressBothShiftAlt();
    468         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    469                 KeyEvent.KEYCODE_DPAD_RIGHT, shiftAltEvent));
    470         // first line
    471         // second |line|
    472         // last line
    473         verifySelection(SPACE_IN_2ND_LINE, END_OF_2ND_LINE);
    474 
    475         pressBothShiftAlt();
    476         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    477                 KeyEvent.KEYCODE_DPAD_RIGHT, shiftAltEvent));
    478         // first line
    479         // second |line|
    480         // last line
    481         verifySelection(SPACE_IN_2ND_LINE, END_OF_2ND_LINE);
    482 
    483         // shift tests
    484         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
    485                 0, KeyEvent.META_SHIFT_ON);
    486 
    487         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    488         pressShift();
    489         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    490                 KeyEvent.KEYCODE_DPAD_RIGHT, shiftEvent));
    491         // first line
    492         // second |l|ine
    493         // last line
    494         verifySelection(SPACE_IN_2ND_LINE, SPACE_IN_2ND_LINE + 1);
    495 
    496         pressShift();
    497         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    498                 KeyEvent.KEYCODE_DPAD_RIGHT, shiftEvent));
    499         // first line
    500         // second |li|ne
    501         // last line
    502         verifySelection(SPACE_IN_2ND_LINE, SPACE_IN_2ND_LINE + 2);
    503 
    504         // alt tests
    505         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT,
    506                 0, KeyEvent.META_ALT_ON);
    507 
    508         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    509         pressAlt();
    510         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    511                 KeyEvent.KEYCODE_DPAD_RIGHT, altEvent));
    512         // first line
    513         // second line|
    514         // last line
    515         verifySelection(END_OF_2ND_LINE);
    516 
    517         pressAlt();
    518         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    519                 KeyEvent.KEYCODE_DPAD_RIGHT, altEvent));
    520         // first line
    521         // second line|
    522         // last line
    523         verifySelection(END_OF_2ND_LINE);
    524 
    525         // no-meta tests
    526         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    527                 KeyEvent.KEYCODE_DPAD_RIGHT, 0, 0);
    528 
    529         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    530         MetaKeyKeyListener.resetMetaState(mEditable);
    531         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    532                 KeyEvent.KEYCODE_DPAD_RIGHT, noMetaEvent));
    533         // first line
    534         // second l|ine
    535         // last line
    536         verifySelection(SPACE_IN_2ND_LINE + 1);
    537 
    538         Selection.setSelection(mEditable, END_OF_2ND_LINE);
    539         // first line
    540         // second line|
    541         // last line
    542         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    543                 KeyEvent.KEYCODE_DPAD_RIGHT, noMetaEvent));
    544         // first line
    545         // second line
    546         // |last line
    547         verifySelection(START_OF_3RD_LINE);
    548     }
    549 
    550     @UiThreadTest
    551     @Test
    552     public void testOnKeyDownWithKeyCodePageUp() {
    553         // shift+alt tests
    554         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    555                 KeyEvent.KEYCODE_PAGE_UP, 0, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
    556 
    557         // first line
    558         // second |line
    559         // last line
    560         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    561         pressBothShiftAlt();
    562         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    563                 KeyEvent.KEYCODE_PAGE_UP, shiftAltEvent));
    564         // |first line
    565         // second |line
    566         // last line
    567         verifySelection(SPACE_IN_2ND_LINE, 0);
    568 
    569         // shift tests
    570         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP,
    571                 0, KeyEvent.META_SHIFT_ON);
    572 
    573         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    574         pressShift();
    575         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    576                 KeyEvent.KEYCODE_PAGE_UP, shiftEvent));
    577         // |first line
    578         // second |line
    579         // last line
    580         verifySelection(SPACE_IN_2ND_LINE, 0);
    581 
    582         // alt tests
    583         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP, 0,
    584                 KeyEvent.META_ALT_ON);
    585 
    586         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    587         pressAlt();
    588         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    589                 KeyEvent.KEYCODE_PAGE_UP, altEvent));
    590         // |first line
    591         // second line
    592         // last line
    593         verifySelection(0);
    594 
    595         // no-meta tests
    596         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP,
    597                 0, 0);
    598 
    599         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    600         MetaKeyKeyListener.resetMetaState(mEditable);
    601         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    602                 KeyEvent.KEYCODE_PAGE_UP, noMetaEvent));
    603         // |first line
    604         // second line
    605         // last line
    606         verifySelection(0);
    607     }
    608 
    609     @UiThreadTest
    610     @Test
    611     public void testOnKeyDownWithKeyCodePageDown() {
    612         // shift+alt tests
    613         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    614                 KeyEvent.KEYCODE_PAGE_DOWN, 0, KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON);
    615 
    616         // first line
    617         // second |line
    618         // last line
    619         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    620         pressBothShiftAlt();
    621         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    622                 KeyEvent.KEYCODE_PAGE_DOWN, shiftAltEvent));
    623         // first line
    624         // second |line
    625         // last line|
    626         verifySelection(SPACE_IN_2ND_LINE, END_OF_ALL_TEXT);
    627 
    628         // shift tests
    629         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN,
    630                 0, KeyEvent.META_SHIFT_ON);
    631 
    632         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    633         pressShift();
    634         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    635                 KeyEvent.KEYCODE_PAGE_DOWN, shiftEvent));
    636         // first line
    637         // second |line
    638         // last line|
    639         verifySelection(SPACE_IN_2ND_LINE, END_OF_ALL_TEXT);
    640 
    641         // alt tests
    642         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN, 0,
    643                 KeyEvent.META_ALT_ON);
    644 
    645         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    646         pressAlt();
    647         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    648                 KeyEvent.KEYCODE_PAGE_DOWN, altEvent));
    649         // first line
    650         // second line
    651         // last line|
    652         verifySelection(END_OF_ALL_TEXT);
    653 
    654         // no-meta tests
    655         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN,
    656                 0, 0);
    657 
    658         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    659         MetaKeyKeyListener.resetMetaState(mEditable);
    660         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    661                 KeyEvent.KEYCODE_PAGE_DOWN, noMetaEvent));
    662         // first line
    663         // second line
    664         // last line|
    665         verifySelection(END_OF_ALL_TEXT);
    666     }
    667 
    668     @UiThreadTest
    669     @Test
    670     public void testOnKeyDownWithKeyCodeMoveHome() {
    671         // shift+ctrl tests
    672         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    673                 KeyEvent.KEYCODE_MOVE_HOME, 0, KeyEvent.META_SHIFT_ON | KeyEvent.META_CTRL_ON);
    674 
    675         // first line
    676         // second |line
    677         // last line
    678         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    679         pressShift();
    680         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    681                 KeyEvent.KEYCODE_MOVE_HOME, shiftAltEvent));
    682         // |first line
    683         // second |line
    684         // last line
    685         verifySelection(SPACE_IN_2ND_LINE, 0);
    686 
    687         // shift tests
    688         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_HOME,
    689                 0, KeyEvent.META_SHIFT_ON);
    690 
    691         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    692         pressShift();
    693         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    694                 KeyEvent.KEYCODE_MOVE_HOME, shiftEvent));
    695         // first line
    696         // |second |line
    697         // last line
    698         verifySelection(SPACE_IN_2ND_LINE, START_OF_2ND_LINE);
    699 
    700         pressShift();
    701         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    702                 KeyEvent.KEYCODE_MOVE_HOME, shiftEvent));
    703         // first line
    704         // |second |line
    705         // last line
    706         verifySelection(SPACE_IN_2ND_LINE, START_OF_2ND_LINE);
    707 
    708         // ctrl tests
    709         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_HOME, 0,
    710                 KeyEvent.META_CTRL_ON);
    711 
    712         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    713         MetaKeyKeyListener.resetMetaState(mEditable);
    714         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    715                 KeyEvent.KEYCODE_MOVE_HOME, altEvent));
    716         // |first line
    717         // second line
    718         // last line
    719         verifySelection(0);
    720 
    721         // no-meta tests
    722         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_HOME,
    723                 0, 0);
    724 
    725         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    726         MetaKeyKeyListener.resetMetaState(mEditable);
    727         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    728                 KeyEvent.KEYCODE_MOVE_HOME, noMetaEvent));
    729         // first line
    730         // |second line
    731         // last line
    732         verifySelection(START_OF_2ND_LINE);
    733 
    734         MetaKeyKeyListener.resetMetaState(mEditable);
    735         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    736                 KeyEvent.KEYCODE_MOVE_HOME, noMetaEvent));
    737         // first line
    738         // |second line
    739         // last line
    740         verifySelection(START_OF_2ND_LINE);
    741     }
    742 
    743     @UiThreadTest
    744     @Test
    745     public void testOnKeyDownWithKeyCodeMoveEnd() {
    746         // shift+ctrl tests
    747         KeyEvent shiftAltEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
    748                 KeyEvent.KEYCODE_MOVE_END, 0, KeyEvent.META_SHIFT_ON | KeyEvent.META_CTRL_ON);
    749 
    750         // first line
    751         // second |line
    752         // last line
    753         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    754         pressShift();
    755         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    756                 KeyEvent.KEYCODE_MOVE_END, shiftAltEvent));
    757         // first line
    758         // second |line
    759         // last line|
    760         verifySelection(SPACE_IN_2ND_LINE, END_OF_ALL_TEXT);
    761 
    762         // shift tests
    763         KeyEvent shiftEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_END,
    764                 0, KeyEvent.META_SHIFT_ON);
    765 
    766         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    767         pressShift();
    768         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    769                 KeyEvent.KEYCODE_MOVE_END, shiftEvent));
    770         // first line
    771         // second |line|
    772         // last line
    773         verifySelection(SPACE_IN_2ND_LINE, END_OF_2ND_LINE);
    774 
    775         pressShift();
    776         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    777                 KeyEvent.KEYCODE_MOVE_END, shiftEvent));
    778         // first line
    779         // second |line|
    780         // last line
    781         verifySelection(SPACE_IN_2ND_LINE, END_OF_2ND_LINE);
    782 
    783         // ctrl tests
    784         KeyEvent altEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_END, 0,
    785                 KeyEvent.META_CTRL_ON);
    786 
    787         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    788         MetaKeyKeyListener.resetMetaState(mEditable);
    789         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    790                 KeyEvent.KEYCODE_MOVE_END, altEvent));
    791         // first line
    792         // second line
    793         // last line|
    794         verifySelection(END_OF_ALL_TEXT);
    795 
    796         // no-meta tests
    797         KeyEvent noMetaEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_END,
    798                 0, 0);
    799 
    800         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    801         MetaKeyKeyListener.resetMetaState(mEditable);
    802         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    803                 KeyEvent.KEYCODE_MOVE_END, noMetaEvent));
    804         // first line
    805         // second line|
    806         // last line
    807         verifySelection(END_OF_2ND_LINE);
    808 
    809         MetaKeyKeyListener.resetMetaState(mEditable);
    810         assertTrue(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    811                 KeyEvent.KEYCODE_MOVE_END, noMetaEvent));
    812         // first line
    813         // second line|
    814         // last line
    815         verifySelection(END_OF_2ND_LINE);
    816     }
    817 
    818     @UiThreadTest
    819     @Test(expected=NullPointerException.class)
    820     public void testOnKeyDownWithNullLayout() {
    821         initTextViewWithNullLayout();
    822         // Should throw NullPointerException when layout of the view is null
    823         mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable, KeyEvent.KEYCODE_DPAD_RIGHT, null);
    824     }
    825 
    826     @UiThreadTest
    827     @Test
    828     public void testOnKeyOther() {
    829         // first line
    830         // second |line
    831         // last line
    832         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    833 
    834         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    835                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_CENTER, 2)));
    836         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    837                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_0, 2)));
    838         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    839                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_E, 2)));
    840         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    841                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_UNKNOWN, 2)));
    842 
    843         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    844                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP, 0)));
    845         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    846                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN, 0)));
    847         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    848                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, 0)));
    849         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    850                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, 0)));
    851         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    852                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_UP, 0)));
    853         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    854                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_PAGE_DOWN, 0)));
    855         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    856                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_HOME, 0)));
    857         assertFalse(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    858                 new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MOVE_END, 0)));
    859 
    860         // only repeat arrow key, page up/down and move home/end events get handled
    861         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    862                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_UP, 2)));
    863         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    864                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_DOWN, 2)));
    865         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    866                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_LEFT, 2)));
    867         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    868                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_DPAD_RIGHT, 2)));
    869         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    870                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_PAGE_UP, 2)));
    871         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    872                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_PAGE_DOWN, 2)));
    873         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    874                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_MOVE_HOME, 2)));
    875         assertTrue(mArrowKeyMovementMethod.onKeyOther(mTextView, mEditable,
    876                 new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_MOVE_END, 2)));
    877     }
    878 
    879     @UiThreadTest
    880     @Test
    881     public void testOnKeyDownWithOtherKeyCode() {
    882         // first line
    883         // second |line
    884         // last line
    885         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    886 
    887         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    888                 KeyEvent.KEYCODE_DPAD_CENTER, new KeyEvent(KeyEvent.ACTION_DOWN,
    889                         KeyEvent.KEYCODE_DPAD_CENTER)));
    890         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    891                 KeyEvent.KEYCODE_0, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0)));
    892         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    893                 KeyEvent.KEYCODE_E, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_E)));
    894         assertFalse(mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable,
    895                 KeyEvent.KEYCODE_UNKNOWN, new KeyEvent(KeyEvent.ACTION_DOWN,
    896                         KeyEvent.KEYCODE_UNKNOWN)));
    897     }
    898 
    899     @UiThreadTest
    900     @Test
    901     public void testOnTouchEvent() throws Throwable {
    902         long now = SystemClock.currentThreadTimeMillis();
    903         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    904         assertFalse(mArrowKeyMovementMethod.onTouchEvent(mTextView, mEditable,
    905                 MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 1, 1, 0)));
    906         verifySelection(SPACE_IN_2ND_LINE);
    907 
    908         assertFalse(mArrowKeyMovementMethod.onTouchEvent(mTextView, mEditable,
    909                 MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 1, 1,
    910                         KeyEvent.META_SHIFT_ON)));
    911         verifySelection(SPACE_IN_2ND_LINE);
    912     }
    913 
    914     @UiThreadTest
    915     @Test
    916     public void testOnTouchEventWithNullLayout() {
    917         initTextViewWithNullLayout();
    918         mTextView.setFocusable(true);
    919         mTextView.requestFocus();
    920         assertTrue(mTextView.isFocused());
    921 
    922         long now = SystemClock.currentThreadTimeMillis();
    923         assertFalse(mArrowKeyMovementMethod.onTouchEvent(mTextView, mEditable,
    924                     MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 1, 1, 0)));
    925     }
    926 
    927     @UiThreadTest
    928     @Test
    929     public void testOnTouchEventWithoutFocus() {
    930         long now = SystemClock.currentThreadTimeMillis();
    931         Selection.setSelection(mEditable, SPACE_IN_2ND_LINE);
    932         assertFalse(mArrowKeyMovementMethod.onTouchEvent(mTextView, mEditable,
    933                 MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 1, 1, 0)));
    934         verifySelection(SPACE_IN_2ND_LINE);
    935     }
    936 
    937     @UiThreadTest
    938     @Test(expected=NullPointerException.class)
    939     public void testOnTouchEventNullView() {
    940         // Should throw NullPointerException when param textView is null
    941         mArrowKeyMovementMethod.onTouchEvent(null, mEditable, MotionEvent.obtain(0, 0, 0, 1, 1, 0));
    942     }
    943 
    944     @UiThreadTest
    945     @Test(expected=NullPointerException.class)
    946     public void testOnTouchEventNullSpannable() {
    947         initTextViewWithNullLayout();
    948         // Should throw NullPointerException when param spannable is null
    949         mArrowKeyMovementMethod.onTouchEvent(mTextView, null, MotionEvent.obtain(0, 0, 0, 1, 1, 0));
    950     }
    951 
    952     @UiThreadTest
    953     @Test(expected=NullPointerException.class)
    954     public void testOnTouchEventNullEvent() {
    955         initTextViewWithNullLayout();
    956         // Should throw NullPointerException when param motionEvent is null
    957         mArrowKeyMovementMethod.onTouchEvent(mTextView, mEditable, null);
    958     }
    959 
    960     @Test
    961     public void testInitialize() {
    962         Spannable spannable = new SpannableString("test content");
    963         ArrowKeyMovementMethod method = new ArrowKeyMovementMethod();
    964 
    965         assertEquals(-1, Selection.getSelectionStart(spannable));
    966         assertEquals(-1, Selection.getSelectionEnd(spannable));
    967         method.initialize(null, spannable);
    968         assertEquals(0, Selection.getSelectionStart(spannable));
    969         assertEquals(0, Selection.getSelectionEnd(spannable));
    970 
    971         Selection.setSelection(spannable, 2);
    972         assertEquals(2, Selection.getSelectionStart(spannable));
    973         assertEquals(2, Selection.getSelectionEnd(spannable));
    974         method.initialize(null, spannable);
    975         assertEquals(0, Selection.getSelectionStart(spannable));
    976         assertEquals(0, Selection.getSelectionEnd(spannable));
    977     }
    978 
    979     @Test(expected=NullPointerException.class)
    980     public void testIntializeNullSpannable() {
    981         ArrowKeyMovementMethod method = new ArrowKeyMovementMethod();
    982         // Should throw NullPointerException when param spannable is null
    983         method.initialize(mTextView, null);
    984     }
    985 
    986     @UiThreadTest
    987     @Test
    988     public void testOnTrackballEven() {
    989         assertFalse(mArrowKeyMovementMethod.onTrackballEvent(mTextView, mEditable,
    990                 MotionEvent.obtain(0, 0, 0, 1, 1, 0)));
    991 
    992         initTextViewWithNullLayout();
    993 
    994         assertFalse(mArrowKeyMovementMethod.onTrackballEvent(mTextView, mEditable,
    995                 MotionEvent.obtain(0, 0, 0, 1, 1, 0)));
    996 
    997         assertFalse(mArrowKeyMovementMethod.onTrackballEvent(mTextView, null,
    998                 MotionEvent.obtain(0, 0, 0, 1, 1, 0)));
    999 
   1000         assertFalse(mArrowKeyMovementMethod.onTrackballEvent(mTextView, mEditable, null));
   1001     }
   1002 
   1003     @UiThreadTest
   1004     @Test
   1005     public void testOnKeyUp() {
   1006         ArrowKeyMovementMethod method = new ArrowKeyMovementMethod();
   1007         SpannableString spannable = new SpannableString("Test Content");
   1008         KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
   1009         TextView view = new TextViewNoIme(mActivityRule.getActivity());
   1010         view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
   1011 
   1012         assertFalse(method.onKeyUp(view, spannable, KeyEvent.KEYCODE_0, event));
   1013         assertFalse(method.onKeyUp(null, null, 0, null));
   1014         assertFalse(method.onKeyUp(null, spannable, KeyEvent.KEYCODE_0, event));
   1015         assertFalse(method.onKeyUp(view, null, KeyEvent.KEYCODE_0, event));
   1016         assertFalse(method.onKeyUp(view, spannable, 0, event));
   1017         assertFalse(method.onKeyUp(view, spannable, KeyEvent.KEYCODE_0, null));
   1018     }
   1019 
   1020     private static final String TEXT_WORDS =
   1021             "Lorem ipsum; dolor sit \u00e4met, conse\u0ca0_\u0ca0ctetur?       Adipiscing"
   1022             + ".elit.integ\u00e9r. Etiam    tristique\ntortor nec   ?:?    \n\n"
   1023             + "lectus porta consequ\u00e4t...  LOReM iPSuM";
   1024 
   1025     @UiThreadTest
   1026     @Test
   1027     public void testFollowingWordStartToEnd() {
   1028 
   1029         // NOTE: there seems to be much variation in how word boundaries are
   1030         // navigated; the behaviors asserted here were derived from Google
   1031         // Chrome 10.0.648.133 beta.
   1032 
   1033         initTextViewWithNullLayout(TEXT_WORDS);
   1034 
   1035         // |Lorem ipsum; dolor sit $met,
   1036         Selection.setSelection(mEditable, 0);
   1037         verifySelection(0);
   1038 
   1039         // Lorem| ipsum; dolor sit $met,
   1040         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1041         verifySelection(5);
   1042 
   1043         // Lorem ipsum|; dolor sit $met,
   1044         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1045         verifySelection(11);
   1046 
   1047         // Lorem ipsum; dolor| sit $met,
   1048         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1049         verifySelection(18);
   1050 
   1051         // Lorem ipsum; dolor sit| $met,
   1052         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1053         verifySelection(22);
   1054 
   1055         // $met|, conse$_$ctetur$       Adipiscing.elit.integ$r.
   1056         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1057         verifySelection(27);
   1058 
   1059         // $met, conse$_$ctetur|$       Adipiscing.elit.integ$r.
   1060         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1061         verifySelection(43);
   1062 
   1063         // TODO: enable these two additional word breaks when implemented
   1064 //        // $met, conse$_$ctetur$       Adipiscing|.elit.integ$r.
   1065 //        assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1066 //        verifySelection(61);
   1067 //
   1068 //        // $met, conse$_$ctetur$       Adipiscing.elit|.integ$r.
   1069 //        assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1070 //        verifySelection(66);
   1071 
   1072         // $met, conse$_$ctetur$       Adipiscing.elit.integ$r|.
   1073         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1074         verifySelection(74);
   1075 
   1076         // integ$r. Etiam|    tristique$tortor nec   ?:?    $$lectus porta
   1077         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1078         verifySelection(81);
   1079 
   1080         // integ$r. Etiam    tristique|$tortor nec   ?:?    $$lectus porta
   1081         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1082         verifySelection(94);
   1083 
   1084         // integ$r. Etiam    tristique$tortor| nec   ?:?    $$lectus porta
   1085         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1086         verifySelection(101);
   1087 
   1088         // integ$r. Etiam    tristique$tortor nec|   ?:?    $$lectus porta
   1089         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1090         verifySelection(105);
   1091 
   1092         // integ$r. Etiam    tristique$tortor nec   ?:?    $$lectus| porta
   1093         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1094         verifySelection(123);
   1095 
   1096         // $$lectus porta| consequ$t...  LOReM iPSuM
   1097         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1098         verifySelection(129);
   1099 
   1100         // $$lectus porta consequ$t|...  LOReM iPSuM
   1101         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1102         verifySelection(139);
   1103 
   1104         // $$lectus porta consequ$t...  LOReM| iPSuM
   1105         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1106         verifySelection(149);
   1107 
   1108         // $$lectus porta consequ$t...  LOReM iPSuM|
   1109         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1110         verifySelection(155);
   1111 
   1112         // keep trying to push beyond end, which should fail
   1113         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1114         verifySelection(155);
   1115         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1116         verifySelection(155);
   1117         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1118         verifySelection(155);
   1119 
   1120     }
   1121 
   1122     @UiThreadTest
   1123     @Test
   1124     public void testPrecedingWordEndToStart() {
   1125 
   1126         // NOTE: there seems to be much variation in how word boundaries are
   1127         // navigated; the behaviors asserted here were derived from Google
   1128         // Chrome 10.0.648.133 beta.
   1129 
   1130         initTextViewWithNullLayout(TEXT_WORDS);
   1131 
   1132         // $$lectus porta consequ$t...  LOReM iPSuM|
   1133         Selection.setSelection(mEditable, mEditable.length());
   1134         verifySelection(155);
   1135 
   1136         // $$lectus porta consequ$t...  LOReM |iPSuM
   1137         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1138         verifySelection(150);
   1139 
   1140         // $$lectus porta consequ$t...  |LOReM iPSuM
   1141         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1142         verifySelection(144);
   1143 
   1144         // $$lectus porta |consequ$t...  LOReM iPSuM
   1145         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1146         verifySelection(130);
   1147 
   1148         // $$lectus |porta consequ$t...  LOReM iPSuM
   1149         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1150         verifySelection(124);
   1151 
   1152         // integ$r. Etiam    tristique$tortor nec   ?:?    $$|lectus
   1153         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1154         verifySelection(117);
   1155 
   1156         // integ$r. Etiam    tristique$tortor |nec   ?:?    $$lectus
   1157         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1158         verifySelection(102);
   1159 
   1160         // integ$r. Etiam    tristique$|tortor nec   ?:?    $$lectus
   1161         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1162         verifySelection(95);
   1163 
   1164         // integ$r. Etiam    |tristique$tortor nec   ?:?    $$lectus
   1165         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1166         verifySelection(85);
   1167 
   1168         // integ$r. |Etiam    tristique$tortor nec   ?:?    $$lectus
   1169         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1170         verifySelection(76);
   1171 
   1172         // TODO: enable these two additional word breaks when implemented
   1173 //        // dolor sit $met, conse$_$ctetur$       Adipiscing.elit.|integ$r.
   1174 //        assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1175 //        verifySelection(67);
   1176 //
   1177 //        // dolor sit $met, conse$_$ctetur$       Adipiscing.|elit.integ$r.
   1178 //        assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1179 //        verifySelection(62);
   1180 
   1181         // dolor sit $met, conse$_$ctetur$       |Adipiscing.elit.integ$r.
   1182         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1183         verifySelection(51);
   1184 
   1185         // dolor sit $met, |conse$_$ctetur$       Adipiscing.elit.integ$r.
   1186         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1187         verifySelection(29);
   1188 
   1189         // dolor sit |$met, conse$_$ctetur$       Adipiscing.elit.integ$r.
   1190         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1191         verifySelection(23);
   1192 
   1193         // Lorem ipsum; dolor |sit $met
   1194         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1195         verifySelection(19);
   1196 
   1197         // Lorem ipsum; |dolor sit $met
   1198         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1199         verifySelection(13);
   1200 
   1201         // Lorem |ipsum; dolor sit $met
   1202         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1203         verifySelection(6);
   1204 
   1205         // |Lorem ipsum; dolor sit $met
   1206         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1207         verifySelection(0);
   1208 
   1209         // keep trying to push before beginning, which should fail
   1210         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1211         verifySelection(0);
   1212         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1213         verifySelection(0);
   1214         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1215         verifySelection(0);
   1216 
   1217     }
   1218 
   1219     private static final String TEXT_WORDS_WITH_NUMBERS =
   1220             "Lorem ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4";
   1221 
   1222     @UiThreadTest
   1223     @Test
   1224     public void testFollowingWordStartToEndWithNumbers() {
   1225 
   1226         initTextViewWithNullLayout(TEXT_WORDS_WITH_NUMBERS);
   1227 
   1228         // |Lorem ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4
   1229         Selection.setSelection(mEditable, 0);
   1230         verifySelection(0);
   1231 
   1232         // Lorem| ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4
   1233         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1234         verifySelection(5);
   1235 
   1236         // Lorem ipsum123,456.90|   dolor sit.. 4-0.0=2 ADipiscing4
   1237         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1238         verifySelection(21);
   1239 
   1240         // Lorem ipsum123,456.90   dolor| sit.. 4-0.0=2 ADipiscing4
   1241         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1242         verifySelection(29);
   1243 
   1244         // Lorem ipsum123,456.90   dolor sit|.. 4-0.0=2 ADipiscing4
   1245         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1246         verifySelection(33);
   1247 
   1248         // Lorem ipsum123,456.90   dolor sit.. 4|-0.0=2 ADipiscing4
   1249         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1250         verifySelection(37);
   1251 
   1252         // Lorem ipsum123,456.90   dolor sit.. 4-0.0|=2 ADipiscing4
   1253         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1254         verifySelection(41);
   1255 
   1256         // Lorem ipsum123,456.90   dolor sit.. 4-0.0=2| ADipiscing4
   1257         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1258         verifySelection(43);
   1259 
   1260         // Lorem ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4|
   1261         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1262         verifySelection(55);
   1263 
   1264         // keep trying to push beyond end, which should fail
   1265         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1266         verifySelection(55);
   1267         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1268         verifySelection(55);
   1269         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1270         verifySelection(55);
   1271 
   1272     }
   1273 
   1274     @UiThreadTest
   1275     @Test
   1276     public void testFollowingWordEndToStartWithNumbers() {
   1277 
   1278         initTextViewWithNullLayout(TEXT_WORDS_WITH_NUMBERS);
   1279 
   1280         // Lorem ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4|
   1281         Selection.setSelection(mEditable, mEditable.length());
   1282         verifySelection(55);
   1283 
   1284         // Lorem ipsum123,456.90   dolor sit.. 4-0.0=2 |ADipiscing4
   1285         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1286         verifySelection(44);
   1287 
   1288         // Lorem ipsum123,456.90   dolor sit.. 4-0.0=|2 ADipiscing4
   1289         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1290         verifySelection(42);
   1291 
   1292         // Lorem ipsum123,456.90   dolor sit.. 4-|0.0=2 ADipiscing4
   1293         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1294         verifySelection(38);
   1295 
   1296         // Lorem ipsum123,456.90   dolor sit.. |4-0.0=2 ADipiscing4
   1297         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1298         verifySelection(36);
   1299 
   1300         // Lorem ipsum123,456.90   dolor |sit.. 4-0.0=2 ADipiscing4
   1301         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1302         verifySelection(30);
   1303 
   1304         // Lorem ipsum123,456.90   |dolor sit.. 4-0.0=2 ADipiscing4
   1305         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1306         verifySelection(24);
   1307 
   1308         // Lorem |ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4
   1309         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1310         verifySelection(6);
   1311 
   1312         // |Lorem ipsum123,456.90   dolor sit.. 4-0.0=2 ADipiscing4
   1313         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1314         verifySelection(0);
   1315 
   1316         // keep trying to push before beginning, which should fail
   1317         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1318         verifySelection(0);
   1319         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1320         verifySelection(0);
   1321         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1322         verifySelection(0);
   1323 
   1324     }
   1325 
   1326     private static final String TEXT_WORDS_WITH_1CHAR_FINAL_WORD = "abc d";
   1327 
   1328     @UiThreadTest
   1329     @Test
   1330     public void testFollowingWordStartToEndWithOneCharFinalWord() {
   1331 
   1332         initTextViewWithNullLayout(TEXT_WORDS_WITH_1CHAR_FINAL_WORD);
   1333 
   1334         // |abc d
   1335         Selection.setSelection(mEditable, 0);
   1336         verifySelection(0);
   1337 
   1338         // abc| d
   1339         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1340         verifySelection(3);
   1341 
   1342         // abc d|
   1343         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1344         verifySelection(mEditable.length());
   1345 
   1346     }
   1347 
   1348     @UiThreadTest
   1349     @Test
   1350     public void testFollowingWordEndToStartWithOneCharFinalWord() {
   1351 
   1352         initTextViewWithNullLayout(TEXT_WORDS_WITH_1CHAR_FINAL_WORD);
   1353 
   1354         // abc d|
   1355         Selection.setSelection(mEditable, mEditable.length());
   1356         verifySelection(5);
   1357 
   1358         // abc |d
   1359         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1360         verifySelection(4);
   1361 
   1362         // |abc d
   1363         assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1364         verifySelection(0);
   1365 
   1366     }
   1367 
   1368     @UiThreadTest
   1369     @Test
   1370     public void testMovementFromMiddleOfWord() {
   1371 
   1372         initTextViewWithNullLayout("before word after");
   1373         verifyMoveFromInsideWord(7, 10);
   1374 
   1375         // Surrogate characters: bairkan should be considered as a standard letter
   1376         final String BAIRKAN = "\uD800\uDF31";
   1377 
   1378         initTextViewWithNullLayout("before wo" + BAIRKAN + "rd after");
   1379         verifyMoveFromInsideWord(7, 12);
   1380 
   1381         initTextViewWithNullLayout("before " + BAIRKAN + BAIRKAN + "xx after");
   1382         verifyMoveFromInsideWord(7, 12);
   1383 
   1384         initTextViewWithNullLayout("before xx" + BAIRKAN + BAIRKAN + " after");
   1385         verifyMoveFromInsideWord(7, 12);
   1386 
   1387         initTextViewWithNullLayout("before x" + BAIRKAN + "x" + BAIRKAN + " after");
   1388         verifyMoveFromInsideWord(7, 12);
   1389 
   1390         initTextViewWithNullLayout("before " + BAIRKAN + "x" + BAIRKAN + "x after");
   1391         verifyMoveFromInsideWord(7, 12);
   1392 
   1393         initTextViewWithNullLayout("before " + BAIRKAN + BAIRKAN + BAIRKAN + " after");
   1394         verifyMoveFromInsideWord(7, 12);
   1395     }
   1396 
   1397     private void verifyMoveFromInsideWord(int wordStart, int wordEnd) {
   1398 
   1399         CharSequence text = mTextView.getText();
   1400 
   1401         // Check following always goes at the end of the word
   1402         for (int offset = wordStart; offset != wordEnd + 1; offset++) {
   1403             // Skip positions located between a pair of surrogate characters
   1404             if (Character.isSurrogatePair(text.charAt(offset - 1), text.charAt(offset))) {
   1405                 continue;
   1406             }
   1407             Selection.setSelection(mEditable, offset);
   1408             assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_RIGHT));
   1409             verifySelection(wordEnd + 1);
   1410         }
   1411 
   1412         // Check preceding always goes at the beginning of the word
   1413         for (int offset = wordEnd + 1; offset != wordStart; offset--) {
   1414             if (Character.isSurrogatePair(text.charAt(offset - 1), text.charAt(offset))) {
   1415                 continue;
   1416             }
   1417             Selection.setSelection(mEditable, offset);
   1418             assertTrue(pressCtrlChord(KeyEvent.KEYCODE_DPAD_LEFT));
   1419             verifySelection(wordStart);
   1420         }
   1421     }
   1422 
   1423     private void initTextViewWithNullLayout() {
   1424         initTextViewWithNullLayout(THREE_LINES_TEXT);
   1425     }
   1426 
   1427     private void initTextViewWithNullLayout(CharSequence text) {
   1428         mTextView = new TextViewNoIme(mActivityRule.getActivity());
   1429         mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
   1430         mTextView.setText(text, BufferType.EDITABLE);
   1431         assertNull(mTextView.getLayout());
   1432         mEditable = (Editable) mTextView.getText();
   1433     }
   1434 
   1435     private void pressMetaKey(int metakey, int expectedState) {
   1436         mMetaListener.onKeyDown(null, mEditable, metakey, null);
   1437         assertEquals(1, MetaKeyKeyListener.getMetaState(mEditable, expectedState));
   1438     }
   1439 
   1440     private void pressShift() {
   1441         MetaKeyKeyListener.resetMetaState(mEditable);
   1442         pressMetaKey(KeyEvent.KEYCODE_SHIFT_LEFT, MetaKeyKeyListener.META_SHIFT_ON);
   1443     }
   1444 
   1445     private void pressAlt() {
   1446         MetaKeyKeyListener.resetMetaState(mEditable);
   1447         pressMetaKey(KeyEvent.KEYCODE_ALT_LEFT, MetaKeyKeyListener.META_ALT_ON);
   1448     }
   1449 
   1450     private void pressBothShiftAlt() {
   1451         MetaKeyKeyListener.resetMetaState(mEditable);
   1452         pressMetaKey(KeyEvent.KEYCODE_SHIFT_LEFT, MetaKeyKeyListener.META_SHIFT_ON);
   1453         pressMetaKey(KeyEvent.KEYCODE_ALT_LEFT, MetaKeyKeyListener.META_ALT_ON);
   1454     }
   1455 
   1456     private boolean pressCtrlChord(int keyCode) {
   1457         final long now = System.currentTimeMillis();
   1458         final KeyEvent keyEvent = new KeyEvent(
   1459                 now, now, KeyEvent.ACTION_DOWN, keyCode, 0, KeyEvent.META_CTRL_LEFT_ON);
   1460         return mArrowKeyMovementMethod.onKeyDown(mTextView, mEditable, keyCode, keyEvent);
   1461     }
   1462 
   1463     private void verifySelection(int expectedPosition) {
   1464         verifySelection(expectedPosition, expectedPosition);
   1465     }
   1466 
   1467     private void verifySelection(int expectedStart, int expectedEnd) {
   1468         final int actualStart = Selection.getSelectionStart(mEditable);
   1469         final int actualEnd = Selection.getSelectionEnd(mEditable);
   1470 
   1471         verifyCharSequenceIndexEquals(mEditable, expectedStart, actualStart);
   1472         verifyCharSequenceIndexEquals(mEditable, expectedEnd, actualEnd);
   1473     }
   1474 
   1475     private static void verifyCharSequenceIndexEquals(CharSequence text, int expected, int actual) {
   1476         final String message = "expected <" + getCursorSnippet(text, expected) + "> but was <"
   1477                 + getCursorSnippet(text, actual) + ">";
   1478         assertEquals(message, expected, actual);
   1479     }
   1480 
   1481     private static String getCursorSnippet(CharSequence text, int index) {
   1482         if (index >= 0 && index < text.length()) {
   1483             return text.subSequence(Math.max(0, index - 5), index) + "|"
   1484                     + text.subSequence(index, Math.min(text.length() - 1, index + 5));
   1485         } else {
   1486             return null;
   1487         }
   1488     }
   1489 
   1490     private void verifySelectEndOfContent() {
   1491         Selection.removeSelection(mEditable);
   1492         mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_DOWN);
   1493         verifySelection(END_OF_ALL_TEXT);
   1494 
   1495         Selection.removeSelection(mEditable);
   1496         mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_RIGHT);
   1497         verifySelection(END_OF_ALL_TEXT);
   1498 
   1499         verifySelectEndOfContentExceptFocusForward();
   1500     }
   1501 
   1502     private void verifySelectEndOfContentExceptFocusForward() {
   1503         Selection.removeSelection(mEditable);
   1504         mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_UP);
   1505         verifySelection(END_OF_ALL_TEXT);
   1506 
   1507         Selection.removeSelection(mEditable);
   1508         mArrowKeyMovementMethod.onTakeFocus(mTextView, mEditable, View.FOCUS_LEFT);
   1509         verifySelection(END_OF_ALL_TEXT);
   1510     }
   1511 
   1512     private static class MyMetaKeyKeyListener extends MetaKeyKeyListener {
   1513     }
   1514 }
   1515