Skip to content

Commit 59ba2b2

Browse files
Gilles DebunneAndroid (Google) Code Review
authored andcommitted
Merge "Bug 5250788: LatinIME slows down as amount of Text increases"
2 parents 70fa87b + 287d6c6 commit 59ba2b2

File tree

5 files changed

+276
-355
lines changed

5 files changed

+276
-355
lines changed

core/java/android/text/CharSequenceIterator.java

Lines changed: 0 additions & 100 deletions
This file was deleted.

core/java/android/text/method/ArrowKeyMovementMethod.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ private static boolean isSelecting(Spannable buffer) {
3535
(MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0));
3636
}
3737

38-
private int getCurrentLineTop(Spannable buffer, Layout layout) {
38+
private static int getCurrentLineTop(Spannable buffer, Layout layout) {
3939
return layout.getLineTop(layout.getLineForOffset(Selection.getSelectionEnd(buffer)));
4040
}
4141

42-
private int getPageHeight(TextView widget) {
42+
private static int getPageHeight(TextView widget) {
4343
// This calculation does not take into account the view transformations that
4444
// may have been applied to the child or its containers. In case of scaling or
4545
// rotation, the calculated page height may be incorrect.
@@ -196,14 +196,16 @@ protected boolean lineEnd(TextView widget, Spannable buffer) {
196196
/** {@hide} */
197197
@Override
198198
protected boolean leftWord(TextView widget, Spannable buffer) {
199-
mWordIterator.setCharSequence(buffer);
199+
final int selectionEnd = widget.getSelectionEnd();
200+
mWordIterator.setCharSequence(buffer, selectionEnd, selectionEnd);
200201
return Selection.moveToPreceding(buffer, mWordIterator, isSelecting(buffer));
201202
}
202203

203204
/** {@hide} */
204205
@Override
205206
protected boolean rightWord(TextView widget, Spannable buffer) {
206-
mWordIterator.setCharSequence(buffer);
207+
final int selectionEnd = widget.getSelectionEnd();
208+
mWordIterator.setCharSequence(buffer, selectionEnd, selectionEnd);
207209
return Selection.moveToFollowing(buffer, mWordIterator, isSelecting(buffer));
208210
}
209211

core/java/android/text/method/WordIterator.java

Lines changed: 53 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@
1717

1818
package android.text.method;
1919

20-
import android.text.CharSequenceIterator;
21-
import android.text.Editable;
2220
import android.text.Selection;
23-
import android.text.Spanned;
24-
import android.text.TextWatcher;
2521

2622
import java.text.BreakIterator;
27-
import java.text.CharacterIterator;
2823
import java.util.Locale;
2924

3025
/**
@@ -36,8 +31,11 @@
3631
* {@hide}
3732
*/
3833
public class WordIterator implements Selection.PositionIterator {
39-
private CharSequence mCurrent;
40-
private boolean mCurrentDirty = false;
34+
// Size of the window for the word iterator, should be greater than the longest word's length
35+
private static final int WINDOW_WIDTH = 50;
36+
37+
private String mString;
38+
private int mOffsetShift;
4139

4240
private BreakIterator mIterator;
4341

@@ -56,70 +54,40 @@ public WordIterator(Locale locale) {
5654
mIterator = BreakIterator.getWordInstance(locale);
5755
}
5856

59-
private final TextWatcher mWatcher = new TextWatcher() {
60-
/** {@inheritDoc} */
61-
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
62-
// ignored
63-
}
64-
65-
/** {@inheritDoc} */
66-
public void onTextChanged(CharSequence s, int start, int before, int count) {
67-
mCurrentDirty = true;
68-
}
69-
70-
/** {@inheritDoc} */
71-
public void afterTextChanged(Editable s) {
72-
// ignored
73-
}
74-
};
75-
76-
public void setCharSequence(CharSequence incoming) {
77-
// When incoming is different object, move listeners to new sequence
78-
// and mark as dirty so we reload contents.
79-
if (mCurrent != incoming) {
80-
if (mCurrent instanceof Editable) {
81-
((Editable) mCurrent).removeSpan(mWatcher);
82-
}
57+
public void setCharSequence(CharSequence charSequence, int start, int end) {
58+
mOffsetShift = Math.max(0, start - WINDOW_WIDTH);
59+
final int windowEnd = Math.min(charSequence.length(), end + WINDOW_WIDTH);
8360

84-
if (incoming instanceof Editable) {
85-
((Editable) incoming).setSpan(
86-
mWatcher, 0, incoming.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
87-
}
88-
89-
mCurrent = incoming;
90-
mCurrentDirty = true;
91-
}
92-
93-
if (mCurrentDirty) {
94-
final CharacterIterator charIterator = new CharSequenceIterator(mCurrent);
95-
mIterator.setText(charIterator);
96-
97-
mCurrentDirty = false;
98-
}
61+
mString = charSequence.toString().substring(mOffsetShift, windowEnd);
62+
mIterator.setText(mString);
9963
}
10064

10165
/** {@inheritDoc} */
10266
public int preceding(int offset) {
67+
int shiftedOffset = offset - mOffsetShift;
10368
do {
104-
offset = mIterator.preceding(offset);
105-
if (offset == BreakIterator.DONE || isOnLetterOrDigit(offset)) {
106-
break;
69+
shiftedOffset = mIterator.preceding(shiftedOffset);
70+
if (shiftedOffset == BreakIterator.DONE) {
71+
return BreakIterator.DONE;
72+
}
73+
if (isOnLetterOrDigit(shiftedOffset)) {
74+
return shiftedOffset + mOffsetShift;
10775
}
10876
} while (true);
109-
110-
return offset;
11177
}
11278

11379
/** {@inheritDoc} */
11480
public int following(int offset) {
81+
int shiftedOffset = offset - mOffsetShift;
11582
do {
116-
offset = mIterator.following(offset);
117-
if (offset == BreakIterator.DONE || isAfterLetterOrDigit(offset)) {
118-
break;
83+
shiftedOffset = mIterator.following(shiftedOffset);
84+
if (shiftedOffset == BreakIterator.DONE) {
85+
return BreakIterator.DONE;
86+
}
87+
if (isAfterLetterOrDigit(shiftedOffset)) {
88+
return shiftedOffset + mOffsetShift;
11989
}
12090
} while (true);
121-
122-
return offset;
12391
}
12492

12593
/** If <code>offset</code> is within a word, returns the index of the first character of that
@@ -135,17 +103,18 @@ public int following(int offset) {
135103
* @throws IllegalArgumentException is offset is not valid.
136104
*/
137105
public int getBeginning(int offset) {
138-
checkOffsetIsValid(offset);
106+
final int shiftedOffset = offset - mOffsetShift;
107+
checkOffsetIsValid(shiftedOffset);
139108

140-
if (isOnLetterOrDigit(offset)) {
141-
if (mIterator.isBoundary(offset)) {
142-
return offset;
109+
if (isOnLetterOrDigit(shiftedOffset)) {
110+
if (mIterator.isBoundary(shiftedOffset)) {
111+
return shiftedOffset + mOffsetShift;
143112
} else {
144-
return mIterator.preceding(offset);
113+
return mIterator.preceding(shiftedOffset) + mOffsetShift;
145114
}
146115
} else {
147-
if (isAfterLetterOrDigit(offset)) {
148-
return mIterator.preceding(offset);
116+
if (isAfterLetterOrDigit(shiftedOffset)) {
117+
return mIterator.preceding(shiftedOffset) + mOffsetShift;
149118
}
150119
}
151120
return BreakIterator.DONE;
@@ -164,58 +133,44 @@ public int getBeginning(int offset) {
164133
* @throws IllegalArgumentException is offset is not valid.
165134
*/
166135
public int getEnd(int offset) {
167-
checkOffsetIsValid(offset);
136+
final int shiftedOffset = offset - mOffsetShift;
137+
checkOffsetIsValid(shiftedOffset);
168138

169-
if (isAfterLetterOrDigit(offset)) {
170-
if (mIterator.isBoundary(offset)) {
171-
return offset;
139+
if (isAfterLetterOrDigit(shiftedOffset)) {
140+
if (mIterator.isBoundary(shiftedOffset)) {
141+
return shiftedOffset + mOffsetShift;
172142
} else {
173-
return mIterator.following(offset);
143+
return mIterator.following(shiftedOffset) + mOffsetShift;
174144
}
175145
} else {
176-
if (isOnLetterOrDigit(offset)) {
177-
return mIterator.following(offset);
146+
if (isOnLetterOrDigit(shiftedOffset)) {
147+
return mIterator.following(shiftedOffset) + mOffsetShift;
178148
}
179149
}
180150
return BreakIterator.DONE;
181151
}
182152

183-
private boolean isAfterLetterOrDigit(int offset) {
184-
if (offset - 1 >= 0) {
185-
final char previousChar = mCurrent.charAt(offset - 1);
186-
if (Character.isLetterOrDigit(previousChar)) return true;
187-
if (offset - 2 >= 0) {
188-
final char previousPreviousChar = mCurrent.charAt(offset - 2);
189-
if (Character.isSurrogatePair(previousPreviousChar, previousChar)) {
190-
final int codePoint = Character.toCodePoint(previousPreviousChar, previousChar);
191-
return Character.isLetterOrDigit(codePoint);
192-
}
193-
}
153+
private boolean isAfterLetterOrDigit(int shiftedOffset) {
154+
if (shiftedOffset >= 1 && shiftedOffset <= mString.length()) {
155+
final int codePoint = mString.codePointBefore(shiftedOffset);
156+
if (Character.isLetterOrDigit(codePoint)) return true;
194157
}
195158
return false;
196159
}
197160

198-
private boolean isOnLetterOrDigit(int offset) {
199-
final int length = mCurrent.length();
200-
if (offset < length) {
201-
final char currentChar = mCurrent.charAt(offset);
202-
if (Character.isLetterOrDigit(currentChar)) return true;
203-
if (offset + 1 < length) {
204-
final char nextChar = mCurrent.charAt(offset + 1);
205-
if (Character.isSurrogatePair(currentChar, nextChar)) {
206-
final int codePoint = Character.toCodePoint(currentChar, nextChar);
207-
return Character.isLetterOrDigit(codePoint);
208-
}
209-
}
161+
private boolean isOnLetterOrDigit(int shiftedOffset) {
162+
if (shiftedOffset >= 0 && shiftedOffset < mString.length()) {
163+
final int codePoint = mString.codePointAt(shiftedOffset);
164+
if (Character.isLetterOrDigit(codePoint)) return true;
210165
}
211166
return false;
212167
}
213168

214-
private void checkOffsetIsValid(int offset) {
215-
if (offset < 0 || offset > mCurrent.length()) {
216-
final String message = "Invalid offset: " + offset +
217-
". Valid range is [0, " + mCurrent.length() + "]";
218-
throw new IllegalArgumentException(message);
169+
private void checkOffsetIsValid(int shiftedOffset) {
170+
if (shiftedOffset < 0 || shiftedOffset > mString.length()) {
171+
throw new IllegalArgumentException("Invalid offset: " + (shiftedOffset + mOffsetShift) +
172+
". Valid range is [" + mOffsetShift + ", " + (mString.length() + mOffsetShift) +
173+
"]");
219174
}
220175
}
221176
}

0 commit comments

Comments
 (0)