Use translation of fallback umlauts digraphs for German.
For German : handle "ae", "oe" and "ue" to be alternate forms for umlaut-bearing versions of "a", "o" and "u". Issue: 3275926 Change-Id: I056c707cdacc464ceab63be56c016c7f8439196cmain
parent
e59491460b
commit
c2bbc6a449
|
@ -65,6 +65,7 @@
|
||||||
android:label="@string/subtype_mode_de_keyboard"
|
android:label="@string/subtype_mode_de_keyboard"
|
||||||
android:imeSubtypeLocale="de"
|
android:imeSubtypeLocale="de"
|
||||||
android:imeSubtypeMode="keyboard"
|
android:imeSubtypeMode="keyboard"
|
||||||
|
android:imeSubtypeExtraValue="requiresGermanUmlautProcessing"
|
||||||
/>
|
/>
|
||||||
<subtype android:icon="@drawable/ic_subtype_mic"
|
<subtype android:icon="@drawable/ic_subtype_mic"
|
||||||
android:label="@string/subtype_mode_de_voice"
|
android:label="@string/subtype_mode_de_voice"
|
||||||
|
|
|
@ -58,6 +58,25 @@ public class BinaryDictionary extends Dictionary {
|
||||||
private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
|
private final int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
|
||||||
|
|
||||||
private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance();
|
||||||
|
private final SubtypeSwitcher mSubtypeSwitcher = SubtypeSwitcher.getInstance();
|
||||||
|
|
||||||
|
private static class Flags {
|
||||||
|
private static class FlagEntry {
|
||||||
|
public final String mName;
|
||||||
|
public final int mValue;
|
||||||
|
public FlagEntry(String name, int value) {
|
||||||
|
mName = name;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static final FlagEntry[] ALL_FLAGS = {
|
||||||
|
// Here should reside all flags that trigger some special processing
|
||||||
|
// These *must* match the definition in UnigramDictionary enum in
|
||||||
|
// unigram_dictionary.h so please update both at the same time.
|
||||||
|
new FlagEntry("requiresGermanUmlautProcessing", 0x1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private int mFlags = 0;
|
||||||
|
|
||||||
private BinaryDictionary() {
|
private BinaryDictionary() {
|
||||||
}
|
}
|
||||||
|
@ -91,6 +110,7 @@ public class BinaryDictionary extends Dictionary {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sInstance.initFlags();
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,16 +129,26 @@ public class BinaryDictionary extends Dictionary {
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initFlags() {
|
||||||
|
int flags = 0;
|
||||||
|
for (Flags.FlagEntry entry : Flags.ALL_FLAGS) {
|
||||||
|
if (mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(entry.mName))
|
||||||
|
flags |= entry.mValue;
|
||||||
|
}
|
||||||
|
mFlags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Utils.loadNativeLibrary();
|
Utils.loadNativeLibrary();
|
||||||
}
|
}
|
||||||
|
|
||||||
private native int openNative(String sourceDir, long dictOffset, long dictSize,
|
private native int openNative(String sourceDir, long dictOffset, long dictSize,
|
||||||
int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength,
|
int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength,
|
||||||
int maxWords, int maxAlternatives);
|
int maxWords, int maxAlternatives);
|
||||||
private native void closeNative(int dict);
|
private native void closeNative(int dict);
|
||||||
private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
|
private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
|
||||||
private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates,
|
private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates,
|
||||||
int[] yCoordinates, int[] inputCodes, int codesSize, char[] outputChars,
|
int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars,
|
||||||
int[] frequencies);
|
int[] frequencies);
|
||||||
private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
|
private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
|
||||||
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
|
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
|
||||||
|
@ -207,7 +237,7 @@ public class BinaryDictionary extends Dictionary {
|
||||||
return getSuggestionsNative(
|
return getSuggestionsNative(
|
||||||
mNativeDict, keyboard.getProximityInfo(),
|
mNativeDict, keyboard.getProximityInfo(),
|
||||||
codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
|
codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
|
||||||
outputChars, frequencies);
|
mFlags, outputChars, frequencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -74,10 +74,10 @@ public class SubtypeSwitcher {
|
||||||
private InputMethodInfo mShortcutInputMethodInfo;
|
private InputMethodInfo mShortcutInputMethodInfo;
|
||||||
private InputMethodSubtype mShortcutSubtype;
|
private InputMethodSubtype mShortcutSubtype;
|
||||||
private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
|
private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
|
||||||
|
private InputMethodSubtype mCurrentSubtype;
|
||||||
private Locale mSystemLocale;
|
private Locale mSystemLocale;
|
||||||
private Locale mInputLocale;
|
private Locale mInputLocale;
|
||||||
private String mInputLocaleStr;
|
private String mInputLocaleStr;
|
||||||
private String mMode;
|
|
||||||
private VoiceInput mVoiceInput;
|
private VoiceInput mVoiceInput;
|
||||||
/*-----------------------------------------------------------*/
|
/*-----------------------------------------------------------*/
|
||||||
|
|
||||||
|
@ -110,8 +110,7 @@ public class SubtypeSwitcher {
|
||||||
mSystemLocale = null;
|
mSystemLocale = null;
|
||||||
mInputLocale = null;
|
mInputLocale = null;
|
||||||
mInputLocaleStr = null;
|
mInputLocaleStr = null;
|
||||||
// Mode is initialized to KEYBOARD_MODE, in case that LatinIME can't obtain currentSubtype
|
mCurrentSubtype = null;
|
||||||
mMode = KEYBOARD_MODE;
|
|
||||||
mAllEnabledSubtypesOfCurrentInputMethod = null;
|
mAllEnabledSubtypesOfCurrentInputMethod = null;
|
||||||
// TODO: Voice input should be created here
|
// TODO: Voice input should be created here
|
||||||
mVoiceInput = null;
|
mVoiceInput = null;
|
||||||
|
@ -145,6 +144,7 @@ public class SubtypeSwitcher {
|
||||||
|
|
||||||
// Reload enabledSubtypes from the framework.
|
// Reload enabledSubtypes from the framework.
|
||||||
private void updateEnabledSubtypes() {
|
private void updateEnabledSubtypes() {
|
||||||
|
final String currentMode = getCurrentSubtypeMode();
|
||||||
boolean foundCurrentSubtypeBecameDisabled = true;
|
boolean foundCurrentSubtypeBecameDisabled = true;
|
||||||
mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
|
mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
|
||||||
null, true);
|
null, true);
|
||||||
|
@ -157,7 +157,7 @@ public class SubtypeSwitcher {
|
||||||
if (mLocaleSplitter.hasNext()) {
|
if (mLocaleSplitter.hasNext()) {
|
||||||
mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next());
|
mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next());
|
||||||
}
|
}
|
||||||
if (locale.equals(mInputLocaleStr) && mode.equals(mMode)) {
|
if (locale.equals(mInputLocaleStr) && mode.equals(currentMode)) {
|
||||||
foundCurrentSubtypeBecameDisabled = false;
|
foundCurrentSubtypeBecameDisabled = false;
|
||||||
}
|
}
|
||||||
if (KEYBOARD_MODE.equals(ims.getMode())) {
|
if (KEYBOARD_MODE.equals(ims.getMode())) {
|
||||||
|
@ -168,7 +168,7 @@ public class SubtypeSwitcher {
|
||||||
&& mIsSystemLanguageSameAsInputLanguage);
|
&& mIsSystemLanguageSameAsInputLanguage);
|
||||||
if (foundCurrentSubtypeBecameDisabled) {
|
if (foundCurrentSubtypeBecameDisabled) {
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + mMode);
|
Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + currentMode);
|
||||||
Log.w(TAG, "Last subtype was disabled. Update to the current one.");
|
Log.w(TAG, "Last subtype was disabled. Update to the current one.");
|
||||||
}
|
}
|
||||||
updateSubtype(mImm.getCurrentInputMethodSubtype());
|
updateSubtype(mImm.getCurrentInputMethodSubtype());
|
||||||
|
@ -209,9 +209,10 @@ public class SubtypeSwitcher {
|
||||||
public void updateSubtype(InputMethodSubtype newSubtype) {
|
public void updateSubtype(InputMethodSubtype newSubtype) {
|
||||||
final String newLocale;
|
final String newLocale;
|
||||||
final String newMode;
|
final String newMode;
|
||||||
|
final String oldMode = getCurrentSubtypeMode();
|
||||||
if (newSubtype == null) {
|
if (newSubtype == null) {
|
||||||
// Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
|
// Normally, newSubtype shouldn't be null. But just in case newSubtype was null,
|
||||||
// fallback to the default locale and mode.
|
// fallback to the default locale.
|
||||||
Log.w(TAG, "Couldn't get the current subtype.");
|
Log.w(TAG, "Couldn't get the current subtype.");
|
||||||
newLocale = "en_US";
|
newLocale = "en_US";
|
||||||
newMode = KEYBOARD_MODE;
|
newMode = KEYBOARD_MODE;
|
||||||
|
@ -220,8 +221,8 @@ public class SubtypeSwitcher {
|
||||||
newMode = newSubtype.getMode();
|
newMode = newSubtype.getMode();
|
||||||
}
|
}
|
||||||
if (DBG) {
|
if (DBG) {
|
||||||
Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode
|
Log.w(TAG, "Update subtype to:" + newLocale + "," + newSubtype.getMode()
|
||||||
+ ", from: " + mInputLocaleStr + ", " + mMode);
|
+ ", from: " + mInputLocaleStr + ", " + oldMode);
|
||||||
}
|
}
|
||||||
boolean languageChanged = false;
|
boolean languageChanged = false;
|
||||||
if (!newLocale.equals(mInputLocaleStr)) {
|
if (!newLocale.equals(mInputLocaleStr)) {
|
||||||
|
@ -231,13 +232,12 @@ public class SubtypeSwitcher {
|
||||||
updateInputLocale(newLocale);
|
updateInputLocale(newLocale);
|
||||||
}
|
}
|
||||||
boolean modeChanged = false;
|
boolean modeChanged = false;
|
||||||
String oldMode = mMode;
|
if (!newMode.equals(oldMode)) {
|
||||||
if (!newMode.equals(mMode)) {
|
if (oldMode != null) {
|
||||||
if (mMode != null) {
|
|
||||||
modeChanged = true;
|
modeChanged = true;
|
||||||
}
|
}
|
||||||
mMode = newMode;
|
|
||||||
}
|
}
|
||||||
|
mCurrentSubtype = newSubtype;
|
||||||
|
|
||||||
// If the old mode is voice input, we need to reset or cancel its status.
|
// If the old mode is voice input, we need to reset or cancel its status.
|
||||||
// We cancel its status when we change mode, while we reset otherwise.
|
// We cancel its status when we change mode, while we reset otherwise.
|
||||||
|
@ -262,7 +262,7 @@ public class SubtypeSwitcher {
|
||||||
triggerVoiceIME();
|
triggerVoiceIME();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Unknown subtype mode: " + mMode);
|
Log.w(TAG, "Unknown subtype mode: " + newMode);
|
||||||
if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
|
if (VOICE_MODE.equals(oldMode) && mVoiceInput != null) {
|
||||||
// We need to reset the voice input to release the resources and to reset its status
|
// We need to reset the voice input to release the resources and to reset its status
|
||||||
// as it is not the current input mode.
|
// as it is not the current input mode.
|
||||||
|
@ -483,7 +483,7 @@ public class SubtypeSwitcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isKeyboardMode() {
|
public boolean isKeyboardMode() {
|
||||||
return KEYBOARD_MODE.equals(mMode);
|
return KEYBOARD_MODE.equals(getCurrentSubtypeMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -506,7 +506,7 @@ public class SubtypeSwitcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVoiceMode() {
|
public boolean isVoiceMode() {
|
||||||
return VOICE_MODE.equals(mMode);
|
return null == mCurrentSubtype ? false : VOICE_MODE.equals(getCurrentSubtypeMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void triggerVoiceIME() {
|
private void triggerVoiceIME() {
|
||||||
|
@ -572,6 +572,30 @@ public class SubtypeSwitcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// Other utility functions //
|
||||||
|
/////////////////////////////
|
||||||
|
|
||||||
|
public String getCurrentSubtypeExtraValue() {
|
||||||
|
// If null, return what an empty ExtraValue would return : the empty string.
|
||||||
|
return null != mCurrentSubtype ? mCurrentSubtype.getExtraValue() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean currentSubtypeContainsExtraValueKey(String key) {
|
||||||
|
// If null, return what an empty ExtraValue would return : false.
|
||||||
|
return null != mCurrentSubtype ? mCurrentSubtype.containsExtraValueKey(key) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentSubtypeExtraValueOf(String key) {
|
||||||
|
// If null, return what an empty ExtraValue would return : null.
|
||||||
|
return null != mCurrentSubtype ? mCurrentSubtype.getExtraValueOf(key) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentSubtypeMode() {
|
||||||
|
return null != mCurrentSubtype ? mCurrentSubtype.getMode() : KEYBOARD_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// A list of locales which are supported by default for voice input, unless we get a
|
// A list of locales which are supported by default for voice input, unless we get a
|
||||||
// different list from Gservices.
|
// different list from Gservices.
|
||||||
private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
|
private static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
|
||||||
|
|
|
@ -126,7 +126,8 @@ static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
|
||||||
|
|
||||||
static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict,
|
static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict,
|
||||||
jint proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray,
|
jint proximityInfo, jintArray xCoordinatesArray, jintArray yCoordinatesArray,
|
||||||
jintArray inputArray, jint arraySize, jcharArray outputArray, jintArray frequencyArray) {
|
jintArray inputArray, jint arraySize, jint flags,
|
||||||
|
jcharArray outputArray, jintArray frequencyArray) {
|
||||||
Dictionary *dictionary = (Dictionary*)dict;
|
Dictionary *dictionary = (Dictionary*)dict;
|
||||||
if (!dictionary) return 0;
|
if (!dictionary) return 0;
|
||||||
ProximityInfo *pInfo = (ProximityInfo*)proximityInfo;
|
ProximityInfo *pInfo = (ProximityInfo*)proximityInfo;
|
||||||
|
@ -140,7 +141,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object,
|
||||||
jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
|
jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
|
||||||
|
|
||||||
int count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, inputCodes,
|
int count = dictionary->getSuggestions(pInfo, xCoordinates, yCoordinates, inputCodes,
|
||||||
arraySize, (unsigned short*) outputChars, frequencies);
|
arraySize, flags, (unsigned short*) outputChars, frequencies);
|
||||||
|
|
||||||
env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
|
env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
|
||||||
env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
|
env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
|
||||||
|
@ -213,7 +214,7 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jint di
|
||||||
static JNINativeMethod sMethods[] = {
|
static JNINativeMethod sMethods[] = {
|
||||||
{"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open},
|
{"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open},
|
||||||
{"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
|
{"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
|
||||||
{"getSuggestionsNative", "(II[I[I[II[C[I)I", (void*)latinime_BinaryDictionary_getSuggestions},
|
{"getSuggestionsNative", "(II[I[I[III[C[I)I", (void*)latinime_BinaryDictionary_getSuggestions},
|
||||||
{"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord},
|
{"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord},
|
||||||
{"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
|
{"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
|
||||||
};
|
};
|
||||||
|
|
|
@ -55,4 +55,15 @@ static inline void LOGI_S16_PLUS(unsigned short* string, const unsigned int leng
|
||||||
// usleep(10);
|
// usleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void printDebug(const char* tag, int* codes, int codesSize, int MAX_PROXIMITY_CHARS) {
|
||||||
|
unsigned char *buf = (unsigned char*)malloc((1 + codesSize) * sizeof(*buf));
|
||||||
|
|
||||||
|
buf[codesSize] = 0;
|
||||||
|
while (--codesSize >= 0)
|
||||||
|
buf[codesSize] = (unsigned char)codes[codesSize * MAX_PROXIMITY_CHARS];
|
||||||
|
LOGI("%s, WORD = %s", tag, buf);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // LATINIME_DEBUG_H
|
#endif // LATINIME_DEBUG_H
|
||||||
|
|
|
@ -29,9 +29,9 @@ public:
|
||||||
Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler,
|
Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler,
|
||||||
int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives);
|
int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives);
|
||||||
int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates,
|
int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates,
|
||||||
int *codes, int codesSize, unsigned short *outWords, int *frequencies) {
|
int *codes, int codesSize, int flags, unsigned short *outWords, int *frequencies) {
|
||||||
return mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
|
return mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
|
||||||
codesSize, outWords, frequencies);
|
codesSize, flags, outWords, frequencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Call mBigramDictionary instead of mUnigramDictionary
|
// TODO: Call mBigramDictionary instead of mUnigramDictionary
|
||||||
|
|
|
@ -29,20 +29,136 @@
|
||||||
|
|
||||||
namespace latinime {
|
namespace latinime {
|
||||||
|
|
||||||
|
const UnigramDictionary::digraph_t UnigramDictionary::GERMAN_UMLAUT_DIGRAPHS[] =
|
||||||
|
{ { 'a', 'e' },
|
||||||
|
{ 'o', 'e' },
|
||||||
|
{ 'u', 'e' } };
|
||||||
|
|
||||||
UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterMultiplier,
|
UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterMultiplier,
|
||||||
int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
|
int fullWordMultiplier, int maxWordLength, int maxWords, int maxProximityChars,
|
||||||
const bool isLatestDictVersion)
|
const bool isLatestDictVersion)
|
||||||
: DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
|
: DICT(dict), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
|
||||||
MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion),
|
MAX_PROXIMITY_CHARS(maxProximityChars), IS_LATEST_DICT_VERSION(isLatestDictVersion),
|
||||||
TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
|
TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
|
||||||
ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0) {
|
ROOT_POS(isLatestDictVersion ? DICTIONARY_HEADER_SIZE : 0),
|
||||||
|
BYTES_IN_ONE_CHAR(MAX_PROXIMITY_CHARS * sizeof(*mInputCodes)) {
|
||||||
if (DEBUG_DICT) LOGI("UnigramDictionary - constructor");
|
if (DEBUG_DICT) LOGI("UnigramDictionary - constructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
UnigramDictionary::~UnigramDictionary() {}
|
UnigramDictionary::~UnigramDictionary() {}
|
||||||
|
|
||||||
int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates,
|
static inline unsigned int getCodesBufferSize(const int* codes, const int codesSize,
|
||||||
int *ycoordinates, int *codes, int codesSize, unsigned short *outWords, int *frequencies) {
|
const int MAX_PROXIMITY_CHARS) {
|
||||||
|
return sizeof(*codes) * MAX_PROXIMITY_CHARS * codesSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnigramDictionary::isDigraph(const int* codes, const int i, const int codesSize) const {
|
||||||
|
|
||||||
|
// There can't be a digraph if we don't have at least 2 characters to examine
|
||||||
|
if (i + 2 > codesSize) return false;
|
||||||
|
|
||||||
|
// Search for the first char of some digraph
|
||||||
|
int lastDigraphIndex = -1;
|
||||||
|
const int thisChar = codes[i * MAX_PROXIMITY_CHARS];
|
||||||
|
for (lastDigraphIndex = sizeof(GERMAN_UMLAUT_DIGRAPHS) / sizeof(GERMAN_UMLAUT_DIGRAPHS[0]) - 1;
|
||||||
|
lastDigraphIndex >= 0; --lastDigraphIndex) {
|
||||||
|
if (thisChar == GERMAN_UMLAUT_DIGRAPHS[lastDigraphIndex].first) break;
|
||||||
|
}
|
||||||
|
// No match: return early
|
||||||
|
if (lastDigraphIndex < 0) return false;
|
||||||
|
|
||||||
|
// It's an interesting digraph if the second char matches too.
|
||||||
|
return GERMAN_UMLAUT_DIGRAPHS[lastDigraphIndex].second == codes[(i + 1) * MAX_PROXIMITY_CHARS];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mostly the same arguments as the non-recursive version, except:
|
||||||
|
// codes is the original value. It points to the start of the work buffer, and gets passed as is.
|
||||||
|
// codesSize is the size of the user input (thus, it is the size of codesSrc).
|
||||||
|
// codesDest is the current point in the work buffer.
|
||||||
|
// codesSrc is the current point in the user-input, original, content-unmodified buffer.
|
||||||
|
// codesRemain is the remaining size in codesSrc.
|
||||||
|
void UnigramDictionary::getWordWithDigraphSuggestionsRec(const ProximityInfo *proximityInfo,
|
||||||
|
const int *xcoordinates, const int* ycoordinates, const int *codesBuffer,
|
||||||
|
const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain,
|
||||||
|
int* codesDest, unsigned short* outWords, int* frequencies) {
|
||||||
|
|
||||||
|
for (int i = 0; i < codesRemain; ++i) {
|
||||||
|
if (isDigraph(codesSrc, i, codesRemain)) {
|
||||||
|
// Found a digraph. We will try both spellings. eg. the word is "pruefen"
|
||||||
|
|
||||||
|
// Copy the word up to the first char of the digraph, then continue processing
|
||||||
|
// on the remaining part of the word, skipping the second char of the digraph.
|
||||||
|
// In our example, copy "pru" and continue running on "fen"
|
||||||
|
memcpy(codesDest, codesSrc, i * BYTES_IN_ONE_CHAR);
|
||||||
|
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
|
||||||
|
codesBufferSize, flags, codesSrc + (i + 1) * MAX_PROXIMITY_CHARS,
|
||||||
|
codesRemain - i - 1, codesDest + i * MAX_PROXIMITY_CHARS,
|
||||||
|
outWords, frequencies);
|
||||||
|
|
||||||
|
// Copy the second char of the digraph in place, then continue processing on
|
||||||
|
// the remaining part of the word.
|
||||||
|
// In our example, after "pru" in the buffer copy the "e", and continue running on "fen"
|
||||||
|
memcpy(codesDest + i * MAX_PROXIMITY_CHARS, codesSrc + i * MAX_PROXIMITY_CHARS,
|
||||||
|
BYTES_IN_ONE_CHAR);
|
||||||
|
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
|
||||||
|
codesBufferSize, flags, codesSrc + i * MAX_PROXIMITY_CHARS, codesRemain - i,
|
||||||
|
codesDest + i * MAX_PROXIMITY_CHARS, outWords, frequencies);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we come here, we hit the end of the word: let's check it against the dictionary.
|
||||||
|
// In our example, we'll come here once for "prufen" and then once for "pruefen".
|
||||||
|
// If the word contains several digraphs, we'll come it for the product of them.
|
||||||
|
// eg. if the word is "ueberpruefen" we'll test, in order, against
|
||||||
|
// "uberprufen", "uberpruefen", "ueberprufen", "ueberpruefen".
|
||||||
|
const unsigned int remainingBytes = BYTES_IN_ONE_CHAR * codesRemain;
|
||||||
|
if (0 != remainingBytes)
|
||||||
|
memcpy(codesDest, codesSrc, remainingBytes);
|
||||||
|
|
||||||
|
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
|
||||||
|
(codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, outWords, frequencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
int UnigramDictionary::getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
|
||||||
|
const int *ycoordinates, const int *codes, const int codesSize, const int flags,
|
||||||
|
unsigned short *outWords, int *frequencies) {
|
||||||
|
|
||||||
|
if (REQUIRES_GERMAN_UMLAUT_PROCESSING & flags)
|
||||||
|
{ // Incrementally tune the word and try all possibilities
|
||||||
|
int codesBuffer[getCodesBufferSize(codes, codesSize, MAX_PROXIMITY_CHARS)];
|
||||||
|
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
|
||||||
|
codesSize, flags, codes, codesSize, codesBuffer, outWords, frequencies);
|
||||||
|
} else { // Normal processing
|
||||||
|
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize,
|
||||||
|
outWords, frequencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
PROF_START(6);
|
||||||
|
// Get the word count
|
||||||
|
int suggestedWordsCount = 0;
|
||||||
|
while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) {
|
||||||
|
suggestedWordsCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_DICT) {
|
||||||
|
LOGI("Returning %d words", suggestedWordsCount);
|
||||||
|
LOGI("Next letters: ");
|
||||||
|
for (int k = 0; k < NEXT_LETTERS_SIZE; k++) {
|
||||||
|
if (mNextLettersFrequency[k] > 0) {
|
||||||
|
LOGI("%c = %d,", k, mNextLettersFrequency[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PROF_END(6);
|
||||||
|
PROF_CLOSE;
|
||||||
|
return suggestedWordsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnigramDictionary::getWordSuggestions(const ProximityInfo *proximityInfo,
|
||||||
|
const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize,
|
||||||
|
unsigned short *outWords, int *frequencies) {
|
||||||
|
|
||||||
PROF_OPEN;
|
PROF_OPEN;
|
||||||
PROF_START(0);
|
PROF_START(0);
|
||||||
initSuggestions(codes, codesSize, outWords, frequencies);
|
initSuggestions(codes, codesSize, outWords, frequencies);
|
||||||
|
@ -103,30 +219,10 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, int *xcoordi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PROF_END(5);
|
PROF_END(5);
|
||||||
|
|
||||||
PROF_START(6);
|
|
||||||
// Get the word count
|
|
||||||
int suggestedWordsCount = 0;
|
|
||||||
while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) {
|
|
||||||
suggestedWordsCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG_DICT) {
|
|
||||||
LOGI("Returning %d words", suggestedWordsCount);
|
|
||||||
LOGI("Next letters: ");
|
|
||||||
for (int k = 0; k < NEXT_LETTERS_SIZE; k++) {
|
|
||||||
if (mNextLettersFrequency[k] > 0) {
|
|
||||||
LOGI("%c = %d,", k, mNextLettersFrequency[k]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PROF_END(6);
|
|
||||||
PROF_CLOSE;
|
|
||||||
return suggestedWordsCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnigramDictionary::initSuggestions(int *codes, int codesSize, unsigned short *outWords,
|
void UnigramDictionary::initSuggestions(const int *codes, const int codesSize,
|
||||||
int *frequencies) {
|
unsigned short *outWords, int *frequencies) {
|
||||||
if (DEBUG_DICT) LOGI("initSuggest");
|
if (DEBUG_DICT) LOGI("initSuggest");
|
||||||
mFrequencies = frequencies;
|
mFrequencies = frequencies;
|
||||||
mOutputChars = outWords;
|
mOutputChars = outWords;
|
||||||
|
@ -204,7 +300,7 @@ bool UnigramDictionary::sameAsTyped(unsigned short *word, int length) {
|
||||||
if (length != mInputLength) {
|
if (length != mInputLength) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int *inputCodes = mInputCodes;
|
const int *inputCodes = mInputCodes;
|
||||||
while (length--) {
|
while (length--) {
|
||||||
if ((unsigned int) *inputCodes != (unsigned int) *word) {
|
if ((unsigned int) *inputCodes != (unsigned int) *word) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -423,7 +519,7 @@ inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex
|
||||||
const int currentChar = *getInputCharsAt(inputIndex);
|
const int currentChar = *getInputCharsAt(inputIndex);
|
||||||
const int leftIndex = inputIndex - 1;
|
const int leftIndex = inputIndex - 1;
|
||||||
if (leftIndex >= 0) {
|
if (leftIndex >= 0) {
|
||||||
int *leftChars = getInputCharsAt(leftIndex);
|
const int *leftChars = getInputCharsAt(leftIndex);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (leftChars[i] > 0 && i < MAX_PROXIMITY_CHARS) {
|
while (leftChars[i] > 0 && i < MAX_PROXIMITY_CHARS) {
|
||||||
if (leftChars[i++] == currentChar) return true;
|
if (leftChars[i++] == currentChar) return true;
|
||||||
|
@ -431,7 +527,7 @@ inline bool UnigramDictionary::existsAdjacentProximityChars(const int inputIndex
|
||||||
}
|
}
|
||||||
const int rightIndex = inputIndex + 1;
|
const int rightIndex = inputIndex + 1;
|
||||||
if (rightIndex < inputLength) {
|
if (rightIndex < inputLength) {
|
||||||
int *rightChars = getInputCharsAt(rightIndex);
|
const int *rightChars = getInputCharsAt(rightIndex);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (rightChars[i] > 0 && i < MAX_PROXIMITY_CHARS) {
|
while (rightChars[i] > 0 && i < MAX_PROXIMITY_CHARS) {
|
||||||
if (rightChars[i++] == currentChar) return true;
|
if (rightChars[i++] == currentChar) return true;
|
||||||
|
@ -523,7 +619,7 @@ inline bool UnigramDictionary::processCurrentNode(const int pos, const int depth
|
||||||
*newDiffs = diffs;
|
*newDiffs = diffs;
|
||||||
*newInputIndex = inputIndex;
|
*newInputIndex = inputIndex;
|
||||||
} else {
|
} else {
|
||||||
int *currentChars = getInputCharsAt(inputIndex);
|
const int *currentChars = getInputCharsAt(inputIndex);
|
||||||
|
|
||||||
if (transposedPos >= 0) {
|
if (transposedPos >= 0) {
|
||||||
if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS;
|
if (inputIndex == transposedPos) currentChars += MAX_PROXIMITY_CHARS;
|
||||||
|
|
|
@ -33,12 +33,22 @@ class UnigramDictionary {
|
||||||
public:
|
public:
|
||||||
UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier,
|
UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier,
|
||||||
int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion);
|
int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion);
|
||||||
int getSuggestions(ProximityInfo *proximityInfo, int *xcoordinates, int *ycoordinates,
|
int getSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
|
||||||
int *codes, int codesSize, unsigned short *outWords, int *frequencies);
|
const int *ycoordinates, const int *codes, const int codesSize, const int flags,
|
||||||
|
unsigned short *outWords, int *frequencies);
|
||||||
~UnigramDictionary();
|
~UnigramDictionary();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies);
|
void getWordSuggestions(const ProximityInfo *proximityInfo, const int *xcoordinates,
|
||||||
|
const int *ycoordinates, const int *codes, const int codesSize,
|
||||||
|
unsigned short *outWords, int *frequencies);
|
||||||
|
bool isDigraph(const int* codes, const int i, const int codesSize) const;
|
||||||
|
void getWordWithDigraphSuggestionsRec(const ProximityInfo *proximityInfo,
|
||||||
|
const int *xcoordinates, const int* ycoordinates, const int *codesBuffer,
|
||||||
|
const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain,
|
||||||
|
int* codesDest, unsigned short* outWords, int* frequencies);
|
||||||
|
void initSuggestions(const int *codes, const int codesSize, unsigned short *outWords,
|
||||||
|
int *frequencies);
|
||||||
void getSuggestionCandidates(const int skipPos, const int excessivePos,
|
void getSuggestionCandidates(const int skipPos, const int excessivePos,
|
||||||
const int transposedPos, int *nextLetters, const int nextLettersSize,
|
const int transposedPos, int *nextLetters, const int nextLettersSize,
|
||||||
const int maxDepth);
|
const int maxDepth);
|
||||||
|
@ -86,7 +96,7 @@ private:
|
||||||
const int startInputIndex, const int depth, unsigned short *word,
|
const int startInputIndex, const int depth, unsigned short *word,
|
||||||
int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos);
|
int *newChildPosition, int *newCount, bool *newTerminal, int *newFreq, int *siblingPos);
|
||||||
bool existsAdjacentProximityChars(const int inputIndex, const int inputLength);
|
bool existsAdjacentProximityChars(const int inputIndex, const int inputLength);
|
||||||
inline int* getInputCharsAt(const int index) {
|
inline const int* getInputCharsAt(const int index) {
|
||||||
return mInputCodes + (index * MAX_PROXIMITY_CHARS);
|
return mInputCodes + (index * MAX_PROXIMITY_CHARS);
|
||||||
}
|
}
|
||||||
const unsigned char *DICT;
|
const unsigned char *DICT;
|
||||||
|
@ -97,10 +107,20 @@ private:
|
||||||
const int TYPED_LETTER_MULTIPLIER;
|
const int TYPED_LETTER_MULTIPLIER;
|
||||||
const int FULL_WORD_MULTIPLIER;
|
const int FULL_WORD_MULTIPLIER;
|
||||||
const int ROOT_POS;
|
const int ROOT_POS;
|
||||||
|
const unsigned int BYTES_IN_ONE_CHAR;
|
||||||
|
|
||||||
|
// Flags for special processing
|
||||||
|
// Those *must* match the flags in BinaryDictionary.Flags.ALL_FLAGS in BinaryDictionary.java
|
||||||
|
// or something very bad (like, the apocalypse) will happen.
|
||||||
|
// Please update both at the same time.
|
||||||
|
enum {
|
||||||
|
REQUIRES_GERMAN_UMLAUT_PROCESSING = 0x1
|
||||||
|
};
|
||||||
|
static const struct digraph_t { int first; int second; } GERMAN_UMLAUT_DIGRAPHS[];
|
||||||
|
|
||||||
int *mFrequencies;
|
int *mFrequencies;
|
||||||
unsigned short *mOutputChars;
|
unsigned short *mOutputChars;
|
||||||
int *mInputCodes;
|
const int *mInputCodes;
|
||||||
int mInputLength;
|
int mInputLength;
|
||||||
// MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH
|
// MAX_WORD_LENGTH_INTERNAL must be bigger than MAX_WORD_LENGTH
|
||||||
unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
|
unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
|
||||||
|
|
Loading…
Reference in New Issue