blob: 17ce4f639f28911d7090e9e3a2a15b12a1e5f9d8 [file] [log] [blame]
Svetoslav Ganov6d17a932012-04-27 19:30:38 -07001/*
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
17package android.view;
18
19import android.content.ComponentCallbacks;
20import android.content.Context;
21import android.content.pm.ActivityInfo;
22import android.content.res.Configuration;
23
24import java.text.BreakIterator;
25import 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 */
36public 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 {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -070050
51 protected String mText;
52
53 private final int[] mSegment = new int[2];
54
55 public void initialize(String text) {
56 mText = text;
57 }
58
59 protected int[] getRange(int start, int end) {
60 if (start < 0 || end < 0 || start == end) {
61 return null;
62 }
63 mSegment[0] = start;
64 mSegment[1] = end;
65 return mSegment;
66 }
67 }
68
69 static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
70 implements ComponentCallbacks {
71 private static CharacterTextSegmentIterator sInstance;
72
Svetoslav Ganovbbd31552012-06-11 12:08:18 -070073 private Locale mLocale;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -070074
75 protected BreakIterator mImpl;
76
Svetoslav Ganovbbd31552012-06-11 12:08:18 -070077 public static CharacterTextSegmentIterator getInstance(Locale locale) {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -070078 if (sInstance == null) {
Svetoslav Ganovbbd31552012-06-11 12:08:18 -070079 sInstance = new CharacterTextSegmentIterator(locale);
Svetoslav Ganov6d17a932012-04-27 19:30:38 -070080 }
81 return sInstance;
82 }
83
Svetoslav Ganovbbd31552012-06-11 12:08:18 -070084 private CharacterTextSegmentIterator(Locale locale) {
85 mLocale = locale;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -070086 onLocaleChanged(locale);
87 ViewRootImpl.addConfigCallback(this);
88 }
89
90 @Override
91 public void initialize(String text) {
92 super.initialize(text);
93 mImpl.setText(text);
94 }
95
96 @Override
97 public int[] following(int offset) {
98 final int textLegth = mText.length();
99 if (textLegth <= 0) {
100 return null;
101 }
102 if (offset >= textLegth) {
103 return null;
104 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700105 int start = offset;
106 if (start < 0) {
107 start = 0;
108 }
109 while (!mImpl.isBoundary(start)) {
110 start = mImpl.following(start);
111 if (start == BreakIterator.DONE) {
112 return null;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700113 }
114 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700115 final int end = mImpl.following(start);
116 if (end == BreakIterator.DONE) {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700117 return null;
118 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700119 return getRange(start, end);
120 }
121
122 @Override
123 public int[] preceding(int offset) {
124 final int textLegth = mText.length();
125 if (textLegth <= 0) {
126 return null;
127 }
128 if (offset <= 0) {
129 return null;
130 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700131 int end = offset;
132 if (end > textLegth) {
133 end = textLegth;
134 }
135 while (!mImpl.isBoundary(end)) {
136 end = mImpl.preceding(end);
137 if (end == BreakIterator.DONE) {
138 return null;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700139 }
140 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700141 final int start = mImpl.preceding(end);
142 if (start == BreakIterator.DONE) {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700143 return null;
144 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700145 return getRange(start, end);
146 }
147
148 @Override
149 public void onConfigurationChanged(Configuration newConfig) {
Svetoslav Ganovbbd31552012-06-11 12:08:18 -0700150 Locale locale = newConfig.locale;
151 if (!mLocale.equals(locale)) {
152 mLocale = locale;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700153 onLocaleChanged(locale);
154 }
155 }
156
157 @Override
158 public void onLowMemory() {
159 /* ignore */
160 }
161
162 protected void onLocaleChanged(Locale locale) {
163 mImpl = BreakIterator.getCharacterInstance(locale);
164 }
165 }
166
167 static class WordTextSegmentIterator extends CharacterTextSegmentIterator {
168 private static WordTextSegmentIterator sInstance;
169
Svetoslav Ganovbbd31552012-06-11 12:08:18 -0700170 public static WordTextSegmentIterator getInstance(Locale locale) {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700171 if (sInstance == null) {
Svetoslav Ganovbbd31552012-06-11 12:08:18 -0700172 sInstance = new WordTextSegmentIterator(locale);
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700173 }
174 return sInstance;
175 }
176
Svetoslav Ganovbbd31552012-06-11 12:08:18 -0700177 private WordTextSegmentIterator(Locale locale) {
178 super(locale);
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700179 }
180
181 @Override
182 protected void onLocaleChanged(Locale locale) {
183 mImpl = BreakIterator.getWordInstance(locale);
184 }
185
186 @Override
187 public int[] following(int offset) {
188 final int textLegth = mText.length();
189 if (textLegth <= 0) {
190 return null;
191 }
192 if (offset >= mText.length()) {
193 return null;
194 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700195 int start = offset;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700196 if (start < 0) {
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700197 start = 0;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700198 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700199 while (!isLetterOrDigit(start) && !isStartBoundary(start)) {
200 start = mImpl.following(start);
201 if (start == BreakIterator.DONE) {
202 return null;
203 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700204 }
205 final int end = mImpl.following(start);
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700206 if (end == BreakIterator.DONE || !isEndBoundary(end)) {
207 return null;
208 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700209 return getRange(start, end);
210 }
211
212 @Override
213 public int[] preceding(int offset) {
214 final int textLegth = mText.length();
215 if (textLegth <= 0) {
216 return null;
217 }
218 if (offset <= 0) {
219 return null;
220 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700221 int end = offset;
222 if (end > textLegth) {
223 end = textLegth;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700224 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700225 while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) {
226 end = mImpl.preceding(end);
227 if (end == BreakIterator.DONE) {
228 return null;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700229 }
230 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700231 final int start = mImpl.preceding(end);
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700232 if (start == BreakIterator.DONE || !isStartBoundary(start)) {
233 return null;
234 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700235 return getRange(start, end);
236 }
237
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700238 private boolean isStartBoundary(int index) {
239 return isLetterOrDigit(index)
240 && (index == 0 || !isLetterOrDigit(index - 1));
241 }
242
243 private boolean isEndBoundary(int index) {
244 return (index > 0 && isLetterOrDigit(index - 1))
245 && (index == mText.length() || !isLetterOrDigit(index));
246 }
247
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700248 private boolean isLetterOrDigit(int index) {
249 if (index >= 0 && index < mText.length()) {
250 final int codePoint = mText.codePointAt(index);
251 return Character.isLetterOrDigit(codePoint);
252 }
253 return false;
254 }
255 }
256
257 static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator {
258 private static ParagraphTextSegmentIterator sInstance;
259
260 public static ParagraphTextSegmentIterator getInstance() {
261 if (sInstance == null) {
262 sInstance = new ParagraphTextSegmentIterator();
263 }
264 return sInstance;
265 }
266
267 @Override
268 public int[] following(int offset) {
269 final int textLength = mText.length();
270 if (textLength <= 0) {
271 return null;
272 }
273 if (offset >= textLength) {
274 return null;
275 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700276 int start = offset;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700277 if (start < 0) {
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700278 start = 0;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700279 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700280 while (start < textLength && mText.charAt(start) == '\n'
281 && !isStartBoundary(start)) {
alanv97688322012-05-17 13:54:30 -0700282 start++;
283 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700284 if (start >= textLength) {
285 return null;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700286 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700287 int end = start + 1;
288 while (end < textLength && !isEndBoundary(end)) {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700289 end++;
290 }
291 return getRange(start, end);
292 }
293
294 @Override
295 public int[] preceding(int offset) {
296 final int textLength = mText.length();
297 if (textLength <= 0) {
298 return null;
299 }
300 if (offset <= 0) {
301 return null;
302 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700303 int end = offset;
304 if (end > textLength) {
305 end = textLength;
306 }
307 while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) {
308 end--;
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700309 }
310 if (end <= 0) {
311 return null;
312 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700313 int start = end - 1;
314 while (start > 0 && !isStartBoundary(start)) {
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700315 start--;
316 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700317 return getRange(start, end);
318 }
Svetoslav Ganov39f2aee2012-05-29 09:15:30 -0700319
320 private boolean isStartBoundary(int index) {
321 return (mText.charAt(index) != '\n'
322 && (index == 0 || mText.charAt(index - 1) == '\n'));
323 }
324
325 private boolean isEndBoundary(int index) {
326 return (index > 0 && mText.charAt(index - 1) != '\n'
327 && (index == mText.length() || mText.charAt(index) == '\n'));
328 }
Svetoslav Ganov6d17a932012-04-27 19:30:38 -0700329 }
330}