content: /*
   * 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);
          }
      }
  }
  