aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java49
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/ActionBatch.java12
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java16
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java2
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java37
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java3
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataParser.java2
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java13
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java5
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java37
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java17
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java16
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java20
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java30
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java6
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java16
-rw-r--r--java/src/com/android/inputmethod/latin/PrevWordsInfo.java14
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java55
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java6
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java17
-rw-r--r--java/src/com/android/inputmethod/latin/WordListInfo.java4
-rw-r--r--java/src/com/android/inputmethod/latin/define/ProductionFlag.java3
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java70
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java28
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java24
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java11
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilter.java82
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java41
29 files changed, 409 insertions, 231 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 46caef625..2c87fc1e9 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -33,6 +33,7 @@ import java.util.Locale;
public final class KeyCodeDescriptionMapper {
private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName();
+ private static final String SPOKEN_LETTER_RESOURCE_NAME_FORMAT = "spoken_accented_letter_%04X";
private static final String SPOKEN_EMOJI_RESOURCE_NAME_FORMAT = "spoken_emoji_%04X";
// The resource ID of the string spoken for obscured keys
@@ -71,6 +72,15 @@ public final class KeyCodeDescriptionMapper {
mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS,
R.string.spoken_description_action_previous);
mKeyCodeMap.put(Constants.CODE_EMOJI, R.string.spoken_description_emoji);
+ // Because the upper-case and lower-case mappings of the following letters is depending on
+ // the locale, the upper case descriptions should be defined here. The lower case
+ // descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}.
+ // U+0049: "I" LATIN CAPITAL LETTER I
+ // U+0069: "i" LATIN SMALL LETTER I
+ // U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ mKeyCodeMap.put(0x0049, R.string.spoken_letter_0049);
+ mKeyCodeMap.put(0x0130, R.string.spoken_letter_0130);
}
/**
@@ -271,15 +281,19 @@ public final class KeyCodeDescriptionMapper {
if (shouldObscure && isDefinedNonCtrl) {
return context.getString(OBSCURED_KEY_RES_ID);
}
- if (mKeyCodeMap.indexOfKey(code) >= 0) {
- return context.getString(mKeyCodeMap.get(code));
+ final int index = mKeyCodeMap.indexOfKey(code);
+ if (index >= 0) {
+ return context.getString(mKeyCodeMap.valueAt(index));
}
+ final String accentedLetter = getSpokenAccentedLetterDescriptionId(context, code);
+ if (accentedLetter != null) {
+ return accentedLetter;
+ }
+ // Here, <code>code</code> may be a base letter.
final int spokenEmojiId = getSpokenDescriptionId(
context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT);
if (spokenEmojiId != 0) {
- final String spokenEmoji = context.getString(spokenEmojiId);
- mKeyCodeMap.append(code, spokenEmojiId);
- return spokenEmoji;
+ return context.getString(spokenEmojiId);
}
if (isDefinedNonCtrl) {
return Character.toString((char) code);
@@ -290,12 +304,31 @@ public final class KeyCodeDescriptionMapper {
return context.getString(R.string.spoken_description_unknown, code);
}
- private static int getSpokenDescriptionId(final Context context, final int code,
+ private String getSpokenAccentedLetterDescriptionId(final Context context, final int code) {
+ final boolean isUpperCase = Character.isUpperCase(code);
+ final int baseCode = isUpperCase ? Character.toLowerCase(code) : code;
+ final int baseIndex = mKeyCodeMap.indexOfKey(baseCode);
+ final int resId = (baseIndex >= 0) ? mKeyCodeMap.valueAt(baseIndex)
+ : getSpokenDescriptionId(context, baseCode, SPOKEN_LETTER_RESOURCE_NAME_FORMAT);
+ if (resId == 0) {
+ return null;
+ }
+ final String spokenText = context.getString(resId);
+ return isUpperCase ? context.getString(R.string.spoke_description_upper_case, spokenText)
+ : spokenText;
+ }
+
+ private int getSpokenDescriptionId(final Context context, final int code,
final String resourceNameFormat) {
final String resourceName = String.format(Locale.ROOT, resourceNameFormat, code);
final Resources resources = context.getResources();
- final String packageName = resources.getResourcePackageName(
+ // Note that the resource package name may differ from the context package name.
+ final String resourcePackageName = resources.getResourcePackageName(
R.string.spoken_description_unknown);
- return resources.getIdentifier(resourceName, "string", packageName);
+ final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName);
+ if (resId != 0) {
+ mKeyCodeMap.append(code, resId);
+ }
+ return resId;
}
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
index 706bdea8e..3f69cedee 100644
--- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
+++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java
@@ -325,8 +325,9 @@ public final class ActionBatch {
MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_AVAILABLE,
mWordList.mId, mWordList.mLocale, mWordList.mDescription,
null == mWordList.mLocalFilename ? "" : mWordList.mLocalFilename,
- mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum,
- mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
+ mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
+ mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion,
+ mWordList.mFormatVersion);
PrivateLog.log("Insert 'available' record for " + mWordList.mDescription
+ " and locale " + mWordList.mLocale);
db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
@@ -374,7 +375,7 @@ public final class ActionBatch {
final ContentValues values = MetadataDbHelper.makeContentValues(0,
MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_INSTALLED,
mWordList.mId, mWordList.mLocale, mWordList.mDescription,
- "", mWordList.mRemoteFilename, mWordList.mLastUpdate,
+ "", mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion,
mWordList.mFormatVersion);
PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription
@@ -416,8 +417,9 @@ public final class ActionBatch {
oldValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN),
mWordList.mId, mWordList.mLocale, mWordList.mDescription,
oldValues.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN),
- mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum,
- mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
+ mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
+ mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion,
+ mWordList.mFormatVersion);
PrivateLog.log("Updating record for " + mWordList.mDescription
+ " and locale " + mWordList.mLocale);
db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
index 80def701d..c35995b24 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
@@ -89,10 +89,13 @@ public final class DictionaryProvider extends ContentProvider {
private static final class WordListInfo {
public final String mId;
public final String mLocale;
+ public final String mRawChecksum;
public final int mMatchLevel;
- public WordListInfo(final String id, final String locale, final int matchLevel) {
+ public WordListInfo(final String id, final String locale, final String rawChecksum,
+ final int matchLevel) {
mId = id;
mLocale = locale;
+ mRawChecksum = rawChecksum;
mMatchLevel = matchLevel;
}
}
@@ -106,7 +109,8 @@ public final class DictionaryProvider extends ContentProvider {
private static final class ResourcePathCursor extends AbstractCursor {
// Column names for the cursor returned by this content provider.
- static private final String[] columnNames = { "id", "locale" };
+ static private final String[] columnNames = { MetadataDbHelper.WORDLISTID_COLUMN,
+ MetadataDbHelper.LOCALE_COLUMN, MetadataDbHelper.RAW_CHECKSUM_COLUMN };
// The list of word lists served by this provider that match the client request.
final WordListInfo[] mWordLists;
@@ -141,6 +145,7 @@ public final class DictionaryProvider extends ContentProvider {
switch (column) {
case 0: return mWordLists[mPos].mId;
case 1: return mWordLists[mPos].mLocale;
+ case 2: return mWordLists[mPos].mRawChecksum;
default : return null;
}
}
@@ -357,6 +362,8 @@ public final class DictionaryProvider extends ContentProvider {
final int localeIndex = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN);
final int localFileNameIndex =
results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
+ final int rawChecksumIndex =
+ results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN);
final int statusIndex = results.getColumnIndex(MetadataDbHelper.STATUS_COLUMN);
if (results.moveToFirst()) {
do {
@@ -379,6 +386,7 @@ public final class DictionaryProvider extends ContentProvider {
}
final String wordListLocale = results.getString(localeIndex);
final String wordListLocalFilename = results.getString(localFileNameIndex);
+ final String wordListRawChecksum = results.getString(rawChecksumIndex);
final int wordListStatus = results.getInt(statusIndex);
// Test the requested locale against this wordlist locale. The requested locale
// has to either match exactly or be more specific than the dictionary - a
@@ -412,8 +420,8 @@ public final class DictionaryProvider extends ContentProvider {
final WordListInfo currentBestMatch = dicts.get(wordListCategory);
if (null == currentBestMatch
|| currentBestMatch.mMatchLevel < matchLevel) {
- dicts.put(wordListCategory,
- new WordListInfo(wordListId, wordListLocale, matchLevel));
+ dicts.put(wordListCategory, new WordListInfo(wordListId, wordListLocale,
+ wordListRawChecksum, matchLevel));
}
} while (results.moveToNext());
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java b/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java
index e47e86e4b..ccd054c84 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java
@@ -20,7 +20,7 @@ import java.io.InputStream;
import java.io.IOException;
import java.security.MessageDigest;
-final class MD5Calculator {
+public final class MD5Calculator {
private MD5Calculator() {} // This helper class is not instantiable
public static String checksum(final InputStream in) throws IOException {
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
index 4a8fa51ee..668eb925b 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
@@ -20,6 +20,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
@@ -46,7 +47,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
// used to identify the versions for upgrades. This should never change going forward.
private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 6;
// The current database version.
- private static final int CURRENT_METADATA_DATABASE_VERSION = 7;
+ private static final int CURRENT_METADATA_DATABASE_VERSION = 8;
private final static long NOT_A_DOWNLOAD_ID = -1;
@@ -66,7 +67,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
public static final String VERSION_COLUMN = "version";
public static final String FORMATVERSION_COLUMN = "formatversion";
public static final String FLAGS_COLUMN = "flags";
- public static final int COLUMN_COUNT = 13;
+ public static final String RAW_CHECKSUM_COLUMN = "rawChecksum";
+ public static final int COLUMN_COUNT = 14;
private static final String CLIENT_CLIENT_ID_COLUMN = "clientid";
private static final String CLIENT_METADATA_URI_COLUMN = "uri";
@@ -119,8 +121,9 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
+ CHECKSUM_COLUMN + " TEXT, "
+ FILESIZE_COLUMN + " INTEGER, "
+ VERSION_COLUMN + " INTEGER,"
- + FORMATVERSION_COLUMN + " INTEGER,"
- + FLAGS_COLUMN + " INTEGER,"
+ + FORMATVERSION_COLUMN + " INTEGER, "
+ + FLAGS_COLUMN + " INTEGER, "
+ + RAW_CHECKSUM_COLUMN + " TEXT,"
+ "PRIMARY KEY (" + WORDLISTID_COLUMN + "," + VERSION_COLUMN + "));";
private static final String METADATA_CREATE_CLIENT_TABLE =
"CREATE TABLE IF NOT EXISTS " + CLIENT_TABLE_NAME + " ("
@@ -136,7 +139,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
static final String[] METADATA_TABLE_COLUMNS = { PENDINGID_COLUMN, TYPE_COLUMN,
STATUS_COLUMN, WORDLISTID_COLUMN, LOCALE_COLUMN, DESCRIPTION_COLUMN,
LOCAL_FILENAME_COLUMN, REMOTE_FILENAME_COLUMN, DATE_COLUMN, CHECKSUM_COLUMN,
- FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN };
+ FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN,
+ RAW_CHECKSUM_COLUMN };
// List of all client table columns.
static final String[] CLIENT_TABLE_COLUMNS = { CLIENT_CLIENT_ID_COLUMN,
CLIENT_METADATA_URI_COLUMN, CLIENT_PENDINGID_COLUMN, FLAGS_COLUMN };
@@ -215,6 +219,17 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
createClientTable(db);
}
+ private void addRawChecksumColumnUnlessPresent(final SQLiteDatabase db, final String clientId) {
+ try {
+ db.execSQL("SELECT " + RAW_CHECKSUM_COLUMN + " FROM "
+ + METADATA_TABLE_NAME + " LIMIT 0;");
+ } catch (SQLiteException e) {
+ Log.i(TAG, "No " + RAW_CHECKSUM_COLUMN + " column : creating it");
+ db.execSQL("ALTER TABLE " + METADATA_TABLE_NAME + " ADD COLUMN "
+ + RAW_CHECKSUM_COLUMN + " TEXT;");
+ }
+ }
+
/**
* Upgrade the database. Upgrade from version 3 is supported.
* Version 3 has a DB named METADATA_DATABASE_NAME_STEM containing a table METADATA_TABLE_NAME.
@@ -260,6 +275,12 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME);
onCreate(db);
}
+ // A rawChecksum column that did not exist in the previous versions was added that
+ // corresponds to the md5 checksum of the file after decompression/decryption. This is to
+ // strengthen the system against corrupted dictionary files.
+ // The most secure way to upgrade a database is to just test for the column presence, and
+ // add it if it's not there.
+ addRawChecksumColumnUnlessPresent(db, mClientId);
}
/**
@@ -431,7 +452,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
public static ContentValues makeContentValues(final int pendingId, final int type,
final int status, final String wordlistId, final String locale,
final String description, final String filename, final String url, final long date,
- final String checksum, final long filesize, final int version,
+ final String rawChecksum, final String checksum, final long filesize, final int version,
final int formatVersion) {
final ContentValues result = new ContentValues(COLUMN_COUNT);
result.put(PENDINGID_COLUMN, pendingId);
@@ -443,6 +464,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
result.put(LOCAL_FILENAME_COLUMN, filename);
result.put(REMOTE_FILENAME_COLUMN, url);
result.put(DATE_COLUMN, date);
+ result.put(RAW_CHECKSUM_COLUMN, rawChecksum);
result.put(CHECKSUM_COLUMN, checksum);
result.put(FILESIZE_COLUMN, filesize);
result.put(VERSION_COLUMN, version);
@@ -478,6 +500,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
if (null == result.get(REMOTE_FILENAME_COLUMN)) result.put(REMOTE_FILENAME_COLUMN, "");
// 0 for the update date : 1970/1/1. Unless specified.
if (null == result.get(DATE_COLUMN)) result.put(DATE_COLUMN, 0);
+ // Raw checksum unknown unless specified
+ if (null == result.get(RAW_CHECKSUM_COLUMN)) result.put(RAW_CHECKSUM_COLUMN, "");
// Checksum unknown unless specified
if (null == result.get(CHECKSUM_COLUMN)) result.put(CHECKSUM_COLUMN, "");
// No filesize unless specified
@@ -525,6 +549,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
putStringResult(result, cursor, LOCAL_FILENAME_COLUMN);
putStringResult(result, cursor, REMOTE_FILENAME_COLUMN);
putIntResult(result, cursor, DATE_COLUMN);
+ putStringResult(result, cursor, RAW_CHECKSUM_COLUMN);
putStringResult(result, cursor, CHECKSUM_COLUMN);
putIntResult(result, cursor, FILESIZE_COLUMN);
putIntResult(result, cursor, VERSION_COLUMN);
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
index 5c2289911..63e419871 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java
@@ -52,6 +52,8 @@ public class MetadataHandler {
final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN);
final int updateIndex = results.getColumnIndex(MetadataDbHelper.DATE_COLUMN);
final int fileSizeIndex = results.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN);
+ final int rawChecksumIndex =
+ results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN);
final int checksumIndex = results.getColumnIndex(MetadataDbHelper.CHECKSUM_COLUMN);
final int localFilenameIndex =
results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
@@ -66,6 +68,7 @@ public class MetadataHandler {
results.getString(descriptionColumn),
results.getLong(updateIndex),
results.getLong(fileSizeIndex),
+ results.getString(rawChecksumIndex),
results.getString(checksumIndex),
results.getString(localFilenameIndex),
results.getString(remoteFilenameIndex),
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java b/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java
index 27670fddf..a88173e8e 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java
@@ -37,6 +37,7 @@ public class MetadataParser {
private static final String DESCRIPTION_FIELD_NAME = MetadataDbHelper.DESCRIPTION_COLUMN;
private static final String UPDATE_FIELD_NAME = "update";
private static final String FILESIZE_FIELD_NAME = MetadataDbHelper.FILESIZE_COLUMN;
+ private static final String RAW_CHECKSUM_FIELD_NAME = MetadataDbHelper.RAW_CHECKSUM_COLUMN;
private static final String CHECKSUM_FIELD_NAME = MetadataDbHelper.CHECKSUM_COLUMN;
private static final String REMOTE_FILENAME_FIELD_NAME =
MetadataDbHelper.REMOTE_FILENAME_COLUMN;
@@ -80,6 +81,7 @@ public class MetadataParser {
arguments.get(DESCRIPTION_FIELD_NAME),
Long.parseLong(arguments.get(UPDATE_FIELD_NAME)),
Long.parseLong(arguments.get(FILESIZE_FIELD_NAME)),
+ arguments.get(RAW_CHECKSUM_FIELD_NAME),
arguments.get(CHECKSUM_FIELD_NAME),
null,
arguments.get(REMOTE_FILENAME_FIELD_NAME),
diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java b/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java
index 69bff9597..9e510a68b 100644
--- a/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java
+++ b/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java
@@ -30,6 +30,7 @@ public class WordListMetadata {
public final String mDescription;
public final long mLastUpdate;
public final long mFileSize;
+ public final String mRawChecksum;
public final String mChecksum;
public final String mLocalFilename;
public final String mRemoteFilename;
@@ -50,13 +51,15 @@ public class WordListMetadata {
public WordListMetadata(final String id, final int type,
final String description, final long lastUpdate, final long fileSize,
- final String checksum, final String localFilename, final String remoteFilename,
- final int version, final int formatVersion, final int flags, final String locale) {
+ final String rawChecksum, final String checksum, final String localFilename,
+ final String remoteFilename, final int version, final int formatVersion,
+ final int flags, final String locale) {
mId = id;
mType = type;
mDescription = description;
mLastUpdate = lastUpdate; // In milliseconds
mFileSize = fileSize;
+ mRawChecksum = rawChecksum;
mChecksum = checksum;
mLocalFilename = localFilename;
mRemoteFilename = remoteFilename;
@@ -77,6 +80,7 @@ public class WordListMetadata {
final String description = values.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN);
final Long lastUpdate = values.getAsLong(MetadataDbHelper.DATE_COLUMN);
final Long fileSize = values.getAsLong(MetadataDbHelper.FILESIZE_COLUMN);
+ final String rawChecksum = values.getAsString(MetadataDbHelper.RAW_CHECKSUM_COLUMN);
final String checksum = values.getAsString(MetadataDbHelper.CHECKSUM_COLUMN);
final String localFilename = values.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN);
final String remoteFilename = values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
@@ -98,8 +102,8 @@ public class WordListMetadata {
|| null == locale) {
throw new IllegalArgumentException();
}
- return new WordListMetadata(id, type, description, lastUpdate, fileSize, checksum,
- localFilename, remoteFilename, version, formatVersion, flags, locale);
+ return new WordListMetadata(id, type, description, lastUpdate, fileSize, rawChecksum,
+ checksum, localFilename, remoteFilename, version, formatVersion, flags, locale);
}
@Override
@@ -110,6 +114,7 @@ public class WordListMetadata {
sb.append("\nDescription : ").append(mDescription);
sb.append("\nLastUpdate : ").append(mLastUpdate);
sb.append("\nFileSize : ").append(mFileSize);
+ sb.append("\nRawChecksum : ").append(mRawChecksum);
sb.append("\nChecksum : ").append(mChecksum);
sb.append("\nLocalFilename : ").append(mLocalFilename);
sb.append("\nRemoteFilename : ").append(mRemoteFilename);
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 65242dd76..4a2b37e4c 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -130,7 +130,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
if (mCurrentKey != null && mActivePointerId == pointerId) {
updateReleaseKeyGraphics(mCurrentKey);
- onCodeInput(mCurrentKey.getCode(), x, y);
+ onKeyInput(mCurrentKey, x, y);
mCurrentKey = null;
}
}
@@ -138,7 +138,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
/**
* Performs the specific action for this panel when the user presses a key on the panel.
*/
- protected void onCodeInput(final int code, final int x, final int y) {
+ protected void onKeyInput(final Key key, final int x, final int y) {
+ final int code = key.getCode();
if (code == Constants.CODE_OUTPUT_TEXT) {
mListener.onTextInput(mCurrentKey.getOutputText());
} else if (code != Constants.CODE_UNSPECIFIED) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index b8cf3f89c..e7ab02ac1 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -359,14 +359,16 @@ public final class BinaryDictionary extends Dictionary {
}
@UsedForTesting
- public boolean isValidBigram(final String word0, final String word1) {
- return getBigramProbability(word0, word1) != NOT_A_PROBABILITY;
+ public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) {
+ return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY;
}
- public int getBigramProbability(final String word0, final String word1) {
- if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY;
- final int[] codePoints0 = StringUtils.toCodePointArray(word0);
- final int[] codePoints1 = StringUtils.toCodePointArray(word1);
+ public int getNgramProbability(final PrevWordsInfo prevWordsInfo, final String word) {
+ if (TextUtils.isEmpty(prevWordsInfo.mPrevWord) || TextUtils.isEmpty(word)) {
+ return NOT_A_PROBABILITY;
+ }
+ final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
+ final int[] codePoints1 = StringUtils.toCodePointArray(word);
return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
}
@@ -417,7 +419,7 @@ public final class BinaryDictionary extends Dictionary {
}
// Add a unigram entry to binary dictionary with unigram attributes in native code.
- public void addUnigramWord(final String word, final int probability,
+ public void addUnigramEntry(final String word, final int probability,
final String shortcutTarget, final int shortcutProbability, final boolean isNotAWord,
final boolean isBlacklisted, final int timestamp) {
if (TextUtils.isEmpty(word)) {
@@ -431,25 +433,26 @@ public final class BinaryDictionary extends Dictionary {
mHasUpdated = true;
}
- // Add a bigram entry to binary dictionary with timestamp in native code.
- public void addBigramWords(final String word0, final String word1, final int probability,
+ // Add an n-gram entry to the binary dictionary with timestamp in native code.
+ public void addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
+ final int probability,
final int timestamp) {
- if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
+ if (TextUtils.isEmpty(prevWordsInfo.mPrevWord) || TextUtils.isEmpty(word)) {
return;
}
- final int[] codePoints0 = StringUtils.toCodePointArray(word0);
- final int[] codePoints1 = StringUtils.toCodePointArray(word1);
+ final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
+ final int[] codePoints1 = StringUtils.toCodePointArray(word);
addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability, timestamp);
mHasUpdated = true;
}
- // Remove a bigram entry form binary dictionary in native code.
- public void removeBigramWords(final String word0, final String word1) {
- if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
+ // Remove an n-gram entry from the binary dictionary in native code.
+ public void removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) {
+ if (TextUtils.isEmpty(prevWordsInfo.mPrevWord) || TextUtils.isEmpty(word)) {
return;
}
- final int[] codePoints0 = StringUtils.toCodePointArray(word0);
- final int[] codePoints1 = StringUtils.toCodePointArray(word1);
+ final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
+ final int[] codePoints1 = StringUtils.toCodePointArray(word);
removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
mHasUpdated = true;
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index e428b1d54..72757e086 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -28,6 +28,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
+import com.android.inputmethod.dictionarypack.MD5Calculator;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils.DictionaryInfo;
@@ -38,6 +39,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -167,8 +169,9 @@ public final class BinaryDictionaryFileDumper {
do {
final String wordListId = cursor.getString(0);
final String wordListLocale = cursor.getString(1);
+ final String wordListRawChecksum = cursor.getString(2);
if (TextUtils.isEmpty(wordListId)) continue;
- list.add(new WordListInfo(wordListId, wordListLocale));
+ list.add(new WordListInfo(wordListId, wordListLocale, wordListRawChecksum));
} while (cursor.moveToNext());
return list;
} catch (RemoteException e) {
@@ -217,7 +220,8 @@ public final class BinaryDictionaryFileDumper {
* and creating it (and its containing directory) if necessary.
*/
private static void cacheWordList(final String wordlistId, final String locale,
- final ContentProviderClient providerClient, final Context context) {
+ final String rawChecksum, final ContentProviderClient providerClient,
+ final Context context) {
final int COMPRESSED_CRYPTED_COMPRESSED = 0;
final int CRYPTED_COMPRESSED = 1;
final int COMPRESSED_CRYPTED = 2;
@@ -299,6 +303,13 @@ public final class BinaryDictionaryFileDumper {
checkMagicAndCopyFileTo(bufferedInputStream, bufferedOutputStream);
bufferedOutputStream.flush();
bufferedOutputStream.close();
+ final String actualRawChecksum = MD5Calculator.checksum(
+ new BufferedInputStream(new FileInputStream(outputFile)));
+ Log.i(TAG, "Computed checksum for downloaded dictionary. Expected = " + rawChecksum
+ + " ; actual = " + actualRawChecksum);
+ if (!TextUtils.isEmpty(rawChecksum) && !rawChecksum.equals(actualRawChecksum)) {
+ throw new IOException("Could not decode the file correctly : checksum differs");
+ }
final File finalFile = new File(finalFileName);
finalFile.delete();
if (!outputFile.renameTo(finalFile)) {
@@ -408,7 +419,7 @@ public final class BinaryDictionaryFileDumper {
final List<WordListInfo> idList = getWordListWordListInfos(locale, context,
hasDefaultWordList);
for (WordListInfo id : idList) {
- cacheWordList(id.mId, id.mLocale, providerClient, context);
+ cacheWordList(id.mId, id.mLocale, id.mRawChecksum, providerClient, context);
}
} finally {
providerClient.release();
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index e04fcda27..3fb76b142 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -142,7 +142,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
Log.d(TAG, "loadAccountVocabulary: " + word);
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addWordDynamicallyLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */,
+ addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */,
0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */,
BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
@@ -224,7 +224,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private void addNameLocked(final String name) {
int len = StringUtils.codePointCount(name);
- String prevWord = null;
+ PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
if (Character.isLetter(name.codePointAt(i))) {
@@ -239,19 +239,19 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
if (DEBUG) {
- Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord);
+ Log.d(TAG, "addName " + name + ", " + word + ", "
+ + prevWordsInfo.mPrevWord);
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addWordDynamicallyLocked(word, FREQUENCY_FOR_CONTACTS,
+ addUnigramLocked(word, FREQUENCY_FOR_CONTACTS,
null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */,
false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) {
+ if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && mUseFirstLastBigrams) {
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addBigramDynamicallyLocked(prevWord, word,
- FREQUENCY_FOR_CONTACTS_BIGRAM,
+ addNgramEntryLocked(prevWordsInfo, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
- prevWord = word;
+ prevWordsInfo = new PrevWordsInfo(word);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
index 14c8bb6c3..301b832b6 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
@@ -370,22 +370,23 @@ public class DictionaryFacilitatorForSuggest {
}
public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized,
- final String previousWord, final int timeStampInSeconds,
+ final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds,
final boolean blockPotentiallyOffensive) {
final Dictionaries dictionaries = mDictionaries;
final String[] words = suggestion.split(Constants.WORD_SEPARATOR);
for (int i = 0; i < words.length; i++) {
final String currentWord = words[i];
- final String prevWord = (i == 0) ? previousWord : words[i - 1];
+ final PrevWordsInfo prevWordsInfoForCurrentWord =
+ (i == 0) ? prevWordsInfo : new PrevWordsInfo(words[i - 1]);
final boolean wasCurrentWordAutoCapitalized = (i == 0) ? wasAutoCapitalized : false;
- addWordToUserHistory(dictionaries, prevWord, currentWord,
+ addWordToUserHistory(dictionaries, prevWordsInfoForCurrentWord, currentWord,
wasCurrentWordAutoCapitalized, timeStampInSeconds, blockPotentiallyOffensive);
}
}
- private void addWordToUserHistory(final Dictionaries dictionaries, final String prevWord,
- final String word, final boolean wasAutoCapitalized, final int timeStampInSeconds,
- final boolean blockPotentiallyOffensive) {
+ private void addWordToUserHistory(final Dictionaries dictionaries,
+ final PrevWordsInfo prevWordsInfo, final String word, final boolean wasAutoCapitalized,
+ final int timeStampInSeconds, final boolean blockPotentiallyOffensive) {
final ExpandableBinaryDictionary userHistoryDictionary =
dictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY);
if (userHistoryDictionary == null) {
@@ -430,15 +431,16 @@ public class DictionaryFacilitatorForSuggest {
// We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
// We don't add words with 0-frequency (assuming they would be profanity etc.).
final boolean isValid = maxFreq > 0;
- UserHistoryDictionary.addToDictionary(userHistoryDictionary, prevWord, secondWord,
+ UserHistoryDictionary.addToDictionary(userHistoryDictionary, prevWordsInfo, secondWord,
isValid, timeStampInSeconds);
}
- public void cancelAddingUserHistory(final String previousWord, final String committedWord) {
+ public void cancelAddingUserHistory(final PrevWordsInfo prevWordsInfo,
+ final String committedWord) {
final ExpandableBinaryDictionary userHistoryDictionary =
mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY);
if (userHistoryDictionary != null) {
- userHistoryDictionary.removeBigramDynamically(previousWord, committedWord);
+ userHistoryDictionary.removeNgramDynamically(prevWordsInfo, committedWord);
}
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 629f3fd18..d67253c3b 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -269,9 +269,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
/**
- * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
+ * Adds unigram information of a word to the dictionary. May overwrite an existing entry.
*/
- public void addWordDynamically(final String word, final int frequency,
+ public void addUnigramEntry(final String word, final int frequency,
final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
final boolean isBlacklisted, final int timestamp) {
reloadDictionaryIfRequired();
@@ -282,23 +282,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return;
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addWordDynamicallyLocked(word, frequency, shortcutTarget, shortcutFreq,
+ addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq,
isNotAWord, isBlacklisted, timestamp);
}
});
}
- protected void addWordDynamicallyLocked(final String word, final int frequency,
+ protected void addUnigramLocked(final String word, final int frequency,
final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
final boolean isBlacklisted, final int timestamp) {
- mBinaryDictionary.addUnigramWord(word, frequency, shortcutTarget, shortcutFreq,
+ mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq,
isNotAWord, isBlacklisted, timestamp);
}
/**
- * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry.
+ * Adds n-gram information of a word to the dictionary. May overwrite an existing entry.
*/
- public void addBigramDynamically(final String word0, final String word1,
+ public void addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
final int frequency, final int timestamp) {
reloadDictionaryIfRequired();
asyncExecuteTaskWithWriteLock(new Runnable() {
@@ -308,20 +308,20 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return;
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addBigramDynamicallyLocked(word0, word1, frequency, timestamp);
+ addNgramEntryLocked(prevWordsInfo, word, frequency, timestamp);
}
});
}
- protected void addBigramDynamicallyLocked(final String word0, final String word1,
+ protected void addNgramEntryLocked(final PrevWordsInfo prevWordsInfo, final String word,
final int frequency, final int timestamp) {
- mBinaryDictionary.addBigramWords(word0, word1, frequency, timestamp);
+ mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp);
}
/**
- * Dynamically remove a word bigram in the dictionary.
+ * Dynamically remove the n-gram entry in the dictionary.
*/
- public void removeBigramDynamically(final String word0, final String word1) {
+ public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word1) {
reloadDictionaryIfRequired();
asyncExecuteTaskWithWriteLock(new Runnable() {
@Override
@@ -330,7 +330,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return;
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- mBinaryDictionary.removeBigramWords(word0, word1);
+ mBinaryDictionary.removeNgramEntry(prevWordsInfo, word1);
}
});
}
@@ -428,9 +428,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return mBinaryDictionary.isValidWord(word);
}
- protected boolean isValidBigramLocked(final String word1, final String word2) {
+ protected boolean isValidNgramLocked(final PrevWordsInfo prevWordsInfo, final String word) {
if (mBinaryDictionary == null) return false;
- return mBinaryDictionary.isValidBigram(word1, word2);
+ return mBinaryDictionary.isValidNgram(prevWordsInfo, word);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 232bf7407..9caec3e01 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -48,7 +48,7 @@ public final class LastComposedWord {
public final String mTypedWord;
public final CharSequence mCommittedWord;
public final String mSeparatorString;
- public final String mPrevWord;
+ public final PrevWordsInfo mPrevWordsInfo;
public final int mCapitalizedMode;
public final InputPointers mInputPointers =
new InputPointers(Constants.DICTIONARY_MAX_WORD_LENGTH);
@@ -64,7 +64,7 @@ public final class LastComposedWord {
public LastComposedWord(final ArrayList<Event> events,
final InputPointers inputPointers, final String typedWord,
final CharSequence committedWord, final String separatorString,
- final String prevWord, final int capitalizedMode) {
+ final PrevWordsInfo prevWordsInfo, final int capitalizedMode) {
if (inputPointers != null) {
mInputPointers.copy(inputPointers);
}
@@ -73,7 +73,7 @@ public final class LastComposedWord {
mCommittedWord = committedWord;
mSeparatorString = separatorString;
mActive = true;
- mPrevWord = prevWord;
+ mPrevWordsInfo = prevWordsInfo;
mCapitalizedMode = capitalizedMode;
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5e45275f8..ab7e66a09 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -84,7 +84,6 @@ import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
import com.android.inputmethod.latin.utils.DistracterFilter;
-import com.android.inputmethod.latin.utils.DistracterFilterUtils;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
@@ -1435,12 +1434,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We're checking the previous word in the text field against the memorized previous
// word. If we are composing a word we should have the second word before the cursor
// memorized, otherwise we should have the first.
- final CharSequence rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion(
- currentSettings.mSpacingAndPunctuations,
- mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
- if (!TextUtils.equals(prevWordsInfo.mPrevWord, rereadPrevWord)) {
+ final PrevWordsInfo rereadPrevWordsInfo =
+ mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ currentSettings.mSpacingAndPunctuations,
+ mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
+ if (!TextUtils.equals(prevWordsInfo.mPrevWord, rereadPrevWordsInfo.mPrevWord)) {
throw new RuntimeException("Unexpected previous word: "
- + prevWordsInfo.mPrevWord + " <> " + rereadPrevWord);
+ + prevWordsInfo.mPrevWord + " <> " + rereadPrevWordsInfo.mPrevWord);
}
}
}
@@ -1747,7 +1747,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@UsedForTesting
/* package for test */ DistracterFilter createDistracterFilter() {
- return DistracterFilterUtils.createDistracterFilter(this /* Context */, mKeyboardSwitcher);
+ return new DistracterFilter(this /* Context */,
+ mRichImm.getMyEnabledInputMethodSubtypeList(
+ true /* allowsImplicitlySelectedSubtypes */));
}
public void dumpDictionaryForDebug(final String dictName) {
diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
index 9d8543183..ecc8947db 100644
--- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
+++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
@@ -16,6 +16,9 @@
package com.android.inputmethod.latin;
+import android.util.Log;
+
+// TODO: Support multiple previous words for n-gram.
public class PrevWordsInfo {
// The previous word. May be null after resetting and before starting a new composing word, or
// when there is no context like at the start of text for example. It can also be set to null
@@ -23,7 +26,18 @@ public class PrevWordsInfo {
// or a comma.
public final String mPrevWord;
+ // TODO: Have sentence separator.
+ // Whether the current context is beginning of sentence or not.
+ public final boolean mIsBeginningOfSentence;
+
+ // Beginning of sentence.
+ public PrevWordsInfo() {
+ mPrevWord = null;
+ mIsBeginningOfSentence = true;
+ }
+
public PrevWordsInfo(final String prevWord) {
mPrevWord = prevWord;
+ mIsBeginningOfSentence = false;
}
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 606bb775e..2c54e10aa 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -538,10 +538,12 @@ public final class RichInputConnection {
}
@SuppressWarnings("unused")
- public String getNthPreviousWord(final SpacingAndPunctuations spacingAndPunctuations,
- final int n) {
+ public PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(
+ final SpacingAndPunctuations spacingAndPunctuations, final int n) {
mIC = mParent.getCurrentInputConnection();
- if (null == mIC) return null;
+ if (null == mIC) {
+ return new PrevWordsInfo(null);
+ }
final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
if (DEBUG_PREVIOUS_TEXT && null != prev) {
final int checkLength = LOOKBACK_CHARACTER_NUM - 1;
@@ -561,46 +563,57 @@ public final class RichInputConnection {
}
}
}
- return getNthPreviousWord(prev, spacingAndPunctuations, n);
+ return getPrevWordsInfoFromNthPreviousWord(prev, spacingAndPunctuations, n);
}
private static boolean isSeparator(final int code, final int[] sortedSeparators) {
return Arrays.binarySearch(sortedSeparators, code) >= 0;
}
- // Get the nth word before cursor. n = 1 retrieves the word immediately before the cursor,
- // n = 2 retrieves the word before that, and so on. This splits on whitespace only.
+ // Get information of the nth word before cursor. n = 1 retrieves the word immediately before
+ // the cursor, n = 2 retrieves the word before that, and so on. This splits on whitespace only.
// Also, it won't return words that end in a separator (if the nth word before the cursor
- // ends in a separator, it returns null).
+ // ends in a separator, it returns information represents beginning-of-sentence).
// Example :
// (n = 1) "abc def|" -> def
// (n = 1) "abc def |" -> def
- // (n = 1) "abc def. |" -> null
- // (n = 1) "abc def . |" -> null
+ // (n = 1) "abc def. |" -> beginning-of-sentence
+ // (n = 1) "abc def . |" -> beginning-of-sentence
// (n = 2) "abc def|" -> abc
// (n = 2) "abc def |" -> abc
// (n = 2) "abc def. |" -> abc
// (n = 2) "abc def . |" -> def
- // (n = 2) "abc|" -> null
- // (n = 2) "abc |" -> null
- // (n = 2) "abc. def|" -> null
- public static String getNthPreviousWord(final CharSequence prev,
+ // (n = 2) "abc|" -> beginning-of-sentence
+ // (n = 2) "abc |" -> beginning-of-sentence
+ // (n = 2) "abc. def|" -> beginning-of-sentence
+ public static PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(final CharSequence prev,
final SpacingAndPunctuations spacingAndPunctuations, final int n) {
- if (prev == null) return null;
+ if (prev == null) return new PrevWordsInfo(null);
final String[] w = spaceRegex.split(prev);
- // If we can't find n words, or we found an empty word, return null.
- if (w.length < n) return null;
+ // If we can't find n words, or we found an empty word, the context is
+ // beginning-of-sentence.
+ if (w.length < n) {
+ return new PrevWordsInfo();
+ }
final String nthPrevWord = w[w.length - n];
final int length = nthPrevWord.length();
- if (length <= 0) return null;
+ if (length <= 0) {
+ return new PrevWordsInfo();
+ }
- // If ends in a separator, return null
+ // If ends in a sentence separator, the context is beginning-of-sentence.
final char lastChar = nthPrevWord.charAt(length - 1);
+ if (spacingAndPunctuations.isSentenceSeparator(lastChar)) {
+ new PrevWordsInfo();
+ }
+ // If ends in a word separator or connector, the context is unclear.
+ // TODO: Return meaningful context for this case.
if (spacingAndPunctuations.isWordSeparator(lastChar)
- || spacingAndPunctuations.isWordConnector(lastChar)) return null;
-
- return nthPrevWord;
+ || spacingAndPunctuations.isWordConnector(lastChar)) {
+ return new PrevWordsInfo(null);
+ }
+ return new PrevWordsInfo(nthPrevWord);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index e3759a586..43daee4d2 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin;
import android.text.TextUtils;
-import com.android.inputmethod.event.Event;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
@@ -112,7 +111,10 @@ public final class Suggest {
additionalFeaturesOptions, SESSION_TYPING, rawSuggestions);
final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
- final boolean isAllUpperCase = wordComposer.isAllUpperCase();
+ // If resumed, then we don't want to upcase everything: resuming on a fully-capitalized
+ // words is rarely done to switch to another fully-capitalized word, but usually to a
+ // normal, non-capitalized suggestion.
+ final boolean isAllUpperCase = wordComposer.isAllUpperCase() && !wordComposer.isResumed();
final String firstSuggestion;
final String whitelistedWord;
if (suggestionResults.isEmpty()) {
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index c8ffbe443..b89ab84b2 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -258,12 +258,12 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
// Safeguard against adding really long words.
if (word.length() < MAX_WORD_LENGTH) {
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addWordDynamicallyLocked(word, adjustedFrequency, null /* shortcutTarget */,
+ addUnigramLocked(word, adjustedFrequency, null /* shortcutTarget */,
0 /* shortcutFreq */, false /* isNotAWord */,
false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) {
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- addWordDynamicallyLocked(shortcut, adjustedFrequency, word,
+ addUnigramLocked(shortcut, adjustedFrequency, word,
USER_DICT_SHORTCUT_FREQUENCY, true /* isNotAWord */,
false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 227b42bde..6ecb37346 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -294,11 +294,10 @@ public final class WordComposer {
* This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
* @param codePoints the code points to set as the composing word.
* @param coordinates the x, y coordinates of the key in the CoordinateUtils format
- * @param previousWord the previous word, to use as context for suggestions. Can be null if
- * the context is nil (typically, at start of text).
+ * @param prevWordsInfo the information of previous words, to use as context for suggestions
*/
public void setComposingWord(final int[] codePoints, final int[] coordinates,
- final CharSequence previousWord) {
+ final PrevWordsInfo prevWordsInfo) {
reset();
final int length = codePoints.length;
for (int i = 0; i < length; ++i) {
@@ -307,7 +306,7 @@ public final class WordComposer {
CoordinateUtils.yFromArray(coordinates, i)));
}
mIsResumed = true;
- mPrevWordsInfo = new PrevWordsInfo(null == previousWord ? null : previousWord.toString());
+ mPrevWordsInfo = prevWordsInfo;
}
/**
@@ -372,12 +371,12 @@ public final class WordComposer {
* Also, batch input needs to know about the current caps mode to display correctly
* capitalized suggestions.
* @param mode the mode at the time of start
- * @param previousWord the previous word as context for suggestions. May be null if none.
+ * @param prevWordsInfo the information of previous words
*/
public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
- final CharSequence previousWord) {
+ final PrevWordsInfo prevWordsInfo) {
mCapitalizedMode = mode;
- mPrevWordsInfo = new PrevWordsInfo(null == previousWord ? null : previousWord.toString());
+ mPrevWordsInfo = prevWordsInfo;
}
/**
@@ -413,13 +412,13 @@ public final class WordComposer {
// `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
// committedWord should contain suggestion spans if applicable.
public LastComposedWord commitWord(final int type, final CharSequence committedWord,
- final String separatorString, final String prevWord) {
+ final String separatorString, final PrevWordsInfo prevWordsInfo) {
// Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
// or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
// the last composed word to ensure this does not happen.
final LastComposedWord lastComposedWord = new LastComposedWord(mEvents,
mInputPointers, mTypedWordCache.toString(), committedWord, separatorString,
- prevWord, mCapitalizedMode);
+ prevWordsInfo, mCapitalizedMode);
mInputPointers.reset();
if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
&& type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
diff --git a/java/src/com/android/inputmethod/latin/WordListInfo.java b/java/src/com/android/inputmethod/latin/WordListInfo.java
index 5ac806a0c..268fe9818 100644
--- a/java/src/com/android/inputmethod/latin/WordListInfo.java
+++ b/java/src/com/android/inputmethod/latin/WordListInfo.java
@@ -22,8 +22,10 @@ package com.android.inputmethod.latin;
public final class WordListInfo {
public final String mId;
public final String mLocale;
- public WordListInfo(final String id, final String locale) {
+ public final String mRawChecksum;
+ public WordListInfo(final String id, final String locale, final String rawChecksum) {
mId = id;
mLocale = locale;
+ mRawChecksum = rawChecksum;
}
}
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
index af899c040..761f457ea 100644
--- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
+++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
@@ -38,4 +38,7 @@ public final class ProductionFlag {
// Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}.
public static final boolean INCLUDE_RAW_SUGGESTIONS = false;
+
+ // When false, the metrics logging is not yet ready to be enabled.
+ public static final boolean IS_METRICS_LOGGING_SUPPORTED = false;
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index ea58abc14..7536ff94c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -37,6 +37,7 @@ import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LastComposedWord;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.PrevWordsInfo;
import com.android.inputmethod.latin.RichInputConnection;
import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
@@ -574,7 +575,7 @@ public final class InputLogic {
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
// Prev word is 1st word before cursor
- getNthPreviousWordForSuggestion(
+ getPrevWordsInfoFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
}
@@ -613,7 +614,8 @@ public final class InputLogic {
getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(settingsValues,
- keyboardSwitcher.getKeyboardShiftMode()), commitParts[0]);
+ keyboardSwitcher.getKeyboardShiftMode()),
+ new PrevWordsInfo(commitParts[0]));
++mAutoCommitSequenceNumber;
}
}
@@ -764,7 +766,8 @@ public final class InputLogic {
// We pass 1 to getPreviousWordForSuggestion because we were not composing a word
// yet, so the word we want is the 1st word before the cursor.
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- inputTransaction.mShiftState, getNthPreviousWordForSuggestion(
+ inputTransaction.mShiftState,
+ getPrevWordsInfoFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
}
mConnection.setComposingText(getTextWithUnderline(
@@ -802,10 +805,11 @@ public final class InputLogic {
final int codePoint = inputTransaction.mEvent.mCodePoint;
final SettingsValues settingsValues = inputTransaction.mSettingsValues;
boolean didAutoCorrect = false;
+ final boolean wasComposingWord = mWordComposer.isComposingWord();
// We avoid sending spaces in languages without spaces if we were composing.
final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint
&& !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
- && mWordComposer.isComposingWord();
+ && wasComposingWord;
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the separator at the current cursor position.
@@ -849,7 +853,7 @@ public final class InputLogic {
promotePhantomSpace(settingsValues);
}
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleSeparator(codePoint, mWordComposer.isComposingWord());
+ ResearchLogger.latinIME_handleSeparator(codePoint, wasComposingWord);
}
if (!shouldAvoidSendingCode) {
@@ -865,7 +869,9 @@ public final class InputLogic {
}
startDoubleSpacePeriodCountdown(inputTransaction);
- inputTransaction.setRequiresUpdateSuggestions();
+ if (wasComposingWord) {
+ inputTransaction.setRequiresUpdateSuggestions();
+ }
} else {
if (swapWeakSpace) {
swapSwapperAndSpace(inputTransaction);
@@ -1233,7 +1239,7 @@ public final class InputLogic {
}
private void performAdditionToUserHistoryDictionary(final SettingsValues settingsValues,
- final String suggestion, final String prevWord) {
+ final String suggestion, final PrevWordsInfo prevWordsInfo) {
// If correction is not enabled, we don't add words to the user history dictionary.
// That's to avoid unintended additions in some sensitive fields, or fields that
// expect to receive non-words.
@@ -1244,8 +1250,8 @@ public final class InputLogic {
mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps();
final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
System.currentTimeMillis());
- mSuggest.mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized, prevWord,
- timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive);
+ mSuggest.mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized,
+ prevWordsInfo, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive);
}
public void performUpdateSuggestionStripSync(final SettingsValues settingsValues) {
@@ -1325,7 +1331,8 @@ public final class InputLogic {
// Show predictions.
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
WordComposer.CAPS_MODE_OFF,
- getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations, 1));
+ getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ settingsValues.mSpacingAndPunctuations, 1));
mLatinIME.mHandler.postUpdateSuggestionStrip();
return;
}
@@ -1370,13 +1377,14 @@ public final class InputLogic {
}
}
final int[] codePoints = StringUtils.toCodePointArray(typedWord);
+ // We want the previous word for suggestion. If we have chars in the word
+ // before the cursor, then we want the word before that, hence 2; otherwise,
+ // we want the word immediately before the cursor, hence 1.
+ final PrevWordsInfo prevWordsInfo = getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ settingsValues.mSpacingAndPunctuations,
+ 0 == numberOfCharsInWordBeforeCursor ? 1 : 2);
mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints),
- getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations,
- // We want the previous word for suggestion. If we have chars in the word
- // before the cursor, then we want the word before that, hence 2; otherwise,
- // we want the word immediately before the cursor, hence 1.
- 0 == numberOfCharsInWordBeforeCursor ? 1 : 2));
+ mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
mWordComposer.setCursorPositionWithinWord(
typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
@@ -1431,7 +1439,7 @@ public final class InputLogic {
* @param inputTransaction The transaction in progress.
*/
private void revertCommit(final InputTransaction inputTransaction) {
- final String previousWord = mLastComposedWord.mPrevWord;
+ final PrevWordsInfo prevWordsInfo = mLastComposedWord.mPrevWordsInfo;
final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord;
final CharSequence committedWord = mLastComposedWord.mCommittedWord;
final String committedWordString = committedWord.toString();
@@ -1453,9 +1461,9 @@ public final class InputLogic {
}
}
mConnection.deleteSurroundingText(deleteLength, 0);
- if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
+ if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && !TextUtils.isEmpty(committedWord)) {
mSuggest.mDictionaryFacilitator.cancelAddingUserHistory(
- previousWord, committedWordString);
+ prevWordsInfo, committedWordString);
}
final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString;
final SpannableString textToCommit = new SpannableString(stringToCommit);
@@ -1504,7 +1512,7 @@ public final class InputLogic {
// with the typed word, so we need to resume suggestions right away.
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), previousWord);
+ mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
mConnection.setComposingText(textToCommit, 1);
}
if (inputTransaction.mSettingsValues.mIsInternal) {
@@ -1586,21 +1594,23 @@ public final class InputLogic {
}
/**
- * Get the nth previous word before the cursor as context for the suggestion process.
+ * Get information fo previous words from the nth previous word before the cursor as context
+ * for the suggestion process.
* @param spacingAndPunctuations the current spacing and punctuations settings.
* @param nthPreviousWord reverse index of the word to get (1-indexed)
- * @return the nth previous word before the cursor.
+ * @return the information of previous words
*/
// TODO: Make this private
- public CharSequence getNthPreviousWordForSuggestion(
+ public PrevWordsInfo getPrevWordsInfoFromNthPreviousWordForSuggestion(
final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) {
if (spacingAndPunctuations.mCurrentLanguageHasSpaces) {
// If we are typing in a language with spaces we can just look up the previous
- // word from textview.
- return mConnection.getNthPreviousWord(spacingAndPunctuations, nthPreviousWord);
+ // word information from textview.
+ return mConnection.getPrevWordsInfoFromNthPreviousWord(
+ spacingAndPunctuations, nthPreviousWord);
} else {
- return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
- : mLastComposedWord.mCommittedWord;
+ return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? new PrevWordsInfo()
+ : new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
}
}
@@ -1968,17 +1978,17 @@ public final class InputLogic {
suggestedWords);
// Use the 2nd previous word as the previous word because the 1st previous word is the word
// to be committed.
- final String prevWord = mConnection.getNthPreviousWord(
+ final PrevWordsInfo prevWordsInfo = mConnection.getPrevWordsInfoFromNthPreviousWord(
settingsValues.mSpacingAndPunctuations, 2);
mConnection.commitText(chosenWordWithSuggestions, 1);
// Add the word to the user history dictionary
- performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWord);
+ performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo);
// TODO: figure out here if this is an auto-correct or if the best word is actually
// what user typed. Note: currently this is done much later in
// LastComposedWord#didCommitTypedWord by string equality of the remembered
// strings.
mLastComposedWord = mWordComposer.commitWord(commitType,
- chosenWordWithSuggestions, separatorString, prevWord);
+ chosenWordWithSuggestions, separatorString, prevWordsInfo);
final boolean shouldDiscardPreviousWordForSuggestion;
if (0 == StringUtils.codePointCount(separatorString)) {
// Separator is 0-length, we can keep the previous word for suggestion. Either this
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 818cd9a5f..f89caf921 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -22,6 +22,7 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+import com.android.inputmethod.latin.PrevWordsInfo;
import java.io.File;
import java.util.Locale;
@@ -52,29 +53,32 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
}
/**
- * Pair will be added to the user history dictionary.
+ * Add a word to the user history dictionary.
*
- * The first word may be null. That means we don't know the context, in other words,
- * it's only a unigram. The first word may also be an empty string : this means start
- * context, as in beginning of a sentence for example.
- * The second word may not be null (a NullPointerException would be thrown).
+ * @param userHistoryDictionary the user history dictionary
+ * @param prevWordsInfo the information of previous words
+ * @param word the word the user inputted
+ * @param isValid whether the word is valid or not
+ * @param timestamp the timestamp when the word has been inputted
*/
public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary,
- final String word0, final String word1, final boolean isValid, final int timestamp) {
- if (word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
- (word0 != null && word0.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
+ final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid,
+ final int timestamp) {
+ final String prevWord = prevWordsInfo.mPrevWord;
+ if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
+ (prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
return;
}
final int frequency = isValid ?
FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS;
- userHistoryDictionary.addWordDynamically(word1, frequency, null /* shortcutTarget */,
+ userHistoryDictionary.addUnigramEntry(word, frequency, null /* shortcutTarget */,
0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, timestamp);
// Do not insert a word as a bigram of itself
- if (word1.equals(word0)) {
+ if (word.equals(prevWord)) {
return;
}
- if (null != word0) {
- userHistoryDictionary.addBigramDynamically(word0, word1, frequency, timestamp);
+ if (null != prevWord) {
+ userHistoryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 5a325ea82..e90b15ca5 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -27,14 +27,13 @@ import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.TypefaceUtils;
public final class MoreSuggestions extends Keyboard {
- public static final int SUGGESTION_CODE_BASE = 1024;
-
public final SuggestedWords mSuggestedWords;
public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
@@ -178,7 +177,7 @@ public final class MoreSuggestions extends Keyboard {
}
}
- private static boolean isIndexSubjectToAutoCorrection(final SuggestedWords suggestedWords,
+ static boolean isIndexSubjectToAutoCorrection(final SuggestedWords suggestedWords,
final int index) {
return suggestedWords.mWillAutoCorrect && index == SuggestedWords.INDEX_OF_AUTO_CORRECTION;
}
@@ -226,11 +225,7 @@ public final class MoreSuggestions extends Keyboard {
word = mSuggestedWords.getLabel(index);
info = mSuggestedWords.getDebugString(index);
}
- final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE;
- final Key key = new Key(word, KeyboardIconsSet.ICON_UNDEFINED,
- indexInMoreSuggestions, null /* outputText */, info, 0 /* labelFlags */,
- Key.BACKGROUND_TYPE_NORMAL, x, y, width, params.mDefaultRowHeight,
- params.mHorizontalGap, params.mVerticalGap);
+ final Key key = new MoreSuggestionKey(word, info, index, params);
params.markAsEdgeKey(key, index);
params.onAddKey(key);
final int columnNumber = params.getColumnNumber(index);
@@ -245,6 +240,19 @@ public final class MoreSuggestions extends Keyboard {
}
}
+ static final class MoreSuggestionKey extends Key {
+ public final int mSuggestedWordIndex;
+
+ public MoreSuggestionKey(final String word, final String info, final int index,
+ final MoreSuggestionsParam params) {
+ super(word /* label */, KeyboardIconsSet.ICON_UNDEFINED, Constants.CODE_OUTPUT_TEXT,
+ word /* outputText */, info, 0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL,
+ params.getX(index), params.getY(index), params.getWidth(index),
+ params.mDefaultRowHeight, params.mHorizontalGap, params.mVerticalGap);
+ mSuggestedWordIndex = index;
+ }
+ }
+
private static final class Divider extends Key.Spacer {
private final Drawable mIcon;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 549ff0d9d..7fd64c4bf 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -20,10 +20,12 @@ import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
+import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionKey;
import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
/**
@@ -59,7 +61,12 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView {
}
@Override
- public void onCodeInput(final int code, final int x, final int y) {
+ protected void onKeyInput(final Key key, final int x, final int y) {
+ if (!(key instanceof MoreSuggestionKey)) {
+ Log.e(TAG, "Expected key is MoreSuggestionKey, but found "
+ + key.getClass().getName());
+ return;
+ }
final Keyboard keyboard = getKeyboard();
if (!(keyboard instanceof MoreSuggestions)) {
Log.e(TAG, "Expected keyboard is MoreSuggestions, but found "
@@ -67,7 +74,7 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView {
return;
}
final SuggestedWords suggestedWords = ((MoreSuggestions)keyboard).mSuggestedWords;
- final int index = code - MoreSuggestions.SUGGESTION_CODE_BASE;
+ final int index = ((MoreSuggestionKey)key).mSuggestedWordIndex;
if (index < 0 || index >= suggestedWords.size()) {
Log.e(TAG, "Selected suggestion has an illegal index: " + index);
return;
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
index a21953259..f1057da0b 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
@@ -16,13 +16,23 @@
package com.android.inputmethod.latin.utils;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import android.content.Context;
+import android.content.res.Resources;
+import android.text.InputType;
import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardLayoutSet;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.PrevWordsInfo;
import com.android.inputmethod.latin.Suggest;
@@ -41,8 +51,10 @@ public class DistracterFilter {
private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120;
private final Context mContext;
+ private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap;
+ private final Map<Locale, Keyboard> mLocaleToKeyboardMap;
private final Suggest mSuggest;
- private final Keyboard mKeyboard;
+ private Keyboard mKeyboard;
// If the score of the top suggestion exceeds this value, the tested word (e.g.,
// an OOV, a misspelling, or an in-vocabulary word) would be considered as a distracter to
@@ -51,17 +63,34 @@ public class DistracterFilter {
// the dictionary.
private static final float DISTRACTER_WORD_SCORE_THRESHOLD = 2.0f;
+ // Create empty distracter filter.
+ public DistracterFilter() {
+ this(null, new ArrayList<InputMethodSubtype>());
+ }
+
/**
* Create a DistracterFilter instance.
*
* @param context the context.
- * @param keyboard the keyboard that is currently being used. This information is needed
- * when calling mSuggest.getSuggestedWords(...) to obtain a list of suggestions.
+ * @param enabledSubtypes the enabled subtypes.
*/
- public DistracterFilter(final Context context, final Keyboard keyboard) {
+ public DistracterFilter(final Context context, final List<InputMethodSubtype> enabledSubtypes) {
mContext = context;
+ mLocaleToSubtypeMap = new HashMap<>();
+ if (enabledSubtypes != null) {
+ for (final InputMethodSubtype subtype : enabledSubtypes) {
+ final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
+ if (mLocaleToSubtypeMap.containsKey(locale)) {
+ // Multiple subtypes are enabled for one locale.
+ // TODO: Investigate what we should do for this case.
+ continue;
+ }
+ mLocaleToSubtypeMap.put(locale, subtype);
+ }
+ }
+ mLocaleToKeyboardMap = new HashMap<>();
mSuggest = new Suggest();
- mKeyboard = keyboard;
+ mKeyboard = null;
}
private static boolean suggestionExceedsDistracterThreshold(
@@ -78,6 +107,30 @@ public class DistracterFilter {
return false;
}
+ private void loadKeyboardForLocale(final Locale newLocale) {
+ final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale);
+ if (cachedKeyboard != null) {
+ mKeyboard = cachedKeyboard;
+ return;
+ }
+ final InputMethodSubtype subtype = mLocaleToSubtypeMap.get(newLocale);
+ if (subtype == null) {
+ return;
+ }
+ final EditorInfo editorInfo = new EditorInfo();
+ editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
+ final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
+ mContext, editorInfo);
+ final Resources res = mContext.getResources();
+ final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
+ final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
+ builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
+ builder.setSubtype(subtype);
+ builder.setIsSpellChecker(false /* isSpellChecker */);
+ final KeyboardLayoutSet layoutSet = builder.build();
+ mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
+ }
+
private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException {
mSuggest.mDictionaryFacilitator.resetDictionaries(mContext, newlocale,
false /* useContactsDict */, false /* usePersonalizedDicts */,
@@ -92,15 +145,21 @@ public class DistracterFilter {
* @param prevWordsInfo the information of previous words.
* @param testedWord the word that will be tested to see whether it is a distracter to words
* in dictionaries.
- * @param locale the locale of words.
+ * @param locale the locale of word.
* @return true if testedWord is a distracter, otherwise false.
*/
public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo,
final String testedWord, final Locale locale) {
- if (mKeyboard == null || locale == null) {
+ if (locale == null) {
return false;
}
if (!locale.equals(mSuggest.mDictionaryFacilitator.getLocale())) {
+ if (!mLocaleToSubtypeMap.containsKey(locale)) {
+ Log.e(TAG, "Locale " + locale + " is not enabled.");
+ // TODO: Investigate what we should do for disabled locales.
+ return false;
+ }
+ loadKeyboardForLocale(locale);
// Reset dictionaries for the locale.
try {
loadDictionariesForLocale(locale);
@@ -109,12 +168,13 @@ public class DistracterFilter {
return false;
}
}
-
+ if (mKeyboard == null) {
+ return false;
+ }
final WordComposer composer = new WordComposer();
final int[] codePoints = StringUtils.toCodePointArray(testedWord);
- final int[] coordinates;
- coordinates = mKeyboard.getCoordinates(codePoints);
- composer.setComposingWord(codePoints, coordinates, prevWordsInfo.mPrevWord);
+ final int[] coordinates = mKeyboard.getCoordinates(codePoints);
+ composer.setComposingWord(codePoints, coordinates, prevWordsInfo);
final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord);
final String consideredWord = trailingSingleQuotesCount > 0 ?
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java
deleted file mode 100644
index 8a711a24e..000000000
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.content.Context;
-
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.MainKeyboardView;
-
-public class DistracterFilterUtils {
- private DistracterFilterUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static final DistracterFilter createDistracterFilter(final Context context,
- final KeyboardSwitcher keyboardSwitcher) {
- final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView();
- // TODO: Create Keyboard when mainKeyboardView is null.
- // TODO: Figure out the most reasonable keyboard for the filter. Refer to the
- // spellchecker's logic.
- final Keyboard keyboard = (mainKeyboardView != null) ?
- mainKeyboardView.getKeyboard() : null;
- final DistracterFilter distracterFilter = new DistracterFilter(context, keyboard);
- return distracterFilter;
- }
-}