aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java18
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java47
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java23
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecorator.java294
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java35
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java9
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java5
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java41
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java9
-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.java210
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java193
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java8
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java2
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java5
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java15
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java11
-rw-r--r--java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java55
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FragmentUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java34
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SuggestionResults.java13
25 files changed, 665 insertions, 402 deletions
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
index 8a2818508..c937eeeaa 100644
--- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java
@@ -41,6 +41,8 @@ public final class CursorAnchorInfoCompatWrapper {
// Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX).
private static final CompatUtils.ClassWrapper sCursorAnchorInfoClass;
+ private static final CompatUtils.ToIntMethodWrapper sGetSelectionStartMethod;
+ private static final CompatUtils.ToIntMethodWrapper sGetSelectionEndMethod;
private static final CompatUtils.ToObjectMethodWrapper<RectF> sGetCharacterBoundsMethod;
private static final CompatUtils.ToIntMethodWrapper sGetCharacterBoundsFlagsMethod;
private static final CompatUtils.ToObjectMethodWrapper<CharSequence> sGetComposingTextMethod;
@@ -52,10 +54,14 @@ public final class CursorAnchorInfoCompatWrapper {
private static final CompatUtils.ToObjectMethodWrapper<Matrix> sGetMatrixMethod;
private static final CompatUtils.ToIntMethodWrapper sGetInsertionMarkerFlagsMethod;
- private static int COMPOSING_TEXT_START_DEFAULT = -1;
+ private static int INVALID_TEXT_INDEX = -1;
static {
sCursorAnchorInfoClass = CompatUtils.getClassWrapper(
"android.view.inputmethod.CursorAnchorInfo");
+ sGetSelectionStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
+ "getSelectionStart", INVALID_TEXT_INDEX);
+ sGetSelectionEndMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
+ "getSelectionEnd", INVALID_TEXT_INDEX);
sGetCharacterBoundsMethod = sCursorAnchorInfoClass.getMethod(
"getCharacterBounds", (RectF)null, int.class);
sGetCharacterBoundsFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
@@ -63,7 +69,7 @@ public final class CursorAnchorInfoCompatWrapper {
sGetComposingTextMethod = sCursorAnchorInfoClass.getMethod(
"getComposingText", (CharSequence)null);
sGetComposingTextStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
- "getComposingTextStart", COMPOSING_TEXT_START_DEFAULT);
+ "getComposingTextStart", INVALID_TEXT_INDEX);
sGetInsertionMarkerBaselineMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
"getInsertionMarkerBaseline", 0.0f);
sGetInsertionMarkerBottomMethod = sCursorAnchorInfoClass.getPrimitiveMethod(
@@ -105,6 +111,14 @@ public final class CursorAnchorInfoCompatWrapper {
return FakeHolder.sInstance;
}
+ public int getSelectionStart() {
+ return sGetSelectionStartMethod.invoke(mInstance);
+ }
+
+ public int getSelectionEnd() {
+ return sGetSelectionEndMethod.invoke(mInstance);
+ }
+
public CharSequence getComposingText() {
return sGetComposingTextMethod.invoke(mInstance);
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 99a34bec7..863a8b7ad 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -98,6 +98,16 @@ public class Key implements Comparable<Key> {
private final int mWidth;
/** Height of the key, excluding the gap */
private final int mHeight;
+ /**
+ * The combined width in pixels of the horizontal gaps belonging to this key, both to the left
+ * and to the right. I.e., mWidth + mHorizontalGap = total width belonging to the key.
+ */
+ private final int mHorizontalGap;
+ /**
+ * The combined height in pixels of the vertical gaps belonging to this key, both above and
+ * below. I.e., mHeight + mVerticalGap = total height belonging to the key.
+ */
+ private final int mVerticalGap;
/** X coordinate of the top-left corner of the key in the keyboard layout, excluding the gap. */
private final int mX;
/** Y coordinate of the top-left corner of the key in the keyboard layout, excluding the gap. */
@@ -198,8 +208,10 @@ public class Key implements Comparable<Key> {
final String hintLabel, final int labelFlags, final int backgroundType, final int x,
final int y, final int width, final int height, final int horizontalGap,
final int verticalGap) {
- mHeight = height - verticalGap;
mWidth = width - horizontalGap;
+ mHeight = height - verticalGap;
+ mHorizontalGap = horizontalGap;
+ mVerticalGap = verticalGap;
mHintLabel = hintLabel;
mLabelFlags = labelFlags;
mBackgroundType = backgroundType;
@@ -214,7 +226,7 @@ public class Key implements Comparable<Key> {
mEnabled = (code != CODE_UNSPECIFIED);
mIconId = iconId;
// Horizontal gap is divided equally to both sides of the key.
- mX = x + horizontalGap / 2;
+ mX = x + mHorizontalGap / 2;
mY = y;
mHitBox.set(x, y, x + width + 1, y + height);
mKeyVisualAttributes = null;
@@ -235,18 +247,21 @@ public class Key implements Comparable<Key> {
*/
public Key(final String keySpec, final TypedArray keyAttr, final KeyStyle style,
final KeyboardParams params, final KeyboardRow row) {
- final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
+ mHorizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
+ mVerticalGap = params.mVerticalGap;
+
+ final float horizontalGapFloat = mHorizontalGap;
final int rowHeight = row.getRowHeight();
- mHeight = rowHeight - params.mVerticalGap;
+ mHeight = rowHeight - mVerticalGap;
final float keyXPos = row.getKeyX(keyAttr);
final float keyWidth = row.getKeyWidth(keyAttr, keyXPos);
final int keyYPos = row.getKeyY();
// Horizontal gap is divided equally to both sides of the key.
- mX = Math.round(keyXPos + horizontalGap / 2);
+ mX = Math.round(keyXPos + horizontalGapFloat / 2);
mY = keyYPos;
- mWidth = Math.round(keyWidth - horizontalGap);
+ mWidth = Math.round(keyWidth - horizontalGapFloat);
mHitBox.set(Math.round(keyXPos), keyYPos, Math.round(keyXPos + keyWidth) + 1,
keyYPos + rowHeight);
// Update row to have current x coordinate.
@@ -388,6 +403,8 @@ public class Key implements Comparable<Key> {
mIconId = key.mIconId;
mWidth = key.mWidth;
mHeight = key.mHeight;
+ mHorizontalGap = key.mHorizontalGap;
+ mVerticalGap = key.mVerticalGap;
mX = key.mX;
mY = key.mY;
mHitBox.set(key.mHitBox);
@@ -788,6 +805,24 @@ public class Key implements Comparable<Key> {
}
/**
+ * The combined width in pixels of the horizontal gaps belonging to this key, both above and
+ * below. I.e., getWidth() + getHorizontalGap() = total width belonging to the key.
+ * @return Horizontal gap belonging to this key.
+ */
+ public int getHorizontalGap() {
+ return mHorizontalGap;
+ }
+
+ /**
+ * The combined height in pixels of the vertical gaps belonging to this key, both above and
+ * below. I.e., getHeight() + getVerticalGap() = total height belonging to the key.
+ * @return Vertical gap belonging to this key.
+ */
+ public int getVerticalGap() {
+ return mVerticalGap;
+ }
+
+ /**
* Gets the x-coordinate of the top-left corner of the key in pixels, excluding the gap.
* @return The x-coordinate of the top-left corner of the key in pixels, excluding the gap.
*/
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 43c61443e..f9cf3535e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -77,8 +77,7 @@ public final class KeyboardId {
private final int mHashCode;
- public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params,
- boolean isSplitLayout) {
+ public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) {
mSubtype = params.mSubtype;
mLocale = SubtypeLocaleUtils.getSubtypeLocale(mSubtype);
mWidth = params.mKeyboardWidth;
@@ -91,7 +90,7 @@ public final class KeyboardId {
mCustomActionLabel = (mEditorInfo.actionLabel != null)
? mEditorInfo.actionLabel.toString() : null;
mHasShortcutKey = params.mVoiceInputKeyEnabled;
- mIsSplitLayout = isSplitLayout;
+ mIsSplitLayout = params.mIsSplitLayoutEnabled;
mHashCode = computeHashCode(this);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 1dbecdc81..47fb7b320 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -115,6 +115,12 @@ public final class KeyboardLayoutSet {
int mKeyboardWidth;
int mKeyboardHeight;
int mScriptId = ScriptUtils.SCRIPT_LATIN;
+ // Indicates if the user has enabled the split-layout preference
+ // and the required ProductionFlags are enabled.
+ boolean mIsSplitLayoutEnabledByUser;
+ // Indicates if split layout is actually enabled, taking into account
+ // whether the user has enabled it, and the keyboard layout supports it.
+ boolean mIsSplitLayoutEnabled;
// Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
new SparseArray<>();
@@ -170,9 +176,9 @@ public final class KeyboardLayoutSet {
// specified as an elementKeyboard attribute in the file.
// The KeyboardId is an internal key for a Keyboard object.
- // TODO: AND mSupportsSplitLayout with the user preference that forces a split.
- final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams,
- elementParams.mSupportsSplitLayout);
+ mParams.mIsSplitLayoutEnabled = mParams.mIsSplitLayoutEnabledByUser
+ && elementParams.mSupportsSplitLayout;
+ final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams);
try {
return getKeyboard(elementParams, id);
} catch (final RuntimeException e) {
@@ -290,12 +296,19 @@ public final class KeyboardLayoutSet {
return this;
}
- public void disableTouchPositionCorrectionData() {
+ public Builder disableTouchPositionCorrectionData() {
mParams.mDisableTouchPositionCorrectionDataForTest = true;
+ return this;
}
- public void setScriptId(final int scriptId) {
+ public Builder setScriptId(final int scriptId) {
mParams.mScriptId = scriptId;
+ return this;
+ }
+
+ public Builder setSplitLayoutEnabledByUser(final boolean enabled) {
+ mParams.mIsSplitLayoutEnabledByUser = enabled;
+ return this;
}
public KeyboardLayoutSet build() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 6cd7955ce..246d11bac 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -36,6 +36,7 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.define.ProductionFlags;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.utils.ResourceUtils;
@@ -114,6 +115,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
+ builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
+ && settingsValues.mIsSplitKeyboardEnabled);
mKeyboardLayoutSet = builder.build();
try {
mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
index f614b2257..315d36313 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -17,23 +17,22 @@
package com.android.inputmethod.keyboard;
import android.graphics.Matrix;
-import android.graphics.PointF;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
import javax.annotation.Nonnull;
/**
- * A controller class of commit/add-to-dictionary indicator (a.k.a. TextDecorator). This class
+ * A controller class of the add-to-dictionary indicator (a.k.a. TextDecorator). This class
* is designed to be independent of UI subsystems such as {@link View}. All the UI related
* operations are delegated to {@link TextDecoratorUi} via {@link TextDecoratorUiOperator}.
*/
@@ -41,18 +40,22 @@ public class TextDecorator {
private static final String TAG = TextDecorator.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final int MODE_NONE = 0;
- private static final int MODE_COMMIT = 1;
- private static final int MODE_ADD_TO_DICTIONARY = 2;
+ private static final int INVALID_CURSOR_INDEX = -1;
- private int mMode = MODE_NONE;
+ private static final int MODE_MONITOR = 0;
+ private static final int MODE_WAITING_CURSOR_INDEX = 1;
+ private static final int MODE_SHOWING_INDICATOR = 2;
- private final PointF mLocalOrigin = new PointF();
- private final RectF mRelativeIndicatorBounds = new RectF();
- private final RectF mRelativeComposingTextBounds = new RectF();
+ private int mMode = MODE_MONITOR;
+
+ private String mLastComposingText = null;
+ private RectF mIndicatorBoundsForLastComposingText = new RectF();
+ private RectF mComposingTextBoundsForLastComposingText = new RectF();
private boolean mIsFullScreenMode = false;
- private SuggestedWordInfo mWaitingWord = null;
+ private String mWaitingWord = null;
+ private int mWaitingCursorStart = INVALID_CURSOR_INDEX;
+ private int mWaitingCursorEnd = INVALID_CURSOR_INDEX;
private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null;
@Nonnull
@@ -63,16 +66,10 @@ public class TextDecorator {
public interface Listener {
/**
- * Called when the user clicks the composing text to commit.
- * @param wordInfo the suggested word which the user clicked on.
+ * Called when the user clicks the indicator to add the word into the dictionary.
+ * @param word the word which the user clicked on.
*/
- void onClickComposingTextToCommit(final SuggestedWordInfo wordInfo);
-
- /**
- * Called when the user clicks the composing text to add the word into the dictionary.
- * @param wordInfo the suggested word which the user clicked on.
- */
- void onClickComposingTextToAddToDictionary(final SuggestedWordInfo wordInfo);
+ void onClickComposingTextToAddToDictionary(final String word);
}
public TextDecorator(final Listener listener) {
@@ -103,46 +100,19 @@ public class TextDecorator {
}
/**
- * Shows the "Commit" indicator and associates it with the given suggested word.
- *
- * <p>The effect of {@link #showCommitIndicator(SuggestedWordInfo)} and
- * {@link #showAddToDictionaryIndicator(SuggestedWordInfo)} are exclusive to each other. Call
- * {@link #reset()} to hide the indicator.</p>
- *
- * @param wordInfo the suggested word which should be associated with the indicator. This object
- * will be passed back in {@link Listener#onClickComposingTextToCommit(SuggestedWordInfo)}
- */
- public void showCommitIndicator(final SuggestedWordInfo wordInfo) {
- if (mMode == MODE_COMMIT && wordInfo != null &&
- TextUtils.equals(mWaitingWord.mWord, wordInfo.mWord)) {
- // Skip layout for better performance.
- return;
- }
- mWaitingWord = wordInfo;
- mMode = MODE_COMMIT;
- layoutLater();
- }
-
- /**
- * Shows the "Add to dictionary" indicator and associates it with associating the given
- * suggested word.
- *
- * <p>The effect of {@link #showCommitIndicator(SuggestedWordInfo)} and
- * {@link #showAddToDictionaryIndicator(SuggestedWordInfo)} are exclusive to each other. Call
- * {@link #reset()} to hide the indicator.</p>
+ * Shows the "Add to dictionary" indicator and associates it with associating the given word.
*
- * @param wordInfo the suggested word which should be associated with the indicator. This object
- * will be passed back in
- * {@link Listener#onClickComposingTextToAddToDictionary(SuggestedWordInfo)}.
+ * @param word the word which should be associated with the indicator. This object will be
+ * passed back in {@link Listener#onClickComposingTextToAddToDictionary(String)}.
+ * @param selectionStart the cursor index (inclusive) when the indicator should be displayed.
+ * @param selectionEnd the cursor index (exclusive) when the indicator should be displayed.
*/
- public void showAddToDictionaryIndicator(final SuggestedWordInfo wordInfo) {
- if (mMode == MODE_ADD_TO_DICTIONARY && wordInfo != null &&
- TextUtils.equals(mWaitingWord.mWord, wordInfo.mWord)) {
- // Skip layout for better performance.
- return;
- }
- mWaitingWord = wordInfo;
- mMode = MODE_ADD_TO_DICTIONARY;
+ public void showAddToDictionaryIndicator(final String word, final int selectionStart,
+ final int selectionEnd) {
+ mWaitingWord = word;
+ mWaitingCursorStart = selectionStart;
+ mWaitingCursorEnd = selectionEnd;
+ mMode = MODE_WAITING_CURSOR_INDEX;
layoutLater();
return;
}
@@ -165,18 +135,19 @@ public class TextDecorator {
*/
public void reset() {
mWaitingWord = null;
- mMode = MODE_NONE;
- mLocalOrigin.set(0.0f, 0.0f);
- mRelativeIndicatorBounds.set(0.0f, 0.0f, 0.0f, 0.0f);
- mRelativeComposingTextBounds.set(0.0f, 0.0f, 0.0f, 0.0f);
+ mMode = MODE_MONITOR;
+ mWaitingCursorStart = INVALID_CURSOR_INDEX;
+ mWaitingCursorEnd = INVALID_CURSOR_INDEX;
cancelLayoutInternalExpectedly("Resetting internal state.");
}
/**
- * Must be called when the {@link InputMethodService#onUpdateCursorAnchorInfo()} is called.
+ * Must be called when the {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}
+ * is called.
*
* <p>CAVEAT: Currently the input method author is responsible for ignoring
- * {@link InputMethodService#onUpdateCursorAnchorInfo()} called in full screen mode.</p>
+ * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} called in full screen
+ * mode.</p>
* @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}.
*/
public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
@@ -185,29 +156,6 @@ public class TextDecorator {
layoutImmediately();
}
- /**
- * Hides indicator if the new composing text doesn't match the expected one.
- *
- * <p>Calling this method is optional but recommended whenever the new composition is passed to
- * the application. The motivation of this method is to reduce the UI latency. With this method,
- * we can hide the indicator without waiting the arrival of the
- * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} callback, assuming that
- * the application accepts the new composing text without any modification. Even if this
- * assumption is false, the indicator will be shown again when
- * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is actually received.
- * </p>
- *
- * @param newComposingText the new composing text that is being passed to the application.
- */
- public void hideIndicatorIfNecessary(final CharSequence newComposingText) {
- if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) {
- return;
- }
- if (!TextUtils.equals(newComposingText, mWaitingWord.mWord)) {
- mUiOperator.hideUi();
- }
- }
-
private void cancelLayoutInternalUnexpectedly(final String message) {
mUiOperator.hideUi();
Log.d(TAG, message);
@@ -232,15 +180,6 @@ public class TextDecorator {
}
private void layoutMain() {
- if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) {
- if (mMode == MODE_NONE) {
- cancelLayoutInternalExpectedly("Not ready for layouting.");
- } else {
- cancelLayoutInternalUnexpectedly("Unknown mMode=" + mMode);
- }
- return;
- }
-
final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper;
if (info == null) {
@@ -254,104 +193,117 @@ public class TextDecorator {
}
final CharSequence composingText = info.getComposingText();
- if (mMode == MODE_COMMIT) {
- if (composingText == null) {
- cancelLayoutInternalExpectedly("composingText is null.");
- return;
- }
+ if (!TextUtils.isEmpty(composingText)) {
final int composingTextStart = info.getComposingTextStart();
final int lastCharRectIndex = composingTextStart + composingText.length() - 1;
final RectF lastCharRect = info.getCharacterBounds(lastCharRectIndex);
- final int lastCharRectFlag = info.getCharacterBoundsFlags(lastCharRectIndex);
+ final int lastCharRectFlags = info.getCharacterBoundsFlags(lastCharRectIndex);
final boolean hasInvisibleRegionInLastCharRect =
- (lastCharRectFlag & CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION)
+ (lastCharRectFlags & CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION)
!= 0;
if (lastCharRect == null || matrix == null || hasInvisibleRegionInLastCharRect) {
mUiOperator.hideUi();
return;
}
- final RectF segmentStartCharRect = new RectF(lastCharRect);
- for (int i = composingText.length() - 2; i >= 0; --i) {
- final RectF charRect = info.getCharacterBounds(composingTextStart + i);
- if (charRect == null) {
+
+ // Note that the following layout information is fragile, and must be invalidated
+ // even when surrounding text next to the composing text is changed because it can
+ // affect how the composing text is rendered.
+ // TODO: Investigate if we can change the input logic to make the target text
+ // composing state so that we can retrieve the character bounds reliably.
+ final String composingTextString = composingText.toString();
+ final float top = lastCharRect.top;
+ final float bottom = lastCharRect.bottom;
+ float left = lastCharRect.left;
+ float right = lastCharRect.right;
+ boolean useRtlLayout = false;
+ for (int i = composingText.length() - 1; i >= 0; --i) {
+ final int characterIndex = composingTextStart + i;
+ final RectF characterBounds = info.getCharacterBounds(characterIndex);
+ final int characterBoundsFlags = info.getCharacterBoundsFlags(characterIndex);
+ if (characterBounds == null) {
break;
}
- if (charRect.top != segmentStartCharRect.top) {
+ if (characterBounds.top != top) {
break;
}
- if (charRect.bottom != segmentStartCharRect.bottom) {
+ if (characterBounds.bottom != bottom) {
break;
}
- segmentStartCharRect.set(charRect);
- }
-
- mLocalOrigin.set(lastCharRect.right, lastCharRect.top);
- mRelativeIndicatorBounds.set(lastCharRect.right, lastCharRect.top,
- lastCharRect.right + lastCharRect.height(), lastCharRect.bottom);
- mRelativeIndicatorBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
-
- mRelativeIndicatorBounds.set(lastCharRect.right, lastCharRect.top,
- lastCharRect.right + lastCharRect.height(), lastCharRect.bottom);
- mRelativeIndicatorBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
-
- mRelativeComposingTextBounds.set(segmentStartCharRect.left, segmentStartCharRect.top,
- segmentStartCharRect.right, segmentStartCharRect.bottom);
- mRelativeComposingTextBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
-
- if (mWaitingWord == null) {
- cancelLayoutInternalExpectedly("mWaitingText is null.");
- return;
- }
- if (TextUtils.isEmpty(mWaitingWord.mWord)) {
- cancelLayoutInternalExpectedly("mWaitingText.mWord is empty.");
- return;
+ if ((characterBoundsFlags & CursorAnchorInfoCompatWrapper.FLAG_IS_RTL) != 0) {
+ // This is for both RTL text and bi-directional text. RTL languages usually mix
+ // RTL characters with LTR characters and in this case we should display the
+ // indicator on the left, while in LTR languages that normally never happens.
+ // TODO: Try to come up with a better algorithm.
+ useRtlLayout = true;
+ }
+ left = Math.min(characterBounds.left, left);
+ right = Math.max(characterBounds.right, right);
}
- if (!TextUtils.equals(composingText, mWaitingWord.mWord)) {
- // This is indeed an expected situation because of the asynchronous nature of
- // input method framework in Android. Note that composingText is notified from the
- // application, while mWaitingWord.mWord is obtained directly from the InputLogic.
- cancelLayoutInternalExpectedly(
- "Composing text doesn't match the one we are waiting for.");
- return;
+ mLastComposingText = composingTextString;
+ mComposingTextBoundsForLastComposingText.set(left, top, right, bottom);
+ // The height and width of the indicator is the same as the height of the composing
+ // text.
+ final float indicatorSize = bottom - top;
+ mIndicatorBoundsForLastComposingText.set(0.0f, 0.0f, indicatorSize, indicatorSize);
+ // The horizontal position of the indicator depends on the text direction.
+ final float indicatorTop = top;
+ final float indicatorLeft;
+ if (useRtlLayout) {
+ indicatorLeft = left - indicatorSize;
+ } else {
+ indicatorLeft = right;
}
- } else {
- if (!mIsFullScreenMode && !TextUtils.isEmpty(composingText)) {
- // This is an unexpected case.
- // TODO: Document this.
+ mIndicatorBoundsForLastComposingText.offset(indicatorLeft, indicatorTop);
+ }
+
+ final int selectionStart = info.getSelectionStart();
+ final int selectionEnd = info.getSelectionEnd();
+ switch (mMode) {
+ case MODE_MONITOR:
mUiOperator.hideUi();
return;
- }
- // In MODE_ADD_TO_DICTIONARY, we cannot retrieve the character position at all because
- // of the lack of composing text. We will use the insertion marker position instead.
- if ((info.getInsertionMarkerFlags() &
- CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION) != 0) {
- mUiOperator.hideUi();
+ case MODE_WAITING_CURSOR_INDEX:
+ if (selectionStart != mWaitingCursorStart || selectionEnd != mWaitingCursorEnd) {
+ mUiOperator.hideUi();
+ return;
+ }
+ mMode = MODE_SHOWING_INDICATOR;
+ break;
+ case MODE_SHOWING_INDICATOR:
+ if (selectionStart != mWaitingCursorStart || selectionEnd != mWaitingCursorEnd) {
+ mUiOperator.hideUi();
+ mMode = MODE_MONITOR;
+ mWaitingCursorStart = INVALID_CURSOR_INDEX;
+ mWaitingCursorEnd = INVALID_CURSOR_INDEX;
+ return;
+ }
+ break;
+ default:
+ cancelLayoutInternalUnexpectedly("Unexpected internal mode=" + mMode);
return;
- }
- final float insertionMarkerHolizontal = info.getInsertionMarkerHorizontal();
- final float insertionMarkerTop = info.getInsertionMarkerTop();
- mLocalOrigin.set(insertionMarkerHolizontal, insertionMarkerTop);
}
- final RectF indicatorBounds = new RectF(mRelativeIndicatorBounds);
- final RectF composingTextBounds = new RectF(mRelativeComposingTextBounds);
- indicatorBounds.offset(mLocalOrigin.x, mLocalOrigin.y);
- composingTextBounds.offset(mLocalOrigin.x, mLocalOrigin.y);
- mUiOperator.layoutUi(mMode == MODE_COMMIT, matrix, indicatorBounds, composingTextBounds);
+ if (!TextUtils.equals(mLastComposingText, mWaitingWord)) {
+ cancelLayoutInternalUnexpectedly("mLastComposingText doesn't match mWaitingWord");
+ return;
+ }
+
+ if ((info.getInsertionMarkerFlags() &
+ CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION) != 0) {
+ mUiOperator.hideUi();
+ return;
+ }
+
+ mUiOperator.layoutUi(matrix, mIndicatorBoundsForLastComposingText,
+ mComposingTextBoundsForLastComposingText);
}
private void onClickIndicator() {
- if (mWaitingWord == null || TextUtils.isEmpty(mWaitingWord.mWord)) {
+ if (mMode != MODE_SHOWING_INDICATOR) {
return;
}
- switch (mMode) {
- case MODE_COMMIT:
- mListener.onClickComposingTextToCommit(mWaitingWord);
- break;
- case MODE_ADD_TO_DICTIONARY:
- mListener.onClickComposingTextToAddToDictionary(mWaitingWord);
- break;
- }
+ mListener.onClickComposingTextToAddToDictionary(mWaitingWord);
}
private final LayoutInvalidator mLayoutInvalidator = new LayoutInvalidator(this);
@@ -407,10 +359,7 @@ public class TextDecorator {
private final static Listener EMPTY_LISTENER = new Listener() {
@Override
- public void onClickComposingTextToCommit(SuggestedWordInfo wordInfo) {
- }
- @Override
- public void onClickComposingTextToAddToDictionary(SuggestedWordInfo wordInfo) {
+ public void onClickComposingTextToAddToDictionary(final String word) {
}
};
@@ -425,8 +374,7 @@ public class TextDecorator {
public void setOnClickListener(Runnable listener) {
}
@Override
- public void layoutUi(boolean isCommitMode, Matrix matrix, RectF indicatorBounds,
- RectF composingTextBounds) {
+ public void layoutUi(Matrix matrix, RectF indicatorBounds, RectF composingTextBounds) {
}
};
}
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
index 6e215a9ca..b67d17789 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
@@ -46,7 +46,6 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
private static final int VISUAL_DEBUG_HIT_AREA_COLOR = 0x80ff8000;
private final RelativeLayout mLocalRootView;
- private final CommitIndicatorView mCommitIndicatorView;
private final AddToDictionaryIndicatorView mAddToDictionaryIndicatorView;
private final PopupWindow mTouchEventWindow;
private final View mTouchEventWindowClickListenerView;
@@ -73,9 +72,7 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
mLocalRootView.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
final ViewGroup contentView = getContentView(inputView);
- mCommitIndicatorView = new CommitIndicatorView(context);
mAddToDictionaryIndicatorView = new AddToDictionaryIndicatorView(context);
- mLocalRootView.addView(mCommitIndicatorView);
mLocalRootView.addView(mAddToDictionaryIndicatorView);
if (contentView != null) {
contentView.addView(mLocalRootView);
@@ -110,17 +107,15 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
@Override
public void hideUi() {
- mCommitIndicatorView.setVisibility(View.GONE);
mAddToDictionaryIndicatorView.setVisibility(View.GONE);
mTouchEventWindow.dismiss();
}
@Override
- public void layoutUi(final boolean isCommitMode, final Matrix matrix,
- final RectF indicatorBounds, final RectF composingTextBounds) {
+ public void layoutUi(final Matrix matrix, final RectF indicatorBounds,
+ final RectF composingTextBounds) {
final RectF indicatorBoundsInScreenCoordinates = new RectF();
matrix.mapRect(indicatorBoundsInScreenCoordinates, indicatorBounds);
- mCommitIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
mAddToDictionaryIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
final RectF hitAreaBounds = new RectF(composingTextBounds);
@@ -133,20 +128,9 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
mLocalRootView.getLocationOnScreen(originScreen);
final int viewOriginX = originScreen[0];
final int viewOriginY = originScreen[1];
-
- final View toBeShown;
- final View toBeHidden;
- if (isCommitMode) {
- toBeShown = mCommitIndicatorView;
- toBeHidden = mAddToDictionaryIndicatorView;
- } else {
- toBeShown = mAddToDictionaryIndicatorView;
- toBeHidden = mCommitIndicatorView;
- }
- toBeShown.setX(indicatorBoundsInScreenCoordinates.left - viewOriginX);
- toBeShown.setY(indicatorBoundsInScreenCoordinates.top - viewOriginY);
- toBeShown.setVisibility(View.VISIBLE);
- toBeHidden.setVisibility(View.GONE);
+ mAddToDictionaryIndicatorView.setX(indicatorBoundsInScreenCoordinates.left - viewOriginX);
+ mAddToDictionaryIndicatorView.setY(indicatorBoundsInScreenCoordinates.top - viewOriginY);
+ mAddToDictionaryIndicatorView.setVisibility(View.VISIBLE);
if (mTouchEventWindow.isShowing()) {
mTouchEventWindow.update((int)hitAreaBoundsInScreenCoordinates.left - viewOriginX,
@@ -239,15 +223,6 @@ public final class TextDecoratorUi implements TextDecoratorUiOperator {
return windowContentView;
}
- private static final class CommitIndicatorView extends TextDecoratorUi.IndicatorView {
- public CommitIndicatorView(final Context context) {
- super(context, R.array.text_decorator_commit_indicator_path,
- R.integer.text_decorator_commit_indicator_path_size,
- R.color.text_decorator_commit_indicator_background_color,
- R.color.text_decorator_commit_indicator_foreground_color);
- }
- }
-
private static final class AddToDictionaryIndicatorView extends TextDecoratorUi.IndicatorView {
public AddToDictionaryIndicatorView(final Context context) {
super(context, R.array.text_decorator_add_to_dictionary_indicator_path,
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
index f84e12d8c..9c0b64ad4 100644
--- a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
@@ -44,12 +44,10 @@ public interface TextDecoratorUiOperator {
/**
* Called when the layout should be updated.
- * @param isCommitMode {@code true} if the commit indicator should be shown. Show the
- * add-to-dictionary indicator otherwise.
* @param matrix The matrix that transforms the local coordinates into the screen coordinates.
* @param indicatorBounds The bounding box of the indicator, in local coordinates.
* @param composingTextBounds The bounding box of the composing text, in local coordinates.
*/
- void layoutUi(final boolean isCommitMode, final Matrix matrix, final RectF indicatorBounds,
+ void layoutUi(final Matrix matrix, final RectF indicatorBounds,
final RectF composingTextBounds);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index fa4192790..2056a0b9d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -674,15 +674,18 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
final boolean countryCodeMatched = matchString(caseAttr,
R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
+ final boolean splitLayoutMatched = matchBoolean(caseAttr,
+ R.styleable.Keyboard_Case_isSplitLayout, id.mIsSplitLayout);
final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched
&& keyboardThemeMacthed && modeMatched && navigateNextMatched
&& navigatePreviousMatched && passwordInputMatched && clobberSettingsKeyMatched
&& hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
&& isMultiLineMatched && imeActionMatched && isIconDefinedMatched
- && localeCodeMatched && languageCodeMatched && countryCodeMatched;
+ && localeCodeMatched && languageCodeMatched && countryCodeMatched
+ && splitLayoutMatched;
if (DEBUG) {
- startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
+ startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
textAttr(caseAttr.getString(
R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"),
textAttr(caseAttr.getString(
@@ -707,6 +710,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
"languageSwitchKeyEnabled"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_isMultiLine,
"isMultiLine"),
+ booleanAttr(caseAttr, R.styleable.Keyboard_Case_isSplitLayout,
+ "splitLayout"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_isIconDefined),
"isIconDefined"),
textAttr(caseAttr.getString(R.styleable.Keyboard_Case_localeCode),
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index eced45ea5..0f09daf86 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -595,8 +595,9 @@ public class DictionaryFacilitator {
final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo,
final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) {
final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
- final SuggestionResults suggestionResults =
- new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS);
+ final SuggestionResults suggestionResults = new SuggestionResults(
+ SuggestedWords.MAX_SUGGESTIONS,
+ prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence);
final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT };
for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 475782042..69fe6de9a 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -189,9 +189,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;
@@ -202,7 +201,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private int mDelayInMillisecondsToUpdateSuggestions;
private int mDelayInMillisecondsToUpdateShiftState;
- private int mDelayInMillisecondsToShowCommitIndicator;
public UIHandler(final LatinIME ownerInstance) {
super(ownerInstance);
@@ -218,8 +216,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
@@ -277,14 +273,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;
@@ -385,19 +373,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;
@@ -1516,19 +1491,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 dc00ecc8f..d672430a1 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -252,7 +252,7 @@ public final class RichInputConnection {
* See {@link InputConnection#commitText(CharSequence, int)}.
*/
public void commitText(final CharSequence text, final int newCursorPosition) {
- commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT);
+ commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT, text.length());
}
/**
@@ -265,9 +265,11 @@ public final class RichInputConnection {
* the background color. Note that this method specifies {@link BackgroundColorSpan} with
* {@link Spanned#SPAN_COMPOSING} flag, meaning that the background color persists until
* {@link #finishComposingText()} is called.
+ * @param coloredTextLength the length of text, in Java chars, which should be rendered with
+ * the given background color.
*/
public void commitTextWithBackgroundColor(final CharSequence text, final int newCursorPosition,
- final int color) {
+ final int color, final int coloredTextLength) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
mCommittedTextBeforeComposingText.append(text);
@@ -285,7 +287,8 @@ public final class RichInputConnection {
mTempObjectForCommitText.clear();
mTempObjectForCommitText.append(text);
final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(color);
- mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, text.length(),
+ final int spanLength = Math.min(coloredTextLength, text.length());
+ mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, spanLength,
Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mIC.commitText(mTempObjectForCommitText, newCursorPosition);
mLastCommittedTextHasBackgroundColor = true;
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 4ad5ba65e..1ecc995b2 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -188,8 +188,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
@@ -244,6 +250,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 dcfaa3f6d..3eefafc1f 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,
@@ -180,6 +180,7 @@ public class SuggestedWords {
return "SuggestedWords:"
+ " mTypedWordValid=" + mTypedWordValid
+ " mWillAutoCorrect=" + mWillAutoCorrect
+ + " mInputStyle=" + mInputStyle
+ " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
}
@@ -386,8 +387,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 0942c078f..8eccd5cee 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;
@@ -91,12 +92,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();
}
});
@@ -171,6 +168,7 @@ public final class InputLogic {
mConnection.requestCursorUpdates(true /* enableMonitor */,
true /* requestImmediateCallback */);
}
+ mTextDecorator.reset();
}
}
@@ -207,6 +205,8 @@ public final class InputLogic {
public void finishInput() {
if (mWordComposer.isComposingWord()) {
mConnection.finishComposingText();
+ StatsUtils.onWordCommitUserTyped(
+ mWordComposer.getTypedWord(), mWordComposer.isBatchMode());
}
resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogicHandler.reset();
@@ -253,6 +253,7 @@ public final class InputLogic {
promotePhantomSpace(settingsValues);
}
mConnection.commitText(text, 1);
+ StatsUtils.onWordCommitUserTyped(mEnteredText, mWordComposer.isBatchMode());
mConnection.endBatchEdit();
// Space state must be updated before calling updateShiftState
mSpaceState = SpaceState.NONE;
@@ -334,17 +335,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();
@@ -359,11 +351,10 @@ 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);
- }
StatsUtils.onPickSuggestionManually(mSuggestedWords, suggestionInfo);
+ StatsUtils.onWordCommitSuggestionPickedManually(
+ suggestionInfo.mWord, mWordComposer.isBatchMode());
return inputTransaction;
}
@@ -433,6 +424,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 */);
@@ -511,7 +505,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.
@@ -582,6 +578,7 @@ public final class InputLogic {
batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
promotePhantomSpace(settingsValues);
mConnection.commitText(commitParts[0], 0);
+ StatsUtils.onWordCommitUserTyped(commitParts[0], mWordComposer.isBatchMode());
mSpaceState = SpaceState.PHANTOM;
keyboardSwitcher.requestUpdatingShiftState(
getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
@@ -630,42 +627,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
@@ -843,13 +804,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;
@@ -1108,8 +1070,10 @@ public final class InputLogic {
inputTransaction.setRequiresUpdateSuggestions();
} else {
if (mLastComposedWord.canRevertCommit()) {
- revertCommit(inputTransaction);
+ final String lastComposedWord = mLastComposedWord.mTypedWord;
+ revertCommit(inputTransaction, inputTransaction.mSettingsValues);
StatsUtils.onRevertAutoCorrect();
+ StatsUtils.onWordCommitUserTyped(lastComposedWord, mWordComposer.isBatchMode());
return;
}
if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
@@ -1609,14 +1573,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) {
@@ -1635,7 +1604,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;
@@ -1672,23 +1641,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();
+ }
}
/**
@@ -2010,6 +2009,8 @@ public final class InputLogic {
final int indexOfLastSpace = batchInputText.lastIndexOf(Constants.CODE_SPACE) + 1;
if (0 != indexOfLastSpace) {
mConnection.commitText(batchInputText.substring(0, indexOfLastSpace), 1);
+ StatsUtils.onWordCommitUserTyped(
+ batchInputText.substring(0, indexOfLastSpace), mWordComposer.isBatchMode());
final SuggestedWords suggestedWordsForLastWordOfPhraseGesture =
suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture();
mLatinIME.showSuggestionStrip(suggestedWordsForLastWordOfPhraseGesture);
@@ -2048,8 +2049,10 @@ public final class InputLogic {
if (!mWordComposer.isComposingWord()) return;
final String typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
+ final boolean isBatchMode = mWordComposer.isBatchMode();
commitChosenWord(settingsValues, typedWord,
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString);
+ StatsUtils.onWordCommitUserTyped(typedWord, isBatchMode);
}
}
@@ -2095,6 +2098,7 @@ public final class InputLogic {
throw new RuntimeException("We have an auto-correction but the typed word "
+ "is empty? Impossible! I must commit suicide.");
}
+ final boolean isBatchMode = mWordComposer.isBatchMode();
commitChosenWord(settingsValues, autoCorrection,
LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator);
if (!typedWord.equals(autoCorrection)) {
@@ -2107,16 +2111,17 @@ public final class InputLogic {
mConnection.commitCorrection(new CorrectionInfo(
mConnection.getExpectedSelectionEnd() - autoCorrection.length(),
typedWord, autoCorrection));
- StatsUtils.onAutoCorrection(typedWord, autoCorrection, mWordComposer.isBatchMode(),
+ StatsUtils.onAutoCorrection(typedWord, autoCorrection, isBatchMode,
mWordComposer.getAutoCorrectionDictionaryTypeOrNull());
+ StatsUtils.onWordCommitAutoCorrect(autoCorrection, isBatchMode);
+ } else {
+ StatsUtils.onWordCommitUserTyped(autoCorrection, isBatchMode);
}
}
}
/**
- * 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.
@@ -2125,23 +2130,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,
@@ -2151,7 +2139,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
@@ -2235,7 +2223,7 @@ public final class InputLogic {
private void setComposingTextInternal(final CharSequence newComposingText,
final int newCursorPosition) {
setComposingTextInternalWithBackgroundColor(newComposingText, newCursorPosition,
- Color.TRANSPARENT);
+ Color.TRANSPARENT, newComposingText.length());
}
/**
@@ -2251,9 +2239,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;
@@ -2261,7 +2251,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;
}
@@ -2283,7 +2274,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) {
@@ -2307,12 +2299,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.
@@ -2321,24 +2313,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/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
new file mode 100644
index 000000000..06ab1e2d2
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java
@@ -0,0 +1,193 @@
+/*
+ * 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.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.text.TextUtils;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.define.ProductionFlags;
+import com.android.inputmethod.latin.utils.LoginAccountUtils;
+
+import javax.annotation.Nullable;
+
+/**
+ * "Accounts & Privacy" settings sub screen.
+ *
+ * This settings sub screen handles the following preferences:
+ * <li> Account selection/management for IME
+ * <li> TODO: Sync preferences
+ * <li> TODO: Privacy preferences
+ */
+public final class AccountsSettingsFragment extends SubScreenFragment {
+ static final String PREF_ACCCOUNT_SWITCHER = "account_switcher";
+
+ private final DialogInterface.OnClickListener mAccountSelectedListener =
+ new AccountSelectedListener();
+ private final DialogInterface.OnClickListener mAccountSignedOutListener =
+ new AccountSignedOutListener();
+
+ @Override
+ public void onCreate(final Bundle icicle) {
+ super.onCreate(icicle);
+ addPreferencesFromResource(R.xml.prefs_screen_accounts);
+
+ final Resources res = getResources();
+ 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()}.
+ SubtypeSwitcher.init(context);
+
+ if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) {
+ final Preference enableMetricsLogging =
+ findPreference(Settings.PREF_ENABLE_METRICS_LOGGING);
+ if (enableMetricsLogging != null) {
+ final String enableMetricsLoggingTitle = res.getString(
+ R.string.enable_metrics_logging, getApplicationName());
+ enableMetricsLogging.setTitle(enableMetricsLoggingTitle);
+ }
+ } else {
+ removePreference(Settings.PREF_ENABLE_METRICS_LOGGING);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshAccountSelection();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
+ // TODO: Look at the preference that changed before refreshing the view.
+ refreshAccountSelection();
+ }
+
+ private void refreshAccountSelection() {
+ final String currentAccount = getCurrentlySelectedAccount();
+ final Preference accountSwitcher = findPreference(PREF_ACCCOUNT_SWITCHER);
+ if (currentAccount == null) {
+ // No account is currently selected.
+ accountSwitcher.setSummary(getString(R.string.no_accounts_selected));
+ } else {
+ // Set the currently selected account.
+ accountSwitcher.setSummary(getString(R.string.account_selected, currentAccount));
+ }
+ final Context context = getActivity();
+ final String[] accountsForLogin = LoginAccountUtils.getAccountsForLogin(context);
+ accountSwitcher.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (accountsForLogin.length == 0) {
+ // TODO: Handle account addition.
+ Toast.makeText(getActivity(),
+ getString(R.string.account_select_cancel), Toast.LENGTH_SHORT).show();
+ } else {
+ createAccountPicker(accountsForLogin, currentAccount).show();
+ }
+ return true;
+ }
+ });
+
+ // TODO: Depending on the account selection, enable/disable preferences that
+ // depend on an account.
+ }
+
+ @Nullable
+ private String getCurrentlySelectedAccount() {
+ return getSharedPreferences().getString(Settings.PREF_ACCOUNT_NAME, null);
+ }
+
+ /**
+ * Creates an account picker dialog showing the given accounts in a list and selecting
+ * the selected account by default.
+ * The list of accounts must not be null/empty.
+ *
+ * Package-private for testing.
+ */
+ AlertDialog createAccountPicker(final String[] accounts,
+ final String selectedAccount) {
+ if (accounts == null || accounts.length == 0) {
+ throw new IllegalArgumentException("List of accounts must not be empty");
+ }
+
+ // See if the currently selected account is in the list.
+ // If it is, the entry is selected, and a sign-out button is provided.
+ // If it isn't, select the 0th account by default which will get picked up
+ // if the user presses OK.
+ int index = 0;
+ boolean isSignedIn = false;
+ for (int i = 0; i < accounts.length; i++) {
+ if (TextUtils.equals(accounts[i], selectedAccount)) {
+ index = i;
+ isSignedIn = true;
+ break;
+ }
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.account_select_title)
+ .setSingleChoiceItems(accounts, index, null)
+ .setPositiveButton(R.string.account_select_ok, mAccountSelectedListener)
+ .setNegativeButton(R.string.account_select_cancel, null);
+ if (isSignedIn) {
+ builder.setNeutralButton(R.string.account_select_sign_out, mAccountSignedOutListener);
+ }
+ return builder.create();
+ }
+
+ /**
+ * Listener for an account being selected from the picker.
+ * Persists the account to shared preferences.
+ */
+ class AccountSelectedListener implements DialogInterface.OnClickListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final ListView lv = ((AlertDialog)dialog).getListView();
+ final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition());
+ getSharedPreferences()
+ .edit()
+ .putString(Settings.PREF_ACCOUNT_NAME, (String) selectedItem)
+ .apply();
+ }
+ }
+
+ /**
+ * Listener for sign-out being initiated from from the picker.
+ * Removed the account from shared preferences.
+ */
+ class AccountSignedOutListener implements DialogInterface.OnClickListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ getSharedPreferences()
+ .edit()
+ .remove(Settings.PREF_ACCOUNT_NAME)
+ .apply();
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
index 00f2c73dd..a6cb55db1 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java
@@ -93,14 +93,16 @@ public final class AdvancedSettingsFragment extends SubScreenFragment {
removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON);
}
+ // If metrics logging isn't supported, or account sign in is enabled
+ // don't show the logging preference.
+ // TODO: Eventually when we enable account sign in by default,
+ // we'll remove logging preference from here.
if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) {
final Preference enableMetricsLogging =
findPreference(Settings.PREF_ENABLE_METRICS_LOGGING);
if (enableMetricsLogging != null) {
- final int applicationLabelRes = context.getApplicationInfo().labelRes;
- final String applicationName = res.getString(applicationLabelRes);
final String enableMetricsLoggingTitle = res.getString(
- R.string.enable_metrics_logging, applicationName);
+ R.string.enable_metrics_logging, getApplicationName());
enableMetricsLogging.setTitle(enableMetricsLoggingTitle);
}
} else {
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 529f8a093..a171fc330 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -43,6 +43,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
private static final String TAG = Settings.class.getSimpleName();
// Settings screens
public static final String SCREEN_PREFERENCES = "screen_preferences";
+ public static final String SCREEN_ACCOUNTS = "screen_accounts";
public static final String SCREEN_APPEARANCE = "screen_appearance";
public static final String SCREEN_THEME = "screen_theme";
public static final String SCREEN_MULTILINGUAL = "screen_multilingual";
@@ -104,6 +105,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
+ public static final String PREF_ACCOUNT_NAME = "pref_account_name";
// This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
// This is being used only for the backward compatibility.
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 4fc17387f..8c4801798 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -25,6 +25,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.define.ProductionFlags;
import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.FeedbackUtils;
import com.android.inputmethodcommon.InputMethodSettingsFragment;
@@ -51,6 +52,10 @@ public final class SettingsFragment extends InputMethodSettingsFragment {
final Preference multilingualOptions = findPreference(Settings.SCREEN_MULTILINGUAL);
preferenceScreen.removePreference(multilingualOptions);
}
+ if (!ProductionFlags.ENABLE_ACCOUNT_SIGN_IN) {
+ final Preference accountsPreference = findPreference(Settings.SCREEN_ACCOUNTS);
+ preferenceScreen.removePreference(accountsPreference);
+ }
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 57610221c..3339ab57f 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -102,10 +102,7 @@ public 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;
@@ -183,12 +180,6 @@ public 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);
@@ -443,12 +434,6 @@ public 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/SubScreenFragment.java b/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java
index ca5b395ce..240f8f89b 100644
--- a/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java
@@ -20,6 +20,7 @@ import android.app.backup.BackupManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
@@ -79,6 +80,16 @@ abstract class SubScreenFragment extends PreferenceFragment
return getPreferenceManager().getSharedPreferences();
}
+ /**
+ * Gets the application name to display on the UI.
+ */
+ final String getApplicationName() {
+ final Context context = getActivity();
+ final Resources res = getResources();
+ final int applicationLabelRes = context.getApplicationInfo().labelRes;
+ return res.getString(applicationLabelRes);
+ }
+
@Override
public void addPreferencesFromResource(final int preferencesResId) {
super.addPreferencesFromResource(preferencesResId);
diff --git a/java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java b/java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java
new file mode 100644
index 000000000..254bc6567
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/settings/TestFragmentActivity.java
@@ -0,0 +1,55 @@
+/*
+ * 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.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Test activity to use when testing preference fragments. <br/>
+ * Usage: <br/>
+ * Create an ActivityInstrumentationTestCase2 for this activity
+ * and call setIntent() with an intent that specifies the fragment to load in the activity.
+ * The fragment can then be obtained from this activity and used for testing/verification.
+ */
+public final class TestFragmentActivity extends Activity {
+ /**
+ * The fragment name that should be loaded when starting this activity.
+ * This must be specified when starting this activity, as this activity is only
+ * meant to test fragments from instrumentation tests.
+ */
+ public static final String EXTRA_SHOW_FRAGMENT = "show_fragment";
+
+ public Fragment mFragment;
+
+ @Override
+ protected void onCreate(final Bundle savedState) {
+ super.onCreate(savedState);
+ final Intent intent = getIntent();
+ final String fragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
+ if (fragmentName == null) {
+ throw new IllegalArgumentException("No fragment name specified for testing");
+ }
+
+ mFragment = Fragment.instantiate(this, fragmentName);
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction().add(mFragment, fragmentName).commit();
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
index c2167a76b..ae2de44c7 100644
--- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
@@ -18,6 +18,7 @@ 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.AccountsSettingsFragment;
import com.android.inputmethod.latin.settings.AdvancedSettingsFragment;
import com.android.inputmethod.latin.settings.AppearanceSettingsFragment;
import com.android.inputmethod.latin.settings.CorrectionSettingsFragment;
@@ -42,6 +43,7 @@ public class FragmentUtils {
sLatinImeFragments.add(DictionarySettingsFragment.class.getName());
sLatinImeFragments.add(AboutPreferences.class.getName());
sLatinImeFragments.add(PreferencesSettingsFragment.class.getName());
+ sLatinImeFragments.add(AccountsSettingsFragment.class.getName());
sLatinImeFragments.add(AppearanceSettingsFragment.class.getName());
sLatinImeFragments.add(ThemeSettingsFragment.class.getName());
sLatinImeFragments.add(MultiLingualSettingsFragment.class.getName());
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 55557de9d..bbcef990d 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.
}
@@ -594,4 +602,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 eaa5743d4..d6f644228 100644
--- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
+++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java
@@ -22,7 +22,6 @@ import com.android.inputmethod.latin.define.ProductionFlags;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
-import java.util.Locale;
import java.util.TreeSet;
/**
@@ -31,14 +30,17 @@ import java.util.TreeSet;
*/
public final class SuggestionResults extends TreeSet<SuggestedWordInfo> {
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 int capacity) {
- this(sSuggestedWordInfoComparator, capacity);
+ public SuggestionResults(final int capacity, final boolean isBeginningOfSentence) {
+ this(sSuggestedWordInfoComparator, capacity, isBeginningOfSentence);
}
- public SuggestionResults(final Comparator<SuggestedWordInfo> comparator,
- final int capacity) {
+ private SuggestionResults(final Comparator<SuggestedWordInfo> comparator,
+ final int capacity, final boolean isBeginningOfSentence) {
super(comparator);
mCapacity = capacity;
if (ProductionFlags.INCLUDE_RAW_SUGGESTIONS) {
@@ -46,6 +48,7 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> {
} else {
mRawSuggestions = null;
}
+ mIsBeginningOfSentence = isBeginningOfSentence;
}
@Override