aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java7
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java28
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java36
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java96
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java13
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java3
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java41
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java40
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java4
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/WordProperty.java31
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java5
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java119
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java6
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CollectionUtils.java10
15 files changed, 326 insertions, 118 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 85dfea4e7..d35c8fae1 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -108,10 +108,9 @@ public class Keyboard {
mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping);
mIconsSet = params.mIconsSet;
- mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
- params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
- mMostCommonKeyWidth, mMostCommonKeyHeight, mSortedKeys,
- params.mTouchPositionCorrection);
+ mProximityInfo = new ProximityInfo(params.GRID_WIDTH, params.GRID_HEIGHT,
+ mOccupiedWidth, mOccupiedHeight, mMostCommonKeyWidth, mMostCommonKeyHeight,
+ mSortedKeys, params.mTouchPositionCorrection);
mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 246d11bac..7f2957fff 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -254,8 +254,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void onToggleEmojiKeyboard() {
- if (mKeyboardLayoutSet == null || !isShowingEmojiPalettes()) {
- mLatinIME.startShowingInputView();
+ final boolean needsToLoadKeyboard = (mKeyboardLayoutSet == null);
+ if (needsToLoadKeyboard || !isShowingEmojiPalettes()) {
+ mLatinIME.startShowingInputView(needsToLoadKeyboard);
setEmojiKeyboard();
} else {
mLatinIME.stopShowingInputView();
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index c19cd671a..9c5abcd71 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -52,18 +52,11 @@ public class ProximityInfo {
private final int mMostCommonKeyHeight;
private final List<Key> mSortedKeys;
private final List<Key>[] mGridNeighbors;
- private final String mLocaleStr;
@SuppressWarnings("unchecked")
- ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight,
- final int minWidth, final int height, final int mostCommonKeyWidth,
- final int mostCommonKeyHeight, final List<Key> sortedKeys,
+ ProximityInfo(final int gridWidth, final int gridHeight, final int minWidth, final int height,
+ final int mostCommonKeyWidth, final int mostCommonKeyHeight, final List<Key> sortedKeys,
final TouchPositionCorrection touchPositionCorrection) {
- if (TextUtils.isEmpty(localeStr)) {
- mLocaleStr = "";
- } else {
- mLocaleStr = localeStr;
- }
mGridWidth = gridWidth;
mGridHeight = gridHeight;
mGridSize = mGridWidth * mGridHeight;
@@ -89,11 +82,10 @@ public class ProximityInfo {
}
// TODO: Stop passing proximityCharsArray
- private static native long setProximityInfoNative(String locale,
- int displayWidth, int displayHeight, int gridWidth, int gridHeight,
- int mostCommonKeyWidth, int mostCommonKeyHeight, int[] proximityCharsArray,
- int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths,
- int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
+ private static native long setProximityInfoNative(int displayWidth, int displayHeight,
+ int gridWidth, int gridHeight, int mostCommonKeyWidth, int mostCommonKeyHeight,
+ int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
+ int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs,
float[] sweetSpotCenterYs, float[] sweetSpotRadii);
private static native void releaseProximityInfoNative(long nativeProximityInfo);
@@ -221,10 +213,10 @@ public class ProximityInfo {
}
// TODO: Stop passing proximityCharsArray
- return setProximityInfoNative(mLocaleStr, mKeyboardMinWidth, mKeyboardHeight,
- mGridWidth, mGridHeight, mMostCommonKeyWidth, mMostCommonKeyHeight,
- proximityCharsArray, keyCount, keyXCoordinates, keyYCoordinates, keyWidths,
- keyHeights, keyCharCodes, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
+ return setProximityInfoNative(mKeyboardMinWidth, mKeyboardHeight, mGridWidth, mGridHeight,
+ mMostCommonKeyWidth, mMostCommonKeyHeight, proximityCharsArray, keyCount,
+ keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
+ sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
}
public long getNativeProximityInfo() {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 6dc1e8273..1f0317288 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -99,6 +99,30 @@ public class DictionaryFacilitator {
DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length);
/**
+ * Returns whether this facilitator is exactly for this list of locales.
+ * @param locales the list of locales to test against
+ * @return true if this facilitator handles exactly this list of locales, false otherwise
+ */
+ public boolean isForLocales(final Locale[] locales) {
+ if (locales.length != mDictionaryGroups.length) {
+ return false;
+ }
+ for (final Locale locale : locales) {
+ boolean found = false;
+ for (final DictionaryGroup group : mDictionaryGroups) {
+ if (locale.equals(group.mLocale)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* A group of dictionaries that work together for a single language.
*/
private static class DictionaryGroup {
@@ -199,6 +223,18 @@ public class DictionaryFacilitator {
return mDictionaryGroups[0].mLocale;
}
+ /**
+ * Returns the primary locale among all currently active locales. BE CAREFUL using this.
+ *
+ * DO NOT USE THIS just because it's convenient. Use it when it's correct, for example when
+ * choosing what dictionary to put a word in, or when changing the capitalization of a typed
+ * string.
+ * @return the primary active locale
+ */
+ public Locale getPrimaryLocale() {
+ return mDictionaryGroups[0].mLocale;
+ }
+
private static ExpandableBinaryDictionary getSubDict(final String dictType,
final Context context, final Locale locale, final File dictFile,
final String dictNamePrefix) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 099d4850e..be2efb2dc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -84,12 +84,12 @@ import com.android.inputmethod.latin.settings.SettingsActivity;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
+import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
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;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
@@ -102,6 +102,7 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@@ -177,6 +178,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private final boolean mIsHardwareAcceleratedDrawingEnabled;
+ private GestureConsumer mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
+
public final UIHandler mHandler = new UIHandler(this);
public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
@@ -253,12 +256,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We need to re-evaluate the currently composing word in case the script has
// changed.
postWaitForDictionaryLoad();
- latinIme.resetSuggest();
+ latinIme.resetDictionaryFacilitatorIfNecessary();
break;
case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED:
+ final SuggestedWords suggestedWords = (SuggestedWords) msg.obj;
latinIme.mInputLogic.onUpdateTailBatchInputCompleted(
latinIme.mSettings.getCurrent(),
- (SuggestedWords) msg.obj, latinIme.mKeyboardSwitcher);
+ suggestedWords, latinIme.mKeyboardSwitcher);
+ latinIme.onTailBatchInputResultShown(suggestedWords);
break;
case MSG_RESET_CACHES:
final SettingsValues settingsValues = latinIme.mSettings.getCurrent();
@@ -537,9 +542,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.onCreate();
DEBUG = DebugFlags.DEBUG_ENABLED;
- // TODO: Resolve mutual dependencies of {@link #loadSettings()} and {@link #initSuggest()}.
+ // TODO: Resolve mutual dependencies of {@link #loadSettings()} and
+ // {@link #resetDictionaryFacilitatorIfNecessary()}.
loadSettings();
- resetSuggest();
+ resetDictionaryFacilitatorIfNecessary();
// Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack.
@@ -580,7 +586,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// been displayed. Opening dictionaries never affects responsivity as dictionaries are
// asynchronously loaded.
if (!mHandler.hasPendingReopenDictionaries()) {
- resetSuggestForLocale(locale);
+ resetDictionaryFacilitatorForLocale(locale);
}
mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList(
true /* allowsImplicitlySelectedSubtypes */));
@@ -621,8 +627,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- private void resetSuggest() {
+ private void resetDictionaryFacilitatorIfNecessary() {
final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ if (mDictionaryFacilitator.isForLocales(new Locale[] { switcherSubtypeLocale })) {
+ return;
+ }
final String switcherLocaleStr = switcherSubtypeLocale.toString();
final Locale subtypeLocale;
if (TextUtils.isEmpty(switcherLocaleStr)) {
@@ -637,15 +646,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
subtypeLocale = switcherSubtypeLocale;
}
- resetSuggestForLocale(subtypeLocale);
+ resetDictionaryFacilitatorForLocale(subtypeLocale);
}
/**
- * Reset suggest by loading dictionaries for the locale and the current settings values.
+ * Reset the facilitator by loading dictionaries for the locale and the current settings values.
*
* @param locale the locale
*/
- private void resetSuggestForLocale(final Locale locale) {
+ // TODO: make sure the current settings always have the right locale, and read from them
+ private void resetDictionaryFacilitatorForLocale(final Locale locale) {
final SettingsValues settingsValues = mSettings.getCurrent();
mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
@@ -723,6 +733,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void setInputView(final View view) {
super.setInputView(view);
mInputView = view;
+ updateSoftInputWindowLayoutParameters();
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
if (hasSuggestionStripView()) {
mSuggestionStripView.setListener(this, view);
@@ -795,6 +806,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
StatsUtils.onFinishInputView();
mHandler.onFinishInputView(finishingInput);
mStatsUtilsManager.onFinishInputView();
+ mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
}
@Override
@@ -820,6 +832,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@SuppressWarnings("deprecation")
private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
super.onStartInputView(editorInfo, restarting);
+ // Switch to the null consumer to handle cases leading to early exit below, for which we
+ // also wouldn't be consuming gesture data.
+ mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
mRichImm.clearSubtypeCaches();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
switcher.updateKeyboardTheme();
@@ -863,6 +878,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
+ // Update to a gesture consumer with the current editor and IME state.
+ mGestureConsumer = GestureConsumer.newInstance(editorInfo,
+ mInputLogic.getPrivateCommandPerformer(),
+ Collections.singletonList(mSubtypeSwitcher.getCurrentSubtypeLocale()),
+ switcher.getKeyboard());
+
// Forward this event to the accessibility utilities, if enabled.
final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
if (accessUtils.isTouchExplorationEnabled()) {
@@ -901,12 +922,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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();
- }
+ resetDictionaryFacilitatorIfNecessary();
// TODO[IL]: Can the following be moved to InputLogic#startInput?
if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
@@ -1132,6 +1148,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onComputeInsets(final InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
+ // This method may be called before {@link #setInputView(View)}.
+ if (mInputView == null) {
+ return;
+ }
final SettingsValues settingsValues = mSettings.getCurrent();
final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
if (visibleKeyboardView == null || !hasSuggestionStripView()) {
@@ -1166,12 +1186,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
outInsets.visibleTopInsets = visibleTopY;
}
- public void startShowingInputView() {
+ public void startShowingInputView(final boolean needsToLoadKeyboard) {
mIsExecutingStartShowingInputView = true;
// This {@link #showWindow(boolean)} will eventually call back
// {@link #onEvaluateInputViewShown()}.
showWindow(true /* showInput */);
mIsExecutingStartShowingInputView = false;
+ if (needsToLoadKeyboard) {
+ loadKeyboard();
+ }
}
public void stopShowingInputView() {
@@ -1179,6 +1202,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
+ public boolean onShowInputRequested(final int flags, final boolean configChange) {
+ if (Settings.getInstance().getCurrent().mHasHardwareKeyboard) {
+ return true;
+ }
+ return super.onShowInputRequested(flags, configChange);
+ }
+
+ @Override
public boolean onEvaluateInputViewShown() {
if (mIsExecutingStartShowingInputView) {
return true;
@@ -1208,8 +1239,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void updateFullscreenMode() {
+ super.updateFullscreenMode();
+ mInputLogic.onUpdateFullscreenMode(isFullscreenMode());
+ updateSoftInputWindowLayoutParameters();
+ }
+
+ private void updateSoftInputWindowLayoutParameters() {
// Override layout parameters to expand {@link SoftInputWindow} to the entire screen.
- // See {@link InputMethodService#setinputView(View) and
+ // See {@link InputMethodService#setinputView(View)} and
// {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}.
final Window window = getWindow().getWindow();
ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT);
@@ -1228,8 +1265,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM);
ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight);
}
- super.updateFullscreenMode();
- mInputLogic.onUpdateFullscreenMode(isFullscreenMode());
}
private int getCurrentAutoCapsState() {
@@ -1398,6 +1433,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onStartBatchInput() {
mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
+ mGestureConsumer.onGestureStarted(
+ Collections.singletonList(mSubtypeSwitcher.getCurrentSubtypeLocale()),
+ mKeyboardSwitcher.getKeyboard());
}
@Override
@@ -1408,11 +1446,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onEndBatchInput(final InputPointers batchPointers) {
mInputLogic.onEndBatchInput(batchPointers);
+ mGestureConsumer.onGestureCompleted(batchPointers);
}
@Override
public void onCancelBatchInput() {
mInputLogic.onCancelBatchInput(mHandler);
+ mGestureConsumer.onGestureCanceled();
+ }
+
+ /**
+ * To be called after the InputLogic has gotten a chance to act on the on-device decoding
+ * for the full gesture, possibly updating the TextView to reflect the first decoding.
+ * <p>
+ * This method must be run on the UI Thread.
+ * @param suggestedWords On-device decoding for the full gesture.
+ */
+ public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) {
+ mGestureConsumer.onImeSuggestionsProcessed(suggestedWords,
+ mInputLogic.getComposingStart(), mInputLogic.getComposingLength());
}
// This method must run on the UI Thread.
@@ -1554,7 +1606,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
final String wordToShow;
if (CapsModeUtils.isAutoCapsMode(mInputLogic.mLastComposedWord.mCapitalizedMode)) {
- wordToShow = word.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
+ wordToShow = word.toLowerCase(mDictionaryFacilitator.getPrimaryLocale());
} else {
wordToShow = word;
}
@@ -1840,7 +1892,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void dumpDictionaryForDebug(final String dictName) {
if (mDictionaryFacilitator.getLocale() == null) {
- resetSuggest();
+ resetDictionaryFacilitatorIfNecessary();
}
mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index a7ea2a1c8..62a258b20 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin;
import android.graphics.Color;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
+import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -33,6 +34,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import com.android.inputmethod.compat.InputConnectionCompatUtils;
+import com.android.inputmethod.latin.inputlogic.PrivateCommandPerformer;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.DebugLogUtils;
@@ -52,7 +54,7 @@ import java.util.Arrays;
* all the time to find out what text is in the buffer, when we need it to determine caps mode
* for example.
*/
-public final class RichInputConnection {
+public final class RichInputConnection implements PrivateCommandPerformer {
private static final String TAG = RichInputConnection.class.getSimpleName();
private static final boolean DBG = false;
private static final boolean DEBUG_PREVIOUS_TEXT = false;
@@ -896,6 +898,15 @@ public final class RichInputConnection {
}
}
+ @Override
+ public boolean performPrivateCommand(final String action, final Bundle data) {
+ mIC = mParent.getCurrentInputConnection();
+ if (mIC == null) {
+ return false;
+ }
+ return mIC.performPrivateCommand(action, data);
+ }
+
public int getExpectedSelectionStart() {
return mExpectedSelStart;
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index f85b34b5e..157bd1565 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -128,8 +128,7 @@ public final class WordComposer {
* Number of keystrokes in the composing word.
* @return the number of keystrokes
*/
- // This may be made public if need be, but right now it's not used anywhere
- /* package for tests */ int size() {
+ public int size() {
return mCodePointSize;
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index b4a1c3e65..1b1d5e7e5 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -2263,6 +2263,47 @@ public final class InputLogic {
mConnection.setComposingText(composingTextToBeSet, newCursorPosition);
}
+ /**
+ * Gets an object allowing private IME commands to be sent to the
+ * underlying editor.
+ * @return An object for sending private commands to the underlying editor.
+ */
+ public PrivateCommandPerformer getPrivateCommandPerformer() {
+ return mConnection;
+ }
+
+ /**
+ * Gets the expected index of the first char of the composing span within the editor's text.
+ * Returns a negative value in case there appears to be no valid composing span.
+ *
+ * @see #getComposingLength()
+ * @see RichInputConnection#hasSelection()
+ * @see RichInputConnection#isCursorPositionKnown()
+ * @see RichInputConnection#getExpectedSelectionStart()
+ * @see RichInputConnection#getExpectedSelectionEnd()
+ * @return The expected index in Java chars of the first char of the composing span.
+ */
+ // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+ // never need to know this.
+ public int getComposingStart() {
+ if (!mConnection.isCursorPositionKnown() || mConnection.hasSelection()) {
+ return -1;
+ }
+ return mConnection.getExpectedSelectionStart() - mWordComposer.size();
+ }
+
+ /**
+ * Gets the expected length in Java chars of the composing span.
+ * May be 0 if there is no valid composing span.
+ * @see #getComposingStart()
+ * @return The expected length of the composing span.
+ */
+ // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+ // never need to know this.
+ public int getComposingLength() {
+ return mWordComposer.size();
+ }
+
//////////////////////////////////////////////////////////////////////////////////////////////
// Following methods are tentatively placed in this class for the integration with
// TextDecorator.
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
new file mode 100644
index 000000000..42eaa9c82
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.inputlogic;
+
+import android.os.Bundle;
+
+/**
+ * Provides an interface matching
+ * {@link android.view.inputmethod.InputConnection#performPrivateCommand(String,Bundle)}.
+ */
+public interface PrivateCommandPerformer {
+ /**
+ * API to send private commands from an input method to its connected
+ * editor. This can be used to provide domain-specific features that are
+ * only known between certain input methods and their clients.
+ *
+ * @param action Name of the command to be performed. This must be a scoped
+ * name, i.e. prefixed with a package name you own, so that
+ * different developers will not create conflicting commands.
+ * @param data Any data to include with the command.
+ * @return true if the command was sent (regardless of whether the
+ * associated editor understood it), false if the input connection is no
+ * longer valid.
+ */
+ boolean performPrivateCommand(String action, Bundle data);
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 2661d5d48..34edfa0da 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -163,13 +163,15 @@ public final class FormatSpec {
static final int NOT_A_VERSION_NUMBER = -1;
// These MUST have the same values as the relevant constants in format_utils.h.
- // From version 4 on, we use version * 100 + revision as a version number. That allows
+ // From version 2.01 on, we use version * 100 + revision as a version number. That allows
// us to change the format during development while having testing devices remove
// older files with each upgrade, while still having a readable versioning scheme.
// When we bump up the dictionary format version, we should update
// ExpandableDictionary.needsToMigrateDictionary() and
// ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType().
public static final int VERSION2 = 2;
+ public static final int VERSION201 = 201;
+ public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201;
// Dictionary version used for testing.
public static final int VERSION4_ONLY_FOR_TESTING = 399;
public static final int VERSION401 = 401;
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
index 46705f9db..a180d060e 100644
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
@@ -54,11 +54,15 @@ public final class WordProperty implements Comparable<WordProperty> {
mWord = word;
mProbabilityInfo = probabilityInfo;
mShortcutTargets = shortcutTargets;
- mNgrams = new ArrayList<>();
- final NgramContext ngramContext = new NgramContext(new WordInfo(mWord));
- if (bigrams != null) {
- for (final WeightedString bigramTarget : bigrams) {
- mNgrams.add(new NgramProperty(bigramTarget, ngramContext));
+ if (null == bigrams) {
+ mNgrams = null;
+ } else {
+ mNgrams = new ArrayList<>();
+ final NgramContext ngramContext = new NgramContext(new WordInfo(mWord));
+ if (bigrams != null) {
+ for (final WeightedString bigramTarget : bigrams) {
+ mNgrams.add(new NgramProperty(bigramTarget, ngramContext));
+ }
}
}
mIsBeginningOfSentence = false;
@@ -87,7 +91,7 @@ public final class WordProperty implements Comparable<WordProperty> {
mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
mShortcutTargets = new ArrayList<>();
- mNgrams = new ArrayList<>();
+ final ArrayList<NgramProperty> ngrams = new ArrayList<>();
mIsBeginningOfSentence = isBeginningOfSentence;
mIsNotAWord = isNotAWord;
mIsBlacklistEntry = isBlacklisted;
@@ -104,8 +108,9 @@ public final class WordProperty implements Comparable<WordProperty> {
final WeightedString ngramTarget = new WeightedString(ngramTargetString,
createProbabilityInfoFromArray(bigramProbabilityInfo.get(i)));
// TODO: Support n-gram.
- mNgrams.add(new NgramProperty(ngramTarget, ngramContext));
+ ngrams.add(new NgramProperty(ngramTarget, ngramContext));
}
+ mNgrams = ngrams.isEmpty() ? null : ngrams;
final int shortcutTargetCount = shortcutTargets.size();
for (int i = 0; i < shortcutTargetCount; i++) {
@@ -118,6 +123,9 @@ public final class WordProperty implements Comparable<WordProperty> {
// TODO: Remove
public ArrayList<WeightedString> getBigrams() {
+ if (null == mNgrams) {
+ return null;
+ }
final ArrayList<WeightedString> bigrams = new ArrayList<>();
for (final NgramProperty ngram : mNgrams) {
if (ngram.mNgramContext.getPrevWordCount() == 1) {
@@ -167,11 +175,18 @@ public final class WordProperty implements Comparable<WordProperty> {
if (!(o instanceof WordProperty)) return false;
WordProperty w = (WordProperty)o;
return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord)
- && mShortcutTargets.equals(w.mShortcutTargets) && mNgrams.equals(w.mNgrams)
+ && mShortcutTargets.equals(w.mShortcutTargets) && equals(mNgrams, w.mNgrams)
&& mIsNotAWord == w.mIsNotAWord && mIsBlacklistEntry == w.mIsBlacklistEntry
&& mHasNgrams == w.mHasNgrams && mHasShortcuts && w.mHasNgrams;
}
+ private <T> boolean equals(final ArrayList<T> a, final ArrayList<T> b) {
+ if (null == a) {
+ return null == b;
+ }
+ return a.equals(b);
+ }
+
@Override
public int hashCode() {
if (mHashCode == 0) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
index 51c4b1ee8..9ddee8629 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
@@ -145,9 +145,8 @@ public class SentenceLevelAdapter {
int wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
while (wordStart <= end && wordEnd != -1 && wordStart != -1) {
if (wordEnd >= start && wordEnd > wordStart) {
- CharSequence subSequence = originalText.subSequence(wordStart, wordEnd).toString();
- final TextInfo ti = TextInfoCompatUtils.newInstance(subSequence, 0,
- subSequence.length(), cookie, subSequence.hashCode());
+ final TextInfo ti = TextInfoCompatUtils.newInstance(originalText, wordStart,
+ wordEnd, cookie, originalText.subSequence(wordStart, wordEnd).hashCode());
wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
}
wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 839fce051..a651774c6 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -60,6 +60,9 @@ import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.util.ArrayList;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
final class SuggestionStripLayoutHelper {
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
@@ -213,15 +216,14 @@ final class SuggestionStripLayoutHelper {
return word;
}
- final int len = word.length();
final Spannable spannedWord = new SpannableString(word);
final int options = mSuggestionStripOptions;
if ((isAutoCorrection && (options & AUTO_CORRECT_BOLD) != 0)
|| (isTypedWordValid && (options & VALID_TYPED_WORD_BOLD) != 0)) {
- spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ addStyleSpan(spannedWord, BOLD_SPAN);
}
if (isAutoCorrection && (options & AUTO_CORRECT_UNDERLINE) != 0) {
- spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+ addStyleSpan(spannedWord, UNDERLINE_SPAN);
}
return spannedWord;
}
@@ -446,10 +448,11 @@ final class SuggestionStripLayoutHelper {
// {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
// Use a simple {@link String} to avoid the issue.
wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
- final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
- final float scaleX = getTextScaleX(word, width, wordView.getPaint());
+ final CharSequence text = getEllipsizedTextWithSettingScaleX(
+ word, width, wordView.getPaint());
+ final float scaleX = wordView.getTextScaleX();
wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
- wordView.setTextScaleX(Math.max(scaleX, MIN_TEXT_XSCALE));
+ wordView.setTextScaleX(scaleX);
// A <code>wordView</code> should be disabled when <code>word</code> is empty in order to
// make it unclickable.
// With accessibility touch exploration on, <code>wordView</code> should be enabled even
@@ -562,7 +565,8 @@ final class SuggestionStripLayoutHelper {
final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save);
wordView.setTextColor(mColorTypedWord);
final int wordWidth = (int)(width * mCenterSuggestionWeight);
- final CharSequence wordToSave = getEllipsizedText(word, wordWidth, wordView.getPaint());
+ final CharSequence wordToSave = getEllipsizedTextWithSettingScaleX(
+ word, wordWidth, wordView.getPaint());
final float wordScaleX = wordView.getTextScaleX();
wordView.setText(wordToSave);
wordView.setTextScaleX(wordScaleX);
@@ -596,7 +600,7 @@ final class SuggestionStripLayoutHelper {
}
hintView.setTextColor(mColorAutoCorrect);
final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint());
- hintView.setText(hintText);
+ hintView.setText(hintText); // TextView.setText() resets text scale x to 1.0.
hintView.setTextScaleX(hintScaleX);
setLayoutWeight(hintView, hintWeight, ViewGroup.LayoutParams.MATCH_PARENT);
}
@@ -608,8 +612,7 @@ final class SuggestionStripLayoutHelper {
final int width = titleView.getWidth() - titleView.getPaddingLeft()
- titleView.getPaddingRight();
titleView.setTextColor(mColorAutoCorrect);
- titleView.setText(importantNoticeTitle);
- titleView.setTextScaleX(1.0f); // Reset textScaleX.
+ titleView.setText(importantNoticeTitle); // TextView.setText() resets text scale x to 1.0.
final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint());
titleView.setTextScaleX(titleScaleX);
}
@@ -624,18 +627,19 @@ final class SuggestionStripLayoutHelper {
}
}
- private static float getTextScaleX(final CharSequence text, final int maxWidth,
+ private static float getTextScaleX(@Nullable final CharSequence text, final int maxWidth,
final TextPaint paint) {
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
if (width <= maxWidth || maxWidth <= 0) {
return 1.0f;
}
- return maxWidth / (float)width;
+ return maxWidth / (float) width;
}
- private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
- final TextPaint paint) {
+ @Nullable
+ private static CharSequence getEllipsizedTextWithSettingScaleX(
+ @Nullable final CharSequence text, final int maxWidth, @Nonnull final TextPaint paint) {
if (text == null) {
return null;
}
@@ -645,62 +649,63 @@ final class SuggestionStripLayoutHelper {
return text;
}
- // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To
- // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE).
- final float upscaledWidth = maxWidth / MIN_TEXT_XSCALE;
- CharSequence ellipsized = TextUtils.ellipsize(
- text, paint, upscaledWidth, TextUtils.TruncateAt.MIDDLE);
- // For an unknown reason, ellipsized seems to return a text that does indeed fit inside the
- // passed width according to paint.measureText, but not according to paint.getTextWidths.
- // But when rendered, the text seems to actually take up as many pixels as returned by
- // paint.getTextWidths, hence problem.
- // To save this case, we compare the measured size of the new text, and if it's too much,
- // try it again removing the difference. This may still give a text too long by one or
- // two pixels so we take an additional 2 pixels cushion and call it a day.
- // TODO: figure out why getTextWidths and measureText don't agree with each other, and
- // remove the following code.
- final float ellipsizedTextWidth = getTextWidth(ellipsized, paint);
- if (upscaledWidth <= ellipsizedTextWidth) {
- ellipsized = TextUtils.ellipsize(
- text, paint, upscaledWidth - (ellipsizedTextWidth - upscaledWidth) - 2,
- TextUtils.TruncateAt.MIDDLE);
- }
+ // <code>text</code> must be ellipsized with minimum text scale x.
paint.setTextScaleX(MIN_TEXT_XSCALE);
- return ellipsized;
+ final boolean hasBoldStyle = hasStyleSpan(text, BOLD_SPAN);
+ final boolean hasUnderlineStyle = hasStyleSpan(text, UNDERLINE_SPAN);
+ // TextUtils.ellipsize erases any span object existed after ellipsized point.
+ // We have to restore these spans afterward.
+ final CharSequence ellipsizedText = TextUtils.ellipsize(
+ text, paint, maxWidth, TextUtils.TruncateAt.MIDDLE);
+ if (!hasBoldStyle && !hasUnderlineStyle) {
+ return ellipsizedText;
+ }
+ final Spannable spannableText = (ellipsizedText instanceof Spannable)
+ ? (Spannable)ellipsizedText : new SpannableString(ellipsizedText);
+ if (hasBoldStyle) {
+ addStyleSpan(spannableText, BOLD_SPAN);
+ }
+ if (hasUnderlineStyle) {
+ addStyleSpan(spannableText, UNDERLINE_SPAN);
+ }
+ return spannableText;
+ }
+
+ private static boolean hasStyleSpan(@Nullable final CharSequence text,
+ final CharacterStyle style) {
+ if (text instanceof Spanned) {
+ return ((Spanned)text).getSpanStart(style) >= 0;
+ }
+ return false;
+ }
+
+ private static void addStyleSpan(@Nonnull final Spannable text, final CharacterStyle style) {
+ text.removeSpan(style);
+ text.setSpan(style, 0, text.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
- private static int getTextWidth(final CharSequence text, final TextPaint paint) {
+ private static int getTextWidth(@Nullable final CharSequence text, final TextPaint paint) {
if (TextUtils.isEmpty(text)) {
return 0;
}
+ final int length = text.length();
+ final float[] widths = new float[length];
+ final int count;
final Typeface savedTypeface = paint.getTypeface();
- paint.setTypeface(getTextTypeface(text));
- final int len = text.length();
- final float[] widths = new float[len];
- final int count = paint.getTextWidths(text, 0, len, widths);
+ try {
+ paint.setTypeface(getTextTypeface(text));
+ count = paint.getTextWidths(text, 0, length, widths);
+ } finally {
+ paint.setTypeface(savedTypeface);
+ }
int width = 0;
for (int i = 0; i < count; i++) {
width += Math.round(widths[i] + 0.5f);
}
- paint.setTypeface(savedTypeface);
return width;
}
- private static Typeface getTextTypeface(final CharSequence text) {
- if (!(text instanceof SpannableString)) {
- return Typeface.DEFAULT;
- }
-
- final SpannableString ss = (SpannableString)text;
- final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class);
- if (styles.length == 0) {
- return Typeface.DEFAULT;
- }
-
- if (styles[0].getStyle() == Typeface.BOLD) {
- return Typeface.DEFAULT_BOLD;
- }
- // TODO: BOLD_ITALIC, ITALIC case?
- return Typeface.DEFAULT;
+ private static Typeface getTextTypeface(@Nullable final CharSequence text) {
+ return hasStyleSpan(text, BOLD_SPAN) ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT;
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index b421a7eb5..e40fd8800 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -400,6 +400,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (mStripVisibilityGroup.isShowingImportantNoticeStrip()) {
return false;
}
+ // Detecting sliding up finger to show {@link MoreSuggestionsView}.
if (!mMoreSuggestionsView.isShowingInParent()) {
mLastX = (int)me.getX();
mLastY = (int)me.getY();
@@ -439,6 +440,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public boolean onTouchEvent(final MotionEvent me) {
+ if (!mMoreSuggestionsView.isShowingInParent()) {
+ // Ignore any touch event while more suggestions panel hasn't been shown.
+ // Detecting sliding up is done at {@link #onInterceptTouchEvent}.
+ return true;
+ }
// In the sliding input mode. {@link MotionEvent} should be forwarded to
// {@link MoreSuggestionsView}.
final int index = me.getActionIndex();
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index 61292fc36..fb36b7c50 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.latin.utils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
@@ -40,4 +41,13 @@ public final class CollectionUtils {
}
return list;
}
+
+ /**
+ * Tests whether c contains no elements, true if c is null or c is empty.
+ * @param c Collection to test.
+ * @return Whether c contains no elements.
+ */
+ public static boolean isNullOrEmpty(final Collection c) {
+ return c == null || c.isEmpty();
+ }
}