diff options
Diffstat (limited to 'java/src/com/android/inputmethod/event')
5 files changed, 134 insertions, 36 deletions
diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java new file mode 100644 index 000000000..ab6b70c04 --- /dev/null +++ b/java/src/com/android/inputmethod/event/Combiner.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.event; + +/** + * A generic interface for combiners. + */ +public interface Combiner { + /** + * Combine an event with the existing state and return the new event. + * @param event the event to combine with the existing state. + * @return the resulting event. + */ + Event combine(Event event); +} diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java new file mode 100644 index 000000000..52987d571 --- /dev/null +++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.event; + +import android.text.TextUtils; +import android.view.KeyCharacterMap; + +import com.android.inputmethod.latin.Constants; + +/** + * A combiner that handles dead keys. + */ +public class DeadKeyCombiner implements Combiner { + final StringBuilder mDeadSequence = new StringBuilder(); + + @Override + public Event combine(final Event event) { + if (null == event) return null; // Just in case some combiner is broken + if (TextUtils.isEmpty(mDeadSequence)) { + if (event.isDead()) { + mDeadSequence.appendCodePoint(event.mCodePoint); + } + return event; + } else { + // TODO: Allow combining for several dead chars rather than only the first one. + // The framework doesn't know how to do this now. + final int deadCodePoint = mDeadSequence.codePointAt(0); + mDeadSequence.setLength(0); + final int resultingCodePoint = + KeyCharacterMap.getDeadChar(deadCodePoint, event.mCodePoint); + if (0 == resultingCodePoint) { + // We can't combine both characters. We need to commit the dead key as a committable + // character, and the next char too unless it's a space (because as a special case, + // dead key + space should result in only the dead key being committed - that's + // how dead keys work). + // If the event is a space, we should commit the dead char alone, but if it's + // not, we need to commit both. + return Event.createCommittableEvent(deadCodePoint, + Constants.CODE_SPACE == event.mCodePoint ? null : event /* next */); + } else { + // We could combine the characters. + return Event.createCommittableEvent(resultingCodePoint, null /* next */); + } + } + } + +} diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index 215e4dee5..1f3320eb7 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -54,39 +54,40 @@ public class Event { final private static int NOT_A_CODE_POINT = 0; - private int mType; // The type of event - one of the constants above + final private int mType; // The type of event - one of the constants above // The code point associated with the event, if relevant. This is a unicode code point, and // has nothing to do with other representations of the key. It is only relevant if this event // is the right type: COMMITTABLE or DEAD or TOGGLE, but for a mode key like hankaku/zenkaku or // ctrl, there is no code point associated so this should be NOT_A_CODE_POINT to avoid // unintentional use of its value when it's not relevant. - private int mCodePoint; + final public int mCodePoint; + // The next event, if any. Null if there is no next event yet. + final public Event mNextEvent; - static Event obtainEvent() { - // TODO: create an event pool instead - return new Event(); + // This method is private - to create a new event, use one of the create* utility methods. + private Event(final int type, final int codePoint, final Event next) { + mType = type; + mCodePoint = codePoint; + mNextEvent = next; } - public void setDeadEvent(final int codePoint) { - mType = EVENT_DEAD; - mCodePoint = codePoint; + public static Event createDeadEvent(final int codePoint, final Event next) { + return new Event(EVENT_DEAD, codePoint, next); } - public void setCommittableEvent(final int codePoint) { - mType = EVENT_COMMITTABLE; - mCodePoint = codePoint; + public static Event createCommittableEvent(final int codePoint, final Event next) { + return new Event(EVENT_COMMITTABLE, codePoint, next); } - public void setNotHandledEvent() { - mType = EVENT_NOT_HANDLED; - mCodePoint = NOT_A_CODE_POINT; // Just in case + public static Event createNotHandledEvent() { + return new Event(EVENT_NOT_HANDLED, NOT_A_CODE_POINT, null); } public boolean isCommittable() { return EVENT_COMMITTABLE == mType; } - public int getCodePoint() { - return mCodePoint; + public boolean isDead() { + return EVENT_DEAD == mType; } } diff --git a/java/src/com/android/inputmethod/event/EventInterpreter.java b/java/src/com/android/inputmethod/event/EventInterpreter.java index 1bd0cca00..6efe899bb 100644 --- a/java/src/com/android/inputmethod/event/EventInterpreter.java +++ b/java/src/com/android/inputmethod/event/EventInterpreter.java @@ -19,9 +19,12 @@ package com.android.inputmethod.event; import android.util.SparseArray; import android.view.KeyEvent; +import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinIME; +import java.util.ArrayList; + /** * This class implements the logic between receiving events and generating code points. * @@ -40,6 +43,7 @@ public class EventInterpreter { final SparseArray<HardwareEventDecoder> mHardwareEventDecoders; final SoftwareEventDecoder mSoftwareEventDecoder; final LatinIME mLatinIme; + final ArrayList<Combiner> mCombiners; /** * Create a default interpreter. @@ -74,6 +78,8 @@ public class EventInterpreter { // capacity of 1. mHardwareEventDecoders = new SparseArray<HardwareEventDecoder>(1); mSoftwareEventDecoder = new SoftwareKeyboardEventDecoder(); + mCombiners = CollectionUtils.newArrayList(); + mCombiners.add(new DeadKeyCombiner()); mLatinIme = latinIme; } @@ -106,19 +112,22 @@ public class EventInterpreter { } private boolean onEvent(final Event event) { - if (event.isCommittable()) { - mLatinIme.onCodeInput(event.getCodePoint(), - Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE); - return true; + Event currentlyProcessingEvent = event; + boolean processed = false; + for (int i = 0; i < mCombiners.size(); ++i) { + currentlyProcessingEvent = mCombiners.get(i).combine(event); + } + while (null != currentlyProcessingEvent) { + if (currentlyProcessingEvent.isCommittable()) { + mLatinIme.onCodeInput(currentlyProcessingEvent.mCodePoint, + Constants.EXTERNAL_KEYBOARD_COORDINATE, + Constants.EXTERNAL_KEYBOARD_COORDINATE); + processed = true; + } else if (event.isDead()) { + processed = true; + } + currentlyProcessingEvent = currentlyProcessingEvent.mNextEvent; } - // TODO: Classify the event - input or non-input (see design doc) - // TODO: IF action event - // Send decoded action back to LatinIME - // ELSE - // Send input event to the combiner - // Get back new input material + visual feedback + combiner state - // Route the event to Latin IME - // ENDIF - return false; + return processed; } } diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java index 2dbc9f00b..2fb7fe8b4 100644 --- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java +++ b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java @@ -38,7 +38,6 @@ public class HardwareKeyboardEventDecoder implements HardwareEventDecoder { @Override public Event decodeHardwareKey(final KeyEvent keyEvent) { - final Event event = Event.obtainEvent(); // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value // that includes both the unicode char in the lower 21 bits and flags in the upper bits, // hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info. @@ -48,22 +47,21 @@ public class HardwareKeyboardEventDecoder implements HardwareEventDecoder { // the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock. final int keyCode = keyEvent.getKeyCode(); if (KeyEvent.KEYCODE_DEL == keyCode) { - event.setCommittableEvent(Constants.CODE_DELETE); - return event; + return Event.createCommittableEvent(Constants.CODE_DELETE, null /* next */); } if (keyEvent.isPrintingKey() || KeyEvent.KEYCODE_SPACE == keyCode || KeyEvent.KEYCODE_ENTER == keyCode) { if (0 != (codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT)) { // A dead key. - event.setDeadEvent(codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT_MASK); + return Event.createDeadEvent( + codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT_MASK, null /* next */); } else { // A committable character. This should be committed right away, taking into // account the current state. - event.setCommittableEvent(codePointAndFlags); + return Event.createCommittableEvent(codePointAndFlags, null /* next */); } } else { - event.setNotHandledEvent(); + return Event.createNotHandledEvent(); } - return event; } } |