19f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su/*
29f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * Copyright (C) 2008 The Android Open Source Project
39f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su *
49f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * Licensed under the Apache License, Version 2.0 (the "License");
59f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * you may not use this file except in compliance with the License.
69f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * You may obtain a copy of the License at
79f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su *
89f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su *      http://www.apache.org/licenses/LICENSE-2.0
99f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su *
109f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * Unless required by applicable law or agreed to in writing, software
119f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * distributed under the License is distributed on an "AS IS" BASIS,
129f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * See the License for the specific language governing permissions and
149f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * limitations under the License.
159f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su */
169f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
179f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Supackage android.text.method.cts;
189f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
199f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
209f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.os.SystemClock;
219f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.test.ActivityInstrumentationTestCase2;
229f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.test.UiThreadTest;
239f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.Selection;
249f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.Spannable;
259f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.SpannableString;
269f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.Spanned;
279f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.method.LinkMovementMethod;
289f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.method.MovementMethod;
299f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.text.style.ClickableSpan;
309f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.view.KeyEvent;
319f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.view.MotionEvent;
329f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.view.View;
339f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.widget.TextView;
349f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Suimport android.widget.TextView.BufferType;
359f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
369f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su/**
379f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * Test {@link LinkMovementMethod}. The class is an implementation of interface
389f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * {@link MovementMethod}. The typical usage of {@link MovementMethod} is tested in
399f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * {@link android.widget.cts.TextViewTest} and this test case is only focused on the
409f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * implementation of the methods.
419f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su *
429f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su * @see android.widget.cts.TextViewTest
439f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su */
449f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Supublic class LinkMovementMethodTest extends
459f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        ActivityInstrumentationTestCase2<StubActivity> {
469f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private static final String CONTENT = "clickable\nunclickable\nclickable";
479f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
489f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private LinkMovementMethod mMethod;
499f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
509f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private TextView mView;
519f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
529f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private Spannable mSpannable;
539f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
549f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private MockClickableSpan mClickable0;
559f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
569f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private MockClickableSpan mClickable1;
579f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
589f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public LinkMovementMethodTest() {
599f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        super("com.android.cts.stub", StubActivity.class);
609f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
619f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
629f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @Override
639f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    protected void setUp() throws Exception {
649f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        super.setUp();
659f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mMethod = new LinkMovementMethod();
669f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
679f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // Set the content view with a text view which contains 3 lines,
689f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mView = new TextView(getActivity());
699f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mView.setText(CONTENT, BufferType.SPANNABLE);
709f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        getInstrumentation().runOnMainSync(new Runnable() {
719f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            public void run() {
729f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                getActivity().setContentView(mView);
739f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            }
749f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        });
759f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        getInstrumentation().waitForIdleSync();
769f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
779f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mSpannable = (Spannable) mView.getText();
789f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // make first line clickable
799f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0 = markClickable(0, CONTENT.indexOf('\n'));
809f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // make last line clickable
819f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1 = markClickable(CONTENT.lastIndexOf('\n'), CONTENT.length());
829f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
839f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
849f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testConstructor() {
859f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        new LinkMovementMethod();
869f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
879f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
889f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testGetInstance() {
899f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        MovementMethod method0 = LinkMovementMethod.getInstance();
909f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method0 instanceof LinkMovementMethod);
919f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
929f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        MovementMethod method1 = LinkMovementMethod.getInstance();
939f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertNotNull(method1);
949f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSame(method0, method1);
959f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
969f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
979f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testOnTakeFocus() {
989f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        LinkMovementMethod method = new LinkMovementMethod();
999f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Spannable spannable = new SpannableString("test sequence");
1009f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(spannable, 0, spannable.length());
1019f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1029f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, 0, spannable.length());
103c59fe85a7cd516caf51deea78daee810a0b4cf90Rasmus Tengblad        assertTrue("Expected at least 2 spans", 2 <= spannable.getSpans(0, spannable.length(), Object.class).length);
1049f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        method.onTakeFocus(null, spannable, View.FOCUS_UP);
1059f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, -1);
1069f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(1, spannable.getSpans(0, spannable.length(), Object.class).length);
1079f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Object span = spannable.getSpans(0, spannable.length(), Object.class)[0];
1089f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(0, spannable.getSpanStart(span));
1099f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(0, spannable.getSpanEnd(span));
1109f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(Spanned.SPAN_POINT_POINT, spannable.getSpanFlags(span));
1119f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1129f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // focus forwards
1139f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(spannable, 0, spannable.length());
1149f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, 0, spannable.length());
115c59fe85a7cd516caf51deea78daee810a0b4cf90Rasmus Tengblad        assertTrue("Expected at least 3 spans", 3 <= spannable.getSpans(0, spannable.length(), Object.class).length);
1169f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        method.onTakeFocus(null, spannable, View.FOCUS_RIGHT);
1179f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, -1);
1189f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(0, spannable.getSpans(0, spannable.length(), Object.class).length);
1199f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1209f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // force adding span while focus backward
1219f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        method.onTakeFocus(null, spannable, View.FOCUS_UP);
1229f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // param direction is unknown(0)
1239f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(spannable, 0, spannable.length());
1249f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, 0, spannable.length());
125c59fe85a7cd516caf51deea78daee810a0b4cf90Rasmus Tengblad        assertTrue("Expected at least 3 spans", 3 <= spannable.getSpans(0, spannable.length(), Object.class).length);
1269f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        method.onTakeFocus(null, spannable, 0);
1279f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, -1);
1289f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(0, spannable.getSpans(0, spannable.length(), Object.class).length);
1299f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1309f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
1319f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
1329f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.onTakeFocus(new TextView(getActivity()), null, View.FOCUS_RIGHT);
1339f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
1349f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
1359f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
1369f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
1379f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
1389f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1399f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
1409f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testOnKeyDown() {
1419f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // no selection
1429f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
1439f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0.reset();
1449f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1.reset();
1459f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_ENTER,
1469f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)));
1479f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable0.hasCalledOnClick());
1489f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable1.hasCalledOnClick());
1499f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1509f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // select clickable0
1519f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(mSpannable, mSpannable.getSpanStart(mClickable0),
1529f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                mSpannable.getSpanEnd(mClickable0));
1539f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0.reset();
1549f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1.reset();
1559f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
1569f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)));
1579f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(mClickable0.hasCalledOnClick());
1589f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable1.hasCalledOnClick());
1599f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1609f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // select unclickable
1619f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(mSpannable, mSpannable.getSpanEnd(mClickable0),
1629f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                mSpannable.getSpanStart(mClickable1));
1639f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0.reset();
1649f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1.reset();
1659f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_ENTER,
1669f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)));
1679f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable0.hasCalledOnClick());
1689f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable1.hasCalledOnClick());
1699f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1709f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // select all clickables(more than one)
1719f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.selectAll(mSpannable);
1729f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0.reset();
1739f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1.reset();
1749f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
1759f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)));
1769f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable0.hasCalledOnClick());
1779f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable1.hasCalledOnClick());
1789f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1799f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // part of selection is clickable
1809f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(mSpannable, mSpannable.getSpanEnd(mClickable0),
1819f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                mSpannable.getSpanEnd(mClickable1));
1829f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0.reset();
1839f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1.reset();
1849f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
1859f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER)));
1869f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable0.hasCalledOnClick());
1879f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(mClickable1.hasCalledOnClick());
1889f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1899f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // selection contains only clickable1 and repeat count of the event is not 0
1909f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(mSpannable, mSpannable.getSpanEnd(mClickable0),
1919f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mSpannable.getSpanEnd(mClickable1));
1929f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        long now = SystemClock.uptimeMillis();
1939f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
1949f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                KeyEvent.KEYCODE_DPAD_CENTER, 1);
1959f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
1969f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable0.reset();
1979f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mClickable1.reset();
1989f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER, event));
1999f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable0.hasCalledOnClick());
2009f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable1.hasCalledOnClick());
2019f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2029f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
2039f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
2049f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mMethod.onKeyDown(null, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER,
2059f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                    new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER));
2069f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param view is null.");
2079f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
2089f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
2099f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
2109f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2119f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
2129f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mMethod.onKeyDown(mView, null, KeyEvent.KEYCODE_DPAD_CENTER,
2139f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                    new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER));
2149f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
2159f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
2169f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
2179f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
2189f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2199f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
2209f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mMethod.onKeyDown(mView, mSpannable, KeyEvent.KEYCODE_DPAD_CENTER, null);
2219f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param keyEvent is null.");
2229f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
2239f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
2249f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
2259f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
2269f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2279f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testOnKeyUp() {
2289f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        LinkMovementMethod method = new LinkMovementMethod();
2299f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // always returns false
2309f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.onKeyUp(null, null, 0, null));
2319f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.onKeyUp(new TextView(getActivity()), null, 0, null));
2329f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.onKeyUp(null, new SpannableString("blahblah"), 0, null));
2339f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.onKeyUp(null, null, KeyEvent.KEYCODE_0,
2349f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0)));
2359f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
2369f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2379f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
2389f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testOnTouchEvent() {
2399f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
2409f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2419f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // press on first line (Clickable)
2429f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(pressOnLine(0));
2439f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable0);
2449f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2459f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // release on first line
2469f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable0.hasCalledOnClick());
2479f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(releaseOnLine(0));
2489f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(mClickable0.hasCalledOnClick());
2499f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2509f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // press on second line (unclickable)
2519f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable0);
2529f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // just clear selection
2539f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        pressOnLine(1);
2549f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
2559f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2569f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // press on last line  (Clickable)
2579f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(pressOnLine(2));
2589f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable1);
2599f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2609f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // release on last line
2619f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(mClickable1.hasCalledOnClick());
2629f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(releaseOnLine(2));
2639f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(mClickable1.hasCalledOnClick());
2649f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2659f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // release on second line (unclickable)
2669f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable1);
2679f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // just clear selection
2689f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        releaseOnLine(1);
2699f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
2709f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2719f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
2729f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        long now = SystemClock.uptimeMillis();
2739f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        int y = (mView.getLayout().getLineTop(1) + mView.getLayout().getLineBottom(1)) / 2;
2749f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
2759f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mMethod.onTouchEvent(null, mSpannable,
2769f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                    MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 5, y, 0));
2779f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param view is null.");
2789f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
2799f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
2809f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
2819f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2829f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
2839f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mMethod.onTouchEvent(mView, null,
2849f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                    MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 5, y, 0));
2859f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
2869f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
2879f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
2889f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
2899f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2909f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
2919f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mMethod.onTouchEvent(mView, mSpannable, null);
2929f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param keyEvent is null.");
2939f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
2949f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
2959f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
2969f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
2979f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
2989f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
2999f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testUp() {
3009f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        final MyLinkMovementMethod method = new MyLinkMovementMethod();
3019f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
3029f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3039f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.up(mView, mSpannable));
3049f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableRightToLeft(mSpannable, mClickable1);
3059f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3069f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.up(mView, mSpannable));
3079f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableRightToLeft(mSpannable, mClickable0);
3089f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3099f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.up(mView, mSpannable));
3109f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableRightToLeft(mSpannable, mClickable0);
3119f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3129f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
3139f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
3149f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.up(null, mSpannable);
3159f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param view is null.");
3169f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
3179f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
3189f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
3199f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3209f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
3219f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.up(mView, null);
3229f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
3239f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
3249f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
3259f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
3269f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
3279f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3289f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
3299f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testDown() {
3309f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        final MyLinkMovementMethod method = new MyLinkMovementMethod();
3319f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
3329f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3339f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.down(mView, mSpannable));
3349f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable0);
3359f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3369f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.down(mView, mSpannable));
3379f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable1);
3389f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3399f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.down(mView, mSpannable));
3409f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable1);
3419f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3429f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
3439f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
3449f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.down(null, mSpannable);
3459f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param view is null.");
3469f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
3479f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
3489f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
3499f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3509f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
3519f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.down(mView, null);
3529f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
3539f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
3549f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
3559f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
3569f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
3579f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3589f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
3599f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testLeft() {
3609f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        final MyLinkMovementMethod method = new MyLinkMovementMethod();
3619f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
3629f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3639f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.left(mView, mSpannable));
3649f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableRightToLeft(mSpannable, mClickable1);
3659f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3669f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.left(mView, mSpannable));
3679f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableRightToLeft(mSpannable, mClickable0);
3689f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3699f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.left(mView, mSpannable));
3709f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableRightToLeft(mSpannable, mClickable0);
3719f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3729f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
3739f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
3749f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.left(null, mSpannable);
3759f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param view is null.");
3769f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
3779f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
3789f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
3799f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3809f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
3819f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.left(mView, null);
3829f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
3839f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
3849f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
3859f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
3869f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
3879f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3889f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
3899f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testRight() {
3909f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        final MyLinkMovementMethod method = new MyLinkMovementMethod();
3919f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
3929f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3939f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.right(mView, mSpannable));
3949f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable0);
3959f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3969f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertTrue(method.right(mView, mSpannable));
3979f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable1);
3989f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
3999f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.right(mView, mSpannable));
4009f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelectClickableLeftToRight(mSpannable, mClickable1);
4019f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4029f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        // null parameters
4039f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
4049f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.right(null, mSpannable);
4059f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param view is null.");
4069f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
4079f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
4089f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
4099f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4109f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
4119f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.right(mView, null);
4129f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
4139f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
4149f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
4159f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
4169f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4179f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4189f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    @UiThreadTest
4199f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testMoveAroundUnclickable() {
4209f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        final MyLinkMovementMethod method = new MyLinkMovementMethod();
4219f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mSpannable.removeSpan(mClickable0);
4229f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        mSpannable.removeSpan(mClickable1);
4239f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
4249f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4259f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.up(mView, mSpannable));
4269f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
4279f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4289f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.down(mView, mSpannable));
4299f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
4309f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4319f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.left(mView, mSpannable));
4329f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
4339f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4349f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertFalse(method.right(mView, mSpannable));
4359f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(mSpannable, -1);
4369f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4379f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4389f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    public void testInitialize() {
4399f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        LinkMovementMethod method = new LinkMovementMethod();
4409f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Spannable spannable = new SpannableString("test sequence");
4419f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        method.onTakeFocus(null, spannable, View.FOCUS_UP);
4429f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        Selection.setSelection(spannable, 0, spannable.length());
4439f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4449f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, 0, spannable.length());
445c59fe85a7cd516caf51deea78daee810a0b4cf90Rasmus Tengblad        assertTrue("Expected at least 3 spans", 3 <= spannable.getSpans(0, spannable.length(), Object.class).length);
4469f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        method.initialize(null, spannable);
4479f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, -1);
4489f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(0, spannable.getSpans(0, spannable.length(), Object.class).length);
4499f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4509f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        try {
4519f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            method.initialize(mView, null);
4529f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            fail("The method did not throw NullPointerException when param spannable is null.");
4539f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        } catch (NullPointerException e) {
4549f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            // expected
4559f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
4569f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4579f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4589f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private MockClickableSpan markClickable(final int start, final int end) {
4599f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        final MockClickableSpan clickableSpan = new MockClickableSpan();
4609f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        getInstrumentation().runOnMainSync(new Runnable() {
4619f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            public void run() {
4629f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                mSpannable.setSpan(clickableSpan, start, end, Spanned.SPAN_MARK_MARK);
4639f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            }
4649f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        });
4659f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        getInstrumentation().waitForIdleSync();
4669f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        return clickableSpan;
4679f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4689f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4699f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private boolean performMotionOnLine(int line, int action) {
4709f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        int x = (mView.getLayout().getLineStart(line) + mView.getLayout().getLineEnd(line)) / 2;
4719f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        int y = (mView.getLayout().getLineTop(line) + mView.getLayout().getLineBottom(line)) / 2;
4729f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        long now = SystemClock.uptimeMillis();
4739f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4749f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        return mMethod.onTouchEvent(mView, mSpannable,
4759f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                MotionEvent.obtain(now, now, action, x, y, 0));
4769f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4779f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4789f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private boolean pressOnLine(int line) {
4799f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        return performMotionOnLine(line, MotionEvent.ACTION_DOWN);
4809f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4819f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4829f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private boolean releaseOnLine(int line) {
4839f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        return performMotionOnLine(line, MotionEvent.ACTION_UP);
4849f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4859f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4869f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private void assertSelection(Spannable spannable, int start, int end) {
4879f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(start, Selection.getSelectionStart(spannable));
4889f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertEquals(end, Selection.getSelectionEnd(spannable));
4899f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4909f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4919f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private void assertSelection(Spannable spannable, int position) {
4929f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, position, position);
4939f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
4949f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
4959f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private void assertSelectClickableLeftToRight(Spannable spannable,
4969f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            MockClickableSpan clickableSpan) {
4979f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable, spannable.getSpanStart(clickableSpan),
4989f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                spannable.getSpanEnd(clickableSpan));
4999f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
5009f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5019f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private void assertSelectClickableRightToLeft(Spannable spannable,
5029f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            MockClickableSpan clickableSpan) {
5039f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        assertSelection(spannable,  spannable.getSpanEnd(clickableSpan),
5049f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su                spannable.getSpanStart(clickableSpan));
5059f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
5069f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5079f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private static class MyLinkMovementMethod extends LinkMovementMethod {
5089f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        @Override
5099f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        protected boolean down(TextView widget, Spannable buffer) {
5109f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            return super.down(widget, buffer);
5119f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5129f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5139f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        @Override
5149f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        protected boolean left(TextView widget, Spannable buffer) {
5159f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            return super.left(widget, buffer);
5169f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5179f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5189f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        @Override
5199f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        protected boolean right(TextView widget, Spannable buffer) {
5209f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            return super.right(widget, buffer);
5219f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5229f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5239f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        @Override
5249f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        protected boolean up(TextView widget, Spannable buffer) {
5259f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            return super.up(widget, buffer);
5269f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5279f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
5289f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5299f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    private static class MockClickableSpan extends ClickableSpan {
5309f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        private boolean mHasCalledOnClick;
5319f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5329f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        @Override
5339f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        public void onClick(View widget) {
5349f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mHasCalledOnClick = true;
5359f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5369f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5379f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        public boolean hasCalledOnClick() {
5389f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            return mHasCalledOnClick;
5399f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5409f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su
5419f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        public void reset() {
5429f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su            mHasCalledOnClick = false;
5439f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su        }
5449f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su    }
5459f4ffc968cc16769cdb7dd8a47301240a0f5d3fcScott Su}
546