16d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/* 26d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Copyright (C) 2012 The Android Open Source Project 36d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 46d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Licensed under the Apache License, Version 2.0 (the "License"); 56d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * you may not use this file except in compliance with the License. 66d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * You may obtain a copy of the License at 76d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 86d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * http://www.apache.org/licenses/LICENSE-2.0 96d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Unless required by applicable law or agreed to in writing, software 116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * distributed under the License is distributed on an "AS IS" BASIS, 126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * See the License for the specific language governing permissions and 146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * limitations under the License. 156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 176d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpackage android.view; 186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 196d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.ComponentCallbacks; 206d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.Context; 216d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.pm.ActivityInfo; 226d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport android.content.res.Configuration; 236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 246d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.text.BreakIterator; 256d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.util.Locale; 266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/** 286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * This class contains the implementation of text segment iterators 296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * for accessibility support. 306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Note: Such iterators are needed in the view package since we want 326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * to be able to iterator over content description of any view. 336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 366d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpublic final class AccessibilityIterators { 376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /** 396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static interface TextSegmentIterator { 426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int current); 436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int current); 446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /** 476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator { 506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected String mText; 526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private final int[] mSegment = new int[2]; 546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mText = text; 576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected int[] getRange(int start, int end) { 606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0 || end < 0 || start == end) { 616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[0] = start; 646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[1] = end; 656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return mSegment; 666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator 706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov implements ComponentCallbacks { 716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static CharacterTextSegmentIterator sInstance; 726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 73bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private Locale mLocale; 746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected BreakIterator mImpl; 766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 77bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov public static CharacterTextSegmentIterator getInstance(Locale locale) { 786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 79bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov sInstance = new CharacterTextSegmentIterator(locale); 806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 84bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private CharacterTextSegmentIterator(Locale locale) { 85bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov mLocale = locale; 866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov ViewRootImpl.addConfigCallback(this); 886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov super.initialize(text); 936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl.setText(text); 946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLegth) { 1036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 10539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 1066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 10739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 1086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 10939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!mImpl.isBoundary(start)) { 11039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = mImpl.following(start); 11139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 11239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 11339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int end = mImpl.following(start); 11639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 11739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 11839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 1296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 13139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 13239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLegth) { 13339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLegth; 1346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 13539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!mImpl.isBoundary(end)) { 13639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = mImpl.preceding(end); 13739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 13839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 13939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 14139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int start = mImpl.preceding(end); 14239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void onConfigurationChanged(Configuration newConfig) { 150bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov Locale locale = newConfig.locale; 151bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov if (!mLocale.equals(locale)) { 152bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov mLocale = locale; 1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void onLowMemory() { 1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /* ignore */ 1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getCharacterInstance(locale); 1646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class WordTextSegmentIterator extends CharacterTextSegmentIterator { 1686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static WordTextSegmentIterator sInstance; 1696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 170bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov public static WordTextSegmentIterator getInstance(Locale locale) { 1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 172bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov sInstance = new WordTextSegmentIterator(locale); 1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 1756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 177bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private WordTextSegmentIterator(Locale locale) { 178bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov super(locale); 1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getWordInstance(locale); 1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= mText.length()) { 1936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 19539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 1966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 19739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 19839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 19939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!isLetterOrDigit(start) && !isStartBoundary(start)) { 20039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = mImpl.following(start); 20139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 20239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 20539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int end = mImpl.following(start); 20639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE || !isEndBoundary(end)) { 2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 2156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 2196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 22139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 22239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLegth) { 22339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLegth; 2246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 22539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) { 22639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = mImpl.preceding(end); 22739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 22839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 23139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int start = mImpl.preceding(end); 23239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE || !isStartBoundary(start)) { 2336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 23839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isStartBoundary(int index) { 23939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return isLetterOrDigit(index) 24039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == 0 || !isLetterOrDigit(index - 1)); 24139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 24239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 24339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isEndBoundary(int index) { 24439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (index > 0 && isLetterOrDigit(index - 1)) 24539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == mText.length() || !isLetterOrDigit(index)); 24639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 24739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private boolean isLetterOrDigit(int index) { 2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (index >= 0 && index < mText.length()) { 2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int codePoint = mText.codePointAt(index); 2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return Character.isLetterOrDigit(codePoint); 2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return false; 2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator { 2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static ParagraphTextSegmentIterator sInstance; 2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static ParagraphTextSegmentIterator getInstance() { 2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov sInstance = new ParagraphTextSegmentIterator(); 2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLength) { 2746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 27639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 2776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 27839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 2796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 28039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (start < textLength && mText.charAt(start) == '\n' 28139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && !isStartBoundary(start)) { 2829768832da837e7716555d394bd13fb879a223860alanv start++; 2839768832da837e7716555d394bd13fb879a223860alanv } 28439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start >= textLength) { 28539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 28739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = start + 1; 28839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (end < textLength && !isEndBoundary(end)) { 2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end++; 2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 3016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 30339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 30439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLength) { 30539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLength; 30639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 30739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) { 30839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end--; 3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end <= 0) { 3116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 31339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = end - 1; 31439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (start > 0 && !isStartBoundary(start)) { 3156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start--; 3166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 3186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 31939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 32039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isStartBoundary(int index) { 32139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (mText.charAt(index) != '\n' 32239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == 0 || mText.charAt(index - 1) == '\n')); 32339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 32439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 32539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isEndBoundary(int index) { 32639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (index > 0 && mText.charAt(index - 1) != '\n' 32739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == mText.length() || mText.charAt(index) == '\n')); 32839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 3296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov} 331