1 /* 2 * Copyright (C) 2016 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 android.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 21 import static android.widget.TextView.BufferType.EDITABLE; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertTrue; 26 27 import android.app.Activity; 28 import android.app.Instrumentation; 29 import android.os.SystemClock; 30 import androidx.annotation.NonNull; 31 import android.support.test.InstrumentationRegistry; 32 import android.support.test.filters.MediumTest; 33 import android.support.test.rule.ActivityTestRule; 34 import android.support.test.runner.AndroidJUnit4; 35 import android.text.Spannable; 36 import android.text.method.BaseMovementMethod; 37 import android.view.InputDevice; 38 import android.view.KeyEvent; 39 import android.view.MotionEvent; 40 import android.view.MotionEvent.PointerProperties; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.widget.FrameLayout; 44 import android.widget.TextView; 45 46 import com.android.compatibility.common.util.WidgetTestUtils; 47 48 import org.junit.Before; 49 import org.junit.Rule; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 53 /** 54 * Test {@link BaseMovementMethod}. 55 */ 56 @MediumTest 57 @RunWith(AndroidJUnit4.class) 58 public class BaseMovementMethodTest { 59 private Instrumentation mInstrumentation; 60 private BaseMovementMethod mMovementMethod; 61 private TextView mTextView; 62 63 @Rule 64 public ActivityTestRule<CtsActivity> mActivityRule = new ActivityTestRule<>(CtsActivity.class); 65 66 @Before 67 public void setup() { 68 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 69 mMovementMethod = new BaseMovementMethod(); 70 } 71 72 @Test 73 public void testOnGenericMotionEvent_horizontalScroll() throws Throwable { 74 final String testLine = "some text some text"; 75 final String testString = testLine + "\n" + testLine; 76 77 mActivityRule.runOnUiThread(() -> mTextView = createTextView()); 78 // limit lines for horizontal scroll 79 mTextView.setSingleLine(); 80 mTextView.setText(testString, EDITABLE); 81 82 // limit width for horizontal scroll 83 84 setContentView(mTextView, (int) mTextView.getPaint().measureText(testLine) / 3); 85 // assert the default scroll position 86 assertEquals(0, mTextView.getScrollX()); 87 88 final Spannable text = (Spannable) mTextView.getText(); 89 final double lineSpacing = Math.ceil(mTextView.getPaint().getFontSpacing()); 90 91 // scroll right 92 MotionEvent event = createScrollEvent(1, 0); 93 assertTrue(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 94 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, null); 95 assertEquals(lineSpacing, mTextView.getScrollX(), lineSpacing / 4); 96 event.recycle(); 97 98 // scroll left 99 event = createScrollEvent(-1, 0); 100 assertTrue(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 101 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, null); 102 assertEquals(0, mTextView.getScrollX()); 103 event.recycle(); 104 105 // cannot scroll to left 106 event = createScrollEvent(-1, 0); 107 assertFalse(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 108 event.recycle(); 109 110 // cannot scroll to right 111 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, 112 () -> mTextView.scrollTo((int) mTextView.getLayout().getLineWidth(0), 0)); 113 event = createScrollEvent(1, 0); 114 assertFalse(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 115 event.recycle(); 116 117 // meta shift on 118 // reset scroll 119 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, 120 () -> mTextView.scrollTo(0, 0)); 121 122 // scroll top becomes scroll right 123 event = createScrollEvent(0, 1, KeyEvent.META_SHIFT_ON); 124 assertTrue(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 125 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, null); 126 assertEquals(lineSpacing, mTextView.getScrollX(), lineSpacing / 4); 127 event.recycle(); 128 129 // scroll down becomes scroll left 130 event = createScrollEvent(0, -1, KeyEvent.META_SHIFT_ON); 131 assertTrue(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 132 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, null); 133 assertEquals(0, mTextView.getScrollX()); 134 event.recycle(); 135 } 136 137 @Test 138 public void testOnGenericMotionEvent_verticalScroll() throws Throwable { 139 final String testLine = "some text some text"; 140 final String testString = testLine + "\n" + testLine; 141 142 mActivityRule.runOnUiThread(() -> mTextView = createTextView()); 143 // limit lines for vertical scroll 144 mTextView.setMaxLines(1); 145 mTextView.setText(testString, EDITABLE); 146 setContentView(mTextView, WRAP_CONTENT); 147 // assert the default scroll positions 148 assertEquals(0, mTextView.getScrollY()); 149 150 final Spannable text = (Spannable) mTextView.getText(); 151 final int lineHeight = mTextView.getLineHeight(); 152 153 // scroll down 154 MotionEvent event = createScrollEvent(0, -1); 155 assertTrue(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 156 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, null); 157 assertEquals(lineHeight, mTextView.getScrollY(), lineHeight / 4); 158 event.recycle(); 159 160 // scroll up 161 event = createScrollEvent(0, 1); 162 assertTrue(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 163 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, null); 164 assertEquals(0, mTextView.getScrollY()); 165 event.recycle(); 166 167 // cannot scroll up 168 event = createScrollEvent(0, 1); 169 assertFalse(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 170 event.recycle(); 171 172 // cannot scroll down 173 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mTextView, 174 () -> mTextView.scrollTo(0, mTextView.getLayout().getHeight())); 175 event = createScrollEvent(0, -1); 176 assertFalse(mMovementMethod.onGenericMotionEvent(mTextView, text, event)); 177 event.recycle(); 178 } 179 180 private TextView createTextView() { 181 final TextView textView = new TextViewNoIme(mActivityRule.getActivity()); 182 textView.setFocusable(true); 183 textView.setMovementMethod(mMovementMethod); 184 textView.setTextDirection(View.TEXT_DIRECTION_LTR); 185 return textView; 186 } 187 188 private void setContentView(@NonNull TextView textView, int textWidth) throws Throwable { 189 final Activity activity = mActivityRule.getActivity(); 190 final FrameLayout layout = new FrameLayout(activity); 191 layout.addView(textView, new ViewGroup.LayoutParams(textWidth, WRAP_CONTENT)); 192 193 mActivityRule.runOnUiThread(() -> { 194 activity.setContentView(layout, new ViewGroup.LayoutParams(MATCH_PARENT, 195 MATCH_PARENT)); 196 textView.requestFocus(); 197 }); 198 mInstrumentation.waitForIdleSync(); 199 assertTrue(textView.isFocused()); 200 } 201 202 private static MotionEvent createScrollEvent(int horizontal, int vertical) { 203 return createScrollEvent(horizontal, vertical, 0); 204 } 205 206 private static MotionEvent createScrollEvent(int horizontal, int vertical, int meta) { 207 final PointerProperties[] pointerProperties = new PointerProperties[1]; 208 pointerProperties[0] = new PointerProperties(); 209 pointerProperties[0].id = 0; 210 final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1]; 211 coords[0] = new MotionEvent.PointerCoords(); 212 coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, vertical); 213 coords[0].setAxisValue(MotionEvent.AXIS_HSCROLL, horizontal); 214 final long time = SystemClock.uptimeMillis(); 215 return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1, 216 pointerProperties, coords, meta, 0, 1.0f, 1.0f, 0, 0, 217 InputDevice.SOURCE_CLASS_POINTER, 0); 218 } 219 } 220