1ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka/* 2ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Copyright (C) 2016 The Android Open Source Project 3ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 4ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Licensed under the Apache License, Version 2.0 (the "License"); 5ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * you may not use this file except in compliance with the License. 6ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * You may obtain a copy of the License at 7ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 8ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * http://www.apache.org/licenses/LICENSE-2.0 9ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 10ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Unless required by applicable law or agreed to in writing, software 11ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * distributed under the License is distributed on an "AS IS" BASIS, 12ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * See the License for the specific language governing permissions and 14ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * limitations under the License. 15ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka */ 16ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 17ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakapackage android.text.method; 18ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 19bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport static org.mockito.Matchers.any; 20bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport static org.mockito.Mockito.doNothing; 21bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport static org.mockito.Mockito.mock; 22bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanuimport static org.mockito.Mockito.when; 23bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu 24ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.graphics.Canvas; 25ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.graphics.Paint; 26ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.Editable; 27ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.Spannable; 28ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.SpannableString; 29ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.style.ReplacementSpan; 30ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 31ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport junit.framework.Assert; 32ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 33ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka/** 34ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Represents an editor state. 35ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 36ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * The editor state can be specified by following string format. 37ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Components are separated by space(U+0020). 38ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Single-quoted string for printable ASCII characters, e.g. 'a', '123'. 39ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - U+XXXX form can be used for a Unicode code point. 40ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Components inside '[' and ']' are in selection. 41ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Components inside '(' and ')' are in ReplacementSpan. 42ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - '|' is for specifying cursor position. 43ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 44ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Selection and cursor can not be specified at the same time. 45ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 46ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Example: 47ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - "'Hello,' | U+0020 'world!'" means "Hello, world!" is displayed and the cursor position 48ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * is 6. 49ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - "'abc' [ 'def' ] 'ghi'" means "abcdefghi" is displayed and "def" is selected. 50ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - "U+1F441 | ( U+1F441 U+1F441 )" means three U+1F441 characters are displayed and 51ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * ReplacementSpan is set from offset 2 to 6. 52ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka */ 53ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakapublic class EditorState { 54ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String REPLACEMENT_SPAN_START = "("; 55ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String REPLACEMENT_SPAN_END = ")"; 56ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String SELECTION_START = "["; 57ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String SELECTION_END = "]"; 58ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String CURSOR = "|"; 59ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 60ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public Editable mText; 61ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public int mSelectionStart = -1; 62ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public int mSelectionEnd = -1; 63ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 64ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public EditorState() { 65ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 66ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 67ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Returns true if the code point is ASCII and graph. 68ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private boolean isGraphicAscii(int codePoint) { 69ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return 0x20 < codePoint && codePoint < 0x7F; 70ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 71ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 72ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Setup editor state with string. Please see class description for string format. 73ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public void setByString(String string) { 74ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final StringBuilder sb = new StringBuilder(); 75ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int replacementSpanStart = -1; 76ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int replacementSpanEnd = -1; 77ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionStart = -1; 78ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionEnd = -1; 79ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 80ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final String[] tokens = string.split(" +"); 81ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka for (String token : tokens) { 82ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (token.startsWith("'") && token.endsWith("'")) { 83ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka for (int i = 1; i < token.length() - 1; ++i) { 84ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final char ch = token.charAt(1); 85ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (!isGraphicAscii(ch)) { 86ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 87ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Only printable characters can be in single quote. " + 88ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Use U+" + Integer.toHexString(ch).toUpperCase() + " instead"); 89ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 90ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 91ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka sb.append(token.substring(1, token.length() - 1)); 92ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.startsWith("U+")) { 93ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int codePoint = Integer.parseInt(token.substring(2), 16); 94ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (codePoint < 0 || 0x10FFFF < codePoint) { 95ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException("Invalid code point is specified:" + token); 96ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 97ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka sb.append(Character.toChars(codePoint)); 98ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(CURSOR)) { 99ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionStart != -1 || mSelectionEnd != -1) { 100ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 101ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Two or more cursor/selection positions are specified."); 102ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 103ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionStart = mSelectionEnd = sb.length(); 104ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(SELECTION_START)) { 105ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionStart != -1) { 106ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 107ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Two or more cursor/selection positions are specified."); 108ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 109ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionStart = sb.length(); 110ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(SELECTION_END)) { 111ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionEnd != -1) { 112ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 113ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Two or more cursor/selection positions are specified."); 114ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 115ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionEnd = sb.length(); 116ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(REPLACEMENT_SPAN_START)) { 117ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart != -1) { 118ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 119ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Only one replacement span is supported"); 120ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 121ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka replacementSpanStart = sb.length(); 122ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(REPLACEMENT_SPAN_END)) { 123ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanEnd != -1) { 124ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 125ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Only one replacement span is supported"); 126ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 127ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka replacementSpanEnd = sb.length(); 128ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 129ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException("Unknown or invalid token: " + token); 130ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 131ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 132ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 133ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionStart == -1 || mSelectionEnd == -1) { 134ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionEnd != -1) { 135ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 136ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Selection start position doesn't exist."); 137ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (mSelectionStart != -1) { 138ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 139ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Selection end position doesn't exist."); 140ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 141ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 142ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "At least cursor position or selection range must be specified."); 143ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 144ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (mSelectionStart > mSelectionEnd) { 145ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 146ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Selection start position appears after end position."); 147ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 148ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 149ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final Spannable spannable = new SpannableString(sb.toString()); 150ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 151ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart != -1 || replacementSpanEnd != -1) { 152ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart == -1) { 153ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 154ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "ReplacementSpan start position doesn't exist."); 155ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 156ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanEnd == -1) { 157ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 158ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "ReplacementSpan end position doesn't exist."); 159ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 160ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart > replacementSpanEnd) { 161ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 162ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "ReplacementSpan start position appears after end position."); 163ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 164bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu 165bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu ReplacementSpan mockReplacementSpan = mock(ReplacementSpan.class); 166bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu when(mockReplacementSpan.getSize(any(), any(), any(), any(), any())) 167bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu .thenReturn(0); 168bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu doNothing().when(mockReplacementSpan) 169bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu .draw(any(), any(), any(), any(), any(), any(), any(), any(), any()); 170bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu 171bbe51f8290af05b4071c7ed44fcbaa98e2f85e13Andrei Stingaceanu spannable.setSpan(mockReplacementSpan, replacementSpanStart, replacementSpanEnd, 172ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 173ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 174ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mText = Editable.Factory.getInstance().newEditable(spannable); 175ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 176ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 177ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public void assertEquals(String string) { 178ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka EditorState expected = new EditorState(); 179ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka expected.setByString(string); 180ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 181ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Assert.assertEquals(expected.mText.toString(), mText.toString()); 182ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Assert.assertEquals(expected.mSelectionStart, mSelectionStart); 183ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Assert.assertEquals(expected.mSelectionEnd, mSelectionEnd); 184ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 185ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka} 186ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 187