aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java289
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java284
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java82
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java126
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java3
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java53
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java4
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java50
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java21
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java38
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java11
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java143
-rw-r--r--java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java8
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java140
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java9
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java3
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java11
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictDecoder.java3
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java81
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java61
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java46
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java5
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java5
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java6
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java76
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java128
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettings.java52
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java31
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java6
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java20
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java181
-rw-r--r--java/src/com/android/inputmethod/latin/utils/WordProperty.java89
33 files changed, 1103 insertions, 968 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 810bd9150..f3cfae203 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -16,10 +16,7 @@
package com.android.inputmethod.keyboard;
-import android.animation.Animator;
import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.SharedPreferences;
@@ -35,13 +32,10 @@ import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
@@ -53,6 +47,7 @@ import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer;
import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
import com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview;
@@ -71,9 +66,6 @@ import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils;
import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import com.android.inputmethod.research.ResearchLogger;
-import java.util.ArrayDeque;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.WeakHashMap;
/**
@@ -161,25 +153,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
// Key preview
private static final boolean FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED = false;
- private final int mKeyPreviewLayoutId;
- private final int mKeyPreviewOffset;
- private final int mKeyPreviewHeight;
- // Free {@link TextView} pool that can be used for key preview.
- private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque();
- // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview.
- private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap();
- private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
- private boolean mShowKeyPreviewPopup = true;
- private int mKeyPreviewLingerTimeout;
- private int mKeyPreviewZoomInDuration;
- private int mKeyPreviewZoomOutDuration;
- private static final float KEY_PREVIEW_START_ZOOM_IN_SCALE = 0.7f;
- private static final float KEY_PREVIEW_END_ZOOM_IN_SCALE = 1.0f;
- private static final float KEY_PREVIEW_END_ZOOM_OUT_SCALE = 0.7f;
- private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
- new AccelerateInterpolator();
- private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
- new DecelerateInterpolator();
+ private final KeyPreviewDrawParams mKeyPreviewDrawParams;
+ private final KeyPreviewChoreographer mKeyPreviewChoreographer;
// More keys keyboard
private final Paint mBackgroundDimAlphaPaint = new Paint();
@@ -267,21 +242,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
- mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
- R.styleable.MainKeyboardView_keyPreviewOffset, 0);
- mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
- R.styleable.MainKeyboardView_keyPreviewHeight, 0);
- mKeyPreviewLingerTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
- mKeyPreviewLayoutId = mainKeyboardViewAttr.getResourceId(
- R.styleable.MainKeyboardView_keyPreviewLayout, 0);
- if (mKeyPreviewLayoutId == 0) {
- mShowKeyPreviewPopup = false;
- }
- mKeyPreviewZoomInDuration = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyPreviewZoomInDuration, 0);
- mKeyPreviewZoomOutDuration = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyPreviewZoomOutDuration, 0);
+ mKeyPreviewDrawParams = new KeyPreviewDrawParams(mainKeyboardViewAttr);
+ mKeyPreviewChoreographer = new KeyPreviewChoreographer(mKeyPreviewDrawParams);
+
final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
@@ -455,8 +418,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
* @see #isKeyPreviewPopupEnabled()
*/
public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
- mShowKeyPreviewPopup = previewEnabled;
- mKeyPreviewLingerTimeout = delay;
+ mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay);
}
private void locatePreviewPlacerView() {
@@ -496,62 +458,16 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
* @see #setKeyPreviewPopupEnabled(boolean, int)
*/
public boolean isKeyPreviewPopupEnabled() {
- return mShowKeyPreviewPopup;
- }
-
- private TextView getKeyPreviewTextView(final Key key) {
- TextView previewTextView = mShowingKeyPreviewTextViews.remove(key);
- if (previewTextView != null) {
- return previewTextView;
- }
- previewTextView = mFreeKeyPreviewTextViews.poll();
- if (previewTextView != null) {
- return previewTextView;
- }
- final Context context = getContext();
- if (mKeyPreviewLayoutId != 0) {
- previewTextView = (TextView)LayoutInflater.from(context)
- .inflate(mKeyPreviewLayoutId, null);
- } else {
- previewTextView = new TextView(context);
- }
- locatePreviewPlacerView();
- mDrawingPreviewPlacerView.addView(
- previewTextView, ViewLayoutUtils.newLayoutParam(mDrawingPreviewPlacerView, 0, 0));
- return previewTextView;
+ return mKeyPreviewDrawParams.isPopupEnabled();
}
// Implements {@link DrawingHandler.Callbacks} method.
@Override
public void dismissAllKeyPreviews() {
- for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) {
- dismissKeyPreviewWithoutDelay(key);
- }
+ mKeyPreviewChoreographer.dismissAllKeyPreviews();
PointerTracker.setReleasedKeyGraphicsToAllKeys();
}
- // Background state set
- private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
- { // STATE_MIDDLE
- EMPTY_STATE_SET,
- { R.attr.state_has_morekeys }
- },
- { // STATE_LEFT
- { R.attr.state_left_edge },
- { R.attr.state_left_edge, R.attr.state_has_morekeys }
- },
- { // STATE_RIGHT
- { R.attr.state_right_edge },
- { R.attr.state_right_edge, R.attr.state_has_morekeys }
- }
- };
- private static final int STATE_MIDDLE = 0;
- private static final int STATE_LEFT = 1;
- private static final int STATE_RIGHT = 2;
- private static final int STATE_NORMAL = 0;
- private static final int STATE_HAS_MOREKEYS = 1;
-
- // TODO: Take this method out of this class.
@Override
public void showKeyPreview(final Key key) {
// If key is invalid or IME is already closed, we must not show key preview.
@@ -563,197 +479,36 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
final Keyboard keyboard = getKeyboard();
- if (!mShowKeyPreviewPopup) {
- previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap;
+ if (!previewParams.isPopupEnabled()) {
+ previewParams.setVisibleOffset(-keyboard.mVerticalGap);
return;
}
- final TextView previewTextView = getKeyPreviewTextView(key);
- final KeyDrawParams drawParams = mKeyDrawParams;
- previewTextView.setTextColor(drawParams.mPreviewTextColor);
- final Drawable background = previewTextView.getBackground();
- final String label = key.getPreviewLabel();
- // What we show as preview should match what we show on a key top in onDraw().
- if (label != null) {
- // TODO Should take care of temporaryShiftLabel here.
- previewTextView.setCompoundDrawables(null, null, null, null);
- previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
- key.selectPreviewTextSize(drawParams));
- previewTextView.setTypeface(key.selectPreviewTypeface(drawParams));
- previewTextView.setText(label);
- } else {
- previewTextView.setCompoundDrawables(null, null, null,
- key.getPreviewIcon(keyboard.mIconsSet));
- previewTextView.setText(null);
- }
-
- previewTextView.measure(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- final int keyDrawWidth = key.getDrawWidth();
- final int previewWidth = previewTextView.getMeasuredWidth();
- final int previewHeight = mKeyPreviewHeight;
- // The width and height of visible part of the key preview background. The content marker
- // of the background 9-patch have to cover the visible part of the background.
- previewParams.mPreviewVisibleWidth = previewWidth - previewTextView.getPaddingLeft()
- - previewTextView.getPaddingRight();
- previewParams.mPreviewVisibleHeight = previewHeight - previewTextView.getPaddingTop()
- - previewTextView.getPaddingBottom();
- // The distance between the top edge of the parent key and the bottom of the visible part
- // of the key preview background.
- previewParams.mPreviewVisibleOffset =
- mKeyPreviewOffset - previewTextView.getPaddingBottom();
+ locatePreviewPlacerView();
+ final TextView previewTextView = mKeyPreviewChoreographer.getKeyPreviewTextView(
+ key, mDrawingPreviewPlacerView);
getLocationInWindow(mOriginCoords);
- // The key preview is horizontally aligned with the center of the visible part of the
- // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
- // the left/right background is used if such background is specified.
- final int statePosition;
- int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
- + CoordinateUtils.x(mOriginCoords);
- if (previewX < 0) {
- previewX = 0;
- statePosition = STATE_LEFT;
- } else if (previewX > getWidth() - previewWidth) {
- previewX = getWidth() - previewWidth;
- statePosition = STATE_RIGHT;
- } else {
- statePosition = STATE_MIDDLE;
- }
- // The key preview is placed vertically above the top edge of the parent key with an
- // arbitrary offset.
- final int previewY = key.getY() - previewHeight + mKeyPreviewOffset
- + CoordinateUtils.y(mOriginCoords);
-
- if (background != null) {
- final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
- background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
- }
- ViewLayoutUtils.placeViewAt(
- previewTextView, previewX, previewY, previewWidth, previewHeight);
-
- if (!isHardwareAccelerated()) {
- previewTextView.setVisibility(VISIBLE);
- mShowingKeyPreviewTextViews.put(key, previewTextView);
- return;
- }
- previewTextView.setPivotX(previewWidth / 2.0f);
- previewTextView.setPivotY(previewHeight);
-
- final Animator zoomIn = createZoomInAniation(key, previewTextView);
- final Animator zoomOut = createZoomOutAnimation(key, previewTextView);
- final KeyPreviewAnimations animation = new KeyPreviewAnimations(zoomIn, zoomOut);
- previewTextView.setTag(animation);
- animation.startZoomIn();
- }
-
- // TODO: Move this internal class out to a separate external class.
- private static class KeyPreviewAnimations extends AnimatorListenerAdapter {
- private final Animator mZoomIn;
- private final Animator mZoomOut;
-
- public KeyPreviewAnimations(final Animator zoomIn, final Animator zoomOut) {
- mZoomIn = zoomIn;
- mZoomOut = zoomOut;
- }
-
- public void startZoomIn() {
- mZoomIn.start();
- }
-
- public void startZoomOut() {
- if (mZoomIn.isRunning()) {
- mZoomIn.addListener(this);
- return;
- }
- mZoomOut.start();
- }
-
- @Override
- public void onAnimationEnd(final Animator animation) {
- mZoomOut.start();
- }
- }
-
- // TODO: Take this method out of this class.
- private Animator createZoomInAniation(final Key key, final TextView previewTextView) {
- final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
- previewTextView, SCALE_X, KEY_PREVIEW_START_ZOOM_IN_SCALE,
- KEY_PREVIEW_END_ZOOM_IN_SCALE);
- final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
- previewTextView, SCALE_Y, KEY_PREVIEW_START_ZOOM_IN_SCALE,
- KEY_PREVIEW_END_ZOOM_IN_SCALE);
- final AnimatorSet zoomInAnimation = new AnimatorSet();
- zoomInAnimation.play(scaleXAnimation).with(scaleYAnimation);
- // TODO: Implement preference option to control key preview animation duration.
- zoomInAnimation.setDuration(mKeyPreviewZoomInDuration);
- zoomInAnimation.setInterpolator(DECELERATE_INTERPOLATOR);
- zoomInAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(final Animator animation) {
- previewTextView.setVisibility(VISIBLE);
- mShowingKeyPreviewTextViews.put(key, previewTextView);
- }
- });
- return zoomInAnimation;
- }
-
- // TODO: Take this method out of this class.
- private Animator createZoomOutAnimation(final Key key, final TextView previewTextView) {
- final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
- previewTextView, SCALE_X, KEY_PREVIEW_END_ZOOM_OUT_SCALE);
- final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
- previewTextView, SCALE_Y, KEY_PREVIEW_END_ZOOM_OUT_SCALE);
- final AnimatorSet zoomOutAnimation = new AnimatorSet();
- zoomOutAnimation.play(scaleXAnimation).with(scaleYAnimation);
- // TODO: Implement preference option to control key preview animation duration.
- zoomOutAnimation.setDuration(mKeyPreviewZoomOutDuration);
- zoomOutAnimation.setInterpolator(ACCELERATE_INTERPOLATOR);
- zoomOutAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(final Animator animation) {
- dismissKeyPreviewWithoutDelay(key);
- }
- });
- return zoomOutAnimation;
+ mKeyPreviewChoreographer.placeKeyPreview(key, previewTextView, keyboard.mIconsSet,
+ mKeyDrawParams, getWidth(), mOriginCoords);
+ mKeyPreviewChoreographer.showKeyPreview(key, previewTextView, isHardwareAccelerated());
}
// Implements {@link TimerHandler.Callbacks} method.
- // TODO: Take this method out of this class.
@Override
public void dismissKeyPreviewWithoutDelay(final Key key) {
- if (key == null) {
- return;
- }
- final TextView previewTextView = mShowingKeyPreviewTextViews.remove(key);
- if (previewTextView != null) {
- final Object tag = previewTextView.getTag();
- if (tag instanceof Animator) {
- ((Animator)tag).cancel();
- }
- previewTextView.setTag(null);
- previewTextView.setVisibility(INVISIBLE);
- mFreeKeyPreviewTextViews.add(previewTextView);
- }
+ mKeyPreviewChoreographer.dismissKeyPreview(key, false /* withAnimation */);
// To redraw key top letter.
invalidateKey(key);
}
- // TODO: Take this method out of this class.
@Override
public void dismissKeyPreview(final Key key) {
- final TextView previewTextView = mShowingKeyPreviewTextViews.get(key);
- if (previewTextView == null) {
- return;
- }
if (!isHardwareAccelerated()) {
// TODO: Implement preference option to control key preview method and duration.
- mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, key);
+ mDrawingHandler.dismissKeyPreview(mKeyPreviewDrawParams.getLingerTimeout(), key);
return;
}
- final Object tag = previewTextView.getTag();
- if (tag instanceof KeyPreviewAnimations) {
- final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag;
- animation.startZoomOut();
- }
+ mKeyPreviewChoreographer.dismissKeyPreview(key, true /* withAnimation */);
}
public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
@@ -913,7 +668,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
// aligned with the bottom edge of the visible part of the key preview.
// {@code mPreviewVisibleOffset} has been set appropriately in
// {@link KeyboardView#showKeyPreview(PointerTracker)}.
- final int pointY = key.getY() + mKeyPreviewDrawParams.mPreviewVisibleOffset;
+ final int pointY = key.getY() + mKeyPreviewDrawParams.getVisibleOffset();
moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
tracker.onShowMoreKeysPanel(moreKeysPanel);
// TODO: Implement zoom in animation of more keys panel.
@@ -1126,7 +881,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
// Don't draw key top letter when key preview is showing.
if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED
- && mShowingKeyPreviewTextViews.containsKey(key)) {
+ && mKeyPreviewChoreographer.isShowingKeyPreview(key)) {
// TODO: Fade out animation for the key top letter, and fade in animation for the key
// background color when the user presses the key.
return;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index 670524380..2bff107eb 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -285,7 +285,7 @@ public final class MoreKeysKeyboard extends Keyboard {
// {@link MoreKeysKeyboardParams#setParameters(int,int,int,int,int,int,boolean,int)}.
final boolean singleMoreKeyWithPreview = parentKeyboardView.isKeyPreviewPopupEnabled()
&& !parentKey.noKeyPreview() && moreKeys.length == 1
- && keyPreviewDrawParams.mPreviewVisibleWidth > 0;
+ && keyPreviewDrawParams.getVisibleWidth() > 0;
if (singleMoreKeyWithPreview) {
// Use pre-computed width and height if this more keys keyboard has only one key to
// mitigate visual flicker between key preview and more keys keyboard.
@@ -294,8 +294,8 @@ public final class MoreKeysKeyboard extends Keyboard {
// left/right/top paddings. The bottom paddings of both backgrounds don't need to
// be considered because the vertical positions of both backgrounds were already
// adjusted with their bottom paddings deducted.
- width = keyPreviewDrawParams.mPreviewVisibleWidth;
- height = keyPreviewDrawParams.mPreviewVisibleHeight + mParams.mVerticalGap;
+ width = keyPreviewDrawParams.getVisibleWidth();
+ height = keyPreviewDrawParams.getVisibleHeight() + mParams.mVerticalGap;
} else {
final float padding = context.getResources().getDimension(
R.dimen.config_more_keys_keyboard_key_horizontal_padding)
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
new file mode 100644
index 000000000..df869b2bc
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
@@ -0,0 +1,284 @@
+/*
+ * 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.keyboard.internal;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.ViewLayoutUtils;
+
+import java.util.ArrayDeque;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * This class controls pop up key previews. This class decides:
+ * - what kind of key previews should be shown.
+ * - where key previews should be placed.
+ * - how key previews should be shown and dismissed.
+ */
+public final class KeyPreviewChoreographer {
+ // Free {@link TextView} pool that can be used for key preview.
+ private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque();
+ // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview.
+ private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap();
+
+ private final KeyPreviewDrawParams mParams;
+
+ public KeyPreviewChoreographer(final KeyPreviewDrawParams params) {
+ mParams = params;
+ }
+
+ public TextView getKeyPreviewTextView(final Key key, final ViewGroup placerView) {
+ TextView previewTextView = mShowingKeyPreviewTextViews.remove(key);
+ if (previewTextView != null) {
+ return previewTextView;
+ }
+ previewTextView = mFreeKeyPreviewTextViews.poll();
+ if (previewTextView != null) {
+ return previewTextView;
+ }
+ final Context context = placerView.getContext();
+ if (mParams.mLayoutId != 0) {
+ previewTextView = (TextView)LayoutInflater.from(context)
+ .inflate(mParams.mLayoutId, null);
+ } else {
+ previewTextView = new TextView(context);
+ }
+ placerView.addView(previewTextView, ViewLayoutUtils.newLayoutParam(placerView, 0, 0));
+ return previewTextView;
+ }
+
+ public boolean isShowingKeyPreview(final Key key) {
+ return mShowingKeyPreviewTextViews.containsKey(key);
+ }
+
+ public void dismissAllKeyPreviews() {
+ for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) {
+ dismissKeyPreview(key, false /* withAnimation */);
+ }
+ }
+
+ public void dismissKeyPreview(final Key key, final boolean withAnimation) {
+ if (key == null) {
+ return;
+ }
+ final TextView previewTextView = mShowingKeyPreviewTextViews.get(key);
+ if (previewTextView == null) {
+ return;
+ }
+ final Object tag = previewTextView.getTag();
+ if (withAnimation) {
+ if (tag instanceof KeyPreviewAnimations) {
+ final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag;
+ animation.startZoomOut();
+ }
+ return;
+ }
+ // Dismiss preview without animation.
+ if (tag instanceof Animator) {
+ ((Animator)tag).cancel();
+ }
+ previewTextView.setTag(null);
+ previewTextView.setVisibility(View.INVISIBLE);
+ mFreeKeyPreviewTextViews.add(previewTextView);
+ }
+
+ // Background state set
+ private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
+ { // STATE_MIDDLE
+ {},
+ { R.attr.state_has_morekeys }
+ },
+ { // STATE_LEFT
+ { R.attr.state_left_edge },
+ { R.attr.state_left_edge, R.attr.state_has_morekeys }
+ },
+ { // STATE_RIGHT
+ { R.attr.state_right_edge },
+ { R.attr.state_right_edge, R.attr.state_has_morekeys }
+ }
+ };
+ private static final int STATE_MIDDLE = 0;
+ private static final int STATE_LEFT = 1;
+ private static final int STATE_RIGHT = 2;
+ private static final int STATE_NORMAL = 0;
+ private static final int STATE_HAS_MOREKEYS = 1;
+
+ public void placeKeyPreview(final Key key, final TextView previewTextView,
+ final KeyboardIconsSet iconsSet, final KeyDrawParams drawParams,
+ final int keyboardViewWidth, final int[] originCoords) {
+ previewTextView.setTextColor(drawParams.mPreviewTextColor);
+ final Drawable background = previewTextView.getBackground();
+ final String label = key.getPreviewLabel();
+ // What we show as preview should match what we show on a key top in onDraw().
+ if (label != null) {
+ // TODO Should take care of temporaryShiftLabel here.
+ previewTextView.setCompoundDrawables(null, null, null, null);
+ previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ key.selectPreviewTextSize(drawParams));
+ previewTextView.setTypeface(key.selectPreviewTypeface(drawParams));
+ previewTextView.setText(label);
+ } else {
+ previewTextView.setCompoundDrawables(null, null, null, key.getPreviewIcon(iconsSet));
+ previewTextView.setText(null);
+ }
+
+ previewTextView.measure(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ mParams.setGeometry(previewTextView);
+ final int previewWidth = previewTextView.getMeasuredWidth();
+ final int previewHeight = mParams.mPreviewHeight;
+ final int keyDrawWidth = key.getDrawWidth();
+ // The key preview is horizontally aligned with the center of the visible part of the
+ // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
+ // the left/right background is used if such background is specified.
+ final int statePosition;
+ int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
+ + CoordinateUtils.x(originCoords);
+ if (previewX < 0) {
+ previewX = 0;
+ statePosition = STATE_LEFT;
+ } else if (previewX > keyboardViewWidth - previewWidth) {
+ previewX = keyboardViewWidth - previewWidth;
+ statePosition = STATE_RIGHT;
+ } else {
+ statePosition = STATE_MIDDLE;
+ }
+ // The key preview is placed vertically above the top edge of the parent key with an
+ // arbitrary offset.
+ final int previewY = key.getY() - previewHeight + mParams.mPreviewOffset
+ + CoordinateUtils.y(originCoords);
+
+ if (background != null) {
+ final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
+ background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
+ }
+ ViewLayoutUtils.placeViewAt(
+ previewTextView, previewX, previewY, previewWidth, previewHeight);
+ previewTextView.setPivotX(previewWidth / 2.0f);
+ previewTextView.setPivotY(previewHeight);
+ }
+
+ public void showKeyPreview(final Key key, final TextView previewTextView,
+ final boolean withAnimation) {
+ if (!withAnimation) {
+ previewTextView.setVisibility(View.VISIBLE);
+ mShowingKeyPreviewTextViews.put(key, previewTextView);
+ return;
+ }
+
+ // Show preview with animation.
+ final Animator zoomIn = createZoomInAniation(key, previewTextView);
+ final Animator zoomOut = createZoomOutAnimation(key, previewTextView);
+ final KeyPreviewAnimations animation = new KeyPreviewAnimations(zoomIn, zoomOut);
+ previewTextView.setTag(animation);
+ animation.startZoomIn();
+ }
+
+ // TODO: Move these parameters to resources or preferences.
+ private static final float KEY_PREVIEW_START_ZOOM_IN_SCALE = 0.7f;
+ private static final float KEY_PREVIEW_END_ZOOM_IN_SCALE = 1.0f;
+ private static final float KEY_PREVIEW_END_ZOOM_OUT_SCALE = 0.7f;
+ private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR =
+ new AccelerateInterpolator();
+ private static final DecelerateInterpolator DECELERATE_INTERPOLATOR =
+ new DecelerateInterpolator();
+
+ private Animator createZoomInAniation(final Key key, final TextView previewTextView) {
+ final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
+ previewTextView, View.SCALE_X, KEY_PREVIEW_START_ZOOM_IN_SCALE,
+ KEY_PREVIEW_END_ZOOM_IN_SCALE);
+ final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
+ previewTextView, View.SCALE_Y, KEY_PREVIEW_START_ZOOM_IN_SCALE,
+ KEY_PREVIEW_END_ZOOM_IN_SCALE);
+ final AnimatorSet zoomInAnimation = new AnimatorSet();
+ zoomInAnimation.play(scaleXAnimation).with(scaleYAnimation);
+ // TODO: Implement preference option to control key preview animation duration.
+ zoomInAnimation.setDuration(mParams.mZoomInDuration);
+ zoomInAnimation.setInterpolator(DECELERATE_INTERPOLATOR);
+ zoomInAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(final Animator animation) {
+ showKeyPreview(key, previewTextView, false /* withAnimation */);
+ }
+ });
+ return zoomInAnimation;
+ }
+
+ private Animator createZoomOutAnimation(final Key key, final TextView previewTextView) {
+ final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat(
+ previewTextView, View.SCALE_X, KEY_PREVIEW_END_ZOOM_OUT_SCALE);
+ final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat(
+ previewTextView, View.SCALE_Y, KEY_PREVIEW_END_ZOOM_OUT_SCALE);
+ final AnimatorSet zoomOutAnimation = new AnimatorSet();
+ zoomOutAnimation.play(scaleXAnimation).with(scaleYAnimation);
+ // TODO: Implement preference option to control key preview animation duration.
+ final int zoomOutDuration = Math.min(mParams.mZoomOutDuration, mParams.getLingerTimeout());
+ zoomOutAnimation.setDuration(zoomOutDuration);
+ zoomOutAnimation.setInterpolator(ACCELERATE_INTERPOLATOR);
+ zoomOutAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(final Animator animation) {
+ dismissKeyPreview(key, false /* withAnimation */);
+ }
+ });
+ return zoomOutAnimation;
+ }
+
+ private static class KeyPreviewAnimations extends AnimatorListenerAdapter {
+ private final Animator mZoomIn;
+ private final Animator mZoomOut;
+
+ public KeyPreviewAnimations(final Animator zoomIn, final Animator zoomOut) {
+ mZoomIn = zoomIn;
+ mZoomOut = zoomOut;
+ }
+
+ public void startZoomIn() {
+ mZoomIn.start();
+ }
+
+ public void startZoomOut() {
+ if (mZoomIn.isRunning()) {
+ mZoomIn.addListener(this);
+ return;
+ }
+ mZoomOut.start();
+ }
+
+ @Override
+ public void onAnimationEnd(final Animator animation) {
+ mZoomOut.start();
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
index 609d1a57f..ff493b198 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
@@ -16,7 +16,22 @@
package com.android.inputmethod.keyboard.internal;
+import android.content.res.TypedArray;
+import android.view.View;
+
+import com.android.inputmethod.latin.R;
+
public final class KeyPreviewDrawParams {
+ // XML attributes of {@link MainKeyboardView}.
+ public final int mLayoutId;
+ public final int mPreviewOffset;
+ public final int mPreviewHeight;
+ // TODO: Move those parameters to preferences.
+ public final int mZoomInDuration;
+ public final int mZoomOutDuration;
+ private int mLingerTimeout;
+ private boolean mShowPopup = true;
+
// The graphical geometry of the key preview.
// <-width->
// +-------+ ^
@@ -34,11 +49,72 @@ public final class KeyPreviewDrawParams {
// paddings. To align the more keys keyboard panel's visible part with the visible part of
// the background, we need to record the width and height of key preview that don't include
// invisible paddings.
- public int mPreviewVisibleWidth;
- public int mPreviewVisibleHeight;
+ private int mVisibleWidth;
+ private int mVisibleHeight;
// The key preview may have an arbitrary offset and its background that may have a bottom
// padding. To align the more keys keyboard and the key preview we also need to record the
// offset between the top edge of parent key and the bottom of the visible part of key
// preview background.
- public int mPreviewVisibleOffset;
+ private int mVisibleOffset;
+
+ public KeyPreviewDrawParams(final TypedArray mainKeyboardViewAttr) {
+ mPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
+ R.styleable.MainKeyboardView_keyPreviewOffset, 0);
+ mPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
+ R.styleable.MainKeyboardView_keyPreviewHeight, 0);
+ mLingerTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0);
+ mLayoutId = mainKeyboardViewAttr.getResourceId(
+ R.styleable.MainKeyboardView_keyPreviewLayout, 0);
+ if (mLayoutId == 0) {
+ mShowPopup = false;
+ }
+ mZoomInDuration = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyPreviewZoomInDuration, 0);
+ mZoomOutDuration = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyPreviewZoomOutDuration, 0);
+ }
+
+ public void setVisibleOffset(final int previewVisibleOffset) {
+ mVisibleOffset = previewVisibleOffset;
+ }
+
+ public int getVisibleOffset() {
+ return mVisibleOffset;
+ }
+
+ public void setGeometry(final View previewTextView) {
+ final int previewWidth = previewTextView.getMeasuredWidth();
+ final int previewHeight = mPreviewHeight;
+ // The width and height of visible part of the key preview background. The content marker
+ // of the background 9-patch have to cover the visible part of the background.
+ mVisibleWidth = previewWidth - previewTextView.getPaddingLeft()
+ - previewTextView.getPaddingRight();
+ mVisibleHeight = previewHeight - previewTextView.getPaddingTop()
+ - previewTextView.getPaddingBottom();
+ // The distance between the top edge of the parent key and the bottom of the visible part
+ // of the key preview background.
+ setVisibleOffset(mPreviewOffset - previewTextView.getPaddingBottom());
+ }
+
+ public int getVisibleWidth() {
+ return mVisibleWidth;
+ }
+
+ public int getVisibleHeight() {
+ return mVisibleHeight;
+ }
+
+ public void setPopupEnabled(final boolean enabled, final int lingerTimeout) {
+ mShowPopup = enabled;
+ mLingerTimeout = lingerTimeout;
+ }
+
+ public boolean isPopupEnabled() {
+ return mShowPopup;
+ }
+
+ public int getLingerTimeout() {
+ return mLingerTimeout;
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index c51941d32..2925a4b76 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -56,10 +56,9 @@ public final class KeySpecParser {
return keySpec.startsWith(KeyboardIconsSet.PREFIX_ICON);
}
- private static boolean hasCode(final String keySpec) {
- final int end = indexOfLabelEnd(keySpec, 0);
- if (end > 0 && end + 1 < keySpec.length() && keySpec.startsWith(
- KeyboardCodesSet.PREFIX_CODE, end + 1)) {
+ private static boolean hasCode(final String keySpec, final int labelEnd) {
+ if (labelEnd > 0 && labelEnd + 1 < keySpec.length()
+ && keySpec.startsWith(KeyboardCodesSet.PREFIX_CODE, labelEnd + 1)) {
return true;
}
return false;
@@ -84,16 +83,20 @@ public final class KeySpecParser {
return sb.toString();
}
- private static int indexOfLabelEnd(final String keySpec, final int start) {
- if (keySpec.indexOf(BACKSLASH, start) < 0) {
- final int end = keySpec.indexOf(VERTICAL_BAR, start);
- if (end == 0) {
- throw new KeySpecParserError(VERTICAL_BAR + " at " + start + ": " + keySpec);
+ private static int indexOfLabelEnd(final String keySpec) {
+ final int length = keySpec.length();
+ if (keySpec.indexOf(BACKSLASH) < 0) {
+ final int labelEnd = keySpec.indexOf(VERTICAL_BAR);
+ if (labelEnd == 0) {
+ if (length == 1) {
+ // Treat a sole vertical bar as a special case of key label.
+ return -1;
+ }
+ throw new KeySpecParserError("Empty label");
}
- return end;
+ return labelEnd;
}
- final int length = keySpec.length();
- for (int pos = start; pos < length; pos++) {
+ for (int pos = 0; pos < length; pos++) {
final char c = keySpec.charAt(pos);
if (c == BACKSLASH && pos + 1 < length) {
// Skip escape char
@@ -105,35 +108,55 @@ public final class KeySpecParser {
return -1;
}
+ private static String getBeforeLabelEnd(final String keySpec, final int labelEnd) {
+ return (labelEnd < 0) ? keySpec : keySpec.substring(0, labelEnd);
+ }
+
+ private static String getAfterLabelEnd(final String keySpec, final int labelEnd) {
+ return keySpec.substring(labelEnd + /* VERTICAL_BAR */1);
+ }
+
+ private static void checkDoubleLabelEnd(final String keySpec, final int labelEnd) {
+ if (indexOfLabelEnd(getAfterLabelEnd(keySpec, labelEnd)) < 0) {
+ return;
+ }
+ throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + keySpec);
+ }
+
public static String getLabel(final String keySpec) {
+ if (keySpec == null) {
+ // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
+ return null;
+ }
if (hasIcon(keySpec)) {
return null;
}
- final int end = indexOfLabelEnd(keySpec, 0);
- final String label = (end > 0) ? parseEscape(keySpec.substring(0, end))
- : parseEscape(keySpec);
+ final int labelEnd = indexOfLabelEnd(keySpec);
+ final String label = parseEscape(getBeforeLabelEnd(keySpec, labelEnd));
if (label.isEmpty()) {
throw new KeySpecParserError("Empty label: " + keySpec);
}
return label;
}
- private static String getOutputTextInternal(final String keySpec) {
- final int end = indexOfLabelEnd(keySpec, 0);
- if (end <= 0) {
+ private static String getOutputTextInternal(final String keySpec, final int labelEnd) {
+ if (labelEnd <= 0) {
return null;
}
- if (indexOfLabelEnd(keySpec, end + 1) >= 0) {
- throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + keySpec);
- }
- return parseEscape(keySpec.substring(end + /* VERTICAL_BAR */1));
+ checkDoubleLabelEnd(keySpec, labelEnd);
+ return parseEscape(getAfterLabelEnd(keySpec, labelEnd));
}
public static String getOutputText(final String keySpec) {
- if (hasCode(keySpec)) {
+ if (keySpec == null) {
+ // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
return null;
}
- final String outputText = getOutputTextInternal(keySpec);
+ final int labelEnd = indexOfLabelEnd(keySpec);
+ if (hasCode(keySpec, labelEnd)) {
+ return null;
+ }
+ final String outputText = getOutputTextInternal(keySpec, labelEnd);
if (outputText != null) {
if (StringUtils.codePointCount(outputText) == 1) {
// If output text is one code point, it should be treated as a code.
@@ -154,14 +177,16 @@ public final class KeySpecParser {
}
public static int getCode(final String keySpec, final KeyboardCodesSet codesSet) {
- if (hasCode(keySpec)) {
- final int end = indexOfLabelEnd(keySpec, 0);
- if (indexOfLabelEnd(keySpec, end + 1) >= 0) {
- throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + keySpec);
- }
- return parseCode(keySpec.substring(end + 1), codesSet, CODE_UNSPECIFIED);
+ if (keySpec == null) {
+ // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
+ return CODE_UNSPECIFIED;
}
- final String outputText = getOutputTextInternal(keySpec);
+ final int labelEnd = indexOfLabelEnd(keySpec);
+ if (hasCode(keySpec, labelEnd)) {
+ checkDoubleLabelEnd(keySpec, labelEnd);
+ return parseCode(getAfterLabelEnd(keySpec, labelEnd), codesSet, CODE_UNSPECIFIED);
+ }
+ final String outputText = getOutputTextInternal(keySpec, labelEnd);
if (outputText != null) {
// If output text is one code point, it should be treated as a code.
// See {@link #getOutputText(String)}.
@@ -171,35 +196,40 @@ public final class KeySpecParser {
return CODE_OUTPUT_TEXT;
}
final String label = getLabel(keySpec);
- // Code is automatically generated for one letter label.
- if (StringUtils.codePointCount(label) == 1) {
- return label.codePointAt(0);
+ if (label == null) {
+ throw new KeySpecParserError("Empty label: " + keySpec);
}
- return CODE_OUTPUT_TEXT;
+ // Code is automatically generated for one letter label.
+ return (StringUtils.codePointCount(label) == 1) ? label.codePointAt(0) : CODE_OUTPUT_TEXT;
}
+ // TODO: Make this method private once Key.code attribute is removed.
public static int parseCode(final String text, final KeyboardCodesSet codesSet,
final int defCode) {
- if (text == null) return defCode;
+ if (text == null) {
+ return defCode;
+ }
if (text.startsWith(KeyboardCodesSet.PREFIX_CODE)) {
return codesSet.getCode(text.substring(KeyboardCodesSet.PREFIX_CODE.length()));
- } else if (text.startsWith(PREFIX_HEX)) {
+ }
+ if (text.startsWith(PREFIX_HEX)) {
return Integer.parseInt(text.substring(PREFIX_HEX.length()), 16);
- } else {
- return Integer.parseInt(text);
}
+ return Integer.parseInt(text);
}
public static int getIconId(final String keySpec) {
- if (keySpec != null && hasIcon(keySpec)) {
- final int end = keySpec.indexOf(
- VERTICAL_BAR, KeyboardIconsSet.PREFIX_ICON.length());
- final String name = (end < 0)
- ? keySpec.substring(KeyboardIconsSet.PREFIX_ICON.length())
- : keySpec.substring(KeyboardIconsSet.PREFIX_ICON.length(), end);
- return KeyboardIconsSet.getIconId(name);
- }
- return KeyboardIconsSet.ICON_UNDEFINED;
+ if (keySpec == null) {
+ // TODO: Throw {@link KeySpecParserError} once Key.keyLabel attribute becomes mandatory.
+ return KeyboardIconsSet.ICON_UNDEFINED;
+ }
+ if (!hasIcon(keySpec)) {
+ return KeyboardIconsSet.ICON_UNDEFINED;
+ }
+ final int labelEnd = indexOfLabelEnd(keySpec);
+ final String iconName = getBeforeLabelEnd(keySpec, labelEnd)
+ .substring(KeyboardIconsSet.PREFIX_ICON.length());
+ return KeyboardIconsSet.getIconId(iconName);
}
@SuppressWarnings("serial")
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
index d3bc0c2b2..0551e9e98 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
@@ -46,6 +46,9 @@ public final class MoreKeySpec {
public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, final Locale locale,
final KeyboardCodesSet codesSet) {
+ if (TextUtils.isEmpty(moreKeySpec)) {
+ throw new KeySpecParser.KeySpecParserError("Empty more key spec");
+ }
mLabel = StringUtils.toUpperCaseOfStringForLocale(
KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale);
final int code = StringUtils.toUpperCaseOfCodeForLocale(
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 00eb57c9f..b20bcd1f9 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -22,7 +22,10 @@ import android.util.SparseArray;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.makedict.Word;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.settings.NativeSuggestOptions;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.JniUtils;
@@ -33,6 +36,7 @@ import com.android.inputmethod.latin.utils.WordProperty;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -135,9 +139,12 @@ public final class BinaryDictionary extends Dictionary {
}
private static native boolean createEmptyDictFileNative(String filePath, long dictVersion,
- String[] attributeKeyStringArray, String[] attributeValueStringArray);
+ String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray);
private static native long openNative(String sourceDir, long dictOffset, long dictSize,
boolean isUpdatable);
+ private static native void getHeaderInfoNative(long dict, int[] outHeaderSize,
+ int[] outFormatVersion, ArrayList<int[]> outAttributeKeys,
+ ArrayList<int[]> outAttributeValues);
private static native void flushNative(long dict, String filePath);
private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
private static native void flushWithGCNative(long dict, String filePath);
@@ -171,9 +178,8 @@ public final class BinaryDictionary extends Dictionary {
private static native int setCurrentTimeForTestNative(int currentTime);
private static native String getPropertyNative(long dict, String query);
- @UsedForTesting
public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
- final Map<String, String> attributeMap) {
+ final Locale locale, final Map<String, String> attributeMap) {
final String[] keyArray = new String[attributeMap.size()];
final String[] valueArray = new String[attributeMap.size()];
int index = 0;
@@ -182,7 +188,8 @@ public final class BinaryDictionary extends Dictionary {
valueArray[index] = attributeMap.get(key);
index++;
}
- return createEmptyDictFileNative(filePath, dictVersion, keyArray, valueArray);
+ return createEmptyDictFileNative(filePath, dictVersion, locale.toString(), keyArray,
+ valueArray);
}
// TODO: Move native dict into session
@@ -191,6 +198,33 @@ public final class BinaryDictionary extends Dictionary {
mNativeDict = openNative(path, startOffset, length, isUpdatable);
}
+ @UsedForTesting
+ public DictionaryHeader getHeader() throws UnsupportedFormatException {
+ if (mNativeDict == 0) {
+ return null;
+ }
+ final int[] outHeaderSize = new int[1];
+ final int[] outFormatVersion = new int[1];
+ final ArrayList<int[]> outAttributeKeys = CollectionUtils.newArrayList();
+ final ArrayList<int[]> outAttributeValues = CollectionUtils.newArrayList();
+ getHeaderInfoNative(mNativeDict, outHeaderSize, outFormatVersion, outAttributeKeys,
+ outAttributeValues);
+ final HashMap<String, String> attributes = new HashMap<String, String>();
+ for (int i = 0; i < outAttributeKeys.size(); i++) {
+ final String attributeKey = StringUtils.getStringFromNullTerminatedCodePointArray(
+ outAttributeKeys.get(i));
+ final String attributeValue = StringUtils.getStringFromNullTerminatedCodePointArray(
+ outAttributeValues.get(i));
+ attributes.put(attributeKey, attributeValue);
+ }
+ final boolean hasHistoricalInfo =
+ attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY).equals(
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ return new DictionaryHeader(outHeaderSize[0], new DictionaryOptions(attributes),
+ new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo));
+ }
+
+
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
final String prevWord, final ProximityInfo proximityInfo,
@@ -308,7 +342,6 @@ public final class BinaryDictionary extends Dictionary {
return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
}
- @UsedForTesting
public WordProperty getWordProperty(final String word) {
if (TextUtils.isEmpty(word)) {
return null;
@@ -348,16 +381,10 @@ public final class BinaryDictionary extends Dictionary {
* Method to iterate all words in the dictionary for makedict.
* If token is 0, this method newly starts iterating the dictionary.
*/
- @UsedForTesting
public GetNextWordPropertyResult getNextWordProperty(final int token) {
final int[] codePoints = new int[MAX_WORD_LENGTH];
final int nextToken = getNextWordNative(mNativeDict, token, codePoints);
- int len = 0;
- // codePoints is null-terminated if its length is shorter than the array length.
- while (len < MAX_WORD_LENGTH && codePoints[len] != 0) {
- ++len;
- }
- final String word = new String(mOutputCodePoints, 0, len);
+ final String word = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
return new GetNextWordPropertyResult(getWordProperty(word), nextToken);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 7e97802e1..a7008379f 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -22,8 +22,8 @@ import android.content.res.AssetFileDescriptor;
import android.util.Log;
import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
@@ -230,7 +230,7 @@ final public class BinaryDictionaryGetter {
try {
// Read the version of the file
final DictDecoder dictDecoder = FormatSpec.getDictDecoder(f);
- final FileHeader header = dictDecoder.readHeader();
+ final DictionaryHeader header = dictDecoder.readHeader();
final String version = header.mDictionaryOptions.mAttributes.get(VERSION_KEY);
if (null == version) {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java
new file mode 100644
index 000000000..ee2fdc6c7
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DictionaryDumpBroadcastReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class DictionaryDumpBroadcastReceiver extends BroadcastReceiver {
+ private static final String TAG = DictionaryDumpBroadcastReceiver.class.getSimpleName();
+
+ private static final String DOMAIN = "com.android.inputmethod.latin";
+ public static final String DICTIONARY_DUMP_INTENT_ACTION = DOMAIN + ".DICT_DUMP";
+ public static final String DICTIONARY_NAME_KEY = "dictName";
+
+ final LatinIME mLatinIme;
+
+ public DictionaryDumpBroadcastReceiver(final LatinIME latinIme) {
+ mLatinIme = latinIme;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(DICTIONARY_DUMP_INTENT_ACTION)) {
+ final String dictName = intent.getStringExtra(DICTIONARY_NAME_KEY);
+ if (dictName == null) {
+ Log.e(TAG, "Received dictionary dump intent action " +
+ "but the dictionary name is not set.");
+ return;
+ }
+ mLatinIme.dumpDictionaryForDebug(dictName);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
index 8b02984e0..e68c6b771 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
@@ -534,4 +534,25 @@ public class DictionaryFacilitatorForSuggest {
mPersonalizationDictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams,
callback);
}
+
+ public void dumpDictionaryForDebug(final String dictName) {
+ final ExpandableBinaryDictionary dictToDump;
+ if (dictName.equals(Dictionary.TYPE_CONTACTS)) {
+ dictToDump = mContactsDictionary;
+ } else if (dictName.equals(Dictionary.TYPE_USER)) {
+ dictToDump = mUserDictionary;
+ } else if (dictName.equals(Dictionary.TYPE_USER_HISTORY)) {
+ dictToDump = mUserHistoryDictionary;
+ } else if (dictName.equals(Dictionary.TYPE_PERSONALIZATION)) {
+ dictToDump = mPersonalizationDictionary;
+ } else {
+ dictToDump = null;
+ }
+ if (dictToDump == null) {
+ Log.e(TAG, "Cannot dump " + dictName + ". "
+ + "The dictionary is not being used for suggestion or cannot be dumped.");
+ return;
+ }
+ dictToDump.dumpAllWordsForDebug();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 4dee84a7b..565d6a1f9 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -21,6 +21,7 @@ import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
@@ -28,6 +29,7 @@ import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor;
+import com.android.inputmethod.latin.utils.WordProperty;
import java.io.File;
import java.util.ArrayList;
@@ -267,9 +269,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected Map<String, String> getHeaderAttributeMap() {
HashMap<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, mDictName);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY,
+ attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, mDictName);
+ attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
+ attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
return attributeMap;
}
@@ -287,10 +289,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
Log.e(TAG, "Can't remove a file: " + file.getName());
}
BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
- DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
+ DICTIONARY_FORMAT_VERSION, mLocale, getHeaderAttributeMap());
mBinaryDictionary = new BinaryDictionary(
file.getAbsolutePath(), 0 /* offset */, file.length(),
- true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
+ true /* useFullEditDistance */, mLocale, mDictType, mIsUpdatable);
} else {
mDictionaryWriter.clear();
}
@@ -592,7 +594,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
Log.e(TAG, "Can't remove a file: " + file.getName());
}
BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
- DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
+ DICTIONARY_FORMAT_VERSION, mLocale, getHeaderAttributeMap());
} else {
if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
mBinaryDictionary.flushWithGC();
@@ -778,16 +780,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@UsedForTesting
- protected void runAfterGcForDebug(final Runnable r) {
- getExecutor(mDictName).executePrioritized(new Runnable() {
+ public void dumpAllWordsForDebug() {
+ reloadDictionaryIfRequired();
+ getExecutor(mDictName).execute(new Runnable() {
@Override
public void run() {
- try {
- mBinaryDictionary.flushWithGC();
- r.run();
- } finally {
- mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
- }
+ Log.d(TAG, "dictionary=" + mDictName);
+ int token = 0;
+ do {
+ final BinaryDictionary.GetNextWordPropertyResult result =
+ mBinaryDictionary.getNextWordProperty(token);
+ final WordProperty wordProperty = result.mWordProperty;
+ if (wordProperty == null) {
+ Log.d(TAG, " dictionary is empty.");
+ break;
+ }
+ Log.d(TAG, wordProperty.toString());
+ token = result.mNextToken;
+ } while (token != 0);
}
});
}
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index c3bcf3785..47bc6b078 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.latin;
import android.util.Log;
+import android.util.SparseIntArray;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.utils.ResizableIntArray;
@@ -160,15 +161,21 @@ public final class InputPointers {
private boolean isValidTimeStamps() {
final int[] times = mTimes.getPrimitiveArray();
+ final int[] pointerIds = mPointerIds.getPrimitiveArray();
+ final SparseIntArray lastTimeOfPointers = new SparseIntArray();
final int size = getPointerSize();
- for (int i = 1; i < size; ++i) {
- if (times[i] < times[i - 1]) {
+ for (int i = 0; i < size; ++i) {
+ final int pointerId = pointerIds[i];
+ final int time = times[i];
+ final int lastTime = lastTimeOfPointers.get(pointerId, time);
+ if (time < lastTime) {
// dump
for (int j = 0; j < size; ++j) {
Log.d(TAG, "--- (" + j + ") " + times[j]);
}
return false;
}
+ lastTimeOfPointers.put(pointerId, time);
}
return true;
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6517ef29d..e55c08dae 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -67,7 +67,6 @@ import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.inputlogic.InputLogic;
-import com.android.inputmethod.latin.inputlogic.SpaceState;
import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever;
import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegistrar;
import com.android.inputmethod.latin.personalization.PersonalizationHelper;
@@ -75,6 +74,7 @@ import com.android.inputmethod.latin.settings.Settings;
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.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.CompletionInfoUtils;
@@ -82,7 +82,6 @@ import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
-import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import com.android.inputmethod.research.ResearchLogger;
@@ -97,7 +96,7 @@ import java.util.concurrent.TimeUnit;
* Input method implementation for Qwerty'ish keyboard.
*/
public class LatinIME extends InputMethodService implements KeyboardActionListener,
- SuggestionStripView.Listener,
+ SuggestionStripView.Listener, SuggestionStripViewAccessor,
DictionaryFacilitatorForSuggest.DictionaryInitializationListener {
private static final String TAG = LatinIME.class.getSimpleName();
private static final boolean TRACE = false;
@@ -116,13 +115,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private static final String SCHEME_PACKAGE = "package";
private final Settings mSettings;
- private final InputLogic mInputLogic = new InputLogic(this);
+ private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */,
+ this /* SuggestionStripViewAccessor */);
private View mExtractArea;
private View mKeyPreviewBackingView;
private SuggestionStripView mSuggestionStripView;
- private CompletionInfo[] mApplicationSpecifiedCompletions;
+ // TODO[IL]: remove this member completely.
+ public CompletionInfo[] mApplicationSpecifiedCompletions;
private RichInputMethodManager mRichImm;
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
@@ -133,6 +134,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private BroadcastReceiver mDictionaryPackInstallReceiver =
new DictionaryPackInstallBroadcastReceiver(this);
+ private BroadcastReceiver mDictionaryDumpBroadcastReceiver =
+ new DictionaryDumpBroadcastReceiver(this);
+
private AlertDialog mOptionsDialog;
private final boolean mIsHardwareAcceleratedDrawingEnabled;
@@ -487,6 +491,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+ final IntentFilter dictDumpFilter = new IntentFilter();
+ dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
+ registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter);
+
DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this);
}
@@ -1298,13 +1306,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Nothing to do so far.
}
- // TODO[IL]: Move this to InputLogic and make it private
- // Outside LatinIME, only used by the test suite.
+ // TODO: remove this, read this directly from mInputLogic or something in the tests
@UsedForTesting
public boolean isShowingPunctuationList() {
- if (mInputLogic.mSuggestedWords == null) return false;
- return mSettings.getCurrent().mSpacingAndPunctuations.mSuggestPuncList
- == mInputLogic.mSuggestedWords;
+ return mInputLogic.isShowingPunctuationList(mSettings.getCurrent());
}
// TODO[IL]: Define a clear interface for this
@@ -1323,6 +1328,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return currentSettings.isSuggestionsRequested();
}
+ @Override
+ public boolean hasSuggestionStripView() {
+ return null != mSuggestionStripView;
+ }
+
+ @Override
+ public boolean isShowingAddToDictionaryHint() {
+ return hasSuggestionStripView() && mSuggestionStripView.isShowingAddToDictionaryHint();
+ }
+
+ @Override
public void dismissAddToDictionaryHint() {
if (null != mSuggestionStripView) {
mSuggestionStripView.dismissAddToDictionaryHint();
@@ -1392,8 +1408,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// the "add to dictionary" hint, we need to revert to suggestions - although it is unclear
// how we can come here if it's displayed.
if (suggestedWords.size() > 1 || typedWord.length() <= 1
- || null == mSuggestionStripView
- || mSuggestionStripView.isShowingAddToDictionaryHint()) {
+ || null == mSuggestionStripView || isShowingAddToDictionaryHint()) {
return suggestedWords;
} else {
final SuggestedWords punctuationList =
@@ -1411,7 +1426,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- // TODO[IL]: Define a clean interface for this
+ @Override
public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) {
final SuggestedWords suggestedWords =
sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords;
@@ -1439,99 +1454,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// interface
@Override
public void pickSuggestionManually(final int index, final SuggestedWordInfo suggestionInfo) {
- final SuggestedWords suggestedWords = mInputLogic.mSuggestedWords;
- final String suggestion = suggestionInfo.mWord;
- // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
- if (suggestion.length() == 1 && isShowingPunctuationList()) {
- // Word separators are suggested before the user inputs something.
- // So, LatinImeLogger logs "" as a user's input.
- LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
- // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
- final int primaryCode = suggestion.charAt(0);
- onCodeInput(primaryCode,
- Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
- false /* isBatchMode */, suggestedWords.mIsPrediction);
- }
- return;
- }
-
- mInputLogic.mConnection.beginBatchEdit();
- final SettingsValues currentSettings = mSettings.getCurrent();
- if (SpaceState.PHANTOM == mInputLogic.mSpaceState && suggestion.length() > 0
- // In the batch input mode, a manually picked suggested word should just replace
- // the current batch input text and there is no need for a phantom space.
- && !mInputLogic.mWordComposer.isBatchMode()) {
- final int firstChar = Character.codePointAt(suggestion, 0);
- if (!currentSettings.isWordSeparator(firstChar)
- || currentSettings.isUsuallyPrecededBySpace(firstChar)) {
- mInputLogic.promotePhantomSpace(currentSettings);
- }
- }
-
- if (currentSettings.isApplicationSpecifiedCompletionsOn()
- && mApplicationSpecifiedCompletions != null
- && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
- mInputLogic.mSuggestedWords = SuggestedWords.EMPTY;
- if (mSuggestionStripView != null) {
- mSuggestionStripView.clear();
- }
- mKeyboardSwitcher.updateShiftState();
- mInputLogic.resetComposingState(true /* alsoResetLastComposedWord */);
- final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
- mInputLogic.mConnection.commitCompletion(completionInfo);
- mInputLogic.mConnection.endBatchEdit();
- return;
- }
-
- // We need to log before we commit, because the word composer will store away the user
- // typed word.
- final String replacedWord = mInputLogic.mWordComposer.getTypedWord();
- LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
- mInputLogic.commitChosenWord(currentSettings, suggestion,
- LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
- mInputLogic.mWordComposer.isBatchMode(), suggestionInfo.mScore,
- suggestionInfo.mKind, suggestionInfo.mSourceDict.mDictType);
- }
- mInputLogic.mConnection.endBatchEdit();
- // Don't allow cancellation of manual pick
- mInputLogic.mLastComposedWord.deactivate();
- // Space state must be updated before calling updateShiftState
- mInputLogic.mSpaceState = SpaceState.PHANTOM;
- mKeyboardSwitcher.updateShiftState();
+ mInputLogic.onPickSuggestionManually(mSettings.getCurrent(), index, suggestionInfo,
+ mHandler, mKeyboardSwitcher);
+ }
- // We should show the "Touch again to save" hint if the user pressed the first entry
- // AND it's in none of our current dictionaries (main, user or otherwise).
- // Please note that if mSuggest is null, it means that everything is off: suggestion
- // and correction, so we shouldn't try to show the hint
- final Suggest suggest = mInputLogic.mSuggest;
- final boolean showingAddToDictionaryHint =
- (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
- || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
- && suggest != null
- // If the suggestion is not in the dictionary, the hint should be shown.
- && !suggest.mDictionaryFacilitator.isValidWord(suggestion,
- true /* ignoreCase */);
-
- if (currentSettings.mIsInternal) {
- LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
- }
- if (showingAddToDictionaryHint
- && suggest.mDictionaryFacilitator.isUserDictionaryEnabled()) {
- mSuggestionStripView.showAddToDictionaryHint(suggestion);
- } else {
- // If we're not showing the "Touch again to save", then update the suggestion strip.
- mHandler.postUpdateSuggestionStrip();
- }
+ @Override
+ public void showAddToDictionaryHint(final String word) {
+ if (null == mSuggestionStripView) return;
+ mSuggestionStripView.showAddToDictionaryHint(word);
}
// TODO[IL]: Define a clean interface for this
// This will show either an empty suggestion strip (if prediction is enabled) or
// punctuation suggestions (if it's disabled).
+ @Override
public void setNeutralSuggestionStrip() {
final SettingsValues currentSettings = mSettings.getCurrent();
if (currentSettings.mBigramPredictionEnabled) {
@@ -1758,6 +1694,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetSuggest(new Suggest(locale, dictionaryFacilitator));
}
+ public void dumpDictionaryForDebug(final String dictName) {
+ if (mInputLogic.mSuggest == null) {
+ initSuggest();
+ }
+ mInputLogic.mSuggest.mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
+ }
+
public void debugDumpStateAndCrashWithException(final String context) {
final SettingsValues settingsValues = mSettings.getCurrent();
final StringBuilder s = new StringBuilder(settingsValues.toString());
diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
index 028f78a87..800f56597 100644
--- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
@@ -26,7 +26,7 @@ import android.os.Environment;
import com.android.inputmethod.latin.BinaryDictionaryFileDumper;
import com.android.inputmethod.latin.BinaryDictionaryGetter;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
import com.android.inputmethod.latin.utils.LocaleUtils;
@@ -51,7 +51,7 @@ public class ExternalDictionaryGetterForDebug {
final File[] files = new File(SOURCE_FOLDER).listFiles();
final ArrayList<String> eligibleList = CollectionUtils.newArrayList();
for (File f : files) {
- final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f);
+ final DictionaryHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f);
if (null == header) continue;
eligibleList.add(f.getName());
}
@@ -99,7 +99,7 @@ public class ExternalDictionaryGetterForDebug {
public static void askInstallFile(final Context context, final String dirPath,
final String fileName, final Runnable completeRunnable) {
final File file = new File(dirPath, fileName.toString());
- final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file);
+ final DictionaryHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file);
final StringBuilder message = new StringBuilder();
final String locale = header.getLocaleString();
for (String key : header.mDictionaryOptions.mAttributes.keySet()) {
@@ -143,7 +143,7 @@ public class ExternalDictionaryGetterForDebug {
}
private static void installFile(final Context context, final File file,
- final FileHeader header) {
+ final DictionaryHeader header) {
BufferedOutputStream outputStream = null;
File tempFile = null;
try {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index f53183f37..3ecf5f0fb 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -23,6 +23,7 @@ import android.text.style.SuggestionSpan;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
@@ -44,6 +45,7 @@ import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
@@ -65,6 +67,7 @@ public final class InputLogic {
// TODO : Remove this member when we can.
private final LatinIME mLatinIME;
+ private final SuggestionStripViewAccessor mSuggestionStripViewAccessor;
// Never null.
private InputLogicHandler mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
@@ -94,8 +97,10 @@ public final class InputLogic {
// Find a way to remove it for readability.
public boolean mIsAutoCorrectionIndicatorOn;
- public InputLogic(final LatinIME latinIME) {
+ public InputLogic(final LatinIME latinIME,
+ final SuggestionStripViewAccessor suggestionStripViewAccessor) {
mLatinIME = latinIME;
+ mSuggestionStripViewAccessor = suggestionStripViewAccessor;
mWordComposer = new WordComposer();
mEventInterpreter = new EventInterpreter(latinIME);
mConnection = new RichInputConnection(latinIME);
@@ -179,6 +184,108 @@ public final class InputLogic {
}
/**
+ * A suggestion was picked from the suggestion strip.
+ * @param settingsValues the current values of the settings.
+ * @param index the index of the suggestion.
+ * @param suggestionInfo the suggestion info.
+ */
+ // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
+ // interface
+ public void onPickSuggestionManually(final SettingsValues settingsValues,
+ final int index, final SuggestedWordInfo suggestionInfo,
+ // TODO: remove these two arguments
+ final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) {
+ final SuggestedWords suggestedWords = mSuggestedWords;
+ final String suggestion = suggestionInfo.mWord;
+ // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
+ if (suggestion.length() == 1 && isShowingPunctuationList(settingsValues)) {
+ // Word separators are suggested before the user inputs something.
+ // So, LatinImeLogger logs "" as a user's input.
+ LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
+ // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
+ final int primaryCode = suggestion.charAt(0);
+ onCodeInput(primaryCode,
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE,
+ settingsValues, handler, keyboardSwitcher);
+ if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
+ false /* isBatchMode */, suggestedWords.mIsPrediction);
+ }
+ return;
+ }
+
+ mConnection.beginBatchEdit();
+ if (SpaceState.PHANTOM == mSpaceState && suggestion.length() > 0
+ // In the batch input mode, a manually picked suggested word should just replace
+ // the current batch input text and there is no need for a phantom space.
+ && !mWordComposer.isBatchMode()) {
+ final int firstChar = Character.codePointAt(suggestion, 0);
+ if (!settingsValues.isWordSeparator(firstChar)
+ || settingsValues.isUsuallyPrecededBySpace(firstChar)) {
+ promotePhantomSpace(settingsValues);
+ }
+ }
+
+ // TODO: stop relying on mApplicationSpecifiedCompletions. The SuggestionInfo object
+ // should contain a reference to the CompletionInfo instead.
+ if (settingsValues.isApplicationSpecifiedCompletionsOn()
+ && mLatinIME.mApplicationSpecifiedCompletions != null
+ && index >= 0 && index < mLatinIME.mApplicationSpecifiedCompletions.length) {
+ mSuggestedWords = SuggestedWords.EMPTY;
+ mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
+ keyboardSwitcher.updateShiftState();
+ resetComposingState(true /* alsoResetLastComposedWord */);
+ final CompletionInfo completionInfo = mLatinIME.mApplicationSpecifiedCompletions[index];
+ mConnection.commitCompletion(completionInfo);
+ mConnection.endBatchEdit();
+ return;
+ }
+
+ // We need to log before we commit, because the word composer will store away the user
+ // typed word.
+ final String replacedWord = mWordComposer.getTypedWord();
+ LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
+ commitChosenWord(settingsValues, suggestion,
+ LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
+ if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
+ mWordComposer.isBatchMode(), suggestionInfo.mScore,
+ suggestionInfo.mKind, suggestionInfo.mSourceDict.mDictType);
+ }
+ mConnection.endBatchEdit();
+ // Don't allow cancellation of manual pick
+ mLastComposedWord.deactivate();
+ // Space state must be updated before calling updateShiftState
+ mSpaceState = SpaceState.PHANTOM;
+ keyboardSwitcher.updateShiftState();
+
+ // We should show the "Touch again to save" hint if the user pressed the first entry
+ // AND it's in none of our current dictionaries (main, user or otherwise).
+ // Please note that if mSuggest is null, it means that everything is off: suggestion
+ // and correction, so we shouldn't try to show the hint
+ final Suggest suggest = mSuggest;
+ final boolean showingAddToDictionaryHint =
+ (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
+ || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
+ && suggest != null
+ // If the suggestion is not in the dictionary, the hint should be shown.
+ && !suggest.mDictionaryFacilitator.isValidWord(suggestion,
+ true /* ignoreCase */);
+
+ if (settingsValues.mIsInternal) {
+ LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ }
+ if (showingAddToDictionaryHint
+ && suggest.mDictionaryFacilitator.isUserDictionaryEnabled()) {
+ mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
+ } else {
+ // If we're not showing the "Touch again to save", then update the suggestion strip.
+ handler.postUpdateSuggestionStrip();
+ }
+ }
+
+ /**
* Consider an update to the cursor position. Evaluate whether this update has happened as
* part of normal typing or whether it was an explicit cursor move by the user. In any case,
* do the necessary adjustments.
@@ -638,7 +745,7 @@ public final class InputLogic {
mSpaceState = SpaceState.WEAK;
}
// In case the "add to dictionary" hint was still displayed.
- mLatinIME.dismissAddToDictionaryHint();
+ mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
}
handler.postUpdateSuggestionStrip();
if (settingsValues.mIsInternal) {
@@ -714,7 +821,7 @@ public final class InputLogic {
if (maybeDoubleSpacePeriod(settingsValues, handler)) {
keyboardSwitcher.updateShiftState();
mSpaceState = SpaceState.DOUBLE;
- } else if (!mLatinIME.isShowingPunctuationList()) {
+ } else if (!isShowingPunctuationList(settingsValues)) {
mSpaceState = SpaceState.WEAK;
}
}
@@ -745,7 +852,7 @@ public final class InputLogic {
// Set punctuation right away. onUpdateSelection will fire but tests whether it is
// already displayed or not, so it's okay.
- mLatinIME.setNeutralSuggestionStrip();
+ mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
}
keyboardSwitcher.updateShiftState();
@@ -1098,7 +1205,7 @@ public final class InputLogic {
}
if (!mWordComposer.isComposingWord() && !settingsValues.mBigramPredictionEnabled) {
- mLatinIME.setNeutralSuggestionStrip();
+ mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
return;
}
@@ -1120,7 +1227,7 @@ public final class InputLogic {
final SuggestedWords suggestedWords = holder.get(null,
Constants.GET_SUGGESTED_WORDS_TIMEOUT);
if (suggestedWords != null) {
- mLatinIME.showSuggestionStrip(suggestedWords);
+ mSuggestionStripViewAccessor.showSuggestionStrip(suggestedWords);
}
}
@@ -1326,6 +1433,15 @@ public final class InputLogic {
}
/**
+ * Find out if the punctuation list is shown in the suggestion strip.
+ * @return whether the current suggestions are the punctuation list.
+ */
+ // TODO: make this private. It's used through LatinIME for tests.
+ public boolean isShowingPunctuationList(final SettingsValues settingsValues) {
+ return settingsValues.mSpacingAndPunctuations.mSuggestPuncList == mSuggestedWords;
+ }
+
+ /**
* Factor in auto-caps and manual caps and compute the current caps mode.
* @param settingsValues the current settings values.
* @param keyboardShiftMode the current shift mode of the keyboard. See
@@ -1482,7 +1598,7 @@ public final class InputLogic {
final int newSelStart, final int newSelEnd) {
final boolean shouldFinishComposition = mWordComposer.isComposingWord();
resetComposingState(true /* alsoResetLastComposedWord */);
- mLatinIME.setNeutralSuggestionStrip();
+ mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
shouldFinishComposition);
}
@@ -1616,7 +1732,10 @@ public final class InputLogic {
if (SpaceState.PHANTOM == mSpaceState) {
promotePhantomSpace(settingsValues);
}
- if (settingsValues.mPhraseGestureEnabled) {
+ final SuggestedWordInfo autoCommitCandidate = mSuggestedWords.getAutoCommitCandidate();
+ // Commit except the last word for phrase gesture if the top suggestion is eligible for auto
+ // commit.
+ if (settingsValues.mPhraseGestureEnabled && null != autoCommitCandidate) {
// Find the last space
final int indexOfLastSpace = batchInputText.lastIndexOf(Constants.CODE_SPACE) + 1;
if (0 != indexOfLastSpace) {
@@ -1720,9 +1839,8 @@ public final class InputLogic {
// the segment of text starting at the supplied index and running for the length
// of the auto-correction flash. At this moment, the "typedWord" argument is
// ignored by TextView.
- mConnection.commitCorrection(
- new CorrectionInfo(
- mConnection.getExpectedSelectionEnd() - typedWord.length(),
+ mConnection.commitCorrection(new CorrectionInfo(
+ mConnection.getExpectedSelectionEnd() - autoCorrection.length(),
typedWord, autoCorrection));
}
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
index 1a9118147..5c7c4b8e3 100644
--- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
@@ -36,7 +35,7 @@ public abstract class AbstractDictDecoder implements DictDecoder {
private static final int ERROR_CANNOT_READ = 1;
private static final int ERROR_WRONG_FORMAT = 2;
- protected FileHeader readHeader(final DictBuffer headerBuffer)
+ protected DictionaryHeader readHeader(final DictBuffer headerBuffer)
throws IOException, UnsupportedFormatException {
if (headerBuffer == null) {
openDictBuffer();
@@ -57,10 +56,10 @@ public abstract class AbstractDictDecoder implements DictDecoder {
final HashMap<String, String> attributes = HeaderReader.readAttributes(headerBuffer,
headerSize);
- final FileHeader header = new FileHeader(headerSize,
+ final DictionaryHeader header = new DictionaryHeader(headerSize,
new FusionDictionary.DictionaryOptions(attributes),
- new FormatOptions(version, FileHeader.ATTRIBUTE_VALUE_TRUE.equals(
- attributes.get(FileHeader.HAS_HISTORICAL_INFO_KEY))));
+ new FormatOptions(version, DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals(
+ attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY))));
return header;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 31747155e..369184573 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
@@ -598,7 +597,7 @@ public final class BinaryDictDecoderUtils {
/* package */ static FusionDictionary readDictionaryBinary(final DictDecoder dictDecoder,
final FusionDictionary dict) throws IOException, UnsupportedFormatException {
// Read header
- final FileHeader fileHeader = dictDecoder.readHeader();
+ final DictionaryHeader fileHeader = dictDecoder.readHeader();
Map<Integer, PtNodeArray> reverseNodeArrayMapping = new TreeMap<Integer, PtNodeArray>();
Map<Integer, PtNode> reversePtNodeMapping = new TreeMap<Integer, PtNode>();
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 0dc50d14e..dea9f2e28 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -20,7 +20,6 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
@@ -151,7 +150,7 @@ public final class BinaryDictIOUtils {
final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException,
UnsupportedFormatException {
// Read header
- final FileHeader header = dictDecoder.readHeader();
+ final DictionaryHeader header = dictDecoder.readHeader();
readUnigramsAndBigramsBinaryInner(dictDecoder, header.mBodyOffset, words,
frequencies, bigrams, header.mFormatOptions);
}
@@ -172,7 +171,7 @@ public final class BinaryDictIOUtils {
if (word == null) return FormatSpec.NOT_VALID_WORD;
dictDecoder.setPosition(0);
- final FileHeader header = dictDecoder.readHeader();
+ final DictionaryHeader header = dictDecoder.readHeader();
int wordPos = 0;
final int wordLen = word.codePointCount(0, word.length());
for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) {
@@ -311,7 +310,7 @@ public final class BinaryDictIOUtils {
* @param length The length of the data file.
* @return the header of the specified dictionary file.
*/
- private static FileHeader getDictionaryFileHeader(
+ private static DictionaryHeader getDictionaryFileHeader(
final File file, final long offset, final long length)
throws FileNotFoundException, IOException, UnsupportedFormatException {
final byte[] buffer = new byte[HEADER_READING_BUFFER_SIZE];
@@ -337,10 +336,10 @@ public final class BinaryDictIOUtils {
return dictDecoder.readHeader();
}
- public static FileHeader getDictionaryFileHeaderOrNull(final File file, final long offset,
+ public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file, final long offset,
final long length) {
try {
- final FileHeader header = getDictionaryFileHeader(file, offset, length);
+ final DictionaryHeader header = getDictionaryFileHeader(file, offset, length);
return header;
} catch (UnsupportedFormatException e) {
return null;
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
index b4838f00f..bba1d434f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
@@ -41,7 +40,7 @@ public interface DictDecoder {
/**
* Reads and returns the file header.
*/
- public FileHeader readHeader() throws IOException, UnsupportedFormatException;
+ public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException;
/**
* Reads PtNode from ptNodePos.
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
new file mode 100644
index 000000000..b99e281da
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/DictionaryHeader.java
@@ -0,0 +1,81 @@
+/*
+ * 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.makedict;
+
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
+
+/**
+ * Class representing dictionary header.
+ */
+public final class DictionaryHeader {
+ public final int mBodyOffset;
+ public final DictionaryOptions mDictionaryOptions;
+ public final FormatOptions mFormatOptions;
+
+ // Note that these are corresponding definitions in native code in latinime::HeaderPolicy
+ // and latinime::HeaderReadWriteUtils.
+ // TODO: Standardize the key names and bump up the format version, taking care not to
+ // break format version 2 dictionaries.
+ public static final String DICTIONARY_VERSION_KEY = "version";
+ public static final String DICTIONARY_LOCALE_KEY = "locale";
+ public static final String DICTIONARY_ID_KEY = "dictionary";
+ public static final String DICTIONARY_DESCRIPTION_KEY = "description";
+ public static final String DICTIONARY_DATE_KEY = "date";
+ public static final String HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
+ public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
+ public static final String ATTRIBUTE_VALUE_TRUE = "1";
+
+ public DictionaryHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
+ final FormatOptions formatOptions) throws UnsupportedFormatException {
+ mDictionaryOptions = dictionaryOptions;
+ mFormatOptions = formatOptions;
+ mBodyOffset = formatOptions.mVersion < FormatSpec.VERSION4 ? headerSize : 0;
+ if (null == getLocaleString()) {
+ throw new UnsupportedFormatException("Cannot create a FileHeader without a locale");
+ }
+ if (null == getVersion()) {
+ throw new UnsupportedFormatException(
+ "Cannot create a FileHeader without a version");
+ }
+ if (null == getId()) {
+ throw new UnsupportedFormatException("Cannot create a FileHeader without an ID");
+ }
+ }
+
+ // Helper method to get the locale as a String
+ public String getLocaleString() {
+ return mDictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_KEY);
+ }
+
+ // Helper method to get the version String
+ public String getVersion() {
+ return mDictionaryOptions.mAttributes.get(DICTIONARY_VERSION_KEY);
+ }
+
+ // Helper method to get the dictionary ID as a String
+ public String getId() {
+ return mDictionaryOptions.mAttributes.get(DICTIONARY_ID_KEY);
+ }
+
+ // Helper method to get the description
+ public String getDescription() {
+ // TODO: Right now each dictionary file comes with a description in its own language.
+ // It will display as is no matter the device's locale. It should be internationalized.
+ return mDictionaryOptions.mAttributes.get(DICTIONARY_DESCRIPTION_KEY);
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 74e305976..5a3807389 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
-import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
import java.io.File;
@@ -330,66 +329,6 @@ public final class FormatSpec {
}
/**
- * Class representing file header.
- */
- public static final class FileHeader {
- public final int mBodyOffset;
- public final DictionaryOptions mDictionaryOptions;
- public final FormatOptions mFormatOptions;
-
- // Note that these are corresponding definitions in native code in latinime::HeaderPolicy
- // and latinime::HeaderReadWriteUtils.
- // TODO: Standardize the key names and bump up the format version, taking care not to
- // break format version 2 dictionaries.
- public static final String DICTIONARY_VERSION_KEY = "version";
- public static final String DICTIONARY_LOCALE_KEY = "locale";
- public static final String DICTIONARY_ID_KEY = "dictionary";
- public static final String DICTIONARY_DESCRIPTION_KEY = "description";
- public static final String DICTIONARY_DATE_KEY = "date";
- public static final String HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
- public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
- public static final String ATTRIBUTE_VALUE_TRUE = "1";
- public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
- final FormatOptions formatOptions) throws UnsupportedFormatException {
- mDictionaryOptions = dictionaryOptions;
- mFormatOptions = formatOptions;
- mBodyOffset = formatOptions.mVersion < VERSION4 ? headerSize : 0;
- if (null == getLocaleString()) {
- throw new UnsupportedFormatException("Cannot create a FileHeader without a locale");
- }
- if (null == getVersion()) {
- throw new UnsupportedFormatException(
- "Cannot create a FileHeader without a version");
- }
- if (null == getId()) {
- throw new UnsupportedFormatException("Cannot create a FileHeader without an ID");
- }
- }
-
- // Helper method to get the locale as a String
- public String getLocaleString() {
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_KEY);
- }
-
- // Helper method to get the version String
- public String getVersion() {
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_KEY);
- }
-
- // Helper method to get the dictionary ID as a String
- public String getId() {
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_ID_KEY);
- }
-
- // Helper method to get the description
- public String getDescription() {
- // TODO: Right now each dictionary file comes with a description in its own language.
- // It will display as is no matter the device's locale. It should be internationalized.
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_DESCRIPTION_KEY);
- }
- }
-
- /**
* Returns new dictionary decoder.
*
* @param dictFile the dictionary file.
diff --git a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
new file mode 100644
index 000000000..c1a43cedf
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java
@@ -0,0 +1,46 @@
+/*
+ * 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.makedict;
+
+import com.android.inputmethod.latin.BinaryDictionary;
+
+public final class ProbabilityInfo {
+ public final int mProbability;
+ // mTimestamp, mLevel and mCount are historical info. These values are depend on the
+ // implementation in native code; thus, we must not use them and have any assumptions about
+ // them except for tests.
+ public final int mTimestamp;
+ public final int mLevel;
+ public final int mCount;
+
+ public ProbabilityInfo(final int probability) {
+ this(probability, BinaryDictionary.NOT_A_VALID_TIMESTAMP, 0, 0);
+ }
+
+ public ProbabilityInfo(final int probability, final int timestamp, final int level,
+ final int count) {
+ mProbability = probability;
+ mTimestamp = timestamp;
+ mLevel = level;
+ mCount = count;
+ }
+
+ @Override
+ public String toString() {
+ return mTimestamp + ":" + mLevel + ":" + mCount;
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index ea0a2c6c2..d35f780cf 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
@@ -90,11 +89,11 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
}
@Override
- public FileHeader readHeader() throws IOException, UnsupportedFormatException {
+ public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException {
if (mDictBuffer == null) {
openDictBuffer();
}
- final FileHeader header = super.readHeader(mDictBuffer);
+ final DictionaryHeader header = super.readHeader(mDictBuffer);
final int version = header.mFormatOptions.mVersion;
if (!(version >= 2 && version <= 3)) {
throw new UnsupportedFormatException("File header has a wrong version : " + version);
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index e459e4861..9ddaaf734 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
@@ -166,12 +165,12 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
}
@Override
- public FileHeader readHeader() throws IOException, UnsupportedFormatException {
+ public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException {
if (mHeaderBuffer == null) {
openDictBuffer();
}
mHeaderBuffer.position(0);
- final FileHeader header = super.readHeader(mHeaderBuffer);
+ final DictionaryHeader header = super.readHeader(mHeaderBuffer);
final int version = header.mFormatOptions.mVersion;
if (version != FormatSpec.VERSION4) {
throw new UnsupportedFormatException("File header has a wrong version : " + version);
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index b12f79b07..4b0efbad0 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -55,14 +55,16 @@ public class Ver4DictEncoder implements DictEncoder {
throw new UnsupportedFormatException("Given path is not a directory.");
}
if (!BinaryDictionary.createEmptyDictFile(mDictPlacedDir.getAbsolutePath(),
- FormatSpec.VERSION4, dict.mOptions.mAttributes)) {
+ FormatSpec.VERSION4, LocaleUtils.constructLocaleFromString(
+ dict.mOptions.mAttributes.get(DictionaryHeader.DICTIONARY_LOCALE_KEY)),
+ dict.mOptions.mAttributes)) {
throw new IOException("Cannot create dictionary file : "
+ mDictPlacedDir.getAbsolutePath());
}
final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(),
0l, mDictPlacedDir.length(), true /* useFullEditDistance */,
LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get(
- FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY)),
+ DictionaryHeader.DICTIONARY_LOCALE_KEY)),
Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */,
true /* isUpdatable */);
if (!binaryDict.isValidDictionary()) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index d636a253a..6a7a3368e 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -17,21 +17,15 @@
package com.android.inputmethod.latin.personalization;
import android.content.Context;
-import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
-import com.android.inputmethod.latin.makedict.DictDecoder;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.utils.LanguageModelParam;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
@@ -44,7 +38,6 @@ import java.util.concurrent.TimeUnit;
*/
public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableBinaryDictionary {
private static final String TAG = DecayingExpandableBinaryDictionaryBase.class.getSimpleName();
- public static final boolean DBG_SAVE_RESTORE = false;
private static final boolean DBG_DUMP_ON_CLOSE = false;
/** Any pair being typed or picked */
@@ -53,8 +46,6 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
public static final int FREQUENCY_FOR_WORDS_IN_DICTS = FREQUENCY_FOR_TYPED;
public static final int FREQUENCY_FOR_WORDS_NOT_IN_DICTS = Dictionary.NOT_A_PROBABILITY;
- public static final int REQUIRED_BINARY_DICTIONARY_VERSION = FormatSpec.VERSION4;
-
/** The locale for this dictionary. */
public final Locale mLocale;
@@ -95,13 +86,13 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
@Override
protected Map<String, String> getHeaderAttributeMap() {
HashMap<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY,
- FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_KEY,
- FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, mDictName);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY,
+ attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
+ DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
+ attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, mDictName);
+ attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
+ attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
return attributeMap;
}
@@ -161,57 +152,6 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
}
@UsedForTesting
- public void dumpAllWordsForDebug() {
- runAfterGcForDebug(new Runnable() {
- @Override
- public void run() {
- dumpAllWordsForDebugLocked();
- }
- });
- }
-
- private void dumpAllWordsForDebugLocked() {
- Log.d(TAG, "dumpAllWordsForDebug started.");
- final OnAddWordListener listener = new OnAddWordListener() {
- @Override
- public void setUnigram(final String word, final String shortcutTarget,
- final int frequency, final int shortcutFreq) {
- Log.d(TAG, "load unigram: " + word + "," + frequency);
- }
-
- @Override
- public void setBigram(final String word0, final String word1, final int frequency) {
- if (word0.length() < Constants.DICTIONARY_MAX_WORD_LENGTH
- && word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) {
- Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency);
- } else {
- Log.d(TAG, "Skip inserting a too long bigram: " + word0 + "," + word1 + ","
- + frequency);
- }
- }
- };
-
- // Load the dictionary from binary file
- final File dictFile = new File(mContext.getFilesDir(), mDictName);
- final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile,
- DictDecoder.USE_BYTEARRAY);
- if (dictDecoder == null) {
- // This is an expected condition: we don't have a user history dictionary for this
- // language yet. It will be created sometime later.
- return;
- }
-
- try {
- dictDecoder.openDictBuffer();
- UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
- } catch (IOException e) {
- Log.d(TAG, "IOException on opening a bytebuffer", e);
- } catch (UnsupportedFormatException e) {
- Log.d(TAG, "Unsupported format, can't read the dictionary", e);
- }
- }
-
- @UsedForTesting
public void clearAndFlushDictionary() {
// Clear the node structure on memory
clear();
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
deleted file mode 100644
index 55a90ee51..000000000
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-
-import java.util.HashMap;
-import java.util.Set;
-
-/**
- * A store of bigrams which will be updated when the user history dictionary is closed
- * All bigrams including stale ones in SQL DB should be stored in this class to avoid adding stale
- * bigrams when we write to the SQL DB.
- */
-@UsedForTesting
-public final class UserHistoryDictionaryBigramList {
- public static final byte FORGETTING_CURVE_INITIAL_VALUE = 0;
- private static final String TAG = UserHistoryDictionaryBigramList.class.getSimpleName();
- private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = CollectionUtils.newHashMap();
- private final HashMap<String, HashMap<String, Byte>> mBigramMap = CollectionUtils.newHashMap();
- private int mSize = 0;
-
- public void evictAll() {
- mSize = 0;
- mBigramMap.clear();
- }
-
- /**
- * Called when the user typed a word.
- */
- @UsedForTesting
- public void addBigram(String word1, String word2) {
- addBigram(word1, word2, FORGETTING_CURVE_INITIAL_VALUE);
- }
-
- /**
- * Called when loaded from the SQL DB.
- */
- public void addBigram(String word1, String word2, byte fcValue) {
- if (DecayingExpandableBinaryDictionaryBase.DBG_SAVE_RESTORE) {
- Log.d(TAG, "--- add bigram: " + word1 + ", " + word2 + ", " + fcValue);
- }
- final HashMap<String, Byte> map;
- if (mBigramMap.containsKey(word1)) {
- map = mBigramMap.get(word1);
- } else {
- map = CollectionUtils.newHashMap();
- mBigramMap.put(word1, map);
- }
- if (!map.containsKey(word2)) {
- ++mSize;
- map.put(word2, fcValue);
- }
- }
-
- /**
- * Called when inserted to the SQL DB.
- */
- public void updateBigram(String word1, String word2, byte fcValue) {
- if (DecayingExpandableBinaryDictionaryBase.DBG_SAVE_RESTORE) {
- Log.d(TAG, "--- update bigram: " + word1 + ", " + word2 + ", " + fcValue);
- }
- final HashMap<String, Byte> map;
- if (mBigramMap.containsKey(word1)) {
- map = mBigramMap.get(word1);
- } else {
- return;
- }
- if (!map.containsKey(word2)) {
- return;
- }
- map.put(word2, fcValue);
- }
-
- public int size() {
- return mSize;
- }
-
- public boolean isEmpty() {
- return mBigramMap.isEmpty();
- }
-
- public boolean containsKey(String word) {
- return mBigramMap.containsKey(word);
- }
-
- public Set<String> keySet() {
- return mBigramMap.keySet();
- }
-
- public HashMap<String, Byte> getBigrams(String word1) {
- if (mBigramMap.containsKey(word1)) return mBigramMap.get(word1);
- // TODO: lower case according to locale
- final String lowerWord1 = word1.toLowerCase();
- if (mBigramMap.containsKey(lowerWord1)) return mBigramMap.get(lowerWord1);
- return EMPTY_BIGRAM_MAP;
- }
-
- public boolean removeBigram(String word1, String word2) {
- final HashMap<String, Byte> set = getBigrams(word1);
- if (set.isEmpty()) {
- return false;
- }
- if (set.containsKey(word2)) {
- set.remove(word2);
- --mSize;
- return true;
- }
- return false;
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index 29bbed8bd..fa5ae92e7 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -16,15 +16,18 @@
package com.android.inputmethod.latin.settings;
+import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Process;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.debug.ExternalDictionaryGetterForDebug;
@@ -40,6 +43,11 @@ public final class DebugSettings extends PreferenceFragment
public static final String PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG =
"use_only_personalization_dictionary_for_debug";
private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary";
+ private static final String PREF_DUMP_CONTACTS_DICT = "dump_contacts_dict";
+ private static final String PREF_DUMP_USER_DICT = "dump_user_dict";
+ private static final String PREF_DUMP_USER_HISTORY_DICT = "dump_user_history_dict";
+ private static final String PREF_DUMP_PERSONALIZATION_DICT = "dump_personalization_dict";
+
private static final boolean SHOW_STATISTICS_LOGGING = false;
private boolean mServiceNeedsRestart = false;
@@ -83,11 +91,53 @@ public final class DebugSettings extends PreferenceFragment
});
}
+ final OnPreferenceClickListener dictDumpPrefClickListener =
+ new DictDumpPrefClickListener(this);
+ findPreference(PREF_DUMP_CONTACTS_DICT).setOnPreferenceClickListener(
+ dictDumpPrefClickListener);
+ findPreference(PREF_DUMP_USER_DICT).setOnPreferenceClickListener(
+ dictDumpPrefClickListener);
+ findPreference(PREF_DUMP_USER_HISTORY_DICT).setOnPreferenceClickListener(
+ dictDumpPrefClickListener);
+ findPreference(PREF_DUMP_PERSONALIZATION_DICT).setOnPreferenceClickListener(
+ dictDumpPrefClickListener);
+
mServiceNeedsRestart = false;
mDebugMode = (CheckBoxPreference) findPreference(PREF_DEBUG_MODE);
updateDebugMode();
}
+ private static class DictDumpPrefClickListener implements OnPreferenceClickListener {
+ final PreferenceFragment mPreferenceFragment;
+
+ public DictDumpPrefClickListener(final PreferenceFragment preferenceFragment) {
+ mPreferenceFragment = preferenceFragment;
+ }
+
+ @Override
+ public boolean onPreferenceClick(final Preference arg0) {
+ final String dictName;
+ if (arg0.getKey().equals(PREF_DUMP_CONTACTS_DICT)) {
+ dictName = Dictionary.TYPE_CONTACTS;
+ } else if (arg0.getKey().equals(PREF_DUMP_USER_DICT)) {
+ dictName = Dictionary.TYPE_USER;
+ } else if (arg0.getKey().equals(PREF_DUMP_USER_HISTORY_DICT)) {
+ dictName = Dictionary.TYPE_USER_HISTORY;
+ } else if (arg0.getKey().equals(PREF_DUMP_PERSONALIZATION_DICT)) {
+ dictName = Dictionary.TYPE_PERSONALIZATION;
+ } else {
+ dictName = null;
+ }
+ if (dictName != null) {
+ final Intent intent =
+ new Intent(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
+ intent.putExtra(DictionaryDumpBroadcastReceiver.DICTIONARY_NAME_KEY, dictName);
+ mPreferenceFragment.getActivity().sendBroadcast(intent);
+ }
+ return true;
+ }
+ }
+
@Override
public void onStop() {
super.onStop();
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
new file mode 100644
index 000000000..60f1c7a4e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java
@@ -0,0 +1,31 @@
+/*
+ * 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.suggestions;
+
+import com.android.inputmethod.latin.SuggestedWords;
+
+/**
+ * An object that gives basic control of a suggestion strip and some info on it.
+ */
+public interface SuggestionStripViewAccessor {
+ public boolean hasSuggestionStripView();
+ public void showAddToDictionaryHint(final String word);
+ public boolean isShowingAddToDictionaryHint();
+ public void dismissAddToDictionaryHint();
+ public void setNeutralSuggestionStrip();
+ public void showSuggestionStrip(final SuggestedWords suggestedWords);
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 306735779..a15556511 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -29,7 +29,7 @@ import com.android.inputmethod.latin.BinaryDictionaryGetter;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import java.io.File;
@@ -282,7 +282,7 @@ public class DictionaryInfoUtils {
BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.getLanguage().toString();
}
- public static FileHeader getDictionaryFileHeaderOrNull(final File file) {
+ public static DictionaryHeader getDictionaryFileHeaderOrNull(final File file) {
return BinaryDictIOUtils.getDictionaryFileHeaderOrNull(file, 0, file.length());
}
@@ -294,7 +294,7 @@ public class DictionaryInfoUtils {
*/
private static DictionaryInfo createDictionaryInfoFromFileAddress(
final AssetFileAddress fileAddress) {
- final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeaderOrNull(
+ final DictionaryHeader header = BinaryDictIOUtils.getDictionaryFileHeaderOrNull(
new File(fileAddress.mFilename), fileAddress.mOffset, fileAddress.mLength);
if (header == null) {
return null;
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index c632a71a9..e7932b5a6 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -46,7 +46,7 @@ public final class StringUtils {
public static String newSingleCodePointString(int codePoint) {
if (Character.charCount(codePoint) == 1) {
- // Optimization: avoid creating an temporary array for characters that are
+ // Optimization: avoid creating a temporary array for characters that are
// represented by a single char value
return String.valueOf((char) codePoint);
}
@@ -205,6 +205,24 @@ public final class StringUtils {
return codePoints;
}
+ /**
+ * Construct a String from a code point array
+ *
+ * @param codePoints a code point array that is null terminated when its logical length is
+ * shorter than the array length.
+ * @return a string constructed from the code point array.
+ */
+ public static String getStringFromNullTerminatedCodePointArray(final int[] codePoints) {
+ int stringLength = codePoints.length;
+ for (int i = 0; i < codePoints.length; i++) {
+ if (codePoints[i] == 0) {
+ stringLength = i;
+ break;
+ }
+ }
+ return new String(codePoints, 0 /* offset */, stringLength);
+ }
+
// This method assumes the text is not null. For the empty string, it returns CAPITALIZE_NONE.
public static int getCapitalizationType(final String text) {
// If the first char is not uppercase, then the word is either all lower case or
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
deleted file mode 100644
index 7af03da59..000000000
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
-import com.android.inputmethod.latin.makedict.DictDecoder;
-import com.android.inputmethod.latin.makedict.DictEncoder;
-import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
-import com.android.inputmethod.latin.makedict.PendingAttribute;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Reads and writes Binary files for a UserHistoryDictionary.
- *
- * All the methods in this class are static.
- */
-public final class UserHistoryDictIOUtils {
- private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- public interface OnAddWordListener {
- /**
- * Callback to be notified when a word is added to the dictionary.
- * @param word The added word.
- * @param shortcutTarget A shortcut target for this word, or null if none.
- * @param frequency The frequency for this word.
- * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist).
- * Unspecified if shortcutTarget is null - do not rely on its value.
- */
- public void setUnigram(final String word, final String shortcutTarget, final int frequency,
- final int shortcutFreq);
- public void setBigram(final String word1, final String word2, final int frequency);
- }
-
- @UsedForTesting
- public interface BigramDictionaryInterface {
- public int getFrequency(final String word1, final String word2);
- }
-
- /**
- * Writes dictionary to file.
- */
- @UsedForTesting
- public static void writeDictionary(final DictEncoder dictEncoder,
- final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
- final FormatOptions formatOptions, final HashMap<String, String> options) {
- final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams, options);
- fusionDict.addOptionAttribute(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY,
- FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- fusionDict.addOptionAttribute(FormatSpec.FileHeader.DICTIONARY_DATE_KEY,
- String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
- try {
- dictEncoder.writeDictionary(fusionDict, formatOptions);
- Log.d(TAG, "end writing");
- } catch (IOException e) {
- Log.e(TAG, "IO exception while writing file", e);
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "Unsupported format", e);
- }
- }
-
- /**
- * Constructs a new FusionDictionary from BigramDictionaryInterface.
- */
- @UsedForTesting
- static FusionDictionary constructFusionDictionary(final BigramDictionaryInterface dict,
- final UserHistoryDictionaryBigramList bigrams, final HashMap<String, String> options) {
- final FusionDictionary fusionDict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(options));
- int profTotal = 0;
- for (final String word1 : bigrams.keySet()) {
- final HashMap<String, Byte> word1Bigrams = bigrams.getBigrams(word1);
- for (final String word2 : word1Bigrams.keySet()) {
- final int freq = dict.getFrequency(word1, word2);
- if (freq == -1) {
- // don't add this bigram.
- continue;
- }
- if (DEBUG) {
- if (word1 == null) {
- Log.d(TAG, "add unigram: " + word2 + "," + Integer.toString(freq));
- } else {
- Log.d(TAG, "add bigram: " + word1
- + "," + word2 + "," + Integer.toString(freq));
- }
- profTotal++;
- }
- if (word1 == null) { // unigram
- fusionDict.add(word2, freq, null, false /* isNotAWord */);
- } else { // bigram
- if (FusionDictionary.findWordInTree(fusionDict.mRootNodeArray, word1) == null) {
- fusionDict.add(word1, 2, null, false /* isNotAWord */);
- }
- fusionDict.setBigram(word1, word2, freq);
- }
- bigrams.updateBigram(word1, word2, (byte)freq);
- }
- }
- if (DEBUG) {
- Log.d(TAG, "add " + profTotal + "words");
- }
- return fusionDict;
- }
-
- /**
- * Reads dictionary from file.
- */
- public static void readDictionaryBinary(final DictDecoder dictDecoder,
- final OnAddWordListener dict) {
- final TreeMap<Integer, String> unigrams = CollectionUtils.newTreeMap();
- final TreeMap<Integer, Integer> frequencies = CollectionUtils.newTreeMap();
- final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
- try {
- dictDecoder.readUnigramsAndBigramsBinary(unigrams, frequencies, bigrams);
- } catch (IOException e) {
- Log.e(TAG, "IO exception while reading file", e);
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "Unsupported format", e);
- } catch (ArrayIndexOutOfBoundsException e) {
- Log.e(TAG, "ArrayIndexOutOfBoundsException while reading file", e);
- }
- addWordsFromWordMap(unigrams, frequencies, bigrams, dict);
- }
-
- /**
- * Adds all unigrams and bigrams in maps to OnAddWordListener.
- */
- @UsedForTesting
- static void addWordsFromWordMap(final TreeMap<Integer, String> unigrams,
- final TreeMap<Integer, Integer> frequencies,
- final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams,
- final OnAddWordListener to) {
- for (Entry<Integer, String> entry : unigrams.entrySet()) {
- final String word1 = entry.getValue();
- final int unigramFrequency = frequencies.get(entry.getKey());
- to.setUnigram(word1, null /* shortcutTarget */, unigramFrequency, 0 /* shortcutFreq */);
- final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
- if (attrList != null) {
- for (final PendingAttribute attr : attrList) {
- final String word2 = unigrams.get(attr.mAddress);
- if (word1 == null || word2 == null) {
- Log.e(TAG, "Invalid bigram pair detected: " + word1 + ", " + word2);
- continue;
- }
- to.setBigram(word1, word2,
- BinaryDictIOUtils.reconstructBigramFrequency(unigramFrequency,
- attr.mFrequency));
- }
- }
- }
-
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/WordProperty.java b/java/src/com/android/inputmethod/latin/utils/WordProperty.java
index ba9b114b0..da56b213f 100644
--- a/java/src/com/android/inputmethod/latin/utils/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/utils/WordProperty.java
@@ -20,6 +20,7 @@ package com.android.inputmethod.latin.utils;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.makedict.ProbabilityInfo;
import java.util.ArrayList;
@@ -37,32 +38,12 @@ public class WordProperty {
public final ArrayList<ProbabilityInfo> mBigramProbabilityInfo = CollectionUtils.newArrayList();
public final ArrayList<WeightedString> mShortcutTargets = CollectionUtils.newArrayList();
- // TODO: Use this kind of Probability class for dictionary read/write code under the makedict
- // package.
- public static final class ProbabilityInfo {
- public final int mProbability;
- // wTimestamp, mLevel and mCount are historical info. These values are depend on the
- // implementation in native code; thus, we must not use them and have any assumptions about
- // them except for tests.
- public final int mTimestamp;
- public final int mLevel;
- public final int mCount;
-
- public ProbabilityInfo(final int[] probabilityInfo) {
- mProbability = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX];
- mTimestamp = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX];
- mLevel = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX];
- mCount = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX];
- }
- }
-
- private static int getCodePointCount(final int[] codePoints) {
- for (int i = 0; i < codePoints.length; i++) {
- if (codePoints[i] == 0) {
- return i;
- }
- }
- return codePoints.length;
+ private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) {
+ return new ProbabilityInfo(
+ probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX],
+ probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX],
+ probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX],
+ probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]);
}
// This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY.
@@ -72,20 +53,19 @@ public class WordProperty {
final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo,
final ArrayList<int[]> shortcutTargets,
final ArrayList<Integer> shortcutProbabilities) {
- mCodePoints = new String(codePoints, 0 /* offset */, getCodePointCount(codePoints));
+ mCodePoints = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
mIsNotAWord = isNotAWord;
mIsBlacklisted = isBlacklisted;
mHasBigrams = hasBigram;
mHasShortcuts = hasShortcuts;
- mProbabilityInfo = new ProbabilityInfo(probabilityInfo);
+ mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
final int bigramTargetCount = bigramTargets.size();
for (int i = 0; i < bigramTargetCount; i++) {
- final int[] bigramTargetCodePointArray = bigramTargets.get(i);
- final String bigramTargetString = new String(bigramTargetCodePointArray,
- 0 /* offset */, getCodePointCount(bigramTargetCodePointArray));
+ final String bigramTargetString =
+ StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i));
final ProbabilityInfo bigramProbability =
- new ProbabilityInfo(bigramProbabilityInfo.get(i));
+ createProbabilityInfoFromArray(bigramProbabilityInfo.get(i));
mBigramTargets.add(
new WeightedString(bigramTargetString, bigramProbability.mProbability));
mBigramProbabilityInfo.add(bigramProbability);
@@ -93,9 +73,8 @@ public class WordProperty {
final int shortcutTargetCount = shortcutTargets.size();
for (int i = 0; i < shortcutTargetCount; i++) {
- final int[] shortcutTargetCodePointArray = shortcutTargets.get(i);
- final String shortcutTargetString = new String(shortcutTargetCodePointArray,
- 0 /* offset */, getCodePointCount(shortcutTargetCodePointArray));
+ final String shortcutTargetString =
+ StringUtils.getStringFromNullTerminatedCodePointArray(shortcutTargets.get(i));
mShortcutTargets.add(
new WeightedString(shortcutTargetString, shortcutProbabilities.get(i)));
}
@@ -105,4 +84,44 @@ public class WordProperty {
public boolean isValid() {
return mProbabilityInfo.mProbability != BinaryDictionary.NOT_A_PROBABILITY;
}
+
+ @Override
+ public String toString() {
+ // TODO: Move this logic to CombinedInputOutput.
+ final StringBuffer builder = new StringBuffer();
+ builder.append(" word=" + mCodePoints);
+ builder.append(",");
+ builder.append("f=" + mProbabilityInfo.mProbability);
+ if (mIsNotAWord) {
+ builder.append(",");
+ builder.append("not_a_word=true");
+ }
+ if (mIsBlacklisted) {
+ builder.append(",");
+ builder.append("blacklisted=true");
+ }
+ if (mProbabilityInfo.mTimestamp != BinaryDictionary.NOT_A_VALID_TIMESTAMP) {
+ builder.append(",");
+ builder.append("historicalInfo=" + mProbabilityInfo);
+ }
+ builder.append("\n");
+ for (int i = 0; i < mBigramTargets.size(); i++) {
+ builder.append(" bigram=" + mBigramTargets.get(i).mWord);
+ builder.append(",");
+ builder.append("f=" + mBigramTargets.get(i).mFrequency);
+ if (mBigramProbabilityInfo.get(i).mTimestamp
+ != BinaryDictionary.NOT_A_VALID_TIMESTAMP) {
+ builder.append(",");
+ builder.append("historicalInfo=" + mBigramProbabilityInfo.get(i));
+ }
+ builder.append("\n");
+ }
+ for (int i = 0; i < mShortcutTargets.size(); i++) {
+ builder.append(" shortcut=" + mShortcutTargets.get(i).mWord);
+ builder.append(",");
+ builder.append("f=" + mShortcutTargets.get(i).mFrequency);
+ builder.append("\n");
+ }
+ return builder.toString();
+ }
} \ No newline at end of file