WordComposer.java revision c83359f9746ca6f0269a1a7017b585c1a5cab9b8
1/* 2 * Copyright (C) 2008 The Android Open Source Project 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 17package com.android.inputmethod.latin; 18 19import com.android.inputmethod.keyboard.Keyboard; 20import com.android.inputmethod.keyboard.KeyDetector; 21 22import java.util.ArrayList; 23import java.util.Arrays; 24 25/** 26 * A place to store the currently composing word with information such as adjacent key codes as well 27 */ 28public class WordComposer { 29 30 public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE; 31 public static final int NOT_A_COORDINATE = -1; 32 33 /** 34 * The list of unicode values for each keystroke (including surrounding keys) 35 */ 36 private ArrayList<int[]> mCodes; 37 38 private int[] mXCoordinates; 39 private int[] mYCoordinates; 40 41 private StringBuilder mTypedWord; 42 43 private int mCapsCount; 44 45 private boolean mAutoCapitalized; 46 // Cache this value for performance 47 private boolean mIsLastCharASingleQuote; 48 49 /** 50 * Whether the user chose to capitalize the first char of the word. 51 */ 52 private boolean mIsFirstCharCapitalized; 53 54 public WordComposer() { 55 final int N = BinaryDictionary.MAX_WORD_LENGTH; 56 mCodes = new ArrayList<int[]>(N); 57 mTypedWord = new StringBuilder(N); 58 mXCoordinates = new int[N]; 59 mYCoordinates = new int[N]; 60 mIsLastCharASingleQuote = false; 61 } 62 63 public WordComposer(WordComposer source) { 64 init(source); 65 } 66 67 public void init(WordComposer source) { 68 mCodes = new ArrayList<int[]>(source.mCodes); 69 mTypedWord = new StringBuilder(source.mTypedWord); 70 mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length); 71 mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length); 72 mCapsCount = source.mCapsCount; 73 mIsFirstCharCapitalized = source.mIsFirstCharCapitalized; 74 mAutoCapitalized = source.mAutoCapitalized; 75 mIsLastCharASingleQuote = source.mIsLastCharASingleQuote; 76 } 77 78 /** 79 * Clear out the keys registered so far. 80 */ 81 public void reset() { 82 mCodes.clear(); 83 mTypedWord.setLength(0); 84 mCapsCount = 0; 85 mIsFirstCharCapitalized = false; 86 mIsLastCharASingleQuote = false; 87 } 88 89 /** 90 * Number of keystrokes in the composing word. 91 * @return the number of keystrokes 92 */ 93 public final int size() { 94 return mTypedWord.length(); 95 } 96 97 /** 98 * Returns the codes at a particular position in the word. 99 * @param index the position in the word 100 * @return the unicode for the pressed and surrounding keys 101 */ 102 public int[] getCodesAt(int index) { 103 return mCodes.get(index); 104 } 105 106 public int[] getXCoordinates() { 107 return mXCoordinates; 108 } 109 110 public int[] getYCoordinates() { 111 return mYCoordinates; 112 } 113 114 private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) { 115 if (index == 0) return Character.isUpperCase(codePoint); 116 return previous && !Character.isUpperCase(codePoint); 117 } 118 119 /** 120 * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of 121 * the array containing unicode for adjacent keys, sorted by reducing probability/proximity. 122 * @param codes the array of unicode values 123 */ 124 public void add(int primaryCode, int[] codes, int x, int y) { 125 final int newIndex = size(); 126 mTypedWord.append((char) primaryCode); 127 correctPrimaryJuxtapos(primaryCode, codes); 128 mCodes.add(codes); 129 if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { 130 mXCoordinates[newIndex] = x; 131 mYCoordinates[newIndex] = y; 132 } 133 mIsFirstCharCapitalized = isFirstCharCapitalized( 134 newIndex, primaryCode, mIsFirstCharCapitalized); 135 if (Character.isUpperCase(primaryCode)) mCapsCount++; 136 mIsLastCharASingleQuote = Keyboard.CODE_SINGLE_QUOTE == primaryCode; 137 } 138 139 /** 140 * Swaps the first and second values in the codes array if the primary code is not the first 141 * value in the array but the second. This happens when the preferred key is not the key that 142 * the user released the finger on. 143 * @param primaryCode the preferred character 144 * @param codes array of codes based on distance from touch point 145 */ 146 private static void correctPrimaryJuxtapos(int primaryCode, int[] codes) { 147 if (codes.length < 2) return; 148 if (codes[0] > 0 && codes[1] > 0 && codes[0] != primaryCode && codes[1] == primaryCode) { 149 codes[1] = codes[0]; 150 codes[0] = primaryCode; 151 } 152 } 153 154 /** 155 * Delete the last keystroke as a result of hitting backspace. 156 */ 157 public void deleteLast() { 158 final int size = size(); 159 if (size > 0) { 160 final int lastPos = size - 1; 161 char lastChar = mTypedWord.charAt(lastPos); 162 mCodes.remove(lastPos); 163 mTypedWord.deleteCharAt(lastPos); 164 if (Character.isUpperCase(lastChar)) mCapsCount--; 165 } 166 if (size() == 0) { 167 mIsFirstCharCapitalized = false; 168 mIsLastCharASingleQuote = false; 169 } else { 170 mIsLastCharASingleQuote = 171 Keyboard.CODE_SINGLE_QUOTE == mTypedWord.codePointAt(mTypedWord.length() - 1); 172 } 173 } 174 175 /** 176 * Returns the word as it was typed, without any correction applied. 177 * @return the word that was typed so far 178 */ 179 public String getTypedWord() { 180 if (size() == 0) { 181 return null; 182 } 183 return mTypedWord.toString(); 184 } 185 186 /** 187 * Whether or not the user typed a capital letter as the first letter in the word 188 * @return capitalization preference 189 */ 190 public boolean isFirstCharCapitalized() { 191 return mIsFirstCharCapitalized; 192 } 193 194 public boolean isLastCharASingleQuote() { 195 return mIsLastCharASingleQuote; 196 } 197 198 /** 199 * Whether or not all of the user typed chars are upper case 200 * @return true if all user typed chars are upper case, false otherwise 201 */ 202 public boolean isAllUpperCase() { 203 return (mCapsCount > 0) && (mCapsCount == size()); 204 } 205 206 /** 207 * Returns true if more than one character is upper case, otherwise returns false. 208 */ 209 public boolean isMostlyCaps() { 210 return mCapsCount > 1; 211 } 212 213 /** 214 * Saves the reason why the word is capitalized - whether it was automatic or 215 * due to the user hitting shift in the middle of a sentence. 216 * @param auto whether it was an automatic capitalization due to start of sentence 217 */ 218 public void setAutoCapitalized(boolean auto) { 219 mAutoCapitalized = auto; 220 } 221 222 /** 223 * Returns whether the word was automatically capitalized. 224 * @return whether the word was automatically capitalized 225 */ 226 public boolean isAutoCapitalized() { 227 return mAutoCapitalized; 228 } 229} 230