Home | History | Annotate | Download | only in voice
      1 /*
      2  * Copyright (C) 2009 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.voice;
     18 
     19 import android.view.inputmethod.ExtractedText;
     20 import android.view.inputmethod.ExtractedTextRequest;
     21 import android.view.inputmethod.InputConnection;
     22 
     23 /**
     24  * Utility methods to deal with editing text through an InputConnection.
     25  */
     26 public class EditingUtil {
     27     private EditingUtil() {};
     28 
     29     /**
     30      * Append newText to the text field represented by connection.
     31      * The new text becomes selected.
     32      */
     33     public static void appendText(InputConnection connection, String newText) {
     34         if (connection == null) {
     35             return;
     36         }
     37 
     38         // Commit the composing text
     39         connection.finishComposingText();
     40 
     41         // Add a space if the field already has text.
     42         CharSequence charBeforeCursor = connection.getTextBeforeCursor(1, 0);
     43         if (charBeforeCursor != null
     44                 && !charBeforeCursor.equals(" ")
     45                 && (charBeforeCursor.length() > 0)) {
     46             newText = " " + newText;
     47         }
     48 
     49         connection.setComposingText(newText, 1);
     50     }
     51 
     52     private static int getCursorPosition(InputConnection connection) {
     53         ExtractedText extracted = connection.getExtractedText(
     54             new ExtractedTextRequest(), 0);
     55         if (extracted == null) {
     56           return -1;
     57         }
     58         return extracted.startOffset + extracted.selectionStart;
     59     }
     60 
     61     private static int getSelectionEnd(InputConnection connection) {
     62         ExtractedText extracted = connection.getExtractedText(
     63             new ExtractedTextRequest(), 0);
     64         if (extracted == null) {
     65           return -1;
     66         }
     67         return extracted.startOffset + extracted.selectionEnd;
     68     }
     69 
     70     /**
     71      * @param connection connection to the current text field.
     72      * @param sep characters which may separate words
     73      * @return the word that surrounds the cursor, including up to one trailing
     74      *   separator. For example, if the field contains "he|llo world", where |
     75      *   represents the cursor, then "hello " will be returned.
     76      */
     77     public static String getWordAtCursor(
     78         InputConnection connection, String separators) {
     79         Range range = getWordRangeAtCursor(connection, separators);
     80         return (range == null) ? null : range.word;
     81     }
     82 
     83     /**
     84      * Removes the word surrounding the cursor. Parameters are identical to
     85      * getWordAtCursor.
     86      */
     87     public static void deleteWordAtCursor(
     88         InputConnection connection, String separators) {
     89 
     90         Range range = getWordRangeAtCursor(connection, separators);
     91         if (range == null) return;
     92 
     93         connection.finishComposingText();
     94         // Move cursor to beginning of word, to avoid crash when cursor is outside
     95         // of valid range after deleting text.
     96         int newCursor = getCursorPosition(connection) - range.charsBefore;
     97         connection.setSelection(newCursor, newCursor);
     98         connection.deleteSurroundingText(0, range.charsBefore + range.charsAfter);
     99     }
    100 
    101     /**
    102      * Represents a range of text, relative to the current cursor position.
    103      */
    104     private static class Range {
    105         /** Characters before selection start */
    106         int charsBefore;
    107 
    108         /**
    109          * Characters after selection start, including one trailing word
    110          * separator.
    111          */
    112         int charsAfter;
    113 
    114         /** The actual characters that make up a word */
    115         String word;
    116 
    117         public Range(int charsBefore, int charsAfter, String word) {
    118             if (charsBefore < 0 || charsAfter < 0) {
    119                 throw new IndexOutOfBoundsException();
    120             }
    121             this.charsBefore = charsBefore;
    122             this.charsAfter = charsAfter;
    123             this.word = word;
    124         }
    125     }
    126 
    127     private static Range getWordRangeAtCursor(
    128         InputConnection connection, String sep) {
    129         if (connection == null || sep == null) {
    130             return null;
    131         }
    132         CharSequence before = connection.getTextBeforeCursor(1000, 0);
    133         CharSequence after = connection.getTextAfterCursor(1000, 0);
    134         if (before == null || after == null) {
    135             return null;
    136         }
    137 
    138         // Find first word separator before the cursor
    139         int start = before.length();
    140         while (--start > 0 && !isWhitespace(before.charAt(start - 1), sep));
    141 
    142         // Find last word separator after the cursor
    143         int end = -1;
    144         while (++end < after.length() && !isWhitespace(after.charAt(end), sep));
    145         if (end < after.length() - 1) {
    146             end++; // Include trailing space, if it exists, in word
    147         }
    148 
    149         int cursor = getCursorPosition(connection);
    150         if (start >= 0 && cursor + end <= after.length() + before.length()) {
    151             String word = before.toString().substring(start, before.length())
    152                 + after.toString().substring(0, end);
    153             return new Range(before.length() - start, end, word);
    154         }
    155 
    156         return null;
    157     }
    158 
    159     private static boolean isWhitespace(int code, String whitespace) {
    160         return whitespace.contains(String.valueOf((char) code));
    161     }
    162 }
    163