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 19ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.graphics.Canvas; 20ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.graphics.Paint; 21ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.Editable; 22ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.Spannable; 23ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.SpannableString; 24ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport android.text.style.ReplacementSpan; 25ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 26ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakaimport junit.framework.Assert; 27ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 28ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka/** 29ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Represents an editor state. 30ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 31ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * The editor state can be specified by following string format. 32ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Components are separated by space(U+0020). 33ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Single-quoted string for printable ASCII characters, e.g. 'a', '123'. 34ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - U+XXXX form can be used for a Unicode code point. 35ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Components inside '[' and ']' are in selection. 36ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - Components inside '(' and ')' are in ReplacementSpan. 37ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - '|' is for specifying cursor position. 38ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 39ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Selection and cursor can not be specified at the same time. 40ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * 41ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * Example: 42ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - "'Hello,' | U+0020 'world!'" means "Hello, world!" is displayed and the cursor position 43ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * is 6. 44ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - "'abc' [ 'def' ] 'ghi'" means "abcdefghi" is displayed and "def" is selected. 45ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * - "U+1F441 | ( U+1F441 U+1F441 )" means three U+1F441 characters are displayed and 46ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * ReplacementSpan is set from offset 2 to 6. 47ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka */ 48ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonakapublic class EditorState { 49ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String REPLACEMENT_SPAN_START = "("; 50ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String REPLACEMENT_SPAN_END = ")"; 51ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String SELECTION_START = "["; 52ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String SELECTION_END = "]"; 53ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static final String CURSOR = "|"; 54ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 55ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public Editable mText; 56ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public int mSelectionStart = -1; 57ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public int mSelectionEnd = -1; 58ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 59ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public EditorState() { 60ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 61ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 62ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka /** 63ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka * A mocked {@link android.text.style.ReplacementSpan} for testing purpose. 64ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka */ 65ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private static class MockReplacementSpan extends ReplacementSpan { 66ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public int getSize(Paint paint, CharSequence text, int start, int end, 67ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Paint.FontMetricsInt fm) { 68ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return 0; 69ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 70ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, 71ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int y, int bottom, Paint paint) { 72ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 73ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 74ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 75ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Returns true if the code point is ASCII and graph. 76ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka private boolean isGraphicAscii(int codePoint) { 77ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka return 0x20 < codePoint && codePoint < 0x7F; 78ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 79ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 80ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka // Setup editor state with string. Please see class description for string format. 81ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public void setByString(String string) { 82ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final StringBuilder sb = new StringBuilder(); 83ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int replacementSpanStart = -1; 84ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka int replacementSpanEnd = -1; 85ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionStart = -1; 86ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionEnd = -1; 87ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 88ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final String[] tokens = string.split(" +"); 89ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka for (String token : tokens) { 90ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (token.startsWith("'") && token.endsWith("'")) { 91ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka for (int i = 1; i < token.length() - 1; ++i) { 92ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final char ch = token.charAt(1); 93ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (!isGraphicAscii(ch)) { 94ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 95ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Only printable characters can be in single quote. " + 96ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Use U+" + Integer.toHexString(ch).toUpperCase() + " instead"); 97ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 98ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 99ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka sb.append(token.substring(1, token.length() - 1)); 100ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.startsWith("U+")) { 101ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final int codePoint = Integer.parseInt(token.substring(2), 16); 102ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (codePoint < 0 || 0x10FFFF < codePoint) { 103ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException("Invalid code point is specified:" + token); 104ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 105ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka sb.append(Character.toChars(codePoint)); 106ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(CURSOR)) { 107ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionStart != -1 || mSelectionEnd != -1) { 108ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 109ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Two or more cursor/selection positions are specified."); 110ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 111ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionStart = mSelectionEnd = sb.length(); 112ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(SELECTION_START)) { 113ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionStart != -1) { 114ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 115ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Two or more cursor/selection positions are specified."); 116ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 117ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionStart = sb.length(); 118ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(SELECTION_END)) { 119ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionEnd != -1) { 120ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 121ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Two or more cursor/selection positions are specified."); 122ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 123ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mSelectionEnd = sb.length(); 124ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(REPLACEMENT_SPAN_START)) { 125ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart != -1) { 126ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 127ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Only one replacement span is supported"); 128ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 129ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka replacementSpanStart = sb.length(); 130ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (token.equals(REPLACEMENT_SPAN_END)) { 131ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanEnd != -1) { 132ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 133ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Only one replacement span is supported"); 134ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 135ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka replacementSpanEnd = sb.length(); 136ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 137ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException("Unknown or invalid token: " + token); 138ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 139ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 140ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 141ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionStart == -1 || mSelectionEnd == -1) { 142ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (mSelectionEnd != -1) { 143ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 144ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Selection start position doesn't exist."); 145ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (mSelectionStart != -1) { 146ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 147ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Selection end position doesn't exist."); 148ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else { 149ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 150ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "At least cursor position or selection range must be specified."); 151ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 152ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } else if (mSelectionStart > mSelectionEnd) { 153ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 154ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "Selection start position appears after end position."); 155ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 156ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 157ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka final Spannable spannable = new SpannableString(sb.toString()); 158ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 159ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart != -1 || replacementSpanEnd != -1) { 160ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart == -1) { 161ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 162ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "ReplacementSpan start position doesn't exist."); 163ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 164ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanEnd == -1) { 165ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 166ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "ReplacementSpan end position doesn't exist."); 167ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 168ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka if (replacementSpanStart > replacementSpanEnd) { 169ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka throw new IllegalArgumentException( 170ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka "ReplacementSpan start position appears after end position."); 171ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 172ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka spannable.setSpan(new MockReplacementSpan(), replacementSpanStart, replacementSpanEnd, 173ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 174ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 175ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka mText = Editable.Factory.getInstance().newEditable(spannable); 176ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 177ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 178ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka public void assertEquals(String string) { 179ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka EditorState expected = new EditorState(); 180ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka expected.setByString(string); 181ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 182ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Assert.assertEquals(expected.mText.toString(), mText.toString()); 183ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Assert.assertEquals(expected.mSelectionStart, mSelectionStart); 184ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka Assert.assertEquals(expected.mSelectionEnd, mSelectionEnd); 185ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka } 186ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka} 187ff3bfd5a79a95dcfccd1b8ad6c08c715ce6fa0e3Seigo Nonaka 188