Skip to content

Commit a5c896a

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Updating the behaviour of accessibility text iterators." into jb-dev
2 parents 48d1dac + 39f2aee commit a5c896a

File tree

4 files changed

+128
-158
lines changed

4 files changed

+128
-158
lines changed

core/java/android/view/AccessibilityIterators.java

Lines changed: 74 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ public static interface TextSegmentIterator {
4747
* @hide
4848
*/
4949
public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
50-
protected static final int DONE = -1;
5150

5251
protected String mText;
5352

@@ -104,20 +103,20 @@ public int[] following(int offset) {
104103
if (offset >= textLegth) {
105104
return null;
106105
}
107-
int start = -1;
108-
if (offset < 0) {
109-
offset = 0;
110-
if (mImpl.isBoundary(offset)) {
111-
start = offset;
112-
}
113-
}
106+
int start = offset;
114107
if (start < 0) {
115-
start = mImpl.following(offset);
108+
start = 0;
116109
}
117-
if (start < 0) {
118-
return null;
110+
while (!mImpl.isBoundary(start)) {
111+
start = mImpl.following(start);
112+
if (start == BreakIterator.DONE) {
113+
return null;
114+
}
119115
}
120116
final int end = mImpl.following(start);
117+
if (end == BreakIterator.DONE) {
118+
return null;
119+
}
121120
return getRange(start, end);
122121
}
123122

@@ -130,20 +129,20 @@ public int[] preceding(int offset) {
130129
if (offset <= 0) {
131130
return null;
132131
}
133-
int end = -1;
134-
if (offset > mText.length()) {
135-
offset = mText.length();
136-
if (mImpl.isBoundary(offset)) {
137-
end = offset;
138-
}
132+
int end = offset;
133+
if (end > textLegth) {
134+
end = textLegth;
139135
}
140-
if (end < 0) {
141-
end = mImpl.preceding(offset);
136+
while (!mImpl.isBoundary(end)) {
137+
end = mImpl.preceding(end);
138+
if (end == BreakIterator.DONE) {
139+
return null;
140+
}
142141
}
143-
if (end < 0) {
142+
final int start = mImpl.preceding(end);
143+
if (start == BreakIterator.DONE) {
144144
return null;
145145
}
146-
final int start = mImpl.preceding(end);
147146
return getRange(start, end);
148147
}
149148

@@ -195,25 +194,20 @@ public int[] following(int offset) {
195194
if (offset >= mText.length()) {
196195
return null;
197196
}
198-
int start = -1;
199-
if (offset < 0) {
200-
offset = 0;
201-
if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) {
202-
start = offset;
203-
}
204-
}
197+
int start = offset;
205198
if (start < 0) {
206-
while ((offset = mImpl.following(offset)) != DONE) {
207-
if (isLetterOrDigit(offset)) {
208-
start = offset;
209-
break;
210-
}
199+
start = 0;
200+
}
201+
while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
202+
start = mImpl.following(start);
203+
if (start == BreakIterator.DONE) {
204+
return null;
211205
}
212206
}
213-
if (start < 0) {
207+
final int end = mImpl.following(start);
208+
if (end == BreakIterator.DONE || !isEndBoundary(end)) {
214209
return null;
215210
}
216-
final int end = mImpl.following(start);
217211
return getRange(start, end);
218212
}
219213

@@ -226,28 +220,33 @@ public int[] preceding(int offset) {
226220
if (offset <= 0) {
227221
return null;
228222
}
229-
int end = -1;
230-
if (offset > mText.length()) {
231-
offset = mText.length();
232-
if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) {
233-
end = offset;
234-
}
223+
int end = offset;
224+
if (end > textLegth) {
225+
end = textLegth;
235226
}
236-
if (end < 0) {
237-
while ((offset = mImpl.preceding(offset)) != DONE) {
238-
if (offset > 0 && isLetterOrDigit(offset - 1)) {
239-
end = offset;
240-
break;
241-
}
227+
while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
228+
end = mImpl.preceding(end);
229+
if (end == BreakIterator.DONE) {
230+
return null;
242231
}
243232
}
244-
if (end < 0) {
233+
final int start = mImpl.preceding(end);
234+
if (start == BreakIterator.DONE || !isStartBoundary(start)) {
245235
return null;
246236
}
247-
final int start = mImpl.preceding(end);
248237
return getRange(start, end);
249238
}
250239

240+
private boolean isStartBoundary(int index) {
241+
return isLetterOrDigit(index)
242+
&& (index == 0 || !isLetterOrDigit(index - 1));
243+
}
244+
245+
private boolean isEndBoundary(int index) {
246+
return (index > 0 && isLetterOrDigit(index - 1))
247+
&& (index == mText.length() || !isLetterOrDigit(index));
248+
}
249+
251250
private boolean isLetterOrDigit(int index) {
252251
if (index >= 0 && index < mText.length()) {
253252
final int codePoint = mText.codePointAt(index);
@@ -276,31 +275,19 @@ public int[] following(int offset) {
276275
if (offset >= textLength) {
277276
return null;
278277
}
279-
int start = -1;
280-
if (offset < 0) {
281-
start = 0;
282-
} else {
283-
for (int i = offset + 1; i < textLength; i++) {
284-
if (mText.charAt(i) == '\n') {
285-
start = i;
286-
break;
287-
}
288-
}
289-
}
278+
int start = offset;
290279
if (start < 0) {
291-
return null;
280+
start = 0;
292281
}
293-
while (start < textLength && mText.charAt(start) == '\n') {
282+
while (start < textLength && mText.charAt(start) == '\n'
283+
&& !isStartBoundary(start)) {
294284
start++;
295285
}
296-
int end = start;
297-
for (int i = end + 1; i < textLength; i++) {
298-
end = i;
299-
if (mText.charAt(i) == '\n') {
300-
break;
301-
}
286+
if (start >= textLength) {
287+
return null;
302288
}
303-
while (end < textLength && mText.charAt(end) == '\n') {
289+
int end = start + 1;
290+
while (end < textLength && !isEndBoundary(end)) {
304291
end++;
305292
}
306293
return getRange(start, end);
@@ -315,38 +302,31 @@ public int[] preceding(int offset) {
315302
if (offset <= 0) {
316303
return null;
317304
}
318-
int end = -1;
319-
if (offset > mText.length()) {
320-
end = mText.length();
321-
} else {
322-
if (offset > 0 && mText.charAt(offset - 1) == '\n') {
323-
offset--;
324-
}
325-
for (int i = offset - 1; i >= 0; i--) {
326-
if (i > 0 && mText.charAt(i - 1) == '\n') {
327-
end = i;
328-
break;
329-
}
330-
}
305+
int end = offset;
306+
if (end > textLength) {
307+
end = textLength;
308+
}
309+
while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
310+
end--;
331311
}
332312
if (end <= 0) {
333313
return null;
334314
}
335-
int start = end;
336-
while (start > 0 && mText.charAt(start - 1) == '\n') {
315+
int start = end - 1;
316+
while (start > 0 && !isStartBoundary(start)) {
337317
start--;
338318
}
339-
if (start == 0 && mText.charAt(start) == '\n') {
340-
return null;
341-
}
342-
for (int i = start - 1; i >= 0; i--) {
343-
start = i;
344-
if (start > 0 && mText.charAt(i - 1) == '\n') {
345-
break;
346-
}
347-
}
348-
start = Math.max(0, start);
349319
return getRange(start, end);
350320
}
321+
322+
private boolean isStartBoundary(int index) {
323+
return (mText.charAt(index) != '\n'
324+
&& (index == 0 || mText.charAt(index - 1) == '\n'));
325+
}
326+
327+
private boolean isEndBoundary(int index) {
328+
return (index > 0 && mText.charAt(index - 1) != '\n'
329+
&& (index == mText.length() || mText.charAt(index) == '\n'));
330+
}
351331
}
352332
}

core/java/android/view/View.java

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,7 +1596,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
15961596
/**
15971597
* @hide
15981598
*/
1599-
private int mAccessibilityCursorPosition = -1;
1599+
private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
16001600

16011601
/**
16021602
* The view's tag.
@@ -2534,6 +2534,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
25342534
*/
25352535
public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
25362536

2537+
/**
2538+
* The undefined cursor position.
2539+
*/
2540+
private static final int ACCESSIBILITY_CURSOR_POSITION_UNDEFINED = -1;
2541+
25372542
/**
25382543
* Indicates that the screen has changed state and is now off.
25392544
*
@@ -6273,7 +6278,7 @@ public void clearAccessibilityFocus() {
62736278
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
62746279
notifyAccessibilityStateChanged();
62756280
// Clear the text navigation state.
6276-
setAccessibilityCursorPosition(-1);
6281+
setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
62776282
}
62786283
// Clear the global reference of accessibility focus if this
62796284
// view or any of its descendants had accessibility focus.
@@ -6323,6 +6328,7 @@ public boolean canTakeAccessibilityFocusFromHover() {
63236328
void clearAccessibilityFocusNoCallbacks() {
63246329
if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
63256330
mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
6331+
setAccessibilityCursorPosition(ACCESSIBILITY_CURSOR_POSITION_UNDEFINED);
63266332
invalidate();
63276333
}
63286334
}
@@ -6819,12 +6825,11 @@ private boolean nextAtGranularity(int granularity) {
68196825
final int current = getAccessibilityCursorPosition();
68206826
final int[] range = iterator.following(current);
68216827
if (range == null) {
6822-
setAccessibilityCursorPosition(-1);
68236828
return false;
68246829
}
68256830
final int start = range[0];
68266831
final int end = range[1];
6827-
setAccessibilityCursorPosition(start);
6832+
setAccessibilityCursorPosition(end);
68286833
sendViewTextTraversedAtGranularityEvent(
68296834
AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY,
68306835
granularity, start, end);
@@ -6840,16 +6845,26 @@ private boolean previousAtGranularity(int granularity) {
68406845
if (iterator == null) {
68416846
return false;
68426847
}
6843-
final int selectionStart = getAccessibilityCursorPosition();
6844-
final int current = selectionStart >= 0 ? selectionStart : text.length() + 1;
6848+
int current = getAccessibilityCursorPosition();
6849+
if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) {
6850+
current = text.length();
6851+
} else if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
6852+
// When traversing by character we always put the cursor after the character
6853+
// to ease edit and have to compensate before asking the for previous segment.
6854+
current--;
6855+
}
68456856
final int[] range = iterator.preceding(current);
68466857
if (range == null) {
6847-
setAccessibilityCursorPosition(-1);
68486858
return false;
68496859
}
68506860
final int start = range[0];
68516861
final int end = range[1];
6852-
setAccessibilityCursorPosition(end);
6862+
// Always put the cursor after the character to ease edit.
6863+
if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) {
6864+
setAccessibilityCursorPosition(end);
6865+
} else {
6866+
setAccessibilityCursorPosition(start);
6867+
}
68536868
sendViewTextTraversedAtGranularityEvent(
68546869
AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
68556870
granularity, start, end);

0 commit comments

Comments
 (0)