blob: e19dfe84e7398622f2888f2084b62144cf41aea2 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.text.method.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.method.MovementMethod;
import android.text.method.ScrollingMovementMethod;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;
import android.widget.TextView.BufferType;
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Test {@link ScrollingMovementMethod}. The class is an implementation of interface
* {@link MovementMethod}. The typical usage of {@link MovementMethod} is tested in
* {@link android.widget.cts.TextViewTest} and this test case is only focused on the
* implementation of the methods.
*
* @see android.widget.cts.TextViewTest
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class ScrollingMovementMethodTest {
private static final int LITTLE_SPACE = 20;
private static final String THREE_LINES_TEXT = "first line\nsecond line\nlast line";
private Instrumentation mInstrumentation;
private Activity mActivity;
private TextView mTextView;
private Spannable mSpannable;
private int mScaledTouchSlop;
@Rule
public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class);
@UiThreadTest
@Before
public void setup() throws Throwable {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
mTextView = new TextViewNoIme(mActivity);
mTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
mTextView.setText(THREE_LINES_TEXT, BufferType.EDITABLE);
mSpannable = (Spannable) mTextView.getText();
mScaledTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
}
@Test
public void testConstructor() {
new ScrollingMovementMethod();
}
@Test
public void testGetInstance() {
final MovementMethod method0 = ScrollingMovementMethod.getInstance();
assertTrue(method0 instanceof ScrollingMovementMethod);
final MovementMethod method1 = ScrollingMovementMethod.getInstance();
assertSame(method0, method1);
}
@Test
public void testOnTouchEventHorizontalMotion() throws Throwable {
final ScrollingMovementMethod method = new ScrollingMovementMethod();
runActionOnUiThread(() -> {
mTextView.setText("hello world", BufferType.SPANNABLE);
mTextView.setSingleLine();
mSpannable = (Spannable) mTextView.getText();
final int width = WidgetTestUtils.convertDipToPixels(mActivity, LITTLE_SPACE);
mActivity.setContentView(mTextView,
new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
final float rightMost = mTextView.getLayout().getLineRight(0) - mTextView.getWidth()
+ mTextView.getTotalPaddingLeft() + mTextView.getTotalPaddingRight();
final int leftMost = mTextView.getScrollX();
final long now = SystemClock.uptimeMillis();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// press
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
}
}));
final int tinyDist = -(mScaledTouchSlop - 1);
int previousScrollX = mTextView.getScrollX();
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for short distance
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, tinyDist, 0, 0));
}
}));
assertEquals(previousScrollX, mTextView.getScrollX());
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
// release
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, tinyDist, 0, 0));
}
}));
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// press
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
}
}));
final int distFar = -mScaledTouchSlop;
previousScrollX = mTextView.getScrollX();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for enough distance
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, distFar, 0, 0));
}
}));
assertTrue(mTextView.getScrollX() > previousScrollX);
assertTrue(mTextView.getScrollX() < rightMost);
previousScrollX = mTextView.getScrollX();
final int distTooFar = (int) (-rightMost * 10);
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for long distance
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, distTooFar, 0, 0));
}
}));
assertTrue(mTextView.getScrollX() > previousScrollX);
assertEquals(rightMost, mTextView.getScrollX(), 1.0f);
previousScrollX = mTextView.getScrollX();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move back
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, 0, 0, 0));
}
}));
assertTrue(mTextView.getScrollX() < previousScrollX);
assertEquals(leftMost, mTextView.getScrollX());
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// release
mResult = method.onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0, 0, 0));
}
}));
}
@Test
public void testOnTouchEventVerticalMotion() throws Throwable {
final ScrollingMovementMethod method = new ScrollingMovementMethod();
runActionOnUiThread(() -> {
mTextView.setLines(1);
mActivity.setContentView(mTextView,
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
final float bottom = mTextView.getLayout().getHeight() - mTextView.getHeight()
+ mTextView.getTotalPaddingTop() + mTextView.getTotalPaddingBottom();
final int top = mTextView.getScrollY();
final long now = SystemClock.uptimeMillis();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// press
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_DOWN, 0, 0, 0));
}
}));
final int tinyDist = -(mScaledTouchSlop - 1);
int previousScrollY = mTextView.getScrollY();
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for short distance
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_MOVE, 0, tinyDist, 0));
}
}));
assertEquals(previousScrollY, mTextView.getScrollY());
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
// release
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_UP, 0, tinyDist, 0));
}
}));
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// press
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_DOWN, 0, 0, 0));
}
}));
final int distFar = -mScaledTouchSlop;
previousScrollY = mTextView.getScrollY();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for enough distance
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_MOVE, 0, distFar, 0));
}
}));
assertTrue(mTextView.getScrollY() > previousScrollY);
assertTrue(mTextView.getScrollX() < bottom);
previousScrollY = mTextView.getScrollY();
final int distTooFar = (int) (-bottom * 10);
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move for long distance
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_MOVE, 0, distTooFar, 0));
}
}));
assertTrue(mTextView.getScrollY() > previousScrollY);
assertEquals(bottom, mTextView.getScrollY(), 0f);
previousScrollY = mTextView.getScrollY();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// move back
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_MOVE, 0, 0, 0));
}
}));
assertTrue(mTextView.getScrollY() < previousScrollY);
assertEquals(top, mTextView.getScrollX());
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
// release
mResult = method.onTouchEvent(mTextView, mSpannable, MotionEvent.obtain(now, now,
MotionEvent.ACTION_UP, 0, 0, 0));
}
}));
}
@Test
public void testOnTouchEventExceptional() throws Throwable {
runActionOnUiThread(() -> {
final int width = WidgetTestUtils.convertDipToPixels(mActivity, LITTLE_SPACE);
mActivity.setContentView(mTextView,
new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
runActionOnUiThread(() -> {
try {
new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable, null);
} catch (NullPointerException e) {
// NPE is acceptable
}
long now = SystemClock.uptimeMillis();
try {
new ScrollingMovementMethod().onTouchEvent(mTextView, null,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
try {
new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, -10000, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new ScrollingMovementMethod().onTouchEvent(mTextView, null,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, -10000, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new ScrollingMovementMethod().onTouchEvent(null, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, -10000, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new ScrollingMovementMethod().onTouchEvent(mTextView, null,
MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, -10000, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
});
}
@Test
public void testCanSelectArbitrarily() {
assertFalse(new ScrollingMovementMethod().canSelectArbitrarily());
}
@Test
public void testOnKeyDownVerticalMovement() throws Throwable {
runActionOnUiThread(() -> mActivity.setContentView(mTextView));
assertNotNull(mTextView.getLayout());
verifyVisibleLineInTextView(0);
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
runActionOnUiThread(() -> method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_DOWN,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN)));
verifyVisibleLineInTextView(1);
runActionOnUiThread(() -> method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_UP,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP)));
verifyVisibleLineInTextView(0);
}
@Test
public void testOnKeyDownHorizontalMovement() throws Throwable {
runActionOnUiThread(() -> {
mTextView.setText("short");
mTextView.setSingleLine();
final int width = WidgetTestUtils.convertDipToPixels(mActivity, LITTLE_SPACE);
mActivity.setContentView(mTextView,
new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
int previousScrollX = mTextView.getScrollX();
runActionOnUiThread(() -> method.onKeyDown(mTextView, (Spannable) mTextView.getText(),
KeyEvent.KEYCODE_DPAD_RIGHT, new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_DPAD_RIGHT)));
assertTrue(mTextView.getScrollX() > previousScrollX);
previousScrollX = mTextView.getScrollX();
runActionOnUiThread(() -> method.onKeyDown(mTextView, (Spannable) mTextView.getText(),
KeyEvent.KEYCODE_DPAD_LEFT, new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT)));
assertTrue(mTextView.getScrollX() < previousScrollX);
previousScrollX = mTextView.getScrollX();
verifyVisibleLineInTextView(0);
runActionOnUiThread(() -> assertFalse(method.onKeyDown(mTextView, mSpannable, 0,
new KeyEvent(KeyEvent.ACTION_DOWN, 0))));
assertEquals(previousScrollX, mTextView.getScrollX());
verifyVisibleLineInTextView(0);
}
@Test
public void testOnKeyDownExceptions() throws Throwable {
runActionOnUiThread(() -> mActivity.setContentView(mTextView));
assertNotNull(mTextView.getLayout());
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
runActionOnUiThread(() -> {
try {
method.onKeyDown(null, mSpannable, KeyEvent.KEYCODE_DPAD_RIGHT,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
method.onKeyDown(mTextView, null, KeyEvent.KEYCODE_DPAD_RIGHT,
new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP));
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
method.onKeyDown(mTextView, mSpannable, KeyEvent.KEYCODE_DPAD_RIGHT, null);
} catch (NullPointerException e) {
// NPE is acceptable
}
});
}
@Test
public void testVerticalMovement() throws Throwable {
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
runActionOnUiThread(() -> {
mTextView.setLines(1);
mActivity.setContentView(mTextView,
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.down(mTextView, mSpannable);
}
}));
verifyVisibleLineInTextView(1);
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.down(mTextView, mSpannable);
}
}));
verifyVisibleLineInTextView(2);
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.down(mTextView, mSpannable);
}
}));
verifyVisibleLineInTextView(2);
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.up(mTextView, mSpannable);
}
}));
verifyVisibleLineInTextView(1);
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.up(mTextView, mSpannable);
}
}));
verifyVisibleLineInTextView(0);
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.up(mTextView, mSpannable);
}
}));
verifyVisibleLineInTextView(0);
runActionOnUiThread(() -> {
try {
method.up(null, mSpannable);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
method.up(mTextView, null);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
method.down(null, mSpannable);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
method.down(mTextView, null);
} catch (NullPointerException e) {
// NPE is acceptable
}
});
}
@Test
public void testMovementWithNullLayout() {
assertNull(mTextView.getLayout());
try {
new MyScrollingMovementMethod().down(mTextView, mSpannable);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new MyScrollingMovementMethod().up(mTextView, mSpannable);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new MyScrollingMovementMethod().left(mTextView, mSpannable);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
new MyScrollingMovementMethod().right(mTextView, mSpannable);
} catch (NullPointerException e) {
// NPE is acceptable
}
final long now = SystemClock.uptimeMillis();
try {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
new ScrollingMovementMethod().onKeyDown(mTextView, mSpannable,
KeyEvent.KEYCODE_DPAD_RIGHT, event);
} catch (NullPointerException e) {
// NPE is acceptable
}
new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 0, 0, 0));
try {
new ScrollingMovementMethod().onTouchEvent(mTextView, mSpannable,
MotionEvent.obtain(now, now, MotionEvent.ACTION_MOVE, - 10000, 0, 0));
} catch (NullPointerException e) {
// NPE is acceptable
}
}
@Test
public void testInitialize() {
new ScrollingMovementMethod().initialize(null, null);
}
@Test
public void testOnTrackballEvent() {
final long now = SystemClock.uptimeMillis();
final MotionEvent event = MotionEvent.obtain(now, now, 0, 2, -2, 0);
final MyScrollingMovementMethod mockMethod = new MyScrollingMovementMethod();
assertFalse(mockMethod.onTrackballEvent(mTextView, mSpannable, event));
assertFalse(mockMethod.onTrackballEvent(null, mSpannable, event));
assertFalse(mockMethod.onTrackballEvent(mTextView, mSpannable, null));
assertFalse(mockMethod.onTrackballEvent(mTextView, null, event));
}
@UiThreadTest
@Test
public void testOnKeyUp() {
final ScrollingMovementMethod method = new ScrollingMovementMethod();
final SpannableString spannable = new SpannableString("Test Content");
final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0);
final TextView view = new TextViewNoIme(mActivity);
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
assertFalse(method.onKeyUp(view, spannable, KeyEvent.KEYCODE_0, event));
assertFalse(method.onKeyUp(null, null, 0, null));
assertFalse(method.onKeyUp(null, spannable, KeyEvent.KEYCODE_0, event));
assertFalse(method.onKeyUp(view, null, KeyEvent.KEYCODE_0, event));
assertFalse(method.onKeyUp(view, spannable, 0, event));
assertFalse(method.onKeyUp(view, spannable, KeyEvent.KEYCODE_0, null));
}
@Test
public void testOnTakeFocus() throws Throwable {
final ScrollingMovementMethod method = new ScrollingMovementMethod();
// wait until the text view gets layout
assertNull(mTextView.getLayout());
try {
method.onTakeFocus(mTextView, mSpannable, View.FOCUS_BACKWARD);
} catch (NullPointerException e) {
// NPE is acceptable
}
runActionOnUiThread(() -> {
final int height = WidgetTestUtils.convertDipToPixels(mActivity, LITTLE_SPACE);
mActivity.setContentView(mTextView,
new LayoutParams(LayoutParams.MATCH_PARENT,
height));
});
final Layout layout = mTextView.getLayout();
assertNotNull(layout);
int previousScrollY = mTextView.getScrollY();
runActionOnUiThread(() -> method.onTakeFocus(mTextView, mSpannable, View.FOCUS_BACKWARD));
assertTrue(mTextView.getScrollY() >= previousScrollY);
verifyVisibleLineInTextView(2);
previousScrollY = mTextView.getScrollY();
runActionOnUiThread(() -> method.onTakeFocus(mTextView, mSpannable, View.FOCUS_FORWARD));
assertTrue(mTextView.getScrollY() <= previousScrollY);
verifyVisibleLineInTextView(0);
runActionOnUiThread(() -> {
try {
method.onTakeFocus(null, mSpannable, View.FOCUS_FORWARD);
} catch (NullPointerException e) {
// NPE is acceptable
}
try {
method.onTakeFocus(mTextView, null, View.FOCUS_FORWARD);
} catch (NullPointerException e) {
// NPE is acceptable
}
});
}
@Test
public void testHorizontalMovement() throws Throwable {
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
runActionOnUiThread(() -> {
mTextView.setText("short");
mTextView.setSingleLine();
final DisplayMetrics dm = mActivity.getResources().getDisplayMetrics();
final int width = (int) (LITTLE_SPACE * dm.scaledDensity);
mActivity.setContentView(mTextView,
new LayoutParams(width, LayoutParams.WRAP_CONTENT));
});
assertNotNull(mTextView.getLayout());
int previousScrollX = mTextView.getScrollX();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.right(mTextView, mSpannable);
}
}));
assertTrue(mTextView.getScrollX() > previousScrollX);
previousScrollX = mTextView.getScrollX();
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.right(mTextView, mSpannable);
}
}));
assertEquals(previousScrollX, mTextView.getScrollX());
previousScrollX = mTextView.getScrollX();
assertTrue(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.left(mTextView, mSpannable);
}
}));
assertTrue(mTextView.getScrollX() < previousScrollX);
previousScrollX = mTextView.getScrollX();
assertFalse(getActionResult(new ActionRunnerWithResult() {
public void run() {
mResult = method.left(mTextView, mSpannable);
}
}));
assertEquals(previousScrollX, mTextView.getScrollX());
}
@Test
public void testOnKeyOther() throws Throwable {
runActionOnUiThread(() -> mActivity.setContentView(mTextView));
assertNotNull(mTextView.getLayout());
verifyVisibleLineInTextView(0);
final MyScrollingMovementMethod method = new MyScrollingMovementMethod();
runActionOnUiThread(() -> method.onKeyOther(mTextView, null,
new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE,
KeyEvent.KEYCODE_DPAD_DOWN, 2)));
verifyVisibleLineInTextView(1);
runActionOnUiThread(() -> method.onKeyOther(mTextView, null,
new KeyEvent(0, 0, KeyEvent.ACTION_MULTIPLE,
KeyEvent.KEYCODE_DPAD_UP, 2)));
verifyVisibleLineInTextView(0);
}
private void verifyVisibleLineInTextView(int line) {
final Layout layout = mTextView.getLayout();
final int scrollY = mTextView.getScrollY();
final int padding = mTextView.getTotalPaddingTop() + mTextView.getTotalPaddingBottom();
assertTrue(layout.getLineForVertical(scrollY) <= line);
assertTrue(layout.getLineForVertical(scrollY + mTextView.getHeight() - padding) >= line);
}
private boolean getActionResult(ActionRunnerWithResult actionRunner) throws Throwable {
runActionOnUiThread(actionRunner);
return actionRunner.getResult();
}
private void runActionOnUiThread(Runnable actionRunner) throws Throwable {
mActivityRule.runOnUiThread(actionRunner);
mInstrumentation.waitForIdleSync();
}
private static abstract class ActionRunnerWithResult implements Runnable {
protected boolean mResult = false;
public boolean getResult() {
return mResult;
}
}
private static class MyScrollingMovementMethod extends ScrollingMovementMethod {
@Override
protected boolean down(TextView widget, Spannable buffer) {
return super.down(widget, buffer);
}
@Override
protected boolean left(TextView widget, Spannable buffer) {
return super.left(widget, buffer);
}
@Override
protected boolean right(TextView widget, Spannable buffer) {
return super.right(widget, buffer);
}
@Override
protected boolean up(TextView widget, Spannable buffer) {
return super.up(widget, buffer);
}
}
}