Skip to content

Commit b2ee0d5

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Text traversal at various granularities." into jb-dev
2 parents f4c77df + 6d17a93 commit b2ee0d5

File tree

7 files changed

+861
-13
lines changed

7 files changed

+861
-13
lines changed

api/current.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25088,6 +25088,7 @@ package android.view.accessibility {
2508825088
method public void appendRecord(android.view.accessibility.AccessibilityRecord);
2508925089
method public int describeContents();
2509025090
method public static java.lang.String eventTypeToString(int);
25091+
method public int getAction();
2509125092
method public long getEventTime();
2509225093
method public int getEventType();
2509325094
method public int getMovementGranularity();
@@ -25098,6 +25099,7 @@ package android.view.accessibility {
2509825099
method public static android.view.accessibility.AccessibilityEvent obtain(int);
2509925100
method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
2510025101
method public static android.view.accessibility.AccessibilityEvent obtain();
25102+
method public void setAction(int);
2510125103
method public void setEventTime(long);
2510225104
method public void setEventType(int);
2510325105
method public void setMovementGranularity(int);
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
/*
2+
* Copyright (C) 2012 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.view;
18+
19+
import android.content.ComponentCallbacks;
20+
import android.content.Context;
21+
import android.content.pm.ActivityInfo;
22+
import android.content.res.Configuration;
23+
24+
import java.text.BreakIterator;
25+
import java.util.Locale;
26+
27+
/**
28+
* This class contains the implementation of text segment iterators
29+
* for accessibility support.
30+
*
31+
* Note: Such iterators are needed in the view package since we want
32+
* to be able to iterator over content description of any view.
33+
*
34+
* @hide
35+
*/
36+
public final class AccessibilityIterators {
37+
38+
/**
39+
* @hide
40+
*/
41+
public static interface TextSegmentIterator {
42+
public int[] following(int current);
43+
public int[] preceding(int current);
44+
}
45+
46+
/**
47+
* @hide
48+
*/
49+
public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator {
50+
protected static final int DONE = -1;
51+
52+
protected String mText;
53+
54+
private final int[] mSegment = new int[2];
55+
56+
public void initialize(String text) {
57+
mText = text;
58+
}
59+
60+
protected int[] getRange(int start, int end) {
61+
if (start < 0 || end < 0 || start == end) {
62+
return null;
63+
}
64+
mSegment[0] = start;
65+
mSegment[1] = end;
66+
return mSegment;
67+
}
68+
}
69+
70+
static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
71+
implements ComponentCallbacks {
72+
private static CharacterTextSegmentIterator sInstance;
73+
74+
private final Context mAppContext;
75+
76+
protected BreakIterator mImpl;
77+
78+
public static CharacterTextSegmentIterator getInstance(Context context) {
79+
if (sInstance == null) {
80+
sInstance = new CharacterTextSegmentIterator(context);
81+
}
82+
return sInstance;
83+
}
84+
85+
private CharacterTextSegmentIterator(Context context) {
86+
mAppContext = context.getApplicationContext();
87+
Locale locale = mAppContext.getResources().getConfiguration().locale;
88+
onLocaleChanged(locale);
89+
ViewRootImpl.addConfigCallback(this);
90+
}
91+
92+
@Override
93+
public void initialize(String text) {
94+
super.initialize(text);
95+
mImpl.setText(text);
96+
}
97+
98+
@Override
99+
public int[] following(int offset) {
100+
final int textLegth = mText.length();
101+
if (textLegth <= 0) {
102+
return null;
103+
}
104+
if (offset >= textLegth) {
105+
return null;
106+
}
107+
int start = -1;
108+
if (offset < 0) {
109+
offset = 0;
110+
if (mImpl.isBoundary(offset)) {
111+
start = offset;
112+
}
113+
}
114+
if (start < 0) {
115+
start = mImpl.following(offset);
116+
}
117+
if (start < 0) {
118+
return null;
119+
}
120+
final int end = mImpl.following(start);
121+
return getRange(start, end);
122+
}
123+
124+
@Override
125+
public int[] preceding(int offset) {
126+
final int textLegth = mText.length();
127+
if (textLegth <= 0) {
128+
return null;
129+
}
130+
if (offset <= 0) {
131+
return null;
132+
}
133+
int end = -1;
134+
if (offset > mText.length()) {
135+
offset = mText.length();
136+
if (mImpl.isBoundary(offset)) {
137+
end = offset;
138+
}
139+
}
140+
if (end < 0) {
141+
end = mImpl.preceding(offset);
142+
}
143+
if (end < 0) {
144+
return null;
145+
}
146+
final int start = mImpl.preceding(end);
147+
return getRange(start, end);
148+
}
149+
150+
@Override
151+
public void onConfigurationChanged(Configuration newConfig) {
152+
Configuration oldConfig = mAppContext.getResources().getConfiguration();
153+
final int changed = oldConfig.diff(newConfig);
154+
if ((changed & ActivityInfo.CONFIG_LOCALE) != 0) {
155+
Locale locale = newConfig.locale;
156+
onLocaleChanged(locale);
157+
}
158+
}
159+
160+
@Override
161+
public void onLowMemory() {
162+
/* ignore */
163+
}
164+
165+
protected void onLocaleChanged(Locale locale) {
166+
mImpl = BreakIterator.getCharacterInstance(locale);
167+
}
168+
}
169+
170+
static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
171+
private static WordTextSegmentIterator sInstance;
172+
173+
public static WordTextSegmentIterator getInstance(Context context) {
174+
if (sInstance == null) {
175+
sInstance = new WordTextSegmentIterator(context);
176+
}
177+
return sInstance;
178+
}
179+
180+
private WordTextSegmentIterator(Context context) {
181+
super(context);
182+
}
183+
184+
@Override
185+
protected void onLocaleChanged(Locale locale) {
186+
mImpl = BreakIterator.getWordInstance(locale);
187+
}
188+
189+
@Override
190+
public int[] following(int offset) {
191+
final int textLegth = mText.length();
192+
if (textLegth <= 0) {
193+
return null;
194+
}
195+
if (offset >= mText.length()) {
196+
return null;
197+
}
198+
int start = -1;
199+
if (offset < 0) {
200+
offset = 0;
201+
if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) {
202+
start = offset;
203+
}
204+
}
205+
if (start < 0) {
206+
while ((offset = mImpl.following(offset)) != DONE) {
207+
if (isLetterOrDigit(offset)) {
208+
start = offset;
209+
break;
210+
}
211+
}
212+
}
213+
if (start < 0) {
214+
return null;
215+
}
216+
final int end = mImpl.following(start);
217+
return getRange(start, end);
218+
}
219+
220+
@Override
221+
public int[] preceding(int offset) {
222+
final int textLegth = mText.length();
223+
if (textLegth <= 0) {
224+
return null;
225+
}
226+
if (offset <= 0) {
227+
return null;
228+
}
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+
}
235+
}
236+
if (end < 0) {
237+
while ((offset = mImpl.preceding(offset)) != DONE) {
238+
if (offset > 0 && isLetterOrDigit(offset - 1)) {
239+
end = offset;
240+
break;
241+
}
242+
}
243+
}
244+
if (end < 0) {
245+
return null;
246+
}
247+
final int start = mImpl.preceding(end);
248+
return getRange(start, end);
249+
}
250+
251+
private boolean isLetterOrDigit(int index) {
252+
if (index >= 0 && index < mText.length()) {
253+
final int codePoint = mText.codePointAt(index);
254+
return Character.isLetterOrDigit(codePoint);
255+
}
256+
return false;
257+
}
258+
}
259+
260+
static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
261+
private static ParagraphTextSegmentIterator sInstance;
262+
263+
public static ParagraphTextSegmentIterator getInstance() {
264+
if (sInstance == null) {
265+
sInstance = new ParagraphTextSegmentIterator();
266+
}
267+
return sInstance;
268+
}
269+
270+
@Override
271+
public int[] following(int offset) {
272+
final int textLength = mText.length();
273+
if (textLength <= 0) {
274+
return null;
275+
}
276+
if (offset >= textLength) {
277+
return null;
278+
}
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+
}
290+
while (start < textLength && mText.charAt(start) == '\n') {
291+
start++;
292+
}
293+
if (start < 0) {
294+
return null;
295+
}
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+
}
302+
}
303+
while (end < textLength && mText.charAt(end) == '\n') {
304+
end++;
305+
}
306+
return getRange(start, end);
307+
}
308+
309+
@Override
310+
public int[] preceding(int offset) {
311+
final int textLength = mText.length();
312+
if (textLength <= 0) {
313+
return null;
314+
}
315+
if (offset <= 0) {
316+
return null;
317+
}
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+
}
331+
}
332+
if (end <= 0) {
333+
return null;
334+
}
335+
int start = end;
336+
while (start > 0 && mText.charAt(start - 1) == '\n') {
337+
start--;
338+
}
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);
349+
return getRange(start, end);
350+
}
351+
}
352+
}

0 commit comments

Comments
 (0)