1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.widget; 18 19 import android.graphics.Rect; 20 import android.text.Layout; 21 import android.text.Spannable; 22 import android.view.AccessibilityIterators.AbstractTextSegmentIterator; 23 24 /** 25 * This class contains the implementation of text segment iterators 26 * for accessibility support. 27 */ 28 final class AccessibilityIterators { 29 30 static class LineTextSegmentIterator extends AbstractTextSegmentIterator { 31 private static LineTextSegmentIterator sLineInstance; 32 33 protected static final int DIRECTION_START = -1; 34 protected static final int DIRECTION_END = 1; 35 36 protected Layout mLayout; 37 getInstance()38 public static LineTextSegmentIterator getInstance() { 39 if (sLineInstance == null) { 40 sLineInstance = new LineTextSegmentIterator(); 41 } 42 return sLineInstance; 43 } 44 initialize(Spannable text, Layout layout)45 public void initialize(Spannable text, Layout layout) { 46 mText = text.toString(); 47 mLayout = layout; 48 } 49 50 @Override following(int offset)51 public int[] following(int offset) { 52 final int textLegth = mText.length(); 53 if (textLegth <= 0) { 54 return null; 55 } 56 if (offset >= mText.length()) { 57 return null; 58 } 59 int nextLine; 60 if (offset < 0) { 61 nextLine = mLayout.getLineForOffset(0); 62 } else { 63 final int currentLine = mLayout.getLineForOffset(offset); 64 if (getLineEdgeIndex(currentLine, DIRECTION_START) == offset) { 65 nextLine = currentLine; 66 } else { 67 nextLine = currentLine + 1; 68 } 69 } 70 if (nextLine >= mLayout.getLineCount()) { 71 return null; 72 } 73 final int start = getLineEdgeIndex(nextLine, DIRECTION_START); 74 final int end = getLineEdgeIndex(nextLine, DIRECTION_END) + 1; 75 return getRange(start, end); 76 } 77 78 @Override preceding(int offset)79 public int[] preceding(int offset) { 80 final int textLegth = mText.length(); 81 if (textLegth <= 0) { 82 return null; 83 } 84 if (offset <= 0) { 85 return null; 86 } 87 int previousLine; 88 if (offset > mText.length()) { 89 previousLine = mLayout.getLineForOffset(mText.length()); 90 } else { 91 final int currentLine = mLayout.getLineForOffset(offset); 92 if (getLineEdgeIndex(currentLine, DIRECTION_END) + 1 == offset) { 93 previousLine = currentLine; 94 } else { 95 previousLine = currentLine - 1; 96 } 97 } 98 if (previousLine < 0) { 99 return null; 100 } 101 final int start = getLineEdgeIndex(previousLine, DIRECTION_START); 102 final int end = getLineEdgeIndex(previousLine, DIRECTION_END) + 1; 103 return getRange(start, end); 104 } 105 getLineEdgeIndex(int lineNumber, int direction)106 protected int getLineEdgeIndex(int lineNumber, int direction) { 107 final int paragraphDirection = mLayout.getParagraphDirection(lineNumber); 108 if (direction * paragraphDirection < 0) { 109 return mLayout.getLineStart(lineNumber); 110 } else { 111 return mLayout.getLineEnd(lineNumber) - 1; 112 } 113 } 114 } 115 116 static class PageTextSegmentIterator extends LineTextSegmentIterator { 117 private static PageTextSegmentIterator sPageInstance; 118 119 private TextView mView; 120 121 private final Rect mTempRect = new Rect(); 122 getInstance()123 public static PageTextSegmentIterator getInstance() { 124 if (sPageInstance == null) { 125 sPageInstance = new PageTextSegmentIterator(); 126 } 127 return sPageInstance; 128 } 129 initialize(TextView view)130 public void initialize(TextView view) { 131 super.initialize((Spannable) view.getIterableTextForAccessibility(), view.getLayout()); 132 mView = view; 133 } 134 135 @Override following(int offset)136 public int[] following(int offset) { 137 final int textLength = mText.length(); 138 if (textLength <= 0) { 139 return null; 140 } 141 if (offset >= mText.length()) { 142 return null; 143 } 144 if (!mView.getGlobalVisibleRect(mTempRect)) { 145 return null; 146 } 147 148 final int start = Math.max(0, offset); 149 150 final int currentLine = mLayout.getLineForOffset(start); 151 final int currentLineTop = mLayout.getLineTop(currentLine); 152 final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop() 153 - mView.getTotalPaddingBottom(); 154 final int nextPageStartY = currentLineTop + pageHeight; 155 final int lastLineTop = mLayout.getLineTop(mLayout.getLineCount() - 1); 156 final int currentPageEndLine = (nextPageStartY < lastLineTop) 157 ? mLayout.getLineForVertical(nextPageStartY) - 1 : mLayout.getLineCount() - 1; 158 159 final int end = getLineEdgeIndex(currentPageEndLine, DIRECTION_END) + 1; 160 161 return getRange(start, end); 162 } 163 164 @Override preceding(int offset)165 public int[] preceding(int offset) { 166 final int textLength = mText.length(); 167 if (textLength <= 0) { 168 return null; 169 } 170 if (offset <= 0) { 171 return null; 172 } 173 if (!mView.getGlobalVisibleRect(mTempRect)) { 174 return null; 175 } 176 177 final int end = Math.min(mText.length(), offset); 178 179 final int currentLine = mLayout.getLineForOffset(end); 180 final int currentLineTop = mLayout.getLineTop(currentLine); 181 final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop() 182 - mView.getTotalPaddingBottom(); 183 final int previousPageEndY = currentLineTop - pageHeight; 184 int currentPageStartLine = (previousPageEndY > 0) ? 185 mLayout.getLineForVertical(previousPageEndY) : 0; 186 // If we're at the end of text, we're at the end of the current line rather than the 187 // start of the next line, so we should move up one fewer lines than we would otherwise. 188 if (end == mText.length() && (currentPageStartLine < currentLine)) { 189 currentPageStartLine += 1; 190 } 191 192 final int start = getLineEdgeIndex(currentPageStartLine, DIRECTION_START); 193 194 return getRange(start, end); 195 } 196 } 197 } 198