Home | History | Annotate | Download | only in widget
      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 
     38         public static LineTextSegmentIterator getInstance() {
     39             if (sLineInstance == null) {
     40                 sLineInstance = new LineTextSegmentIterator();
     41             }
     42             return sLineInstance;
     43         }
     44 
     45         public void initialize(Spannable text, Layout layout) {
     46             mText = text.toString();
     47             mLayout = layout;
     48         }
     49 
     50         @Override
     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
     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 
    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 
    123         public static PageTextSegmentIterator getInstance() {
    124             if (sPageInstance == null) {
    125                 sPageInstance = new PageTextSegmentIterator();
    126             }
    127             return sPageInstance;
    128         }
    129 
    130         public void initialize(TextView view) {
    131             super.initialize((Spannable) view.getIterableTextForAccessibility(), view.getLayout());
    132             mView = view;
    133         }
    134 
    135         @Override
    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
    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