diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
17 files changed, 261 insertions, 259 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java b/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java index 7c27e6d51..3d0e29ed0 100644 --- a/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java +++ b/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java @@ -23,7 +23,7 @@ public final class CommonPreferences { private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs"; public static SharedPreferences getCommonPreferences(final Context context) { - return context.getSharedPreferences(COMMON_PREFERENCES_NAME, Context.MODE_WORLD_READABLE); + return context.getSharedPreferences(COMMON_PREFERENCES_NAME, 0); } public static void enable(final SharedPreferences pref, final String id) { diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java index 88b5032e3..384ee3e07 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java @@ -100,6 +100,7 @@ public class DictionaryDownloadProgressBar extends ProgressBar { @Override protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); mIsCurrentlyAttachedToWindow = false; updateReporterThreadRunningStatusAccordingToVisibility(); } diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java index ff0d53865..8dea908e8 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java @@ -71,8 +71,8 @@ import java.util.concurrent.ConcurrentHashMap; * Because of the above reasons, this class doesn't extend {@link KeyboardView}. */ public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener, - ViewPager.OnPageChangeListener, View.OnClickListener, - EmojiPageKeyboardView.OnKeyClickListener { + ViewPager.OnPageChangeListener, View.OnTouchListener, + EmojiPageKeyboardView.OnKeyEventListener { static final String TAG = EmojiPalettesView.class.getSimpleName(); private static final boolean DEBUG_PAGER = false; private final int mKeyBackgroundId; @@ -486,16 +486,16 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange final ImageView alphabetKey = (ImageView)findViewById(R.id.emoji_keyboard_alphabet); alphabetKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); alphabetKey.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL); - alphabetKey.setOnClickListener(this); + alphabetKey.setOnTouchListener(this); final ImageView spaceKey = (ImageView)findViewById(R.id.emoji_keyboard_space); spaceKey.setBackgroundResource(mKeyBackgroundId); spaceKey.setTag(Constants.CODE_SPACE); - spaceKey.setOnClickListener(this); + spaceKey.setOnTouchListener(this); mEmojiLayoutParams.setKeyProperties(spaceKey); final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2); alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL); - alphabetKey2.setOnClickListener(this); + alphabetKey2.setOnTouchListener(this); } @Override @@ -543,31 +543,51 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } } + // Called from {@link EmojiPageKeyboardView} through {@link View.OnTouchListener} interface to + // handle touch events from View-based elements such as the space bar. @Override - public void onClick(final View v) { - if (v.getTag() instanceof Integer) { - final int code = (Integer)v.getTag(); - registerCode(code); - return; + public boolean onTouch(final View v, final MotionEvent event) { + final Object tag = v.getTag(); + if (!(tag instanceof Integer)) { + return false; + } + final int code = (Integer) tag; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mKeyboardActionListener.onPressKey( + code, 0 /* repeatCount */, true /* isSinglePointer */); + break; + case MotionEvent.ACTION_UP: + mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE); + mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); + break; } + return false; } - private void registerCode(final int code) { + // Called from {@link EmojiPageKeyboardView} through + // {@link EmojiPageKeyboardView.OnKeyEventListener} interface to handle touch events from + // non-View-based elements like typical Emoji characters. + @Override + public void onPressKey(final Key key) { + final int code = key.getCode(); mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */); - mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE); - mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); } + // Called from {@link EmojiPageKeyboardView} through + // {@link EmojiPageKeyboardView.OnKeyEventListener} interface to handle touch events from + // non-View-based elements like typical Emoji characters. @Override - public void onKeyClick(final Key key) { + public void onReleaseKey(final Key key) { mEmojiPalettesAdapter.addRecentKey(key); mEmojiCategory.saveLastTypedCategoryPage(); final int code = key.getCode(); if (code == Constants.CODE_OUTPUT_TEXT) { mKeyboardActionListener.onTextInput(key.getOutputText()); - return; + } else { + mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE); } - registerCode(code); + mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); } public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { @@ -630,7 +650,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } private static class EmojiPalettesAdapter extends PagerAdapter { - private final EmojiPageKeyboardView.OnKeyClickListener mListener; + private final EmojiPageKeyboardView.OnKeyEventListener mListener; private final DynamicGridKeyboard mRecentsKeyboard; private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = CollectionUtils.newSparseArray(); @@ -638,7 +658,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange private int mActivePosition = 0; public EmojiPalettesAdapter(final EmojiCategory emojiCategory, - final EmojiPageKeyboardView.OnKeyClickListener listener) { + final EmojiPageKeyboardView.OnKeyEventListener listener) { mEmojiCategory = emojiCategory; mListener = listener; mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0); @@ -702,7 +722,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate( R.layout.emoji_keyboard_page, container, false /* attachToRoot */); keyboardView.setKeyboard(keyboard); - keyboardView.setOnKeyClickListener(mListener); + keyboardView.setOnKeyEventListener(mListener); container.addView(keyboardView); mActiveKeyboardViews.put(position, keyboardView); return keyboardView; diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index e1c841de7..810bd9150 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -424,8 +424,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack */ @Override public void setKeyboard(final Keyboard keyboard) { - // Remove any pending messages. - mKeyTimerHandler.cancelAllKeyTimers(); + // Remove any pending messages, except dismissing preview and key repeat. + mKeyTimerHandler.cancelLongPressTimers(); super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java index 2e80f7962..5c7c6e39d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java @@ -35,16 +35,19 @@ import com.android.inputmethod.latin.R; // TODO: Implement key popup preview. public final class EmojiPageKeyboardView extends KeyboardView implements GestureDetector.OnGestureListener { - public interface OnKeyClickListener { - public void onKeyClick(Key key); + public interface OnKeyEventListener { + public void onPressKey(Key key); + public void onReleaseKey(Key key); } - private static final OnKeyClickListener EMPTY_LISTENER = new OnKeyClickListener() { - @Override - public void onKeyClick(final Key key) {} + private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() { + @Override + public void onPressKey(final Key key) {} + @Override + public void onReleaseKey(final Key key) {} }; - private OnKeyClickListener mListener = EMPTY_LISTENER; + private OnKeyEventListener mListener = EMPTY_LISTENER; private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */); private final GestureDetector mGestureDetector; @@ -59,7 +62,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */); } - public void setOnKeyClickListener(final OnKeyClickListener listener) { + public void setOnKeyEventListener(final OnKeyEventListener listener) { mListener = listener; } @@ -115,9 +118,9 @@ public final class EmojiPageKeyboardView extends KeyboardView implements if (key == null) { return false; } - // TODO: May call {@link KeyboardActionListener#onPressKey(int,int,boolean)}. key.onPressed(); invalidateKey(key); + mListener.onPressKey(key); return false; } @@ -133,10 +136,9 @@ public final class EmojiPageKeyboardView extends KeyboardView implements if (key == null) { return false; } - // TODO: May call {@link KeyboardActionListener#onReleaseKey(int,boolean)}. key.onReleased(); invalidateKey(key); - mListener.onKeyClick(key); + mListener.onReleaseKey(key); return true; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java index 3298a3f24..ec7b9b024 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java @@ -126,7 +126,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple removeMessages(MSG_LONGPRESS_SHIFT_KEY); } - private void cancelLongPressTimers() { + public void cancelLongPressTimers() { removeMessages(MSG_LONGPRESS_KEY); removeMessages(MSG_LONGPRESS_SHIFT_KEY); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 181ad17ea..7e97802e1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -112,7 +112,7 @@ final public class BinaryDictionaryGetter { public DictPackSettings(final Context context) { mDictPreferences = null == context ? null : context.getSharedPreferences(COMMON_PREFERENCES_NAME, - Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS); + Context.MODE_MULTI_PROCESS); } public boolean isWordListActive(final String dictId) { if (null == mDictPreferences) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index aadb65192..c9602bcc4 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -880,8 +880,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Notify ResearchLogger if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput, - mInputLogic.mLastSelectionStart, - mInputLogic.mLastSelectionEnd, getCurrentInputConnection()); + // TODO[IL]: mInputLogic.mConnection should be private + mInputLogic.mConnection.getExpectedSelectionStart(), + mInputLogic.mConnection.getExpectedSelectionEnd(), getCurrentInputConnection()); } } @@ -894,22 +895,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (DEBUG) { Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart + ", ose=" + oldSelEnd - + ", lss=" + mInputLogic.mLastSelectionStart - + ", lse=" + mInputLogic.mLastSelectionEnd + ", nss=" + newSelStart + ", nse=" + newSelEnd + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onUpdateSelection(mInputLogic.mLastSelectionStart, - mInputLogic.mLastSelectionEnd, + ResearchLogger.latinIME_onUpdateSelection(oldSelStart, oldSelEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, composingSpanEnd, mInputLogic.mConnection); } - final boolean selectionChanged = mInputLogic.mLastSelectionStart != newSelStart - || mInputLogic.mLastSelectionEnd != newSelEnd; + final boolean selectionChanged = oldSelStart != newSelStart || oldSelEnd != newSelEnd; // if composingSpanStart and composingSpanEnd are -1, it means there is no composing // span in the view - we can use that to narrow down whether the cursor was moved @@ -972,9 +969,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mKeyboardSwitcher.updateShiftState(); } - // Make a note of the cursor position - mInputLogic.mLastSelectionStart = newSelStart; - mInputLogic.mLastSelectionEnd = newSelEnd; mSubtypeState.currentSubtypeUsed(); } @@ -1396,7 +1390,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // word. If we are composing a word we should have the second word before the cursor // memorized, otherwise we should have the first. final String rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion( - currentSettings, mInputLogic.mWordComposer.isComposingWord() ? 2 : 1); + currentSettings.mSpacingAndPunctuations, + mInputLogic.mWordComposer.isComposingWord() ? 2 : 1); if (!TextUtils.equals(previousWord, rereadPrevWord)) { throw new RuntimeException("Unexpected previous word: " + previousWord + " <> " + rereadPrevWord); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 7cf64a3bc..79d66744b 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -28,6 +28,7 @@ import android.view.inputmethod.InputConnection; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.SpannableStringUtils; @@ -35,7 +36,6 @@ import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.ResearchLogger; -import java.util.Locale; import java.util.regex.Pattern; /** @@ -174,13 +174,15 @@ public final class RichInputConnection { } final int lengthOfTextBeforeCursor = mCommittedTextBeforeComposingText.length(); if (lengthOfTextBeforeCursor > newSelStart - || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE + || (newSelStart != lengthOfTextBeforeCursor + && lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE && newSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { // newSelStart and newSelEnd may be lying -- when rotating the device (probably a - // framework bug). If we have less chars than we asked for, then we know how many chars - // we have, and if we got more than newSelStart says, then we know it was lying. In both - // cases the length is more reliable. Note that we only have to check newSelStart (not - // newSelEnd) since if newSelEnd is wrong, the newSelStart will be wrong as well. + // framework bug). If the values don't agree and we have less chars than we asked + // for, then we know how many chars we have. If we got more than newSelStart says, then + // we also know it was lying. In both cases the length is more reliable. Note that we + // only have to check newSelStart (not newSelEnd) since if newSelEnd is wrong, then + // newSelStart will be wrong as well. mExpectedSelStart = lengthOfTextBeforeCursor; mExpectedSelEnd = lengthOfTextBeforeCursor; } @@ -302,7 +304,7 @@ public final class RichInputConnection { // This never calls InputConnection#getCapsMode - in fact, it's a static method that // never blocks or initiates IPC. return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, - settingsValues, hasSpaceBefore); + settingsValues.mSpacingAndPunctuations, hasSpaceBefore); } public int getCodePointBeforeCursor() { @@ -538,7 +540,8 @@ public final class RichInputConnection { } @SuppressWarnings("unused") - public String getNthPreviousWord(final SettingsValues currentSettingsValues, final int n) { + public String getNthPreviousWord(final SpacingAndPunctuations spacingAndPunctuations, + final int n) { mIC = mParent.getCurrentInputConnection(); if (null == mIC) return null; final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); @@ -557,7 +560,7 @@ public final class RichInputConnection { } } } - return getNthPreviousWord(prev, currentSettingsValues, n); + return getNthPreviousWord(prev, spacingAndPunctuations, n); } private static boolean isSeparator(int code, String sep) { @@ -581,7 +584,7 @@ public final class RichInputConnection { // (n = 2) "abc |" -> null // (n = 2) "abc. def|" -> null public static String getNthPreviousWord(final CharSequence prev, - final SettingsValues currentSettingsValues, final int n) { + final SpacingAndPunctuations spacingAndPunctuations, final int n) { if (prev == null) return null; final String[] w = spaceRegex.split(prev); @@ -593,8 +596,8 @@ public final class RichInputConnection { // If ends in a separator, return null final char lastChar = nthPrevWord.charAt(length - 1); - if (currentSettingsValues.isWordSeparator(lastChar) - || currentSettingsValues.isWordConnector(lastChar)) return null; + if (spacingAndPunctuations.isWordSeparator(lastChar) + || spacingAndPunctuations.isWordConnector(lastChar)) return null; return nthPrevWord; } @@ -782,17 +785,17 @@ public final class RichInputConnection { */ public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart, final int oldSelEnd, final int newSelEnd) { - // This update is "belated" if we are expecting it. That is, mExpectedSelStart and + // This update is "belated" if we are expecting it. That is, mExpectedSelStart and // mExpectedSelEnd match the new values that the TextView is updating TO. if (mExpectedSelStart == newSelStart && mExpectedSelEnd == newSelEnd) return true; - // This update is not belated if mExpectedSelStart and mExpeectedSelend match the old - // values, and one of newSelStart or newSelEnd is updated to a different value. In this - // case, there is likely something other than the IME that has moved the selection endpoint + // This update is not belated if mExpectedSelStart and mExpectedSelEnd match the old + // values, and one of newSelStart or newSelEnd is updated to a different value. In this + // case, there is likely something other than the IME has moved the selection endpoint // to the new value. if (mExpectedSelStart == oldSelStart && mExpectedSelEnd == oldSelEnd && (oldSelStart != newSelStart || oldSelEnd != newSelEnd)) return false; // If nether of the above two cases holds, then the system may be having trouble keeping up - // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart + // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart // and mExpectedSelStart, and 3) newSelEnd is between oldSelEnd and mExpectedSelEnd, then // assume a belated update. return (newSelStart == newSelEnd) @@ -810,4 +813,54 @@ public final class RichInputConnection { public boolean textBeforeCursorLooksLikeURL() { return StringUtils.lastPartLooksLikeURL(mCommittedTextBeforeComposingText); } + + /** + * Try to get the text from the editor to expose lies the framework may have been + * telling us. Concretely, when the device rotates, the frameworks tells us about where the + * cursor used to be initially in the editor at the time it first received the focus; this + * may be completely different from the place it is upon rotation. Since we don't have any + * means to get the real value, try at least to ask the text view for some characters and + * detect the most damaging cases: when the cursor position is declared to be much smaller + * than it really is. + */ + public void tryFixLyingCursorPosition() { + final CharSequence textBeforeCursor = getTextBeforeCursor( + Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); + if (null == textBeforeCursor) { + mExpectedSelStart = mExpectedSelEnd = Constants.NOT_A_CURSOR_POSITION; + } else { + final int textLength = textBeforeCursor.length(); + if (textLength > mExpectedSelStart + || (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE + && mExpectedSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { + // It should not be possible to have only one of those variables be + // NOT_A_CURSOR_POSITION, so if they are equal, either the selection is zero-sized + // (simple cursor, no selection) or there is no cursor/we don't know its pos + final boolean wasEqual = mExpectedSelStart == mExpectedSelEnd; + mExpectedSelStart = textLength; + // We can't figure out the value of mLastSelectionEnd :( + // But at least if it's smaller than mLastSelectionStart something is wrong, + // and if they used to be equal we also don't want to make it look like there is a + // selection. + if (wasEqual || mExpectedSelStart > mExpectedSelEnd) { + mExpectedSelEnd = mExpectedSelStart; + } + } + } + } + + public int getExpectedSelectionStart() { + return mExpectedSelStart; + } + + public int getExpectedSelectionEnd() { + return mExpectedSelEnd; + } + + /** + * @return whether there is a selection currently active. + */ + public boolean hasSelection() { + return mExpectedSelEnd != mExpectedSelStart; + } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 968129a96..7222b73b3 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -46,6 +46,7 @@ import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -68,7 +69,8 @@ public final class InputLogic { // TODO : Remove this member when we can. private final LatinIME mLatinIME; - private InputLogicHandler mInputLogicHandler; + // Never null. + private InputLogicHandler mInputLogicHandler = InputLogicHandler.NULL_HANDLER; // TODO : make all these fields private as soon as possible. // Current space state of the input method. This can be any of the above constants. @@ -84,10 +86,6 @@ public final class InputLogic { public final RichInputConnection mConnection; public final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus(); - // Keep track of the last selection range to decide if we need to show word alternatives - public int mLastSelectionStart = Constants.NOT_A_CURSOR_POSITION; - public int mLastSelectionEnd = Constants.NOT_A_CURSOR_POSITION; - private int mDeleteCount; private long mLastKeyTime; public final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet(); @@ -104,7 +102,7 @@ public final class InputLogic { mWordComposer = new WordComposer(); mEventInterpreter = new EventInterpreter(latinIME); mConnection = new RichInputConnection(latinIME); - mInputLogicHandler = null; + mInputLogicHandler = InputLogicHandler.NULL_HANDLER; } /** @@ -127,11 +125,9 @@ public final class InputLogic { mRecapitalizeStatus.deactivate(); mCurrentlyPressedHardwareKeys.clear(); mSuggestedWords = SuggestedWords.EMPTY; - mLastSelectionStart = editorInfo.initialSelStart; - mLastSelectionEnd = editorInfo.initialSelEnd; // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying // so we try using some heuristics to find out about these and fix them. - tryFixLyingCursorPosition(); + mConnection.tryFixLyingCursorPosition(); mInputLogicHandler = new InputLogicHandler(mLatinIME, this); } @@ -144,7 +140,7 @@ public final class InputLogic { } resetComposingState(true /* alsoResetLastComposedWord */); mInputLogicHandler.destroy(); - mInputLogicHandler = null; + mInputLogicHandler = InputLogicHandler.NULL_HANDLER; } /** @@ -335,7 +331,8 @@ public final class InputLogic { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the batch input at the current cursor position. - resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd); + resetEntireInputState(settingsValues, mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); } else if (wordComposerSize <= 1) { // We auto-correct the previous (typed, not gestured) string iff it's one character // long. The reason for this is, even in the middle of gesture typing, you'll still @@ -369,7 +366,8 @@ public final class InputLogic { mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), // Prev word is 1st word before cursor - getNthPreviousWordForSuggestion(settingsValues, 1 /* nthPreviousWord */)); + getNthPreviousWordForSuggestion( + settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); } /* The sequence number member is only used in onUpdateBatchInput. It is increased each time @@ -463,7 +461,8 @@ public final class InputLogic { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. - resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd); + resetEntireInputState(settingsValues, mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); } else { commitTyped(settingsValues, LastComposedWord.NOT_A_SEPARATOR); } @@ -513,7 +512,8 @@ public final class InputLogic { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. - resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd); + resetEntireInputState(settingsValues, mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); isComposingWord = false; } // We want to find out whether to start composing a new word with this character. If so, @@ -555,7 +555,8 @@ public final class InputLogic { // yet, so the word we want is the 1st word before the cursor. mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), - getNthPreviousWordForSuggestion(settingsValues, 1 /* nthPreviousWord */)); + getNthPreviousWordForSuggestion( + settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); } mConnection.setComposingText(getTextWithUnderline( mWordComposer.getTypedWord()), 1); @@ -599,7 +600,8 @@ public final class InputLogic { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the separator at the current cursor position. - resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd); + resetEntireInputState(settingsValues, mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); } // isComposingWord() may have changed since we stored wasComposing if (mWordComposer.isComposingWord()) { @@ -691,7 +693,8 @@ public final class InputLogic { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can remove the character at the current cursor position. - resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd); + resetEntireInputState(settingsValues, mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); // When we exit this if-clause, mWordComposer.isComposingWord() will return false. } if (mWordComposer.isComposingWord()) { @@ -751,15 +754,12 @@ public final class InputLogic { // No cancelling of commit/double space/swap: we have a regular backspace. // We should backspace one char and restart suggestion if at the end of a word. - if (mLastSelectionStart != mLastSelectionEnd) { + if (mConnection.hasSelection()) { // If there is a selection, remove it. - final int numCharsDeleted = mLastSelectionEnd - mLastSelectionStart; - mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); - // Reset mLastSelectionEnd to mLastSelectionStart. This is what is supposed to - // happen, and if it's wrong, the next call to onUpdateSelection will correct it, - // but we want to set it right away to avoid it being used with the wrong values - // later (typically, in a subsequent press on backspace). - mLastSelectionEnd = mLastSelectionStart; + final int numCharsDeleted = mConnection.getExpectedSelectionEnd() + - mConnection.getExpectedSelectionStart(); + mConnection.setSelection(mConnection.getExpectedSelectionEnd(), + mConnection.getExpectedSelectionEnd()); mConnection.deleteSurroundingText(numCharsDeleted, 0); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_handleBackspace(numCharsDeleted, @@ -767,7 +767,7 @@ public final class InputLogic { } } else { // There is no selection, just delete one character. - if (Constants.NOT_A_CURSOR_POSITION == mLastSelectionEnd) { + if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) { // This should never happen. Log.e(TAG, "Backspace when we don't know the selection position"); } @@ -956,38 +956,32 @@ public final class InputLogic { * @param settingsValues The current settings values. */ private void performRecapitalization(final SettingsValues settingsValues) { - if (mLastSelectionStart == mLastSelectionEnd) { + if (!mConnection.hasSelection()) { return; // No selection } // If we have a recapitalize in progress, use it; otherwise, create a new one. if (!mRecapitalizeStatus.isActive() - || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { + || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd())) { final CharSequence selectedText = mConnection.getSelectedText(0 /* flags, 0 for no styles */); if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection - mRecapitalizeStatus.initialize(mLastSelectionStart, mLastSelectionEnd, - selectedText.toString(), + mRecapitalizeStatus.initialize(mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd(), selectedText.toString(), settingsValues.mLocale, settingsValues.mSpacingAndPunctuations.mWordSeparators); // We trim leading and trailing whitespace. mRecapitalizeStatus.trim(); - // Trimming the object may have changed the length of the string, and we need to - // reposition the selection handles accordingly. As this result in an IPC call, - // only do it if it's actually necessary, in other words if the recapitalize status - // is not set at the same place as before. - if (!mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { - mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart(); - mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd(); - } } mConnection.finishComposingText(); mRecapitalizeStatus.rotate(); - final int numCharsDeleted = mLastSelectionEnd - mLastSelectionStart; - mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); + final int numCharsDeleted = mConnection.getExpectedSelectionEnd() + - mConnection.getExpectedSelectionStart(); + mConnection.setSelection(mConnection.getExpectedSelectionEnd(), + mConnection.getExpectedSelectionEnd()); mConnection.deleteSurroundingText(numCharsDeleted, 0); mConnection.commitText(mRecapitalizeStatus.getRecapitalizedString(), 0); - mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart(); - mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd(); - mConnection.setSelection(mLastSelectionStart, mLastSelectionEnd); + mConnection.setSelection(mRecapitalizeStatus.getNewCursorStart(), + mRecapitalizeStatus.getNewCursorEnd()); } private void performAdditionToUserHistoryDictionary(final SettingsValues settingsValues, @@ -1068,10 +1062,10 @@ public final class InputLogic { // how to segment them yet. if (!settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) return; // If the cursor is not touching a word, or if there is a selection, return right away. - if (mLastSelectionStart != mLastSelectionEnd) return; + if (mConnection.hasSelection()) return; // If we don't know the cursor location, return. - if (mLastSelectionStart < 0) return; - final int expectedCursorPosition = mLastSelectionStart + offset; // We know Start == End + if (mConnection.getExpectedSelectionStart() < 0) return; + final int expectedCursorPosition = mConnection.getExpectedSelectionStart(); if (!mConnection.isCursorTouchingWord(settingsValues)) return; final TextRange range = mConnection.getWordRangeAtCursor( settingsValues.mSpacingAndPunctuations.mWordSeparators, @@ -1107,7 +1101,7 @@ public final class InputLogic { } } mWordComposer.setComposingWord(typedWord, - getNthPreviousWordForSuggestion(settingsValues, + getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations, // We want the previous word for suggestion. If we have chars in the word // before the cursor, then we want the word before that, hence 2; otherwise, // we want the word immediately before the cursor, hence 1. @@ -1285,7 +1279,8 @@ public final class InputLogic { public int getCurrentRecapitalizeState() { if (!mRecapitalizeStatus.isActive() - || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { + || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd())) { // Not recapitalizing at the moment return RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; } @@ -1301,17 +1296,17 @@ public final class InputLogic { /** * Get the nth previous word before the cursor as context for the suggestion process. - * @param currentSettings the current settings values. + * @param spacingAndPunctuations the current spacing and punctuations settings. * @param nthPreviousWord reverse index of the word to get (1-indexed) * @return the nth previous word before the cursor. */ // TODO: Make this private - public String getNthPreviousWordForSuggestion(final SettingsValues currentSettings, - final int nthPreviousWord) { - if (currentSettings.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) { + public String getNthPreviousWordForSuggestion( + final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) { + if (spacingAndPunctuations.mCurrentLanguageHasSpaces) { // If we are typing in a language with spaces we can just look up the previous // word from textview. - return mConnection.getNthPreviousWord(currentSettings, nthPreviousWord); + return mConnection.getNthPreviousWord(spacingAndPunctuations, nthPreviousWord); } else { return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null : mLastComposedWord.mCommittedWord; @@ -1638,7 +1633,8 @@ public final class InputLogic { // of the auto-correction flash. At this moment, the "typedWord" argument is // ignored by TextView. mConnection.commitCorrection( - new CorrectionInfo(mLastSelectionEnd - typedWord.length(), + new CorrectionInfo( + mConnection.getExpectedSelectionEnd() - typedWord.length(), typedWord, autoCorrection)); } } @@ -1659,7 +1655,8 @@ public final class InputLogic { mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, suggestedWords), 1); // TODO: we pass 2 here, but would it be better to move this above and pass 1 instead? - final String prevWord = mConnection.getNthPreviousWord(settingsValues, 2); + final String prevWord = mConnection.getNthPreviousWord( + settingsValues.mSpacingAndPunctuations, 2); // Add the word to the user history dictionary performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWord); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -1686,41 +1683,6 @@ public final class InputLogic { } /** - * Try to get the text from the editor to expose lies the framework may have been - * telling us. Concretely, when the device rotates, the frameworks tells us about where the - * cursor used to be initially in the editor at the time it first received the focus; this - * may be completely different from the place it is upon rotation. Since we don't have any - * means to get the real value, try at least to ask the text view for some characters and - * detect the most damaging cases: when the cursor position is declared to be much smaller - * than it really is. - */ - private void tryFixLyingCursorPosition() { - final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor( - Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); - if (null == textBeforeCursor) { - mLastSelectionStart = mLastSelectionEnd = Constants.NOT_A_CURSOR_POSITION; - } else { - final int textLength = textBeforeCursor.length(); - if (textLength > mLastSelectionStart - || (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE - && mLastSelectionStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { - // It should not be possible to have only one of those variables be - // NOT_A_CURSOR_POSITION, so if they are equal, either the selection is zero-sized - // (simple cursor, no selection) or there is no cursor/we don't know its pos - final boolean wasEqual = mLastSelectionStart == mLastSelectionEnd; - mLastSelectionStart = textLength; - // We can't figure out the value of mLastSelectionEnd :( - // But at least if it's smaller than mLastSelectionStart something is wrong, - // and if they used to be equal we also don't want to make it look like there is a - // selection. - if (wasEqual || mLastSelectionStart > mLastSelectionEnd) { - mLastSelectionEnd = mLastSelectionStart; - } - } - } - } - - /** * Retry resetting caches in the rich input connection. * * When the editor can't be accessed we can't reset the caches, so we schedule a retry. @@ -1737,7 +1699,8 @@ public final class InputLogic { // TODO: remove these arguments final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess( - mLastSelectionStart, mLastSelectionEnd, false)) { + mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), + false)) { if (0 < remainingTries) { handler.postResetCaches(tryResumeSuggestions, remainingTries - 1); return; @@ -1745,7 +1708,7 @@ public final class InputLogic { // If remainingTries is 0, we should stop waiting for new tries, but it's still // better to load the keyboard (less things will be broken). } - tryFixLyingCursorPosition(); + mConnection.tryFixLyingCursorPosition(); keyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), settingsValues); if (tryResumeSuggestions) { handler.postResumeSuggestions(); diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index 3258dcdfb..ea010b6f5 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -40,6 +40,33 @@ public class InputLogicHandler implements Handler.Callback { private static final int MSG_GET_SUGGESTED_WORDS = 1; + // A handler that never does anything. This is used for cases where events come before anything + // is initialized, though probably only the monkey can actually do this. + public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() { + @Override + public void destroy() {} + @Override + public boolean handleMessage(final Message msg) { return true; } + @Override + public void onStartBatchInput() {} + @Override + public void onUpdateBatchInput(final InputPointers batchPointers, + final int sequenceNumber) {} + @Override + public void onCancelBatchInput() {} + @Override + public void onEndBatchInput(final InputPointers batchPointers, final int sequenceNumber) {} + @Override + public void getSuggestedWords(final int sessionId, final int sequenceNumber, + final OnGetSuggestedWordsCallback callback) {} + }; + + private InputLogicHandler() { + mNonUIThreadHandler = null; + mLatinIME = null; + mInputLogic = null; + } + public InputLogicHandler(final LatinIME latinIME, final InputLogic inputLogic) { final HandlerThread handlerThread = new HandlerThread( InputLogicHandler.class.getSimpleName()); diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index b87bca4f3..7db10714a 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -163,7 +163,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { @Override protected SettingsValues job(final Resources res) { - return new SettingsValues(context, prefs, locale, res, inputAttributes); + return new SettingsValues(context, prefs, res, inputAttributes); } }; mSettingsValues = job.runInLocale(mRes, locale); diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 3fa686ba7..e4ae64fdc 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -24,7 +24,6 @@ import android.content.res.Resources; import android.util.Log; import android.view.inputmethod.EditorInfo; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.AppWorkaroundsUtils; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; @@ -96,9 +95,9 @@ public final class SettingsValues { // Debug settings public final boolean mIsInternal; - public SettingsValues(final Context context, final SharedPreferences prefs, final Locale locale, - final Resources res, final InputAttributes inputAttributes) { - mLocale = locale; + public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, + final InputAttributes inputAttributes) { + mLocale = res.getConfiguration().locale; // Get the resources mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); mSpacingAndPunctuations = new SpacingAndPunctuations(res); @@ -166,51 +165,6 @@ public final class SettingsValues { } } - // Only for tests - private SettingsValues(final Locale locale) { - // TODO: locale is saved, but not used yet. May have to change this if tests require. - mLocale = locale; - mDelayUpdateOldSuggestions = 0; - mSpacingAndPunctuations = SpacingAndPunctuations.DEFAULT; - mHintToSaveText = "Touch again to save"; - mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); - mAutoCap = true; - mVibrateOn = true; - mSoundOn = true; - mKeyPreviewPopupOn = true; - mSlidingKeyInputPreviewEnabled = true; - mShowsVoiceInputKey = true; - mIncludesOtherImesInLanguageSwitchList = false; - mShowsLanguageSwitchKey = true; - mUseContactsDict = true; - mUsePersonalizedDicts = true; - mUseDoubleSpacePeriod = true; - mBlockPotentiallyOffensive = true; - mAutoCorrectEnabled = true; - mBigramPredictionEnabled = true; - mKeyLongpressTimeout = 300; - mKeypressVibrationDuration = 5; - mKeypressSoundVolume = 1; - mKeyPreviewPopupDismissDelay = 70; - mAutoCorrectionThreshold = 1; - mGestureInputEnabled = true; - mGestureTrailEnabled = true; - mGestureFloatingPreviewTextEnabled = true; - mPhraseGestureEnabled = true; - mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; - mSuggestionVisibility = 0; - mIsInternal = false; - mUseOnlyPersonalizationDictionaryForDebug = false; - mDisplayOrientation = Configuration.ORIENTATION_PORTRAIT; - mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>(); - mAppWorkarounds.set(null); - } - - @UsedForTesting - public static SettingsValues makeDummySettingsValuesForTest(final Locale locale) { - return new SettingsValues(locale); - } - public boolean isApplicationSpecifiedCompletionsOn() { return mInputAttributes.mApplicationSpecifiedCompletionOn; } diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index 0500f4ad4..dbe30e260 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -29,6 +29,7 @@ import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; +import java.util.Locale; public final class SpacingAndPunctuations { private final int[] mSymbolsPrecededBySpace; @@ -39,23 +40,7 @@ public final class SpacingAndPunctuations { private final int mSentenceSeparator; public final String mSentenceSeparatorAndSpace; public final boolean mCurrentLanguageHasSpaces; - - public static final SpacingAndPunctuations DEFAULT = new SpacingAndPunctuations(); - - private SpacingAndPunctuations() { - mSymbolsPrecededBySpace = new int[] { '(', '[', '{', '&' }; - Arrays.sort(mSymbolsPrecededBySpace); - mSymbolsFollowedBySpace = new int[] { '.', ',', ';', ':', '!', '?', ')', ']', '}', '&' }; - Arrays.sort(mSymbolsFollowedBySpace); - mWordConnectors = new int[] { '\'', '-' }; - Arrays.sort(mWordConnectors); - mSentenceSeparator = Constants.CODE_PERIOD; - mSentenceSeparatorAndSpace = ". "; - final String[] suggestPuncsSpec = new String[] { "!", "?", ",", ":", ";" }; - mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec); - mWordSeparators = "&\t \n()[]{}*&<>+=|.,;:!?/_\""; - mCurrentLanguageHasSpaces = true; - } + public final boolean mUsesAmericanTypography; public SpacingAndPunctuations(final Resources res) { mSymbolsPrecededBySpace = @@ -75,6 +60,10 @@ public final class SpacingAndPunctuations { mSentenceSeparatorAndSpace = new String(new int[] { mSentenceSeparator, Constants.CODE_SPACE }, 0, 2); mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces); + final Locale locale = res.getConfiguration().locale; + // Heuristic: we use American Typography rules because it's the most common rules for all + // English variants. + mUsesAmericanTypography = Locale.ENGLISH.getLanguage().equals(locale.getLanguage()); } // Helper functions to create member values. diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java index 6ad5c77d5..057e332e9 100644 --- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java @@ -21,7 +21,7 @@ import android.text.TextUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.WordComposer; -import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import java.util.Locale; @@ -74,7 +74,7 @@ public final class CapsModeUtils { * @param reqModes The modes to be checked: may be any combination of * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and * {@link TextUtils#CAP_MODE_SENTENCES}. - * @param settingsValues The current settings values. + * @param spacingAndPunctuations The current spacing and punctuations settings. * @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs * * @return Returns the actual capitalization modes that can be in effect @@ -83,7 +83,7 @@ public final class CapsModeUtils { * {@link TextUtils#CAP_MODE_SENTENCES}. */ public static int getCapsMode(final CharSequence cs, final int reqModes, - final SettingsValues settingsValues, final boolean hasSpaceBefore) { + final SpacingAndPunctuations spacingAndPunctuations, final boolean hasSpaceBefore) { // Quick description of what we want to do: // CAP_MODE_CHARACTERS is always on. // CAP_MODE_WORDS is on if there is some whitespace before the cursor. @@ -167,8 +167,7 @@ public final class CapsModeUtils { // No other language has such a rule as far as I know, instead putting inside the quotation // mark as the exact thing quoted and handling the surrounding punctuation independently, // e.g. <<Did he say, "let's go home"?>> - // Hence, specifically for English, we treat this special case here. - if (Locale.ENGLISH.getLanguage().equals(settingsValues.mLocale.getLanguage())) { + if (spacingAndPunctuations.mUsesAmericanTypography) { for (; j > 0; j--) { // Here we look to go over any closing punctuation. This is because in dominant // variants of English, the final period is placed within double quotes and maybe @@ -191,7 +190,7 @@ public final class CapsModeUtils { if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) { return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes; } - if (!settingsValues.mSpacingAndPunctuations.isSentenceSeparator(c) || j <= 0) { + if (!spacingAndPunctuations.isSentenceSeparator(c) || j <= 0) { return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes; } @@ -241,7 +240,7 @@ public final class CapsModeUtils { case WORD: if (Character.isLetter(c)) { state = WORD; - } else if (settingsValues.mSpacingAndPunctuations.isSentenceSeparator(c)) { + } else if (spacingAndPunctuations.isSentenceSeparator(c)) { state = PERIOD; } else { return caps; @@ -257,7 +256,7 @@ public final class CapsModeUtils { case LETTER: if (Character.isLetter(c)) { state = LETTER; - } else if (settingsValues.mSpacingAndPunctuations.isSentenceSeparator(c)) { + } else if (spacingAndPunctuations.isSentenceSeparator(c)) { state = PERIOD; } else { return noCaps; diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index 3daa63ff4..b3c787e44 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -20,13 +20,16 @@ import android.content.ContentValues; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; +import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.AssetFileAddress; import com.android.inputmethod.latin.BinaryDictionaryGetter; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import java.io.File; import java.util.ArrayList; @@ -364,4 +367,29 @@ public class DictionaryInfoUtils { return dictList; } + + @UsedForTesting + public static boolean looksValidForDictionaryInsertion(final CharSequence text, + final SpacingAndPunctuations spacingAndPunctuations) { + if (TextUtils.isEmpty(text)) return false; + final int length = text.length(); + int i = 0; + int digitCount = 0; + while (i < length) { + final int codePoint = Character.codePointAt(text, i); + final int charCount = Character.charCount(codePoint); + i += charCount; + if (Character.isDigit(codePoint)) { + // Count digits: see below + digitCount += charCount; + continue; + } + if (!spacingAndPunctuations.isWordCodePoint(codePoint)) return false; + } + // We reject strings entirely comprised of digits to avoid using PIN codes or credit + // card numbers. It would come in handy for word prediction though; a good example is + // when writing one's address where the street number is usually quite discriminative, + // as well as the postal code. + return digitCount < length; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index 85f44541e..c5ed39310 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -17,15 +17,11 @@ package com.android.inputmethod.latin.utils; import android.text.TextUtils; -import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.settings.SettingsValues; -import java.io.IOException; import java.util.ArrayList; -import java.util.List; import java.util.Locale; public final class StringUtils { @@ -268,31 +264,6 @@ public final class StringUtils { return true; } - @UsedForTesting - public static boolean looksValidForDictionaryInsertion(final CharSequence text, - final SettingsValues settings) { - if (TextUtils.isEmpty(text)) return false; - final int length = text.length(); - int i = 0; - int digitCount = 0; - while (i < length) { - final int codePoint = Character.codePointAt(text, i); - final int charCount = Character.charCount(codePoint); - i += charCount; - if (Character.isDigit(codePoint)) { - // Count digits: see below - digitCount += charCount; - continue; - } - if (!settings.isWordCodePoint(codePoint)) return false; - } - // We reject strings entirely comprised of digits to avoid using PIN codes or credit - // card numbers. It would come in handy for word prediction though; a good example is - // when writing one's address where the street number is usually quite discriminative, - // as well as the postal code. - return digitCount < length; - } - public static boolean isIdenticalAfterCapitalizeEachWord(final String text, final String separators) { boolean needCapsNext = true; |