aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/drawable-hdpi/more_suggestions_hint.pngbin249 -> 242 bytes
-rw-r--r--java/res/drawable-mdpi/more_suggestions_hint.pngbin226 -> 222 bytes
-rw-r--r--java/res/drawable-xhdpi/more_suggestions_hint.pngbin322 -> 310 bytes
-rw-r--r--java/res/values-sw768dp/config.xml1
-rw-r--r--java/res/xml/method.xml22
-rw-r--r--java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java5
-rw-r--r--java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java15
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboard.java15
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java173
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java12
-rw-r--r--java/src/com/android/inputmethod/latin/CandidateView.java9
-rw-r--r--java/src/com/android/inputmethod/latin/FileTransforms.java40
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java12
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java35
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java28
-rw-r--r--native/src/correction.cpp229
-rw-r--r--native/src/correction.h3
-rw-r--r--native/src/defines.h10
-rw-r--r--native/src/unigram_dictionary.cpp35
-rw-r--r--native/src/unigram_dictionary.h3
21 files changed, 442 insertions, 208 deletions
diff --git a/java/res/drawable-hdpi/more_suggestions_hint.png b/java/res/drawable-hdpi/more_suggestions_hint.png
index 4515f4434..93604752b 100644
--- a/java/res/drawable-hdpi/more_suggestions_hint.png
+++ b/java/res/drawable-hdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/drawable-mdpi/more_suggestions_hint.png b/java/res/drawable-mdpi/more_suggestions_hint.png
index 6168de353..7352810d3 100644
--- a/java/res/drawable-mdpi/more_suggestions_hint.png
+++ b/java/res/drawable-mdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/more_suggestions_hint.png b/java/res/drawable-xhdpi/more_suggestions_hint.png
index f577a3617..35fb42087 100644
--- a/java/res/drawable-xhdpi/more_suggestions_hint.png
+++ b/java/res/drawable-xhdpi/more_suggestions_hint.png
Binary files differ
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 0f8f106b7..d88020952 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -32,7 +32,6 @@
<bool name="config_auto_correction_spacebar_led_enabled">false</bool>
<!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
<bool name="config_show_mini_keyboard_at_touched_point">true</bool>
- <integer name="config_delay_update_suggestions">180</integer>
<!-- Long pressing space will invoke IME switcher if > 0, never invoke IME switcher if == 0 -->
<integer name="config_long_press_space_key_timeout">0</integer>
<!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 0bf560d5a..452294e04 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -31,13 +31,13 @@
android:label="@string/subtype_en_US"
android:imeSubtypeLocale="en_US"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="TrySuppressingImeSwitcher"
+ android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_en_GB"
android:imeSubtypeLocale="en_GB"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="TrySuppressingImeSwitcher"
+ android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
@@ -48,61 +48,73 @@
android:label="@string/subtype_generic"
android:imeSubtypeLocale="cs"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="da"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_de_qwerty"
android:imeSubtypeLocale="de_ZZ"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="es"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="fi"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="fr"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="fr_CA"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="fr_CH"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="hr"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="hu"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="it"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -114,21 +126,25 @@
android:label="@string/subtype_generic"
android:imeSubtypeLocale="nb"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="nl"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="pl"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="pt"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
@@ -144,10 +160,12 @@
android:label="@string/subtype_generic"
android:imeSubtypeLocale="sv"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="tr"
android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="AsciiCapable"
/>
</input-method>
diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
index e75559e62..7eb5acda8 100644
--- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java
@@ -21,7 +21,6 @@ import com.android.inputmethod.latin.DictionaryFactory;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.Settings;
import com.android.inputmethod.latin.SharedPreferencesCompat;
-import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.Utils;
import org.xmlpull.v1.XmlPullParserException;
@@ -237,12 +236,12 @@ public class InputLanguageSelection extends PreferenceActivity {
if (finalSize == 0) {
preprocess[finalSize++] =
- new LocaleEntry(SubtypeSwitcher.getFullDisplayName(l, false), l);
+ new LocaleEntry(Utils.getFullDisplayName(l, false), l);
} else {
if (s.equals("zz_ZZ")) {
// ignore this locale
} else {
- final String displayName = SubtypeSwitcher.getFullDisplayName(l, false);
+ final String displayName = Utils.getFullDisplayName(l, false);
preprocess[finalSize++] = new LocaleEntry(displayName, l);
}
}
diff --git a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
index dcb826e8f..71d15dc3d 100644
--- a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
+++ b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.deprecated.voice;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
import android.content.Context;
import android.content.res.Resources;
@@ -221,7 +222,7 @@ public class RecognitionView {
Locale locale = SubtypeSwitcher.getInstance().getInputLocale();
mLanguage.setVisibility(View.VISIBLE);
- mLanguage.setText(SubtypeSwitcher.getFullDisplayName(locale, true));
+ mLanguage.setText(Utils.getFullDisplayName(locale, true));
mPopupLayout.setBackgroundDrawable(mListeningBorder);
break;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 8bf82807a..b1212f424 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -258,8 +258,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
LatinKeyboard keyboard = (ref == null) ? null : ref.get();
if (keyboard == null) {
- final Locale savedLocale = Utils.setSystemLocale(
- mResources, mSubtypeSwitcher.getInputLocale());
+ final Locale savedLocale = Utils.setSystemLocale(mResources, id.mLocale);
try {
keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
} finally {
@@ -319,13 +318,19 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
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, mSubtypeSwitcher.getInputLocale(),
- conf.orientation, dm.widthPixels, mode, editorInfo,
- hasSettingsKey, f2KeyMode, noSettingsKey, voiceKeyEnabled, hasShortcutKey);
+ mResources.getResourceEntryName(xmlId), xmlId, locale, conf.orientation,
+ dm.widthPixels, mode, editorInfo, hasSettingsKey, f2KeyMode, noSettingsKey,
+ voiceKeyEnabled, hasShortcutKey);
}
public int getKeyboardMode() {
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 1b6f57b92..345272044 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -141,7 +141,7 @@ public class LatinKeyboard extends Keyboard {
}
}
- public void setSpacebarTextFadeFactor(float fadeFactor, LatinKeyboardView view) {
+ public void setSpacebarTextFadeFactor(float fadeFactor, KeyboardView view) {
mSpacebarTextFadeFactor = fadeFactor;
updateSpacebarForLocale(false);
if (view != null)
@@ -154,7 +154,7 @@ public class LatinKeyboard extends Keyboard {
return newColor;
}
- public void updateShortcutKey(boolean available, LatinKeyboardView view) {
+ public void updateShortcutKey(boolean available, KeyboardView view) {
if (mShortcutKey == null)
return;
mShortcutKey.setEnabled(available);
@@ -193,9 +193,8 @@ public class LatinKeyboard extends Keyboard {
&& Utils.hasMultipleEnabledIMEsOrSubtypes(imm, true /* include aux subtypes */);
mSpaceKey.setNeedsSpecialPopupHint(shouldShowInputMethodPicker);
// If application locales are explicitly selected.
- if (mSubtypeSwitcher.needsToDisplayLanguage()) {
- mSpaceKey.setIcon(getSpaceDrawable(
- mSubtypeSwitcher.getInputLocale(), isAutoCorrection));
+ if (mSubtypeSwitcher.needsToDisplayLanguage(mId.mLocale)) {
+ mSpaceKey.setIcon(getSpaceDrawable(mId.mLocale, isAutoCorrection));
} else if (isAutoCorrection) {
mSpaceKey.setIcon(getSpaceDrawable(null, true));
} else {
@@ -216,7 +215,7 @@ public class LatinKeyboard extends Keyboard {
final Rect bounds = new Rect();
// Estimate appropriate language name text size to fit in maxTextWidth.
- String language = SubtypeSwitcher.getFullDisplayName(locale, true);
+ String language = Utils.getFullDisplayName(locale, true);
int textWidth = getTextWidth(paint, language, origTextSize, bounds);
// Assuming text width and text size are proportional to each other.
float textSize = origTextSize * Math.min(width / textWidth, 1.0f);
@@ -228,7 +227,7 @@ public class LatinKeyboard extends Keyboard {
final boolean useShortName;
if (useMiddleName) {
- language = SubtypeSwitcher.getMiddleDisplayLanguage(locale);
+ language = Utils.getMiddleDisplayLanguage(locale);
textWidth = getTextWidth(paint, language, origTextSize, bounds);
textSize = origTextSize * Math.min(width / textWidth, 1.0f);
useShortName = (textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME)
@@ -238,7 +237,7 @@ public class LatinKeyboard extends Keyboard {
}
if (useShortName) {
- language = SubtypeSwitcher.getShortDisplayLanguage(locale);
+ language = Utils.getShortDisplayLanguage(locale);
textWidth = getTextWidth(paint, language, origTextSize, bounds);
textSize = origTextSize * Math.min(width / textWidth, 1.0f);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index ed5f83b3b..24bb7b78a 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -63,10 +63,10 @@ public class BinaryDictionaryFileDumper {
}
/**
- * Queries a content provider for the list of dictionaries for a specific locale
+ * Queries a content provider for the list of word lists for a specific locale
* available to copy into Latin IME.
*/
- private static List<String> getDictIdList(final Locale locale, final Context context) {
+ private static List<String> getWordListIds(final Locale locale, final Context context) {
final ContentResolver resolver = context.getContentResolver();
final Uri dictionaryPackUri = getProviderUri(locale.toString());
@@ -87,77 +87,150 @@ public class BinaryDictionaryFileDumper {
return list;
}
+
/**
- * Queries a content provider for dictionary data for some locale and cache the returned files
- *
- * This will query a content provider for dictionary data for a given locale, and copy the
- * files locally so that they can be mmap'ed. This may overwrite previously cached dictionaries
- * with newer versions if a newer version is made available by the content provider.
- * @returns the addresses of the files, or null if no data could be obtained.
- * @throw FileNotFoundException if the provider returns non-existent data.
- * @throw IOException if the provider-returned data could not be read.
+ * Helper method to encapsulate exception handling.
*/
- public static List<AssetFileAddress> cacheDictionariesFromContentProvider(final Locale locale,
- final Context context) {
- final ContentResolver resolver = context.getContentResolver();
- final List<String> idList = getDictIdList(locale, context);
- final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
- for (String id : idList) {
- final Uri wordListUri = getProviderUri(id);
+ private static AssetFileDescriptor openAssetFileDescriptor(final ContentResolver resolver,
+ final Uri uri) {
+ try {
+ return resolver.openAssetFileDescriptor(uri, "r");
+ } catch (FileNotFoundException e) {
+ // I don't want to log the word list URI here for security concerns
+ Log.e(TAG, "Could not find a word list from the dictionary provider.");
+ return null;
+ }
+ }
+
+ /**
+ * Caches a word list the id of which is passed as an argument. This will write the file
+ * to the cache file name designated by its id and locale, overwriting it if already present
+ * and creating it (and its containing directory) if necessary.
+ */
+ private static AssetFileAddress cacheWordList(final String id, final Locale locale,
+ final ContentResolver resolver, final Context context) {
+
+ final int COMPRESSED_CRYPTED_COMPRESSED = 0;
+ final int CRYPTED_COMPRESSED = 1;
+ final int COMPRESSED_CRYPTED = 2;
+ final int COMPRESSED_ONLY = 3;
+ final int CRYPTED_ONLY = 4;
+ final int NONE = 5;
+ final int MODE_MIN = COMPRESSED_CRYPTED_COMPRESSED;
+ final int MODE_MAX = NONE;
+
+ final Uri wordListUri = getProviderUri(id);
+ final String outputFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
+
+ for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
+ InputStream originalSourceStream = null;
+ InputStream inputStream = null;
+ FileOutputStream outputStream = null;
AssetFileDescriptor afd = null;
try {
- afd = resolver.openAssetFileDescriptor(wordListUri, "r");
- } catch (FileNotFoundException e) {
- // leave null inside afd and continue
- }
- if (null == afd) continue;
- try {
- final String fileName = copyFileTo(afd.createInputStream(),
- BinaryDictionaryGetter.getCacheFileName(id, locale, context));
- afd.close();
+ // Open input.
+ afd = openAssetFileDescriptor(resolver, wordListUri);
+ // If we can't open it at all, don't even try a number of times.
+ if (null == afd) return null;
+ originalSourceStream = afd.createInputStream();
+ // Open output.
+ outputStream = new FileOutputStream(outputFileName);
+ // Get the appropriate decryption method for this try
+ switch (mode) {
+ case COMPRESSED_CRYPTED_COMPRESSED:
+ inputStream = FileTransforms.getUncompressedStream(
+ FileTransforms.getDecryptedStream(
+ FileTransforms.getUncompressedStream(
+ originalSourceStream)));
+ break;
+ case CRYPTED_COMPRESSED:
+ inputStream = FileTransforms.getUncompressedStream(
+ FileTransforms.getDecryptedStream(originalSourceStream));
+ break;
+ case COMPRESSED_CRYPTED:
+ inputStream = FileTransforms.getDecryptedStream(
+ FileTransforms.getUncompressedStream(originalSourceStream));
+ break;
+ case COMPRESSED_ONLY:
+ inputStream = FileTransforms.getUncompressedStream(originalSourceStream);
+ break;
+ case CRYPTED_ONLY:
+ inputStream = FileTransforms.getDecryptedStream(originalSourceStream);
+ break;
+ case NONE:
+ inputStream = originalSourceStream;
+ break;
+ }
+ copyFileTo(inputStream, outputStream);
if (0 >= resolver.delete(wordListUri, null, null)) {
- // I'd rather not print the word list ID to the log out of security concerns
Log.e(TAG, "Could not have the dictionary pack delete a word list");
}
- fileAddressList.add(AssetFileAddress.makeFromFileName(fileName));
- } catch (IOException e) {
- // Can't read the file for some reason. Continue onto the next file.
- Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e);
+ // Success! Close files (through the finally{} clause) and return.
+ return AssetFileAddress.makeFromFileName(outputFileName);
+ } catch (Exception e) {
+ Log.e(TAG, "Can't open word list in mode " + mode + " : " + e);
+ // Try the next method.
+ } finally {
+ // Ignore exceptions while closing files.
+ try {
+ // afd.close() will close inputStream, we should not call inputStream.close().
+ if (null != afd) afd.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while closing a cross-process file descriptor : " + e);
+ }
+ try {
+ if (null != outputStream) outputStream.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while closing a file : " + e);
+ }
}
}
- return fileAddressList;
+
+ // We could not copy the file at all. This is very unexpected.
+ // I'd rather not print the word list ID to the log out of security concerns
+ Log.e(TAG, "Could not copy a word list. Will not be able to use it.");
+ // If we can't copy it we should probably delete it to avoid trying to copy it over
+ // and over each time we open LatinIME.
+ if (0 >= resolver.delete(wordListUri, null, null)) {
+ Log.e(TAG, "In addition, we were unable to delete it.");
+ }
+ return null;
}
/**
- * Accepts a resource number as dictionary data for some locale and returns the name of a file.
+ * Queries a content provider for word list data for some locale and cache the returned files
*
- * This will make the resource the cached dictionary for this locale, overwriting any previous
- * cached data.
+ * This will query a content provider for word list data for a given locale, and copy the
+ * files locally so that they can be mmap'ed. This may overwrite previously cached word lists
+ * with newer versions if a newer version is made available by the content provider.
+ * @returns the addresses of the word list files, or null if no data could be obtained.
+ * @throw FileNotFoundException if the provider returns non-existent data.
+ * @throw IOException if the provider-returned data could not be read.
*/
- public static String getDictionaryFileFromResource(int resource, Locale locale,
- Context context) throws FileNotFoundException, IOException {
- final Resources res = context.getResources();
- final Locale savedLocale = Utils.setSystemLocale(res, locale);
- final InputStream stream = res.openRawResource(resource);
- Utils.setSystemLocale(res, savedLocale);
- return copyFileTo(stream,
- BinaryDictionaryGetter.getCacheFileName(Integer.toString(resource),
- locale, context));
+ public static List<AssetFileAddress> cacheWordListsFromContentProvider(final Locale locale,
+ final Context context) {
+ final ContentResolver resolver = context.getContentResolver();
+ final List<String> idList = getWordListIds(locale, context);
+ final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
+ for (String id : idList) {
+ final AssetFileAddress afd = cacheWordList(id, locale, resolver, context);
+ if (null != afd) {
+ fileAddressList.add(afd);
+ }
+ }
+ return fileAddressList;
}
/**
- * Copies the data in an input stream to a target file, creating the file if necessary and
- * overwriting it if it already exists.
+ * Copies the data in an input stream to a target file.
* @param input the stream to be copied.
- * @param outputFileName the name of a file to copy the data to. It is created if necessary.
+ * @param outputFile an outputstream to copy the data to.
*/
- private static String copyFileTo(final InputStream input, final String outputFileName)
+ private static void copyFileTo(final InputStream input, final FileOutputStream output)
throws FileNotFoundException, IOException {
final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE];
- final FileOutputStream output = new FileOutputStream(outputFileName);
for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer))
output.write(buffer, 0, readBytes);
input.close();
- return outputFileName;
}
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 5d2dab0a9..38344300c 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -205,7 +205,7 @@ class BinaryDictionaryGetter {
* @param context the context on which to open the files upon.
* @return an array of binary dictionary files, which may be empty but may not be null.
*/
- private static File[] getCachedDictionaryList(final Locale locale,
+ private static File[] getCachedWordLists(final Locale locale,
final Context context) {
final String directoryName = getCacheDirectoryForLocale(locale, context);
final File[] cacheFiles = new File(directoryName).listFiles();
@@ -235,11 +235,11 @@ class BinaryDictionaryGetter {
public static List<AssetFileAddress> getDictionaryFiles(final Locale locale,
final Context context, final int fallbackResId) {
- // cacheDictionariesFromContentProvider returns the list of files it copied to local
+ // cacheWordListsFromContentProvider returns the list of files it copied to local
// storage, but we don't really care about what was copied NOW: what we want is the
// list of everything we ever cached, so we ignore the return value.
- BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context);
- final File[] cachedDictionaryList = getCachedDictionaryList(locale, context);
+ BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context);
+ final File[] cachedWordLists = getCachedWordLists(locale, context);
final String mainDictId = getMainDictId(locale);
@@ -247,8 +247,8 @@ class BinaryDictionaryGetter {
boolean foundMainDict = false;
final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
- // cachedDictionaryList may not be null, see doc for getCachedDictionaryList
- for (final File f : cachedDictionaryList) {
+ // cachedWordLists may not be null, see doc for getCachedDictionaryList
+ for (final File f : cachedWordLists) {
final String wordListId = getWordListIdFromFileName(f.getName());
if (wordListId.equals(mainDictId)) {
foundMainDict = true;
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index d46b4b5b5..5f20c70b4 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -413,8 +413,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
// TODO: This "more suggestions hint" should have nicely designed icon.
word.setCompoundDrawablesWithIntrinsicBounds(
null, null, null, mMoreCandidateHint);
+ // HACK: To align with other TextView that has no compound drawables.
+ word.setCompoundDrawablePadding(-mMoreCandidateHint.getIntrinsicHeight());
} else {
- word.setCompoundDrawables(null, null, null, null);
+ word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
// Disable this candidate if the suggestion is null or empty.
@@ -426,7 +428,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
word.setText(text); // TextView.setText() resets text scale x to 1.0.
word.setTextScaleX(scaleX);
stripView.addView(word);
- setLayoutWeight(word, getCandidateWeight(index), mCandidateStripHeight);
+ setLayoutWeight(word, getCandidateWeight(index), MATCH_PARENT);
if (DBG) {
final CharSequence debugInfo = getDebugInfo(suggestions, pos);
@@ -526,8 +528,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
mPreviewPopup = new PopupWindow(context);
mPreviewText = (TextView) inflater.inflate(R.layout.candidate_preview, null);
- mPreviewPopup.setWindowLayoutMode(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
+ mPreviewPopup.setWindowLayoutMode(WRAP_CONTENT, WRAP_CONTENT);
mPreviewPopup.setContentView(mPreviewText);
mPreviewPopup.setBackgroundDrawable(null);
diff --git a/java/src/com/android/inputmethod/latin/FileTransforms.java b/java/src/com/android/inputmethod/latin/FileTransforms.java
new file mode 100644
index 000000000..d0374e01e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/FileTransforms.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+
+public class FileTransforms {
+ public static OutputStream getCryptedStream(OutputStream out) {
+ // Crypt the stream.
+ return out;
+ }
+
+ public static InputStream getDecryptedStream(InputStream in) {
+ // Decrypt the stream.
+ return in;
+ }
+
+ public static InputStream getUncompressedStream(InputStream in) throws IOException {
+ return new GZIPInputStream(in);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 394414d03..552517bc8 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -106,6 +106,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
*/
public static final String IME_OPTION_NO_SETTINGS_KEY = "noSettingsKey";
+ /**
+ * The private IME option used to indicate that the given text field needs
+ * ASCII code points input.
+ */
+ public static final String IME_OPTION_FORCE_ASCII = "forceAscii";
+
+ /**
+ * The subtype extra value used to indicate that the subtype keyboard layout is capable for
+ * typing ASCII characters.
+ */
+ public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable";
+
private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
// How many continuous deletes at which to start deleting at a higher speed.
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 0a391a77e..d969e39eb 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -415,7 +415,10 @@ public class SubtypeSwitcher {
return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
}
- public boolean needsToDisplayLanguage() {
+ public boolean needsToDisplayLanguage(Locale keyboardLocale) {
+ if (!keyboardLocale.equals(mInputLocale)) {
+ return false;
+ }
return mNeedsToDisplayLanguage;
}
@@ -492,36 +495,8 @@ public class SubtypeSwitcher {
KeyboardSwitcher.getInstance().getKeyboardView().getWindowToken());
}
- public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
- if (returnsNameInThisLocale) {
- return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
- } else {
- return toTitleCase(locale.getDisplayName(), locale);
- }
- }
-
- public static String getDisplayLanguage(Locale locale) {
- return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
- }
-
- public static String getMiddleDisplayLanguage(Locale locale) {
- return toTitleCase((Utils.constructLocaleFromString(
- locale.getLanguage()).getDisplayLanguage(locale)), locale);
- }
-
- public static String getShortDisplayLanguage(Locale locale) {
- return toTitleCase(locale.getLanguage(), locale);
- }
-
- private static String toTitleCase(String s, Locale locale) {
- if (s.length() == 0) {
- return s;
- }
- return s.toUpperCase(locale).charAt(0) + s.substring(1);
- }
-
public String getInputLanguageName() {
- return getDisplayLanguage(getInputLocale());
+ return Utils.getDisplayLanguage(getInputLocale());
}
/////////////////////////////
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 36fbfd951..ff051dcbb 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -769,4 +769,32 @@ public class Utils {
StringBuilderPool.recycle((StringBuilder)garbage);
}
}
+
+ public static String getFullDisplayName(Locale locale, boolean returnsNameInThisLocale) {
+ if (returnsNameInThisLocale) {
+ return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
+ } else {
+ return toTitleCase(locale.getDisplayName(), locale);
+ }
+ }
+
+ public static String getDisplayLanguage(Locale locale) {
+ return toTitleCase(SubtypeLocale.getFullDisplayName(locale), locale);
+ }
+
+ public static String getMiddleDisplayLanguage(Locale locale) {
+ return toTitleCase((constructLocaleFromString(
+ locale.getLanguage()).getDisplayLanguage(locale)), locale);
+ }
+
+ public static String getShortDisplayLanguage(Locale locale) {
+ return toTitleCase(locale.getLanguage(), locale);
+ }
+
+ private static String toTitleCase(String s, Locale locale) {
+ if (s.length() <= 1) {
+ return s;
+ }
+ return s.toUpperCase(locale).charAt(0) + s.substring(1);
+ }
}
diff --git a/native/src/correction.cpp b/native/src/correction.cpp
index fb160149d..fcb8bea5c 100644
--- a/native/src/correction.cpp
+++ b/native/src/correction.cpp
@@ -190,15 +190,15 @@ void Correction::startToTraverseAllNodes() {
}
bool Correction::needsToPrune() const {
- return (mOutputIndex - 1 >= (mTransposedPos >= 0 ? mInputLength - 1 : mMaxDepth)
- || mProximityCount > mMaxEditDistance);
+ return mOutputIndex - 1 >= mMaxDepth || mProximityCount > mMaxEditDistance;
}
+// TODO: inline?
Correction::CorrectionType Correction::processSkipChar(
- const int32_t c, const bool isTerminal) {
+ const int32_t c, const bool isTerminal, const bool inputIndexIncremented) {
mWord[mOutputIndex] = c;
if (needsToTraverseAllNodes() && isTerminal) {
- mTerminalInputIndex = mInputIndex;
+ mTerminalInputIndex = mInputIndex - (inputIndexIncremented ? 1 : 0);
mTerminalOutputIndex = mOutputIndex;
incrementOutputIndex();
return TRAVERSE_ALL_ON_TERMINAL;
@@ -210,15 +210,28 @@ Correction::CorrectionType Correction::processSkipChar(
Correction::CorrectionType Correction::processCharAndCalcState(
const int32_t c, const bool isTerminal) {
+ const int correctionCount = (mSkippedCount + mExcessiveCount + mTransposedCount);
+ // TODO: Change the limit if we'll allow two or more corrections
+ const bool noCorrectionsHappenedSoFar = correctionCount == 0;
+ const bool canTryCorrection = noCorrectionsHappenedSoFar;
if (mNeedsToTraverseAllNodes || isQuote(c)) {
- if (mLastCharExceeded > 0 && mInputIndex == mInputLength - 1
- && mProximityInfo->getMatchedProximityId(mInputIndex, c, false)
- == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
- mLastCharExceeded = false;
- --mExcessiveCount;
+ bool incremented = false;
+ if (mLastCharExceeded && mInputIndex == mInputLength - 1) {
+ // TODO: Do not check the proximity if EditDistance exceeds the threshold
+ const int matchId = mProximityInfo->getMatchedProximityId(mInputIndex, c, true);
+ if (matchId == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
+ mLastCharExceeded = false;
+ --mExcessiveCount;
+ } else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) {
+ mLastCharExceeded = false;
+ --mExcessiveCount;
+ ++mProximityCount;
+ }
+ incrementInputIndex();
+ incremented = true;
}
- return processSkipChar(c, isTerminal);
+ return processSkipChar(c, isTerminal, incremented);
}
if (mExcessivePos >= 0) {
@@ -226,7 +239,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
mExcessivePos = mOutputIndex;
}
if (mExcessivePos < mInputLength - 1) {
- mExceeding = mExcessivePos == mInputIndex;
+ mExceeding = mExcessivePos == mInputIndex && canTryCorrection;
}
}
@@ -237,7 +250,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
}
mSkipPos = mOutputIndex;
}
- mSkipping = mSkipPos == mOutputIndex;
+ mSkipping = mSkipPos == mOutputIndex && canTryCorrection;
}
if (mTransposedPos >= 0) {
@@ -245,7 +258,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
mTransposedPos = mOutputIndex;
}
if (mTransposedPos < mInputLength - 1) {
- mTransposing = mInputIndex == mTransposedPos;
+ mTransposing = mInputIndex == mTransposedPos && canTryCorrection;
}
}
@@ -258,46 +271,95 @@ Correction::CorrectionType Correction::processCharAndCalcState(
} else if (mCorrectionStates[mOutputIndex].mExceeding) {
--mTransposedCount;
++mExcessiveCount;
+ --mExcessivePos;
incrementInputIndex();
} else {
--mTransposedCount;
+ if (DEBUG_CORRECTION) {
+ DUMP_WORD(mWord, mOutputIndex);
+ LOGI("UNRELATED(0): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
+ mTransposedCount, mExcessiveCount, c);
+ }
return UNRELATED;
}
}
- // TODO: sum counters
- const bool checkProximityChars =
- !(mSkippedCount > 0 || mExcessivePos >= 0 || mTransposedPos >= 0);
+ // TODO: Change the limit if we'll allow two or more proximity chars with corrections
+ const bool checkProximityChars = noCorrectionsHappenedSoFar || mProximityCount == 0;
const int matchedProximityCharId = secondTransposing
? ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR
: mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars);
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
- if (mInputIndex - 1 < mInputLength && (mExceeding || mTransposing)
+ // TODO: Optimize
+ // As the current char turned out to be an unrelated char,
+ // we will try other correction-types. Please note that mCorrectionStates[mOutputIndex]
+ // here refers to the previous state.
+ if (canTryCorrection && mCorrectionStates[mOutputIndex].mProximityMatching
+ && mCorrectionStates[mOutputIndex].mExceeding
+ && mProximityInfo->getMatchedProximityId(mInputIndex, mWord[mOutputIndex], false)
+ == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
+ // Conversion p->e
+ ++mExcessiveCount;
+ --mProximityCount;
+ } else if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0
+ && !mCorrectionStates[mOutputIndex].mTransposing
+ && mCorrectionStates[mOutputIndex - 1].mTransposing
+ && mProximityInfo->getMatchedProximityId(
+ mInputIndex, mWord[mOutputIndex - 1], false)
+ == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR
&& mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false)
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
- if (mTransposing) {
- ++mTransposedCount;
- } else {
- ++mExcessiveCount;
- incrementInputIndex();
- }
- } else if (mSkipping && mProximityCount == 0) {
- // Skip this letter and continue deeper
+ // Conversion t->e
+ // Example:
+ // occaisional -> occa sional
+ // mmmmttx -> mmmm(E)mmmmmm
+ mTransposedCount -= 2;
+ ++mExcessiveCount;
+ ++mInputIndex;
+ } else if (mOutputIndex > 0 && mInputIndex > 0 && mTransposedCount > 0
+ && !mCorrectionStates[mOutputIndex].mTransposing
+ && mCorrectionStates[mOutputIndex - 1].mTransposing
+ && mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false)
+ == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
+ // Conversion t->s
+ // Example:
+ // chcolate -> chocolate
+ // mmttx -> mmsmmmmmm
+ mTransposedCount -= 2;
++mSkippedCount;
- return processSkipChar(c, isTerminal);
- } else if (checkProximityChars
- && mInputIndex > 0
+ --mInputIndex;
+ } else if (canTryCorrection && mInputIndex > 0
&& mCorrectionStates[mOutputIndex].mProximityMatching
&& mCorrectionStates[mOutputIndex].mSkipping
&& mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false)
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
+ // Conversion p->s
// Note: This logic tries saving cases like contrst --> contrast -- "a" is one of
// proximity chars of "s", but it should rather be handled as a skipped char.
++mSkippedCount;
--mProximityCount;
- return processSkipChar(c, isTerminal);
+ return processSkipChar(c, isTerminal, false);
+ } else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputLength
+ && mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false)
+ == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
+ // 1.2. Excessive or transpose correction
+ if (mTransposing) {
+ ++mTransposedCount;
+ } else {
+ ++mExcessiveCount;
+ incrementInputIndex();
+ }
+ } else if (mSkipping) {
+ // 3. Skip correction
+ ++mSkippedCount;
+ return processSkipChar(c, isTerminal, false);
} else {
+ if (DEBUG_CORRECTION) {
+ DUMP_WORD(mWord, mOutputIndex);
+ LOGI("UNRELATED(1): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
+ mTransposedCount, mExcessiveCount, c);
+ }
return UNRELATED;
}
} else if (secondTransposing
@@ -312,10 +374,9 @@ Correction::CorrectionType Correction::processCharAndCalcState(
mWord[mOutputIndex] = c;
- mLastCharExceeded = mExcessiveCount == 0 && mSkippedCount == 0
- && mProximityCount == 0 && mTransposedCount == 0
- // TODO: remove this line once excessive correction is conmibned to others.
- && mExcessivePos >= 0 && (mInputIndex == mInputLength - 2);
+ // 4. Last char excessive correction
+ mLastCharExceeded = mExcessiveCount == 0 && mSkippedCount == 0 && mTransposedCount == 0
+ && mProximityCount == 0 && (mInputIndex == mInputLength - 2);
const bool isSameAsUserTypedLength = (mInputLength == mInputIndex + 1) || mLastCharExceeded;
if (mLastCharExceeded) {
++mExcessiveCount;
@@ -326,6 +387,9 @@ Correction::CorrectionType Correction::processCharAndCalcState(
startToTraverseAllNodes();
}
+ const bool needsToTryOnTerminalForTheLastPossibleExcessiveChar =
+ mExceeding && mInputIndex == mInputLength - 2;
+
// Finally, we are ready to go to the next character, the next "virtual node".
// We should advance the input index.
// We do this in this branch of the 'if traverseAllNodes' because we are still matching
@@ -335,7 +399,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
// Also, the next char is one "virtual node" depth more than this char.
incrementOutputIndex();
- if (isSameAsUserTypedLength && isTerminal) {
+ if ((needsToTryOnTerminalForTheLastPossibleExcessiveChar
+ || isSameAsUserTypedLength) && isTerminal) {
mTerminalInputIndex = mInputIndex - 1;
mTerminalOutputIndex = mOutputIndex - 1;
return ON_TERMINAL;
@@ -453,35 +518,25 @@ inline static int editDistance(
int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const int outputIndex,
const int freq, int* editDistanceTable, const Correction* correction) {
const int excessivePos = correction->getExcessivePos();
- const int transposedPos = correction->getTransposedPos();
const int inputLength = correction->mInputLength;
const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER;
const int fullWordMultiplier = correction->FULL_WORD_MULTIPLIER;
const ProximityInfo *proximityInfo = correction->mProximityInfo;
const int skippedCount = correction->mSkippedCount;
- const int transposedCount = correction->mTransposedCount;
- const int excessiveCount = correction->mExcessiveCount;
+ const int transposedCount = correction->mTransposedCount / 2;
+ const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2;
const int proximityMatchedCount = correction->mProximityCount;
const bool lastCharExceeded = correction->mLastCharExceeded;
if (skippedCount >= inputLength || inputLength == 0) {
return -1;
}
- // TODO: remove
- if (transposedPos >= 0 && transposedCount == 0) {
- return -1;
- }
-
- // TODO: remove
- if (excessivePos >= 0 && excessiveCount == 0) {
- return -1;
- }
-
- const bool sameLength = lastCharExceeded ? (inputLength == inputIndex + 2)
+ // TODO: find more robust way
+ bool sameLength = lastCharExceeded ? (inputLength == inputIndex + 2)
: (inputLength == inputIndex + 1);
// TODO: use mExcessiveCount
- int matchCount = inputLength - correction->mProximityCount - (excessivePos >= 0 ? 1 : 0);
+ const int matchCount = inputLength - correction->mProximityCount - excessiveCount;
const unsigned short* word = correction->mWord;
const bool skipped = skippedCount > 0;
@@ -490,29 +545,51 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
- getQuoteCount(proximityInfo->getPrimaryInputWord(), inputLength));
// TODO: Calculate edit distance for transposed and excessive
- int matchWeight;
int ed = 0;
- int adJustedProximityMatchedCount = proximityMatchedCount;
+ int adjustedProximityMatchedCount = proximityMatchedCount;
+
+ int finalFreq = freq;
// TODO: Optimize this.
- if (excessivePos < 0 && transposedPos < 0 && (proximityMatchedCount > 0 || skipped)) {
+ // TODO: Ignoring edit distance for transposed char, for now
+ if (transposedCount == 0 && (proximityMatchedCount > 0 || skipped || excessiveCount > 0)) {
const unsigned short* primaryInputWord = proximityInfo->getPrimaryInputWord();
ed = editDistance(editDistanceTable, primaryInputWord,
inputLength, word, outputIndex + 1);
- matchWeight = powerIntCapped(typedLetterMultiplier, outputIndex + 1 - ed);
- if (ed == 1 && inputLength == outputIndex) {
- // Promote a word with just one skipped char
- multiplyRate(WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE, &matchWeight);
+ const int matchWeight = powerIntCapped(typedLetterMultiplier,
+ max(inputLength, outputIndex + 1) - ed);
+ multiplyIntCapped(matchWeight, &finalFreq);
+
+ // TODO: Demote further if there are two or more excessive chars with longer user input?
+ if (inputLength > outputIndex + 1) {
+ multiplyRate(INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE, &finalFreq);
}
+
ed = max(0, ed - quoteDiffCount);
- adJustedProximityMatchedCount = min(max(0, ed - (outputIndex + 1 - inputLength)),
+
+ if (ed == 1 && (inputLength == outputIndex || inputLength == outputIndex + 2)) {
+ // Promote a word with just one skipped or excessive char
+ if (sameLength) {
+ multiplyRate(WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE, &finalFreq);
+ } else {
+ multiplyIntCapped(typedLetterMultiplier, &finalFreq);
+ }
+ } else if (ed == 0) {
+ multiplyIntCapped(typedLetterMultiplier, &finalFreq);
+ sameLength = true;
+ }
+ adjustedProximityMatchedCount = min(max(0, ed - (outputIndex + 1 - inputLength)),
proximityMatchedCount);
} else {
- matchWeight = powerIntCapped(typedLetterMultiplier, matchCount);
+ // TODO: Calculate the edit distance for transposed char
+ const int matchWeight = powerIntCapped(typedLetterMultiplier, matchCount);
+ multiplyIntCapped(matchWeight, &finalFreq);
}
- // TODO: Demote by edit distance
- int finalFreq = freq * matchWeight;
+ if (proximityInfo->getMatchedProximityId(0, word[0], true)
+ == ProximityInfo::UNRELATED_CHAR) {
+ multiplyRate(FIRST_CHAR_DIFFERENT_DEMOTION_RATE, &finalFreq);
+ }
///////////////////////////////////////////////
// Promotion and Demotion for each correction
@@ -530,13 +607,16 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
}
// Demotion for a word with transposed character
- if (transposedPos >= 0) multiplyRate(
+ if (transposedCount > 0) multiplyRate(
WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq);
// Demotion for a word with excessive character
- if (excessivePos >= 0) {
+ if (excessiveCount > 0) {
multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq);
- if (!proximityInfo->existsAdjacentProximityChars(inputIndex)) {
+ if (!lastCharExceeded && !proximityInfo->existsAdjacentProximityChars(excessivePos)) {
+ if (DEBUG_CORRECTION_FREQ) {
+ LOGI("Double excessive demotion");
+ }
// If an excessive character is not adjacent to the left char or the right char,
// we will demote this word.
multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE, &finalFreq);
@@ -544,7 +624,7 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
}
// Promotion for a word with proximity characters
- for (int i = 0; i < adJustedProximityMatchedCount; ++i) {
+ for (int i = 0; i < adjustedProximityMatchedCount; ++i) {
// A word with proximity corrections
if (DEBUG_DICT_FULL) {
LOGI("Found a proximity correction.");
@@ -553,20 +633,22 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
}
- const int errorCount = proximityMatchedCount + skippedCount;
+ const int errorCount = adjustedProximityMatchedCount > 0
+ ? adjustedProximityMatchedCount
+ : (proximityMatchedCount + transposedCount);
multiplyRate(
100 - CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE * errorCount / inputLength, &finalFreq);
// Promotion for an exactly matched word
- if (matchCount == outputIndex + 1) {
+ if (ed == 0) {
// Full exact match
- if (sameLength && transposedPos < 0 && !skipped && excessivePos < 0) {
+ if (sameLength && transposedCount == 0 && !skipped && excessiveCount == 0) {
finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq);
}
}
// Promote a word with no correction
- if (proximityMatchedCount == 0 && transposedPos < 0 && !skipped && excessivePos < 0) {
+ if (proximityMatchedCount == 0 && transposedCount == 0 && !skipped && excessiveCount == 0) {
multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq);
}
@@ -584,12 +666,16 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
m ... matching
s ... skipping
a ... traversing all
+ t ... transposing
+ e ... exceeding
+ p ... proximity matching
*/
if (matchCount == inputLength && matchCount >= 2 && !skipped
&& word[matchCount] == word[matchCount - 1]) {
multiplyRate(WORDS_WITH_MATCH_SKIP_PROMOTION_RATE, &finalFreq);
}
+ // TODO: Do not use sameLength?
if (sameLength) {
multiplyIntCapped(fullWordMultiplier, &finalFreq);
}
@@ -598,6 +684,13 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
LOGI("calc: %d, %d", outputIndex, sameLength);
}
+ if (DEBUG_CORRECTION_FREQ) {
+ DUMP_WORD(correction->mWord, outputIndex + 1);
+ LOGI("FinalFreq: [P%d, S%d, T%d, E%d] %d, %d, %d, %d, %d", proximityMatchedCount,
+ skippedCount, transposedCount, excessiveCount, lastCharExceeded, sameLength,
+ quoteDiffCount, ed, finalFreq);
+ }
+
return finalFreq;
}
diff --git a/native/src/correction.h b/native/src/correction.h
index 3cd600cf0..f3194b788 100644
--- a/native/src/correction.h
+++ b/native/src/correction.h
@@ -99,7 +99,8 @@ private:
inline bool needsToTraverseAllNodes();
inline void startToTraverseAllNodes();
inline bool isQuote(const unsigned short c);
- inline CorrectionType processSkipChar(const int32_t c, const bool isTerminal);
+ inline CorrectionType processSkipChar(
+ const int32_t c, const bool isTerminal, const bool inputIndexIncremented);
// TODO: remove
inline void incrementProximityCount() {
diff --git a/native/src/defines.h b/native/src/defines.h
index a29fb7e5b..009d0ad3d 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -95,10 +95,12 @@ static void prof_out(void) {
#define DEBUG_DICT true
#define DEBUG_DICT_FULL false
#define DEBUG_EDIT_DISTANCE false
-#define DEBUG_SHOW_FOUND_WORD DEBUG_DICT_FULL
+#define DEBUG_SHOW_FOUND_WORD false
#define DEBUG_NODE DEBUG_DICT_FULL
#define DEBUG_TRACE DEBUG_DICT_FULL
#define DEBUG_PROXIMITY_INFO true
+#define DEBUG_CORRECTION false
+#define DEBUG_CORRECTION_FREQ true
#define DUMP_WORD(word, length) do { dumpWord(word, length); } while(0)
@@ -121,6 +123,8 @@ static void dumpWord(const unsigned short* word, const int length) {
#define DEBUG_NODE false
#define DEBUG_TRACE false
#define DEBUG_PROXIMITY_INFO false
+#define DEBUG_CORRECTION false
+#define DEBUG_CORRECTION_FREQ false
#define DUMP_WORD(word, length)
@@ -178,7 +182,9 @@ static void dumpWord(const unsigned short* word, const int length) {
#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
#define WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE 160
-#define CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE 42
+#define CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE 45
+#define INPUT_EXCEEDS_OUTPUT_DEMOTION_RATE 70
+#define FIRST_CHAR_DIFFERENT_DEMOTION_RATE 96
// This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
// This is only used for the size of array. Not to be used in c functions.
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index 805e1cbb7..4e671a1c4 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -189,32 +189,19 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
// TODO: remove
PROF_START(1);
- // Note: This line is intentionally left blank
+ getSuggestionCandidates();
PROF_END(1);
PROF_START(2);
- // Suggestion with missing character
- if (DEBUG_DICT) {
- LOGI("--- Suggest missing characters");
- }
- getSuggestionCandidates(0, -1, -1);
+ // Note: This line is intentionally left blank
PROF_END(2);
PROF_START(3);
- // Suggestion with excessive character
- if (DEBUG_DICT) {
- LOGI("--- Suggest excessive characters");
- }
- getSuggestionCandidates(-1, 0, -1);
+ // Note: This line is intentionally left blank
PROF_END(3);
PROF_START(4);
- // Suggestion with transposed characters
- // Only suggest words that length is mInputLength
- if (DEBUG_DICT) {
- LOGI("--- Suggest transposed characters");
- }
- getSuggestionCandidates(-1, -1, 0);
+ // Note: This line is intentionally left blank
PROF_END(4);
PROF_START(5);
@@ -328,14 +315,9 @@ bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency)
static const char QUOTE = '\'';
static const char SPACE = ' ';
-void UnigramDictionary::getSuggestionCandidates(const int skipPos,
- const int excessivePos, const int transposedPos) {
- if (DEBUG_DICT) {
- assert(transposedPos + 1 < mInputLength);
- assert(excessivePos < mInputLength);
- assert(missingPos < mInputLength);
- }
- mCorrection->setCorrectionParams(skipPos, excessivePos, transposedPos,
+void UnigramDictionary::getSuggestionCandidates() {
+ // TODO: Remove setCorrectionParams
+ mCorrection->setCorrectionParams(0, 0, 0,
-1 /* spaceProximityPos */, -1 /* missingSpacePos */);
int rootPosition = ROOT_POS;
// Get the number of children of root, then increment the position
@@ -727,6 +709,9 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos,
pos = BinaryFormat::skipFrequency(flags, pos);
*nextSiblingPosition =
BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos);
+ if (DEBUG_DICT_FULL) {
+ LOGI("Traversing was pruned.");
+ }
return false;
}
}
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index cfe63ff79..65746db8d 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -87,8 +87,7 @@ private:
void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
const int *ycoordinates, const int *codes, const int codesSize,
unsigned short *outWords, int *frequencies);
- void getSuggestionCandidates(const int skipPos, const int excessivePos,
- const int transposedPos);
+ void getSuggestionCandidates();
bool addWord(unsigned short *word, int length, int frequency);
void getSplitTwoWordsSuggestion(const int inputLength, Correction *correction);
void getMissingSpaceWords(