Create the path for specifying combining rules in the subtype

Also add the skeleton of the combining rules for Myanmar
reordering as a pilot.

Bug: 13945569
Change-Id: Iabeb6838f45f1efff482143595ce5b48543e0c43
main
Jean Chalard 2014-05-08 14:53:56 +09:00
parent c41d261b7e
commit be99616afa
10 changed files with 138 additions and 3 deletions

View File

@ -503,7 +503,7 @@
android:subtypeId="0xea266ea4" android:subtypeId="0xea266ea4"
android:imeSubtypeLocale="my_MM" android:imeSubtypeLocale="my_MM"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable" android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable,CombiningRules=MyanmarReordering"
android:isAsciiCapable="false" android:isAsciiCapable="false"
/> />
<subtype android:icon="@drawable/ic_ime_switcher_dark" <subtype android:icon="@drawable/ic_ime_switcher_dark"

View File

@ -23,6 +23,7 @@ import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
/** /**
* This class implements the logic chain between receiving events and generating code points. * This class implements the logic chain between receiving events and generating code points.
@ -43,6 +44,13 @@ public class CombinerChain {
private SpannableStringBuilder mStateFeedback; private SpannableStringBuilder mStateFeedback;
private final ArrayList<Combiner> mCombiners; private final ArrayList<Combiner> mCombiners;
private static final HashMap<String, Class> IMPLEMENTED_COMBINERS
= new HashMap<String, Class>();
static {
IMPLEMENTED_COMBINERS.put("MyanmarReordering", MyanmarReordering.class);
}
private static final String COMBINER_SPEC_SEPARATOR = ";";
/** /**
* Create an combiner chain. * Create an combiner chain.
* *
@ -56,6 +64,9 @@ public class CombinerChain {
mCombiners = CollectionUtils.newArrayList(); mCombiners = CollectionUtils.newArrayList();
// The dead key combiner is always active, and always first // The dead key combiner is always active, and always first
mCombiners.add(new DeadKeyCombiner()); mCombiners.add(new DeadKeyCombiner());
for (final Combiner combiner : combinerList) {
mCombiners.add(combiner);
}
mCombinedText = new StringBuilder(); mCombinedText = new StringBuilder();
mStateFeedback = new SpannableStringBuilder(); mStateFeedback = new SpannableStringBuilder();
} }
@ -114,4 +125,29 @@ public class CombinerChain {
final SpannableStringBuilder s = new SpannableStringBuilder(mCombinedText); final SpannableStringBuilder s = new SpannableStringBuilder(mCombinedText);
return s.append(mStateFeedback); return s.append(mStateFeedback);
} }
public static Combiner[] createCombiners(final String spec) {
if (TextUtils.isEmpty(spec)) {
return new Combiner[0];
}
final String[] combinerDescriptors = spec.split(COMBINER_SPEC_SEPARATOR);
final Combiner[] combiners = new Combiner[combinerDescriptors.length];
int i = 0;
for (final String combinerDescriptor : combinerDescriptors) {
final Class combinerClass = IMPLEMENTED_COMBINERS.get(combinerDescriptor);
if (null == combinerClass) {
throw new RuntimeException("Unknown combiner descriptor: " + combinerDescriptor);
}
try {
combiners[i++] = (Combiner)combinerClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Unable to instantiate combiner: " + combinerDescriptor,
e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate combiner: " + combinerDescriptor,
e);
}
}
return combiners;
}
} }

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 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 java.util.ArrayList;
/**
* A combiner that reorders input for Myanmar.
*/
public class MyanmarReordering implements Combiner {
@Override
public Event processEvent(ArrayList<Event> previousEvents, Event event) {
return event;
}
@Override
public CharSequence getCombiningStateFeedback() {
return "";
}
@Override
public void reset() {
}
}

View File

@ -115,6 +115,11 @@ public final class Constants {
*/ */
public static final String IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype"; public static final String IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype";
/**
* The subtype extra value used to specify the combining rules.
*/
public static final String COMBINING_RULES = "CombiningRules";
private ExtraValue() { private ExtraValue() {
// This utility class is not publicly instantiable. // This utility class is not publicly instantiable.
} }

View File

@ -733,6 +733,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread. // is not guaranteed. It may even be called at the same time on a different thread.
mSubtypeSwitcher.onSubtypeChanged(subtype); mSubtypeSwitcher.onSubtypeChanged(subtype);
mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype));
loadKeyboard(); loadKeyboard();
} }
@ -808,7 +809,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// The app calling setText() has the effect of clearing the composing // The app calling setText() has the effect of clearing the composing
// span, so we should reset our state unconditionally, even if restarting is true. // span, so we should reset our state unconditionally, even if restarting is true.
mInputLogic.startInput(restarting, editorInfo); // We also tell the input logic about the combining rules for the current subtype, so
// it can adjust its combiners if needed.
mInputLogic.startInput(restarting, editorInfo,
mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype());
// Note: the following does a round-trip IPC on the main thread: be careful // Note: the following does a round-trip IPC on the main thread: be careful
final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();

View File

@ -326,4 +326,8 @@ public final class SubtypeSwitcher {
+ DUMMY_EMOJI_SUBTYPE); + DUMMY_EMOJI_SUBTYPE);
return DUMMY_EMOJI_SUBTYPE; return DUMMY_EMOJI_SUBTYPE;
} }
public String getCombiningRulesExtraValueOfCurrentSubtype() {
return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype());
}
} }

View File

@ -41,6 +41,7 @@ public final class WordComposer {
public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7; public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
private CombinerChain mCombinerChain; private CombinerChain mCombinerChain;
private String mCombiningSpec; // Memory so that we don't uselessly recreate the combiner chain
// The list of events that served to compose this string. // The list of events that served to compose this string.
private final ArrayList<Event> mEvents; private final ArrayList<Event> mEvents;
@ -90,6 +91,21 @@ public final class WordComposer {
refreshTypedWordCache(); refreshTypedWordCache();
} }
/**
* Restart input with a new combining spec.
* @param combiningSpec The spec string for combining. This is found in the extra value.
*/
public void restart(final String combiningSpec) {
final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec;
if (nonNullCombiningSpec.equals(mCombiningSpec)) {
mCombinerChain.reset();
} else {
mCombinerChain = new CombinerChain(CombinerChain.createCombiners(nonNullCombiningSpec));
mCombiningSpec = nonNullCombiningSpec;
}
reset();
}
/** /**
* Clear out the keys registered so far. * Clear out the keys registered so far.
*/ */

View File

@ -97,6 +97,11 @@ public final class InputLogic {
private boolean mIsAutoCorrectionIndicatorOn; private boolean mIsAutoCorrectionIndicatorOn;
private long mDoubleSpacePeriodCountdownStart; private long mDoubleSpacePeriodCountdownStart;
/**
* Create a new instance of the input logic.
* @param latinIME the instance of the parent LatinIME. We should remove this when we can.
* @param suggestionStripViewAccessor an object to access the suggestion strip view.
*/
public InputLogic(final LatinIME latinIME, public InputLogic(final LatinIME latinIME,
final SuggestionStripViewAccessor suggestionStripViewAccessor) { final SuggestionStripViewAccessor suggestionStripViewAccessor) {
mLatinIME = latinIME; mLatinIME = latinIME;
@ -117,9 +122,12 @@ public final class InputLogic {
* *
* @param restarting whether input is starting in the same field as before. Unused for now. * @param restarting whether input is starting in the same field as before. Unused for now.
* @param editorInfo the editorInfo associated with the editor. * @param editorInfo the editorInfo associated with the editor.
* @param combiningSpec the combining spec string for this subtype
*/ */
public void startInput(final boolean restarting, final EditorInfo editorInfo) { public void startInput(final boolean restarting, final EditorInfo editorInfo,
final String combiningSpec) {
mEnteredText = null; mEnteredText = null;
mWordComposer.restart(combiningSpec);
resetComposingState(true /* alsoResetLastComposedWord */); resetComposingState(true /* alsoResetLastComposedWord */);
mDeleteCount = 0; mDeleteCount = 0;
mSpaceState = SpaceState.NONE; mSpaceState = SpaceState.NONE;
@ -137,6 +145,14 @@ public final class InputLogic {
} }
} }
/**
* Call this when the subtype changes.
* @param combiningSpec the spec string for the combining rules
*/
public void onSubtypeChanged(final String combiningSpec) {
mWordComposer.restart(combiningSpec);
}
/** /**
* Clean up the input logic after input is finished. * Clean up the input logic after input is finished.
*/ */

View File

@ -324,4 +324,8 @@ public final class SubtypeLocaleUtils {
public static boolean isRtlLanguage(final InputMethodSubtype subtype) { public static boolean isRtlLanguage(final InputMethodSubtype subtype) {
return isRtlLanguage(getSubtypeLocale(subtype)); return isRtlLanguage(getSubtypeLocale(subtype));
} }
public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) {
return subtype.getExtraValueOf(Constants.Subtype.ExtraValue.COMBINING_RULES);
}
} }

View File

@ -20,6 +20,13 @@ import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
/**
* Compatibility class that stands in for the combiner chain in LatinIME.
*
* This is not used by dicttool, it's just needed by the dependency chain.
*/
// TODO: there should not be a dependency to this in dicttool, so there
// should be a sensible way to separate them cleanly.
public class CombinerChain { public class CombinerChain {
private StringBuilder mComposingWord = new StringBuilder(); private StringBuilder mComposingWord = new StringBuilder();
public CombinerChain(final Combiner... combinerList) {} public CombinerChain(final Combiner... combinerList) {}
@ -35,4 +42,9 @@ public class CombinerChain {
public void reset() { public void reset() {
mComposingWord.setLength(0); mComposingWord.setLength(0);
} }
public static Combiner[] createCombiners(final String spec) {
// Dicttool never uses a combiner at all, so we just return a zero-sized array.
return new Combiner[0];
}
} }