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/BinaryDictionaryFileDumper.java21
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java54
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java11
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java60
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java28
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsFragment.java5
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java52
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeLocale.java2
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java20
-rw-r--r--java/src/com/android/inputmethod/latin/XmlParseUtils.java29
-rw-r--r--java/src/com/android/inputmethod/latin/define/ProductionFlag.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java26
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupActivity.java15
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java35
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java8
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java7
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java11
18 files changed, 260 insertions, 133 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 4bec99c04..562e1d0b7 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -450,4 +450,25 @@ public final class BinaryDictionaryFileDumper {
info.toContentValues());
}
}
+
+ /**
+ * Initialize a client record with the dictionary content provider.
+ *
+ * This merely acquires the content provider and calls
+ * #reinitializeClientRecordInDictionaryContentProvider.
+ *
+ * @param context the context for resources and providers.
+ * @param clientId the client ID to use.
+ */
+ public static void initializeClientRecordHelper(final Context context,
+ final String clientId) {
+ try {
+ final ContentProviderClient client = context.getContentResolver().
+ acquireContentProviderClient(getProviderUriBuilder("").build());
+ if (null == client) return;
+ reinitializeClientRecordInDictionaryContentProvider(context, client, clientId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot contact the dictionary content provider", e);
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
index 35f3119ea..41fcb83e6 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
@@ -25,14 +25,35 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.util.Log;
/**
- * Takes action to reload the necessary data when a dictionary pack was added/removed.
+ * Receives broadcasts pertaining to dictionary management and takes the appropriate action.
+ *
+ * This object receives three types of broadcasts.
+ * - Package installed/added. When a dictionary provider application is added or removed, we
+ * need to query the dictionaries.
+ * - New dictionary broadcast. The dictionary provider broadcasts new dictionary availability. When
+ * this happens, we need to re-query the dictionaries.
+ * - Unknown client. If the dictionary provider is in urgent need of data about some client that
+ * it does not know, it sends this broadcast. When we receive this, we need to tell the dictionary
+ * provider about ourselves. This happens when the settings for the dictionary pack are accessed,
+ * but Latin IME never got a chance to register itself.
*/
public final class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = DictionaryPackInstallBroadcastReceiver.class.getSimpleName();
final LatinIME mService;
+ public DictionaryPackInstallBroadcastReceiver() {
+ // This empty constructor is necessary for the system to instantiate this receiver.
+ // This happens when the dictionary pack says it can't find a record for our client,
+ // which happens when the dictionary pack settings are called before the keyboard
+ // was ever started once.
+ Log.i(TAG, "Latin IME dictionary broadcast receiver instantiated from the framework.");
+ mService = null;
+ }
+
public DictionaryPackInstallBroadcastReceiver(final LatinIME service) {
mService = service;
}
@@ -44,6 +65,11 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei
// We need to reread the dictionary if a new dictionary package is installed.
if (action.equals(Intent.ACTION_PACKAGE_ADDED)) {
+ if (null == mService) {
+ Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
+ + "should never happen");
+ return;
+ }
final Uri packageUri = intent.getData();
if (null == packageUri) return; // No package name : we can't do anything
final String packageName = packageUri.getSchemeSpecificPart();
@@ -71,6 +97,11 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei
return;
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
&& !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ if (null == mService) {
+ Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
+ + "should never happen");
+ return;
+ }
// When the dictionary package is removed, we need to reread dictionary (to use the
// next-priority one, or stop using a dictionary at all if this was the only one,
// since this is the user request).
@@ -82,7 +113,28 @@ public final class DictionaryPackInstallBroadcastReceiver extends BroadcastRecei
// read dictionary from?
mService.resetSuggestMainDict();
} else if (action.equals(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)) {
+ if (null == mService) {
+ Log.e(TAG, "Called with intent " + action + " but we don't know the service: this "
+ + "should never happen");
+ return;
+ }
mService.resetSuggestMainDict();
+ } else if (action.equals(DictionaryPackConstants.UNKNOWN_DICTIONARY_PROVIDER_CLIENT)) {
+ if (null != mService) {
+ // Careful! This is returning if the service is NOT null. This is because we
+ // should come here instantiated by the framework in reaction to a broadcast of
+ // the above action, so we should gave gone through the no-args constructor.
+ Log.e(TAG, "Called with intent " + action + " but we have a reference to the "
+ + "service: this should never happen");
+ return;
+ }
+ // The dictionary provider does not know about some client. We check that it's really
+ // us that it needs to know about, and if it's the case, we register with the provider.
+ final String wantedClientId =
+ intent.getStringExtra(DictionaryPackConstants.DICTIONARY_PROVIDER_CLIENT_EXTRA);
+ final String myClientId = context.getString(R.string.dictionary_pack_client_id);
+ if (!wantedClientId.equals(myClientId)) return; // Not for us
+ BinaryDictionaryFileDumper.initializeClientRecordHelper(context, myClientId);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index ae2ee577f..fd81d13ca 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
+import android.util.Log;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -31,6 +32,7 @@ import java.util.LinkedList;
* be searched for suggestions and valid words.
*/
public class ExpandableDictionary extends Dictionary {
+ private static final String TAG = ExpandableDictionary.class.getSimpleName();
/**
* The weight to give to a word if it's length is the same as the number of typed characters.
*/
@@ -551,8 +553,13 @@ public class ExpandableDictionary extends Dictionary {
// word. We do want however to return the correct case for the right hand side.
// So we want to squash the case of the left hand side, and preserve that of the right
// hand side word.
- Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
- Node secondWord = searchWord(mRoots, word2, 0, null);
+ final String word1Lower = word1.toLowerCase();
+ if (TextUtils.isEmpty(word1Lower) || TextUtils.isEmpty(word2)) {
+ Log.e(TAG, "Invalid bigram pair: " + word1 + ", " + word1Lower + ", " + word2);
+ return frequency;
+ }
+ final Node firstWord = searchWord(mRoots, word1Lower, 0, null);
+ final Node secondWord = searchWord(mRoots, word2, 0, null);
LinkedList<NextWord> bigrams = firstWord.mNGrams;
if (bigrams == null || bigrams.size() == 0) {
firstWord.mNGrams = CollectionUtils.newLinkedList();
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index e3650d9cc..56b1c786e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -72,6 +72,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.Utils.Stats;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
@@ -156,7 +157,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private PositionalInfoForUserDictPendingAddition
mPositionalInfoForUserDictPendingAddition = null;
private final WordComposer mWordComposer = new WordComposer();
- private RichInputConnection mConnection = new RichInputConnection(this);
+ private final RichInputConnection mConnection = new RichInputConnection(this);
// Keep track of the last selection range to decide if we need to show word alternatives
private static final int NOT_A_CURSOR_POSITION = -1;
@@ -803,10 +804,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
@Override
public void onWindowHidden() {
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd,
- getCurrentInputConnection());
- }
super.onWindowHidden();
final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
if (mainKeyboardView != null) {
@@ -834,8 +831,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Remove pending messages related to update suggestions
mHandler.cancelUpdateSuggestionStrip();
resetComposingState(true /* alsoResetLastComposedWord */);
+ // Notify ResearchLogger
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.getInstance().latinIME_onFinishInputViewInternal();
+ ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput, mLastSelectionStart,
+ mLastSelectionEnd, getCurrentInputConnection());
}
}
@@ -1145,11 +1144,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (!mWordComposer.isComposingWord()) return;
final String typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
- commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
- separatorString);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.getInstance().onWordFinished(typedWord, mWordComposer.isBatchMode());
}
+ commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
+ separatorString);
}
}
@@ -1907,7 +1906,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private boolean handleSeparator(final int primaryCode, final int x, final int y,
final int spaceState) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.recordTimeForLogUnitSplit();
ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
}
boolean didAutoCorrect = false;
@@ -2176,8 +2174,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
// interface
@Override
- public void pickSuggestionManually(final int index, final String suggestion) {
+ public void pickSuggestionManually(final int index, final SuggestedWordInfo suggestionInfo) {
final SuggestedWords suggestedWords = mSuggestedWords;
+ final String suggestion = suggestionInfo.mWord;
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) {
// Word separators are suggested before the user inputs something.
@@ -2243,7 +2242,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// AND it's in none of our current dictionaries (main, user or otherwise).
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
- final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
+ final boolean showingAddToDictionaryHint =
+ SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind && mSuggest != null
// If the suggestion is not in the dictionary, the hint should be shown.
&& !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
@@ -2297,25 +2297,27 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// expect to receive non-words.
if (!mSettings.getCurrent().mCorrectionEnabled) return null;
+ final Suggest suggest = mSuggest;
final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
- if (userHistoryDictionary != null) {
- final String prevWord
- = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2);
- final String secondWord;
- if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
- secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
- } else {
- secondWord = suggestion;
- }
- // 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 int maxFreq = AutoCorrection.getMaxFrequency(
- mSuggest.getUnigramDictionaries(), suggestion);
- if (maxFreq == 0) return null;
- userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
- return prevWord;
+ if (suggest == null || userHistoryDictionary == null) {
+ // Avoid concurrent issue
+ return null;
+ }
+ final String prevWord
+ = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2);
+ final String secondWord;
+ if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
+ secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
+ } else {
+ secondWord = suggestion;
}
- return null;
+ // 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 int maxFreq = AutoCorrection.getMaxFrequency(
+ suggest.getUnigramDictionaries(), suggestion);
+ if (maxFreq == 0) return null;
+ userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
+ return prevWord;
}
/**
@@ -2526,7 +2528,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
final CharSequence[] items = new CharSequence[] {
// TODO: Should use new string "Select active input modes".
getString(R.string.language_selection_title),
- getString(R.string.english_ime_settings),
+ getString(Utils.getAcitivityTitleResId(this, SettingsActivity.class)),
};
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 8a7ade49e..16744d1f0 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -60,11 +60,11 @@ public final class RichInputConnection {
* This contains the committed text immediately preceding the cursor and the composing
* text if any. It is refreshed when the cursor moves by calling upon the TextView.
*/
- private StringBuilder mCommittedTextBeforeComposingText = new StringBuilder();
+ private final StringBuilder mCommittedTextBeforeComposingText = new StringBuilder();
/**
* This contains the currently composing text, as LatinIME thinks the TextView is seeing it.
*/
- private StringBuilder mComposingText = new StringBuilder();
+ private final StringBuilder mComposingText = new StringBuilder();
// A hint on how many characters to cache from the TextView. A good value of this is given by
// how many characters we need to be able to almost always find the caps mode.
private static final int DEFAULT_TEXT_CACHE_SIZE = 100;
@@ -334,13 +334,15 @@ public final class RichInputConnection {
mCurrentCursorPosition = end;
final CharSequence textBeforeCursor =
getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE + (end - start), 0);
- final int indexOfStartOfComposingText =
- Math.max(textBeforeCursor.length() - (end - start), 0);
- mComposingText.append(textBeforeCursor.subSequence(indexOfStartOfComposingText,
- textBeforeCursor.length()));
mCommittedTextBeforeComposingText.setLength(0);
- mCommittedTextBeforeComposingText.append(
- textBeforeCursor.subSequence(0, indexOfStartOfComposingText));
+ if (!TextUtils.isEmpty(textBeforeCursor)) {
+ final int indexOfStartOfComposingText =
+ Math.max(textBeforeCursor.length() - (end - start), 0);
+ mComposingText.append(textBeforeCursor.subSequence(indexOfStartOfComposingText,
+ textBeforeCursor.length()));
+ mCommittedTextBeforeComposingText.append(
+ textBeforeCursor.subSequence(0, indexOfStartOfComposingText));
+ }
if (null != mIC) {
mIC.setComposingRegion(start, end);
}
@@ -502,16 +504,6 @@ public final class RichInputConnection {
return (r == null) ? null : r.mWord;
}
- private int getCursorPosition() {
- mIC = mParent.getCurrentInputConnection();
- if (null == mIC) return INVALID_CURSOR_POSITION;
- final ExtractedText extracted = mIC.getExtractedText(new ExtractedTextRequest(), 0);
- if (extracted == null) {
- return INVALID_CURSOR_POSITION;
- }
- return extracted.startOffset + extracted.selectionStart;
- }
-
/**
* Returns the text surrounding the cursor.
*
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index 4fdd83911..928141c32 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -69,6 +69,11 @@ public final class SettingsFragment extends InputMethodSettingsFragment
setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
setSubtypeEnablerTitle(R.string.select_language);
addPreferencesFromResource(R.xml.prefs);
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ if (preferenceScreen != null) {
+ preferenceScreen.setTitle(
+ Utils.getAcitivityTitleResId(getActivity(), SettingsActivity.class));
+ }
final Resources res = getResources();
final Context context = getActivity();
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 90c3fcdd2..59ad28fc9 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -22,6 +22,10 @@ import java.util.ArrayList;
import java.util.Locale;
public final class StringUtils {
+ public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
+ public static final int CAPITALIZE_FIRST = 1; // First only
+ public static final int CAPITALIZE_ALL = 2; // All caps
+
private StringUtils() {
// This utility class is not publicly instantiable.
}
@@ -111,11 +115,12 @@ public final class StringUtils {
// - This does not work for Greek, because it returns upper case instead of title case.
// - It does not work for Serbian, because it fails to account for the "lj" character,
// which should be "Lj" in title case and "LJ" in upper case.
- // - It does not work for Dutch, because it fails to account for the "ij" digraph, which
- // are two different characters but both should be capitalized as "IJ" as if they were
- // a single letter.
- // - It also does not work with unicode surrogate code points.
- return s.toUpperCase(locale).charAt(0) + s.substring(1);
+ // - It does not work for Dutch, because it fails to account for the "ij" digraph when it's
+ // written as two separate code points. They are two different characters but both should
+ // be capitalized as "IJ" as if they were a single letter in most words (not all). If the
+ // unicode char for the ligature is used however, it works.
+ final int cutoff = s.offsetByCodePoints(0, 1);
+ return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale);
}
private static final int[] EMPTY_CODEPOINTS = {};
@@ -171,4 +176,41 @@ public final class StringUtils {
}
return list.toArray(new String[list.size()]);
}
+
+ // This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE.
+ public static int getCapitalizationType(final String text) {
+ // If the first char is not uppercase, then the word is either all lower case or
+ // camel case, and in either case we return CAPITALIZE_NONE.
+ final int len = text.length();
+ int index = 0;
+ for (; index < len; index = text.offsetByCodePoints(index, 1)) {
+ if (Character.isLetter(text.codePointAt(index))) {
+ break;
+ }
+ }
+ if (index == len) return CAPITALIZE_NONE;
+ if (!Character.isUpperCase(text.codePointAt(index))) {
+ return CAPITALIZE_NONE;
+ }
+ int capsCount = 1;
+ int letterCount = 1;
+ for (index = text.offsetByCodePoints(index, 1); index < len;
+ index = text.offsetByCodePoints(index, 1)) {
+ if (1 != capsCount && letterCount != capsCount) break;
+ final int codePoint = text.codePointAt(index);
+ if (Character.isUpperCase(codePoint)) {
+ ++capsCount;
+ ++letterCount;
+ } else if (Character.isLetter(codePoint)) {
+ // We need to discount non-letters since they may not be upper-case, but may
+ // still be part of a word (e.g. single quote or dash, as in "IT'S" or "FULL-TIME")
+ ++letterCount;
+ }
+ }
+ // We know the first char is upper case. So we want to test if either every letter other
+ // than the first is lower case, or if they are all upper case. If the string is exactly
+ // one char long, then we will arrive here with letterCount 1, and this is correct, too.
+ if (1 == capsCount) return CAPITALIZE_FIRST;
+ return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 9cbfe6698..5e28cc2d0 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -114,7 +114,7 @@ public final class SubtypeLocale {
final String[] keyboardLayoutSetMap = res.getStringArray(
R.array.locale_and_extra_value_to_keyboard_layout_set_map);
- for (int i = 0; i < keyboardLayoutSetMap.length; i += 2) {
+ for (int i = 0; i + 1 < keyboardLayoutSetMap.length; i += 2) {
final String key = keyboardLayoutSetMap[i];
final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1];
sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet);
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index 62f2a9750..10931555e 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -207,7 +207,12 @@ public final class UserHistoryDictIOUtils {
final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
if (attrList != null) {
for (final PendingAttribute attr : attrList) {
- to.setBigram(word1, unigrams.get(attr.mAddress),
+ final String word2 = unigrams.get(attr.mAddress);
+ if (word1 == null || word2 == null) {
+ Log.e(TAG, "Invalid bigram pair detected: " + word1 + ", " + word2);
+ continue;
+ }
+ to.setBigram(word1, word2,
BinaryDictInputOutput.reconstructBigramFrequency(unigramFrequency,
attr.mFrequency));
}
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 7a604dc6a..aff5d17d7 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -16,8 +16,13 @@
package com.android.inputmethod.latin;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.AsyncTask;
@@ -45,6 +50,8 @@ import java.util.Date;
import java.util.Locale;
public final class Utils {
+ private static final String TAG = Utils.class.getSimpleName();
+
private Utils() {
// This utility class is not publicly instantiable.
}
@@ -453,4 +460,17 @@ public final class Utils {
if (TextUtils.isEmpty(info)) return null;
return info;
}
+
+ public static int getAcitivityTitleResId(Context context, Class<? extends Activity> cls) {
+ final ComponentName cn = new ComponentName(context, cls);
+ try {
+ final ActivityInfo ai = context.getPackageManager().getActivityInfo(cn, 0);
+ if (ai != null) {
+ return ai.labelRes;
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Failed to get settings activity title res id.", e);
+ }
+ return 0;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
index f01d4c5e6..48e5ed30a 100644
--- a/java/src/com/android/inputmethod/latin/XmlParseUtils.java
+++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
@@ -30,50 +30,53 @@ public final class XmlParseUtils {
@SuppressWarnings("serial")
public static class ParseException extends XmlPullParserException {
- public ParseException(String msg, XmlPullParser parser) {
+ public ParseException(final String msg, final XmlPullParser parser) {
super(msg + " at " + parser.getPositionDescription());
}
}
@SuppressWarnings("serial")
public static final class IllegalStartTag extends ParseException {
- public IllegalStartTag(XmlPullParser parser, String parent) {
- super("Illegal start tag " + parser.getName() + " in " + parent, parser);
+ public IllegalStartTag(final XmlPullParser parser, final String tag, final String parent) {
+ super("Illegal start tag " + tag + " in " + parent, parser);
}
}
@SuppressWarnings("serial")
public static final class IllegalEndTag extends ParseException {
- public IllegalEndTag(XmlPullParser parser, String parent) {
- super("Illegal end tag " + parser.getName() + " in " + parent, parser);
+ public IllegalEndTag(final XmlPullParser parser, final String tag, final String parent) {
+ super("Illegal end tag " + tag + " in " + parent, parser);
}
}
@SuppressWarnings("serial")
public static final class IllegalAttribute extends ParseException {
- public IllegalAttribute(XmlPullParser parser, String attribute) {
- super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser);
+ public IllegalAttribute(final XmlPullParser parser, final String tag,
+ final String attribute) {
+ super("Tag " + tag + " has illegal attribute " + attribute, parser);
}
}
@SuppressWarnings("serial")
public static final class NonEmptyTag extends ParseException{
- public NonEmptyTag(String tag, XmlPullParser parser) {
+ public NonEmptyTag(final XmlPullParser parser, final String tag) {
super(tag + " must be empty tag", parser);
}
}
- public static void checkEndTag(String tag, XmlPullParser parser)
+ public static void checkEndTag(final String tag, final XmlPullParser parser)
throws XmlPullParserException, IOException {
if (parser.next() == XmlPullParser.END_TAG && tag.equals(parser.getName()))
return;
- throw new NonEmptyTag(tag, parser);
+ throw new NonEmptyTag(parser, tag);
}
- public static void checkAttributeExists(TypedArray attr, int attrId, String attrName,
- String tag, XmlPullParser parser) throws XmlPullParserException {
- if (attr.hasValue(attrId))
+ public static void checkAttributeExists(final TypedArray attr, final int attrId,
+ final String attrName, final String tag, final XmlPullParser parser)
+ throws XmlPullParserException {
+ if (attr.hasValue(attrId)) {
return;
+ }
throw new ParseException(
"No " + attrName + " attribute found in <" + tag + "/>", parser);
}
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
index 699e47b6a..dc937fb25 100644
--- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
+++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
@@ -28,5 +28,5 @@ public final class ProductionFlag {
// USES_DEVELOPMENT_ONLY_DIAGNOSTICS must be false for any production build.
public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG = false;
- public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = true;
+ public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 5c805598a..e7c7e2b8a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -620,34 +620,34 @@ public final class FusionDictionary implements Iterable<Word> {
* Helper method to find a word in a given branch.
*/
@SuppressWarnings("unused")
- public static CharGroup findWordInTree(Node node, final String s) {
+ public static CharGroup findWordInTree(Node node, final String string) {
int index = 0;
final StringBuilder checker = DBG ? new StringBuilder() : null;
+ final int[] codePoints = getCodePoints(string);
CharGroup currentGroup;
- final int codePointCountInS = s.codePointCount(0, s.length());
do {
- int indexOfGroup = findIndexOfChar(node, s.codePointAt(index));
+ int indexOfGroup = findIndexOfChar(node, codePoints[index]);
if (CHARACTER_NOT_FOUND == indexOfGroup) return null;
currentGroup = node.mData.get(indexOfGroup);
- if (s.length() - index < currentGroup.mChars.length) return null;
+ if (codePoints.length - index < currentGroup.mChars.length) return null;
int newIndex = index;
- while (newIndex < s.length() && newIndex - index < currentGroup.mChars.length) {
- if (currentGroup.mChars[newIndex - index] != s.codePointAt(newIndex)) return null;
+ while (newIndex < codePoints.length && newIndex - index < currentGroup.mChars.length) {
+ if (currentGroup.mChars[newIndex - index] != codePoints[newIndex]) return null;
newIndex++;
}
index = newIndex;
if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length));
- if (index < codePointCountInS) {
+ if (index < codePoints.length) {
node = currentGroup.mChildren;
}
- } while (null != node && index < codePointCountInS);
+ } while (null != node && index < codePoints.length);
- if (index < codePointCountInS) return null;
+ if (index < codePoints.length) return null;
if (!currentGroup.isTerminal()) return null;
- if (DBG && !s.equals(checker.toString())) return null;
+ if (DBG && !codePoints.equals(checker.toString())) return null;
return currentGroup;
}
@@ -847,12 +847,12 @@ public final class FusionDictionary implements Iterable<Word> {
@Override
public Word next() {
Position currentPos = mPositions.getLast();
- mCurrentString.setLength(mCurrentString.length() - currentPos.length);
+ mCurrentString.setLength(currentPos.length);
do {
if (currentPos.pos.hasNext()) {
final CharGroup currentGroup = currentPos.pos.next();
- currentPos.length = currentGroup.mChars.length;
+ currentPos.length = mCurrentString.length();
for (int i : currentGroup.mChars)
mCurrentString.append(Character.toChars(i));
if (null != currentGroup.mChildren) {
@@ -866,7 +866,7 @@ public final class FusionDictionary implements Iterable<Word> {
} else {
mPositions.removeLast();
currentPos = mPositions.getLast();
- mCurrentString.setLength(mCurrentString.length() - mPositions.getLast().length);
+ mCurrentString.setLength(mPositions.getLast().length);
}
} while (true);
}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
index 7f66c6d3e..15d0bac37 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java
@@ -112,12 +112,13 @@ public final class SetupActivity extends Activity {
// TODO: Use sans-serif-thin font family depending on the system locale white list and
// the SDK version.
final TextView titleView = (TextView)findViewById(R.id.setup_title);
- titleView.setText(getString(R.string.setup_title, getString(R.string.english_ime_name)));
+ final int appName = getApplicationInfo().labelRes;
+ titleView.setText(getString(R.string.setup_title, getString(appName)));
mStepIndicatorView = (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator);
final SetupStep step1 = new SetupStep(findViewById(R.id.setup_step1),
- R.string.setup_step1_title, R.string.setup_step1_instruction,
+ appName, R.string.setup_step1_title, R.string.setup_step1_instruction,
R.drawable.ic_settings_language, R.string.language_settings);
step1.setAction(new Runnable() {
@Override
@@ -129,7 +130,7 @@ public final class SetupActivity extends Activity {
mSetupSteps.addStep(STEP_1, step1);
final SetupStep step2 = new SetupStep(findViewById(R.id.setup_step2),
- R.string.setup_step2_title, R.string.setup_step2_instruction,
+ appName, R.string.setup_step2_title, R.string.setup_step2_instruction,
0 /* actionIcon */, R.string.select_input_method);
step2.setAction(new Runnable() {
@Override
@@ -142,7 +143,7 @@ public final class SetupActivity extends Activity {
mSetupSteps.addStep(STEP_2, step2);
final SetupStep step3 = new SetupStep(findViewById(R.id.setup_step3),
- R.string.setup_step3_title, 0 /* instruction */,
+ appName, R.string.setup_step3_title, 0 /* instruction */,
R.drawable.sym_keyboard_language_switch, R.string.setup_step3_instruction);
step3.setAction(new Runnable() {
@Override
@@ -290,11 +291,11 @@ public final class SetupActivity extends Activity {
private final TextView mActionLabel;
private Runnable mAction;
- public SetupStep(final View rootView, final int title, final int instruction,
- final int actionIcon, final int actionLabel) {
+ public SetupStep(final View rootView, final int appName, final int title,
+ final int instruction, final int actionIcon, final int actionLabel) {
mRootView = rootView;
final Resources res = rootView.getResources();
- final String applicationName = res.getString(R.string.english_ime_name);
+ final String applicationName = res.getString(appName);
final TextView titleView = (TextView)rootView.findViewById(R.id.setup_step_title);
titleView.setText(res.getString(title, applicationName));
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 97e280d79..fbed139f3 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -58,10 +58,6 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts";
- public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
- public static final int CAPITALIZE_FIRST = 1; // First only
- public static final int CAPITALIZE_ALL = 2; // All caps
-
private final static String[] EMPTY_STRING_ARRAY = new String[0];
private Map<String, DictionaryPool> mDictionaryPools = CollectionUtils.newSynchronizedTreeMap();
private Map<String, UserBinaryDictionary> mUserDictionaries =
@@ -325,13 +321,13 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
}
Collections.reverse(mSuggestions);
StringUtils.removeDupes(mSuggestions);
- if (CAPITALIZE_ALL == capitalizeType) {
+ if (StringUtils.CAPITALIZE_ALL == capitalizeType) {
for (int i = 0; i < mSuggestions.size(); ++i) {
// get(i) returns a CharSequence which is actually a String so .toString()
// should return the same object.
mSuggestions.set(i, mSuggestions.get(i).toString().toUpperCase(locale));
}
- } else if (CAPITALIZE_FIRST == capitalizeType) {
+ } else if (StringUtils.CAPITALIZE_FIRST == capitalizeType) {
for (int i = 0; i < mSuggestions.size(); ++i) {
// Likewise
mSuggestions.set(i, StringUtils.toTitleCase(
@@ -434,31 +430,4 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
}
return new DictAndProximity(dictionaryCollection, proximityInfo);
}
-
- // This method assumes the text is not empty or null.
- public static int getCapitalizationType(String text) {
- // If the first char is not uppercase, then the word is either all lower case,
- // and in either case we return CAPITALIZE_NONE.
- if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
- final int len = text.length();
- int capsCount = 1;
- int letterCount = 1;
- for (int i = 1; i < len; i = text.offsetByCodePoints(i, 1)) {
- if (1 != capsCount && letterCount != capsCount) break;
- final int codePoint = text.codePointAt(i);
- if (Character.isUpperCase(codePoint)) {
- ++capsCount;
- ++letterCount;
- } else if (Character.isLetter(codePoint)) {
- // We need to discount non-letters since they may not be upper-case, but may
- // still be part of a word (e.g. single quote or dash, as in "IT'S" or "FULL-TIME")
- ++letterCount;
- }
- }
- // We know the first char is upper case. So we want to test if either every letter other
- // than the first is lower case, or if they are all upper case. If the string is exactly
- // one char long, then we will arrive here with letterCount 1, and this is correct, too.
- if (1 == capsCount) return CAPITALIZE_FIRST;
- return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
- }
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 4f86a3175..b15063235 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -150,7 +150,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
// Greek letters are either in the 370~3FF range (Greek & Coptic), or in the
// 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters.
// Our dictionary also contains a few words with 0xF2; it would be best to check
- // if that's correct, but a Google search does return results for these words so
+ // if that's correct, but a web search does return results for these words so
// they are probably okay.
return (codePoint >= 0x370 && codePoint <= 0x3FF)
|| (codePoint >= 0x1F00 && codePoint <= 0x1FFF)
@@ -214,14 +214,14 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
// If the word is in there as is, then it's in the dictionary. If not, we'll test lower
// case versions, but only if the word is not already all-lower case or mixed case.
if (dict.isValidWord(text)) return true;
- if (AndroidSpellCheckerService.CAPITALIZE_NONE == capitalizeType) return false;
+ if (StringUtils.CAPITALIZE_NONE == capitalizeType) return false;
// If we come here, we have a capitalized word (either First- or All-).
// Downcase the word and look it up again. If the word is only capitalized, we
// tested all possibilities, so if it's still negative we can return false.
final String lowerCaseText = text.toLowerCase(mLocale);
if (dict.isValidWord(lowerCaseText)) return true;
- if (AndroidSpellCheckerService.CAPITALIZE_FIRST == capitalizeType) return false;
+ if (StringUtils.CAPITALIZE_FIRST == capitalizeType) return false;
// If the lower case version is not in the dictionary, it's still possible
// that we have an all-caps version of a word that needs to be capitalized
@@ -296,7 +296,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
}
}
- final int capitalizeType = AndroidSpellCheckerService.getCapitalizationType(text);
+ final int capitalizeType = StringUtils.getCapitalizationType(text);
boolean isInDict = true;
DictAndProximity dictInfo = null;
try {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
index 9606b0352..5ce9d8e47 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
@@ -18,8 +18,10 @@ package com.android.inputmethod.latin.spellcheck;
import android.os.Bundle;
import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.Utils;
/**
* Preference screen.
@@ -35,5 +37,10 @@ public final class SpellCheckerSettingsFragment extends PreferenceFragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
addPreferencesFromResource(R.xml.spell_checker_settings);
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ if (preferenceScreen != null) {
+ preferenceScreen.setTitle(Utils.getAcitivityTitleResId(
+ getActivity(), SpellCheckerSettingsActivity.class));
+ }
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 8c3d3b08c..eeaf828a7 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -62,6 +62,7 @@ import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.ResearchLogger;
@@ -72,7 +73,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
OnLongClickListener {
public interface Listener {
public void addWordToUserDictionary(String word);
- public void pickSuggestionManually(int index, String word);
+ public void pickSuggestionManually(int index, SuggestedWordInfo word);
}
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
@@ -656,8 +657,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public boolean onCustomRequest(final int requestCode) {
final int index = requestCode;
- final String word = mSuggestedWords.getWord(index);
- mListener.pickSuggestionManually(index, word);
+ final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(index);
+ mListener.pickSuggestionManually(index, wordInfo);
dismissMoreSuggestions();
return true;
}
@@ -807,8 +808,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (index >= mSuggestedWords.size())
return;
- final String word = mSuggestedWords.getWord(index);
- mListener.pickSuggestionManually(index, word);
+ final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(index);
+ mListener.pickSuggestionManually(index, wordInfo);
}
@Override