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.res.Configuration; 206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 216d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.text.BreakIterator; 226d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.util.Locale; 236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/** 256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * This class contains the implementation of text segment iterators 266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * for accessibility support. 276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Note: Such iterators are needed in the view package since we want 296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * to be able to iterator over content description of any view. 306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 336d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpublic final class AccessibilityIterators { 346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /** 366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static interface TextSegmentIterator { 396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int current); 406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int current); 416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /** 446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator { 476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected String mText; 496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private final int[] mSegment = new int[2]; 516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mText = text; 546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected int[] getRange(int start, int end) { 576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0 || end < 0 || start == end) { 586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[0] = start; 616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[1] = end; 626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return mSegment; 636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator 67446079600ece83b22cb91865bcbeb694292b0108Andrii Kulian implements ViewRootImpl.ConfigChangedCallback { 686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static CharacterTextSegmentIterator sInstance; 696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 70bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private Locale mLocale; 716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected BreakIterator mImpl; 736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 74bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov public static CharacterTextSegmentIterator getInstance(Locale locale) { 756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 76bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov sInstance = new CharacterTextSegmentIterator(locale); 776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 81bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private CharacterTextSegmentIterator(Locale locale) { 82bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov mLocale = locale; 836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov ViewRootImpl.addConfigCallback(this); 856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov super.initialize(text); 906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl.setText(text); 916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLegth) { 1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 10239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 1036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 10439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 1056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 10639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!mImpl.isBoundary(start)) { 10739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = mImpl.following(start); 10839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 10939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 11039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int end = mImpl.following(start); 11339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 11439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 11539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 12839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 12939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLegth) { 13039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLegth; 1316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 13239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!mImpl.isBoundary(end)) { 13339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = mImpl.preceding(end); 13439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 13539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 13639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 13839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int start = mImpl.preceding(end); 13939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 1406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 146446079600ece83b22cb91865bcbeb694292b0108Andrii Kulian public void onConfigurationChanged(Configuration globalConfig) { 147446079600ece83b22cb91865bcbeb694292b0108Andrii Kulian final Locale locale = globalConfig.getLocales().get(0); 148bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov if (!mLocale.equals(locale)) { 149bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov mLocale = locale; 1506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 1516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getCharacterInstance(locale); 1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class WordTextSegmentIterator extends CharacterTextSegmentIterator { 1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static WordTextSegmentIterator sInstance; 1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 162bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov public static WordTextSegmentIterator getInstance(Locale locale) { 1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 164bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov sInstance = new WordTextSegmentIterator(locale); 1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 169bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private WordTextSegmentIterator(Locale locale) { 170bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov super(locale); 1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getWordInstance(locale); 1766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= mText.length()) { 1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 18739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 18939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 19039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 19139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!isLetterOrDigit(start) && !isStartBoundary(start)) { 19239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = mImpl.following(start); 19339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 19439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 1956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 19739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int end = mImpl.following(start); 19839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE || !isEndBoundary(end)) { 1996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 21339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 21439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLegth) { 21539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLegth; 2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 21739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) { 21839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = mImpl.preceding(end); 21939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 22039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 22339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int start = mImpl.preceding(end); 22439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE || !isStartBoundary(start)) { 2256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 23039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isStartBoundary(int index) { 23139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return isLetterOrDigit(index) 23239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == 0 || !isLetterOrDigit(index - 1)); 23339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 23439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 23539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isEndBoundary(int index) { 23639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (index > 0 && isLetterOrDigit(index - 1)) 23739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == mText.length() || !isLetterOrDigit(index)); 23839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 23939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 2406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private boolean isLetterOrDigit(int index) { 2416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (index >= 0 && index < mText.length()) { 2426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int codePoint = mText.codePointAt(index); 2436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return Character.isLetterOrDigit(codePoint); 2446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return false; 2466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator { 2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static ParagraphTextSegmentIterator sInstance; 2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static ParagraphTextSegmentIterator getInstance() { 2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov sInstance = new ParagraphTextSegmentIterator(); 2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLength) { 2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 26839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 27039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 27239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (start < textLength && mText.charAt(start) == '\n' 27339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && !isStartBoundary(start)) { 2749768832da837e7716555d394bd13fb879a223860alanv start++; 2759768832da837e7716555d394bd13fb879a223860alanv } 27639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start >= textLength) { 27739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 27939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = start + 1; 28039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (end < textLength && !isEndBoundary(end)) { 2816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end++; 2826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 29539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 29639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLength) { 29739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLength; 29839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 29939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) { 30039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end--; 3016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end <= 0) { 3036d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 30539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = end - 1; 30639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (start > 0 && !isStartBoundary(start)) { 3076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start--; 3086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 31139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 31239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isStartBoundary(int index) { 31339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (mText.charAt(index) != '\n' 31439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == 0 || mText.charAt(index - 1) == '\n')); 31539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 31639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 31739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isEndBoundary(int index) { 31839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (index > 0 && mText.charAt(index - 1) != '\n' 31939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == mText.length() || mText.charAt(index) == '\n')); 32039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 3216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov} 323