aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtype.java22
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java19
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java20
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java71
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtils.java99
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java31
-rw-r--r--java/src/com/android/inputmethod/latin/ResearchLogger.java267
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeLocale.java49
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java188
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java97
10 files changed, 360 insertions, 503 deletions
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index 28cec56e6..458d9ee14 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -22,27 +22,8 @@ import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOAR
import android.view.inputmethod.InputMethodSubtype;
-import java.util.HashMap;
public class AdditionalSubtype {
- public static final String QWERTY = "qwerty";
- public static final String QWERTZ = "qwertz";
- public static final String AZERTY = "azerty";
- public static final String[] PREDEFINED_KEYBOARD_LAYOUT_SET = {
- QWERTY,
- QWERTZ,
- AZERTY
- };
-
- // Keyboard layout to subtype name resource id map.
- private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
- new HashMap<String, Integer>();
-
- static {
- sKeyboardLayoutToNameIdsMap.put(QWERTY, R.string.subtype_generic_qwerty);
- sKeyboardLayoutToNameIdsMap.put(QWERTZ, R.string.subtype_generic_qwertz);
- sKeyboardLayoutToNameIdsMap.put(AZERTY, R.string.subtype_generic_azerty);
- }
private AdditionalSubtype() {
// This utility class is not publicly instantiable.
@@ -60,7 +41,8 @@ public class AdditionalSubtype {
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
final String filteredExtraValue = StringUtils.appendToCsvIfNotExists(
IS_ADDITIONAL_SUBTYPE, extraValue);
- Integer nameId = sKeyboardLayoutToNameIdsMap.get(keyboardLayoutSetName);
+ Integer nameId = SubtypeLocale.getSubtypeNameIdFromKeyboardLayoutName(
+ keyboardLayoutSetName);
if (nameId == null) nameId = R.string.subtype_generic;
return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
localeString, KEYBOARD_MODE,
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 7a22c9742..613c20304 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -109,18 +109,15 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
static class KeyboardLayoutSetItem extends Pair<String, String> {
- public KeyboardLayoutSetItem(String keyboardLayoutSetName) {
- super(keyboardLayoutSetName, getDisplayName(keyboardLayoutSetName));
+ public KeyboardLayoutSetItem(InputMethodSubtype subtype) {
+ super(SubtypeLocale.getKeyboardLayoutSetName(subtype),
+ SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype));
}
@Override
public String toString() {
return second;
}
-
- private static String getDisplayName(String keyboardLayoutSetName) {
- return keyboardLayoutSetName.toUpperCase();
- }
}
static class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
@@ -129,8 +126,11 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// TODO: Should filter out already existing combinations of locale and layout.
- for (final String layout : AdditionalSubtype.PREDEFINED_KEYBOARD_LAYOUT_SET) {
- add(new KeyboardLayoutSetItem(layout));
+ for (final String layout : SubtypeLocale.getPredefinedKeyboardLayoutSet()) {
+ // This is a dummy subtype with NO_LANGUAGE, only for display.
+ final InputMethodSubtype subtype = AdditionalSubtype.createAdditionalSubtype(
+ SubtypeLocale.NO_LANGUAGE, layout, null);
+ add(new KeyboardLayoutSetItem(subtype));
}
}
}
@@ -196,8 +196,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
.setNegativeButton(R.string.remove, this);
final SubtypeLocaleItem localeItem = SubtypeLocaleAdapter.createItem(
context, mSubtype.getLocale());
- final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(
- SubtypeLocale.getKeyboardLayoutSetName(mSubtype));
+ final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype);
setSpinnerPosition(mSubtypeLocaleSpinner, localeItem);
setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index e4d081b56..a4670daf2 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -49,7 +49,10 @@ public class BinaryDictionaryFileDumper {
*/
private static final int FILE_READ_BUFFER_SIZE = 1024;
// TODO: make the following data common with the native code
- private static final byte[] MAGIC_NUMBER = new byte[] { 0x78, (byte)0xB1 };
+ private static final byte[] MAGIC_NUMBER_VERSION_1 =
+ new byte[] { (byte)0x78, (byte)0xB1, (byte)0x00, (byte)0x00 };
+ private static final byte[] MAGIC_NUMBER_VERSION_2 =
+ new byte[] { (byte)0x9B, (byte)0xC1, (byte)0x3A, (byte)0xFE };
private static final String DICTIONARY_PROJECTION[] = { "id" };
@@ -268,15 +271,18 @@ public class BinaryDictionaryFileDumper {
private static void checkMagicAndCopyFileTo(final BufferedInputStream input,
final FileOutputStream output) throws FileNotFoundException, IOException {
// Check the magic number
- final byte[] magicNumberBuffer = new byte[MAGIC_NUMBER.length];
- final int readMagicNumberSize = input.read(magicNumberBuffer, 0, MAGIC_NUMBER.length);
- if (readMagicNumberSize < MAGIC_NUMBER.length) {
+ final int length = MAGIC_NUMBER_VERSION_2.length;
+ final byte[] magicNumberBuffer = new byte[length];
+ final int readMagicNumberSize = input.read(magicNumberBuffer, 0, length);
+ if (readMagicNumberSize < length) {
throw new IOException("Less bytes to read than the magic number length");
}
- if (!Arrays.equals(MAGIC_NUMBER, magicNumberBuffer)) {
- throw new IOException("Wrong magic number for downloaded file");
+ if (!Arrays.equals(MAGIC_NUMBER_VERSION_2, magicNumberBuffer)) {
+ if (!Arrays.equals(MAGIC_NUMBER_VERSION_1, magicNumberBuffer)) {
+ throw new IOException("Wrong magic number for downloaded file");
+ }
}
- output.write(MAGIC_NUMBER);
+ output.write(magicNumberBuffer);
// Actually copy the file
final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE];
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 072dec9d1..5acd62904 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -24,6 +24,7 @@ import android.util.Log;
import java.io.File;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Locale;
/**
@@ -46,6 +47,10 @@ class BinaryDictionaryGetter {
*/
private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
+ // Name of the category for the main dictionary
+ private static final String MAIN_DICTIONARY_CATEGORY = "main";
+ public static final String ID_CATEGORY_SEPARATOR = ":";
+
// Prevents this from being instantiated
private BinaryDictionaryGetter() {}
@@ -206,7 +211,40 @@ class BinaryDictionaryGetter {
}
/**
- * Returns the list of cached files for a specific locale.
+ * Returns the category for a given file name.
+ *
+ * This parses the file name, extracts the category, and returns it. See
+ * {@link #getMainDictId(Locale)} and {@link #isMainWordListId(String)}.
+ * @return The category as a string or null if it can't be found in the file name.
+ */
+ private static String getCategoryFromFileName(final String fileName) {
+ final String id = getWordListIdFromFileName(fileName);
+ final String[] idArray = id.split(ID_CATEGORY_SEPARATOR);
+ if (2 != idArray.length) return null;
+ return idArray[0];
+ }
+
+ /**
+ * Utility class for the {@link #getCachedWordLists} method
+ */
+ private static class FileAndMatchLevel {
+ final File mFile;
+ final int mMatchLevel;
+ public FileAndMatchLevel(final File file, final int matchLevel) {
+ mFile = file;
+ mMatchLevel = matchLevel;
+ }
+ }
+
+ /**
+ * Returns the list of cached files for a specific locale, one for each category.
+ *
+ * This will return exactly one file for each word list category that matches
+ * the passed locale. If several files match the locale for any given category,
+ * this returns the file with the closest match to the locale. For example, if
+ * the passed word list is en_US, and for a category we have an en and an en_US
+ * word list available, we'll return only the en_US one.
+ * Thus, the list will contain as many files as there are categories.
*
* @param locale the locale to find the dictionary files for, as a string.
* @param context the context on which to open the files upon.
@@ -216,21 +254,32 @@ class BinaryDictionaryGetter {
final Context context) {
final File[] directoryList = getCachedDirectoryList(context);
if (null == directoryList) return EMPTY_FILE_ARRAY;
- final ArrayList<File> cacheFiles = new ArrayList<File>();
+ final HashMap<String, FileAndMatchLevel> cacheFiles =
+ new HashMap<String, FileAndMatchLevel>();
for (File directory : directoryList) {
if (!directory.isDirectory()) continue;
final String dirLocale = getWordListIdFromFileName(directory.getName());
- if (LocaleUtils.isMatch(LocaleUtils.getMatchLevel(dirLocale, locale))) {
+ final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale);
+ if (LocaleUtils.isMatch(matchLevel)) {
final File[] wordLists = directory.listFiles();
if (null != wordLists) {
for (File wordList : wordLists) {
- cacheFiles.add(wordList);
+ final String category = getCategoryFromFileName(wordList.getName());
+ final FileAndMatchLevel currentBestMatch = cacheFiles.get(category);
+ if (null == currentBestMatch || currentBestMatch.mMatchLevel < matchLevel) {
+ cacheFiles.put(category, new FileAndMatchLevel(wordList, matchLevel));
+ }
}
}
}
}
if (cacheFiles.isEmpty()) return EMPTY_FILE_ARRAY;
- return cacheFiles.toArray(EMPTY_FILE_ARRAY);
+ final File[] result = new File[cacheFiles.size()];
+ int index = 0;
+ for (final FileAndMatchLevel entry : cacheFiles.values()) {
+ result[index++] = entry.mFile;
+ }
+ return result;
}
/**
@@ -245,7 +294,13 @@ class BinaryDictionaryGetter {
// This works because we don't include by default different dictionaries for
// different countries. This actually needs to return the id that we would
// like to use for word lists included in resources, and the following is okay.
- return locale.getLanguage().toString();
+ return MAIN_DICTIONARY_CATEGORY + ID_CATEGORY_SEPARATOR + locale.getLanguage().toString();
+ }
+
+ private static boolean isMainWordListId(final String id) {
+ final String[] idArray = id.split(ID_CATEGORY_SEPARATOR);
+ if (2 != idArray.length) return false;
+ return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
}
/**
@@ -270,9 +325,7 @@ class BinaryDictionaryGetter {
BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context,
hasDefaultWordList);
final File[] cachedWordLists = getCachedWordLists(locale.toString(), context);
-
final String mainDictId = getMainDictId(locale);
-
final DictPackSettings dictPackSettings = new DictPackSettings(context);
boolean foundMainDict = false;
@@ -280,7 +333,7 @@ class BinaryDictionaryGetter {
// cachedWordLists may not be null, see doc for getCachedDictionaryList
for (final File f : cachedWordLists) {
final String wordListId = getWordListIdFromFileName(f.getName());
- if (wordListId.equals(mainDictId)) {
+ if (isMainWordListId(wordListId)) {
foundMainDict = true;
}
if (!dictPackSettings.isWordListActive(wordListId)) continue;
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
index b3f613bae..7d673175e 100644
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ b/java/src/com/android/inputmethod/latin/EditingUtils.java
@@ -16,7 +16,6 @@
package com.android.inputmethod.latin;
-import android.text.TextUtils;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
@@ -37,30 +36,6 @@ public class EditingUtils {
// Unintentional empty constructor for singleton.
}
- /**
- * Append newText to the text field represented by connection.
- * The new text becomes selected.
- */
- public static void appendText(InputConnection connection, String newText) {
- if (connection == null) {
- return;
- }
-
- // Commit the composing text
- connection.finishComposingText();
-
- // Add a space if the field already has text.
- String text = newText;
- CharSequence charBeforeCursor = connection.getTextBeforeCursor(1, 0);
- if (charBeforeCursor != null
- && !charBeforeCursor.equals(" ")
- && (charBeforeCursor.length() > 0)) {
- text = " " + text;
- }
-
- connection.setComposingText(text, 1);
- }
-
private static int getCursorPosition(InputConnection connection) {
if (null == connection) return INVALID_CURSOR_POSITION;
ExtractedText extracted = connection.getExtractedText(
@@ -146,7 +121,6 @@ public class EditingUtils {
private static final Pattern spaceRegex = Pattern.compile("\\s+");
-
public static CharSequence getPreviousWord(InputConnection connection,
String sentenceSeperators) {
//TODO: Should fix this. This could be slow!
@@ -205,77 +179,4 @@ public class EditingUtils {
return w[w.length - 1];
}
-
- public static class SelectedWord {
- public final int mStart;
- public final int mEnd;
- public final CharSequence mWord;
-
- public SelectedWord(int start, int end, CharSequence word) {
- mStart = start;
- mEnd = end;
- mWord = word;
- }
- }
-
- /**
- * Takes a character sequence with a single character and checks if the character occurs
- * in a list of word separators or is empty.
- * @param singleChar A CharSequence with null, zero or one character
- * @param wordSeparators A String containing the word separators
- * @return true if the character is at a word boundary, false otherwise
- */
- private static boolean isWordBoundary(CharSequence singleChar, String wordSeparators) {
- return TextUtils.isEmpty(singleChar) || wordSeparators.contains(singleChar);
- }
-
- /**
- * Checks if the cursor is inside a word or the current selection is a whole word.
- * @param ic the InputConnection for accessing the text field
- * @param selStart the start position of the selection within the text field
- * @param selEnd the end position of the selection within the text field. This could be
- * the same as selStart, if there's no selection.
- * @param wordSeparators the word separator characters for the current language
- * @return an object containing the text and coordinates of the selected/touching word,
- * null if the selection/cursor is not marking a whole word.
- */
- public static SelectedWord getWordAtCursorOrSelection(final InputConnection ic,
- int selStart, int selEnd, String wordSeparators) {
- if (selStart == selEnd) {
- // There is just a cursor, so get the word at the cursor
- // getWordRangeAtCursor returns null if the connection is null
- final EditingUtils.Range range = getWordRangeAtCursor(ic, wordSeparators);
- if (range != null && !TextUtils.isEmpty(range.mWord)) {
- return new SelectedWord(selStart - range.mCharsBefore, selEnd + range.mCharsAfter,
- range.mWord);
- }
- } else {
- if (null == ic) return null;
- // Is the previous character empty or a word separator? If not, return null.
- final CharSequence charsBefore = ic.getTextBeforeCursor(1, 0);
- if (!isWordBoundary(charsBefore, wordSeparators)) {
- return null;
- }
-
- // Is the next character empty or a word separator? If not, return null.
- final CharSequence charsAfter = ic.getTextAfterCursor(1, 0);
- if (!isWordBoundary(charsAfter, wordSeparators)) {
- return null;
- }
-
- // Extract the selection alone
- final CharSequence touching = ic.getSelectedText(0);
- if (TextUtils.isEmpty(touching)) return null;
- // Is any part of the selection a separator? If so, return null.
- final int length = touching.length();
- for (int i = 0; i < length; i++) {
- if (wordSeparators.contains(touching.subSequence(i, i + 1))) {
- return null;
- }
- }
- // Prepare the selected word
- return new SelectedWord(selStart, selEnd, touching);
- }
- return null;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 31c832c31..e1978fca1 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -154,6 +154,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private final SubtypeSwitcher mSubtypeSwitcher;
private boolean mShouldSwitchToLastSubtype = true;
+ private boolean mIsMainDictionaryAvailable;
private UserDictionary mUserDictionary;
private UserHistoryDictionary mUserHistoryDictionary;
private boolean mIsUserDictionaryAvailable;
@@ -449,14 +450,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return new SettingsValues(mPrefs, LatinIME.this);
}
};
- mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getInputLocale());
+ mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
mFeedbackManager = new AudioAndHapticFeedbackManager(this, mSettingsValues);
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
private void initSuggest() {
- final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
- final Locale keyboardLocale = mSubtypeSwitcher.getInputLocale();
+ final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ final String localeStr = subtypeLocale.toString();
final Dictionary oldContactsDictionary;
if (mSuggest != null) {
@@ -465,11 +466,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
oldContactsDictionary = null;
}
- mSuggest = new Suggest(this, keyboardLocale);
+ mSuggest = new Suggest(this, subtypeLocale);
if (mSettingsValues.mAutoCorrectEnabled) {
mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
}
+ mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
+
mUserDictionary = new UserDictionary(this, localeStr);
mSuggest.setUserDictionary(mUserDictionary);
mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
@@ -511,7 +514,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
if (USE_BINARY_CONTACTS_DICTIONARY) {
dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS,
- mSubtypeSwitcher.getInputLocale());
+ mSubtypeSwitcher.getCurrentSubtypeLocale());
} else {
dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
}
@@ -523,7 +526,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
/* package private */ void resetSuggestMainDict() {
- mSuggest.resetMainDict(this, mSubtypeSwitcher.getInputLocale());
+ final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ mSuggest.resetMainDict(this, subtypeLocale);
+ mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
}
@Override
@@ -603,7 +608,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
- SubtypeSwitcher.getInstance().updateSubtype(subtype);
+ mSubtypeSwitcher.updateSubtype(subtype);
}
private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
@@ -629,7 +634,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
editorInfo.inputType, editorInfo.imeOptions));
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_onStartInputViewInternal(editorInfo);
+ ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
}
if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
Log.w(TAG, "Deprecated private IME option specified: "
@@ -1843,6 +1848,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mExpectingUpdateSelection = true;
commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
LastComposedWord.NOT_A_SEPARATOR);
+ // Don't allow cancellation of manual pick
+ mLastComposedWord.deactivate();
// Add the word to the user history dictionary
addToUserHistoryDictionary(suggestion);
mSpaceState = SPACE_STATE_PHANTOM;
@@ -1894,7 +1901,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mSettingsValues.mEnableSuggestionSpanInsertion) {
final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
- this, bestWord, suggestedWords, mSubtypeSwitcher.isDictionaryAvailable()),
+ this, bestWord, suggestedWords, mIsMainDictionaryAvailable),
1);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_commitText(bestWord);
@@ -1972,7 +1979,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
final String secondWord;
if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
- secondWord = suggestion.toString().toLowerCase(mSubtypeSwitcher.getInputLocale());
+ secondWord = suggestion.toString().toLowerCase(
+ mSubtypeSwitcher.getCurrentSubtypeLocale());
} else {
secondWord = suggestion.toString();
}
@@ -2300,8 +2308,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void showOptionDialogInternal(AlertDialog dialog) {
- final IBinder windowToken = KeyboardSwitcher.getInstance().getKeyboardView()
- .getWindowToken();
+ final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
if (windowToken == null) return;
dialog.setCancelable(true);
diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java
index 7072dda23..566af7061 100644
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java
@@ -18,10 +18,12 @@ package com.android.inputmethod.latin;
import android.content.SharedPreferences;
import android.inputmethodservice.InputMethodService;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.os.SystemClock;
+import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
@@ -31,7 +33,6 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.internal.AlphabetShiftState;
import com.android.inputmethod.keyboard.internal.KeyboardState;
import com.android.inputmethod.latin.define.ProductionFlag;
@@ -45,6 +46,7 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
+import java.util.Map;
/**
* Logs the use of the LatinIME keyboard.
@@ -68,7 +70,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
* Isolates management of files. This variable should never be null, but can be changed
* to support testing.
*/
- private LogFileManager mLogFileManager;
+ /* package */ LogFileManager mLogFileManager;
/**
* Manages the file(s) that stores the logs.
@@ -93,63 +95,53 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mIms = ims;
}
- public synchronized boolean createLogFile() {
- try {
- return createLogFile(DEFAULT_FILENAME);
- } catch (IOException e) {
- e.printStackTrace();
- Log.w(TAG, e);
- return false;
- }
+ public synchronized void createLogFile() throws IOException {
+ createLogFile(DEFAULT_FILENAME);
}
- public synchronized boolean createLogFile(final SharedPreferences prefs) {
- try {
- final String filename =
- prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME);
- return createLogFile(filename);
- } catch (IOException e) {
- Log.w(TAG, e);
- e.printStackTrace();
- }
- return false;
+ public synchronized void createLogFile(final SharedPreferences prefs)
+ throws IOException {
+ final String filename =
+ prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME);
+ createLogFile(filename);
}
- public synchronized boolean createLogFile(final String filename)
+ public synchronized void createLogFile(final String filename)
throws IOException {
if (mIms == null) {
- Log.w(TAG, "InputMethodService is not configured. Logging is off.");
- return false;
+ final String msg = "InputMethodService is not configured. Logging is off.";
+ Log.w(TAG, msg);
+ throw new IOException(msg);
}
final File filesDir = mIms.getFilesDir();
if (filesDir == null || !filesDir.exists()) {
- Log.w(TAG, "Storage directory does not exist. Logging is off.");
- return false;
+ final String msg = "Storage directory does not exist. Logging is off.";
+ Log.w(TAG, msg);
+ throw new IOException(msg);
}
close();
final File file = new File(filesDir, filename);
mFile = file;
- file.setReadable(false, false);
boolean append = true;
if (file.exists() && file.lastModified() + LOGFILE_PURGE_INTERVAL <
System.currentTimeMillis()) {
append = false;
}
mPrintWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, append)), true);
- return true;
}
public synchronized boolean append(final String s) {
- final PrintWriter printWriter = mPrintWriter;
- if (printWriter == null) {
+ PrintWriter printWriter = mPrintWriter;
+ if (printWriter == null || !mFile.exists()) {
if (DEBUG) {
Log.w(TAG, "PrintWriter is null... attempting to create default log file");
}
- if (!createLogFile()) {
- if (DEBUG) {
- Log.w(TAG, "Failed to create log file. Not logging.");
- return false;
- }
+ try {
+ createLogFile();
+ printWriter = mPrintWriter;
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to create log file. Not logging.");
+ return false;
}
}
printWriter.print(s);
@@ -161,9 +153,15 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (mPrintWriter != null) {
mPrintWriter.close();
mPrintWriter = null;
+ if (DEBUG) {
+ Log.d(TAG, "logfile closed");
+ }
}
if (mFile != null) {
mFile.delete();
+ if (DEBUG) {
+ Log.d(TAG, "logfile deleted");
+ }
mFile = null;
}
}
@@ -173,6 +171,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mPrintWriter.close();
mPrintWriter = null;
mFile = null;
+ if (DEBUG) {
+ Log.d(TAG, "logfile closed");
+ }
}
}
@@ -240,12 +241,16 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
sInstance.initInternal(ims, prefs);
}
- public void initInternal(final InputMethodService ims, final SharedPreferences prefs) {
+ /* package */ void initInternal(final InputMethodService ims, final SharedPreferences prefs) {
mIms = ims;
final LogFileManager logFileManager = mLogFileManager;
if (logFileManager != null) {
logFileManager.init(ims);
- logFileManager.createLogFile(prefs);
+ try {
+ logFileManager.createLogFile(prefs);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
if (prefs != null) {
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
@@ -254,19 +259,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
/**
- * Change to a different logFileManager.
- *
- * @throws IllegalArgumentException if logFileManager is null
- */
- void setLogFileManager(final LogFileManager manager) {
- if (manager == null) {
- throw new IllegalArgumentException("warning: trying to set null logFileManager");
- } else {
- mLogFileManager = manager;
- }
- }
-
- /**
* Represents a category of logging events that share the same subfield structure.
*/
private static enum LogGroup {
@@ -334,26 +326,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
public static class UnsLogGroup {
private static final boolean DEFAULT_ENABLED = true;
- private static final boolean ALPHABETSHIFTSTATE_SETSHIFTED_ENABLED = DEFAULT_ENABLED;
- private static final boolean ALPHABETSHIFTSTATE_SETSHIFTLOCKED_ENABLED = DEFAULT_ENABLED;
- private static final boolean ALPHABETSHIFTSTATE_SETAUTOMATICSHIFTED_ENABLED
- = DEFAULT_ENABLED;
private static final boolean KEYBOARDSTATE_ONCANCELINPUT_ENABLED = DEFAULT_ENABLED;
private static final boolean KEYBOARDSTATE_ONCODEINPUT_ENABLED = DEFAULT_ENABLED;
private static final boolean KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED = DEFAULT_ENABLED;
private static final boolean KEYBOARDSTATE_ONPRESSKEY_ENABLED = DEFAULT_ENABLED;
private static final boolean KEYBOARDSTATE_ONRELEASEKEY_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONRESTOREKEYBOARDSTATE_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONSAVEKEYBOARDSTATE_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONUPDATESHIFTSTATE_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_SETALPHABETKEYBOARD_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_SETSHIFTED_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_SETSHIFTLOCKED_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_SETSYMBOLSKEYBOARD_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_SETSYMBOLSSHIFTEDKEYBOARD_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_TOGGLEALPHABETANDSYMBOLS_ENABLED
- = DEFAULT_ENABLED;
private static final boolean LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED = DEFAULT_ENABLED;
private static final boolean LATINIME_COMMITTEXT_ENABLED = DEFAULT_ENABLED;
private static final boolean LATINIME_DELETESURROUNDINGTEXT_ENABLED = DEFAULT_ENABLED;
@@ -377,6 +354,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final boolean LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED = DEFAULT_ENABLED;
private static final boolean LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED
= DEFAULT_ENABLED;
+ private static final boolean LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED = DEFAULT_ENABLED;
private static final boolean POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED
= DEFAULT_ENABLED;
private static final boolean POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED
@@ -413,12 +391,21 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (DEBUG) {
Log.d(TAG, "Write: " + '[' + logGroup.mLogString + ']' + log);
}
- if (mLogFileManager.append(builder.toString())) {
+ final String s = builder.toString();
+ if (mLogFileManager.append(s)) {
// success
} else {
if (DEBUG) {
Log.w(TAG, "Unable to write to log.");
}
+ // perhaps logfile was deleted. try to recreate and relog.
+ try {
+ mLogFileManager.createLogFile(PreferenceManager
+ .getDefaultSharedPreferences(mIms));
+ mLogFileManager.append(s);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
}
});
@@ -448,32 +435,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
}
- public static void alphabetShiftState_setShifted(final boolean newShiftState,
- final int oldState, final AlphabetShiftState alphabetShiftState) {
- if (UnsLogGroup.ALPHABETSHIFTSTATE_SETSHIFTED_ENABLED) {
- final String s = "setShifted(" + newShiftState + "): " + oldState
- + " > " + alphabetShiftState;
- logUnstructured("AlphabetShiftState_setShifted", s);
- }
- }
-
- public static void alphabetShiftState_setShiftLocked(final boolean newShiftLockState,
- final int oldState, final AlphabetShiftState alphabetShiftState) {
- if (UnsLogGroup.ALPHABETSHIFTSTATE_SETSHIFTLOCKED_ENABLED) {
- final String s = "setShiftLocked(" + newShiftLockState + "): "
- + oldState + " > " + alphabetShiftState;
- logUnstructured("AlphabetShiftState_setShiftLocked", s);
- }
- }
-
- public static void alphabetShiftState_setAutomaticShifted(final int oldState,
- final AlphabetShiftState alphabetShiftState) {
- if (UnsLogGroup.ALPHABETSHIFTSTATE_SETAUTOMATICSHIFTED_ENABLED) {
- final String s = "setAutomaticShifted: " + oldState + " > " + alphabetShiftState;
- logUnstructured("AlphabetShiftState_setAutomaticShifted", s);
- }
- }
-
public static void keyboardState_onCancelInput(final boolean isSinglePointer,
final KeyboardState keyboardState) {
if (UnsLogGroup.KEYBOARDSTATE_ONCANCELINPUT_ENABLED) {
@@ -520,76 +481,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
- public static void keyboardState_onRestoreKeyboardState(final KeyboardState keyboardState,
- final String savedKeyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONRESTOREKEYBOARDSTATE_ENABLED) {
- final String s = "onRestoreKeyboardState: saved=" + savedKeyboardState + " "
- + keyboardState;
- logUnstructured("KeyboardState_onRestoreKeyboardState", s);
- }
- }
-
- public static void keyboardState_onSaveKeyboardState(final KeyboardState keyboardState,
- final String savedKeyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONSAVEKEYBOARDSTATE_ENABLED) {
- final String s = "onSaveKeyboardState: saved=" + savedKeyboardState + " "
- + keyboardState;
- logUnstructured("KeyboardState_onSaveKeyboardState", s);
- }
- }
-
- public static void keyboardState_onUpdateShiftState(final KeyboardState keyboardState,
- final boolean autoCaps) {
- if (UnsLogGroup.KEYBOARDSTATE_ONUPDATESHIFTSTATE_ENABLED) {
- final String s = "onUpdateShiftState: autoCaps=" + autoCaps + " " + keyboardState;
- logUnstructured("KeyboardState_onUpdateShiftState", s);
- }
- }
-
- public static void keyboardState_setAlphabetKeyboard() {
- if (UnsLogGroup.KEYBOARDSTATE_SETALPHABETKEYBOARD_ENABLED) {
- final String s = "setAlphabetKeyboard";
- logUnstructured("KeyboardState_setAlphabetKeyboard", s);
- }
- }
-
- public static void keyboardState_setShifted(final KeyboardState keyboardState,
- final String shiftMode) {
- if (UnsLogGroup.KEYBOARDSTATE_SETSHIFTED_ENABLED) {
- final String s = "setShifted: shiftMode=" + shiftMode + " " + keyboardState;
- logUnstructured("KeyboardState_setShifted", s);
- }
- }
-
- public static void keyboardState_setShiftLocked(final KeyboardState keyboardState,
- final boolean shiftLocked) {
- if (UnsLogGroup.KEYBOARDSTATE_SETSHIFTLOCKED_ENABLED) {
- final String s = "setShiftLocked: shiftLocked=" + shiftLocked + " " + keyboardState;
- logUnstructured("KeyboardState_setShiftLocked", s);
- }
- }
-
- public static void keyboardState_setSymbolsKeyboard() {
- if (UnsLogGroup.KEYBOARDSTATE_SETSYMBOLSKEYBOARD_ENABLED) {
- final String s = "setSymbolsKeyboard";
- logUnstructured("KeyboardState_setSymbolsKeyboard", s);
- }
- }
-
- public static void keyboardState_setSymbolsShiftedKeyboard() {
- if (UnsLogGroup.KEYBOARDSTATE_SETSYMBOLSSHIFTEDKEYBOARD_ENABLED) {
- final String s = "setSymbolsShiftedKeyboard";
- logUnstructured("KeyboardState_setSymbolsShiftedKeyboard", s);
- }
- }
-
- public static void keyboardState_toggleAlphabetAndSymbols(final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_TOGGLEALPHABETANDSYMBOLS_ENABLED) {
- final String s = "toggleAlphabetAndSymbols: " + keyboardState;
- logUnstructured("KeyboardState_toggleAlphabetAndSymbols", s);
- }
- }
-
public static void latinIME_commitCurrentAutoCorrection(final String typedWord,
final String autoCorrection) {
if (UnsLogGroup.LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED) {
@@ -637,14 +528,22 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
- public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo) {
+ public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
+ final SharedPreferences prefs) {
if (UnsLogGroup.LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED) {
final StringBuilder builder = new StringBuilder();
builder.append("onStartInputView: editorInfo:");
- builder.append("inputType=");
- builder.append(editorInfo.inputType);
- builder.append("imeOptions=");
- builder.append(editorInfo.imeOptions);
+ builder.append("\tinputType=");
+ builder.append(Integer.toHexString(editorInfo.inputType));
+ builder.append("\timeOptions=");
+ builder.append(Integer.toHexString(editorInfo.imeOptions));
+ builder.append("\tdisplay="); builder.append(Build.DISPLAY);
+ builder.append("\tmodel="); builder.append(Build.MODEL);
+ for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
+ builder.append("\t" + entry.getKey());
+ Object value = entry.getValue();
+ builder.append("=" + ((value == null) ? "<null>" : value.toString()));
+ }
logUnstructured("LatinIME_onStartInputViewInternal", builder.toString());
}
}
@@ -745,6 +644,42 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) {
+ if (UnsLogGroup.LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("id=");
+ builder.append(keyboard.mId);
+ builder.append("\tw=");
+ builder.append(keyboard.mOccupiedWidth);
+ builder.append("\th=");
+ builder.append(keyboard.mOccupiedHeight);
+ builder.append("\tkeys=[");
+ boolean first = true;
+ for (Key key : keyboard.mKeys) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(",");
+ }
+ builder.append("{code:");
+ builder.append(key.mCode);
+ builder.append(",altCode:");
+ builder.append(key.mAltCode);
+ builder.append(",x:");
+ builder.append(key.mX);
+ builder.append(",y:");
+ builder.append(key.mY);
+ builder.append(",w:");
+ builder.append(key.mWidth);
+ builder.append(",h:");
+ builder.append(key.mHeight);
+ builder.append("}");
+ }
+ builder.append("]");
+ logUnstructured("LatinKeyboardView_setKeyboard", builder.toString());
+ }
+ }
+
public static void latinIME_revertCommit(final String originallyTypedWord) {
if (UnsLogGroup.LATINIME_REVERTCOMMIT_ENABLED) {
logUnstructured("LatinIME_revertCommit", originallyTypedWord);
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index fc6193287..33ad23a60 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -27,10 +27,23 @@ import java.util.Locale;
public class SubtypeLocale {
private static final String TAG = SubtypeLocale.class.getSimpleName();
+ // This class must be located in the same package as LatinIME.java.
+ private static final String RESOURCE_PACKAGE_NAME =
+ DictionaryFactory.class.getPackage().getName();
// Special language code to represent "no language".
public static final String NO_LANGUAGE = "zz";
+ public static final String QWERTY = "qwerty";
+
+ private static String[] sPredefinedKeyboardLayoutSet;
+ // Keyboard layout to its display name map.
+ private static final HashMap<String, String> sKeyboardKayoutToDisplayNameMap =
+ new HashMap<String, String>();
+ // Keyboard layout to subtype name resource id map.
+ private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
+ new HashMap<String, Integer>();
+ private static final String SUBTYPE_RESOURCE_GENERIC_NAME_PREFIX = "string/subtype_generic_";
// Exceptional locales to display name map.
private static final HashMap<String, String> sExceptionalDisplayNamesMap =
new HashMap<String, String>();
@@ -41,13 +54,36 @@ public class SubtypeLocale {
public static void init(Context context) {
final Resources res = context.getResources();
- final String[] locales = res.getStringArray(R.array.subtype_locale_exception_keys);
- final String[] displayNames = res.getStringArray(R.array.subtype_locale_exception_values);
- for (int i = 0; i < locales.length; i++) {
- sExceptionalDisplayNamesMap.put(locales[i], displayNames[i]);
+
+ final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
+ sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
+ final String[] layoutDisplayNames = res.getStringArray(
+ R.array.predefined_layout_display_names);
+ for (int i = 0; i < predefinedLayoutSet.length; i++) {
+ final String layoutName = predefinedLayoutSet[i];
+ sKeyboardKayoutToDisplayNameMap.put(layoutName, layoutDisplayNames[i]);
+ final String resourceName = SUBTYPE_RESOURCE_GENERIC_NAME_PREFIX + layoutName;
+ final int resId = res.getIdentifier(resourceName, null, RESOURCE_PACKAGE_NAME);
+ sKeyboardLayoutToNameIdsMap.put(layoutName, resId);
+ }
+
+ final String[] exceptionalLocales = res.getStringArray(
+ R.array.subtype_locale_exception_keys);
+ final String[] exceptionalDisplayNames = res.getStringArray(
+ R.array.subtype_locale_exception_values);
+ for (int i = 0; i < exceptionalLocales.length; i++) {
+ sExceptionalDisplayNamesMap.put(exceptionalLocales[i], exceptionalDisplayNames[i]);
}
}
+ public static String[] getPredefinedKeyboardLayoutSet() {
+ return sPredefinedKeyboardLayoutSet;
+ }
+
+ public static int getSubtypeNameIdFromKeyboardLayoutName(String keyboardLayoutName) {
+ return sKeyboardLayoutToNameIdsMap.get(keyboardLayoutName);
+ }
+
// Get InputMethodSubtype's display name in its locale.
// isAdditionalSubtype (T=true, F=false)
// locale layout | Short Middle Full
@@ -115,7 +151,8 @@ public class SubtypeLocale {
}
public static String getKeyboardLayoutSetDisplayName(InputMethodSubtype subtype) {
- return getKeyboardLayoutSetName(subtype).toUpperCase();
+ final String layoutName = getKeyboardLayoutSetName(subtype);
+ return sKeyboardKayoutToDisplayNameMap.get(layoutName);
}
public static String getKeyboardLayoutSetName(InputMethodSubtype subtype) {
@@ -125,7 +162,7 @@ public class SubtypeLocale {
if (keyboardLayoutSet == null) {
android.util.Log.w(TAG, "KeyboardLayoutSet not found, use QWERTY: " +
"locale=" + subtype.getLocale() + " extraValue=" + subtype.getExtraValue());
- return AdditionalSubtype.QWERTY;
+ return QWERTY;
}
return keyboardLayoutSet;
}
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 1a9f373a1..804287309 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,19 +16,16 @@
package com.android.inputmethod.latin;
-import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.IBinder;
-import android.text.TextUtils;
import android.util.Log;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -36,7 +33,6 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -45,37 +41,42 @@ public class SubtypeSwitcher {
private static boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = SubtypeSwitcher.class.getSimpleName();
- private static final char LOCALE_SEPARATOR = '_';
- private final TextUtils.SimpleStringSplitter mLocaleSplitter =
- new TextUtils.SimpleStringSplitter(LOCALE_SEPARATOR);
-
private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
private /* final */ LatinIME mService;
private /* final */ InputMethodManager mImm;
private /* final */ Resources mResources;
private /* final */ ConnectivityManager mConnectivityManager;
- private final ArrayList<InputMethodSubtype> mEnabledKeyboardSubtypesOfCurrentInputMethod =
- new ArrayList<InputMethodSubtype>();
- private final ArrayList<String> mEnabledLanguagesOfCurrentInputMethod = new ArrayList<String>();
/*-----------------------------------------------------------*/
// Variants which should be changed only by reload functions.
- private boolean mNeedsToDisplayLanguage;
- private boolean mIsDictionaryAvailable;
- private boolean mIsSystemLanguageSameAsInputLanguage;
+ private NeedsToDisplayLanguage mNeedsToDisplayLanguage = new NeedsToDisplayLanguage();
private InputMethodInfo mShortcutInputMethodInfo;
private InputMethodSubtype mShortcutSubtype;
- private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod;
private InputMethodSubtype mNoLanguageSubtype;
// Note: This variable is always non-null after {@link #initialize(LatinIME)}.
private InputMethodSubtype mCurrentSubtype;
- private Locale mSystemLocale;
- private Locale mInputLocale;
- private String mInputLocaleStr;
+ private Locale mCurrentSystemLocale;
/*-----------------------------------------------------------*/
private boolean mIsNetworkConnected;
+ static class NeedsToDisplayLanguage {
+ private int mEnabledSubtypeCount;
+ private boolean mIsSystemLanguageSameAsInputLanguage;
+
+ public boolean getValue() {
+ return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
+ }
+
+ public void updateEnabledSubtypeCount(int count) {
+ mEnabledSubtypeCount = count;
+ }
+
+ public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) {
+ mIsSystemLanguageSameAsInputLanguage = isSame;
+ }
+ }
+
public static SubtypeSwitcher getInstance() {
return sInstance;
}
@@ -96,15 +97,10 @@ public class SubtypeSwitcher {
mImm = ImfUtils.getInputMethodManager(service);
mConnectivityManager = (ConnectivityManager) service.getSystemService(
Context.CONNECTIVITY_SERVICE);
- mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
- mEnabledLanguagesOfCurrentInputMethod.clear();
- mSystemLocale = null;
- mInputLocale = null;
- mInputLocaleStr = null;
+ mCurrentSystemLocale = mResources.getConfiguration().locale;
mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
- mAllEnabledSubtypesOfCurrentInputMethod = null;
mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
- service, SubtypeLocale.NO_LANGUAGE, AdditionalSubtype.QWERTY);
+ service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
mIsNetworkConnected = (info != null && info.isConnected());
@@ -113,7 +109,7 @@ public class SubtypeSwitcher {
// Update all parameters stored in SubtypeSwitcher.
// Only configuration changed event is allowed to call this because this is heavy.
private void updateAllParameters() {
- mSystemLocale = mResources.getConfiguration().locale;
+ mCurrentSystemLocale = mResources.getConfiguration().locale;
updateSubtype(mImm.getCurrentInputMethodSubtype());
updateParametersOnStartInputView();
}
@@ -127,31 +123,20 @@ public class SubtypeSwitcher {
// Reload enabledSubtypes from the framework.
private void updateEnabledSubtypes() {
- final String currentMode = mCurrentSubtype.getMode();
+ final InputMethodSubtype currentSubtype = mCurrentSubtype;
boolean foundCurrentSubtypeBecameDisabled = true;
- mAllEnabledSubtypesOfCurrentInputMethod = mImm.getEnabledInputMethodSubtypeList(
- null, true);
- mEnabledLanguagesOfCurrentInputMethod.clear();
- mEnabledKeyboardSubtypesOfCurrentInputMethod.clear();
- for (InputMethodSubtype ims : mAllEnabledSubtypesOfCurrentInputMethod) {
- final String locale = ims.getLocale();
- final String mode = ims.getMode();
- mLocaleSplitter.setString(locale);
- if (mLocaleSplitter.hasNext()) {
- mEnabledLanguagesOfCurrentInputMethod.add(mLocaleSplitter.next());
- }
- if (locale.equals(mInputLocaleStr) && mode.equals(currentMode)) {
+ final List<InputMethodSubtype> enabledSubtypesOfThisIme =
+ mImm.getEnabledInputMethodSubtypeList(null, true);
+ for (InputMethodSubtype ims : enabledSubtypesOfThisIme) {
+ if (ims.equals(currentSubtype)) {
foundCurrentSubtypeBecameDisabled = false;
}
- if (KEYBOARD_MODE.equals(ims.getMode())) {
- mEnabledKeyboardSubtypesOfCurrentInputMethod.add(ims);
- }
}
- mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
- && mIsSystemLanguageSameAsInputLanguage);
+ mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
if (foundCurrentSubtypeBecameDisabled) {
if (DBG) {
- Log.w(TAG, "Current subtype: " + mInputLocaleStr + ", " + currentMode);
+ Log.w(TAG, "Last subtype: "
+ + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue());
Log.w(TAG, "Last subtype was disabled. Update to the current one.");
}
updateSubtype(mImm.getCurrentInputMethodSubtype());
@@ -192,70 +177,20 @@ public class SubtypeSwitcher {
// Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
public void updateSubtype(InputMethodSubtype newSubtype) {
- final String newLocale = newSubtype.getLocale();
- final String newMode = newSubtype.getMode();
- final String oldMode = mCurrentSubtype.getMode();
if (DBG) {
- Log.w(TAG, "Update subtype to:" + newLocale + "," + newMode
- + ", from: " + mInputLocaleStr + ", " + oldMode);
+ Log.w(TAG, "onCurrentInputMethodSubtypeChanged: to: "
+ + newSubtype.getLocale() + "/" + newSubtype.getExtraValue() + ", from: "
+ + mCurrentSubtype.getLocale() + "/" + mCurrentSubtype.getExtraValue());
}
- boolean languageChanged = false;
- if (!newLocale.equals(mInputLocaleStr)) {
- if (mInputLocaleStr != null) {
- languageChanged = true;
- }
- updateInputLocale(newLocale);
- }
- boolean modeChanged = false;
- if (!newMode.equals(oldMode)) {
- if (oldMode != null) {
- modeChanged = true;
- }
- }
- mCurrentSubtype = newSubtype;
+ if (newSubtype.equals(mCurrentSubtype)) return;
- if (KEYBOARD_MODE.equals(mCurrentSubtype.getMode())) {
- if (modeChanged || languageChanged) {
- updateShortcutIME();
- mService.onRefreshKeyboard();
- }
- } else {
- final String packageName = mService.getPackageName();
- int version = -1;
- try {
- version = mService.getPackageManager().getPackageInfo(
- packageName, 0).versionCode;
- } catch (NameNotFoundException e) {
- }
- Log.w(TAG, "Unknown subtype mode: " + newMode + "," + version + ", " + packageName
- + ". IME is already changed to other IME.");
- Log.w(TAG, "Subtype mode:" + newSubtype.getMode());
- Log.w(TAG, "Subtype locale:" + newSubtype.getLocale());
- Log.w(TAG, "Subtype extra value:" + newSubtype.getExtraValue());
- Log.w(TAG, "Subtype is auxiliary:" + newSubtype.isAuxiliary());
- }
- }
+ final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype);
+ mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(
+ mCurrentSystemLocale.equals(newLocale));
- // Update the current input locale from Locale string.
- private void updateInputLocale(String inputLocaleStr) {
- // example: inputLocaleStr = "en_US" "en" ""
- // "en_US" --> language: en & country: US
- // "en" --> language: en
- // "" --> the system locale
- if (!TextUtils.isEmpty(inputLocaleStr)) {
- mInputLocale = LocaleUtils.constructLocaleFromString(inputLocaleStr);
- mInputLocaleStr = inputLocaleStr;
- } else {
- mInputLocale = mSystemLocale;
- String country = mSystemLocale.getCountry();
- mInputLocaleStr = mSystemLocale.getLanguage()
- + (TextUtils.isEmpty(country) ? "" : "_" + mSystemLocale.getLanguage());
- }
- mIsSystemLanguageSameAsInputLanguage = getSystemLocale().getLanguage().equalsIgnoreCase(
- getInputLocale().getLanguage());
- mNeedsToDisplayLanguage = !(getEnabledKeyboardLocaleCount() <= 1
- && mIsSystemLanguageSameAsInputLanguage);
- mIsDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(mService, mInputLocale);
+ mCurrentSubtype = newSubtype;
+ updateShortcutIME();
+ mService.onRefreshKeyboard();
}
////////////////////////////
@@ -323,62 +258,31 @@ public class SubtypeSwitcher {
}
//////////////////////////////////
- // Language Switching functions //
+ // Subtype Switching functions //
//////////////////////////////////
- public int getEnabledKeyboardLocaleCount() {
- return mEnabledKeyboardSubtypesOfCurrentInputMethod.size();
- }
-
public boolean needsToDisplayLanguage(Locale keyboardLocale) {
if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) {
return true;
}
- if (!keyboardLocale.equals(mInputLocale)) {
+ if (!keyboardLocale.equals(getCurrentSubtypeLocale())) {
return false;
}
- return mNeedsToDisplayLanguage;
- }
-
- public Locale getInputLocale() {
- return mInputLocale;
- }
-
- public String getInputLocaleStr() {
- return mInputLocaleStr;
- }
-
- public String[] getEnabledLanguages() {
- int enabledLanguageCount = mEnabledLanguagesOfCurrentInputMethod.size();
- // Workaround for explicitly specifying the voice language
- if (enabledLanguageCount == 1) {
- mEnabledLanguagesOfCurrentInputMethod.add(mEnabledLanguagesOfCurrentInputMethod
- .get(0));
- ++enabledLanguageCount;
- }
- return mEnabledLanguagesOfCurrentInputMethod.toArray(new String[enabledLanguageCount]);
+ return mNeedsToDisplayLanguage.getValue();
}
- public Locale getSystemLocale() {
- return mSystemLocale;
- }
-
- public boolean isSystemLanguageSameAsInputLanguage() {
- return mIsSystemLanguageSameAsInputLanguage;
+ public Locale getCurrentSubtypeLocale() {
+ return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
}
public void onConfigurationChanged(Configuration conf) {
final Locale systemLocale = conf.locale;
// If system configuration was changed, update all parameters.
- if (!TextUtils.equals(systemLocale.toString(), mSystemLocale.toString())) {
+ if (!systemLocale.equals(mCurrentSystemLocale)) {
updateAllParameters();
}
}
- public boolean isDictionaryAvailable() {
- return mIsDictionaryAvailable;
- }
-
public InputMethodSubtype getCurrentSubtype() {
return mCurrentSubtype;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index d22332116..97df98e34 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -21,6 +21,7 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
@@ -272,6 +273,29 @@ public class BinaryDictInputOutput {
}
/**
+ * Writes a string with our character format to a ByteArrayOutputStream.
+ *
+ * This will also write the terminator byte.
+ *
+ * @param buffer the ByteArrayOutputStream to write to.
+ * @param word the string to write.
+ */
+ private static void writeString(ByteArrayOutputStream buffer, final String word) {
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (1 == getCharSize(codePoint)) {
+ buffer.write((byte) codePoint);
+ } else {
+ buffer.write((byte) (0xFF & (codePoint >> 16)));
+ buffer.write((byte) (0xFF & (codePoint >> 8)));
+ buffer.write((byte) (0xFF & codePoint));
+ }
+ }
+ buffer.write(GROUP_CHARACTERS_TERMINATOR);
+ }
+
+ /**
* Reads a string from a RandomAccessFile. This is the converse of the above method.
*/
private static String readString(final RandomAccessFile source) throws IOException {
@@ -894,15 +918,11 @@ public class BinaryDictInputOutput {
final FusionDictionary dict, final int version)
throws IOException, UnsupportedFormatException {
- // Addresses are limited to 3 bytes, so we'll just make a 16MB buffer. Since addresses
- // can be relative to each node, the structure itself is not limited to 16MB at all, but
- // I doubt this will ever be shot. If it is, deciding the order of the nodes becomes
- // a quite complicated problem, because though the dictionary itself does not have a
- // size limit, each node must still be within 16MB of all its children and parents.
- // As long as this is ensured, the dictionary file may grow to any size.
- // Anyway, to make a dictionary bigger than 16MB just increase the size of this buffer.
- final byte[] buffer = new byte[1 << 24];
- int index = 0;
+ // Addresses are limited to 3 bytes, but since addresses can be relative to each node, the
+ // structure itself is not limited to 16MB. However, if it is over 16MB deciding the order
+ // of the nodes becomes a quite complicated problem, because though the dictionary itself
+ // does not have a size limit, each node must still be within 16MB of all its children and
+ // parents. As long as this is ensured, the dictionary file may grow to any size.
if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION) {
throw new UnsupportedFormatException("Requested file format version " + version
@@ -910,47 +930,54 @@ public class BinaryDictInputOutput {
+ MINIMUM_SUPPORTED_VERSION + " through " + MAXIMUM_SUPPORTED_VERSION);
}
+ ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
+
// The magic number in big-endian order.
if (version >= FIRST_VERSION_WITH_HEADER_SIZE) {
// Magic number for version 2+.
- buffer[index++] = (byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 24));
- buffer[index++] = (byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 16));
- buffer[index++] = (byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 8));
- buffer[index++] = (byte) (0xFF & VERSION_2_MAGIC_NUMBER);
+ headerBuffer.write((byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 24)));
+ headerBuffer.write((byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 16)));
+ headerBuffer.write((byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 8)));
+ headerBuffer.write((byte) (0xFF & VERSION_2_MAGIC_NUMBER));
// Dictionary version.
- buffer[index++] = (byte) (0xFF & (version >> 8));
- buffer[index++] = (byte) (0xFF & version);
+ headerBuffer.write((byte) (0xFF & (version >> 8)));
+ headerBuffer.write((byte) (0xFF & version));
} else {
// Magic number for version 1.
- buffer[index++] = (byte) (0xFF & (VERSION_1_MAGIC_NUMBER >> 8));
- buffer[index++] = (byte) (0xFF & VERSION_1_MAGIC_NUMBER);
+ headerBuffer.write((byte) (0xFF & (VERSION_1_MAGIC_NUMBER >> 8)));
+ headerBuffer.write((byte) (0xFF & VERSION_1_MAGIC_NUMBER));
// Dictionary version.
- buffer[index++] = (byte) (0xFF & version);
+ headerBuffer.write((byte) (0xFF & version));
}
// Options flags
final int options = makeOptionsValue(dict.mOptions);
- buffer[index++] = (byte) (0xFF & (options >> 8));
- buffer[index++] = (byte) (0xFF & options);
+ headerBuffer.write((byte) (0xFF & (options >> 8)));
+ headerBuffer.write((byte) (0xFF & options));
if (version >= FIRST_VERSION_WITH_HEADER_SIZE) {
- final int headerSizeOffset = index;
- index += 4; // Size of the header size
-
+ final int headerSizeOffset = headerBuffer.size();
+ // Placeholder to be written later with header size.
+ for (int i = 0; i < 4; ++i) {
+ headerBuffer.write(0);
+ }
// Write out the options.
for (final String key : dict.mOptions.mAttributes.keySet()) {
final String value = dict.mOptions.mAttributes.get(key);
- index += CharEncoding.writeString(buffer, index, key);
- index += CharEncoding.writeString(buffer, index, value);
+ CharEncoding.writeString(headerBuffer, key);
+ CharEncoding.writeString(headerBuffer, value);
}
-
+ final int size = headerBuffer.size();
+ final byte[] bytes = headerBuffer.toByteArray();
// Write out the header size.
- buffer[headerSizeOffset] = (byte) (0xFF & (index >> 24));
- buffer[headerSizeOffset + 1] = (byte) (0xFF & (index >> 16));
- buffer[headerSizeOffset + 2] = (byte) (0xFF & (index >> 8));
- buffer[headerSizeOffset + 3] = (byte) (0xFF & (index >> 0));
+ bytes[headerSizeOffset] = (byte) (0xFF & (size >> 24));
+ bytes[headerSizeOffset + 1] = (byte) (0xFF & (size >> 16));
+ bytes[headerSizeOffset + 2] = (byte) (0xFF & (size >> 8));
+ bytes[headerSizeOffset + 3] = (byte) (0xFF & (size >> 0));
+ destination.write(bytes);
+ } else {
+ headerBuffer.writeTo(destination);
}
- destination.write(buffer, 0, index);
- index = 0;
+ headerBuffer.close();
// Leave the choice of the optimal node order to the flattenTree function.
MakedictLog.i("Flattening the tree...");
@@ -961,6 +988,12 @@ public class BinaryDictInputOutput {
MakedictLog.i("Checking array...");
checkFlatNodeArray(flatNodes);
+ // Create a buffer that matches the final dictionary size.
+ final Node lastNode = flatNodes.get(flatNodes.size() - 1);
+ final int bufferSize =(lastNode.mCachedAddress + lastNode.mCachedSize);
+ final byte[] buffer = new byte[bufferSize];
+ int index = 0;
+
MakedictLog.i("Writing file...");
int dataEndOffset = 0;
for (Node n : flatNodes) {