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 com.android.inputmethod.event; 18 19 import com.android.inputmethod.latin.Constants; 20 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 21 import com.android.inputmethod.latin.utils.StringUtils; 22 23 /** 24 * Class representing a generic input event as handled by Latin IME. 25 * 26 * This contains information about the origin of the event, but it is generalized and should 27 * represent a software keypress, hardware keypress, or d-pad move alike. 28 * Very importantly, this does not necessarily result in inputting one character, or even anything 29 * at all - it may be a dead key, it may be a partial input, it may be a special key on the 30 * keyboard, it may be a cancellation of a keypress (e.g. in a soft keyboard the finger of the 31 * user has slid out of the key), etc. It may also be a batch input from a gesture or handwriting 32 * for example. 33 * The combiner should figure out what to do with this. 34 */ 35 public class Event { 36 // Should the types below be represented by separate classes instead? It would be cleaner 37 // but probably a bit too much 38 // An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard. 39 final public static int EVENT_TYPE_NOT_HANDLED = 0; 40 // A key press that is part of input, for example pressing an alphabetic character on a 41 // hardware qwerty keyboard. It may be part of a sequence that will be re-interpreted later 42 // through combination. 43 final public static int EVENT_TYPE_INPUT_KEYPRESS = 1; 44 // A toggle event is triggered by a key that affects the previous character. An example would 45 // be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with 46 // repeated presses. 47 final public static int EVENT_TYPE_TOGGLE = 2; 48 // A mode event instructs the combiner to change modes. The canonical example would be the 49 // hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard 50 // if handled at the combiner level. 51 final public static int EVENT_TYPE_MODE_KEY = 3; 52 // An event corresponding to a gesture. 53 final public static int EVENT_TYPE_GESTURE = 4; 54 // An event corresponding to the manual pick of a suggestion. 55 final public static int EVENT_TYPE_SUGGESTION_PICKED = 5; 56 // An event corresponding to a string generated by some software process. 57 final public static int EVENT_TYPE_SOFTWARE_GENERATED_STRING = 6; 58 59 // 0 is a valid code point, so we use -1 here. 60 final public static int NOT_A_CODE_POINT = -1; 61 // -1 is a valid key code, so we use 0 here. 62 final public static int NOT_A_KEY_CODE = 0; 63 64 final private static int FLAG_NONE = 0; 65 // This event is a dead character, usually input by a dead key. Examples include dead-acute 66 // or dead-abovering. 67 final private static int FLAG_DEAD = 0x1; 68 // This event is coming from a key repeat, software or hardware. 69 final private static int FLAG_REPEAT = 0x2; 70 // This event has already been consumed. 71 final private static int FLAG_CONSUMED = 0x4; 72 73 final private int mEventType; // The type of event - one of the constants above 74 // The code point associated with the event, if relevant. This is a unicode code point, and 75 // has nothing to do with other representations of the key. It is only relevant if this event 76 // is of KEYPRESS type, but for a mode key like hankaku/zenkaku or ctrl, there is no code point 77 // associated so this should be NOT_A_CODE_POINT to avoid unintentional use of its value when 78 // it's not relevant. 79 final public int mCodePoint; 80 81 // If applicable, this contains the string that should be input. 82 final public CharSequence mText; 83 84 // The key code associated with the event, if relevant. This is relevant whenever this event 85 // has been triggered by a key press, but not for a gesture for example. This has conceptually 86 // no link to the code point, although keys that enter a straight code point may often set 87 // this to be equal to mCodePoint for convenience. If this is not a key, this must contain 88 // NOT_A_KEY_CODE. 89 final public int mKeyCode; 90 91 // Coordinates of the touch event, if relevant. If useful, we may want to replace this with 92 // a MotionEvent or something in the future. This is only relevant when the keypress is from 93 // a software keyboard obviously, unless there are touch-sensitive hardware keyboards in the 94 // future or some other awesome sauce. 95 final public int mX; 96 final public int mY; 97 98 // Some flags that can't go into the key code. It's a bit field of FLAG_* 99 final private int mFlags; 100 101 // If this is of type EVENT_TYPE_SUGGESTION_PICKED, this must not be null (and must be null in 102 // other cases). 103 final public SuggestedWordInfo mSuggestedWordInfo; 104 105 // The next event, if any. Null if there is no next event yet. 106 final public Event mNextEvent; 107 108 // This method is private - to create a new event, use one of the create* utility methods. Event(final int type, final CharSequence text, final int codePoint, final int keyCode, final int x, final int y, final SuggestedWordInfo suggestedWordInfo, final int flags, final Event next)109 private Event(final int type, final CharSequence text, final int codePoint, final int keyCode, 110 final int x, final int y, final SuggestedWordInfo suggestedWordInfo, final int flags, 111 final Event next) { 112 mEventType = type; 113 mText = text; 114 mCodePoint = codePoint; 115 mKeyCode = keyCode; 116 mX = x; 117 mY = y; 118 mSuggestedWordInfo = suggestedWordInfo; 119 mFlags = flags; 120 mNextEvent = next; 121 // Sanity checks 122 // mSuggestedWordInfo is non-null if and only if the type is SUGGESTION_PICKED 123 if (EVENT_TYPE_SUGGESTION_PICKED == mEventType) { 124 if (null == mSuggestedWordInfo) { 125 throw new RuntimeException("Wrong event: SUGGESTION_PICKED event must have a " 126 + "non-null SuggestedWordInfo"); 127 } 128 } else { 129 if (null != mSuggestedWordInfo) { 130 throw new RuntimeException("Wrong event: only SUGGESTION_PICKED events may have " + 131 "a non-null SuggestedWordInfo"); 132 } 133 } 134 } 135 createSoftwareKeypressEvent(final int codePoint, final int keyCode, final int x, final int y, final boolean isKeyRepeat)136 public static Event createSoftwareKeypressEvent(final int codePoint, final int keyCode, 137 final int x, final int y, final boolean isKeyRepeat) { 138 return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, x, y, 139 null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, null); 140 } 141 createHardwareKeypressEvent(final int codePoint, final int keyCode, final Event next, final boolean isKeyRepeat)142 public static Event createHardwareKeypressEvent(final int codePoint, final int keyCode, 143 final Event next, final boolean isKeyRepeat) { 144 return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, 145 Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE, 146 null /* suggestedWordInfo */, isKeyRepeat ? FLAG_REPEAT : FLAG_NONE, next); 147 } 148 149 // This creates an input event for a dead character. @see {@link #FLAG_DEAD} createDeadEvent(final int codePoint, final int keyCode, final Event next)150 public static Event createDeadEvent(final int codePoint, final int keyCode, final Event next) { 151 // TODO: add an argument or something if we ever create a software layout with dead keys. 152 return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, keyCode, 153 Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE, 154 null /* suggestedWordInfo */, FLAG_DEAD, next); 155 } 156 157 /** 158 * Create an input event with nothing but a code point. This is the most basic possible input 159 * event; it contains no information on many things the IME requires to function correctly, 160 * so avoid using it unless really nothing is known about this input. 161 * @param codePoint the code point. 162 * @return an event for this code point. 163 */ createEventForCodePointFromUnknownSource(final int codePoint)164 public static Event createEventForCodePointFromUnknownSource(final int codePoint) { 165 // TODO: should we have a different type of event for this? After all, it's not a key press. 166 return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE, 167 Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, 168 null /* suggestedWordInfo */, FLAG_NONE, null /* next */); 169 } 170 171 /** 172 * Creates an input event with a code point and x, y coordinates. This is typically used when 173 * resuming a previously-typed word, when the coordinates are still known. 174 * @param codePoint the code point to input. 175 * @param x the X coordinate. 176 * @param y the Y coordinate. 177 * @return an event for this code point and coordinates. 178 */ createEventForCodePointFromAlreadyTypedText(final int codePoint, final int x, final int y)179 public static Event createEventForCodePointFromAlreadyTypedText(final int codePoint, 180 final int x, final int y) { 181 // TODO: should we have a different type of event for this? After all, it's not a key press. 182 return new Event(EVENT_TYPE_INPUT_KEYPRESS, null /* text */, codePoint, NOT_A_KEY_CODE, 183 x, y, null /* suggestedWordInfo */, FLAG_NONE, null /* next */); 184 } 185 186 /** 187 * Creates an input event representing the manual pick of a suggestion. 188 * @return an event for this suggestion pick. 189 */ createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo)190 public static Event createSuggestionPickedEvent(final SuggestedWordInfo suggestedWordInfo) { 191 return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, 192 NOT_A_CODE_POINT, NOT_A_KEY_CODE, 193 Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, 194 suggestedWordInfo, FLAG_NONE, null /* next */); 195 } 196 197 /** 198 * Creates an input event with a CharSequence. This is used by some software processes whose 199 * output is a string, possibly with styling. Examples include press on a multi-character key, 200 * or combination that outputs a string. 201 * @param text the CharSequence associated with this event. 202 * @param keyCode the key code, or NOT_A_KEYCODE if not applicable. 203 * @return an event for this text. 204 */ createSoftwareTextEvent(final CharSequence text, final int keyCode)205 public static Event createSoftwareTextEvent(final CharSequence text, final int keyCode) { 206 return new Event(EVENT_TYPE_SOFTWARE_GENERATED_STRING, text, NOT_A_CODE_POINT, keyCode, 207 Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, 208 null /* suggestedWordInfo */, FLAG_NONE, null /* next */); 209 } 210 211 /** 212 * Creates an input event representing the manual pick of a punctuation suggestion. 213 * @return an event for this suggestion pick. 214 */ createPunctuationSuggestionPickedEvent( final SuggestedWordInfo suggestedWordInfo)215 public static Event createPunctuationSuggestionPickedEvent( 216 final SuggestedWordInfo suggestedWordInfo) { 217 final int primaryCode = suggestedWordInfo.mWord.charAt(0); 218 return new Event(EVENT_TYPE_SUGGESTION_PICKED, suggestedWordInfo.mWord, primaryCode, 219 NOT_A_KEY_CODE, Constants.SUGGESTION_STRIP_COORDINATE, 220 Constants.SUGGESTION_STRIP_COORDINATE, suggestedWordInfo, FLAG_NONE, 221 null /* next */); 222 } 223 224 /** 225 * Creates an event identical to the passed event, but that has already been consumed. 226 * @param source the event to copy the properties of. 227 * @return an identical event marked as consumed. 228 */ createConsumedEvent(final Event source)229 public static Event createConsumedEvent(final Event source) { 230 // A consumed event should not input any text at all, so we pass the empty string as text. 231 return new Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, 232 source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags | FLAG_CONSUMED, 233 source.mNextEvent); 234 } 235 createNotHandledEvent()236 public static Event createNotHandledEvent() { 237 return new Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, 238 Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, 239 null /* suggestedWordInfo */, FLAG_NONE, null); 240 } 241 242 // Returns whether this is a function key like backspace, ctrl, settings... as opposed to keys 243 // that result in input like letters or space. isFunctionalKeyEvent()244 public boolean isFunctionalKeyEvent() { 245 // This logic may need to be refined in the future 246 return NOT_A_CODE_POINT == mCodePoint; 247 } 248 249 // Returns whether this event is for a dead character. @see {@link #FLAG_DEAD} isDead()250 public boolean isDead() { 251 return 0 != (FLAG_DEAD & mFlags); 252 } 253 isKeyRepeat()254 public boolean isKeyRepeat() { 255 return 0 != (FLAG_REPEAT & mFlags); 256 } 257 isConsumed()258 public boolean isConsumed() { return 0 != (FLAG_CONSUMED & mFlags); } 259 isGesture()260 public boolean isGesture() { return EVENT_TYPE_GESTURE == mEventType; } 261 262 // Returns whether this is a fake key press from the suggestion strip. This happens with 263 // punctuation signs selected from the suggestion strip. isSuggestionStripPress()264 public boolean isSuggestionStripPress() { 265 return EVENT_TYPE_SUGGESTION_PICKED == mEventType; 266 } 267 isHandled()268 public boolean isHandled() { 269 return EVENT_TYPE_NOT_HANDLED != mEventType; 270 } 271 getTextToCommit()272 public CharSequence getTextToCommit() { 273 if (isConsumed()) { 274 return ""; // A consumed event should input no text. 275 } 276 switch (mEventType) { 277 case EVENT_TYPE_MODE_KEY: 278 case EVENT_TYPE_NOT_HANDLED: 279 case EVENT_TYPE_TOGGLE: 280 return ""; 281 case EVENT_TYPE_INPUT_KEYPRESS: 282 return StringUtils.newSingleCodePointString(mCodePoint); 283 case EVENT_TYPE_GESTURE: 284 case EVENT_TYPE_SOFTWARE_GENERATED_STRING: 285 case EVENT_TYPE_SUGGESTION_PICKED: 286 return mText; 287 } 288 throw new RuntimeException("Unknown event type: " + mEventType); 289 } 290 } 291