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.res.Configuration; 216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 226d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.text.BreakIterator; 236d17a936f73976971135aa1e6248662533343292Svetoslav Ganovimport java.util.Locale; 246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov/** 266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * This class contains the implementation of text segment iterators 276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * for accessibility support. 286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 296d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * Note: Such iterators are needed in the view package since we want 306d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * to be able to iterator over content description of any view. 316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * 326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 346d17a936f73976971135aa1e6248662533343292Svetoslav Ganovpublic final class AccessibilityIterators { 356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 366d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /** 376d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 396d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static interface TextSegmentIterator { 406d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int current); 416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int current); 426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /** 456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov * @hide 466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov */ 476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static abstract class AbstractTextSegmentIterator implements TextSegmentIterator { 486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected String mText; 506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private final int[] mSegment = new int[2]; 526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mText = text; 556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected int[] getRange(int start, int end) { 586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0 || end < 0 || start == end) { 596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[0] = start; 626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mSegment[1] = end; 636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return mSegment; 646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator 686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov implements ComponentCallbacks { 696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static CharacterTextSegmentIterator sInstance; 706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 71bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private Locale mLocale; 726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected BreakIterator mImpl; 746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 75bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov public static CharacterTextSegmentIterator getInstance(Locale locale) { 766d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 77bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov sInstance = new CharacterTextSegmentIterator(locale); 786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 82bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private CharacterTextSegmentIterator(Locale locale) { 83bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov mLocale = locale; 846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov ViewRootImpl.addConfigCallback(this); 866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void initialize(String text) { 906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov super.initialize(text); 916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl.setText(text); 926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLegth) { 1016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 10339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 1046d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 10539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 1066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 10739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!mImpl.isBoundary(start)) { 10839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = mImpl.following(start); 10939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 11039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 11139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int end = mImpl.following(start); 11439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 11539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 11639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1196d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1206d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1216d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 1226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1236d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1246d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1256d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1266d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 1276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 12939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 13039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLegth) { 13139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLegth; 1326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 13339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!mImpl.isBoundary(end)) { 13439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = mImpl.preceding(end); 13539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 13639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 13739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 1386d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 13939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int start = mImpl.preceding(end); 14039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 1416d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1426d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1436d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 1446d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1456d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void onConfigurationChanged(Configuration newConfig) { 148bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov Locale locale = newConfig.locale; 149bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov if (!mLocale.equals(locale)) { 150bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov mLocale = locale; 1516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov onLocaleChanged(locale); 1526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public void onLowMemory() { 1576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov /* ignore */ 1586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getCharacterInstance(locale); 1626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class WordTextSegmentIterator extends CharacterTextSegmentIterator { 1666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static WordTextSegmentIterator sInstance; 1676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 168bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov public static WordTextSegmentIterator getInstance(Locale locale) { 1696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 170bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov sInstance = new WordTextSegmentIterator(locale); 1716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 1736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1746d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 175bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov private WordTextSegmentIterator(Locale locale) { 176bbd31559f32f86a100904fe8a5bc37677b5ba441Svetoslav Ganov super(locale); 1776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1786d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1796d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1806d17a936f73976971135aa1e6248662533343292Svetoslav Ganov protected void onLocaleChanged(Locale locale) { 1816d17a936f73976971135aa1e6248662533343292Svetoslav Ganov mImpl = BreakIterator.getWordInstance(locale); 1826d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1836d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 1846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 1856d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 1866d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 1876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 1886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 1906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= mText.length()) { 1916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 1926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 19339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 1946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 19539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 19639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 19739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (!isLetterOrDigit(start) && !isStartBoundary(start)) { 19839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = mImpl.following(start); 19939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE) { 20039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2016d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2026d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 20339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int end = mImpl.following(start); 20439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE || !isEndBoundary(end)) { 2056d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2066d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2116d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2126d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLegth = mText.length(); 2136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLegth <= 0) { 2146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 2176d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2186d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 21939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 22039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLegth) { 22139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLegth; 2226d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 22339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (end > 0 && !isLetterOrDigit(end - 1) && !isEndBoundary(end)) { 22439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = mImpl.preceding(end); 22539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end == BreakIterator.DONE) { 22639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 22939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov final int start = mImpl.preceding(end); 23039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start == BreakIterator.DONE || !isStartBoundary(start)) { 2316d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2326d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2336d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2346d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2356d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 23639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isStartBoundary(int index) { 23739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return isLetterOrDigit(index) 23839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == 0 || !isLetterOrDigit(index - 1)); 23939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 24039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 24139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isEndBoundary(int index) { 24239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (index > 0 && isLetterOrDigit(index - 1)) 24339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == mText.length() || !isLetterOrDigit(index)); 24439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 24539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 2466d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private boolean isLetterOrDigit(int index) { 2476d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (index >= 0 && index < mText.length()) { 2486d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int codePoint = mText.codePointAt(index); 2496d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return Character.isLetterOrDigit(codePoint); 2506d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2516d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return false; 2526d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2536d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2546d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2556d17a936f73976971135aa1e6248662533343292Svetoslav Ganov static class ParagraphTextSegmentIterator extends AbstractTextSegmentIterator { 2566d17a936f73976971135aa1e6248662533343292Svetoslav Ganov private static ParagraphTextSegmentIterator sInstance; 2576d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2586d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public static ParagraphTextSegmentIterator getInstance() { 2596d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (sInstance == null) { 2606d17a936f73976971135aa1e6248662533343292Svetoslav Ganov sInstance = new ParagraphTextSegmentIterator(); 2616d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2626d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return sInstance; 2636d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2646d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2656d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2666d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] following(int offset) { 2676d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2686d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2696d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2706d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2716d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset >= textLength) { 2726d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2736d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 27439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = offset; 2756d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (start < 0) { 27639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov start = 0; 2776d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 27839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (start < textLength && mText.charAt(start) == '\n' 27939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && !isStartBoundary(start)) { 2809768832da837e7716555d394bd13fb879a223860alanv start++; 2819768832da837e7716555d394bd13fb879a223860alanv } 28239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (start >= textLength) { 28339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return null; 2846d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 28539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = start + 1; 28639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (end < textLength && !isEndBoundary(end)) { 2876d17a936f73976971135aa1e6248662533343292Svetoslav Ganov end++; 2886d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2896d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 2906d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2916d17a936f73976971135aa1e6248662533343292Svetoslav Ganov 2926d17a936f73976971135aa1e6248662533343292Svetoslav Ganov @Override 2936d17a936f73976971135aa1e6248662533343292Svetoslav Ganov public int[] preceding(int offset) { 2946d17a936f73976971135aa1e6248662533343292Svetoslav Ganov final int textLength = mText.length(); 2956d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (textLength <= 0) { 2966d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 2976d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 2986d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (offset <= 0) { 2996d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3006d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 30139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int end = offset; 30239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov if (end > textLength) { 30339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end = textLength; 30439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 30539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while(end > 0 && mText.charAt(end - 1) == '\n' && !isEndBoundary(end)) { 30639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov end--; 3076d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3086d17a936f73976971135aa1e6248662533343292Svetoslav Ganov if (end <= 0) { 3096d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return null; 3106d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 31139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov int start = end - 1; 31239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov while (start > 0 && !isStartBoundary(start)) { 3136d17a936f73976971135aa1e6248662533343292Svetoslav Ganov start--; 3146d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3156d17a936f73976971135aa1e6248662533343292Svetoslav Ganov return getRange(start, end); 3166d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 31739f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 31839f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isStartBoundary(int index) { 31939f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (mText.charAt(index) != '\n' 32039f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == 0 || mText.charAt(index - 1) == '\n')); 32139f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 32239f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov 32339f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov private boolean isEndBoundary(int index) { 32439f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov return (index > 0 && mText.charAt(index - 1) != '\n' 32539f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov && (index == mText.length() || mText.charAt(index) == '\n')); 32639f2aee640eea62b43fa79f28dec3a962e5cb065Svetoslav Ganov } 3276d17a936f73976971135aa1e6248662533343292Svetoslav Ganov } 3286d17a936f73976971135aa1e6248662533343292Svetoslav Ganov} 329