blob: 442ffa136ca32d2b94f9df32921d6c02947e7ecd [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.widget;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Spannable;
import android.view.AccessibilityIterators.AbstractTextSegmentIterator;
/**
* This class contains the implementation of text segment iterators
* for accessibility support.
*/
final class AccessibilityIterators {
static class LineTextSegmentIterator extends AbstractTextSegmentIterator {
private static LineTextSegmentIterator sLineInstance;
protected static final int DIRECTION_START = -1;
protected static final int DIRECTION_END = 1;
protected Layout mLayout;
public static LineTextSegmentIterator getInstance() {
if (sLineInstance == null) {
sLineInstance = new LineTextSegmentIterator();
}
return sLineInstance;
}
public void initialize(Spannable text, Layout layout) {
mText = text.toString();
mLayout = layout;
}
@Override
public int[] following(int offset) {
final int textLegth = mText.length();
if (textLegth <= 0) {
return null;
}
if (offset >= mText.length()) {
return null;
}
int nextLine;
if (offset < 0) {
nextLine = mLayout.getLineForOffset(0);
} else {
final int currentLine = mLayout.getLineForOffset(offset);
if (getLineEdgeIndex(currentLine, DIRECTION_START) == offset) {
nextLine = currentLine;
} else {
nextLine = currentLine + 1;
}
}
if (nextLine >= mLayout.getLineCount()) {
return null;
}
final int start = getLineEdgeIndex(nextLine, DIRECTION_START);
final int end = getLineEdgeIndex(nextLine, DIRECTION_END) + 1;
return getRange(start, end);
}
@Override
public int[] preceding(int offset) {
final int textLegth = mText.length();
if (textLegth <= 0) {
return null;
}
if (offset <= 0) {
return null;
}
int previousLine;
if (offset > mText.length()) {
previousLine = mLayout.getLineForOffset(mText.length());
} else {
final int currentLine = mLayout.getLineForOffset(offset);
if (getLineEdgeIndex(currentLine, DIRECTION_END) + 1 == offset) {
previousLine = currentLine;
} else {
previousLine = currentLine - 1;
}
}
if (previousLine < 0) {
return null;
}
final int start = getLineEdgeIndex(previousLine, DIRECTION_START);
final int end = getLineEdgeIndex(previousLine, DIRECTION_END) + 1;
return getRange(start, end);
}
protected int getLineEdgeIndex(int lineNumber, int direction) {
final int paragraphDirection = mLayout.getParagraphDirection(lineNumber);
if (direction * paragraphDirection < 0) {
return mLayout.getLineStart(lineNumber);
} else {
return mLayout.getLineEnd(lineNumber) - 1;
}
}
}
static class PageTextSegmentIterator extends LineTextSegmentIterator {
private static PageTextSegmentIterator sPageInstance;
private TextView mView;
private final Rect mTempRect = new Rect();
public static PageTextSegmentIterator getInstance() {
if (sPageInstance == null) {
sPageInstance = new PageTextSegmentIterator();
}
return sPageInstance;
}
public void initialize(TextView view) {
super.initialize((Spannable) view.getIterableTextForAccessibility(), view.getLayout());
mView = view;
}
@Override
public int[] following(int offset) {
final int textLength = mText.length();
if (textLength <= 0) {
return null;
}
if (offset >= mText.length()) {
return null;
}
if (!mView.getGlobalVisibleRect(mTempRect)) {
return null;
}
final int start = Math.max(0, offset);
final int currentLine = mLayout.getLineForOffset(start);
final int currentLineTop = mLayout.getLineTop(currentLine);
final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
- mView.getTotalPaddingBottom();
final int nextPageStartY = currentLineTop + pageHeight;
final int lastLineTop = mLayout.getLineTop(mLayout.getLineCount() - 1);
final int currentPageEndLine = (nextPageStartY < lastLineTop)
? mLayout.getLineForVertical(nextPageStartY) - 1 : mLayout.getLineCount() - 1;
final int end = getLineEdgeIndex(currentPageEndLine, DIRECTION_END) + 1;
return getRange(start, end);
}
@Override
public int[] preceding(int offset) {
final int textLength = mText.length();
if (textLength <= 0) {
return null;
}
if (offset <= 0) {
return null;
}
if (!mView.getGlobalVisibleRect(mTempRect)) {
return null;
}
final int end = Math.min(mText.length(), offset);
final int currentLine = mLayout.getLineForOffset(end);
final int currentLineTop = mLayout.getLineTop(currentLine);
final int pageHeight = mTempRect.height() - mView.getTotalPaddingTop()
- mView.getTotalPaddingBottom();
final int previousPageEndY = currentLineTop - pageHeight;
int currentPageStartLine = (previousPageEndY > 0) ?
mLayout.getLineForVertical(previousPageEndY) : 0;
// If we're at the end of text, we're at the end of the current line rather than the
// start of the next line, so we should move up one fewer lines than we would otherwise.
if (end == mText.length() && (currentPageStartLine < currentLine)) {
currentPageStartLine += 1;
}
final int start = getLineEdgeIndex(currentPageStartLine, DIRECTION_START);
return getRange(start, end);
}
}
}