diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
20 files changed, 860 insertions, 1011 deletions
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 75c2cf2c8..b9db9a092 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -16,21 +16,26 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.personalization.AccountUtils; + import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; +import android.net.Uri; import android.os.SystemClock; import android.provider.BaseColumns; +import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; import android.text.TextUtils; import android.util.Log; +import java.util.List; import java.util.Locale; public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { - private static final String[] PROJECTION = {BaseColumns._ID, Contacts.DISPLAY_NAME,}; + private static final String[] PROJECTION = {BaseColumns._ID, Contacts.DISPLAY_NAME}; private static final String[] PROJECTION_ID_ONLY = {BaseColumns._ID}; private static final String TAG = ContactsBinaryDictionary.class.getSimpleName(); @@ -102,9 +107,32 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { @Override public void loadDictionaryAsync() { + clearFusionDictionary(); + loadDeviceAccountsEmailAddresses(); + loadDictionaryAsyncForUri(ContactsContract.Profile.CONTENT_URI); + // TODO: Switch this URL to the newer ContactsContract too + loadDictionaryAsyncForUri(Contacts.CONTENT_URI); + } + + private void loadDeviceAccountsEmailAddresses() { + final List<String> accountVocabulary = + AccountUtils.getDeviceAccountsEmailAddresses(mContext); + if (accountVocabulary == null || accountVocabulary.isEmpty()) { + return; + } + for (String word : accountVocabulary) { + if (DEBUG) { + Log.d(TAG, "loadAccountVocabulary: " + word); + } + super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS, + false /* isNotAWord */); + } + } + + private void loadDictionaryAsyncForUri(final Uri uri) { try { Cursor cursor = mContext.getContentResolver() - .query(Contacts.CONTENT_URI, PROJECTION, null, null, null); + .query(uri, PROJECTION, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { @@ -129,7 +157,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } private void addWords(final Cursor cursor) { - clearFusionDictionary(); int count = 0; while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) { String name = cursor.getString(INDEX_NAME); @@ -173,6 +200,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { // capitalization of i. final int wordLen = StringUtils.codePointCount(word); if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { + if (DEBUG) { + Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord); + } super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS, false /* isNotAWord */); if (!TextUtils.isEmpty(prevWord)) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 953ec310a..347a4c63a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -90,7 +90,7 @@ import java.util.TreeSet; /** * Input method implementation for Qwerty'ish keyboard. */ -public final class LatinIME extends InputMethodService implements KeyboardActionListener, +public class LatinIME extends InputMethodService implements KeyboardActionListener, SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener, Suggest.SuggestInitializationListener { private static final String TAG = LatinIME.class.getSimpleName(); @@ -188,6 +188,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Keeps track of most recently inserted text (multi-character key) for reverting private String mEnteredText; + // TODO: This boolean is persistent state and causes large side effects at unexpected times. + // Find a way to remove it for readability. private boolean mIsAutoCorrectionIndicatorOn; private AlertDialog mOptionsDialog; @@ -250,6 +252,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } public void postResumeSuggestions() { + removeMessages(MSG_RESUME_SUGGESTIONS); sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions); } @@ -759,7 +762,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } mSuggestedWords = SuggestedWords.EMPTY; - mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart); + mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart, + false /* shouldFinishComposition */); if (isDifferentTextField) { mainKeyboardView.closing(); @@ -900,7 +904,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // we know for sure the cursor moved while we were composing and we should reset // the state. final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1; - if (!mExpectingUpdateSelection + // If the keyboard is not visible, we don't need to do all the housekeeping work, as it + // will be reset when the keyboard shows up anyway. + // TODO: revisit this when LatinIME supports hardware keyboards. + // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown(). + // TODO: find a better way to simulate actual execution. + if (isInputViewShown() && !mExpectingUpdateSelection && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) { // TAKE CARE: there is a race condition when we enter this test even when the user // did not explicitly move the cursor. This happens when typing fast, where two keys @@ -1148,13 +1157,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // This will reset the whole input state to the starting state. It will clear // the composing word, reset the last composed word, tell the inputconnection about it. private void resetEntireInputState(final int newCursorPosition) { + final boolean shouldFinishComposition = mWordComposer.isComposingWord(); resetComposingState(true /* alsoResetLastComposedWord */); if (mSettings.getCurrent().mBigramPredictionEnabled) { clearSuggestionStrip(); } else { setSuggestedWords(mSettings.getCurrent().mSuggestPuncList, false); } - mConnection.resetCachesUponCursorMove(newCursorPosition); + mConnection.resetCachesUponCursorMove(newCursorPosition, shouldFinishComposition); } private void resetComposingState(final boolean alsoResetLastComposedWord) { @@ -1577,21 +1587,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction commitTyped(LastComposedWord.NOT_A_SEPARATOR); } mExpectingUpdateSelection = true; - // The following is necessary for the case where the user typed something but didn't - // manual pick it and didn't input any separator: we want to put a space between what - // has been entered and the coming gesture input result, so we go into phantom space - // state, which will be promoted to a space when the gesture result is committed. But if - // the current input ends in a word connector on the other hand, then we want to have - // the next input stick to the current input so we don't switch to phantom space state. - if (!mSettings.getCurrent().isWordConnector(lastChar)) { - mSpaceState = SPACE_STATE_PHANTOM; - } - } else { - final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); - if (Character.isLetter(codePointBeforeCursor) - || mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) { - mSpaceState = SPACE_STATE_PHANTOM; - } + } + final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); + if (Character.isLetterOrDigit(codePointBeforeCursor) + || mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) { + mSpaceState = SPACE_STATE_PHANTOM; } mConnection.endBatchEdit(); mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); @@ -1759,9 +1759,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Called from PointerTracker through the KeyboardActionListener interface @Override + public void onFinishSlidingInput() { + // User finished sliding input. + mKeyboardSwitcher.onFinishSlidingInput(); + } + + // Called from PointerTracker through the KeyboardActionListener interface + @Override public void onCancelInput() { // User released a finger outside any key - mKeyboardSwitcher.onCancelInput(); + // Nothing to do so far. } @Override @@ -1905,6 +1912,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction final int y, final int spaceState) { boolean isComposingWord = mWordComposer.isComposingWord(); + // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead. + // See onStartBatchInput() to see how to do it. if (SPACE_STATE_PHANTOM == spaceState && !mSettings.getCurrent().isWordConnector(primaryCode)) { if (isComposingWord) { @@ -2444,10 +2453,15 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void restartSuggestionsOnWordTouchedByCursor() { // If the cursor is not touching a word, or if there is a selection, return right away. if (mLastSelectionStart != mLastSelectionEnd) return; + // If we don't know the cursor location, return. + if (mLastSelectionStart < 0) return; if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return; final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), 0 /* additionalPrecedingWordsCount */); if (null == range) return; // Happens if we don't have an input connection at all + // If for some strange reason (editor bug or so) we measure the text before the cursor as + // longer than what the entire text is supposed to be, the safe thing to do is bail out. + if (range.mCharsBefore > mLastSelectionStart) return; final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final String typedWord = range.mWord.toString(); if (range.mWord instanceof SpannableString) { @@ -2500,7 +2514,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Note that it's very important here that suggestedWords.mWillAutoCorrect is false. // We never want to auto-correct on a resumed suggestion. Please refer to the three - // places above where suggestedWords is affected. + // places above where suggestedWords is affected. We also need to reset + // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it. + // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition) + mIsAutoCorrectionIndicatorOn = false; showSuggestionStrip(suggestedWords, typedWord); } @@ -2612,8 +2629,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // Callback called by PointerTracker through the KeyboardActionListener. This is called when a // key is depressed; release matching call is onReleaseKey below. @Override - public void onPressKey(final int primaryCode) { - mKeyboardSwitcher.onPressKey(primaryCode); + public void onPressKey(final int primaryCode, final boolean isSinglePointer) { + mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer); } // Callback by PointerTracker through the KeyboardActionListener. This is called when a key diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java index b74b979b5..a9fba5348 100644 --- a/java/src/com/android/inputmethod/latin/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java @@ -19,14 +19,17 @@ package com.android.inputmethod.latin; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Build; +import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; +import com.android.inputmethod.annotations.UsedForTesting; + +import java.util.ArrayList; import java.util.HashMap; public final class ResourceUtils { private static final String TAG = ResourceUtils.class.getSimpleName(); - private static final boolean DEBUG = false; public static final float UNDEFINED_RATIO = -1.0f; public static final int UNDEFINED_DIMENSION = -1; @@ -35,11 +38,32 @@ public final class ResourceUtils { // This utility class is not publicly instantiable. } - private static final String DEFAULT_PREFIX = "DEFAULT,"; - private static final String HARDWARE_PREFIX = Build.HARDWARE + ","; private static final HashMap<String, String> sDeviceOverrideValueMap = CollectionUtils.newHashMap(); + private static final String[] BUILD_KEYS_AND_VALUES = { + "HARDWARE", Build.HARDWARE, + "MODEL", Build.MODEL, + "BRAND", Build.BRAND, + "MANUFACTURER", Build.MANUFACTURER + }; + private static final HashMap<String, String> sBuildKeyValues; + private static final String sBuildKeyValuesDebugString; + + static { + sBuildKeyValues = CollectionUtils.newHashMap(); + final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList(); + final int keyCount = BUILD_KEYS_AND_VALUES.length / 2; + for (int i = 0; i < keyCount; i++) { + final int index = i * 2; + final String key = BUILD_KEYS_AND_VALUES[index]; + final String value = BUILD_KEYS_AND_VALUES[index + 1]; + sBuildKeyValues.put(key, value); + keyValuePairs.add(key + '=' + value); + } + sBuildKeyValuesDebugString = "[" + TextUtils.join(" ", keyValuePairs) + "]"; + } + public static String getDeviceOverrideValue(final Resources res, final int overrideResId) { final int orientation = res.getConfiguration().orientation; final String key = overrideResId + "-" + orientation; @@ -48,33 +72,114 @@ public final class ResourceUtils { } final String[] overrideArray = res.getStringArray(overrideResId); - final String overrideValue = StringUtils.findPrefixedString(HARDWARE_PREFIX, overrideArray); + final String overrideValue = findConstantForKeyValuePairs(sBuildKeyValues, overrideArray); // The overrideValue might be an empty string. if (overrideValue != null) { - if (DEBUG) { - Log.d(TAG, "Find override value:" - + " resource="+ res.getResourceEntryName(overrideResId) - + " Build.HARDWARE=" + Build.HARDWARE + " override=" + overrideValue); - } + Log.i(TAG, "Find override value:" + + " resource="+ res.getResourceEntryName(overrideResId) + + " build=" + sBuildKeyValuesDebugString + + " override=" + overrideValue); sDeviceOverrideValueMap.put(key, overrideValue); return overrideValue; } - final String defaultValue = StringUtils.findPrefixedString(DEFAULT_PREFIX, overrideArray); + final String defaultValue = findDefaultConstant(overrideArray); // The defaultValue might be an empty string. if (defaultValue == null) { Log.w(TAG, "Couldn't find override value nor default value:" + " resource="+ res.getResourceEntryName(overrideResId) - + " Build.HARDWARE=" + Build.HARDWARE); - } else if (DEBUG) { - Log.d(TAG, "Found default value:" - + " resource="+ res.getResourceEntryName(overrideResId) - + " Build.HARDWARE=" + Build.HARDWARE + " default=" + defaultValue); + + " build=" + sBuildKeyValuesDebugString); + } else { + Log.i(TAG, "Found default value:" + + " resource="+ res.getResourceEntryName(overrideResId) + + " build=" + sBuildKeyValuesDebugString + + " default=" + defaultValue); } sDeviceOverrideValueMap.put(key, defaultValue); return defaultValue; } + /** + * Find the condition that fulfills specified key value pairs from an array of + * "condition,constant", and return the corresponding string constant. A condition is + * "pattern1[:pattern2...] (or an empty string for the default). A pattern is + * "key=regexp_value" string. The condition matches only if all patterns of the condition + * are true for the specified key value pairs. + * + * For example, "condition,constant" has the following format. + * (See {@link ResourceUtilsTests#testFindConstantForKeyValuePairsRegexp()}) + * - HARDWARE=mako,constantForNexus4 + * - MODEL=Nexus 4:MANUFACTURER=LGE,constantForNexus4 + * - ,defaultConstant + * + * @param keyValuePairs attributes to be used to look for a matched condition. + * @param conditionConstantArray an array of "condition,constant" elements to be searched. + * @return the constant part of the matched "condition,constant" element. Returns null if no + * condition matches. + */ + @UsedForTesting + static String findConstantForKeyValuePairs(final HashMap<String, String> keyValuePairs, + final String[] conditionConstantArray) { + if (conditionConstantArray == null || keyValuePairs == null) { + return null; + } + for (final String conditionConstant : conditionConstantArray) { + final int posComma = conditionConstant.indexOf(','); + if (posComma < 0) { + throw new RuntimeException("Array element has no comma: " + conditionConstant); + } + final String condition = conditionConstant.substring(0, posComma); + if (condition.isEmpty()) { + // Default condition. The default condition should be searched by + // {@link #findConstantForDefault(String[])}. + continue; + } + if (fulfillsCondition(keyValuePairs, condition)) { + return conditionConstant.substring(posComma + 1); + } + } + return null; + } + + private static boolean fulfillsCondition(final HashMap<String,String> keyValuePairs, + final String condition) { + final String[] patterns = condition.split(":"); + // Check all patterns in a condition are true + for (final String pattern : patterns) { + final int posEqual = pattern.indexOf('='); + if (posEqual < 0) { + throw new RuntimeException("Pattern has no '=': " + condition); + } + final String key = pattern.substring(0, posEqual); + final String value = keyValuePairs.get(key); + if (value == null) { + throw new RuntimeException("Found unknown key: " + condition); + } + final String patternRegexpValue = pattern.substring(posEqual + 1); + if (!value.matches(patternRegexpValue)) { + return false; + } + } + return true; + } + + @UsedForTesting + static String findDefaultConstant(final String[] conditionConstantArray) { + if (conditionConstantArray == null) { + return null; + } + for (final String condition : conditionConstantArray) { + final int posComma = condition.indexOf(','); + if (posComma < 0) { + throw new RuntimeException("Array element has no comma: " + condition); + } + if (posComma == 0) { // condition is empty. + return condition.substring(posComma + 1); + } + } + return null; + } + public static boolean isValidFraction(final float fraction) { return fraction >= 0.0f; } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 8ed7ab264..980215de6 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -135,13 +135,14 @@ public final class RichInputConnection { if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } - public void resetCachesUponCursorMove(final int newCursorPosition) { + public void resetCachesUponCursorMove(final int newCursorPosition, + final boolean shouldFinishComposition) { mCurrentCursorPosition = newCursorPosition; mComposingText.setLength(0); mCommittedTextBeforeComposingText.setLength(0); final CharSequence textBeforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0); if (null != textBeforeCursor) mCommittedTextBeforeComposingText.append(textBeforeCursor); - if (null != mIC) { + if (null != mIC && shouldFinishComposition) { mIC.finishComposingText(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.richInputConnection_finishComposingText(); diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 3f7be99e5..94513e635 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -100,6 +100,12 @@ public final class RichInputMethodManager { throw new RuntimeException("Input method id for " + packageName + " not found."); } + public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList( + boolean allowsImplicitlySelectedSubtypes) { + return mImmWrapper.mImm.getEnabledInputMethodSubtypeList( + mInputMethodInfoOfThisIme, allowsImplicitlySelectedSubtypes); + } + public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { if (mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme)) { return true; @@ -116,8 +122,8 @@ public final class RichInputMethodManager { final boolean onlyCurrentIme) { final InputMethodManager imm = mImmWrapper.mImm; final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype(); - final List<InputMethodSubtype> enabledSubtypes = imm.getEnabledInputMethodSubtypeList( - mInputMethodInfoOfThisIme, true /* allowsImplicitlySelectedSubtypes */); + final List<InputMethodSubtype> enabledSubtypes = getMyEnabledInputMethodSubtypeList( + true /* allowsImplicitlySelectedSubtypes */); final int currentIndex = getSubtypeIndexInList(currentSubtype, enabledSubtypes); if (currentIndex == INDEX_NOT_FOUND) { Log.w(TAG, "Can't find current subtype in enabled subtypes: subtype=" @@ -214,8 +220,8 @@ public final class RichInputMethodManager { final InputMethodSubtype subtype) { final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype); final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList( - subtype, mImmWrapper.mImm.getEnabledInputMethodSubtypeList( - mInputMethodInfoOfThisIme, false /* allowsImplicitlySelectedSubtypes */)); + subtype, getMyEnabledInputMethodSubtypeList( + false /* allowsImplicitlySelectedSubtypes */)); return subtypeEnabled && !subtypeExplicitlyEnabled; } @@ -312,8 +318,7 @@ public final class RichInputMethodManager { if (filteredImisCount > 1) { return true; } - final List<InputMethodSubtype> subtypes = - mImmWrapper.mImm.getEnabledInputMethodSubtypeList(null, true); + final List<InputMethodSubtype> subtypes = getMyEnabledInputMethodSubtypeList(true); int keyboardCount = 0; // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's // both explicitly and implicitly enabled input method subtype. diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java index 9819a02ef..7c4156c48 100644 --- a/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java +++ b/java/src/com/android/inputmethod/latin/SeekBarDialogPreference.java @@ -59,7 +59,7 @@ public final class SeekBarDialogPreference extends DialogPreference public void setInterface(final ValueProxy proxy) { mValueProxy = proxy; - setSummary(getValueText(proxy.readValue(getKey()))); + setSummary(getValueText(clipValue(proxy.readValue(getKey())))); } private String getValueText(final int value) { diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index d5ee58a63..ab050d7a3 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -65,23 +65,6 @@ public final class StringUtils { } /** - * Find a string that start with specified prefix from an array. - * - * @param prefix a prefix string to find. - * @param array an string array to be searched. - * @return the rest part of the string that starts with the prefix. - * Returns null if it couldn't be found. - */ - public static String findPrefixedString(final String prefix, final String[] array) { - for (final String element : array) { - if (element.startsWith(prefix)) { - return element.substring(prefix.length()); - } - } - return null; - } - - /** * Remove duplicates from an array of strings. * * This method will always keep the first occurrence of all strings at their position diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index bef8a3cf1..282b5794f 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -115,7 +115,7 @@ public final class SubtypeSwitcher { */ public void updateParametersOnStartInputView() { final List<InputMethodSubtype> enabledSubtypesOfThisIme = - mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true); + mRichImm.getMyEnabledInputMethodSubtypeList(true); mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size()); updateShortcutIME(); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 51bd901fb..e078f03f4 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -16,7 +16,6 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; @@ -211,9 +210,8 @@ public final class WordComposer { } /** - * Internal method to retrieve reasonable proximity info for a character. + * Add a dummy key by retrieving reasonable coordinates */ - @UsedForTesting public void addKeyInfo(final int codePoint, final Keyboard keyboard) { final int x, y; final Key key; diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java new file mode 100644 index 000000000..93687e193 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.personalization; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.Context; +import android.util.Patterns; + +import java.util.ArrayList; +import java.util.List; + +public class AccountUtils { + private AccountUtils() { + // This utility class is not publicly instantiable. + } + + private static Account[] getAccounts(final Context context) { + return AccountManager.get(context).getAccounts(); + } + + public static List<String> getDeviceAccountsEmailAddresses(final Context context) { + final ArrayList<String> retval = new ArrayList<String>(); + for (final Account account : getAccounts(context)) { + final String name = account.name; + if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) { + retval.add(name); + retval.add(name.split("@")[0]); + } + } + return retval; + } +} diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java index affe3a348..8a2de887d 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java @@ -17,270 +17,27 @@ package com.android.inputmethod.latin.setup; import android.app.Activity; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; -import android.media.MediaPlayer; -import android.net.Uri; import android.os.Bundle; -import android.os.Message; import android.provider.Settings; -import android.util.Log; -import android.view.View; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.VideoView; -import com.android.inputmethod.compat.TextViewCompatUtils; -import com.android.inputmethod.compat.ViewCompatUtils; -import com.android.inputmethod.latin.CollectionUtils; -import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; -import com.android.inputmethod.latin.SettingsActivity; -import com.android.inputmethod.latin.StaticInnerHandlerWrapper; - -import java.util.ArrayList; - -// TODO: Use Fragment to implement welcome screen and setup steps. -public final class SetupActivity extends Activity implements View.OnClickListener { - private static final String TAG = SetupActivity.class.getSimpleName(); - - private View mWelcomeScreen; - private View mSetupScreen; - private Uri mWelcomeVideoUri; - private VideoView mWelcomeVideoView; - private View mActionStart; - private View mActionNext; - private TextView mStep1Bullet; - private TextView mActionFinish; - private SetupStepGroup mSetupStepGroup; - private static final String STATE_STEP = "step"; - private int mStepNumber; - private static final int STEP_WELCOME = 0; - private static final int STEP_1 = 1; - private static final int STEP_2 = 2; - private static final int STEP_3 = 3; - private boolean mWasLanguageAndInputSettingsInvoked; - - private final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this); - - static final class SettingsPoolingHandler extends StaticInnerHandlerWrapper<SetupActivity> { - private static final int MSG_POLLING_IME_SETTINGS = 0; - private static final long IME_SETTINGS_POLLING_INTERVAL = 200; - - public SettingsPoolingHandler(final SetupActivity outerInstance) { - super(outerInstance); - } - - @Override - public void handleMessage(final Message msg) { - final SetupActivity setupActivity = getOuterInstance(); - if (setupActivity == null) { - return; - } - switch (msg.what) { - case MSG_POLLING_IME_SETTINGS: - if (SetupActivity.isThisImeEnabled(setupActivity)) { - setupActivity.invokeSetupWizardOfThisIme(); - return; - } - startPollingImeSettings(); - break; - } - } - - public void startPollingImeSettings() { - sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS), - IME_SETTINGS_POLLING_INTERVAL); - } - - public void cancelPollingImeSettings() { - removeMessages(MSG_POLLING_IME_SETTINGS); - } - } +public final class SetupActivity extends Activity { @Override protected void onCreate(final Bundle savedInstanceState) { - setTheme(android.R.style.Theme_DeviceDefault_Light_NoActionBar); super.onCreate(savedInstanceState); - - setContentView(R.layout.setup_wizard); - - RichInputMethodManager.init(this); - - if (savedInstanceState == null) { - mStepNumber = determineSetupStepNumber(); - if (mStepNumber == STEP_1 && !mWasLanguageAndInputSettingsInvoked) { - mStepNumber = STEP_WELCOME; - } - if (mStepNumber == STEP_3) { - // This IME already has been enabled and set as current IME. - // TODO: Implement tutorial. - invokeSettingsOfThisIme(); - finish(); - return; - } - } else { - mStepNumber = savedInstanceState.getInt(STATE_STEP); - } - - final String applicationName = getResources().getString(getApplicationInfo().labelRes); - mWelcomeScreen = findViewById(R.id.setup_welcome_screen); - final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title); - welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName)); - - mSetupScreen = findViewById(R.id.setup_steps_screen); - final TextView stepsTitle = (TextView)findViewById(R.id.setup_title); - stepsTitle.setText(getString(R.string.setup_steps_title, applicationName)); - - final SetupStepIndicatorView indicatorView = - (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); - mSetupStepGroup = new SetupStepGroup(indicatorView); - - mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet); - mStep1Bullet.setOnClickListener(this); - final SetupStep step1 = new SetupStep(STEP_1, applicationName, - mStep1Bullet, findViewById(R.id.setup_step1), - R.string.setup_step1_title, R.string.setup_step1_instruction, - R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1, - R.string.setup_step1_action); - step1.setAction(new Runnable() { - @Override - public void run() { - invokeLanguageAndInputSettings(); - mHandler.startPollingImeSettings(); - } - }); - mSetupStepGroup.addStep(step1); - - final SetupStep step2 = new SetupStep(STEP_2, applicationName, - (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2), - R.string.setup_step2_title, R.string.setup_step2_instruction, - 0 /* finishedInstruction */, R.drawable.ic_setup_step2, - R.string.setup_step2_action); - step2.setAction(new Runnable() { - @Override - public void run() { - // Invoke input method picker. - RichInputMethodManager.getInstance().getInputMethodManager() - .showInputMethodPicker(); - } - }); - mSetupStepGroup.addStep(step2); - - final SetupStep step3 = new SetupStep(STEP_3, applicationName, - (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3), - R.string.setup_step3_title, R.string.setup_step3_instruction, - 0 /* finishedInstruction */, R.drawable.ic_setup_step3, - R.string.setup_step3_action); - step3.setAction(new Runnable() { - @Override - public void run() { - invokeSubtypeEnablerOfThisIme(); - } - }); - mSetupStepGroup.addStep(step3); - - mWelcomeVideoUri = new Uri.Builder() - .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) - .authority(getPackageName()) - .path(Integer.toString(R.raw.setup_welcome_video)) - .build(); - mWelcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video); - mWelcomeVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(final MediaPlayer mp) { - mp.start(); - } - }); - mWelcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { - @Override - public void onPrepared(final MediaPlayer mp) { - // Now VideoView has been laid-out and ready to play, remove background of it to - // reveal the video. - mWelcomeVideoView.setBackgroundResource(0); - } - }); - final ImageView welcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image); - mWelcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public boolean onError(final MediaPlayer mp, final int what, final int extra) { - Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra); - mWelcomeVideoView.setVisibility(View.GONE); - welcomeImageView.setImageResource(R.raw.setup_welcome_image); - welcomeImageView.setVisibility(View.VISIBLE); - return true; - } - }); - - mActionStart = findViewById(R.id.setup_start_label); - mActionStart.setOnClickListener(this); - mActionNext = findViewById(R.id.setup_next); - mActionNext.setOnClickListener(this); - mActionFinish = (TextView)findViewById(R.id.setup_finish); - TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish, - getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null); - mActionFinish.setOnClickListener(this); - } - - @Override - public void onClick(final View v) { - if (v == mActionFinish) { - finish(); - return; - } - final int currentStep = determineSetupStepNumber(); - final int nextStep; - if (v == mActionStart) { - nextStep = STEP_1; - } else if (v == mActionNext) { - nextStep = mStepNumber + 1; - } else if (v == mStep1Bullet && currentStep == STEP_2) { - nextStep = STEP_1; - } else { - nextStep = mStepNumber; - } - if (mStepNumber != nextStep) { - mStepNumber = nextStep; - updateSetupStepView(); - } - } - - private void invokeSetupWizardOfThisIme() { - final Intent intent = new Intent(); - intent.setClass(this, SetupActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - } - - private void invokeSettingsOfThisIme() { - final Intent intent = new Intent(); - intent.setClass(this, SettingsActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - } - - private void invokeLanguageAndInputSettings() { final Intent intent = new Intent(); - intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - startActivity(intent); - mWasLanguageAndInputSettingsInvoked = true; - } - - private void invokeSubtypeEnablerOfThisIme() { - final InputMethodInfo imi = - RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme(); - final Intent intent = new Intent(); - intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); - intent.addCategory(Intent.CATEGORY_DEFAULT); - intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId()); + intent.setClass(this, SetupWizardActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); + if (!isFinishing()) { + finish(); + } } /** @@ -317,164 +74,4 @@ public final class SetupActivity extends Activity implements View.OnClickListene context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); return myImi.getId().equals(currentImeId); } - - private int determineSetupStepNumber() { - mHandler.cancelPollingImeSettings(); - if (!isThisImeEnabled(this)) { - return STEP_1; - } - if (!isThisImeCurrent(this)) { - return STEP_2; - } - return STEP_3; - } - - @Override - protected void onSaveInstanceState(final Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(STATE_STEP, mStepNumber); - } - - @Override - protected void onRestoreInstanceState(final Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - mStepNumber = savedInstanceState.getInt(STATE_STEP); - } - - @Override - protected void onRestart() { - super.onRestart(); - if (mStepNumber != STEP_WELCOME) { - mStepNumber = determineSetupStepNumber(); - } - } - - @Override - protected void onResume() { - super.onResume(); - updateSetupStepView(); - } - - @Override - public void onBackPressed() { - if (mStepNumber == STEP_1) { - mStepNumber = STEP_WELCOME; - updateSetupStepView(); - return; - } - super.onBackPressed(); - } - - @Override - protected void onPause() { - mWelcomeVideoView.stopPlayback(); - super.onPause(); - } - - @Override - public void onWindowFocusChanged(final boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus && mStepNumber != STEP_WELCOME) { - mStepNumber = determineSetupStepNumber(); - updateSetupStepView(); - } - } - - private void updateSetupStepView() { - final boolean welcomeScreen = (mStepNumber == STEP_WELCOME); - mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE); - mSetupScreen.setVisibility(welcomeScreen ? View.GONE: View.VISIBLE); - if (welcomeScreen) { - mWelcomeVideoView.setVideoURI(mWelcomeVideoUri); - mWelcomeVideoView.start(); - return; - } - mWelcomeVideoView.stopPlayback(); - final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber(); - mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone); - mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE); - mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE); - } - - static final class SetupStep implements View.OnClickListener { - public final int mStepNo; - private final View mStepView; - private final TextView mBulletView; - private final int mActivatedColor; - private final int mDeactivatedColor; - private final String mInstruction; - private final String mFinishedInstruction; - private final TextView mActionLabel; - private Runnable mAction; - - public SetupStep(final int stepNo, final String applicationName, final TextView bulletView, - final View stepView, final int title, final int instruction, - final int finishedInstruction,final int actionIcon, final int actionLabel) { - mStepNo = stepNo; - mStepView = stepView; - mBulletView = bulletView; - final Resources res = stepView.getResources(); - mActivatedColor = res.getColor(R.color.setup_text_action); - mDeactivatedColor = res.getColor(R.color.setup_text_dark); - - final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title); - titleView.setText(res.getString(title, applicationName)); - mInstruction = (instruction == 0) ? null - : res.getString(instruction, applicationName); - mFinishedInstruction = (finishedInstruction == 0) ? null - : res.getString(finishedInstruction, applicationName); - - mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label); - mActionLabel.setText(res.getString(actionLabel)); - if (actionIcon == 0) { - final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); - ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); - } else { - TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( - mActionLabel, res.getDrawable(actionIcon), null, null, null); - } - } - - public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) { - mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE); - mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor); - final TextView instructionView = (TextView)mStepView.findViewById( - R.id.setup_step_instruction); - instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction); - mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE); - } - - public void setAction(final Runnable action) { - mActionLabel.setOnClickListener(this); - mAction = action; - } - - @Override - public void onClick(final View v) { - if (v == mActionLabel && mAction != null) { - mAction.run(); - return; - } - } - } - - static final class SetupStepGroup { - private final SetupStepIndicatorView mIndicatorView; - private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList(); - - public SetupStepGroup(final SetupStepIndicatorView indicatorView) { - mIndicatorView = indicatorView; - } - - public void addStep(final SetupStep step) { - mGroup.add(step); - } - - public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) { - for (final SetupStep step : mGroup) { - step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone); - } - mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size()); - } - } } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java new file mode 100644 index 000000000..78a6478c6 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.setup; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.res.Resources; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; +import android.view.View; +import android.view.inputmethod.InputMethodInfo; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.VideoView; + +import com.android.inputmethod.compat.TextViewCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; +import com.android.inputmethod.latin.CollectionUtils; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.SettingsActivity; +import com.android.inputmethod.latin.StaticInnerHandlerWrapper; + +import java.util.ArrayList; + +// TODO: Use Fragment to implement welcome screen and setup steps. +public final class SetupWizardActivity extends Activity implements View.OnClickListener { + static final String TAG = SetupWizardActivity.class.getSimpleName(); + + private static final boolean ENABLE_WELCOME_VIDEO = true; + + private View mSetupWizard; + private View mWelcomeScreen; + private View mSetupScreen; + private Uri mWelcomeVideoUri; + private VideoView mWelcomeVideoView; + private ImageView mWelcomeImageView; + private View mActionStart; + private View mActionNext; + private TextView mStep1Bullet; + private TextView mActionFinish; + private SetupStepGroup mSetupStepGroup; + private static final String STATE_STEP = "step"; + private int mStepNumber; + private boolean mNeedsToAdjustStepNumberToSystemState; + private static final int STEP_WELCOME = 0; + private static final int STEP_1 = 1; + private static final int STEP_2 = 2; + private static final int STEP_3 = 3; + private static final int STEP_LAUNCHING_IME_SETTINGS = 4; + private static final int STEP_BACK_FROM_IME_SETTINGS = 5; + + final SettingsPoolingHandler mHandler = new SettingsPoolingHandler(this); + + static final class SettingsPoolingHandler + extends StaticInnerHandlerWrapper<SetupWizardActivity> { + private static final int MSG_POLLING_IME_SETTINGS = 0; + private static final long IME_SETTINGS_POLLING_INTERVAL = 200; + + public SettingsPoolingHandler(final SetupWizardActivity outerInstance) { + super(outerInstance); + } + + @Override + public void handleMessage(final Message msg) { + final SetupWizardActivity setupWizardActivity = getOuterInstance(); + if (setupWizardActivity == null) { + return; + } + switch (msg.what) { + case MSG_POLLING_IME_SETTINGS: + if (SetupActivity.isThisImeEnabled(setupWizardActivity)) { + setupWizardActivity.invokeSetupWizardOfThisIme(); + return; + } + startPollingImeSettings(); + break; + } + } + + public void startPollingImeSettings() { + sendMessageDelayed(obtainMessage(MSG_POLLING_IME_SETTINGS), + IME_SETTINGS_POLLING_INTERVAL); + } + + public void cancelPollingImeSettings() { + removeMessages(MSG_POLLING_IME_SETTINGS); + } + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + setTheme(android.R.style.Theme_Translucent_NoTitleBar); + super.onCreate(savedInstanceState); + + setContentView(R.layout.setup_wizard); + mSetupWizard = findViewById(R.id.setup_wizard); + + RichInputMethodManager.init(this); + + if (savedInstanceState == null) { + mStepNumber = determineSetupStepNumberFromLauncher(); + } else { + mStepNumber = savedInstanceState.getInt(STATE_STEP); + } + + final String applicationName = getResources().getString(getApplicationInfo().labelRes); + mWelcomeScreen = findViewById(R.id.setup_welcome_screen); + final TextView welcomeTitle = (TextView)findViewById(R.id.setup_welcome_title); + welcomeTitle.setText(getString(R.string.setup_welcome_title, applicationName)); + + mSetupScreen = findViewById(R.id.setup_steps_screen); + final TextView stepsTitle = (TextView)findViewById(R.id.setup_title); + stepsTitle.setText(getString(R.string.setup_steps_title, applicationName)); + + final SetupStepIndicatorView indicatorView = + (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); + mSetupStepGroup = new SetupStepGroup(indicatorView); + + mStep1Bullet = (TextView)findViewById(R.id.setup_step1_bullet); + mStep1Bullet.setOnClickListener(this); + final SetupStep step1 = new SetupStep(STEP_1, applicationName, + mStep1Bullet, findViewById(R.id.setup_step1), + R.string.setup_step1_title, R.string.setup_step1_instruction, + R.string.setup_step1_finished_instruction, R.drawable.ic_setup_step1, + R.string.setup_step1_action); + step1.setAction(new Runnable() { + @Override + public void run() { + invokeLanguageAndInputSettings(); + mHandler.startPollingImeSettings(); + } + }); + mSetupStepGroup.addStep(step1); + + final SetupStep step2 = new SetupStep(STEP_2, applicationName, + (TextView)findViewById(R.id.setup_step2_bullet), findViewById(R.id.setup_step2), + R.string.setup_step2_title, R.string.setup_step2_instruction, + 0 /* finishedInstruction */, R.drawable.ic_setup_step2, + R.string.setup_step2_action); + step2.setAction(new Runnable() { + @Override + public void run() { + invokeInputMethodPicker(); + } + }); + mSetupStepGroup.addStep(step2); + + final SetupStep step3 = new SetupStep(STEP_3, applicationName, + (TextView)findViewById(R.id.setup_step3_bullet), findViewById(R.id.setup_step3), + R.string.setup_step3_title, R.string.setup_step3_instruction, + 0 /* finishedInstruction */, R.drawable.ic_setup_step3, + R.string.setup_step3_action); + step3.setAction(new Runnable() { + @Override + public void run() { + invokeSubtypeEnablerOfThisIme(); + } + }); + mSetupStepGroup.addStep(step3); + + mWelcomeVideoUri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(getPackageName()) + .path(Integer.toString(R.raw.setup_welcome_video)) + .build(); + final VideoView welcomeVideoView = (VideoView)findViewById(R.id.setup_welcome_video); + welcomeVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(final MediaPlayer mp) { + // Now VideoView has been laid-out and ready to play, remove background of it to + // reveal the video. + welcomeVideoView.setBackgroundResource(0); + mp.setLooping(true); + } + }); + welcomeVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { + @Override + public boolean onError(final MediaPlayer mp, final int what, final int extra) { + Log.e(TAG, "Playing welcome video causes error: what=" + what + " extra=" + extra); + hideWelcomeVideoAndShowWelcomeImage(); + return true; + } + }); + mWelcomeVideoView = welcomeVideoView; + mWelcomeImageView = (ImageView)findViewById(R.id.setup_welcome_image); + + mActionStart = findViewById(R.id.setup_start_label); + mActionStart.setOnClickListener(this); + mActionNext = findViewById(R.id.setup_next); + mActionNext.setOnClickListener(this); + mActionFinish = (TextView)findViewById(R.id.setup_finish); + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(mActionFinish, + getResources().getDrawable(R.drawable.ic_setup_finish), null, null, null); + mActionFinish.setOnClickListener(this); + } + + @Override + public void onClick(final View v) { + if (v == mActionFinish) { + finish(); + return; + } + final int currentStep = determineSetupStepNumber(); + final int nextStep; + if (v == mActionStart) { + nextStep = STEP_1; + } else if (v == mActionNext) { + nextStep = mStepNumber + 1; + } else if (v == mStep1Bullet && currentStep == STEP_2) { + nextStep = STEP_1; + } else { + nextStep = mStepNumber; + } + if (mStepNumber != nextStep) { + mStepNumber = nextStep; + updateSetupStepView(); + } + } + + void invokeSetupWizardOfThisIme() { + final Intent intent = new Intent(); + intent.setClass(this, SetupWizardActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + mNeedsToAdjustStepNumberToSystemState = true; + } + + private void invokeSettingsOfThisIme() { + final Intent intent = new Intent(); + intent.setClass(this, SettingsActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + } + + void invokeLanguageAndInputSettings() { + final Intent intent = new Intent(); + intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + startActivity(intent); + mNeedsToAdjustStepNumberToSystemState = true; + } + + void invokeInputMethodPicker() { + // Invoke input method picker. + RichInputMethodManager.getInstance().getInputMethodManager() + .showInputMethodPicker(); + mNeedsToAdjustStepNumberToSystemState = true; + } + + void invokeSubtypeEnablerOfThisIme() { + final InputMethodInfo imi = + RichInputMethodManager.getInstance().getInputMethodInfoOfThisIme(); + final Intent intent = new Intent(); + intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS); + intent.addCategory(Intent.CATEGORY_DEFAULT); + intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, imi.getId()); + startActivity(intent); + } + + private int determineSetupStepNumberFromLauncher() { + final int stepNumber = determineSetupStepNumber(); + if (stepNumber == STEP_1) { + return STEP_WELCOME; + } + if (stepNumber == STEP_3) { + return STEP_LAUNCHING_IME_SETTINGS; + } + return stepNumber; + } + + private int determineSetupStepNumber() { + mHandler.cancelPollingImeSettings(); + if (!SetupActivity.isThisImeEnabled(this)) { + return STEP_1; + } + if (!SetupActivity.isThisImeCurrent(this)) { + return STEP_2; + } + return STEP_3; + } + + @Override + protected void onSaveInstanceState(final Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(STATE_STEP, mStepNumber); + } + + @Override + protected void onRestoreInstanceState(final Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + mStepNumber = savedInstanceState.getInt(STATE_STEP); + } + + private static boolean isInSetupSteps(final int stepNumber) { + return stepNumber >= STEP_1 && stepNumber <= STEP_3; + } + + @Override + protected void onRestart() { + super.onRestart(); + // Probably the setup wizard has been invoked from "Recent" menu. The setup step number + // needs to be adjusted to system state, because the state (IME is enabled and/or current) + // may have been changed. + if (isInSetupSteps(mStepNumber)) { + mStepNumber = determineSetupStepNumber(); + } + } + + @Override + protected void onResume() { + super.onResume(); + if (mStepNumber == STEP_LAUNCHING_IME_SETTINGS) { + // Prevent white screen flashing while launching settings activity. + mSetupWizard.setVisibility(View.INVISIBLE); + invokeSettingsOfThisIme(); + mStepNumber = STEP_BACK_FROM_IME_SETTINGS; + return; + } + if (mStepNumber == STEP_BACK_FROM_IME_SETTINGS) { + finish(); + return; + } + updateSetupStepView(); + } + + @Override + public void onBackPressed() { + if (mStepNumber == STEP_1) { + mStepNumber = STEP_WELCOME; + updateSetupStepView(); + return; + } + super.onBackPressed(); + } + + void hideWelcomeVideoAndShowWelcomeImage() { + mWelcomeVideoView.setVisibility(View.GONE); + mWelcomeImageView.setImageResource(R.raw.setup_welcome_image); + mWelcomeImageView.setVisibility(View.VISIBLE); + } + + private void showAndStartWelcomeVideo() { + mWelcomeVideoView.setVisibility(View.VISIBLE); + mWelcomeVideoView.setVideoURI(mWelcomeVideoUri); + mWelcomeVideoView.start(); + } + + private void hideAndStopWelcomeVideo() { + mWelcomeVideoView.stopPlayback(); + mWelcomeVideoView.setVisibility(View.GONE); + } + + @Override + protected void onPause() { + hideAndStopWelcomeVideo(); + super.onPause(); + } + + @Override + public void onWindowFocusChanged(final boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus && mNeedsToAdjustStepNumberToSystemState) { + mNeedsToAdjustStepNumberToSystemState = false; + mStepNumber = determineSetupStepNumber(); + updateSetupStepView(); + } + } + + private void updateSetupStepView() { + mSetupWizard.setVisibility(View.VISIBLE); + final boolean welcomeScreen = (mStepNumber == STEP_WELCOME); + mWelcomeScreen.setVisibility(welcomeScreen ? View.VISIBLE : View.GONE); + mSetupScreen.setVisibility(welcomeScreen ? View.GONE : View.VISIBLE); + if (welcomeScreen) { + if (ENABLE_WELCOME_VIDEO) { + showAndStartWelcomeVideo(); + } else { + hideWelcomeVideoAndShowWelcomeImage(); + } + return; + } + hideAndStopWelcomeVideo(); + final boolean isStepActionAlreadyDone = mStepNumber < determineSetupStepNumber(); + mSetupStepGroup.enableStep(mStepNumber, isStepActionAlreadyDone); + mActionNext.setVisibility(isStepActionAlreadyDone ? View.VISIBLE : View.GONE); + mActionFinish.setVisibility((mStepNumber == STEP_3) ? View.VISIBLE : View.GONE); + } + + static final class SetupStep implements View.OnClickListener { + public final int mStepNo; + private final View mStepView; + private final TextView mBulletView; + private final int mActivatedColor; + private final int mDeactivatedColor; + private final String mInstruction; + private final String mFinishedInstruction; + private final TextView mActionLabel; + private Runnable mAction; + + public SetupStep(final int stepNo, final String applicationName, final TextView bulletView, + final View stepView, final int title, final int instruction, + final int finishedInstruction, final int actionIcon, final int actionLabel) { + mStepNo = stepNo; + mStepView = stepView; + mBulletView = bulletView; + final Resources res = stepView.getResources(); + mActivatedColor = res.getColor(R.color.setup_text_action); + mDeactivatedColor = res.getColor(R.color.setup_text_dark); + + final TextView titleView = (TextView)mStepView.findViewById(R.id.setup_step_title); + titleView.setText(res.getString(title, applicationName)); + mInstruction = (instruction == 0) ? null + : res.getString(instruction, applicationName); + mFinishedInstruction = (finishedInstruction == 0) ? null + : res.getString(finishedInstruction, applicationName); + + mActionLabel = (TextView)mStepView.findViewById(R.id.setup_step_action_label); + mActionLabel.setText(res.getString(actionLabel)); + if (actionIcon == 0) { + final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); + ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); + } else { + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( + mActionLabel, res.getDrawable(actionIcon), null, null, null); + } + } + + public void setEnabled(final boolean enabled, final boolean isStepActionAlreadyDone) { + mStepView.setVisibility(enabled ? View.VISIBLE : View.GONE); + mBulletView.setTextColor(enabled ? mActivatedColor : mDeactivatedColor); + final TextView instructionView = (TextView)mStepView.findViewById( + R.id.setup_step_instruction); + instructionView.setText(isStepActionAlreadyDone ? mFinishedInstruction : mInstruction); + mActionLabel.setVisibility(isStepActionAlreadyDone ? View.GONE : View.VISIBLE); + } + + public void setAction(final Runnable action) { + mActionLabel.setOnClickListener(this); + mAction = action; + } + + @Override + public void onClick(final View v) { + if (v == mActionLabel && mAction != null) { + mAction.run(); + return; + } + } + } + + static final class SetupStepGroup { + private final SetupStepIndicatorView mIndicatorView; + private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList(); + + public SetupStepGroup(final SetupStepIndicatorView indicatorView) { + mIndicatorView = indicatorView; + } + + public void addStep(final SetupStep step) { + mGroup.add(step); + } + + public void enableStep(final int enableStepNo, final boolean isStepActionAlreadyDone) { + for (final SetupStep step : mGroup) { + step.setEnabled(step.mStepNo == enableStepNo, isStepActionAlreadyDone); + } + mIndicatorView.setIndicatorPosition(enableStepNo - STEP_1, mGroup.size()); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index aa60496ae..13fcaf48a 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -23,7 +23,7 @@ import android.service.textservice.SpellCheckerService; import android.util.Log; import android.view.textservice.SuggestionsInfo; -import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.ContactsBinaryDictionary; @@ -126,6 +126,19 @@ public final class AndroidSpellCheckerService extends SpellCheckerService return script; } + private static String getKeyboardLayoutNameForScript(final int script) { + switch (script) { + case AndroidSpellCheckerService.SCRIPT_LATIN: + return "qwerty"; + case AndroidSpellCheckerService.SCRIPT_CYRILLIC: + return "east_slavic"; + case AndroidSpellCheckerService.SCRIPT_GREEK: + return "greek"; + default: + throw new RuntimeException("Wrong script supplied: " + script); + } + } + @Override public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { if (!PREF_USE_CONTACTS_KEY.equals(key)) return; @@ -385,9 +398,13 @@ public final class AndroidSpellCheckerService extends SpellCheckerService return pool; } - public DictAndProximity createDictAndProximity(final Locale locale) { + public DictAndKeyboard createDictAndKeyboard(final Locale locale) { final int script = getScriptFromLocale(locale); - final ProximityInfo proximityInfo = new SpellCheckerProximityInfo(script); + final String keyboardLayoutName = getKeyboardLayoutNameForScript(script); + final KeyboardLayoutSet keyboardLayoutSet = + KeyboardLayoutSet.createKeyboardSetForSpellChecker(this, locale.toString(), + keyboardLayoutName); + final DictionaryCollection dictionaryCollection = DictionaryFactory.createMainDictionaryFromManager(this, locale, true /* useFullEditDistance */); @@ -412,6 +429,6 @@ public final class AndroidSpellCheckerService extends SpellCheckerService mDictionaryCollectionsList.add( new WeakReference<DictionaryCollection>(dictionaryCollection)); } - return new DictAndProximity(dictionaryCollection, proximityInfo); + return new DictAndKeyboard(dictionaryCollection, keyboardLayoutSet); } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index c03b77d98..16e9fb77e 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -257,7 +257,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { } if (shouldFilterOut(inText, mScript)) { - DictAndProximity dictInfo = null; + DictAndKeyboard dictInfo = null; try { dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (!DictionaryPool.isAValidDictionary(dictInfo)) { @@ -283,33 +283,26 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { //suggestionsLimit); final SuggestionsGatherer suggestionsGatherer = mService.newSuggestionsGatherer( text, suggestionsLimit); - final WordComposer composer = new WordComposer(); - final int length = text.length(); - for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { - final int codePoint = text.codePointAt(i); - // The getXYForCodePointAndScript method returns (Y << 16) + X - final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript( - codePoint, mScript); - if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) { - composer.add(codePoint, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } else { - composer.add(codePoint, xy & 0xFFFF, xy >> 16); - } - } final int capitalizeType = StringUtils.getCapitalizationType(text); boolean isInDict = true; - DictAndProximity dictInfo = null; + DictAndKeyboard dictInfo = null; try { dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (!DictionaryPool.isAValidDictionary(dictInfo)) { return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); } + final WordComposer composer = new WordComposer(); + final int length = text.length(); + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { + final int codePoint = text.codePointAt(i); + composer.addKeyInfo(codePoint, dictInfo.getKeyboard(codePoint)); + } // TODO: make a spell checker option to block offensive words or not final ArrayList<SuggestedWordInfo> suggestions = dictInfo.mDictionary.getSuggestions(composer, prevWord, - dictInfo.mProximityInfo, true /* blockOffensiveWords */); + dictInfo.getProximityInfo(), + true /* blockOffensiveWords */); for (final SuggestedWordInfo suggestion : suggestions) { final String suggestionStr = suggestion.mWord; suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0, diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java new file mode 100644 index 000000000..b77f3e2c5 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.spellcheck; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.ProximityInfo; + +/** + * A container for a Dictionary and a Keyboard. + */ +public final class DictAndKeyboard { + public final Dictionary mDictionary; + private final Keyboard mKeyboard; + private final Keyboard mManualShiftedKeyboard; + + public DictAndKeyboard( + final Dictionary dictionary, final KeyboardLayoutSet keyboardLayoutSet) { + mDictionary = dictionary; + if (keyboardLayoutSet == null) { + mKeyboard = null; + mManualShiftedKeyboard = null; + return; + } + mKeyboard = keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); + mManualShiftedKeyboard = + keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED); + } + + public Keyboard getKeyboard(final int codePoint) { + if (mKeyboard == null) { + return null; + } + return mKeyboard.getKey(codePoint) != null ? mKeyboard : mManualShiftedKeyboard; + } + + public ProximityInfo getProximityInfo() { + return mKeyboard == null ? null : mKeyboard.getProximityInfo(); + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java deleted file mode 100644 index 017a4f555..000000000 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.spellcheck; - -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.keyboard.ProximityInfo; - -/** - * A simple container for both a Dictionary and a ProximityInfo. - */ -public final class DictAndProximity { - public final Dictionary mDictionary; - public final ProximityInfo mProximityInfo; - public DictAndProximity(final Dictionary dictionary, final ProximityInfo proximityInfo) { - mDictionary = dictionary; - mProximityInfo = proximityInfo; - } -} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index 27964b3c6..a20e09ee8 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -36,7 +36,7 @@ import java.util.concurrent.TimeUnit; * the client code, but may help with sloppy clients. */ @SuppressWarnings("serial") -public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { +public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> { private final static String TAG = DictionaryPool.class.getSimpleName(); // How many seconds we wait for a dictionary to become available. Past this delay, we give up in // fear some bug caused a deadlock, and reset the whole pool. @@ -47,7 +47,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> private int mSize; private volatile boolean mClosed; final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList(); - private final static DictAndProximity dummyDict = new DictAndProximity( + private final static DictAndKeyboard dummyDict = new DictAndKeyboard( new Dictionary(Dictionary.TYPE_MAIN) { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, @@ -64,7 +64,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> } }, null); - static public boolean isAValidDictionary(final DictAndProximity dictInfo) { + static public boolean isAValidDictionary(final DictAndKeyboard dictInfo) { return null != dictInfo && dummyDict != dictInfo; } @@ -79,32 +79,32 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> } @Override - public DictAndProximity poll(final long timeout, final TimeUnit unit) + public DictAndKeyboard poll(final long timeout, final TimeUnit unit) throws InterruptedException { - final DictAndProximity dict = poll(); + final DictAndKeyboard dict = poll(); if (null != dict) return dict; synchronized(this) { if (mSize >= mMaxSize) { // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT // expires to avoid a deadlock. - final DictAndProximity result = super.poll(timeout, unit); + final DictAndKeyboard result = super.poll(timeout, unit); if (null == result) { Log.e(TAG, "Deadlock detected ! Resetting dictionary pool"); clear(); mSize = 1; - return mService.createDictAndProximity(mLocale); + return mService.createDictAndKeyboard(mLocale); } else { return result; } } else { ++mSize; - return mService.createDictAndProximity(mLocale); + return mService.createDictAndKeyboard(mLocale); } } } // Convenience method - public DictAndProximity pollWithDefaultTimeout() { + public DictAndKeyboard pollWithDefaultTimeout() { try { return poll(TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { @@ -115,7 +115,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> public void close() { synchronized(this) { mClosed = true; - for (DictAndProximity dict : this) { + for (DictAndKeyboard dict : this) { dict.mDictionary.close(); } clear(); @@ -123,7 +123,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> } @Override - public boolean offer(final DictAndProximity dict) { + public boolean offer(final DictAndKeyboard dict) { if (mClosed) { dict.mDictionary.close(); return super.offer(dummyDict); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java deleted file mode 100644 index 0c480eaba..000000000 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.spellcheck; - -import android.util.SparseIntArray; - -import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.Constants; - -public final class SpellCheckerProximityInfo extends ProximityInfo { - public SpellCheckerProximityInfo(final int script) { - super(getProximityForScript(script), PROXIMITY_GRID_WIDTH, PROXIMITY_GRID_HEIGHT); - } - - private static final int NUL = Constants.NOT_A_CODE; - - // This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside - // native code - this value is passed at creation of the binary object and reused - // as the size of the passed array afterwards so they can't be different. - private static final int ROW_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; - - // The number of keys in a row of the grid used by the spell checker. - private static final int PROXIMITY_GRID_WIDTH = 11; - // The number of rows in the grid used by the spell checker. - private static final int PROXIMITY_GRID_HEIGHT = 3; - - private static final int NOT_AN_INDEX = -1; - public static final int NOT_A_COORDINATE_PAIR = -1; - - // Helper methods - static void buildProximityIndices(final int[] proximity, final int rowSize, - final SparseIntArray indices) { - for (int i = 0; i < proximity.length; i += rowSize) { - if (NUL != proximity[i]) indices.put(proximity[i], i / rowSize); - } - } - - private static final class Latin { - // The proximity here is the union of - // - the proximity for a QWERTY keyboard. - // - the proximity for an AZERTY keyboard. - // - the proximity for a QWERTZ keyboard. - // ...plus, add all characters in the ('a', 'e', 'i', 'o', 'u') set to each other. - // - // The reasoning behind this construction is, almost any alphabetic text we may want - // to spell check has been entered with one of the keyboards above. Also, specifically - // to English, many spelling errors consist of the last vowel of the word being wrong - // because in English vowels tend to merge with each other in pronunciation. - /* - The Qwerty layout this represents looks like the following: - q w e r t y u i o p - a s d f g h j k l - z x c v b n m - */ - static final int[] PROXIMITY = { - // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter, - // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's. - // The number of rows must be exactly PROXIMITY_GRID_HEIGHT. - 'q', 'w', 's', 'a', 'z', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'w', 'q', 'a', 's', 'd', 'e', 'x', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'e', 'w', 's', 'd', 'f', 'r', 'a', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, - 'r', 'e', 'd', 'f', 'g', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 't', 'r', 'f', 'g', 'h', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'y', 't', 'g', 'h', 'j', 'u', 'a', 's', 'd', 'x', NUL, NUL, NUL, NUL, NUL, NUL, - 'u', 'y', 'h', 'j', 'k', 'i', 'a', 'e', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'i', 'u', 'j', 'k', 'l', 'o', 'a', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'o', 'i', 'k', 'l', 'p', 'a', 'e', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'p', 'o', 'l', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - // Proximity for row 2. See comment above about size. - 'a', 'z', 'x', 's', 'w', 'q', 'e', 'i', 'o', 'u', NUL, NUL, NUL, NUL, NUL, NUL, - 's', 'q', 'a', 'z', 'x', 'c', 'd', 'e', 'w', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'd', 'w', 's', 'x', 'c', 'v', 'f', 'r', 'e', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'f', 'e', 'd', 'c', 'v', 'b', 'g', 't', 'r', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'g', 'r', 'f', 'v', 'b', 'n', 'h', 'y', 't', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'h', 't', 'g', 'b', 'n', 'm', 'j', 'u', 'y', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'j', 'y', 'h', 'n', 'm', 'k', 'i', 'u', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'k', 'u', 'j', 'm', 'l', 'o', 'i', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'l', 'i', 'k', 'p', 'o', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - // Proximity for row 3. See comment above about size. - 'z', 'a', 's', 'd', 'x', 't', 'g', 'h', 'j', 'u', 'q', 'e', NUL, NUL, NUL, NUL, - 'x', 'z', 'a', 's', 'd', 'c', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'c', 'x', 's', 'd', 'f', 'v', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'v', 'c', 'd', 'f', 'g', 'b', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'b', 'v', 'f', 'g', 'h', 'n', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'n', 'b', 'g', 'h', 'j', 'm', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'm', 'n', 'h', 'j', 'k', 'l', 'o', 'p', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - }; - - // This is a mapping array from the code point to the index in the PROXIMITY array. - // When we check the spelling of a word, we need to pass (x,y) coordinates to the native - // code for each letter of the word. These are most easily computed from the index in the - // PROXIMITY array. Since we'll need to do that very often, the index lookup from the code - // point needs to be as fast as possible, and a map is probably the best way to do this. - // To avoid unnecessary boxing conversion to Integer, here we use SparseIntArray. - static final SparseIntArray INDICES = new SparseIntArray(PROXIMITY.length / ROW_SIZE); - - static { - buildProximityIndices(PROXIMITY, ROW_SIZE, INDICES); - } - } - - private static final class Cyrillic { - // TODO: The following table is solely based on the keyboard layout. Consult with Russian - // speakers on commonly misspelled words/letters. - /* - The Russian layout this represents looks like the following: - й ц у к е н г ш щ з х - ф ы в а п р о л д ж э - я ч с м и т ь б ю - - This gives us the following table: - 'й', 'ц', 'ф', 'ы', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ц', 'й', 'ф', 'ы', 'в', 'у', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'у', 'ц', 'ы', 'в', 'а', 'к', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'к', 'у', 'в', 'а', 'п', 'е', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'е', 'к', 'а', 'п', 'р', 'н', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'н', 'е', 'п', 'р', 'о', 'г', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'г', 'н', 'р', 'о', 'л', 'ш', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ш', 'г', 'о', 'л', 'д', 'щ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'щ', 'ш', 'л', 'д', 'ж', 'з', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'з', 'щ', 'д', 'ж', 'э', 'х', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'х', 'з', 'ж', 'э', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - 'ф', 'й', 'ц', 'ы', 'я', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ы', 'й', 'ц', 'у', 'ф', 'в', 'я', 'ч', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'в', 'ц', 'у', 'к', 'ы', 'а', 'я', 'ч', 'с', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'а', 'у', 'к', 'е', 'в', 'п', 'ч', 'с', 'м', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'п', 'к', 'е', 'н', 'а', 'р', 'с', 'м', 'и', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'р', 'е', 'н', 'г', 'п', 'о', 'м', 'и', 'т', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'о', 'н', 'г', 'ш', 'р', 'л', 'и', 'т', 'ь', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'л', 'г', 'ш', 'щ', 'о', 'д', 'т', 'ь', 'б', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'д', 'ш', 'щ', 'з', 'л', 'ж', 'ь', 'б', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ж', 'щ', 'з', 'х', 'д', 'э', 'б', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'э', 'з', 'х', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - 'я', 'ф', 'ы', 'в', 'ч', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ч', 'ы', 'в', 'а', 'я', 'с', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'с', 'в', 'а', 'п', 'ч', 'м', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'м', 'а', 'п', 'р', 'с', 'и', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'и', 'п', 'р', 'о', 'м', 'т', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'т', 'р', 'о', 'л', 'и', 'ь', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ь', 'о', 'л', 'д', 'т', 'б', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'б', 'л', 'д', 'ж', 'ь', 'ю', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ю', 'д', 'ж', 'э', 'б', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - Using the following characters: - */ - private static final int CY_SHORT_I = '\u0439'; // й - private static final int CY_TSE = '\u0446'; // ц - private static final int CY_U = '\u0443'; // у - private static final int CY_KA = '\u043A'; // к - private static final int CY_IE = '\u0435'; // е - private static final int CY_EN = '\u043D'; // н - private static final int CY_GHE = '\u0433'; // г - private static final int CY_SHA = '\u0448'; // ш - private static final int CY_SHCHA = '\u0449'; // щ - private static final int CY_ZE = '\u0437'; // з - private static final int CY_HA = '\u0445'; // х - private static final int CY_EF = '\u0444'; // ф - private static final int CY_YERU = '\u044B'; // ы - private static final int CY_VE = '\u0432'; // в - private static final int CY_A = '\u0430'; // а - private static final int CY_PE = '\u043F'; // п - private static final int CY_ER = '\u0440'; // р - private static final int CY_O = '\u043E'; // о - private static final int CY_EL = '\u043B'; // л - private static final int CY_DE = '\u0434'; // д - private static final int CY_ZHE = '\u0436'; // ж - private static final int CY_E = '\u044D'; // э - private static final int CY_YA = '\u044F'; // я - private static final int CY_CHE = '\u0447'; // ч - private static final int CY_ES = '\u0441'; // с - private static final int CY_EM = '\u043C'; // м - private static final int CY_I = '\u0438'; // и - private static final int CY_TE = '\u0442'; // т - private static final int CY_SOFT_SIGN = '\u044C'; // ь - private static final int CY_BE = '\u0431'; // б - private static final int CY_YU = '\u044E'; // ю - static final int[] PROXIMITY = { - // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter, - // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's. - // The number of rows must be exactly PROXIMITY_GRID_HEIGHT. - CY_SHORT_I, CY_TSE, CY_EF, CY_YERU, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_TSE, CY_SHORT_I, CY_EF, CY_YERU, CY_VE, CY_U, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_U, CY_TSE, CY_YERU, CY_VE, CY_A, CY_KA, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_KA, CY_U, CY_VE, CY_A, CY_PE, CY_IE, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_IE, CY_KA, CY_A, CY_PE, CY_ER, CY_EN, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_EN, CY_IE, CY_PE, CY_ER, CY_O, CY_GHE, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_GHE, CY_EN, CY_ER, CY_O, CY_EL, CY_SHA, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_SHA, CY_GHE, CY_O, CY_EL, CY_DE, CY_SHCHA, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_SHCHA, CY_SHA, CY_EL, CY_DE, CY_ZHE, CY_ZE, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_ZE, CY_SHCHA, CY_DE, CY_ZHE, CY_E, CY_HA, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_HA, CY_ZE, CY_ZHE, CY_E, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - // Proximity for row 2. See comment above about size. - CY_EF, CY_SHORT_I, CY_TSE, CY_YERU, CY_YA, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_YERU, CY_SHORT_I, CY_TSE, CY_U, CY_EF, CY_VE, CY_YA, CY_CHE, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_VE, CY_TSE, CY_U, CY_KA, CY_YERU, CY_A, CY_YA, CY_CHE, - CY_ES, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_A, CY_U, CY_KA, CY_IE, CY_VE, CY_PE, CY_CHE, CY_ES, - CY_EM, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_PE, CY_KA, CY_IE, CY_EN, CY_A, CY_ER, CY_ES, CY_EM, - CY_I, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_ER, CY_IE, CY_EN, CY_GHE, CY_PE, CY_O, CY_EM, CY_I, - CY_TE, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_O, CY_EN, CY_GHE, CY_SHA, CY_ER, CY_EL, CY_I, CY_TE, - CY_SOFT_SIGN, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_EL, CY_GHE, CY_SHA, CY_SHCHA, CY_O, CY_DE, CY_TE, CY_SOFT_SIGN, - CY_BE, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_DE, CY_SHA, CY_SHCHA, CY_ZE, CY_EL, CY_ZHE, CY_SOFT_SIGN, CY_BE, - CY_YU, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_ZHE, CY_SHCHA, CY_ZE, CY_HA, CY_DE, CY_E, CY_BE, CY_YU, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_E, CY_ZE, CY_HA, CY_YU, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - // Proximity for row 3. See comment above about size. - CY_YA, CY_EF, CY_YERU, CY_VE, CY_CHE, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_CHE, CY_YERU, CY_VE, CY_A, CY_YA, CY_ES, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_ES, CY_VE, CY_A, CY_PE, CY_CHE, CY_EM, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_EM, CY_A, CY_PE, CY_ER, CY_ES, CY_I, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_I, CY_PE, CY_ER, CY_O, CY_EM, CY_TE, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_TE, CY_ER, CY_O, CY_EL, CY_I, CY_SOFT_SIGN, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_SOFT_SIGN, CY_O, CY_EL, CY_DE, CY_TE, CY_BE, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_BE, CY_EL, CY_DE, CY_ZHE, CY_SOFT_SIGN, CY_YU, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - CY_YU, CY_DE, CY_ZHE, CY_E, CY_BE, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - }; - - static final SparseIntArray INDICES = new SparseIntArray(PROXIMITY.length / ROW_SIZE); - - static { - buildProximityIndices(PROXIMITY, ROW_SIZE, INDICES); - } - } - - private static final class Greek { - // TODO: The following table is solely based on the keyboard layout. Consult with Greek - // speakers on commonly misspelled words/letters. - /* - The Greek layout this represents looks like the following: - ; ς ε ρ τ υ θ ι ο π - α σ δ φ γ η ξ κ λ - ζ χ ψ ω β ν μ - - This gives us the following table: - 'ς', 'ε', 'α', 'σ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ε', 'ς', 'ρ', 'σ', 'δ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ρ', 'ε', 'τ', 'δ', 'φ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'τ', 'ρ', 'υ', 'φ', 'γ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'υ', 'τ', 'θ', 'γ', 'η', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'θ', 'υ', 'ι', 'η', 'ξ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ι', 'θ', 'ο', 'ξ', 'κ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ο', 'ι', 'π', 'κ', 'λ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'π', 'ο', 'λ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - 'α', 'ς', 'σ', 'ζ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'σ', 'ς', 'ε', 'α', 'δ', 'ζ', 'χ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'δ', 'ε', 'ρ', 'σ', 'φ', 'ζ', 'χ', 'ψ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'φ', 'ρ', 'τ', 'δ', 'γ', 'χ', 'ψ', 'ω', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'γ', 'τ', 'υ', 'φ', 'η', 'ψ', 'ω', 'β', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'η', 'υ', 'θ', 'γ', 'ξ', 'ω', 'β', 'ν', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ξ', 'θ', 'ι', 'η', 'κ', 'β', 'ν', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'κ', 'ι', 'ο', 'ξ', 'λ', 'ν', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'λ', 'ο', 'π', 'κ', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - 'ζ', 'α', 'σ', 'δ', 'χ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'χ', 'σ', 'δ', 'φ', 'ζ', 'ψ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ψ', 'δ', 'φ', 'γ', 'χ', 'ω', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ω', 'φ', 'γ', 'η', 'ψ', 'β', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'β', 'γ', 'η', 'ξ', 'ω', 'ν', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'ν', 'η', 'ξ', 'κ', 'β', 'μ', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - 'μ', 'ξ', 'κ', 'λ', 'ν', NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - Using the following characters: - */ - private static final int GR_FINAL_SIGMA = '\u03C2'; // ς - private static final int GR_EPSILON = '\u03B5'; // ε - private static final int GR_RHO = '\u03C1'; // ρ - private static final int GR_TAU = '\u03C4'; // τ - private static final int GR_UPSILON = '\u03C5'; // υ - private static final int GR_THETA = '\u03B8'; // θ - private static final int GR_IOTA = '\u03B9'; // ι - private static final int GR_OMICRON = '\u03BF'; // ο - private static final int GR_PI = '\u03C0'; // π - private static final int GR_ALPHA = '\u03B1'; // α - private static final int GR_SIGMA = '\u03C3'; // σ - private static final int GR_DELTA = '\u03B4'; // δ - private static final int GR_PHI = '\u03C6'; // φ - private static final int GR_GAMMA = '\u03B3'; // γ - private static final int GR_ETA = '\u03B7'; // η - private static final int GR_XI = '\u03BE'; // ξ - private static final int GR_KAPPA = '\u03BA'; // κ - private static final int GR_LAMDA = '\u03BB'; // λ - private static final int GR_ZETA = '\u03B6'; // ζ - private static final int GR_CHI = '\u03C7'; // χ - private static final int GR_PSI = '\u03C8'; // ψ - private static final int GR_OMEGA = '\u03C9'; // ω - private static final int GR_BETA = '\u03B2'; // β - private static final int GR_NU = '\u03BD'; // ν - private static final int GR_MU = '\u03BC'; // μ - static final int[] PROXIMITY = { - // Proximity for row 1. This must have exactly ROW_SIZE entries for each letter, - // and exactly PROXIMITY_GRID_WIDTH letters for a row. Pad with NUL's. - // The number of rows must be exactly PROXIMITY_GRID_HEIGHT. - GR_FINAL_SIGMA, GR_EPSILON, GR_ALPHA, GR_SIGMA, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_EPSILON, GR_FINAL_SIGMA, GR_RHO, GR_SIGMA, GR_DELTA, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_RHO, GR_EPSILON, GR_TAU, GR_DELTA, GR_PHI, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_TAU, GR_RHO, GR_UPSILON, GR_PHI, GR_GAMMA, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_UPSILON, GR_TAU, GR_THETA, GR_GAMMA, GR_ETA, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_THETA, GR_UPSILON, GR_IOTA, GR_ETA, GR_XI, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_IOTA, GR_THETA, GR_OMICRON, GR_XI, GR_KAPPA, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_OMICRON, GR_IOTA, GR_PI, GR_KAPPA, GR_LAMDA, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_PI, GR_OMICRON, GR_LAMDA, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - GR_ALPHA, GR_FINAL_SIGMA, GR_SIGMA, GR_ZETA, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_SIGMA, GR_FINAL_SIGMA, GR_EPSILON, GR_ALPHA, GR_DELTA, GR_ZETA, GR_CHI, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_DELTA, GR_EPSILON, GR_RHO, GR_SIGMA, GR_PHI, GR_ZETA, GR_CHI, GR_PSI, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_PHI, GR_RHO, GR_TAU, GR_DELTA, GR_GAMMA, GR_CHI, GR_PSI, GR_OMEGA, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_GAMMA, GR_TAU, GR_UPSILON, GR_PHI, GR_ETA, GR_PSI, GR_OMEGA, GR_BETA, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_ETA, GR_UPSILON, GR_THETA, GR_GAMMA, GR_XI, GR_OMEGA, GR_BETA, GR_NU, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_XI, GR_THETA, GR_IOTA, GR_ETA, GR_KAPPA, GR_BETA, GR_NU, GR_MU, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_KAPPA, GR_IOTA, GR_OMICRON, GR_XI, GR_LAMDA, GR_NU, GR_MU, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_LAMDA, GR_OMICRON, GR_PI, GR_KAPPA, GR_MU, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - - GR_ZETA, GR_ALPHA, GR_SIGMA, GR_DELTA, GR_CHI, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_CHI, GR_SIGMA, GR_DELTA, GR_PHI, GR_ZETA, GR_PSI, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_PSI, GR_DELTA, GR_PHI, GR_GAMMA, GR_CHI, GR_OMEGA, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_OMEGA, GR_PHI, GR_GAMMA, GR_ETA, GR_PSI, GR_BETA, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_BETA, GR_GAMMA, GR_ETA, GR_XI, GR_OMEGA, GR_NU, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_NU, GR_ETA, GR_XI, GR_KAPPA, GR_BETA, GR_MU, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - GR_MU, GR_XI, GR_KAPPA, GR_LAMDA, GR_NU, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, - }; - - static final SparseIntArray INDICES = new SparseIntArray(PROXIMITY.length / ROW_SIZE); - - static { - buildProximityIndices(PROXIMITY, ROW_SIZE, INDICES); - } - } - - private static int[] getProximityForScript(final int script) { - switch (script) { - case AndroidSpellCheckerService.SCRIPT_LATIN: - return Latin.PROXIMITY; - case AndroidSpellCheckerService.SCRIPT_CYRILLIC: - return Cyrillic.PROXIMITY; - case AndroidSpellCheckerService.SCRIPT_GREEK: - return Greek.PROXIMITY; - default: - throw new RuntimeException("Wrong script supplied: " + script); - } - } - - private static int getIndexOfCodeForScript(final int codePoint, final int script) { - switch (script) { - case AndroidSpellCheckerService.SCRIPT_LATIN: - return Latin.INDICES.get(codePoint, NOT_AN_INDEX); - case AndroidSpellCheckerService.SCRIPT_CYRILLIC: - return Cyrillic.INDICES.get(codePoint, NOT_AN_INDEX); - case AndroidSpellCheckerService.SCRIPT_GREEK: - return Greek.INDICES.get(codePoint, NOT_AN_INDEX); - default: - throw new RuntimeException("Wrong script supplied: " + script); - } - } - - // Returns (Y << 16) + X to avoid creating a temporary object. This is okay because - // X and Y are limited to PROXIMITY_GRID_WIDTH resp. PROXIMITY_GRID_HEIGHT which is very - // inferior to 1 << 16 - // As an exception, this returns NOT_A_COORDINATE_PAIR if the key is not on the grid - public static int getXYForCodePointAndScript(final int codePoint, final int script) { - final int index = getIndexOfCodeForScript(codePoint, script); - if (NOT_AN_INDEX == index) return NOT_A_COORDINATE_PAIR; - final int y = index / PROXIMITY_GRID_WIDTH; - final int x = index % PROXIMITY_GRID_WIDTH; - if (y > PROXIMITY_GRID_HEIGHT) { - // Safety check, should be entirely useless - throw new RuntimeException("Wrong y coordinate in spell checker proximity"); - } - return (y << 16) + x; - } -} diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java index 5f4c44636..58c8f266c 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java @@ -74,14 +74,14 @@ public class UserDictionaryAddWordFragment extends Fragment @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0, + R.string.user_dict_settings_add_menu_title).setIcon(R.drawable.ic_menu_add); + actionItemAdd.setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); final MenuItem actionItemDelete = menu.add(0, OPTIONS_MENU_DELETE, 0, R.string.user_dict_settings_delete).setIcon(android.R.drawable.ic_menu_delete); actionItemDelete.setShowAsAction( MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - final MenuItem actionItemAdd = menu.add(0, OPTIONS_MENU_ADD, 0, - R.string.user_dict_settings_delete).setIcon(R.drawable.ic_menu_add); - actionItemAdd.setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); } /** diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java index 36bc5ba49..50dda9663 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettings.java @@ -108,6 +108,7 @@ public class UserDictionarySettings extends ListFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getActivity().getActionBar().setTitle(R.string.edit_personal_dictionary); } @Override |