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