aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java5
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java13
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java246
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java29
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java12
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java14
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java203
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java41
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java20
-rw-r--r--java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java43
-rw-r--r--java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java (renamed from java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java)6
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java18
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java11
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java26
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java17
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java4
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java236
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FragmentUtils.java6
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java41
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java63
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SuggestionResults.java13
21 files changed, 750 insertions, 317 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 c348e6cfe..d57db8e9a 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -46,13 +46,14 @@ import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
import android.view.Window;
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;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.annotations.UsedForTesting;
@@ -86,6 +87,7 @@ 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.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;
@@ -152,12 +154,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
private View mInputView;
private SuggestionStripView mSuggestionStripView;
+ private TextView mExtractEditText;
private RichInputMethodManager mRichImm;
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
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 =
@@ -182,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;
@@ -195,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);
@@ -211,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
@@ -270,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;
@@ -378,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;
@@ -713,11 +694,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onConfigurationChanged(final Configuration conf) {
- final SettingsValues settingsValues = mSettings.getCurrent();
+ SettingsValues settingsValues = mSettings.getCurrent();
if (settingsValues.mDisplayOrientation != conf.orientation) {
mHandler.startOrientationChanging();
mInputLogic.onOrientationChange(mSettings.getCurrent());
}
+ if (settingsValues.mHasHardwareKeyboard != Settings.readHasHardwareKeyboard(conf)) {
+ // If the state of having a hardware keyboard changed, then we want to reload the
+ // settings to adjust for that.
+ // TODO: we should probably do this unconditionally here, rather than only when we
+ // have a change in hardware keyboard configuration.
+ loadSettings();
+ settingsValues = mSettings.getCurrent();
+ if (settingsValues.mHasHardwareKeyboard) {
+ // We call cleanupInternalStateForFinishInput() because it's the right thing to do;
+ // however, it seems at the moment the framework is passing us a seemingly valid
+ // but actually non-functional InputConnection object. So if this bug ever gets
+ // fixed we'll be able to remove the composition, but until it is this code is
+ // actually not doing much.
+ cleanupInternalStateForFinishInput();
+ }
+ }
// TODO: Remove this test.
if (!conf.locale.equals(mPersonalizationDictionaryUpdater.getLocale())) {
refreshPersonalizationDictionarySession(settingsValues);
@@ -742,6 +739,49 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
+ public void setExtractView(final View view) {
+ final TextView prevExtractEditText = mExtractEditText;
+ super.setExtractView(view);
+ TextView nextExtractEditText = null;
+ if (view != null) {
+ final View extractEditText = view.findViewById(android.R.id.inputExtractEditText);
+ if (extractEditText instanceof TextView) {
+ nextExtractEditText = (TextView)extractEditText;
+ }
+ }
+ if (prevExtractEditText == nextExtractEditText) {
+ return;
+ }
+ if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && prevExtractEditText != null) {
+ prevExtractEditText.getViewTreeObserver().removeOnPreDrawListener(
+ mExtractTextViewPreDrawListener);
+ }
+ mExtractEditText = nextExtractEditText;
+ if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && mExtractEditText != null) {
+ mExtractEditText.getViewTreeObserver().addOnPreDrawListener(
+ mExtractTextViewPreDrawListener);
+ }
+ }
+
+ private final ViewTreeObserver.OnPreDrawListener mExtractTextViewPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ onExtractTextViewPreDraw();
+ return true;
+ }
+ };
+
+ private void onExtractTextViewPreDraw() {
+ if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || !isFullscreenMode()
+ || mExtractEditText == null) {
+ return;
+ }
+ final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText);
+ mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
+ }
+
+ @Override
public void setCandidatesView(final View view) {
// To ensure that CandidatesView will never be set.
return;
@@ -843,40 +883,52 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Note: This call should be done by InputMethodService?
updateFullscreenMode();
- // The app calling setText() has the effect of clearing the composing
- // span, so we should reset our state unconditionally, even if restarting is true.
- // We also tell the input logic about the combining rules for the current subtype, so
- // it can adjust its combiners if needed.
- mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
- currentSettingsValues);
+ // ALERT: settings have not been reloaded and there is a chance they may be stale.
+ // In the practice, if it is, we should have gotten onConfigurationChanged so it should
+ // be fine, but this is horribly confusing and must be fixed AS SOON AS POSSIBLE.
- // Note: the following does a round-trip IPC on the main thread: be careful
- final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ // In some cases the input connection has not been reset yet and we can't access it. In
+ // this case we will need to call loadKeyboard() later, when it's accessible, so that we
+ // can go into the correct mode, so we need to do some housekeeping here.
+ final boolean needToCallLoadKeyboardLater;
final Suggest suggest = mInputLogic.mSuggest;
- if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
- // TODO: Do this automatically.
- resetSuggest();
- }
+ if (!currentSettingsValues.mHasHardwareKeyboard) {
+ // The app calling setText() has the effect of clearing the composing
+ // span, so we should reset our state unconditionally, even if restarting is true.
+ // We also tell the input logic about the combining rules for the current subtype, so
+ // it can adjust its combiners if needed.
+ mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
+ currentSettingsValues);
+
+ // Note: the following does a round-trip IPC on the main thread: be careful
+ final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) {
+ // TODO: Do this automatically.
+ resetSuggest();
+ }
- // TODO[IL]: Can the following be moved to InputLogic#startInput?
- final boolean canReachInputConnection;
- if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
- editorInfo.initialSelStart, editorInfo.initialSelEnd,
- false /* shouldFinishComposition */)) {
- // Sometimes, while rotating, for some reason the framework tells the app we are not
- // connected to it and that means we can't refresh the cache. In this case, schedule a
- // refresh later.
- // We try resetting the caches up to 5 times before giving up.
- mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
- // mLastSelection{Start,End} are reset later in this method, don't need to do it here
- canReachInputConnection = false;
+ // TODO[IL]: Can the following be moved to InputLogic#startInput?
+ if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
+ editorInfo.initialSelStart, editorInfo.initialSelEnd,
+ false /* shouldFinishComposition */)) {
+ // Sometimes, while rotating, for some reason the framework tells the app we are not
+ // connected to it and that means we can't refresh the cache. In this case, schedule
+ // a refresh later.
+ // We try resetting the caches up to 5 times before giving up.
+ mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
+ // mLastSelection{Start,End} are reset later in this method, no need to do it here
+ needToCallLoadKeyboardLater = true;
+ } else {
+ // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
+ // effort to work around this bug.
+ mInputLogic.mConnection.tryFixLyingCursorPosition();
+ mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
+ true /* shouldDelay */);
+ needToCallLoadKeyboardLater = false;
+ }
} else {
- // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
- // effort to work around this bug.
- mInputLogic.mConnection.tryFixLyingCursorPosition();
- mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
- true /* shouldDelay */);
- canReachInputConnection = true;
+ // If we have a hardware keyboard we don't need to call loadKeyboard later anyway.
+ needToCallLoadKeyboardLater = false;
}
if (isDifferentTextField ||
@@ -894,9 +946,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(),
getCurrentRecapitalizeState());
- if (!canReachInputConnection) {
- // If we can't reach the input connection, we will call loadKeyboard again later,
- // so we need to save its state now. The call will be done in #retryResetCaches.
+ if (needToCallLoadKeyboardLater) {
+ // If we need to call loadKeyboard again later, we need to save its state now. The
+ // later call will be done in #retryResetCaches.
switcher.saveKeyboardState();
}
} else if (restarting) {
@@ -953,6 +1005,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void onFinishInputViewInternal(final boolean finishingInput) {
super.onFinishInputView(finishingInput);
+ cleanupInternalStateForFinishInput();
+ }
+
+ private void cleanupInternalStateForFinishInput() {
mKeyboardSwitcher.deallocateMemory();
// Remove pending messages related to update suggestions
mHandler.cancelUpdateSuggestionStrip();
@@ -972,13 +1028,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd);
}
- // If the keyboard is not visible, we don't need to do all the housekeeping work, as it
- // will be reset when the keyboard shows up anyway.
- // TODO: revisit this when LatinIME supports hardware keyboards.
- // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown().
- // TODO: find a better way to simulate actual execution.
- if (isInputViewShown() &&
- mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) {
+ // This call happens when we have a hardware keyboard as well as when we don't. While we
+ // don't support hardware keyboards yet we should avoid doing the processing associated
+ // 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,
+ settingsValues)) {
mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
getCurrentRecapitalizeState());
}
@@ -987,9 +1043,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We cannot mark this method as @Override until new SDK becomes publicly available.
// @Override
public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) {
- if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) {
- mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
+ if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || isFullscreenMode()) {
+ return;
}
+ mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
}
/**
@@ -1074,12 +1131,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onComputeInsets(final InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
+ final SettingsValues settingsValues = mSettings.getCurrent();
final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
if (visibleKeyboardView == null || !hasSuggestionStripView()) {
return;
}
final int inputHeight = mInputView.getHeight();
- final boolean hasHardwareKeyboard = mKeyboardSwitcher.hasHardwareKeyboard();
+ final boolean hasHardwareKeyboard = settingsValues.mHasHardwareKeyboard;
if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) {
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
// no visual element will be shown on the screen.
@@ -1107,26 +1165,30 @@ 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) {
- if ((flags & InputMethod.SHOW_EXPLICIT) == 0 && mKeyboardSwitcher.hasHardwareKeyboard()) {
- // 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
public boolean onEvaluateFullscreenMode() {
- if (mKeyboardSwitcher.hasHardwareKeyboard()) {
+ final SettingsValues settingsValues = mSettings.getCurrent();
+ if (settingsValues.mHasHardwareKeyboard) {
// If there is a hardware keyboard, disable full screen mode.
return false;
}
@@ -1139,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
@@ -1191,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
@@ -1429,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 6b6384c48..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;
@@ -623,14 +626,24 @@ public final class RichInputConnection {
return Arrays.binarySearch(sortedSeparators, code) >= 0;
}
+ private static boolean isPartOfCompositionForScript(final int codePoint,
+ final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) {
+ // We always consider word connectors part of compositions.
+ return spacingAndPunctuations.isWordConnector(codePoint)
+ // Otherwise, it's part of composition if it's part of script and not a separator.
+ || (!spacingAndPunctuations.isWordSeparator(codePoint)
+ && ScriptUtils.isLetterPartOfScript(codePoint, scriptId));
+ }
+
/**
* Returns the text surrounding the cursor.
*
- * @param sortedSeparators a sorted array of code points that split words.
+ * @param spacingAndPunctuations the rules for spacing and punctuation
* @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_*
* @return a range containing the text surrounding the cursor
*/
- public TextRange getWordRangeAtCursor(final int[] sortedSeparators, final int scriptId) {
+ public TextRange getWordRangeAtCursor(final SpacingAndPunctuations spacingAndPunctuations,
+ final int scriptId) {
mIC = mParent.getCurrentInputConnection();
if (mIC == null) {
return null;
@@ -647,8 +660,7 @@ public final class RichInputConnection {
int startIndexInBefore = before.length();
while (startIndexInBefore > 0) {
final int codePoint = Character.codePointBefore(before, startIndexInBefore);
- if (isSeparator(codePoint, sortedSeparators)
- || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) {
+ if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
break;
}
--startIndexInBefore;
@@ -661,8 +673,7 @@ public final class RichInputConnection {
int endIndexInAfter = -1;
while (++endIndexInAfter < after.length()) {
final int codePoint = Character.codePointAt(after, endIndexInAfter);
- if (isSeparator(codePoint, sortedSeparators)
- || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) {
+ if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) {
break;
}
if (Character.isSupplementaryCodePoint(codePoint)) {
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 89aea4310..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)) {
@@ -1478,8 +1436,7 @@ public final class InputLogic {
return;
}
final TextRange range = mConnection.getWordRangeAtCursor(
- settingsValues.mSpacingAndPunctuations.mSortedWordSeparators,
- currentKeyboardScriptId);
+ settingsValues.mSpacingAndPunctuations, currentKeyboardScriptId);
if (null == range) return; // Happens if we don't have an input connection at all
if (range.length() <= 0) {
// Race condition, or touching a word in a non-supported script.
@@ -1583,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) {
@@ -1609,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;
@@ -1646,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();
+ }
}
/**
@@ -2086,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.
@@ -2097,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,
@@ -2123,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
@@ -2207,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());
}
/**
@@ -2223,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;
@@ -2233,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;
}
@@ -2255,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) {
@@ -2279,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.
@@ -2293,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 9d3c27bbe..0de2d8831 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin.settings;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.preference.PreferenceManager;
@@ -41,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";
@@ -68,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 =
@@ -368,6 +373,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, false);
}
+ public static boolean readHasHardwareKeyboard(final Configuration conf) {
+ // The standard way of finding out whether we have a hardware keyboard. This code is taken
+ // from InputMethodService#onEvaluateInputShown, which canonically determines this.
+ // In a nutshell, we have a keyboard if the configuration says the type of hardware keyboard
+ // is NOKEYS and if it's not hidden (e.g. folded inside the device).
+ return conf.keyboard != Configuration.KEYBOARD_NOKEYS
+ && conf.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_YES;
+ }
+
public static boolean isInternal(final SharedPreferences prefs) {
return prefs.getBoolean(PREF_KEY_IS_INTERNAL, false);
}
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 1da112bf6..d8c548d8b 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -52,7 +52,10 @@ public final class SettingsValues {
public final SpacingAndPunctuations mSpacingAndPunctuations;
public final int mDelayInMillisecondsToUpdateOldSuggestions;
public final long mDoubleSpacePeriodTimeout;
-
+ // From configuration:
+ public final Locale mLocale;
+ public final boolean mHasHardwareKeyboard;
+ public final int mDisplayOrientation;
// From preferences, in the same order as xml/prefs.xml:
public final boolean mAutoCap;
public final boolean mVibrateOn;
@@ -73,7 +76,6 @@ public final class SettingsValues {
public final boolean mSlidingKeyInputPreviewEnabled;
public final boolean mPhraseGestureEnabled;
public final int mKeyLongpressTimeout;
- public final Locale mLocale;
public final boolean mEnableMetricsLogging;
public final boolean mShouldShowUiToAcceptTypedWord;
@@ -88,7 +90,6 @@ public final class SettingsValues {
public final float mAutoCorrectionThreshold;
public final boolean mAutoCorrectionEnabledPerUserSettings;
private final boolean mSuggestionsEnabledPerUserSettings;
- public final int mDisplayOrientation;
private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
// Setting values for additional features
@@ -96,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;
@@ -147,11 +145,13 @@ 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);
mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
+ mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration());
mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
mShouldShowUiToAcceptTypedWord = Settings.HAS_UI_TO_ACCEPT_TYPED_WORD
&& prefs.getBoolean(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD, true);
@@ -172,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);
@@ -423,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/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
index b8d2a2248..49d81104d 100644
--- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
+++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin.settings;
import android.content.res.Resources;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.internal.MoreKeySpec;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.PunctuationSuggestions;
@@ -68,6 +69,22 @@ public final class SpacingAndPunctuations {
mSuggestPuncList = PunctuationSuggestions.newPunctuationSuggestions(suggestPuncsSpec);
}
+ @UsedForTesting
+ public SpacingAndPunctuations(final SpacingAndPunctuations model,
+ final int[] overrideSortedWordSeparators) {
+ mSortedSymbolsPrecededBySpace = model.mSortedSymbolsPrecededBySpace;
+ mSortedSymbolsFollowedBySpace = model.mSortedSymbolsFollowedBySpace;
+ mSortedSymbolsClusteringTogether = model.mSortedSymbolsClusteringTogether;
+ mSortedWordConnectors = model.mSortedWordConnectors;
+ mSortedWordSeparators = overrideSortedWordSeparators;
+ mSuggestPuncList = model.mSuggestPuncList;
+ mSentenceSeparator = model.mSentenceSeparator;
+ mSentenceSeparatorAndSpace = model.mSentenceSeparatorAndSpace;
+ mCurrentLanguageHasSpaces = model.mCurrentLanguageHasSpaces;
+ mUsesAmericanTypography = model.mUsesAmericanTypography;
+ mUsesGermanRules = model.mUsesGermanRules;
+ }
+
public boolean isWordSeparator(final int code) {
return Arrays.binarySearch(mSortedWordSeparators, code) >= 0;
}
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/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
new file mode 100644
index 000000000..9dc0524a2
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java
@@ -0,0 +1,236 @@
+/*
+ * 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.utils;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.inputmethodservice.ExtractEditText;
+import android.inputmethodservice.InputMethodService;
+import android.text.Layout;
+import android.text.Spannable;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.widget.TextView;
+
+/**
+ * This class allows input methods to extract {@link CursorAnchorInfo} directly from the given
+ * {@link TextView}. This is useful and even necessary to support full-screen mode where the default
+ * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} event callback must be
+ * ignored because it reports the character locations of the target application rather than
+ * characters on {@link ExtractEditText}.
+ */
+public final class CursorAnchorInfoUtils {
+ private CursorAnchorInfoUtils() {
+ // This helper class is not instantiable.
+ }
+
+ private static boolean isPositionVisible(final View view, final float positionX,
+ final float positionY) {
+ final float[] position = new float[] { positionX, positionY };
+ View currentView = view;
+
+ while (currentView != null) {
+ if (currentView != view) {
+ // Local scroll is already taken into account in positionX/Y
+ position[0] -= currentView.getScrollX();
+ position[1] -= currentView.getScrollY();
+ }
+
+ if (position[0] < 0 || position[1] < 0 ||
+ position[0] > currentView.getWidth() || position[1] > currentView.getHeight()) {
+ return false;
+ }
+
+ if (!currentView.getMatrix().isIdentity()) {
+ currentView.getMatrix().mapPoints(position);
+ }
+
+ position[0] += currentView.getLeft();
+ position[1] += currentView.getTop();
+
+ final ViewParent parent = currentView.getParent();
+ if (parent instanceof View) {
+ currentView = (View) parent;
+ } else {
+ // We've reached the ViewRoot, stop iterating
+ currentView = null;
+ }
+ }
+
+ // We've been able to walk up the view hierarchy and the position was never clipped
+ return true;
+ }
+
+ /**
+ * Returns {@link CursorAnchorInfo} from the given {@link TextView}.
+ * @param textView the target text view from which {@link CursorAnchorInfo} is to be extracted.
+ * @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it
+ * is not feasible.
+ */
+ public static CursorAnchorInfo getCursorAnchorInfo(final TextView textView) {
+ Layout layout = textView.getLayout();
+ if (layout == null) {
+ return null;
+ }
+
+ final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
+
+ final int selectionStart = textView.getSelectionStart();
+ builder.setSelectionRange(selectionStart, textView.getSelectionEnd());
+
+ // Construct transformation matrix from view local coordinates to screen coordinates.
+ final Matrix viewToScreenMatrix = new Matrix(textView.getMatrix());
+ final int[] viewOriginInScreen = new int[2];
+ textView.getLocationOnScreen(viewOriginInScreen);
+ viewToScreenMatrix.postTranslate(viewOriginInScreen[0], viewOriginInScreen[1]);
+ builder.setMatrix(viewToScreenMatrix);
+
+ if (layout.getLineCount() == 0) {
+ return null;
+ }
+ final Rect lineBoundsWithoutOffset = new Rect();
+ final Rect lineBoundsWithOffset = new Rect();
+ layout.getLineBounds(0, lineBoundsWithoutOffset);
+ textView.getLineBounds(0, lineBoundsWithOffset);
+ final float viewportToContentHorizontalOffset = lineBoundsWithOffset.left
+ - lineBoundsWithoutOffset.left - textView.getScrollX();
+ final float viewportToContentVerticalOffset = lineBoundsWithOffset.top
+ - lineBoundsWithoutOffset.top - textView.getScrollY();
+
+ final CharSequence text = textView.getText();
+ if (text instanceof Spannable) {
+ // Here we assume that the composing text is marked as SPAN_COMPOSING flag. This is not
+ // necessarily true, but basically works.
+ int composingTextStart = text.length();
+ int composingTextEnd = 0;
+ final Spannable spannable = (Spannable) text;
+ final Object[] spans = spannable.getSpans(0, text.length(), Object.class);
+ for (Object span : spans) {
+ final int spanFlag = spannable.getSpanFlags(span);
+ if ((spanFlag & Spannable.SPAN_COMPOSING) != 0) {
+ composingTextStart = Math.min(composingTextStart,
+ spannable.getSpanStart(span));
+ composingTextEnd = Math.max(composingTextEnd, spannable.getSpanEnd(span));
+ }
+ }
+
+ final boolean hasComposingText =
+ (0 <= composingTextStart) && (composingTextStart < composingTextEnd);
+ if (hasComposingText) {
+ final CharSequence composingText = text.subSequence(composingTextStart,
+ composingTextEnd);
+ builder.setComposingText(composingTextStart, composingText);
+
+ final int minLine = layout.getLineForOffset(composingTextStart);
+ final int maxLine = layout.getLineForOffset(composingTextEnd - 1);
+ for (int line = minLine; line <= maxLine; ++line) {
+ final int lineStart = layout.getLineStart(line);
+ final int lineEnd = layout.getLineEnd(line);
+ final int offsetStart = Math.max(lineStart, composingTextStart);
+ final int offsetEnd = Math.min(lineEnd, composingTextEnd);
+ final boolean ltrLine =
+ layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
+ final float[] widths = new float[offsetEnd - offsetStart];
+ layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths);
+ final float top = layout.getLineTop(line);
+ final float bottom = layout.getLineBottom(line);
+ for (int offset = offsetStart; offset < offsetEnd; ++offset) {
+ final float charWidth = widths[offset - offsetStart];
+ final boolean isRtl = layout.isRtlCharAt(offset);
+ final float primary = layout.getPrimaryHorizontal(offset);
+ final float secondary = layout.getSecondaryHorizontal(offset);
+ // TODO: This doesn't work perfectly for text with custom styles and TAB
+ // chars.
+ final float left;
+ final float right;
+ if (ltrLine) {
+ if (isRtl) {
+ left = secondary - charWidth;
+ right = secondary;
+ } else {
+ left = primary;
+ right = primary + charWidth;
+ }
+ } else {
+ if (!isRtl) {
+ left = secondary;
+ right = secondary + charWidth;
+ } else {
+ left = primary - charWidth;
+ right = primary;
+ }
+ }
+ // TODO: Check top-right and bottom-left as well.
+ final float localLeft = left + viewportToContentHorizontalOffset;
+ final float localRight = right + viewportToContentHorizontalOffset;
+ final float localTop = top + viewportToContentVerticalOffset;
+ final float localBottom = bottom + viewportToContentVerticalOffset;
+ final boolean isTopLeftVisible = isPositionVisible(textView,
+ localLeft, localTop);
+ final boolean isBottomRightVisible =
+ isPositionVisible(textView, localRight, localBottom);
+ int characterBoundsFlags = 0;
+ if (isTopLeftVisible || isBottomRightVisible) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+ }
+ if (!isTopLeftVisible || !isTopLeftVisible) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+ }
+ if (isRtl) {
+ characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+ }
+ // Here offset is the index in Java chars.
+ builder.addCharacterBounds(offset, localLeft, localTop, localRight,
+ localBottom, characterBoundsFlags);
+ }
+ }
+ }
+ }
+
+ // Treat selectionStart as the insertion point.
+ if (0 <= selectionStart) {
+ final int offset = selectionStart;
+ final int line = layout.getLineForOffset(offset);
+ final float insertionMarkerX = layout.getPrimaryHorizontal(offset)
+ + viewportToContentHorizontalOffset;
+ final float insertionMarkerTop = layout.getLineTop(line)
+ + viewportToContentVerticalOffset;
+ final float insertionMarkerBaseline = layout.getLineBaseline(line)
+ + viewportToContentVerticalOffset;
+ final float insertionMarkerBottom = layout.getLineBottom(line)
+ + viewportToContentVerticalOffset;
+ final boolean isTopVisible =
+ isPositionVisible(textView, insertionMarkerX, insertionMarkerTop);
+ final boolean isBottomVisible =
+ isPositionVisible(textView, insertionMarkerX, insertionMarkerBottom);
+ int insertionMarkerFlags = 0;
+ if (isTopVisible || isBottomVisible) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+ }
+ if (!isTopVisible || !isBottomVisible) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+ }
+ if (layout.isRtlCharAt(offset)) {
+ insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+ }
+ builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
+ insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags);
+ }
+ return builder.build();
+ }
+}
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