diff options
Diffstat (limited to 'java/src/com')
35 files changed, 550 insertions, 371 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 09f1145e9..61805286d 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -306,8 +306,15 @@ public class Key implements Comparable<Key> { } mActionFlags = actionFlags; + final int code = KeySpecParser.parseCode(style.getString(keyAttr, + R.styleable.Keyboard_Key_code), params.mCodesSet, CODE_UNSPECIFIED); if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) { mLabel = params.mId.mCustomActionLabel; + } else if (code >= Character.MIN_SUPPLEMENTARY_CODE_POINT) { + // This is a workaround to have a key that has a supplementary code point in its label. + // Because we can put a string in resource neither as a XML entity of a supplementary + // code point nor as a surrogate pair. + mLabel = new StringBuilder().appendCodePoint(code).toString(); } else { mLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, R.styleable.Keyboard_Key_keyLabel), needsToUpperCase, locale); @@ -320,8 +327,6 @@ public class Key implements Comparable<Key> { } String outputText = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, R.styleable.Keyboard_Key_keyOutputText), needsToUpperCase, locale); - final int code = KeySpecParser.parseCode(style.getString(keyAttr, - R.styleable.Keyboard_Key_code), params.mCodesSet, CODE_UNSPECIFIED); // Choose the first letter of the label as primary code if not specified. if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) && !TextUtils.isEmpty(mLabel)) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 8864b7603..1dc3c6a4c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; -import android.content.res.Configuration; import android.text.InputType; import android.text.TextUtils; import android.view.inputmethod.EditorInfo; @@ -58,8 +57,6 @@ public final class KeyboardId { public final InputMethodSubtype mSubtype; public final Locale mLocale; - // TODO: Remove this member. It is used only for logging purpose. - public final int mOrientation; public final int mWidth; public final int mHeight; public final int mMode; @@ -77,7 +74,6 @@ public final class KeyboardId { public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) { mSubtype = params.mSubtype; mLocale = SubtypeLocaleUtils.getSubtypeLocale(mSubtype); - mOrientation = params.mOrientation; mWidth = params.mKeyboardWidth; mHeight = params.mKeyboardHeight; mMode = params.mMode; @@ -101,7 +97,6 @@ public final class KeyboardId { private static int computeHashCode(final KeyboardId id) { return Arrays.hashCode(new Object[] { - id.mOrientation, id.mElementId, id.mMode, id.mWidth, @@ -123,8 +118,7 @@ public final class KeyboardId { private boolean equals(final KeyboardId other) { if (other == this) return true; - return other.mOrientation == mOrientation - && other.mElementId == mElementId + return other.mElementId == mElementId && other.mMode == mMode && other.mWidth == mWidth && other.mHeight == mHeight @@ -185,13 +179,10 @@ public final class KeyboardId { @Override public String toString() { - final String orientation = (mOrientation == Configuration.ORIENTATION_PORTRAIT) - ? "port" : "land"; - return String.format(Locale.ROOT, "[%s %s:%s %s:%dx%d %s %s %s%s%s%s%s%s%s%s%s]", + return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s %s%s%s%s%s%s%s%s%s]", elementIdToName(mElementId), - mLocale, - mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), - orientation, mWidth, mHeight, + mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), + mWidth, mHeight, modeName(mMode), imeAction(), (navigateNext() ? "navigateNext" : ""), diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index e15310721..bccf8fb15 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -23,20 +23,16 @@ import static com.android.inputmethod.latin.Constants.ImeOption.NO_SETTINGS_KEY; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.text.InputType; -import android.text.TextUtils; -import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.util.Xml; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardParams; @@ -45,10 +41,8 @@ import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; -import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.XmlParseUtils; @@ -74,8 +68,6 @@ public final class KeyboardLayoutSet { private static final String TAG_ELEMENT = "Element"; private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "keyboard_layout_set_"; - private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480; - private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 800; private final Context mContext; private final Params mParams; @@ -119,7 +111,6 @@ public final class KeyboardLayoutSet { boolean mLanguageSwitchKeyEnabled; InputMethodSubtype mSubtype; boolean mIsSpellChecker; - int mOrientation; int mKeyboardWidth; int mKeyboardHeight; // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. @@ -222,7 +213,6 @@ public final class KeyboardLayoutSet { private final Context mContext; private final String mPackageName; private final Resources mResources; - private final EditorInfo mEditorInfo; private final Params mParams = new Params(); @@ -232,55 +222,25 @@ public final class KeyboardLayoutSet { mContext = context; mPackageName = context.getPackageName(); mResources = context.getResources(); - mEditorInfo = editorInfo; final Params params = mParams; params.mMode = getKeyboardMode(editorInfo); params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO; params.mNoSettingsKey = InputAttributes.inPrivateImeOptions( - mPackageName, NO_SETTINGS_KEY, mEditorInfo); + mPackageName, NO_SETTINGS_KEY, params.mEditorInfo); } - public Builder setScreenGeometry(final int widthPixels, final int heightPixels) { - final Params params = mParams; - params.mOrientation = (heightPixels > widthPixels) - ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; - setDefaultKeyboardSize(widthPixels, heightPixels); + public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHeight) { + mParams.mKeyboardWidth = keyboardWidth; + mParams.mKeyboardHeight = keyboardHeight; return this; } - private void setDefaultKeyboardSize(final int widthPixels, final int heightPixels) { - final String keyboardHeightString = ResourceUtils.getDeviceOverrideValue( - mResources, R.array.keyboard_heights); - final float keyboardHeight; - if (TextUtils.isEmpty(keyboardHeightString)) { - keyboardHeight = mResources.getDimension(R.dimen.keyboardHeight); - } else { - keyboardHeight = Float.parseFloat(keyboardHeightString) - * mResources.getDisplayMetrics().density; - } - final float maxKeyboardHeight = mResources.getFraction( - R.fraction.maxKeyboardHeight, heightPixels, heightPixels); - float minKeyboardHeight = mResources.getFraction( - R.fraction.minKeyboardHeight, heightPixels, heightPixels); - if (minKeyboardHeight < 0.0f) { - // Specified fraction was negative, so it should be calculated against display - // width. - minKeyboardHeight = -mResources.getFraction( - R.fraction.minKeyboardHeight, widthPixels, widthPixels); - } - // Keyboard height will not exceed maxKeyboardHeight and will not be less than - // minKeyboardHeight. - mParams.mKeyboardHeight = (int)Math.max( - Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); - mParams.mKeyboardWidth = widthPixels; - } - public Builder setSubtype(final InputMethodSubtype subtype) { final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE); @SuppressWarnings("deprecation") final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions( - mPackageName, FORCE_ASCII, mEditorInfo); + mPackageName, FORCE_ASCII, mParams.mEditorInfo); final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( mParams.mEditorInfo.imeOptions) || deprecatedForceAscii; @@ -302,9 +262,9 @@ public final class KeyboardLayoutSet { final boolean languageSwitchKeyEnabled) { @SuppressWarnings("deprecation") final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( - null, NO_MICROPHONE_COMPAT, mEditorInfo); + null, NO_MICROPHONE_COMPAT, mParams.mEditorInfo); final boolean noMicrophone = InputAttributes.inPrivateImeOptions( - mPackageName, NO_MICROPHONE, mEditorInfo) + mPackageName, NO_MICROPHONE, mParams.mEditorInfo) || deprecatedNoMicrophone; mParams.mVoiceKeyEnabled = voiceKeyEnabled && !noMicrophone; mParams.mVoiceKeyOnMain = voiceKeyOnMain; @@ -317,8 +277,6 @@ public final class KeyboardLayoutSet { } public KeyboardLayoutSet build() { - if (mParams.mOrientation == Configuration.ORIENTATION_UNDEFINED) - throw new RuntimeException("Screen geometry is not specified"); if (mParams.mSubtype == null) throw new RuntimeException("KeyboardLayoutSet subtype is not specified"); final String packageName = mResources.getResourcePackageName( @@ -442,51 +400,4 @@ public final class KeyboardLayoutSet { } } } - - public static KeyboardLayoutSet createKeyboardSetForSpellChecker(final Context context, - final String locale, final String layout) { - final InputMethodSubtype subtype = - AdditionalSubtypeUtils.createAdditionalSubtype(locale, layout, null); - return createKeyboardSet(context, subtype, SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, - SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT, false /* testCasesHaveTouchCoordinates */, - true /* isSpellChecker */); - } - - @UsedForTesting - public static KeyboardLayoutSet createKeyboardSetForTest(final Context context, - final InputMethodSubtype subtype, final int orientation, - final boolean testCasesHaveTouchCoordinates) { - final DisplayMetrics dm = context.getResources().getDisplayMetrics(); - final int width; - final int height; - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - width = Math.max(dm.widthPixels, dm.heightPixels); - height = Math.min(dm.widthPixels, dm.heightPixels); - } else if (orientation == Configuration.ORIENTATION_PORTRAIT) { - width = Math.min(dm.widthPixels, dm.heightPixels); - height = Math.max(dm.widthPixels, dm.heightPixels); - } else { - throw new RuntimeException("Orientation should be ORIENTATION_LANDSCAPE or " - + "ORIENTATION_PORTRAIT: orientation=" + orientation); - } - return createKeyboardSet(context, subtype, width, height, testCasesHaveTouchCoordinates, - false /* isSpellChecker */); - } - - private static KeyboardLayoutSet createKeyboardSet(final Context context, - final InputMethodSubtype subtype, final int width, final int height, - final boolean testCasesHaveTouchCoordinates, final boolean isSpellChecker) { - final EditorInfo editorInfo = new EditorInfo(); - editorInfo.inputType = InputType.TYPE_CLASS_TEXT; - final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( - context, editorInfo); - builder.setScreenGeometry(width, height); - builder.setSubtype(subtype); - builder.setIsSpellChecker(isSpellChecker); - if (!testCasesHaveTouchCoordinates) { - // For spell checker and tests - builder.disableTouchPositionCorrectionData(); - } - return builder.build(); - } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 8880af48c..1ea0f8b96 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.preference.PreferenceManager; -import android.util.DisplayMetrics; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -39,6 +38,7 @@ import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.utils.ResourceUtils; public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); @@ -137,8 +137,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( mThemeContext, editorInfo); final Resources res = mThemeContext.getResources(); - final DisplayMetrics dm = res.getDisplayMetrics(); - builder.setScreenGeometry(dm.widthPixels, dm.heightPixels); + final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); + final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); + builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); builder.setOptions( settingsValues.isVoiceKeyEnabled(editorInfo), diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 054c503d8..28eb58573 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -196,13 +196,14 @@ public class KeyboardView extends View { @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { - if (mKeyboard != null) { - // The main keyboard expands to the display width. - final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); - setMeasuredDimension(widthMeasureSpec, height); - } else { + if (mKeyboard == null) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; } + // The main keyboard expands to the entire this {@link KeyboardView}. + final int width = mKeyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight(); + final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom(); + setMeasuredDimension(width, height); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 6782317d0..526c2f1ec 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -615,7 +615,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; mSpacebarTextSize = keyHeight * mSpacebarTextRatio; if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.mainKeyboardView_setKeyboard(keyboard); + final int orientation = getContext().getResources().getConfiguration().orientation; + ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation); } // This always needs to be set since the accessibility state can @@ -837,10 +838,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mSlidingKeyInputPreview.dismissSlidingKeyInputPreview(); } - public void setGesturePreviewMode(final boolean drawsGestureTrail, - final boolean drawsGestureFloatingPreviewText) { - mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText); - mGestureTrailsPreview.setPreviewEnabled(drawsGestureTrail); + private void setGesturePreviewMode(final boolean isGestureTrailEnabled, + final boolean isGestureFloatingPreviewTextEnabled) { + mGestureFloatingPreviewText.setPreviewEnabled(isGestureFloatingPreviewTextEnabled); + mGestureTrailsPreview.setPreviewEnabled(isGestureTrailEnabled); } public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { @@ -868,8 +869,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable); } - public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) { - PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser); + public void setGestureHandlingEnabledByUser(final boolean isGestureHandlingEnabledByUser, + final boolean isGestureTrailEnabled, + final boolean isGestureFloatingPreviewTextEnabled) { + PointerTracker.setGestureHandlingEnabledByUser(isGestureHandlingEnabledByUser); + setGesturePreviewMode(isGestureHandlingEnabledByUser && isGestureTrailEnabled, + isGestureHandlingEnabledByUser && isGestureFloatingPreviewTextEnabled); } @Override @@ -973,9 +978,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack // {@link KeyboardView#showKeyPreview(PointerTracker)}. final int pointY = key.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset; moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); - final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords)); - final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords)); - tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel); + tracker.onShowMoreKeysPanel(moreKeysPanel); } public boolean isInSlidingKeyInput() { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 94f6a3cf2..f00f5a99e 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -198,12 +198,6 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel final int x = (int)me.getX(index); final int y = (int)me.getY(index); final int pointerId = me.getPointerId(index); - processMotionEvent(action, x, y, pointerId, eventTime); - return true; - } - - public void processMotionEvent(final int action, final int x, final int y, - final int pointerId, final long eventTime) { switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: @@ -217,6 +211,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel onMoveEvent(x, y, pointerId, eventTime); break; } + return true; } @Override diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index ab5fee99d..b66ee2a65 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -1282,12 +1282,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - public void onShowMoreKeysPanel(final int translatedX, final int translatedY, - final MoreKeysPanel panel) { + public void onShowMoreKeysPanel(final MoreKeysPanel panel) { setReleasedKeyGraphics(mCurrentKey); - final long eventTime = SystemClock.uptimeMillis(); + final int translatedX = panel.translateX(mLastX); + final int translatedY = panel.translateY(mLastY); + panel.onDownEvent(translatedX, translatedY, mPointerId, SystemClock.uptimeMillis()); mMoreKeysPanel = panel; - mMoreKeysPanel.onDownEvent(translatedX, translatedY, mPointerId, eventTime); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index 69ffb4c75..dc815e57d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -53,6 +53,7 @@ public final class KeyboardCodesSet { "key_action_previous", "key_shift_enter", "key_language_switch", + "key_emoji", "key_unspecified", "key_left_parenthesis", "key_right_parenthesis", @@ -89,6 +90,7 @@ public final class KeyboardCodesSet { Constants.CODE_ACTION_PREVIOUS, Constants.CODE_SHIFT_ENTER, Constants.CODE_LANGUAGE_SWITCH, + Constants.CODE_EMOJI, Constants.CODE_UNSPECIFIED, CODE_LEFT_PARENTHESIS, CODE_RIGHT_PARENTHESIS, @@ -116,6 +118,7 @@ public final class KeyboardCodesSet { DEFAULT[12], DEFAULT[13], DEFAULT[14], + DEFAULT[15], CODE_RIGHT_PARENTHESIS, CODE_LEFT_PARENTHESIS, CODE_GREATER_THAN_SIGN, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 4e3f7618b..336db186e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -56,6 +56,7 @@ public final class KeyboardIconsSet { "language_switch_key", R.styleable.Keyboard_iconLanguageSwitchKey, "zwnj_key", R.styleable.Keyboard_iconZwnjKey, "zwj_key", R.styleable.Keyboard_iconZwjKey, + "emoji_key", R.styleable.Keyboard_iconEmojiKey, }; private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 7bb7442f3..bb49f4758 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -27,18 +27,18 @@ import java.util.HashMap; /** * !!!!! DO NOT EDIT THIS FILE !!!!! * - * This file is generated by tools/maketext. The base template file is - * tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl + * This file is generated by tools/make-keyboard-text. The base template file is + * tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl * * This file must be updated when any text resources in keyboard layout files have been changed. * These text resources are referred as "!text/<resource_name>" in keyboard XML definitions, * and should be defined in - * tools/maketext/res/values-<locale>/donottranslate-more-keys.xml + * tools/make-keyboard-text/res/values-<locale>/donottranslate-more-keys.xml * * To update this file, please run the following commands. * $ cd $ANDROID_BUILD_TOP - * $ mmm packages/inputmethods/LatinIME/tools/maketext - * $ maketext -java packages/inputmethods/LatinIME/java/src + * $ mmm packages/inputmethods/LatinIME/tools/make-keyboard-text + * $ make-keyboard-text -java packages/inputmethods/LatinIME/java/src * * The updated source file will be generated to the following path (this file). * packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/internal/ @@ -519,7 +519,7 @@ public final class KeyboardTextsSet { // U+061F: "ØŸ" ARABIC QUESTION MARK // U+060C: "ØŒ" ARABIC COMMA // U+061B: "Ø›" ARABIC SEMICOLON - /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", // U+2605: "★" BLACK STAR // U+066D: "Ù" ARABIC FIVE POINTED STAR /* 54 */ "\u2605,\u066D", @@ -1408,7 +1408,7 @@ public final class KeyboardTextsSet { // U+061F: "ØŸ" ARABIC QUESTION MARK // U+060C: "ØŒ" ARABIC COMMA // U+061B: "Ø›" ARABIC SEMICOLON - /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)", + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", // U+2605: "★" BLACK STAR // U+066D: "Ù" ARABIC FIVE POINTED STAR /* 54 */ "\u2605,\u066D", @@ -1900,8 +1900,9 @@ public final class KeyboardTextsSet { /* 48 */ "!text/single_laqm_raqm_rtl", /* 49 */ "!text/double_laqm_raqm_rtl", /* 50~ */ - null, null, null, null, - /* ~53 */ + null, null, null, + /* ~52 */ + /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(|),)|(", // U+2605: "★" BLACK STAR /* 54 */ "\u2605", /* 55 */ null, @@ -3151,7 +3152,7 @@ public final class KeyboardTextsSet { /* 7 */ "\u00E7", }; - /* Language zz: No language */ + /* Language zz: Alphabet */ private static final String[] LANGUAGE_zz = { // U+00E0: "à " LATIN SMALL LETTER A WITH GRAVE // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -3324,7 +3325,7 @@ public final class KeyboardTextsSet { "uk", LANGUAGE_uk, /* Ukrainian */ "vi", LANGUAGE_vi, /* Vietnamese */ "zu", LANGUAGE_zu, /* Zulu */ - "zz", LANGUAGE_zz, /* No language */ + "zz", LANGUAGE_zz, /* Alphabet */ }; static { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index fa301b5a6..d0a4afd50 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -235,7 +235,7 @@ final public class BinaryDictionaryGetter { new BinaryDictInputOutput.ByteBufferWrapper(inStream.getChannel().map( FileChannel.MapMode.READ_ONLY, 0, f.length())); final int magic = buffer.readInt(); - if (magic != FormatSpec.VERSION_2_MAGIC_NUMBER) { + if (magic != FormatSpec.MAGIC_NUMBER) { return false; } final int formatVersion = buffer.readInt(); diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index f91a21c7a..8aec03f71 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -187,6 +187,7 @@ public final class Constants { public static final int CODE_ACTION_NEXT = -8; public static final int CODE_ACTION_PREVIOUS = -9; public static final int CODE_LANGUAGE_SWITCH = -10; + public static final int CODE_EMOJI = -11; public static final int CODE_SHIFT_ENTER = -12; // Code value representing the code is not specified. public static final int CODE_UNSPECIFIED = -13; @@ -207,6 +208,7 @@ public final class Constants { case CODE_ACTION_NEXT: return "actionNext"; case CODE_ACTION_PREVIOUS: return "actionPrevious"; case CODE_LANGUAGE_SWITCH: return "languageSwitch"; + case CODE_EMOJI: return "emoji"; case CODE_SHIFT_ENTER: return "shiftEnter"; case CODE_UNSPECIFIED: return "unspec"; case CODE_TAB: return "tab"; diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java index 8be04c1c0..47151bf61 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java @@ -37,10 +37,9 @@ import java.util.HashMap; * An in memory dictionary for memorizing entries and writing a binary dictionary. */ public class DictionaryWriter extends AbstractDictionaryWriter { - // TODO: Regenerate version 3 binary dictionary. - private static final int BINARY_DICT_VERSION = 2; + private static final int BINARY_DICT_VERSION = 3; private static final FormatSpec.FormatOptions FORMAT_OPTIONS = - new FormatSpec.FormatOptions(BINARY_DICT_VERSION); + new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */); private FusionDictionary mFusionDictionary; diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java index 5359c8185..81ccf83d8 100644 --- a/java/src/com/android/inputmethod/latin/InputView.java +++ b/java/src/com/android/inputmethod/latin/InputView.java @@ -24,7 +24,7 @@ import android.view.View; import android.widget.LinearLayout; public final class InputView extends LinearLayout { - private View mSuggestionStripContainer; + private View mSuggestionStripView; private View mKeyboardView; private int mKeyboardTopPadding; @@ -33,33 +33,29 @@ public final class InputView extends LinearLayout { private final Rect mEventForwardingRect = new Rect(); private final Rect mEventReceivingRect = new Rect(); - public InputView(Context context, AttributeSet attrs) { + public InputView(final Context context, final AttributeSet attrs) { super(context, attrs, 0); } - public void setKeyboardGeometry(int keyboardTopPadding) { + public void setKeyboardGeometry(final int keyboardTopPadding) { mKeyboardTopPadding = keyboardTopPadding; } @Override protected void onFinishInflate() { - mSuggestionStripContainer = findViewById(R.id.suggestions_container); + mSuggestionStripView = findViewById(R.id.suggestion_strip_view); mKeyboardView = findViewById(R.id.keyboard_view); } @Override - public boolean dispatchTouchEvent(MotionEvent me) { - if (mSuggestionStripContainer.getVisibility() == VISIBLE - && mKeyboardView.getVisibility() == VISIBLE - && forwardTouchEvent(me)) { - return true; + public boolean dispatchTouchEvent(final MotionEvent me) { + if (mSuggestionStripView.getVisibility() != VISIBLE + || mKeyboardView.getVisibility() != VISIBLE) { + return super.dispatchTouchEvent(me); } - return super.dispatchTouchEvent(me); - } - // The touch events that hit the top padding of keyboard should be forwarded to - // {@link SuggestionStripView}. - private boolean forwardTouchEvent(MotionEvent me) { + // The touch events that hit the top padding of keyboard should be forwarded to + // {@link SuggestionStripView}. final Rect rect = mInputViewRect; this.getGlobalVisibleRect(rect); final int x = (int)me.getX() + rect.left; @@ -68,7 +64,7 @@ public final class InputView extends LinearLayout { final Rect forwardingRect = mEventForwardingRect; mKeyboardView.getGlobalVisibleRect(forwardingRect); if (!mIsForwardingEvent && !forwardingRect.contains(x, y)) { - return false; + return super.dispatchTouchEvent(me); } final int forwardingLimitY = forwardingRect.top + mKeyboardTopPadding; @@ -93,11 +89,11 @@ public final class InputView extends LinearLayout { } if (!sendToTarget) { - return false; + return super.dispatchTouchEvent(me); } final Rect receivingRect = mEventReceivingRect; - mSuggestionStripContainer.getGlobalVisibleRect(receivingRect); + mSuggestionStripView.getGlobalVisibleRect(receivingRect); final int translatedX = x - receivingRect.left; final int translatedY; if (y < forwardingLimitY) { @@ -107,7 +103,7 @@ public final class InputView extends LinearLayout { translatedY = y - receivingRect.top; } me.setLocation(translatedX, translatedY); - mSuggestionStripContainer.dispatchTouchEvent(me); + mSuggestionStripView.dispatchTouchEvent(me); return true; } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 385e884ab..a7798e335 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -31,7 +31,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.net.ConnectivityManager; @@ -51,7 +50,6 @@ import android.util.Printer; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; @@ -76,6 +74,7 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.personalization.PersonalizationDictionaryHelper; +import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister; import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; import com.android.inputmethod.latin.settings.Settings; @@ -150,9 +149,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final Settings mSettings; - private View mExtractArea; - private View mKeyPreviewBackingView; - private View mSuggestionsContainer; + private View mInputView; + private int mInputViewMinHeight; private SuggestionStripView mSuggestionStripView; // Never null private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; @@ -473,6 +471,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen KeyboardSwitcher.init(this); AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); + PersonalizationDictionarySessionRegister.init(this); super.onCreate(); @@ -653,6 +652,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mOptionsDialog.dismiss(); } } + PersonalizationDictionarySessionRegister.onConfigurationChanged(this, conf); super.onConfigurationChanged(conf); } @@ -661,18 +661,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled); } + private void setInputViewMinHeight(final int minHeight) { + if (mInputView != null && mInputViewMinHeight != minHeight) { + mInputView.setMinimumHeight(minHeight); + mInputViewMinHeight = minHeight; + } + } + @Override - public void setInputView(final View view) { - super.setInputView(view); - mExtractArea = getWindow().getWindow().getDecorView() - .findViewById(android.R.id.extractArea); - mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); - mSuggestionsContainer = view.findViewById(R.id.suggestions_container); - mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); - if (mSuggestionStripView != null) - mSuggestionStripView.setListener(this, view); + public void setInputView(final View inputView) { + super.setInputView(inputView); + mInputView = inputView; + setInputViewMinHeight(0); + mSuggestionStripView = (SuggestionStripView)inputView.findViewById( + R.id.suggestion_strip_view); + if (mSuggestionStripView != null) { + mSuggestionStripView.setListener(this, inputView); + } if (LatinImeLogger.sVISUALDEBUG) { - mKeyPreviewBackingView.setBackgroundColor(0x10FF0000); + inputView.setBackgroundColor(0x10FF0000); } } @@ -848,8 +855,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mainKeyboardView.setSlidingKeyInputPreviewEnabled( currentSettingsValues.mSlidingKeyInputPreviewEnabled); mainKeyboardView.setGestureHandlingEnabledByUser( - currentSettingsValues.mGestureInputEnabled); - mainKeyboardView.setGesturePreviewMode(currentSettingsValues.mGesturePreviewTrailEnabled, + currentSettingsValues.mGestureInputEnabled, + currentSettingsValues.mGestureTrailEnabled, currentSettingsValues.mGestureFloatingPreviewTextEnabled); // If we have a user dictionary addition in progress, we should check now if we should @@ -1111,19 +1118,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void setSuggestionStripShownInternal(final boolean shown, final boolean needsInputViewShown) { // TODO: Modify this if we support suggestions with hard keyboard - if (onEvaluateInputViewShown() && mSuggestionsContainer != null) { + if (onEvaluateInputViewShown() && mSuggestionStripView != null) { final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); final boolean inputViewShown = (mainKeyboardView != null) ? mainKeyboardView.isShown() : false; final boolean shouldShowSuggestions = shown && (needsInputViewShown ? inputViewShown : true); if (isFullscreenMode()) { - mSuggestionsContainer.setVisibility( + mSuggestionStripView.setVisibility( shouldShowSuggestions ? View.VISIBLE : View.GONE); } else { - mSuggestionsContainer.setVisibility( + mSuggestionStripView.setVisibility( shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE); } + if (shouldShowSuggestions && mainKeyboardView != null) { + final int remainingHeight = getWindow().getWindow().getDecorView().getHeight() + - mainKeyboardView.getHeight() - mSuggestionStripView.getHeight(); + mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight); + } } } @@ -1131,64 +1143,37 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setSuggestionStripShownInternal(shown, /* needsInputViewShown */true); } - private int getAdjustedBackingViewHeight() { - final int currentHeight = mKeyPreviewBackingView.getHeight(); - if (currentHeight > 0) { - return currentHeight; - } - - final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (mainKeyboardView == null) { - return 0; - } - final int keyboardHeight = mainKeyboardView.getHeight(); - final int suggestionsHeight = mSuggestionsContainer.getHeight(); - final int displayHeight = getResources().getDisplayMetrics().heightPixels; - final Rect rect = new Rect(); - mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect); - final int notificationBarHeight = rect.top; - final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight - - keyboardHeight; - - final LayoutParams params = mKeyPreviewBackingView.getLayoutParams(); - params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight); - mKeyPreviewBackingView.setLayoutParams(params); - return params.height; - } - @Override public void onComputeInsets(final InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (mainKeyboardView == null || mSuggestionsContainer == null) { + if (mainKeyboardView == null || mSuggestionStripView == null) { return; } - final int adjustedBackingHeight = getAdjustedBackingViewHeight(); - final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE); - final int backingHeight = backingGone ? 0 : adjustedBackingHeight; - // In fullscreen mode, the height of the extract area managed by InputMethodService should - // be considered. - // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}. - final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0; - final int suggestionsHeight = (mSuggestionsContainer.getVisibility() == View.GONE) ? 0 - : mSuggestionsContainer.getHeight(); - final int extraHeight = extractHeight + backingHeight + suggestionsHeight; - int visibleTopY = extraHeight; + // This method is never called when in fullscreen mode. + // The contentTop is the top coordinate of the keyboard. The application behind will be + // resized/panned above this coordibnate to be able to show an input field. + final int contentTop = mInputView.getHeight() - mainKeyboardView.getHeight(); + final int suggestionsHeight = (mSuggestionStripView.getVisibility() == View.VISIBLE) + ? mSuggestionStripView.getHeight() : 0; + // The visibleTop is the top coordinates of the visible part of this IME. The application + // behind will never be resized, but may be panned or scrolled. + final int visibleTop = mainKeyboardView.isShowingMoreKeysPanel() ? 0 + : contentTop - suggestionsHeight; + outInsets.contentTopInsets = contentTop; + outInsets.visibleTopInsets = visibleTop; // Need to set touchable region only if input view is being shown if (mainKeyboardView.isShown()) { - if (mSuggestionsContainer.getVisibility() == View.VISIBLE) { - visibleTopY -= suggestionsHeight; - } - final int touchY = mainKeyboardView.isShowingMoreKeysPanel() ? 0 : visibleTopY; - final int touchWidth = mainKeyboardView.getWidth(); - final int touchHeight = mainKeyboardView.getHeight() + extraHeight + final int touchLeft = 0; + final int touchTop = visibleTop; + final int touchRight = touchLeft + mainKeyboardView.getWidth(); + final int touchBottom = contentTop + mainKeyboardView.getHeight() // Extend touchable region below the keyboard. + EXTENDED_TOUCHABLE_REGION_HEIGHT; + // The touch event on touchableRegion will be delivered to this IME. + outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom); outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION; - outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight); } - outInsets.contentTopInsets = visibleTopY; - outInsets.visibleTopInsets = visibleTopY; } @Override @@ -1211,11 +1196,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void updateFullscreenMode() { super.updateFullscreenMode(); - - if (mKeyPreviewBackingView == null) return; - // In fullscreen mode, no need to have extra space to show the key preview. - // If not, we should have extra space above the keyboard to show the key preview. - mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE); + if (!isFullscreenMode()) { + // Expand the input view to cover entire display to be able to show key previews and + // more suggestions view that may be displayed above the keyboard. + setInputViewMinHeight(getResources().getDisplayMetrics().heightPixels); + } } // This will reset the whole input state to the starting state. It will clear @@ -1512,6 +1497,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case Constants.CODE_LANGUAGE_SWITCH: handleLanguageSwitchKey(); break; + case Constants.CODE_EMOJI: + // TODO: Implement emoji keyboard switch. + break; case Constants.CODE_ENTER: final EditorInfo editorInfo = getCurrentInputEditorInfo(); final int imeOptionsActionId = @@ -2529,7 +2517,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int maxFreq = AutoCorrectionUtils.getMaxFrequency( suggest.getUnigramDictionaries(), suggestion); if (maxFreq == 0) return null; - userHistoryPredictionDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0); + userHistoryPredictionDictionary + .addToPersonalizationPredictionDictionary(prevWord, secondWord, maxFreq > 0); return prevWord; } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index b69e3f8d2..35920f8cb 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -654,9 +654,11 @@ public final class RichInputConnection { + "\"" + periodSpace + "\" just before the cursor."); return false; } + // Double-space results in ". ". A backspace to cancel this should result in a single + // space in the text field, so we replace ". " with a single space. deleteSurroundingText(2, 0); - final String doubleSpace = " "; - commitText(doubleSpace, 1); + final String singleSpace = " "; + commitText(singleSpace, 1); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.richInputConnection_revertDoubleSpacePeriod(); } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 167c6915c..c7b063daf 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -145,21 +145,21 @@ public final class BinaryDictIOUtils { * Reads unigrams and bigrams from the binary file. * Doesn't make the memory representation of the dictionary. * - * @param buffer the buffer to read. + * @param reader the reader. * @param words the map to store the address as a key and the word as a value. * @param frequencies the map to store the address as a key and the frequency as a value. * @param bigrams the map to store the address as a key and the list of address as a value. * @throws IOException * @throws UnsupportedFormatException */ - public static void readUnigramsAndBigramsBinary(final FusionDictionaryBufferInterface buffer, + public static void readUnigramsAndBigramsBinary(final BinaryDictReader reader, final Map<Integer, String> words, final Map<Integer, Integer> frequencies, final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException, UnsupportedFormatException { // Read header - final FileHeader header = BinaryDictInputOutput.readHeader(buffer); - readUnigramsAndBigramsBinaryInner(buffer, header.mHeaderSize, words, frequencies, bigrams, - header.mFormatOptions); + final FileHeader header = BinaryDictInputOutput.readHeader(reader.getBuffer()); + readUnigramsAndBigramsBinaryInner(reader.getBuffer(), header.mHeaderSize, words, + frequencies, bigrams, header.mFormatOptions); } /** diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 1b187d85d..504349a0b 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -66,6 +66,7 @@ public final class BinaryDictInputOutput { public void position(int newPosition); public void put(final byte b); public int limit(); + @UsedForTesting public int capacity(); } @@ -1210,49 +1211,38 @@ public final class BinaryDictInputOutput { ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256); // The magic number in big-endian order. - if (version >= FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) { - // Magic number for version 2+. - headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 24))); - headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 16))); - headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 8))); - headerBuffer.write((byte) (0xFF & FormatSpec.VERSION_2_MAGIC_NUMBER)); - // Dictionary version. - headerBuffer.write((byte) (0xFF & (version >> 8))); - headerBuffer.write((byte) (0xFF & version)); - } else { - // Magic number for version 1. - headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_1_MAGIC_NUMBER >> 8))); - headerBuffer.write((byte) (0xFF & FormatSpec.VERSION_1_MAGIC_NUMBER)); - // Dictionary version. - headerBuffer.write((byte) (0xFF & version)); - } + // Magic number for all versions. + headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 24))); + headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 16))); + headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 8))); + headerBuffer.write((byte) (0xFF & FormatSpec.MAGIC_NUMBER)); + // Dictionary version. + headerBuffer.write((byte) (0xFF & (version >> 8))); + headerBuffer.write((byte) (0xFF & version)); + // Options flags final int options = makeOptionsValue(dict, formatOptions); headerBuffer.write((byte) (0xFF & (options >> 8))); headerBuffer.write((byte) (0xFF & options)); - if (version >= FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) { - final int headerSizeOffset = headerBuffer.size(); - // Placeholder to be written later with header size. - for (int i = 0; i < 4; ++i) { - headerBuffer.write(0); - } - // Write out the options. - for (final String key : dict.mOptions.mAttributes.keySet()) { - final String value = dict.mOptions.mAttributes.get(key); - CharEncoding.writeString(headerBuffer, key); - CharEncoding.writeString(headerBuffer, value); - } - final int size = headerBuffer.size(); - final byte[] bytes = headerBuffer.toByteArray(); - // Write out the header size. - bytes[headerSizeOffset] = (byte) (0xFF & (size >> 24)); - bytes[headerSizeOffset + 1] = (byte) (0xFF & (size >> 16)); - bytes[headerSizeOffset + 2] = (byte) (0xFF & (size >> 8)); - bytes[headerSizeOffset + 3] = (byte) (0xFF & (size >> 0)); - destination.write(bytes); - } else { - headerBuffer.writeTo(destination); - } + final int headerSizeOffset = headerBuffer.size(); + // Placeholder to be written later with header size. + for (int i = 0; i < 4; ++i) { + headerBuffer.write(0); + } + // Write out the options. + for (final String key : dict.mOptions.mAttributes.keySet()) { + final String value = dict.mOptions.mAttributes.get(key); + CharEncoding.writeString(headerBuffer, key); + CharEncoding.writeString(headerBuffer, value); + } + final int size = headerBuffer.size(); + final byte[] bytes = headerBuffer.toByteArray(); + // Write out the header size. + bytes[headerSizeOffset] = (byte) (0xFF & (size >> 24)); + bytes[headerSizeOffset + 1] = (byte) (0xFF & (size >> 16)); + bytes[headerSizeOffset + 2] = (byte) (0xFF & (size >> 8)); + bytes[headerSizeOffset + 3] = (byte) (0xFF & (size >> 0)); + destination.write(bytes); headerBuffer.close(); @@ -1658,10 +1648,8 @@ public final class BinaryDictInputOutput { */ private static int getFormatVersion(final FusionDictionaryBufferInterface buffer) throws IOException { - final int magic_v1 = buffer.readUnsignedShort(); - if (FormatSpec.VERSION_1_MAGIC_NUMBER == magic_v1) return buffer.readUnsignedByte(); - final int magic_v2 = (magic_v1 << 16) + buffer.readUnsignedShort(); - if (FormatSpec.VERSION_2_MAGIC_NUMBER == magic_v2) return buffer.readUnsignedShort(); + final int magic = buffer.readInt(); + if (FormatSpec.MAGIC_NUMBER == magic) return buffer.readUnsignedShort(); return FormatSpec.NOT_A_VERSION_NUMBER; } @@ -1695,18 +1683,15 @@ public final class BinaryDictInputOutput { final HashMap<String, String> attributes = new HashMap<String, String>(); final int headerSize; - if (version < FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) { - headerSize = buffer.position(); - } else { - headerSize = buffer.readInt(); - populateOptions(buffer, headerSize, attributes); - buffer.position(headerSize); - } + headerSize = buffer.readInt(); if (headerSize < 0) { throw new UnsupportedFormatException("header size can't be negative."); } + populateOptions(buffer, headerSize, attributes); + buffer.position(headerSize); + final FileHeader header = new FileHeader(headerSize, new FusionDictionary.DictionaryOptions(attributes, 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), @@ -1738,23 +1723,30 @@ public final class BinaryDictInputOutput { * FusionDictionary structure. The optional dict argument is an existing dictionary to * which words from the buffer should be added. If it is null, a new dictionary is created. * - * @param buffer the buffer to read. + * @param reader the reader. * @param dict an optional dictionary to add words to, or null. * @return the created (or merged) dictionary. */ @UsedForTesting - public static FusionDictionary readDictionaryBinary( - final FusionDictionaryBufferInterface buffer, final FusionDictionary dict) - throws IOException, UnsupportedFormatException { + public static FusionDictionary readDictionaryBinary(final BinaryDictReader reader, + final FusionDictionary dict) throws FileNotFoundException, IOException, + UnsupportedFormatException { // clear cache wordCache.clear(); + // if the buffer has not been opened, open the buffer with bytebuffer. + if (reader.getBuffer() == null) reader.openBuffer( + new BinaryDictReader.FusionDictionaryBufferFromByteBufferFactory()); + if (reader.getBuffer() == null) { + MakedictLog.e("Cannot open the buffer"); + } + // Read header - final FileHeader header = readHeader(buffer); + final FileHeader header = readHeader(reader.getBuffer()); Map<Integer, Node> reverseNodeMapping = new TreeMap<Integer, Node>(); Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>(); - final Node root = readNode(buffer, header.mHeaderSize, reverseNodeMapping, + final Node root = readNode(reader.getBuffer(), header.mHeaderSize, reverseNodeMapping, reverseGroupMapping, header.mFormatOptions); FusionDictionary newDict = new FusionDictionary(root, header.mDictionaryOptions); diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictReader.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictReader.java new file mode 100644 index 000000000..57a583228 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictReader.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 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.makedict; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface; +import com.android.inputmethod.latin.utils.ByteArrayWrapper; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +public class BinaryDictReader { + + public interface FusionDictionaryBufferFactory { + public FusionDictionaryBufferInterface getFusionDictionaryBuffer(final File file) + throws FileNotFoundException, IOException; + } + + /** + * Creates FusionDictionaryBuffer from a ByteBuffer + */ + public static final class FusionDictionaryBufferFromByteBufferFactory + implements FusionDictionaryBufferFactory { + @Override + public FusionDictionaryBufferInterface getFusionDictionaryBuffer(final File file) + throws FileNotFoundException, IOException { + FileInputStream inStream = null; + ByteBuffer buffer = null; + try { + inStream = new FileInputStream(file); + buffer = inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, + 0, file.length()); + } finally { + if (inStream != null) { + inStream.close(); + } + } + if (buffer != null) { + return new BinaryDictInputOutput.ByteBufferWrapper(buffer); + } + return null; + } + } + + /** + * Creates FusionDictionaryBuffer from a byte array + */ + public static final class FusionDictionaryBufferFromByteArrayFactory + implements FusionDictionaryBufferFactory { + @Override + public FusionDictionaryBufferInterface getFusionDictionaryBuffer(final File file) + throws FileNotFoundException, IOException { + FileInputStream inStream = null; + try { + inStream = new FileInputStream(file); + final byte[] array = new byte[(int) file.length()]; + inStream.read(array); + return new ByteArrayWrapper(array); + } finally { + if (inStream != null) { + inStream.close(); + } + } + } + } + + private final File mDictionaryBinaryFile; + private FusionDictionaryBufferInterface mFusionDictionaryBuffer; + + public BinaryDictReader(final File file) { + mDictionaryBinaryFile = file; + mFusionDictionaryBuffer = null; + } + + public void openBuffer(final FusionDictionaryBufferFactory factory) + throws FileNotFoundException, IOException { + mFusionDictionaryBuffer = factory.getFusionDictionaryBuffer(mDictionaryBinaryFile); + } + + public FusionDictionaryBufferInterface getBuffer() { + return mFusionDictionaryBuffer; + } + + @UsedForTesting + public FusionDictionaryBufferInterface openAndGetBuffer( + final FusionDictionaryBufferFactory factory) + throws FileNotFoundException, IOException { + openBuffer(factory); + return getBuffer(); + } +} diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index feadcda76..2bb5d8b6e 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin.makedict; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; @@ -25,6 +26,40 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions public final class FormatSpec { /* + * File header layout is as follows: + * + * v | + * e | MAGIC_NUMBER + version of the file format, 2 bytes. + * r | + * sion + * + * o | + * p | not used 4 bits + * t | has bigrams ? 1 bit, 1 = yes, 0 = no : CONTAINS_BIGRAMS_FLAG + * i | FRENCH_LIGATURE_PROCESSING_FLAG + * o | supports dynamic updates ? 1 bit, 1 = yes, 0 = no : SUPPORTS_DYNAMIC_UPDATE + * n | GERMAN_UMLAUT_PROCESSING_FLAG + * f | + * lags + * + * h | + * e | size of the file header, 4bytes + * a | including the size of the magic number, the option flags and the header size + * d | + * ersize + * + * | attributes list + * + * attributes list is: + * <key> = | string of characters at the char format described below, with the terminator used + * | to signal the end of the string. + * <value> = | string of characters at the char format described below, with the terminator used + * | to signal the end of the string. + * if the size of already read < headersize, goto key. + * + */ + + /* * Array of Node(FusionDictionary.Node) layout is as follows: * * g | @@ -150,12 +185,10 @@ public final class FormatSpec { * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags */ - static final int VERSION_1_MAGIC_NUMBER = 0x78B1; - public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; - static final int MINIMUM_SUPPORTED_VERSION = 1; + public static final int MAGIC_NUMBER = 0x9BC13AFE; + static final int MINIMUM_SUPPORTED_VERSION = 2; static final int MAXIMUM_SUPPORTED_VERSION = 3; static final int NOT_A_VERSION_NUMBER = -1; - static final int FIRST_VERSION_WITH_HEADER_SIZE = 2; static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3; // These options need to be the same numeric values as the one in the native reading code. @@ -236,9 +269,12 @@ public final class FormatSpec { public static final class FormatOptions { public final int mVersion; public final boolean mSupportsDynamicUpdate; + @UsedForTesting public FormatOptions(final int version) { this(version, false); } + + @UsedForTesting public FormatOptions(final int version, final boolean supportsDynamicUpdate) { mVersion = version; if (version < FIRST_VERSION_WITH_DYNAMIC_UPDATE && supportsDynamicUpdate) { diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java index 9d041f4eb..065e00e4a 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java @@ -28,9 +28,10 @@ import com.android.inputmethod.latin.ExpandableDictionary; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.makedict.BinaryDictReader; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.ByteArrayWrapper; +import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener; @@ -38,7 +39,6 @@ import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils; import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -49,9 +49,6 @@ import java.util.concurrent.locks.ReentrantLock; * This class is a base class of a dictionary for the personalized prediction language model. */ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictionary { - public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) { - // TODO: Implement - } private static final String TAG = DynamicPredictionDictionaryBase.class.getSimpleName(); public static final boolean DBG_SAVE_RESTORE = false; @@ -75,8 +72,11 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona private final ReentrantLock mBigramListLock = new ReentrantLock(); private final SharedPreferences mPrefs; + private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions = + CollectionUtils.newArrayList(); + // Should always be false except when we use this class for test - @UsedForTesting boolean isTest = false; + @UsedForTesting boolean mIsTest = false; /* package */ DynamicPredictionDictionaryBase(final Context context, final String locale, final SharedPreferences sp, final String dictionaryType) { @@ -118,14 +118,15 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona } /** - * Pair will be added to the user history dictionary. + * Pair will be added to the personalization prediction dictionary. * * The first word may be null. That means we don't know the context, in other words, * it's only a unigram. The first word may also be an empty string : this means start * context, as in beginning of a sentence for example. * The second word may not be null (a NullPointerException would be thrown). */ - public int addToUserHistory(final String word1, final String word2, final boolean isValid) { + public int addToPersonalizationPredictionDictionary( + final String word1, final String word2, final boolean isValid) { if (word2.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH || (word1 != null && word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) { return -1; @@ -232,27 +233,17 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona }; // Load the dictionary from binary file - FileInputStream inStream = null; + final BinaryDictReader reader = new BinaryDictReader( + new File(getContext().getFilesDir(), fileName)); try { - final File file = new File(getContext().getFilesDir(), fileName); - final byte[] buffer = new byte[(int)file.length()]; - inStream = new FileInputStream(file); - inStream.read(buffer); - UserHistoryDictIOUtils.readDictionaryBinary( - new ByteArrayWrapper(buffer), listener); + reader.openBuffer(new BinaryDictReader.FusionDictionaryBufferFromByteArrayFactory()); + UserHistoryDictIOUtils.readDictionaryBinary(reader, listener); } catch (FileNotFoundException e) { // This is an expected condition: we don't have a user history dictionary for this // language yet. It will be created sometime later. } catch (IOException e) { Log.e(TAG, "IOException on opening a bytebuffer", e); } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } if (PROFILE_SAVE_RESTORE) { final long diff = System.currentTimeMillis() - now; Log.d(TAG, "PROF: Load UserHistoryDictionary: " @@ -296,8 +287,8 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona @Override protected Void doInBackground(final Void... v) { - if (mDynamicPredictionDictionary.isTest) { - // If isTest == true, wait until the lock is released. + if (mDynamicPredictionDictionary.mIsTest) { + // If mIsTest == true, wait until the lock is released. mDynamicPredictionDictionary.mBigramListLock.lock(); try { doWriteTaskLocked(); @@ -393,9 +384,19 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableDictiona final String word1, final String word2, final boolean isValid) { mBigramListLock.lock(); try { - addToUserHistory(word1, word2, isValid); + addToPersonalizationPredictionDictionary(word1, word2, isValid); } finally { mBigramListLock.unlock(); } } + + public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) { + session.setPredictionDictionary(mLocale, this); + mSessions.add(session); + session.onDictionaryReady(); + } + + public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) { + mSessions.remove(session); + } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index 19554d639..e38a235e9 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -27,7 +27,7 @@ import android.content.Context; public class PersonalizationDictionary extends ExpandableBinaryDictionary { private static final String NAME = "personalization"; - public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) { + public static void registerUpdateListener(PersonalizationDictionaryUpdateSession listener) { // TODO: Implement } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java index 9f013df1c..b4fd25024 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java @@ -20,6 +20,7 @@ import com.android.inputmethod.latin.utils.CollectionUtils; import android.content.Context; import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.util.Log; import java.lang.ref.SoftReference; @@ -58,6 +59,15 @@ public class PersonalizationDictionaryHelper { } } + public static void registerPersonalizationDictionaryUpdateSession(final Context context, + final PersonalizationDictionaryUpdateSession session) { + final PersonalizationPredictionDictionary dictionary = + getPersonalizationPredictionDictionary(context, + context.getResources().getConfiguration().locale.toString(), + PreferenceManager.getDefaultSharedPreferences(context)); + dictionary.registerUpdateSession(session); + } + public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary( final Context context, final String locale, final SharedPreferences sp) { synchronized (sLangPersonalizationDictCache) { diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java index c78e5a95b..534d3c518 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java @@ -16,6 +16,13 @@ package com.android.inputmethod.latin.personalization; -public interface PersonalizationDictionaryUpdateListener { - // TODO: Implement +import android.content.Context; +import android.content.res.Configuration; + +public class PersonalizationDictionarySessionRegister { + public static void init(Context context) { + } + + public static void onConfigurationChanged(final Context context, final Configuration conf) { + } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java new file mode 100644 index 000000000..e9dbbc273 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 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.personalization; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * This class is a session where a data provider can communicate with a personalization + * dictionary. + */ +public abstract class PersonalizationDictionaryUpdateSession { + /** + * This class is a parameter for a new unigram or bigram word which will be added + * to the personalization dictionary. + */ + public static class PersonalizationLanguageModelParam { + public final String mWord0; + public final String mWord1; + public final boolean mIsValid; + public final int mFrequency; + public PersonalizationLanguageModelParam(String word0, String word1, boolean isValid, + int frequency) { + mWord0 = word0; + mWord1 = word1; + mIsValid = isValid; + mFrequency = frequency; + } + } + + // TODO: Use a dynamic binary dictionary instead + public WeakReference<DynamicPredictionDictionaryBase> mPredictionDictionary; + public String mLocale; + + public abstract void onDictionaryReady(); + + public void setPredictionDictionary(String locale, DynamicPredictionDictionaryBase dictionary) { + mPredictionDictionary = new WeakReference<DynamicPredictionDictionaryBase>(dictionary); + mLocale = locale; + } + + protected DynamicPredictionDictionaryBase getPredictionDictionary() { + return mPredictionDictionary == null ? null : mPredictionDictionary.get(); + } + + private void unsetPredictionDictionary() { + final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary(); + if (dictionary == null) { + return; + } + dictionary.unRegisterUpdateSession(this); + } + + + public void closeSession() { + unsetPredictionDictionary(); + } + + public void addToPersonalizationDictionary( + final ArrayList<PersonalizationLanguageModelParam> lmParams) { + final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary(); + if (dictionary == null) { + return; + } + for (final PersonalizationLanguageModelParam lmParam : lmParams) { + dictionary.addToPersonalizationPredictionDictionary( + lmParam.mWord0, lmParam.mWord1, lmParam.mIsValid); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index 446777704..1677e1828 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -229,10 +229,10 @@ public final class SettingsFragment extends InputMethodSettingsFragment if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) { removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen()); - } else { - AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this); } + AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this); + setupKeyLongpressTimeoutSettings(prefs, res); setupKeypressVibrationDurationSettings(prefs, res); setupKeypressSoundVolumeSettings(prefs, res); diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 8aafb0738..a25cf620c 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -71,7 +71,7 @@ public final class SettingsValues { // Use bigrams to predict the next word when there is no input for it yet public final boolean mBigramPredictionEnabled; public final boolean mGestureInputEnabled; - public final boolean mGesturePreviewTrailEnabled; + public final boolean mGestureTrailEnabled; public final boolean mGestureFloatingPreviewTextEnabled; public final boolean mSlidingKeyInputPreviewEnabled; public final int mKeyLongpressTimeout; @@ -157,7 +157,7 @@ public final class SettingsValues { mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff); mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res); - mGesturePreviewTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); + mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true); mGestureFloatingPreviewTextEnabled = prefs.getBoolean( Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 692e7392c..eb6d7c106 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -20,7 +20,10 @@ import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.service.textservice.SpellCheckerService; +import android.text.InputType; import android.util.Log; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SuggestionsInfo; import com.android.inputmethod.keyboard.KeyboardLayoutSet; @@ -33,6 +36,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary; import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary; import com.android.inputmethod.latin.UserBinaryDictionary; +import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.StringUtils; @@ -58,6 +62,9 @@ public final class AndroidSpellCheckerService extends SpellCheckerService public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts"; + private static final int SPELLCHECKER_DUMMY_KEYBOARD_WIDTH = 480; + private static final int SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT = 368; + private final static String[] EMPTY_STRING_ARRAY = new String[0]; private Map<String, DictionaryPool> mDictionaryPools = CollectionUtils.newSynchronizedTreeMap(); private Map<String, UserBinaryDictionary> mUserDictionaries = @@ -401,9 +408,9 @@ public final class AndroidSpellCheckerService extends SpellCheckerService public DictAndKeyboard createDictAndKeyboard(final Locale locale) { final int script = getScriptFromLocale(locale); final String keyboardLayoutName = getKeyboardLayoutNameForScript(script); - final KeyboardLayoutSet keyboardLayoutSet = - KeyboardLayoutSet.createKeyboardSetForSpellChecker(this, locale.toString(), - keyboardLayoutName); + final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype( + locale.toString(), keyboardLayoutName, null); + final KeyboardLayoutSet keyboardLayoutSet = createKeyboardSetForSpellChecker(subtype); final DictionaryCollection dictionaryCollection = DictionaryFactory.createMainDictionaryFromManager(this, locale, @@ -431,4 +438,16 @@ public final class AndroidSpellCheckerService extends SpellCheckerService } return new DictAndKeyboard(dictionaryCollection, keyboardLayoutSet); } + + private KeyboardLayoutSet createKeyboardSetForSpellChecker(final InputMethodSubtype subtype) { + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.inputType = InputType.TYPE_CLASS_TEXT; + final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(this, editorInfo); + builder.setKeyboardGeometry( + SPELLCHECKER_DUMMY_KEYBOARD_WIDTH, SPELLCHECKER_DUMMY_KEYBOARD_HEIGHT); + builder.setSubtype(subtype); + builder.setIsSpellChecker(true /* isSpellChecker */); + builder.disableTouchPositionCorrectionData(); + return builder.build(); + } } diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 1dd04fc4d..bcf64a8e8 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -177,20 +177,9 @@ final class SuggestionStripLayoutHelper { return mMaxMoreSuggestionsRow; } - private int getMoreSuggestionsHeight() { - return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; - } - - public int setMoreSuggestionsHeight(final int remainingHeight) { - final int currentHeight = getMoreSuggestionsHeight(); - if (currentHeight <= remainingHeight) { - return currentHeight; - } - + public void setMoreSuggestionsHeight(final int remainingHeight) { mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) / mMoreSuggestionsRowHeight; - final int newHeight = getMoreSuggestionsHeight(); - return newHeight; } private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 497a791d9..2644f3c9c 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -135,8 +135,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - public int setMoreSuggestionsHeight(final int remainingHeight) { - return mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); + public void setMoreSuggestionsHeight(final int remainingHeight) { + mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); } public boolean isShowingAddToDictionaryHint() { @@ -270,15 +270,10 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return super.dispatchTouchEvent(me); } - final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; final int action = me.getAction(); - final long eventTime = me.getEventTime(); final int index = me.getActionIndex(); - final int id = me.getPointerId(index); final int x = (int)me.getX(index); final int y = (int)me.getY(index); - final int translatedX = moreKeysPanel.translateX(x); - final int translatedY = moreKeysPanel.translateY(y); if (mMoreSuggestionsMode == MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING) { if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance @@ -295,7 +290,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } // MORE_SUGGESTIONS_IN_SLIDING_MODE - mMoreSuggestionsView.processMotionEvent(action, translatedX, translatedY, id, eventTime); + me.setLocation(mMoreSuggestionsView.translateX(x), mMoreSuggestionsView.translateY(y)); + mMoreSuggestionsView.onTouchEvent(me); return true; } diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index ffec57548..22c92446a 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -20,10 +20,12 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Build; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.R; import java.util.ArrayList; import java.util.HashMap; @@ -215,6 +217,35 @@ public final class ResourceUtils { return null; } + public static int getDefaultKeyboardWidth(final Resources res) { + final DisplayMetrics dm = res.getDisplayMetrics(); + return dm.widthPixels; + } + + public static int getDefaultKeyboardHeight(final Resources res) { + final DisplayMetrics dm = res.getDisplayMetrics(); + final String keyboardHeightString = getDeviceOverrideValue(res, R.array.keyboard_heights); + final float keyboardHeight; + if (TextUtils.isEmpty(keyboardHeightString)) { + keyboardHeight = res.getDimension(R.dimen.keyboardHeight); + } else { + keyboardHeight = Float.parseFloat(keyboardHeightString) * dm.density; + } + final float maxKeyboardHeight = res.getFraction( + R.fraction.maxKeyboardHeight, dm.heightPixels, dm.heightPixels); + float minKeyboardHeight = res.getFraction( + R.fraction.minKeyboardHeight, dm.heightPixels, dm.heightPixels); + if (minKeyboardHeight < 0.0f) { + // Specified fraction was negative, so it should be calculated against display + // width. + minKeyboardHeight = -res.getFraction( + R.fraction.minKeyboardHeight, dm.widthPixels, dm.widthPixels); + } + // Keyboard height will not exceed maxKeyboardHeight and will not be less than + // minKeyboardHeight. + return (int)Math.max(Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); + } + public static boolean isValidFraction(final float fraction) { return fraction >= 0.0f; } diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java index d02f7187e..a0ad27cfb 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java @@ -21,7 +21,7 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.BinaryDictInputOutput; -import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface; +import com.android.inputmethod.latin.makedict.BinaryDictReader; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.Node; @@ -118,13 +118,13 @@ public final class UserHistoryDictIOUtils { /** * Reads dictionary from file. */ - public static void readDictionaryBinary(final FusionDictionaryBufferInterface buffer, + public static void readDictionaryBinary(final BinaryDictReader reader, final OnAddWordListener dict) { final Map<Integer, String> unigrams = CollectionUtils.newTreeMap(); final Map<Integer, Integer> frequencies = CollectionUtils.newTreeMap(); final Map<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap(); try { - BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies, + BinaryDictIOUtils.readUnigramsAndBigramsBinary(reader, unigrams, frequencies, bigrams); } catch (IOException e) { Log.e(TAG, "IO exception while reading file", e); diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java index 161386e2e..a75d353c9 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java +++ b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.utils; import android.inputmethodservice.InputMethodService; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.settings.Settings; public final class UserLogRingCharBuffer { @@ -64,6 +65,9 @@ public final class UserLogRingCharBuffer { if (!mEnabled) { return; } + if (LatinImeLogger.sUsabilityStudy) { + UsabilityStudyLogUtils.getInstance().writeChar(c, x, y); + } mCharBuf[mEnd] = c; mXBuf[mEnd] = x; mYBuf[mEnd] = y; diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 0bd493970..3a3408266 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -1414,7 +1414,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang "navigatePrevious", "clobberSettingsKey", "passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled", "isMultiLine", "tw", "th", "keys"); - public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) { + public static void mainKeyboardView_setKeyboard(final Keyboard keyboard, + final int orientation) { final KeyboardId kid = keyboard.mId; final boolean isPasswordView = kid.passwordInput(); final ResearchLogger researchLogger = getInstance(); @@ -1422,7 +1423,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang researchLogger.enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD, KeyboardId.elementIdToName(kid.mElementId), kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), - kid.mOrientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(), + orientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(), kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey, isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey, kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth, |