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