aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--java/Android.mk14
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSet.java134
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java114
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java175
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java31
-rw-r--r--java/src/com/android/inputmethod/latin/define/JniLibName.java21
-rw-r--r--native/Android.mk62
-rw-r--r--native/jni/Android.mk87
-rw-r--r--native/jni/Application.mk1
-rw-r--r--native/src/defines.h2
-rw-r--r--native/src/unigram_dictionary.cpp93
-rw-r--r--native/src/unigram_dictionary.h14
-rw-r--r--native/src/words_priority_queue.h146
-rw-r--r--tests/Android.mk14
-rw-r--r--tools/Android.mk4
17 files changed, 590 insertions, 335 deletions
diff --git a/Android.mk b/Android.mk
index 8f1acc55a..91b2fbbb0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -12,6 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-subdir-makefiles)
diff --git a/java/Android.mk b/java/Android.mk
index 43168e563..36ff506bf 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2011 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.
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
new file mode 100644
index 000000000..a803188da
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 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.keyboard;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SettingsValues;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
+import java.util.Locale;
+
+/**
+ * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard
+ * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
+ * {@link KeyboardSet} are related to each other.
+ * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}.
+ */
+public class KeyboardSet {
+ // TODO: Make these KeyboardId private.
+ public final KeyboardId mAlphabetId;
+ public final KeyboardId mSymbolsId;
+ public final KeyboardId mSymbolsShiftedId;
+
+ KeyboardSet(Builder builder) {
+ mAlphabetId = builder.getKeyboardId(false, false);
+ mSymbolsId = builder.getKeyboardId(true, false);
+ mSymbolsShiftedId = builder.getKeyboardId(true, true);
+ }
+
+ public static class Builder {
+ private final Resources mResources;
+ private final EditorInfo mEditorInfo;
+
+ private final int mMode;
+ private final boolean mVoiceKeyEnabled;
+ private final boolean mNoSettingsKey;
+ private final boolean mHasSettingsKey;
+ private final int mF2KeyMode;
+ private final boolean mVoiceKeyOnMain;
+ private final Locale mLocale;
+ private final Configuration mConf;
+ private final DisplayMetrics mMetrics;
+
+ public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) {
+ mResources = context.getResources();
+ mEditorInfo = editorInfo;
+ final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
+ final String packageName = context.getPackageName();
+
+ mMode = Utils.getKeyboardMode(mEditorInfo);
+ final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
+ @SuppressWarnings("deprecation")
+ final boolean noMicrophone = Utils.inPrivateImeOptions(
+ packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
+ || Utils.inPrivateImeOptions(
+ null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
+ mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone;
+ mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
+ mNoSettingsKey = Utils.inPrivateImeOptions(
+ packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
+ mHasSettingsKey = settingsKeyEnabled && !mNoSettingsKey;
+ mF2KeyMode = getF2KeyMode(settingsKeyEnabled, mNoSettingsKey);
+ final boolean forceAscii = Utils.inPrivateImeOptions(
+ packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
+ final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey(
+ LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
+ mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale();
+ mConf = mResources.getConfiguration();
+ mMetrics = mResources.getDisplayMetrics();
+ }
+
+ public KeyboardSet build() {
+ return new KeyboardSet(this);
+ }
+
+ KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) {
+ final int xmlId = getXmlId(mMode, isSymbols, isShift);
+ final boolean hasShortCutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain);
+ return new KeyboardId(mResources.getResourceEntryName(xmlId), xmlId, mLocale,
+ mConf.orientation, mMetrics.widthPixels, mMode, mEditorInfo, mHasSettingsKey,
+ mF2KeyMode, mNoSettingsKey, mVoiceKeyEnabled, hasShortCutKey);
+ }
+
+ private static int getXmlId(int mode, boolean isSymbols, boolean isShift) {
+ switch (mode) {
+ case KeyboardId.MODE_PHONE:
+ return (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
+ case KeyboardId.MODE_NUMBER:
+ return R.xml.kbd_number;
+ default:
+ if (isSymbols) {
+ return isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
+ }
+ return R.xml.kbd_qwerty;
+ }
+ }
+
+ private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
+ if (noSettingsKey) {
+ // Never shows the Settings key
+ return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
+ }
+
+ if (settingsKeyEnabled) {
+ return KeyboardId.F2KEY_MODE_SETTINGS;
+ } else {
+ // It should be alright to fall back to the Settings key on 7-inch layouts
+ // even when the Settings key is not explicitly enabled.
+ return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index cae0edd9f..165e9aec7 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -18,9 +18,7 @@ package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.content.res.Resources;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
@@ -65,16 +63,12 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
private InputView mCurrentInputView;
private LatinKeyboardView mKeyboardView;
private LatinIME mInputMethodService;
- private String mPackageName;
private Resources mResources;
private KeyboardState mState;
- private KeyboardId mMainKeyboardId;
- private KeyboardId mSymbolsKeyboardId;
- private KeyboardId mSymbolsShiftedKeyboardId;
+ private KeyboardSet mKeyboardSet;
- private KeyboardId mCurrentId;
private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
@@ -101,7 +95,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
private void initInternal(LatinIME ims, SharedPreferences prefs) {
mInputMethodService = ims;
- mPackageName = ims.getPackageName();
mResources = ims.getResources();
mPrefs = prefs;
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
@@ -134,25 +127,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
try {
- mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
- mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
- mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
+ mKeyboardSet = new KeyboardSet.Builder(mInputMethodService, editorInfo, settingsValues)
+ .build();
mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols),
hasDistinctMultitouch());
// TODO: Should get rid of this special case handling for Phone Number layouts once we
// have separate layouts with unique KeyboardIds for alphabet and alphabet-shifted
// respectively.
- if (mMainKeyboardId.isPhoneKeyboard()) {
+ if (mKeyboardSet.mAlphabetId.isPhoneKeyboard()) {
mState.onToggleAlphabetAndSymbols();
}
} catch (RuntimeException e) {
- Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
- LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
+ Log.w(TAG, "loading keyboard failed: " + mKeyboardSet.mAlphabetId, e);
+ LatinImeLogger.logOnException(mKeyboardSet.mAlphabetId.toString(), e);
}
}
public void saveKeyboardState() {
- if (mCurrentId != null) {
+ if (isKeyboardAvailable()) {
mState.onSaveKeyboardState();
}
}
@@ -169,8 +161,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
mKeyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
- mCurrentId = keyboard.mId;
- updateShiftLockState(keyboard);
mKeyboardView.setKeyPreviewPopupEnabled(
SettingsValues.isKeyPreviewPopupEnabled(mPrefs, mResources),
SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
@@ -180,19 +170,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
updateShiftState();
}
- private void updateShiftLockState(Keyboard keyboard) {
- if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
- // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
- // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
- // that takes care of the current keyboard having such ALT key or not.
- keyboard.setShiftLocked(keyboard.hasShiftLockKey());
- } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
- // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
- // indicator, we need to call setShiftLocked(false).
- keyboard.setShiftLocked(false);
- }
- }
-
+ // TODO: Move this method to KeyboardSet.
private LatinKeyboard getKeyboard(KeyboardId id) {
final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
LatinKeyboard keyboard = (ref == null) ? null : ref.get();
@@ -231,55 +209,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
return keyboard;
}
- private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
- final boolean isShift, SettingsValues settingsValues) {
- final int mode = Utils.getKeyboardMode(editorInfo);
- final int xmlId;
- switch (mode) {
- case KeyboardId.MODE_PHONE:
- xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
- break;
- case KeyboardId.MODE_NUMBER:
- xmlId = R.xml.kbd_number;
- break;
- default:
- if (isSymbols) {
- xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
- } else {
- xmlId = R.xml.kbd_qwerty;
- }
- break;
- }
-
- final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
- @SuppressWarnings("deprecation")
- final boolean noMicrophone = Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
- || Utils.inPrivateImeOptions(
- null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
- final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
- && !noMicrophone;
- final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
- final boolean noSettingsKey = Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
- final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
- final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
- final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
- final boolean forceAscii = Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
- final boolean asciiCapable = mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(
- LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
- final Locale locale = (forceAscii && !asciiCapable)
- ? Locale.US : mSubtypeSwitcher.getInputLocale();
- final Configuration conf = mResources.getConfiguration();
- final DisplayMetrics dm = mResources.getDisplayMetrics();
-
- return new KeyboardId(
- mResources.getResourceEntryName(xmlId), xmlId, locale, conf.orientation,
- dm.widthPixels, mode, editorInfo, hasSettingsKey, f2KeyMode, noSettingsKey,
- voiceKeyEnabled, hasShortcutKey);
- }
-
public boolean isAlphabetMode() {
final Keyboard keyboard = getLatinKeyboard();
return keyboard != null && keyboard.mId.isAlphabetKeyboard();
@@ -409,19 +338,25 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsKeyboard() {
- setKeyboard(getKeyboard(mSymbolsKeyboardId));
+ setKeyboard(getKeyboard(mKeyboardSet.mSymbolsId));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetKeyboard() {
- setKeyboard(getKeyboard(mMainKeyboardId));
+ setKeyboard(getKeyboard(mKeyboardSet.mAlphabetId));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsShiftedKeyboard() {
- setKeyboard(getKeyboard(mSymbolsShiftedKeyboardId));
+ final Keyboard keyboard = getKeyboard(mKeyboardSet.mSymbolsShiftedId);
+ setKeyboard(keyboard);
+ // TODO: Remove this logic once we introduce initial keyboard shift state attribute.
+ // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a.
+ // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
+ // that takes care of the current keyboard having such shift key or not.
+ keyboard.setShiftLocked(keyboard.hasShiftLockKey());
}
public boolean isInMomentarySwitchState() {
@@ -528,19 +463,4 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
}
}
}
-
- private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
- if (noSettingsKey) {
- // Never shows the Settings key
- return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
- }
-
- if (settingsKeyEnabled) {
- return KeyboardId.F2KEY_MODE_SETTINGS;
- } else {
- // It should be alright to fall back to the Settings key on 7-inch layouts
- // even when the Settings key is not explicitly enabled.
- return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6ea642c92..2d7eed7ab 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -203,7 +203,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private boolean mApplicationSpecifiedCompletionOn;
private WordComposer mWordComposer = new WordComposer();
- private CharSequence mBestWord;
private boolean mHasUncommittedTypedChars;
private int mCorrectionMode;
@@ -1019,7 +1018,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
.setHasMinimalSuggestion(false);
// When in fullscreen mode, show completions generated by the application
setSuggestions(builder.build());
- mBestWord = null;
+ mWordComposer.deleteAutoCorrection();
setSuggestionStripShown(true);
}
}
@@ -1386,13 +1385,20 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void handleBackspace(final int spaceState) {
if (mVoiceProxy.logAndRevertVoiceInput()) return;
-
final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
ic.beginBatchEdit();
+ handleBackspaceWhileInBatchEdit(spaceState, ic);
+ ic.endBatchEdit();
+ }
+ // "ic" may not be null.
+ private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
mVoiceProxy.handleBackspace();
+ // In many cases, we may have to put the keyboard in auto-shift state again.
+ mHandler.postUpdateShiftKeyState();
+
if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
// Cancel multi-character input: remove the text we just entered.
// This is triggered on backspace after a key that inputs multiple characters,
@@ -1401,27 +1407,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
// reverting any autocorrect at this point. So we can safely return.
- ic.endBatchEdit();
return;
}
- final boolean deleteChar = !mHasUncommittedTypedChars;
if (mHasUncommittedTypedChars) {
final int length = mWordComposer.size();
if (length > 0) {
mWordComposer.deleteLast();
- final CharSequence textWithUnderline =
- mComposingStateManager.isAutoCorrectionIndicatorOn()
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- this, mWordComposer.getTypedWord())
- : mWordComposer.getTypedWord();
- ic.setComposingText(textWithUnderline, 1);
+ ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
if (mWordComposer.size() == 0) {
mHasUncommittedTypedChars = false;
- }
- if (1 == length) {
- // 1 == length means we are about to erase the last character of the word,
- // so we can show bigrams.
+ // Remaining size equals zero means we just erased the last character of the
+ // word, so we can show bigrams.
mHandler.postUpdateBigramPredictions();
} else {
// length > 1, so we still have letters to deduce a suggestion from.
@@ -1430,42 +1427,29 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else {
ic.deleteSurroundingText(1, 0);
}
- // If we deleted the last remaining char of a word, we may have to put the keyboard
- // in auto-shift state again.
- mHandler.postUpdateShiftKeyState();
- // If we had uncommitted chars then we know it's not time to revert any auto-correct
- // and that spaceState is NONE.
- ic.endBatchEdit();
- return;
- }
- mHandler.postUpdateShiftKeyState();
-
- if (null != mWordSavedForAutoCorrectCancellation) {
- Utils.Stats.onAutoCorrectionCancellation();
- cancelAutoCorrect(ic);
- mWordSavedForAutoCorrectCancellation = null;
- ic.endBatchEdit();
- return;
} else {
- mWordSavedForAutoCorrectCancellation = null;
- }
-
- if (SPACE_STATE_DOUBLE == spaceState) {
- if (revertDoubleSpace(ic)) {
- ic.endBatchEdit();
- // No need to reset mSpaceState, it has already be done (that's why we
- // receive it as a parameter)
+ if (null != mWordSavedForAutoCorrectCancellation) {
+ Utils.Stats.onAutoCorrectionCancellation();
+ cancelAutoCorrect(ic);
+ mWordSavedForAutoCorrectCancellation = null;
return;
+ } else {
+ mWordSavedForAutoCorrectCancellation = null;
}
- } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
- if (revertSwapPunctuation(ic)) {
- ic.endBatchEdit();
- // Likewise
- return;
+
+ if (SPACE_STATE_DOUBLE == spaceState) {
+ if (revertDoubleSpace(ic)) {
+ // No need to reset mSpaceState, it has already be done (that's why we
+ // receive it as a parameter)
+ return;
+ }
+ } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
+ if (revertSwapPunctuation(ic)) {
+ // Likewise
+ return;
+ }
}
- }
- if (deleteChar) {
if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
// Go back to the suggestion mode if the user canceled the
// "Touch again to save".
@@ -1483,7 +1467,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
}
}
- ic.endBatchEdit();
}
private void handleTab() {
@@ -1512,12 +1495,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x,
final int y, final int spaceState) {
mVoiceProxy.handleCharacter();
-
final InputConnection ic = getCurrentInputConnection();
- if (ic != null) ic.beginBatchEdit();
+ if (null != ic) ic.beginBatchEdit();
+ // TODO: if ic is null, does it make any sense to call this?
+ handleCharacterWhileInBatchEdit(primaryCode, keyCodes, x, y, spaceState, ic);
+ if (null != ic) ic.endBatchEdit();
+ }
+
+ // "ic" may be null without this crashing, but the behavior will be really strange
+ private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes,
+ final int x, final int y, final int spaceState, final InputConnection ic) {
if (SPACE_STATE_MAGIC == spaceState
&& mSettingsValues.isMagicSpaceStripper(primaryCode)) {
- removeTrailingSpaceWhileInBatchEdit(ic);
+ if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic);
}
int code = primaryCode;
@@ -1536,7 +1526,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (switcher.isShiftedOrShiftLocked()) {
if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
|| keyCodes[0] > Character.MAX_CODE_POINT) {
- if (null != ic) ic.endBatchEdit();
return;
}
code = keyCodes[0];
@@ -1550,7 +1539,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else {
// Some keys, such as [eszett], have upper case as multi-characters.
onTextInput(upperCaseString);
- if (null != ic) ic.endBatchEdit();
return;
}
}
@@ -1563,12 +1551,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mWordComposer.setAutoCapitalized(getCurrentAutoCapsState());
mComposingStateManager.onStartComposingText();
}
- final CharSequence textWithUnderline =
- mComposingStateManager.isAutoCorrectionIndicatorOn()
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- this, mWordComposer.getTypedWord())
- : mWordComposer.getTypedWord();
- ic.setComposingText(textWithUnderline, 1);
+ ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
}
mHandler.postUpdateSuggestions();
} else {
@@ -1585,7 +1568,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else {
Utils.Stats.onNonSeparator((char)code, x, y);
}
- if (null != ic) ic.endBatchEdit();
}
private void handleSeparator(final int primaryCode, final int x, final int y,
@@ -1670,10 +1652,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
Utils.Stats.onSeparator((char)primaryCode, x, y);
if (pickedDefault) {
- CharSequence typedWord = mWordComposer.getTypedWord();
- if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) {
+ final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final String typedWord = mWordComposer.getTypedWord();
+ if (TextUtils.isEmpty(typedWord)) {
+ throw new RuntimeException("We have non-committed chars but the typed word "
+ + "is empty? Impossible! I must commit suicide.");
+ }
+ if (!typedWord.equals(autoCorrection)) {
+ // TODO: if the commitCorrection method is not supported by the platform
+ // this will do nothing and the correction will not be committed at all. What
+ // happens on Froyo/Gingerbread, where this API is not present?
InputConnectionCompatUtils.commitCorrection(
- ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
+ ic, mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection);
}
}
mKeyboardSwitcher.updateShiftState();
@@ -1682,6 +1672,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
+ private CharSequence getTextWithUnderline(final CharSequence text) {
+ return mComposingStateManager.isAutoCorrectionIndicatorOn()
+ ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
+ : mWordComposer.getTypedWord();
+ }
+
private void handleClose() {
commitTyped(getCurrentInputConnection());
mVoiceProxy.handleClose();
@@ -1755,18 +1751,21 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mComposingStateManager.isAutoCorrectionIndicatorOn();
final boolean newAutoCorrectionIndicator = Utils.willAutoCorrect(words);
if (oldAutoCorrectionIndicator != newAutoCorrectionIndicator) {
- if (LatinImeLogger.sDBG) {
+ mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
+ if (DEBUG) {
Log.d(TAG, "Flip the indicator. " + oldAutoCorrectionIndicator
+ " -> " + newAutoCorrectionIndicator);
+ if (newAutoCorrectionIndicator
+ != mComposingStateManager.isAutoCorrectionIndicatorOn()) {
+ throw new RuntimeException("Couldn't flip the indicator! We are not "
+ + "composing a word right now.");
+ }
}
- final CharSequence textWithUnderline = newAutoCorrectionIndicator
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- this, mWordComposer.getTypedWord())
- : mWordComposer.getTypedWord();
+ final CharSequence textWithUnderline =
+ getTextWithUnderline(mWordComposer.getTypedWord());
if (!TextUtils.isEmpty(textWithUnderline)) {
ic.setComposingText(textWithUnderline, 1);
}
- mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
}
}
}
@@ -1859,14 +1858,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
setSuggestions(suggestedWords);
if (suggestedWords.size() > 0) {
if (shouldBlockAutoCorrectionBySafetyNet) {
- mBestWord = typedWord;
+ mWordComposer.setAutoCorrection(typedWord);
} else if (suggestedWords.hasAutoCorrectionWord()) {
- mBestWord = suggestedWords.getWord(1);
+ mWordComposer.setAutoCorrection(suggestedWords.getWord(1));
} else {
- mBestWord = typedWord;
+ mWordComposer.setAutoCorrection(typedWord);
}
} else {
- mBestWord = null;
+ // TODO: replace with mWordComposer.deleteAutoCorrection()?
+ mWordComposer.setAutoCorrection(null);
}
setSuggestionStripShown(isSuggestionsStripVisible());
}
@@ -1877,16 +1877,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mHandler.cancelUpdateSuggestions();
updateSuggestions();
}
- if (mBestWord != null && mBestWord.length() > 0) {
- Utils.Stats.onAutoCorrection(mWordComposer.getTypedWord(), mBestWord.toString(),
- separatorCode);
+ final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ if (autoCorrection != null) {
+ final String typedWord = mWordComposer.getTypedWord();
+ Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCode);
mExpectingUpdateSelection = true;
- commitBestWord(mBestWord);
- if (!mBestWord.equals(mWordComposer.getTypedWord())) {
- mWordSavedForAutoCorrectCancellation = mBestWord.toString();
+ commitBestWord(autoCorrection);
+ if (!autoCorrection.equals(typedWord)) {
+ mWordSavedForAutoCorrectCancellation = autoCorrection.toString();
}
// Add the word to the user unigram dictionary if it's not a known word
- addToUserUnigramAndBigramDictionaries(mBestWord,
+ addToUserUnigramAndBigramDictionaries(autoCorrection,
UserUnigramDictionary.FREQUENCY_FOR_TYPED);
return true;
}
@@ -2165,8 +2166,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
final CharSequence word) {
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard());
- // mBestWord will be set appropriately by updateSuggestions() called by the handler
- mBestWord = null;
mHasUncommittedTypedChars = true;
mComposingStateManager.onStartComposingText();
ic.deleteSurroundingText(word.length(), 0);
@@ -2237,8 +2236,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Here we test whether we indeed have a period and a space before us. This should not
// be needed, but it's there just in case something went wrong.
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
- if (!". ".equals(textBeforeCursor))
- return false;
+ if (!". ".equals(textBeforeCursor)) {
+ // We should not have come here if we aren't just after a ". ".
+ throw new RuntimeException("Tried to revert double-space combo but we didn't find "
+ + "\". \" just before the cursor.");
+ }
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
ic.commitText(" ", 1);
@@ -2252,8 +2254,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
// NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
// enter surrogate pairs this code will have been removed.
- if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))
- return false;
+ if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1)) {
+ // We should not have come here if the text before the cursor is not a space.
+ throw new RuntimeException("Tried to revert a swap of punctiation but we didn't "
+ + "find a space just before the cursor.");
+ }
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index de29e8f74..64f4d058b 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -22,6 +22,7 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.define.JniLibName;
import android.content.Context;
import android.content.Intent;
@@ -691,9 +692,13 @@ public class Utils {
public static void loadNativeLibrary() {
try {
- System.loadLibrary("jni_latinime");
+ System.loadLibrary(JniLibName.JNI_LIB_NAME);
} catch (UnsatisfiedLinkError ule) {
- Log.e(TAG, "Could not load native library jni_latinime");
+ Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME);
+ if (LatinImeLogger.sDBG) {
+ throw new RuntimeException(
+ "Could not load native library " + JniLibName.JNI_LIB_NAME);
+ }
}
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index dfb00c8ab..fcaf81cd5 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -41,6 +41,8 @@ public class WordComposer {
private int[] mYCoordinates;
private StringBuilder mTypedWord;
+ // An auto-correction for this word out of the dictionary.
+ private CharSequence mAutoCorrection;
private int mCapsCount;
@@ -60,6 +62,7 @@ public class WordComposer {
mXCoordinates = new int[N];
mYCoordinates = new int[N];
mTrailingSingleQuotesCount = 0;
+ mAutoCorrection = null;
}
public WordComposer(WordComposer source) {
@@ -75,6 +78,7 @@ public class WordComposer {
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
mAutoCapitalized = source.mAutoCapitalized;
mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
+ mAutoCorrection = null;
}
/**
@@ -86,6 +90,7 @@ public class WordComposer {
mCapsCount = 0;
mIsFirstCharCapitalized = false;
mTrailingSingleQuotesCount = 0;
+ mAutoCorrection = null;
}
/**
@@ -140,6 +145,7 @@ public class WordComposer {
} else {
mTrailingSingleQuotesCount = 0;
}
+ mAutoCorrection = null;
}
/**
@@ -173,6 +179,7 @@ public class WordComposer {
int codePoint = word.charAt(i);
addKeyInfo(codePoint, keyboard, keyDetector);
}
+ mAutoCorrection = null;
}
/**
@@ -224,11 +231,12 @@ public class WordComposer {
++mTrailingSingleQuotesCount;
}
}
+ mAutoCorrection = null;
}
/**
* Returns the word as it was typed, without any correction applied.
- * @return the word that was typed so far
+ * @return the word that was typed so far. Never returns null.
*/
public String getTypedWord() {
return mTypedWord.toString();
@@ -277,4 +285,25 @@ public class WordComposer {
public boolean isAutoCapitalized() {
return mAutoCapitalized;
}
+
+ /**
+ * Sets the auto-correction for this word.
+ */
+ public void setAutoCorrection(final CharSequence correction) {
+ mAutoCorrection = correction;
+ }
+
+ /**
+ * Remove any auto-correction that may have been set.
+ */
+ public void deleteAutoCorrection() {
+ mAutoCorrection = null;
+ }
+
+ /**
+ * @return the auto-correction for this world, or null if none.
+ */
+ public CharSequence getAutoCorrectionOrNull() {
+ return mAutoCorrection;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/define/JniLibName.java b/java/src/com/android/inputmethod/latin/define/JniLibName.java
new file mode 100644
index 000000000..3e94a3c07
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/define/JniLibName.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 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.latin.define;
+
+public class JniLibName {
+ public static final String JNI_LIB_NAME = "jni_latinime";
+}
diff --git a/native/Android.mk b/native/Android.mk
index d2537f055..5053e7d64 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -1,61 +1 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
-
-LOCAL_CFLAGS += -Werror -Wall
-
-# To suppress compiler warnings for unused variables/functions used for debug features etc.
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
-
-LOCAL_SRC_FILES := \
- jni/com_android_inputmethod_keyboard_ProximityInfo.cpp \
- jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
- jni/jni_common.cpp \
- src/basechars.cpp \
- src/bigram_dictionary.cpp \
- src/char_utils.cpp \
- src/correction.cpp \
- src/dictionary.cpp \
- src/proximity_info.cpp \
- src/unigram_dictionary.cpp
-
-#FLAG_DBG := true
-#FLAG_DO_PROFILE := true
-
-TARGETING_UNBUNDLED_FROYO := true
-
-ifeq ($(TARGET_ARCH), x86)
- TARGETING_UNBUNDLED_FROYO := false
-endif
-
-ifeq ($(FLAG_DBG), true)
- TARGETING_UNBUNDLED_FROYO := false
-endif
-
-ifeq ($(FLAG_DO_PROFILE), true)
- TARGETING_UNBUNDLED_FROYO := false
-endif
-
-ifeq ($(TARGETING_UNBUNDLED_FROYO), true)
- LOCAL_NDK_VERSION := 4
- LOCAL_SDK_VERSION := 8
-endif
-
-LOCAL_MODULE := libjni_latinime
-
-LOCAL_MODULE_TAGS := user
-
-ifeq ($(FLAG_DO_PROFILE), true)
- $(warning Making profiling version of native library)
- LOCAL_CFLAGS += -DFLAG_DO_PROFILE
- LOCAL_SHARED_LIBRARIES := libcutils libutils
-else # FLAG_DO_PROFILE
-ifeq ($(FLAG_DBG), true)
- $(warning Making debug version of native library)
- LOCAL_CFLAGS += -DFLAG_DBG
- LOCAL_SHARED_LIBRARIES := libcutils libutils
-endif # FLAG_DBG
-endif # FLAG_DO_PROFILE
-
-include $(BUILD_SHARED_LIBRARY)
+include $(call all-subdir-makefiles)
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
new file mode 100644
index 000000000..c4adbfab4
--- /dev/null
+++ b/native/jni/Android.mk
@@ -0,0 +1,87 @@
+# Copyright (C) 2011 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LATIN_IME_SRC_DIR := ../src
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_SRC_DIR)
+
+LOCAL_CFLAGS += -Werror -Wall
+
+# To suppress compiler warnings for unused variables/functions used for debug features etc.
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+
+LATIN_IME_JNI_SRC_FILES := \
+ com_android_inputmethod_keyboard_ProximityInfo.cpp \
+ com_android_inputmethod_latin_BinaryDictionary.cpp \
+ jni_common.cpp
+
+LATIN_IME_CORE_SRC_FILES := \
+ basechars.cpp \
+ bigram_dictionary.cpp \
+ char_utils.cpp \
+ correction.cpp \
+ dictionary.cpp \
+ proximity_info.cpp \
+ unigram_dictionary.cpp
+
+LOCAL_SRC_FILES := \
+ $(LATIN_IME_JNI_SRC_FILES) \
+ $(addprefix $(LATIN_IME_SRC_DIR)/,$(LATIN_IME_CORE_SRC_FILES))
+
+#FLAG_DBG := true
+#FLAG_DO_PROFILE := true
+
+TARGETING_UNBUNDLED_FROYO := true
+
+ifeq ($(TARGET_ARCH), x86)
+ TARGETING_UNBUNDLED_FROYO := false
+endif
+
+ifeq ($(FLAG_DBG), true)
+ TARGETING_UNBUNDLED_FROYO := false
+endif
+
+ifeq ($(FLAG_DO_PROFILE), true)
+ TARGETING_UNBUNDLED_FROYO := false
+endif
+
+ifeq ($(TARGETING_UNBUNDLED_FROYO), true)
+ LOCAL_NDK_VERSION := 4
+ LOCAL_SDK_VERSION := 8
+endif
+
+LOCAL_MODULE := libjni_latinime
+
+LOCAL_MODULE_TAGS := user
+
+# For STL
+LOCAL_C_INCLUDES += external/stlport/stlport bionic
+LOCAL_SHARED_LIBRARIES += libstlport
+
+ifeq ($(FLAG_DO_PROFILE), true)
+ $(warning Making profiling version of native library)
+ LOCAL_CFLAGS += -DFLAG_DO_PROFILE
+ LOCAL_SHARED_LIBRARIES += libcutils libutils
+else # FLAG_DO_PROFILE
+ifeq ($(FLAG_DBG), true)
+ $(warning Making debug version of native library)
+ LOCAL_CFLAGS += -DFLAG_DBG
+ LOCAL_SHARED_LIBRARIES += libcutils libutils
+endif # FLAG_DBG
+endif # FLAG_DO_PROFILE
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/native/jni/Application.mk b/native/jni/Application.mk
new file mode 100644
index 000000000..caf3b2622
--- /dev/null
+++ b/native/jni/Application.mk
@@ -0,0 +1 @@
+APP_STL := stlport_static
diff --git a/native/src/defines.h b/native/src/defines.h
index ef1beb92f..b59f62306 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -101,6 +101,7 @@ static void prof_out(void) {
#define DEBUG_PROXIMITY_INFO true
#define DEBUG_CORRECTION false
#define DEBUG_CORRECTION_FREQ true
+#define DEBUG_WORDS_PRIORITY_QUEUE true
#define DUMP_WORD(word, length) do { dumpWord(word, length); } while(0)
@@ -125,6 +126,7 @@ static void dumpWord(const unsigned short* word, const int length) {
#define DEBUG_PROXIMITY_INFO false
#define DEBUG_CORRECTION false
#define DEBUG_CORRECTION_FREQ false
+#define DEBUG_WORDS_PRIORITY_QUEUE false
#define DUMP_WORD(word, length)
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index 647bfde04..e17e7d07b 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -49,10 +49,12 @@ UnigramDictionary::UnigramDictionary(const uint8_t* const streamStart, int typed
LOGI("UnigramDictionary - constructor");
}
mCorrection = new Correction(typedLetterMultiplier, fullWordMultiplier);
+ mWordsPriorityQueue = new WordsPriorityQueue(maxWords, maxWordLength);
}
UnigramDictionary::~UnigramDictionary() {
delete mCorrection;
+ delete mWordsPriorityQueue;
}
static inline unsigned int getCodesBufferSize(const int* codes, const int codesSize,
@@ -88,7 +90,7 @@ bool UnigramDictionary::isDigraph(const int* codes, const int i, const int codes
void UnigramDictionary::getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo,
const int *xcoordinates, const int* ycoordinates, const int *codesBuffer,
const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain,
- const int currentDepth, int* codesDest, unsigned short* outWords, int* frequencies) {
+ const int currentDepth, int* codesDest) {
if (currentDepth < MAX_UMLAUT_SEARCH_DEPTH) {
for (int i = 0; i < codesRemain; ++i) {
@@ -105,8 +107,7 @@ void UnigramDictionary::getWordWithDigraphSuggestionsRec(ProximityInfo *proximit
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates,
codesBuffer, codesBufferSize, flags,
codesSrc + (i + 1) * MAX_PROXIMITY_CHARS, codesRemain - i - 1,
- currentDepth + 1, codesDest + i * MAX_PROXIMITY_CHARS, outWords,
- frequencies);
+ currentDepth + 1, codesDest + i * MAX_PROXIMITY_CHARS);
// Copy the second char of the digraph in place, then continue processing on
// the remaining part of the word.
@@ -115,8 +116,7 @@ void UnigramDictionary::getWordWithDigraphSuggestionsRec(ProximityInfo *proximit
BYTES_IN_ONE_CHAR);
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates,
codesBuffer, codesBufferSize, flags, codesSrc + i * MAX_PROXIMITY_CHARS,
- codesRemain - i, currentDepth + 1, codesDest + i * MAX_PROXIMITY_CHARS,
- outWords, frequencies);
+ codesRemain - i, currentDepth + 1, codesDest + i * MAX_PROXIMITY_CHARS);
return;
}
}
@@ -132,8 +132,7 @@ void UnigramDictionary::getWordWithDigraphSuggestionsRec(ProximityInfo *proximit
memcpy(codesDest, codesSrc, remainingBytes);
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
- (codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, outWords, frequencies,
- flags);
+ (codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, flags);
}
int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
@@ -144,28 +143,24 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x
{ // 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, 0, codesBuffer, outWords, frequencies);
+ codesSize, flags, codes, codesSize, 0, codesBuffer);
} else { // Normal processing
- getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize,
- outWords, frequencies, flags);
+ getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize, flags);
}
PROF_START(20);
- // Get the word count
- int suggestedWordsCount = 0;
- while (suggestedWordsCount < MAX_WORDS && mFrequencies[suggestedWordsCount] > 0) {
- suggestedWordsCount++;
- }
+ const int suggestedWordsCount =
+ mWordsPriorityQueue->outputSuggestions(frequencies, outWords);
if (DEBUG_DICT) {
LOGI("Returning %d words", suggestedWordsCount);
/// Print the returned words
for (int j = 0; j < suggestedWordsCount; ++j) {
#ifdef FLAG_DBG
- short unsigned int* w = mOutputChars + j * MAX_WORD_LENGTH;
+ short unsigned int* w = outWords + j * MAX_WORD_LENGTH;
char s[MAX_WORD_LENGTH];
for (int i = 0; i <= MAX_WORD_LENGTH; i++) s[i] = w[i];
- LOGI("%s %i", s, mFrequencies[j]);
+ LOGI("%s %i", s, frequencies[j]);
#endif
}
}
@@ -176,12 +171,12 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x
void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize,
- unsigned short *outWords, int *frequencies, const int flags) {
+ const int flags) {
PROF_OPEN;
PROF_START(0);
initSuggestions(
- proximityInfo, xcoordinates, ycoordinates, codes, codesSize, outWords, frequencies);
+ proximityInfo, xcoordinates, ycoordinates, codes, codesSize);
if (DEBUG_DICT) assert(codesSize == mInputLength);
const int maxDepth = min(mInputLength * MAX_DEPTH_MULTIPLIER, MAX_WORD_LENGTH);
@@ -241,71 +236,19 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
}
void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xCoordinates,
- const int *yCoordinates, const int *codes, const int codesSize,
- unsigned short *outWords, int *frequencies) {
+ const int *yCoordinates, const int *codes, const int codesSize) {
if (DEBUG_DICT) {
LOGI("initSuggest");
}
- mFrequencies = frequencies;
- mOutputChars = outWords;
mInputLength = codesSize;
proximityInfo->setInputParams(codes, codesSize, xCoordinates, yCoordinates);
mProximityInfo = proximityInfo;
+ mWordsPriorityQueue->clear();
}
-// TODO: We need to optimize addWord by using STL or something
// TODO: This needs to take an const unsigned short* and not tinker with its contents
-bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency) {
- word[length] = 0;
- if (DEBUG_DICT && DEBUG_SHOW_FOUND_WORD) {
-#ifdef FLAG_DBG
- char s[length + 1];
- for (int i = 0; i <= length; i++) s[i] = word[i];
- LOGI("Found word = %s, freq = %d", s, frequency);
-#endif
- }
- if (length > MAX_WORD_LENGTH) {
- if (DEBUG_DICT) {
- LOGI("Exceeded max word length.");
- }
- return false;
- }
-
- // Find the right insertion point
- int insertAt = 0;
- while (insertAt < MAX_WORDS) {
- // TODO: How should we sort words with the same frequency?
- if (frequency > mFrequencies[insertAt]) {
- break;
- }
- insertAt++;
- }
- if (insertAt < MAX_WORDS) {
- if (DEBUG_DICT) {
-#ifdef FLAG_DBG
- char s[length + 1];
- for (int i = 0; i <= length; i++) s[i] = word[i];
- LOGI("Added word = %s, freq = %d, %d", s, frequency, S_INT_MAX);
-#endif
- }
- memmove((char*) mFrequencies + (insertAt + 1) * sizeof(mFrequencies[0]),
- (char*) mFrequencies + insertAt * sizeof(mFrequencies[0]),
- (MAX_WORDS - insertAt - 1) * sizeof(mFrequencies[0]));
- mFrequencies[insertAt] = frequency;
- memmove((char*) mOutputChars + (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short),
- (char*) mOutputChars + insertAt * MAX_WORD_LENGTH * sizeof(short),
- (MAX_WORDS - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH);
- unsigned short *dest = mOutputChars + insertAt * MAX_WORD_LENGTH;
- while (length--) {
- *dest++ = *word++;
- }
- *dest = 0; // NULL terminate
- if (DEBUG_DICT) {
- LOGI("Added word at %d", insertAt);
- }
- return true;
- }
- return false;
+void UnigramDictionary::addWord(unsigned short *word, int length, int frequency) {
+ mWordsPriorityQueue->push(frequency, word, length);
}
static const char QUOTE = '\'';
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index 4f4fef267..506ed62fc 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -22,6 +22,7 @@
#include "correction_state.h"
#include "defines.h"
#include "proximity_info.h"
+#include "words_priority_queue.h"
namespace latinime {
@@ -73,18 +74,16 @@ public:
private:
void getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
- const int *ycoordinates, const int *codes, const int codesSize,
- unsigned short *outWords, int *frequencies, const int flags);
+ const int *ycoordinates, const int *codes, const int codesSize, const int flags);
bool isDigraph(const int* codes, const int i, const int codesSize) const;
void getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo,
const int *xcoordinates, const int* ycoordinates, const int *codesBuffer,
const int codesBufferSize, const int flags, const int* codesSrc, const int codesRemain,
- const int currentDepth, int* codesDest, unsigned short* outWords, int* frequencies);
+ const int currentDepth, int* codesDest);
void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
- const int *ycoordinates, const int *codes, const int codesSize,
- unsigned short *outWords, int *frequencies);
+ const int *ycoordinates, const int *codes, const int codesSize);
void getSuggestionCandidates(const bool useFullEditDistance);
- bool addWord(unsigned short *word, int length, int frequency);
+ void addWord(unsigned short *word, int length, int frequency);
void getSplitTwoWordsSuggestion(const int inputLength, Correction *correction);
void getMissingSpaceWords(const int inputLength, const int missingSpacePos,
Correction *correction, const bool useFullEditDistance);
@@ -123,8 +122,7 @@ private:
};
static const struct digraph_t { int first; int second; } GERMAN_UMLAUT_DIGRAPHS[];
- int *mFrequencies;
- unsigned short *mOutputChars;
+ WordsPriorityQueue *mWordsPriorityQueue;
ProximityInfo *mProximityInfo;
Correction *mCorrection;
int mInputLength;
diff --git a/native/src/words_priority_queue.h b/native/src/words_priority_queue.h
new file mode 100644
index 000000000..366b1b67a
--- /dev/null
+++ b/native/src/words_priority_queue.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef LATINIME_WORDS_PRIORITY_QUEUE_H
+#define LATINIME_WORDS_PRIORITY_QUEUE_H
+
+#include <iostream>
+#include <queue>
+#include "defines.h"
+
+namespace latinime {
+
+class WordsPriorityQueue {
+private:
+ class SuggestedWord {
+ public:
+ int mScore;
+ unsigned short mWord[MAX_WORD_LENGTH_INTERNAL];
+ int mWordLength;
+ bool mUsed;
+
+ void setParams(int score, unsigned short* word, int wordLength) {
+ mScore = score;
+ mWordLength = wordLength;
+ memcpy(mWord, word, sizeof(unsigned short) * wordLength);
+ mUsed = true;
+ }
+ };
+
+ struct wordComparator {
+ bool operator ()(SuggestedWord * left, SuggestedWord * right) {
+ return left->mScore > right->mScore;
+ }
+ };
+
+ SuggestedWord* getFreeSuggestedWord(int score, unsigned short* word,
+ int wordLength) {
+ for (unsigned int i = 0; i < MAX_WORD_LENGTH; ++i) {
+ if (!mSuggestedWords[i].mUsed) {
+ mSuggestedWords[i].setParams(score, word, wordLength);
+ return &mSuggestedWords[i];
+ }
+ }
+ return 0;
+ }
+
+ typedef std::priority_queue<SuggestedWord*, std::vector<SuggestedWord*>,
+ wordComparator> Suggestions;
+ Suggestions mSuggestions;
+ const unsigned int MAX_WORDS;
+ const unsigned int MAX_WORD_LENGTH;
+ SuggestedWord* mSuggestedWords;
+
+public:
+ WordsPriorityQueue(int maxWords, int maxWordLength) :
+ MAX_WORDS((unsigned int) maxWords), MAX_WORD_LENGTH(
+ (unsigned int) maxWordLength) {
+ mSuggestedWords = new SuggestedWord[maxWordLength];
+ for (int i = 0; i < maxWordLength; ++i) {
+ mSuggestedWords[i].mUsed = false;
+ }
+ }
+ ~WordsPriorityQueue() {
+ delete[] mSuggestedWords;
+ }
+
+ void push(int score, unsigned short* word, int wordLength) {
+ SuggestedWord* sw = 0;
+ if (mSuggestions.size() >= MAX_WORDS) {
+ sw = mSuggestions.top();
+ const int minScore = sw->mScore;
+ if (minScore >= score) {
+ return;
+ } else {
+ sw->mUsed = false;
+ mSuggestions.pop();
+ }
+ }
+ if (sw == 0) {
+ sw = getFreeSuggestedWord(score, word, wordLength);
+ } else {
+ sw->setParams(score, word, wordLength);
+ }
+ if (sw == 0) {
+ LOGE("SuggestedWord is accidentally null.");
+ return;
+ }
+ if (DEBUG_WORDS_PRIORITY_QUEUE) {
+ LOGI("Push word. %d, %d", score, wordLength);
+ DUMP_WORD(word, wordLength);
+ }
+ mSuggestions.push(sw);
+ }
+
+ int outputSuggestions(int *frequencies, unsigned short *outputChars) {
+ const unsigned int size = min(MAX_WORDS, mSuggestions.size());
+ int index = size - 1;
+ while (!mSuggestions.empty() && index >= 0) {
+ SuggestedWord* sw = mSuggestions.top();
+ if (DEBUG_WORDS_PRIORITY_QUEUE) {
+ LOGI("dump word. %d", sw->mScore);
+ DUMP_WORD(sw->mWord, sw->mWordLength);
+ }
+ const unsigned int wordLength = sw->mWordLength;
+ char* targetAdr = (char*) outputChars
+ + (index) * MAX_WORD_LENGTH * sizeof(short);
+ frequencies[index] = sw->mScore;
+ memcpy(targetAdr, sw->mWord, (wordLength) * sizeof(short));
+ if (wordLength < MAX_WORD_LENGTH) {
+ ((unsigned short*) targetAdr)[wordLength] = 0;
+ }
+ sw->mUsed = false;
+ mSuggestions.pop();
+ --index;
+ }
+ return size;
+ }
+
+ void clear() {
+ while (!mSuggestions.empty()) {
+ SuggestedWord* sw = mSuggestions.top();
+ if (DEBUG_WORDS_PRIORITY_QUEUE) {
+ LOGI("Clear word. %d", sw->mScore);
+ DUMP_WORD(sw->mWord, sw->mWordLength);
+ }
+ sw->mUsed = false;
+ mSuggestions.pop();
+ }
+ }
+};
+}
+
+#endif // LATINIME_WORDS_PRIORITY_QUEUE_H
diff --git a/tests/Android.mk b/tests/Android.mk
index 658e8e294..6634070e9 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -1,3 +1,17 @@
+# Copyright (C) 2011 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.
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
diff --git a/tools/Android.mk b/tools/Android.mk
index 8f1acc55a..91b2fbbb0 100644
--- a/tools/Android.mk
+++ b/tools/Android.mk
@@ -12,6 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-subdir-makefiles)