diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
19 files changed, 343 insertions, 264 deletions
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 36a02669d..fd1f51dd6 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -491,8 +491,9 @@ public class DictionaryFacilitator { final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final Dictionaries dictionaries = mDictionaries; - final SuggestionResults suggestionResults = - new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); + final SuggestionResults suggestionResults = new SuggestionResults( + dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS, + prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index ebe436128..fecb0ef94 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -42,6 +42,7 @@ public final class InputAttributes { final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final public boolean mShouldShowVoiceInputKey; + final public boolean mIsGeneralTextInput; final private int mInputType; final private EditorInfo mEditorInfo; final private String mPackageNameForPrivateImeOptions; @@ -76,6 +77,7 @@ public final class InputAttributes { mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; mShouldShowVoiceInputKey = false; + mIsGeneralTextInput = false; return; } // inputClass == InputType.TYPE_CLASS_TEXT @@ -102,7 +104,7 @@ public final class InputAttributes { mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); final boolean noMicrophone = mIsPasswordField - || InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS == variation + || InputTypeUtils.isEmailVariation(variation) || InputType.TYPE_TEXT_VARIATION_URI == variation || hasNoMicrophoneKeyOption(); mShouldShowVoiceInputKey = !noMicrophone; @@ -117,6 +119,15 @@ public final class InputAttributes { || (!flagAutoCorrect && !flagMultiLine); mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode; + + // If we come here, inputClass is always TYPE_CLASS_TEXT + mIsGeneralTextInput = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS != variation + && InputType.TYPE_TEXT_VARIATION_PASSWORD != variation + && InputType.TYPE_TEXT_VARIATION_PHONETIC != variation + && InputType.TYPE_TEXT_VARIATION_URI != variation + && InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD != variation + && InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != variation + && InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD != variation; } public boolean isTypeNull() { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 86fe6429b..d57db8e9a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -52,7 +52,6 @@ import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethod; import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; @@ -87,8 +86,8 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; -import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; +import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; @@ -162,6 +161,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); private final SpecialKeyDetector mSpecialKeyDetector; + // Working variable for {@link #startShowingInputView()} and + // {@link #onEvaluateInputViewShown()}. + private boolean mIsExecutingStartShowingInputView; // Object for reacting to adding/removing a dictionary pack. private final BroadcastReceiver mDictionaryPackInstallReceiver = @@ -186,9 +188,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6; private static final int MSG_RESET_CACHES = 7; private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8; - private static final int MSG_SHOW_COMMIT_INDICATOR = 9; // Update this when adding new messages - private static final int MSG_LAST = MSG_SHOW_COMMIT_INDICATOR; + private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD; private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; @@ -199,7 +200,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private int mDelayInMillisecondsToUpdateSuggestions; private int mDelayInMillisecondsToUpdateShiftState; - private int mDelayInMillisecondsToShowCommitIndicator; public UIHandler(final LatinIME ownerInstance) { super(ownerInstance); @@ -215,8 +215,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen R.integer.config_delay_in_milliseconds_to_update_suggestions); mDelayInMillisecondsToUpdateShiftState = res.getInteger( R.integer.config_delay_in_milliseconds_to_update_shift_state); - mDelayInMillisecondsToShowCommitIndicator = res.getInteger( - R.integer.text_decorator_delay_in_milliseconds_to_show_commit_indicator); } @Override @@ -274,14 +272,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.getCurrentRecapitalizeState()); } break; - case MSG_SHOW_COMMIT_INDICATOR: - // Protocol of MSG_SET_COMMIT_INDICATOR_ENABLED: - // - what: MSG_SHOW_COMMIT_INDICATOR - // - arg1: not used. - // - arg2: not used. - // - obj: the Runnable object to be called back. - ((Runnable) msg.obj).run(); - break; case MSG_WAIT_FOR_DICTIONARY_LOAD: Log.i(TAG, "Timeout waiting for dictionary load"); break; @@ -382,19 +372,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen obtainMessage(MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED, suggestedWords).sendToTarget(); } - /** - * Posts a delayed task to show the commit indicator. - * - * <p>Only one task can exist in the queue. When this method is called, any prior task that - * has not yet fired will be canceled.</p> - * @param task the runnable object that will be fired when the delayed task is dispatched. - */ - public void postShowCommitIndicatorTask(final Runnable task) { - removeMessages(MSG_SHOW_COMMIT_INDICATOR); - sendMessageDelayed(obtainMessage(MSG_SHOW_COMMIT_INDICATOR, task), - mDelayInMillisecondsToShowCommitIndicator); - } - // Working variables for the following methods. private boolean mIsOrientationChanging; private boolean mPendingSuccessiveImsCallback; @@ -1056,7 +1033,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // with cursor movement when we have a hardware keyboard since we are not in charge. final SettingsValues settingsValues = mSettings.getCurrent(); if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) - && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) { + && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, + settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } @@ -1187,22 +1165,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen outInsets.visibleTopInsets = visibleTopY; } - @Override - public boolean onEvaluateInputViewShown() { - // Always show {@link InputView}. - return true; + public void startShowingInputView() { + mIsExecutingStartShowingInputView = true; + // This {@link #showWindow(boolean)} will eventually call back + // {@link #onEvaluateInputViewShown()}. + showWindow(true /* showInput */); + mIsExecutingStartShowingInputView = false; + } + + public void stopShowingInputView() { + showWindow(false /* showInput */); } @Override - public boolean onShowInputRequested(final int flags, final boolean configChange) { - final SettingsValues settingsValues = mSettings.getCurrent(); - if ((flags & InputMethod.SHOW_EXPLICIT) == 0 && settingsValues.mHasHardwareKeyboard) { - // Even when IME is implicitly shown and physical keyboard is connected, we should - // show {@link InputView}. - // See {@link InputMethodService#onShowInputRequested(int,boolean)}. + public boolean onEvaluateInputViewShown() { + if (mIsExecutingStartShowingInputView) { return true; } - return super.onShowInputRequested(flags, configChange); + return super.onEvaluateInputViewShown(); } @Override @@ -1221,9 +1201,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // hack for now. Let's get rid of this once the framework gets fixed. final EditorInfo ei = getCurrentInputEditorInfo(); return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0)); - } else { - return false; } + return false; } @Override @@ -1273,9 +1252,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (null == keyboard) { return CoordinateUtils.newCoordinateArray(codePoints.length, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } else { - return keyboard.getCoordinates(codePoints); } + return keyboard.getCoordinates(codePoints); } // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is @@ -1511,19 +1489,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean isEmptyApplicationSpecifiedCompletions = currentSettingsValues.isApplicationSpecifiedCompletionsOn() && suggestedWords.isEmpty(); - final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords) + final boolean noSuggestionsFromDictionaries = (SuggestedWords.EMPTY == suggestedWords) || suggestedWords.isPunctuationSuggestions() || isEmptyApplicationSpecifiedCompletions; - if (shouldShowImportantNotice && noSuggestionsToShow) { + final boolean isBeginningOfSentencePrediction = (suggestedWords.mInputStyle + == SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION); + final boolean noSuggestionsToOverrideImportantNotice = noSuggestionsFromDictionaries + || isBeginningOfSentencePrediction; + if (shouldShowImportantNotice && noSuggestionsToOverrideImportantNotice) { if (mSuggestionStripView.maybeShowImportantNoticeTitle()) { return; } } if (currentSettingsValues.isSuggestionsEnabledPerUserSettings() - // We should clear suggestions if there is no suggestion to show. - || noSuggestionsToShow - || currentSettingsValues.isApplicationSpecifiedCompletionsOn()) { + || currentSettingsValues.isApplicationSpecifiedCompletionsOn() + // We should clear the contextual strip if there is no suggestion from dictionaries. + || noSuggestionsFromDictionaries) { mSuggestionStripView.setSuggestions(suggestedWords, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index b5d42dd04..744b0321a 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -252,7 +252,7 @@ public final class RichInputConnection { * See {@link InputConnection#commitText(CharSequence, int)}. */ public void commitText(final CharSequence text, final int newCursorPosition) { - commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT); + commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT, text.length()); } /** @@ -265,9 +265,11 @@ public final class RichInputConnection { * the background color. Note that this method specifies {@link BackgroundColorSpan} with * {@link Spanned#SPAN_COMPOSING} flag, meaning that the background color persists until * {@link #finishComposingText()} is called. + * @param coloredTextLength the length of text, in Java chars, which should be rendered with + * the given background color. */ public void commitTextWithBackgroundColor(final CharSequence text, final int newCursorPosition, - final int color) { + final int color, final int coloredTextLength) { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); mCommittedTextBeforeComposingText.append(text); @@ -285,7 +287,8 @@ public final class RichInputConnection { mTempObjectForCommitText.clear(); mTempObjectForCommitText.append(text); final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(color); - mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, text.length(), + final int spanLength = Math.min(coloredTextLength, text.length()); + mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, spanLength, Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mIC.commitText(mTempObjectForCommitText, newCursorPosition); mLastCommittedTextHasBackgroundColor = true; diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 6779351fd..b03818c1d 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -186,8 +186,14 @@ public final class Suggest { suggestionsList = suggestionsContainer; } - final int inputStyle = resultsArePredictions ? SuggestedWords.INPUT_STYLE_PREDICTION : - inputStyleIfNotPrediction; + final int inputStyle; + if (resultsArePredictions) { + inputStyle = suggestionResults.mIsBeginningOfSentence + ? SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION + : SuggestedWords.INPUT_STYLE_PREDICTION; + } else { + inputStyle = inputStyleIfNotPrediction; + } callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, suggestionResults.mRawSuggestions, // TODO: this first argument is lying. If this is a whitelisted word which is an @@ -240,6 +246,8 @@ public final class Suggest { // In the batch input mode, the most relevant suggested word should act as a "typed word" // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). + // Note that because this method is never used to get predictions, there is no need to + // modify inputType such in getSuggestedWordsForNonBatchInput. callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, suggestionResults.mRawSuggestions, true /* typedWordValid */, diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 6bc3da885..1d221b77f 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -39,6 +39,7 @@ public class SuggestedWords { public static final int INPUT_STYLE_APPLICATION_SPECIFIED = 4; public static final int INPUT_STYLE_RECORRECTION = 5; public static final int INPUT_STYLE_PREDICTION = 6; + public static final int INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION = 7; // The maximum number of suggestions available. public static final int MAX_SUGGESTIONS = 18; @@ -80,10 +81,9 @@ public class SuggestedWords { final int inputStyle, final int sequenceNumber) { this(suggestedWordInfoList, rawSuggestions, - (suggestedWordInfoList.isEmpty() || INPUT_STYLE_PREDICTION == inputStyle) ? null + (suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, - typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, - sequenceNumber); + typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, sequenceNumber); } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, @@ -171,6 +171,7 @@ public class SuggestedWords { return "SuggestedWords:" + " mTypedWordValid=" + mTypedWordValid + " mWillAutoCorrect=" + mWillAutoCorrect + + " mInputStyle=" + mInputStyle + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray()); } @@ -377,8 +378,13 @@ public class SuggestedWords { } } + private static boolean isPrediction(final int inputStyle) { + return INPUT_STYLE_PREDICTION == inputStyle + || INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION == inputStyle; + } + public boolean isPrediction() { - return INPUT_STYLE_PREDICTION == mInputStyle; + return isPrediction(mInputStyle); } // SuggestedWords is an immutable object, as much as possible. We must not just remove diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 5ab3571e6..fdab7f25f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -28,6 +28,7 @@ import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; @@ -90,12 +91,8 @@ public final class InputLogic { private final TextDecorator mTextDecorator = new TextDecorator(new TextDecorator.Listener() { @Override - public void onClickComposingTextToCommit(SuggestedWordInfo wordInfo) { - mLatinIME.pickSuggestionManually(wordInfo); - } - @Override - public void onClickComposingTextToAddToDictionary(SuggestedWordInfo wordInfo) { - mLatinIME.addWordToUserDictionary(wordInfo.mWord); + public void onClickComposingTextToAddToDictionary(final String word) { + mLatinIME.addWordToUserDictionary(word); mLatinIME.dismissAddToDictionaryHint(); } }); @@ -170,6 +167,7 @@ public final class InputLogic { mConnection.requestCursorUpdates(true /* enableMonitor */, true /* requestImmediateCallback */); } + mTextDecorator.reset(); } } @@ -333,17 +331,8 @@ public final class InputLogic { } final boolean shouldShowAddToDictionaryHint = shouldShowAddToDictionaryHint(suggestionInfo); - final boolean shouldShowAddToDictionaryIndicator = - shouldShowAddToDictionaryHint && settingsValues.mShouldShowUiToAcceptTypedWord; - final int backgroundColor; - if (shouldShowAddToDictionaryIndicator) { - backgroundColor = settingsValues.mTextHighlightColorForAddToDictionaryIndicator; - } else { - backgroundColor = Color.TRANSPARENT; - } - commitChosenWordWithBackgroundColor(settingsValues, suggestion, - LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR, - backgroundColor); + commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, + LastComposedWord.NOT_A_SEPARATOR); mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -358,9 +347,6 @@ public final class InputLogic { // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE. handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); } - if (shouldShowAddToDictionaryIndicator) { - mTextDecorator.showAddToDictionaryIndicator(suggestionInfo); - } return inputTransaction; } @@ -372,10 +358,11 @@ public final class InputLogic { * @param oldSelEnd old selection end * @param newSelStart new selection start * @param newSelEnd new selection end + * @param settingsValues the current values of the settings. * @return whether the cursor has moved as a result of user interaction. */ public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd) { + final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) { if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) { return false; } @@ -400,8 +387,9 @@ public final class InputLogic { // should be true, but that is if the framework had taken that wrong cursor position // into account, which means we have to reset the entire composing state whenever there // is or was a selection regardless of whether it changed or not. - if (hasOrHadSelection || (selectionChangedOrSafeToReset - && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions() + || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -430,6 +418,9 @@ public final class InputLogic { mRecapitalizeStatus.enable(); // We moved the cursor and need to invalidate the indicator right now. mTextDecorator.reset(); + // Remaining background color that was used for the add-to-dictionary indicator should be + // removed. + mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); // We moved the cursor. If we are touching a word, we need to resume suggestion. mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */, true /* shouldDelay */); @@ -508,7 +499,9 @@ public final class InputLogic { handler.cancelUpdateSuggestionStrip(); ++mAutoCommitSequenceNumber; mConnection.beginBatchEdit(); - if (mWordComposer.isComposingWord()) { + if (!mWordComposer.isComposingWord()) { + mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); + } else { 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. @@ -620,42 +613,6 @@ public final class InputLogic { } mSuggestedWords = suggestedWords; final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect; - if (shouldShowCommitIndicator(suggestedWords, settingsValues)) { - // typedWordInfo is never null here. - final int textBackgroundColor = settingsValues.mTextHighlightColorForCommitIndicator; - final SuggestedWordInfo typedWordInfo = suggestedWords.getTypedWordInfoOrNull(); - handler.postShowCommitIndicatorTask(new Runnable() { - @Override - public void run() { - // TODO: This needs to be refactored to ensure that mWordComposer is accessed - // only from the UI thread. - if (!mWordComposer.isComposingWord()) { - mTextDecorator.reset(); - return; - } - final SuggestedWordInfo currentTypedWordInfo = - mSuggestedWords.getTypedWordInfoOrNull(); - if (currentTypedWordInfo == null) { - mTextDecorator.reset(); - return; - } - if (!currentTypedWordInfo.equals(typedWordInfo)) { - // Suggested word has been changed. This task is obsolete. - mTextDecorator.reset(); - return; - } - // TODO: As with the above TODO comment, this operation must be performed only - // on the UI thread too. Needs to be refactored. - setComposingTextInternalWithBackgroundColor(typedWordInfo.mWord, - 1 /* newCursorPosition */, textBackgroundColor); - mTextDecorator.showCommitIndicator(typedWordInfo); - } - }); - } else { - // Note: It is OK to not cancel previous postShowCommitIndicatorTask() here. Having a - // cancellation mechanism could improve performance a bit though. - mTextDecorator.reset(); - } // Put a blue underline to a word in TextView which will be auto-corrected. if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator @@ -833,13 +790,14 @@ public final class InputLogic { final InputTransaction inputTransaction, // TODO: remove this argument final LatinIME.UIHandler handler) { - // In case the "add to dictionary" hint was still displayed. - // TODO: Do we really need to check if we have composing text here? - if (!mWordComposer.isComposingWord() && - mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) { - mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); + if (!mWordComposer.isComposingWord()) { mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); - mTextDecorator.reset(); + // In case the "add to dictionary" hint was still displayed. + // TODO: Do we really need to check if we have composing text here? + if (mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) { + mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); + mTextDecorator.reset(); + } } final int codePoint = event.mCodePoint; @@ -1096,7 +1054,7 @@ public final class InputLogic { inputTransaction.setRequiresUpdateSuggestions(); } else { if (mLastComposedWord.canRevertCommit()) { - revertCommit(inputTransaction); + revertCommit(inputTransaction, inputTransaction.mSettingsValues); return; } if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) { @@ -1582,14 +1540,19 @@ public final class InputLogic { * This is triggered upon pressing backspace just after a commit with auto-correction. * * @param inputTransaction The transaction in progress. + * @param settingsValues the current values of the settings. */ - private void revertCommit(final InputTransaction inputTransaction) { + private void revertCommit(final InputTransaction inputTransaction, + final SettingsValues settingsValues) { final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord; + final String originallyTypedWordString = + originallyTypedWord != null ? originallyTypedWord.toString() : ""; final CharSequence committedWord = mLastComposedWord.mCommittedWord; final String committedWordString = committedWord.toString(); final int cancelLength = committedWord.length(); + final String separatorString = mLastComposedWord.mSeparatorString; // We want java chars, not codepoints for the following. - final int separatorLength = mLastComposedWord.mSeparatorString.length(); + final int separatorLength = separatorString.length(); // TODO: should we check our saved separator against the actual contents of the text view? final int deleteLength = cancelLength + separatorLength; if (DebugFlags.DEBUG_ENABLED) { @@ -1608,7 +1571,7 @@ public final class InputLogic { if (!TextUtils.isEmpty(committedWord)) { mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString); } - final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString; + final String stringToCommit = originallyTypedWord + separatorString; final SpannableString textToCommit = new SpannableString(stringToCommit); if (committedWord instanceof SpannableString) { final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord; @@ -1645,23 +1608,53 @@ public final class InputLogic { suggestions.toArray(new String[suggestions.size()]), 0 /* flags */), 0 /* start */, lastCharIndex /* end */, 0 /* flags */); } + + final boolean shouldShowAddToDictionaryForTypedWord = + shouldShowAddToDictionaryForTypedWord(mLastComposedWord, settingsValues); + if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) { // For languages with spaces, we revert to the typed string, but the cursor is still // after the separator so we don't resume suggestions. If the user wants to correct // the word, they have to press backspace again. - mConnection.commitText(textToCommit, 1); + if (shouldShowAddToDictionaryForTypedWord) { + mConnection.commitTextWithBackgroundColor(textToCommit, 1, + settingsValues.mTextHighlightColorForAddToDictionaryIndicator, + originallyTypedWordString.length()); + } else { + mConnection.commitText(textToCommit, 1); + } } else { // For languages without spaces, we revert the typed string but the cursor is flush // with the typed word, so we need to resume suggestions right away. final int[] codePoints = StringUtils.toCodePointArray(stringToCommit); mWordComposer.setComposingWord(codePoints, mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); - setComposingTextInternal(textToCommit, 1); + if (shouldShowAddToDictionaryForTypedWord) { + setComposingTextInternalWithBackgroundColor(textToCommit, 1, + settingsValues.mTextHighlightColorForAddToDictionaryIndicator, + originallyTypedWordString.length()); + } else { + setComposingTextInternal(textToCommit, 1); + } } // Don't restart suggestion yet. We'll restart if the user deletes the separator. mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; - // We have a separator between the word and the cursor: we should show predictions. - inputTransaction.setRequiresUpdateSuggestions(); + + if (shouldShowAddToDictionaryForTypedWord) { + // Due to the API limitation as of L, we cannot reliably retrieve the reverted text + // when the separator causes line breaking. Until this API limitation is addressed in + // the framework, show the indicator only when the separator doesn't contain + // line-breaking characters. + if (!StringUtils.hasLineBreakCharacter(separatorString)) { + mTextDecorator.showAddToDictionaryIndicator(originallyTypedWordString, + mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); + } + mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString); + } else { + // We have a separator between the word and the cursor: we should show predictions. + inputTransaction.setRequiresUpdateSuggestions(); + } } /** @@ -2085,9 +2078,7 @@ public final class InputLogic { } /** - * Commits the chosen word to the text field and saves it for later retrieval. This is a - * synonym of {@code commitChosenWordWithBackgroundColor(settingsValues, chosenWord, - * commitType, separatorString, Color.TRANSPARENT}. + * Commits the chosen word to the text field and saves it for later retrieval. * * @param settingsValues the current values of the settings. * @param chosenWord the word we want to commit. @@ -2096,23 +2087,6 @@ public final class InputLogic { */ private void commitChosenWord(final SettingsValues settingsValues, final String chosenWord, final int commitType, final String separatorString) { - commitChosenWordWithBackgroundColor(settingsValues, chosenWord, commitType, separatorString, - Color.TRANSPARENT); - } - - /** - * Commits the chosen word to the text field and saves it for later retrieval. - * - * @param settingsValues the current values of the settings. - * @param chosenWord the word we want to commit. - * @param commitType the type of the commit, as one of LastComposedWord.COMMIT_TYPE_* - * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none. - * @param backgroundColor the background color to be specified with the committed text. Pass - * {@link Color#TRANSPARENT} to not specify the background color. - */ - private void commitChosenWordWithBackgroundColor(final SettingsValues settingsValues, - final String chosenWord, final int commitType, final String separatorString, - final int backgroundColor) { final SuggestedWords suggestedWords = mSuggestedWords; final CharSequence chosenWordWithSuggestions = SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, @@ -2122,7 +2096,7 @@ public final class InputLogic { // information from the 1st previous word. final PrevWordsInfo prevWordsInfo = mConnection.getPrevWordsInfoFromNthPreviousWord( settingsValues.mSpacingAndPunctuations, mWordComposer.isComposingWord() ? 2 : 1); - mConnection.commitTextWithBackgroundColor(chosenWordWithSuggestions, 1, backgroundColor); + mConnection.commitText(chosenWordWithSuggestions, 1); // Add the word to the user history dictionary performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo); // TODO: figure out here if this is an auto-correct or if the best word is actually @@ -2206,7 +2180,7 @@ public final class InputLogic { private void setComposingTextInternal(final CharSequence newComposingText, final int newCursorPosition) { setComposingTextInternalWithBackgroundColor(newComposingText, newCursorPosition, - Color.TRANSPARENT); + Color.TRANSPARENT, newComposingText.length()); } /** @@ -2222,9 +2196,11 @@ public final class InputLogic { * @param newCursorPosition the new cursor position * @param backgroundColor the background color to be set to the composing text. Set * {@link Color#TRANSPARENT} to disable the background color. + * @param coloredTextLength the length of text, in Java chars, which should be rendered with + * the given background color. */ private void setComposingTextInternalWithBackgroundColor(final CharSequence newComposingText, - final int newCursorPosition, final int backgroundColor) { + final int newCursorPosition, final int backgroundColor, final int coloredTextLength) { final CharSequence composingTextToBeSet; if (backgroundColor == Color.TRANSPARENT) { composingTextToBeSet = newComposingText; @@ -2232,7 +2208,8 @@ public final class InputLogic { final SpannableString spannable = new SpannableString(newComposingText); final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(backgroundColor); - spannable.setSpan(backgroundColorSpan, 0, spannable.length(), + final int spanLength = Math.min(coloredTextLength, spannable.length()); + spannable.setSpan(backgroundColorSpan, 0, spanLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); composingTextToBeSet = spannable; } @@ -2254,7 +2231,8 @@ public final class InputLogic { } /** - * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo} is called. + * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is + * called. * @param info The wrapper object with which we can access cursor/anchor info. */ public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) { @@ -2278,12 +2256,12 @@ public final class InputLogic { } /** - * Returns whether the commit indicator should be shown or not. - * @param suggestedWords the suggested word that is being displayed. + * Returns whether the add to dictionary indicator should be shown or not. + * @param lastComposedWord the last composed word information. * @param settingsValues the current settings value. * @return {@code true} if the commit indicator should be shown. */ - private boolean shouldShowCommitIndicator(final SuggestedWords suggestedWords, + private boolean shouldShowAddToDictionaryForTypedWord(final LastComposedWord lastComposedWord, final SettingsValues settingsValues) { if (!mConnection.isCursorAnchorInfoMonitorEnabled()) { // We cannot help in this case because we are heavily relying on this new API. @@ -2292,24 +2270,16 @@ public final class InputLogic { if (!settingsValues.mShouldShowUiToAcceptTypedWord) { return false; } - final SuggestedWordInfo typedWordInfo = suggestedWords.getTypedWordInfoOrNull(); - if (typedWordInfo == null) { + if (TextUtils.isEmpty(lastComposedWord.mTypedWord)) { return false; } - if (suggestedWords.mInputStyle != SuggestedWords.INPUT_STYLE_TYPING){ + if (TextUtils.equals(lastComposedWord.mTypedWord, lastComposedWord.mCommittedWord)) { return false; } - if (settingsValues.mShowCommitIndicatorOnlyForAutoCorrection - && !suggestedWords.mWillAutoCorrect) { + if (!mDictionaryFacilitator.isUserDictionaryEnabled()) { return false; } - // TODO: Calling shouldShowAddToDictionaryHint(typedWordInfo) multiple times should be fine - // in terms of performance, but we can do better. One idea is to make SuggestedWords include - // a boolean that tells whether the word is a dictionary word or not. - if (settingsValues.mShowCommitIndicatorOnlyForOutOfVocabulary - && !shouldShowAddToDictionaryHint(typedWordInfo)) { - return false; - } - return true; + return !mDictionaryFacilitator.isValidWord(lastComposedWord.mTypedWord, + true /* ignoreCase */); } } diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java new file mode 100644 index 000000000..f5e4d33a2 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.os.Bundle; + +import com.android.inputmethod.latin.R; + + +/** + * "Appearance" settings sub screen. + */ +public final class AppearanceSettingsFragment extends SubScreenFragment { + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_appearance); + } + + @Override + public void onResume() { + super.onResume(); + CustomInputStyleSettingsFragment.updateCustomInputStylesSummary( + findPreference(Settings.PREF_CUSTOM_INPUT_STYLES)); + ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME)); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index d53a61654..9bc398654 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -31,6 +31,7 @@ import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; import android.support.v4.view.ViewCompat; +import android.text.TextUtils; import android.util.Pair; import android.view.LayoutInflater; import android.view.Menu; @@ -396,6 +397,25 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { // Empty constructor for fragment generation. } + static void updateCustomInputStylesSummary(final Preference pref) { + // When we are called from the Settings application but we are not already running, some + // singleton and utility classes may not have been initialized. We have to call + // initialization method of these classes here. See {@link LatinIME#onCreate()}. + SubtypeLocaleUtils.init(pref.getContext()); + + final Resources res = pref.getContext().getResources(); + final SharedPreferences prefs = pref.getSharedPreferences(); + final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); + final InputMethodSubtype[] subtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); + final ArrayList<String> subtypeNames = new ArrayList<>(); + for (final InputMethodSubtype subtype : subtypes) { + subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); + } + // TODO: A delimiter of custom input styles should be localized. + pref.setSummary(TextUtils.join(", ", subtypeNames)); + } + @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java index fcdd39316..b073c50a4 100644 --- a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java @@ -16,66 +16,27 @@ package com.android.inputmethod.latin.settings; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; import android.os.Bundle; -import android.preference.PreferenceScreen; -import android.text.TextUtils; -import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; -import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; /** - * "Multi lingual options" settings sub screen. + * "Multilingual options" settings sub screen. * * This settings sub screen handles the following input preferences. * - Language switch key * - Switch to other input methods - * - Custom input styles */ public final class MultiLingualSettingsFragment extends SubScreenFragment { @Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); - addPreferencesFromResource(R.xml.prefs_screen_multi_lingual); - - final Context context = getActivity(); - - // When we are called from the Settings application but we are not already running, some - // singleton and utility classes may not have been initialized. We have to call - // initialization method of these classes here. See {@link LatinIME#onCreate()}. - SubtypeLocaleUtils.init(context); - + addPreferencesFromResource(R.xml.prefs_screen_multilingual); if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) { removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY); removePreference(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); } } - - @Override - public void onResume() { - super.onResume(); - updateCustomInputStylesSummary(); - } - - private void updateCustomInputStylesSummary() { - final SharedPreferences prefs = getSharedPreferences(); - final Resources res = getResources(); - final PreferenceScreen customInputStyles = - (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES); - final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); - final InputMethodSubtype[] subtypes = - AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); - final ArrayList<String> subtypeNames = new ArrayList<>(); - for (final InputMethodSubtype subtype : subtypes) { - subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); - } - // TODO: A delimiter of custom input styles should be localized. - customInputStyles.setSummary(TextUtils.join(", ", subtypeNames)); - } } diff --git a/java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java index f459d68dd..49db2bdc0 100644 --- a/java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java @@ -27,7 +27,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; /** - * "Input preferences" settings sub screen. + * "Preferences" settings sub screen. * * This settings sub screen handles the following input preferences. * - Auto-capitalization @@ -37,11 +37,11 @@ import com.android.inputmethod.latin.SubtypeSwitcher; * - Popup on keypress * - Voice input key */ -public final class InputSettingsFragment extends SubScreenFragment { +public final class PreferencesSettingsFragment extends SubScreenFragment { @Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); - addPreferencesFromResource(R.xml.prefs_screen_input); + addPreferencesFromResource(R.xml.prefs_screen_preferences); final Resources res = getResources(); final Context context = getActivity(); diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 3c7a99102..0de2d8831 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -42,9 +42,10 @@ import java.util.concurrent.locks.ReentrantLock; public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = Settings.class.getSimpleName(); // Settings screens - public static final String SCREEN_INPUT = "screen_input"; + public static final String SCREEN_PREFERENCES = "screen_preferences"; + public static final String SCREEN_APPEARANCE = "screen_appearance"; public static final String SCREEN_THEME = "screen_theme"; - public static final String SCREEN_MULTI_LINGUAL = "screen_multi_lingual"; + public static final String SCREEN_MULTILINGUAL = "screen_multilingual"; public static final String SCREEN_GESTURE = "screen_gesture"; public static final String SCREEN_CORRECTION = "screen_correction"; public static final String SCREEN_ADVANCED = "screen_advanced"; @@ -69,6 +70,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang "pref_key_use_double_space_period"; public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE = "pref_key_block_potentially_offensive"; + // No multilingual options in Android L and above for now. + public static final boolean SHOW_MULTILINGUAL_SETTINGS = + BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT; public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS = BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT; public static final boolean HAS_UI_TO_ACCEPT_TYPED_WORD = diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index ff7495853..4fc17387f 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.settings; import android.content.Intent; import android.os.Bundle; +import android.preference.Preference; import android.preference.PreferenceScreen; import android.view.Menu; import android.view.MenuInflater; @@ -46,12 +47,10 @@ public final class SettingsFragment extends InputMethodSettingsFragment { final PreferenceScreen preferenceScreen = getPreferenceScreen(); preferenceScreen.setTitle( ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class)); - } - - @Override - public void onResume() { - super.onResume(); - ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME)); + if (!Settings.SHOW_MULTILINGUAL_SETTINGS) { + final Preference multilingualOptions = findPreference(Settings.SCREEN_MULTILINGUAL); + preferenceScreen.removePreference(multilingualOptions); + } } @Override diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 308f3b470..d8c548d8b 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -97,10 +97,7 @@ public final class SettingsValues { new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE]; // TextDecorator - public final int mTextHighlightColorForCommitIndicator; public final int mTextHighlightColorForAddToDictionaryIndicator; - public final boolean mShowCommitIndicatorOnlyForAutoCorrection; - public final boolean mShowCommitIndicatorOnlyForOutOfVocabulary; // Debug settings public final boolean mIsInternal; @@ -148,7 +145,8 @@ public final class SettingsValues { ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */; mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true); - mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true); + mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true) + && inputAttributes.mIsGeneralTextInput; mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res); mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res); mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); @@ -174,12 +172,6 @@ public final class SettingsValues { mSuggestionsEnabledPerUserSettings = readSuggestionsEnabled(prefs); AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray( prefs, mAdditionalFeaturesSettingValues); - mShowCommitIndicatorOnlyForAutoCorrection = res.getBoolean( - R.bool.text_decorator_only_for_auto_correction); - mShowCommitIndicatorOnlyForOutOfVocabulary = res.getBoolean( - R.bool.text_decorator_only_for_out_of_vocabulary); - mTextHighlightColorForCommitIndicator = res.getColor( - R.color.text_decorator_commit_indicator_text_highlight_color); mTextHighlightColorForAddToDictionaryIndicator = res.getColor( R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color); mIsInternal = Settings.isInternal(prefs); @@ -425,12 +417,6 @@ public final class SettingsValues { sb.append("" + (null == awu ? "null" : awu.toString())); sb.append("\n mAdditionalFeaturesSettingValues = "); sb.append("" + Arrays.toString(mAdditionalFeaturesSettingValues)); - sb.append("\n mShowCommitIndicatorOnlyForAutoCorrection = "); - sb.append("" + mShowCommitIndicatorOnlyForAutoCorrection); - sb.append("\n mShowCommitIndicatorOnlyForOutOfVocabulary = "); - sb.append("" + mShowCommitIndicatorOnlyForOutOfVocabulary); - sb.append("\n mTextHighlightColorForCommitIndicator = "); - sb.append("" + mTextHighlightColorForCommitIndicator); sb.append("\n mTextHighlightColorForAddToDictionaryIndicator = "); sb.append("" + mTextHighlightColorForAddToDictionaryIndicator); sb.append("\n mIsInternal = "); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 33745a846..0fd5e139e 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -408,8 +408,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // Decided to be in the sliding suggestion mode only when the touch point has been moved // upward. Further {@link MotionEvent}s will be delivered to // {@link #onTouchEvent(MotionEvent)}. - mNeedsToTransformTouchEventToHoverEvent = AccessibilityUtils.getInstance() - .isTouchExplorationEnabled(); + mNeedsToTransformTouchEventToHoverEvent = + AccessibilityUtils.getInstance().isTouchExplorationEnabled(); mIsDispatchingHoverEventToMoreSuggestions = false; return true; } diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java index 08f5b0b41..c2167a76b 100644 --- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java @@ -19,12 +19,13 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.dictionarypack.DictionarySettingsFragment; import com.android.inputmethod.latin.about.AboutPreferences; import com.android.inputmethod.latin.settings.AdvancedSettingsFragment; +import com.android.inputmethod.latin.settings.AppearanceSettingsFragment; import com.android.inputmethod.latin.settings.CorrectionSettingsFragment; import com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment; import com.android.inputmethod.latin.settings.DebugSettingsFragment; import com.android.inputmethod.latin.settings.GestureSettingsFragment; -import com.android.inputmethod.latin.settings.InputSettingsFragment; import com.android.inputmethod.latin.settings.MultiLingualSettingsFragment; +import com.android.inputmethod.latin.settings.PreferencesSettingsFragment; import com.android.inputmethod.latin.settings.SettingsFragment; import com.android.inputmethod.latin.settings.ThemeSettingsFragment; import com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment; @@ -40,7 +41,8 @@ public class FragmentUtils { static { sLatinImeFragments.add(DictionarySettingsFragment.class.getName()); sLatinImeFragments.add(AboutPreferences.class.getName()); - sLatinImeFragments.add(InputSettingsFragment.class.getName()); + sLatinImeFragments.add(PreferencesSettingsFragment.class.getName()); + sLatinImeFragments.add(AppearanceSettingsFragment.class.getName()); sLatinImeFragments.add(ThemeSettingsFragment.class.getName()); sLatinImeFragments.add(MultiLingualSettingsFragment.class.getName()); sLatinImeFragments.add(CustomInputStyleSettingsFragment.class.getName()); diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java index 8b7077879..ea406fa75 100644 --- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -23,15 +23,24 @@ import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; +import java.util.concurrent.TimeUnit; + public final class ImportantNoticeUtils { private static final String TAG = ImportantNoticeUtils.class.getSimpleName(); // {@link SharedPreferences} name to save the last important notice version that has been // displayed to users. private static final String PREFERENCE_NAME = "important_notice_pref"; - private static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version"; + @UsedForTesting + static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version"; + @UsedForTesting + static final String KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE = + "timestamp_of_first_important_notice"; + @UsedForTesting + static final long TIMEOUT_OF_IMPORTANT_NOTICE = TimeUnit.HOURS.toMillis(23); public static final int VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS = 1; // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key. @@ -56,15 +65,18 @@ public final class ImportantNoticeUtils { } } - private static SharedPreferences getImportantNoticePreferences(final Context context) { + @UsedForTesting + static SharedPreferences getImportantNoticePreferences(final Context context) { return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } - private static int getCurrentImportantNoticeVersion(final Context context) { + @UsedForTesting + static int getCurrentImportantNoticeVersion(final Context context) { return context.getResources().getInteger(R.integer.config_important_notice_version); } - private static int getLastImportantNoticeVersion(final Context context) { + @UsedForTesting + static int getLastImportantNoticeVersion(final Context context) { return getImportantNoticePreferences(context).getInt(KEY_IMPORTANT_NOTICE_VERSION, 0); } @@ -77,6 +89,20 @@ public final class ImportantNoticeUtils { return getCurrentImportantNoticeVersion(context) > lastVersion; } + @UsedForTesting + static boolean hasTimeoutPassed(final Context context, final long currentTimeInMillis) { + final SharedPreferences prefs = getImportantNoticePreferences(context); + if (!prefs.contains(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)) { + prefs.edit() + .putLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, currentTimeInMillis) + .apply(); + } + final long firstDisplayTimeInMillis = prefs.getLong( + KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, currentTimeInMillis); + final long elapsedTime = currentTimeInMillis - firstDisplayTimeInMillis; + return elapsedTime >= TIMEOUT_OF_IMPORTANT_NOTICE; + } + public static boolean shouldShowImportantNotice(final Context context) { if (!hasNewImportantNotice(context)) { return false; @@ -88,6 +114,10 @@ public final class ImportantNoticeUtils { if (isInSystemSetupWizard(context)) { return false; } + if (hasTimeoutPassed(context, System.currentTimeMillis())) { + updateLastImportantNoticeVersion(context); + return false; + } return true; } @@ -95,11 +125,12 @@ public final class ImportantNoticeUtils { getImportantNoticePreferences(context) .edit() .putInt(KEY_IMPORTANT_NOTICE_VERSION, getNextImportantNoticeVersion(context)) + .remove(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE) .apply(); } public static String getNextImportantNoticeTitle(final Context context) { - final int nextVersion = getCurrentImportantNoticeVersion(context); + final int nextVersion = getNextImportantNoticeVersion(context); final String[] importantNoticeTitleArray = context.getResources().getStringArray( R.array.important_notice_title_array); if (nextVersion > 0 && nextVersion < importantNoticeTitleArray.length) { diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index 38f0b3fee..79128dbd2 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -37,6 +37,14 @@ public final class StringUtils { private static final String EMPTY_STRING = ""; + private static final char CHAR_LINE_FEED = 0X000A; + private static final char CHAR_VERTICAL_TAB = 0X000B; + private static final char CHAR_FORM_FEED = 0X000C; + private static final char CHAR_CARRIAGE_RETURN = 0X000D; + private static final char CHAR_NEXT_LINE = 0X0085; + private static final char CHAR_LINE_SEPARATOR = 0X2028; + private static final char CHAR_PARAGRAPH_SEPARATOR = 0X2029; + private StringUtils() { // This utility class is not publicly instantiable. } @@ -123,20 +131,20 @@ public final class StringUtils { public static String capitalizeFirstCodePoint(final String s, final Locale locale) { if (s.length() <= 1) { - return s.toUpperCase(locale); + return toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale); } // Please refer to the comment below in // {@link #capitalizeFirstAndDowncaseRest(String,Locale)} as this has the same shortcomings final int cutoff = s.offsetByCodePoints(0, 1); - return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff); + return toUpperCaseOfStringForLocale( + s.substring(0, cutoff), true /* needsToUpperCase */, locale) + s.substring(cutoff); } public static String capitalizeFirstAndDowncaseRest(final String s, final Locale locale) { if (s.length() <= 1) { - return s.toUpperCase(locale); + return toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale); } // TODO: fix the bugs below - // - This does not work for Greek, because it returns upper case instead of title case. // - It does not work for Serbian, because it fails to account for the "lj" character, // which should be "Lj" in title case and "LJ" in upper case. // - It does not work for Dutch, because it fails to account for the "ij" digraph when it's @@ -144,7 +152,9 @@ public final class StringUtils { // be capitalized as "IJ" as if they were a single letter in most words (not all). If the // unicode char for the ligature is used however, it works. final int cutoff = s.offsetByCodePoints(0, 1); - return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale); + final String titleCaseFirstLetter = toUpperCaseOfStringForLocale( + s.substring(0, cutoff), true /* needsToUpperCase */, locale); + return titleCaseFirstLetter + s.substring(cutoff).toLowerCase(locale); } private static final int[] EMPTY_CODEPOINTS = {}; @@ -481,10 +491,23 @@ public final class StringUtils { return bytes; } + private static final String LANGUAGE_GREEK = "el"; + + private static Locale getLocaleUsedForToTitleCase(final Locale locale) { + // In Greek locale {@link String#toUpperCase(Locale)} eliminates accents from its result. + // In order to get accented upper case letter, {@link Locale#ROOT} should be used. + if (LANGUAGE_GREEK.equals(locale.getLanguage())) { + return Locale.ROOT; + } + return locale; + } + public static String toUpperCaseOfStringForLocale(final String text, final boolean needsToUpperCase, final Locale locale) { - if (text == null || !needsToUpperCase) return text; - return text.toUpperCase(locale); + if (text == null || !needsToUpperCase) { + return text; + } + return text.toUpperCase(getLocaleUsedForToTitleCase(locale)); } public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, @@ -594,4 +617,30 @@ public final class StringUtils { return sb + "]"; } } + + /** + * Returns whether the last composed word contains line-breaking character (e.g. CR or LF). + * @param text the text to be examined. + * @return {@code true} if the last composed word contains line-breaking separator. + */ + @UsedForTesting + public static boolean hasLineBreakCharacter(final String text) { + if (TextUtils.isEmpty(text)) { + return false; + } + for (int i = text.length() - 1; i >= 0; --i) { + final char c = text.charAt(i); + switch (c) { + case CHAR_LINE_FEED: + case CHAR_VERTICAL_TAB: + case CHAR_FORM_FEED: + case CHAR_CARRIAGE_RETURN: + case CHAR_NEXT_LINE: + case CHAR_LINE_SEPARATOR: + case CHAR_PARAGRAPH_SEPARATOR: + return true; + } + } + return false; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index 7170bd789..8cd49509f 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -32,14 +32,18 @@ import java.util.TreeSet; public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { public final Locale mLocale; public final ArrayList<SuggestedWordInfo> mRawSuggestions; + // TODO: Instead of a boolean , we may want to include the context of this suggestion results, + // such as {@link PrevWordsInfo}. + public final boolean mIsBeginningOfSentence; private final int mCapacity; - public SuggestionResults(final Locale locale, final int capacity) { - this(locale, sSuggestedWordInfoComparator, capacity); + public SuggestionResults(final Locale locale, final int capacity, + final boolean isBeginningOfSentence) { + this(locale, sSuggestedWordInfoComparator, capacity, isBeginningOfSentence); } - public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator, - final int capacity) { + private SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator, + final int capacity, final boolean isBeginningOfSentence) { super(comparator); mLocale = locale; mCapacity = capacity; @@ -48,6 +52,7 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { } else { mRawSuggestions = null; } + mIsBeginningOfSentence = isBeginningOfSentence; } @Override |