AccessibilityIterators.java revision 6d17a936f73976971135aa1e6248662533343292
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 protected static final int DONE = -1; 516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected String mText; 536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private final int[] mSegment = new int[2]; 556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mText = text; 586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected int[] getRange(int start, int end) { 616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0 || end < 0 || start == end) { 626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[0] = start; 656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[1] = end; 666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return mSegment; 676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator 716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov implements ComponentCallbacks { 726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static CharacterTextSegmentIterator sInstance; 736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private final Context mAppContext; 756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected BreakIterator mImpl; 776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static CharacterTextSegmentIterator getInstance(Context context) { 796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov sInstance = new CharacterTextSegmentIterator(context); 816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private CharacterTextSegmentIterator(Context context) { 866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mAppContext = context.getApplicationContext(); 876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov Locale locale = mAppContext.getResources().getConfiguration().locale; 886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov ViewRootImpl.addConfigCallback(this); 906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov super.initialize(text); 956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl.setText(text); 966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLegth) { 1056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int start = -1; 1086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset < 0) { 1096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov offset = 0; 1106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (mImpl.isBoundary(offset)) { 1116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = offset; 1126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 1156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = mImpl.following(offset); 1166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 1186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int end = mImpl.following(start); 1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 1316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int end = -1; 1346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset > mText.length()) { 1356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov offset = mText.length(); 1366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (mImpl.isBoundary(offset)) { 1376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = offset; 1386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end < 0) { 1416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = mImpl.preceding(offset); 1426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end < 0) { 1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int start = mImpl.preceding(end); 1476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void onConfigurationChanged(Configuration newConfig) { 1526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov Configuration oldConfig = mAppContext.getResources().getConfiguration(); 1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int changed = oldConfig.diff(newConfig); 1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if ((changed & ActivityInfo.CONFIG_LOCALE) != 0) { 1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov Locale locale = newConfig.locale; 1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void onLowMemory() { 1626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /* ignore */ 1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getCharacterInstance(locale); 1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class WordTextSegmentIterator extends CharacterTextSegmentIterator { 1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static WordTextSegmentIterator sInstance; 1726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static WordTextSegmentIterator getInstance(Context context) { 1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 1756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov sInstance = new WordTextSegmentIterator(context); 1766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 1786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private WordTextSegmentIterator(Context context) { 1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov super(context); 1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getWordInstance(locale); 1876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 1916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= mText.length()) { 1966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int start = -1; 1996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset < 0) { 2006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov offset = 0; 2016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (mImpl.isBoundary(offset) && isLetterOrDigit(offset)) { 2026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = offset; 2036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 2066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov while ((offset = mImpl.following(offset)) != DONE) { 2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (isLetterOrDigit(offset)) { 2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = offset; 2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov break; 2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 2146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int end = mImpl.following(start); 2176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 2236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 2246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 2276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int end = -1; 2306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset > mText.length()) { 2316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov offset = mText.length(); 2326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (mImpl.isBoundary(offset) && offset > 0 && isLetterOrDigit(offset - 1)) { 2336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = offset; 2346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end < 0) { 2376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov while ((offset = mImpl.preceding(offset)) != DONE) { 2386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset > 0 && isLetterOrDigit(offset - 1)) { 2396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = offset; 2406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov break; 2416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end < 0) { 2456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int start = mImpl.preceding(end); 2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private boolean isLetterOrDigit(int index) { 2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (index >= 0 && index < mText.length()) { 2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int codePoint = mText.codePointAt(index); 2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return Character.isLetterOrDigit(codePoint); 2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return false; 2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator { 2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static ParagraphTextSegmentIterator sInstance; 2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static ParagraphTextSegmentIterator getInstance() { 2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov sInstance = new ParagraphTextSegmentIterator(); 2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 2686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 2726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLength) { 2776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int start = -1; 2806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset < 0) { 2816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = 0; 2826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } else { 2836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov for (int i = offset + 1; i < textLength; i++) { 2846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (mText.charAt(i) == '\n') { 2856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = i; 2866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov break; 2876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov while (start < textLength && mText.charAt(start) == '\n') { 2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start++; 2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int end = start; 2976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov for (int i = end + 1; i < textLength; i++) { 2986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = i; 2996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (mText.charAt(i) == '\n') { 3006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov break; 3016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov while (end < textLength && mText.charAt(end) == '\n') { 3046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end++; 3056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 3076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 3116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 3126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 3136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 3166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int end = -1; 3196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset > mText.length()) { 3206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = mText.length(); 3216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } else { 3226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset > 0 && mText.charAt(offset - 1) == '\n') { 3236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov offset--; 3246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov for (int i = offset - 1; i >= 0; i--) { 3266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (i > 0 && mText.charAt(i - 1) == '\n') { 3276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end = i; 3286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov break; 3296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end <= 0) { 3336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov int start = end; 3366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov while (start > 0 && mText.charAt(start - 1) == '\n') { 3376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start--; 3386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start == 0 && mText.charAt(start) == '\n') { 3406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov for (int i = start - 1; i >= 0; i--) { 3436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = i; 3446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start > 0 && mText.charAt(i - 1) == '\n') { 3456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov break; 3466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start = Math.max(0, start); 3496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 3506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov} 353