aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java8
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java (renamed from java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java)30
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java223
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java24
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java34
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java42
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java58
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java714
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java382
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java47
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java77
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java58
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java14
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java13
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java25
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java1
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java1306
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java27
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java (renamed from java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java)66
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java66
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java206
-rw-r--r--java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java16
-rw-r--r--java/src/com/android/inputmethod/latin/AssetFileAddress.java10
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java139
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java26
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java29
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java14
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java7
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java50
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryWriter.java26
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java461
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java894
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java6
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java289
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java213
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java63
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java11
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java5
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java12
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java39
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java48
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java102
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java60
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java88
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictDecoder.java11
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java6
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java152
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java147
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java120
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java123
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java93
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java340
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java103
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java723
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java169
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java190
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java50
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java8
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java84
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java70
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java37
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java23
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettings.java3
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java30
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java119
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java33
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java10
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java1
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java5
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java2
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java11
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java79
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java18
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java12
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FileUtils.java33
-rw-r--r--java/src/com/android/inputmethod/latin/utils/JsonUtils.java103
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LocaleUtils.java47
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ResourceUtils.java8
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java (renamed from java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java)20
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java100
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java4
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java36
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UnigramProperty.java82
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java12
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java9
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java69
99 files changed, 4448 insertions, 4926 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index c601cf17e..73896dfd3 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -82,7 +82,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp
private void initInternal(final InputMethodService inputMethod) {
mInputMethod = inputMethod;
mEdgeSlop = inputMethod.getResources().getDimensionPixelSize(
- R.dimen.config_accessibility_edge_slop);
+ R.dimen.accessibility_edge_slop);
}
/**
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
index d56a3cf25..e23131a30 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
@@ -31,17 +31,17 @@ public class EmojiCategoryPageIndicatorView extends LinearLayout {
private int mCurrentCategoryPageId = 0;
private float mOffset = 0.0f;
- public EmojiCategoryPageIndicatorView(final Context context) {
+ public EmojiCategoryPageIndicatorView(Context context) {
this(context, null /* attrs */);
}
- public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs) {
+ public EmojiCategoryPageIndicatorView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint.setColor(context.getResources().getColor(
R.color.emoji_category_page_id_view_foreground));
}
- public void setCategoryPageId(final int size, final int id, final float offset) {
+ public void setCategoryPageId(int size, int id, float offset) {
mCategoryPageSize = size;
mCurrentCategoryPageId = id;
mOffset = offset;
@@ -49,7 +49,7 @@ public class EmojiCategoryPageIndicatorView extends LinearLayout {
}
@Override
- protected void onDraw(final Canvas canvas) {
+ protected void onDraw(Canvas canvas) {
if (mCategoryPageSize <= 1) {
// If the category is not set yet or contains only one category,
// just clear and return.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
index 12e063261..967448c28 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.inputmethod.keyboard.internal;
+package com.android.inputmethod.keyboard;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.utils.ResourceUtils;
@@ -37,22 +37,22 @@ public class EmojiLayoutParams {
private final int mBottomPadding;
private final int mTopPadding;
- public EmojiLayoutParams(final Resources res) {
+ public EmojiLayoutParams(Resources res) {
final int defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
final int defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
- mKeyVerticalGap = (int) res.getFraction(R.fraction.config_key_vertical_gap_holo,
- defaultKeyboardHeight, defaultKeyboardHeight);
- mBottomPadding = (int) res.getFraction(R.fraction.config_keyboard_bottom_padding_holo,
- defaultKeyboardHeight, defaultKeyboardHeight);
- mTopPadding = (int) res.getFraction(R.fraction.config_keyboard_top_padding_holo,
- defaultKeyboardHeight, defaultKeyboardHeight);
- mKeyHorizontalGap = (int) (res.getFraction(R.fraction.config_key_horizontal_gap_holo,
+ mKeyVerticalGap = (int) res.getFraction(R.fraction.key_bottom_gap_holo,
+ (int) defaultKeyboardHeight, (int) defaultKeyboardHeight);
+ mBottomPadding = (int) res.getFraction(R.fraction.keyboard_bottom_padding_holo,
+ (int) defaultKeyboardHeight, (int) defaultKeyboardHeight);
+ mTopPadding = (int) res.getFraction(R.fraction.keyboard_top_padding_holo,
+ (int) defaultKeyboardHeight, (int) defaultKeyboardHeight);
+ mKeyHorizontalGap = (int) (res.getFraction(R.fraction.key_horizontal_gap_holo,
defaultKeyboardWidth, defaultKeyboardWidth));
mEmojiCategoryPageIdViewHeight =
- (int) (res.getDimension(R.dimen.config_emoji_category_page_id_height));
+ (int) (res.getDimension(R.dimen.emoji_category_page_id_height));
final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding
+ mKeyVerticalGap;
- mEmojiActionBarHeight = baseheight / DEFAULT_KEYBOARD_ROWS
+ mEmojiActionBarHeight = ((int) baseheight) / DEFAULT_KEYBOARD_ROWS
- (mKeyVerticalGap - mBottomPadding) / 2;
mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight
- mEmojiCategoryPageIdViewHeight;
@@ -60,26 +60,26 @@ public class EmojiLayoutParams {
mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1;
}
- public void setPagerProperties(final ViewPager vp) {
+ public void setPagerProperties(ViewPager vp) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams();
lp.height = mEmojiKeyboardHeight;
lp.bottomMargin = mEmojiPagerBottomMargin;
vp.setLayoutParams(lp);
}
- public void setCategoryPageIdViewProperties(final LinearLayout ll) {
+ public void setCategoryPageIdViewProperties(LinearLayout ll) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
lp.height = mEmojiCategoryPageIdViewHeight;
ll.setLayoutParams(lp);
}
- public void setActionBarProperties(final LinearLayout ll) {
+ public void setActionBarProperties(LinearLayout ll) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
lp.height = mEmojiActionBarHeight - mBottomPadding;
ll.setLayoutParams(lp);
}
- public void setKeyProperties(final ImageView ib) {
+ public void setKeyProperties(ImageView ib) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ib.getLayoutParams();
lp.leftMargin = mKeyHorizontalGap / 2;
lp.rightMargin = mKeyHorizontalGap / 2;
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index ff0d53865..f12373503 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -44,8 +44,8 @@ import android.widget.TabHost.OnTabChangeListener;
import android.widget.TextView;
import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard;
-import com.android.inputmethod.keyboard.internal.EmojiLayoutParams;
-import com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView;
+import com.android.inputmethod.keyboard.internal.ScrollKeyboardView;
+import com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -72,15 +72,15 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
ViewPager.OnPageChangeListener, View.OnClickListener,
- EmojiPageKeyboardView.OnKeyClickListener {
- static final String TAG = EmojiPalettesView.class.getSimpleName();
+ ScrollKeyboardView.OnKeyClickListener {
+ private static final String TAG = EmojiPalettesView.class.getSimpleName();
private static final boolean DEBUG_PAGER = false;
private final int mKeyBackgroundId;
private final int mEmojiFunctionalKeyBackgroundId;
+ private final KeyboardLayoutSet mLayoutSet;
private final ColorStateList mTabLabelColor;
private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener;
private EmojiPalettesAdapter mEmojiPalettesAdapter;
- private final EmojiLayoutParams mEmojiLayoutParams;
private TabHost mTabHost;
private ViewPager mEmojiPager;
@@ -149,7 +149,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
public EmojiCategory(final SharedPreferences prefs, final Resources res,
final KeyboardLayoutSet layoutSet) {
mPrefs = prefs;
- mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count);
+ mMaxPageKeyCount = res.getInteger(R.integer.emoji_keyboard_max_key_count);
mLayoutSet = layoutSet;
for (int i = 0; i < sCategoryName.length; ++i) {
mCategoryNameToIdMap.put(sCategoryName[i], i);
@@ -174,7 +174,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
.loadRecentKeys(mCategoryKeyboardMap.values());
}
- private void addShownCategoryId(final int categoryId) {
+ private void addShownCategoryId(int categoryId) {
// Load a keyboard of categoryId
getKeyboard(categoryId, 0 /* cagetoryPageId */);
final CategoryProperties properties =
@@ -182,20 +182,20 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
mShownCategories.add(properties);
}
- public String getCategoryName(final int categoryId, final int categoryPageId) {
+ public String getCategoryName(int categoryId, int categoryPageId) {
return sCategoryName[categoryId] + "-" + categoryPageId;
}
- public int getCategoryId(final String name) {
+ public int getCategoryId(String name) {
final String[] strings = name.split("-");
return mCategoryNameToIdMap.get(strings[0]);
}
- public int getCategoryIcon(final int categoryId) {
+ public int getCategoryIcon(int categoryId) {
return sCategoryIcon[categoryId];
}
- public String getCategoryLabel(final int categoryId) {
+ public String getCategoryLabel(int categoryId) {
return sCategoryLabel[categoryId];
}
@@ -211,7 +211,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
return getCategoryPageSize(mCurrentCategoryId);
}
- public int getCategoryPageSize(final int categoryId) {
+ public int getCategoryPageSize(int categoryId) {
for (final CategoryProperties prop : mShownCategories) {
if (prop.mCategoryId == categoryId) {
return prop.mPageCount;
@@ -222,12 +222,12 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
return 0;
}
- public void setCurrentCategoryId(final int categoryId) {
+ public void setCurrentCategoryId(int categoryId) {
mCurrentCategoryId = categoryId;
Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId);
}
- public void setCurrentCategoryPageId(final int id) {
+ public void setCurrentCategoryPageId(int id) {
mCurrentCategoryPageId = id;
}
@@ -244,7 +244,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
return mCurrentCategoryId == CATEGORY_ID_RECENTS;
}
- public int getTabIdFromCategoryId(final int categoryId) {
+ public int getTabIdFromCategoryId(int categoryId) {
for (int i = 0; i < mShownCategories.size(); ++i) {
if (mShownCategories.get(i).mCategoryId == categoryId) {
return i;
@@ -255,7 +255,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
}
// Returns the view pager's page position for the categoryId
- public int getPageIdFromCategoryId(final int categoryId) {
+ public int getPageIdFromCategoryId(int categoryId) {
final int lastSavedCategoryPageId =
Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId);
int sum = 0;
@@ -274,7 +274,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
return getTabIdFromCategoryId(CATEGORY_ID_RECENTS);
}
- private int getCategoryPageCount(final int categoryId) {
+ private int getCategoryPageCount(int categoryId) {
final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
return (keyboard.getKeys().length - 1) / mMaxPageKeyCount + 1;
}
@@ -283,9 +283,9 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
// position. The category page id is numbered in each category. And the view page position
// is the position of the current shown page in the view pager which contains all pages of
// all categories.
- public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(final int position) {
+ public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(int position) {
int sum = 0;
- for (final CategoryProperties properties : mShownCategories) {
+ for (CategoryProperties properties : mShownCategories) {
final int temp = sum;
sum += properties.mPageCount;
if (sum > position) {
@@ -296,7 +296,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
}
// Returns a keyboard from the view pager's page position.
- public DynamicGridKeyboard getKeyboardFromPagePosition(final int position) {
+ public DynamicGridKeyboard getKeyboardFromPagePosition(int position) {
final Pair<Integer, Integer> categoryAndId =
getCategoryIdAndPageIdFromPagePosition(position);
if (categoryAndId != null) {
@@ -305,41 +305,39 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
return null;
}
- private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) {
- return (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id;
- }
-
- public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
- synchronized (mCategoryKeyboardMap) {
- final Long categotyKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id);
- if (mCategoryKeyboardMap.containsKey(categotyKeyboardMapKey)) {
- return mCategoryKeyboardMap.get(categotyKeyboardMapKey);
- }
-
- if (categoryId == CATEGORY_ID_RECENTS) {
- final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
- mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- mMaxPageKeyCount, categoryId);
- mCategoryKeyboardMap.put(categotyKeyboardMapKey, kbd);
- return kbd;
- }
-
- final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
- final Key[][] sortedKeys = sortKeysIntoPages(keyboard.getKeys(), mMaxPageKeyCount);
- for (int pageId = 0; pageId < sortedKeys.length; ++pageId) {
- final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
- mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
- mMaxPageKeyCount, categoryId);
- for (final Key emojiKey : sortedKeys[pageId]) {
- if (emojiKey == null) {
- break;
+ public DynamicGridKeyboard getKeyboard(int categoryId, int id) {
+ synchronized(mCategoryKeyboardMap) {
+ final long key = (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id;
+ final DynamicGridKeyboard kbd;
+ if (!mCategoryKeyboardMap.containsKey(key)) {
+ if (categoryId != CATEGORY_ID_RECENTS) {
+ final Keyboard keyboard =
+ mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
+ final Key[][] sortedKeys = sortKeys(keyboard.getKeys(), mMaxPageKeyCount);
+ for (int i = 0; i < sortedKeys.length; ++i) {
+ final DynamicGridKeyboard tempKbd = new DynamicGridKeyboard(mPrefs,
+ mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+ mMaxPageKeyCount, categoryId, i /* categoryPageId */);
+ for (Key emojiKey : sortedKeys[i]) {
+ if (emojiKey == null) {
+ break;
+ }
+ tempKbd.addKeyLast(emojiKey);
+ }
+ mCategoryKeyboardMap.put((((long) categoryId)
+ << Constants.MAX_INT_BIT_COUNT) | i, tempKbd);
}
- tempKeyboard.addKeyLast(emojiKey);
+ kbd = mCategoryKeyboardMap.get(key);
+ } else {
+ kbd = new DynamicGridKeyboard(mPrefs,
+ mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+ mMaxPageKeyCount, categoryId, 0 /* categoryPageId */);
+ mCategoryKeyboardMap.put(key, kbd);
}
- mCategoryKeyboardMap.put(
- getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard);
+ } else {
+ kbd = mCategoryKeyboardMap.get(key);
}
- return mCategoryKeyboardMap.get(categotyKeyboardMapKey);
+ return kbd;
}
}
@@ -351,31 +349,29 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
return sum;
}
- private static Comparator<Key> EMOJI_KEY_COMPARATOR = new Comparator<Key>() {
- @Override
- public int compare(final Key lhs, final Key rhs) {
- final Rect lHitBox = lhs.getHitBox();
- final Rect rHitBox = rhs.getHitBox();
- if (lHitBox.top < rHitBox.top) {
- return -1;
- } else if (lHitBox.top > rHitBox.top) {
- return 1;
- }
- if (lHitBox.left < rHitBox.left) {
- return -1;
- } else if (lHitBox.left > rHitBox.left) {
- return 1;
- }
- if (lhs.getCode() == rhs.getCode()) {
- return 0;
+ private Key[][] sortKeys(Key[] inKeys, int maxPageCount) {
+ Key[] keys = Arrays.copyOf(inKeys, inKeys.length);
+ Arrays.sort(keys, 0, keys.length, new Comparator<Key>() {
+ @Override
+ public int compare(Key lhs, Key rhs) {
+ final Rect lHitBox = lhs.getHitBox();
+ final Rect rHitBox = rhs.getHitBox();
+ if (lHitBox.top < rHitBox.top) {
+ return -1;
+ } else if (lHitBox.top > rHitBox.top) {
+ return 1;
+ }
+ if (lHitBox.left < rHitBox.left) {
+ return -1;
+ } else if (lHitBox.left > rHitBox.left) {
+ return 1;
+ }
+ if (lhs.getCode() == rhs.getCode()) {
+ return 0;
+ }
+ return lhs.getCode() < rhs.getCode() ? -1 : 1;
}
- return lhs.getCode() < rhs.getCode() ? -1 : 1;
- }
- };
-
- private static Key[][] sortKeysIntoPages(final Key[] inKeys, final int maxPageCount) {
- final Key[] keys = Arrays.copyOf(inKeys, inKeys.length);
- Arrays.sort(keys, 0, keys.length, EMOJI_KEY_COMPARATOR);
+ });
final int pageCount = (keys.length - 1) / maxPageCount + 1;
final Key[][] retval = new Key[pageCount][maxPageCount];
for (int i = 0; i < keys.length; ++i) {
@@ -408,12 +404,12 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
context, null /* editorInfo */);
final Resources res = context.getResources();
- mEmojiLayoutParams = new EmojiLayoutParams(res);
+ final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res);
builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype());
builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res),
- mEmojiLayoutParams.mEmojiKeyboardHeight);
- builder.setOptions(false /* shortcutImeEnabled */, false /* showsVoiceInputKey */,
- false /* languageSwitchKeyEnabled */);
+ emojiLp.mEmojiKeyboardHeight);
+ builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */);
+ mLayoutSet = builder.build();
mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context),
context.getResources(), builder.build());
mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(context);
@@ -427,7 +423,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
final int width = ResourceUtils.getDefaultKeyboardWidth(res)
+ getPaddingLeft() + getPaddingRight();
final int height = ResourceUtils.getDefaultKeyboardHeight(res)
- + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height)
+ + res.getDimensionPixelSize(R.dimen.suggestions_strip_height)
+ getPaddingTop() + getPaddingBottom();
setMeasuredDimension(width, height);
}
@@ -462,23 +458,25 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
mTabHost.setOnTabChangedListener(this);
mTabHost.getTabWidget().setStripEnabled(true);
- mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this);
+ mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, mLayoutSet, this);
mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager);
mEmojiPager.setAdapter(mEmojiPalettesAdapter);
mEmojiPager.setOnPageChangeListener(this);
mEmojiPager.setOffscreenPageLimit(0);
- mEmojiPager.setPersistentDrawingCache(PERSISTENT_NO_CACHE);
- mEmojiLayoutParams.setPagerProperties(mEmojiPager);
+ mEmojiPager.setPersistentDrawingCache(ViewPager.PERSISTENT_NO_CACHE);
+ final Resources res = getResources();
+ final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res);
+ emojiLp.setPagerProperties(mEmojiPager);
mEmojiCategoryPageIndicatorView =
(EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view);
- mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
+ emojiLp.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */);
final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar);
- mEmojiLayoutParams.setActionBarProperties(actionBar);
+ emojiLp.setActionBarProperties(actionBar);
final ImageView deleteKey = (ImageView)findViewById(R.id.emoji_keyboard_delete);
deleteKey.setTag(Constants.CODE_DELETE);
@@ -491,7 +489,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
spaceKey.setBackgroundResource(mKeyBackgroundId);
spaceKey.setTag(Constants.CODE_SPACE);
spaceKey.setOnClickListener(this);
- mEmojiLayoutParams.setKeyProperties(spaceKey);
+ emojiLp.setKeyProperties(spaceKey);
final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
@@ -630,15 +628,16 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
}
private static class EmojiPalettesAdapter extends PagerAdapter {
- private final EmojiPageKeyboardView.OnKeyClickListener mListener;
+ private final ScrollKeyboardView.OnKeyClickListener mListener;
private final DynamicGridKeyboard mRecentsKeyboard;
- private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews =
+ private final SparseArray<ScrollKeyboardView> mActiveKeyboardViews =
CollectionUtils.newSparseArray();
private final EmojiCategory mEmojiCategory;
private int mActivePosition = 0;
public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
- final EmojiPageKeyboardView.OnKeyClickListener listener) {
+ final KeyboardLayoutSet layoutSet,
+ final ScrollKeyboardView.OnKeyClickListener listener) {
mEmojiCategory = emojiCategory;
mListener = listener;
mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0);
@@ -672,12 +671,11 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
}
@Override
- public void setPrimaryItem(final ViewGroup container, final int position,
- final Object object) {
+ public void setPrimaryItem(final View container, final int position, final Object object) {
if (mActivePosition == position) {
return;
}
- final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
+ final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
if (oldKeyboardView != null) {
oldKeyboardView.releaseCurrentKey();
oldKeyboardView.deallocateMemory();
@@ -690,7 +688,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
if (DEBUG_PAGER) {
Log.d(TAG, "instantiate item: " + position);
}
- final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
+ final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
if (oldKeyboardView != null) {
oldKeyboardView.deallocateMemory();
// This may be redundant but wanted to be safer..
@@ -699,13 +697,18 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
final Keyboard keyboard =
mEmojiCategory.getKeyboardFromPagePosition(position);
final LayoutInflater inflater = LayoutInflater.from(container.getContext());
- final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
+ final View view = inflater.inflate(
R.layout.emoji_keyboard_page, container, false /* attachToRoot */);
+ final ScrollKeyboardView keyboardView = (ScrollKeyboardView)view.findViewById(
+ R.id.emoji_keyboard_page);
keyboardView.setKeyboard(keyboard);
keyboardView.setOnKeyClickListener(mListener);
- container.addView(keyboardView);
+ final ScrollViewWithNotifier scrollView = (ScrollViewWithNotifier)view.findViewById(
+ R.id.emoji_keyboard_scroller);
+ keyboardView.setScrollView(scrollView);
+ container.addView(view);
mActiveKeyboardViews.put(position, keyboardView);
- return keyboardView;
+ return view;
}
@Override
@@ -719,7 +722,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
if (DEBUG_PAGER) {
Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName());
}
- final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position);
+ final ScrollKeyboardView keyboardView = mActiveKeyboardViews.get(position);
if (keyboardView != null) {
keyboardView.deallocateMemory();
mActiveKeyboardViews.remove(position);
@@ -792,7 +795,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
}
}
- public void pressDelete(final int repeatCount) {
+ public void pressDelete(int repeatCount) {
mKeyboardActionListener.onPressKey(
Constants.CODE_DELETE, repeatCount, true /* isSinglePointer */);
mKeyboardActionListener.onCodeInput(
@@ -801,22 +804,22 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
Constants.CODE_DELETE, false /* withSliding */);
}
- public void setKeyboardActionListener(final KeyboardActionListener listener) {
+ public void setKeyboardActionListener(KeyboardActionListener listener) {
mKeyboardActionListener = listener;
}
@Override
- public boolean onTouch(final View v, final MotionEvent event) {
+ public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- v.setBackgroundColor(mDeleteKeyPressedBackgroundColor);
- pressDelete(0 /* repeatCount */);
- startRepeat();
- return true;
- case MotionEvent.ACTION_UP:
- v.setBackgroundColor(0);
- abortRepeat();
- return true;
+ case MotionEvent.ACTION_DOWN:
+ v.setBackgroundColor(mDeleteKeyPressedBackgroundColor);
+ pressDelete(0 /* repeatCount */);
+ startRepeat();
+ return true;
+ case MotionEvent.ACTION_UP:
+ v.setBackgroundColor(0);
+ abortRepeat();
+ return true;
}
return false;
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index b975b9c70..f7ec9509d 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Xml;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
@@ -52,6 +53,8 @@ import java.util.Locale;
* Class for describing the position and characteristics of a single key in the keyboard.
*/
public class Key implements Comparable<Key> {
+ private static final String TAG = Key.class.getSimpleName();
+
/**
* The key code (unicode or custom code) that this key generates.
*/
@@ -81,16 +84,10 @@ public class Key implements Comparable<Key> {
private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800;
private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000;
private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000;
- // The bit to calculate the ratio of key label width against key width. If autoXScale bit is on
- // and autoYScale bit is off, the key label may be shrunk only for X-direction.
- // If both autoXScale and autoYScale bits are on, the key label text size may be auto scaled.
private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000;
- private static final int LABEL_FLAGS_AUTO_Y_SCALE = 0x8000;
- private static final int LABEL_FLAGS_AUTO_SCALE = LABEL_FLAGS_AUTO_X_SCALE
- | LABEL_FLAGS_AUTO_Y_SCALE;
- private static final int LABEL_FLAGS_PRESERVE_CASE = 0x10000;
- private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x20000;
- private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x40000;
+ private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000;
+ private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000;
+ private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x20000;
private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000;
private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000;
@@ -379,6 +376,9 @@ public class Key implements Comparable<Key> {
mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
keyAttr.recycle();
mHashCode = computeHashCode(this);
+ if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) {
+ Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this);
+ }
}
/**
@@ -702,14 +702,10 @@ public class Key implements Comparable<Key> {
return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0;
}
- public final boolean needsAutoXScale() {
+ public final boolean needsXScale() {
return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0;
}
- public final boolean needsAutoScale() {
- return (mLabelFlags & LABEL_FLAGS_AUTO_SCALE) == LABEL_FLAGS_AUTO_SCALE;
- }
-
public final boolean isShiftedLetterActivated() {
return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 149f10fd7..befb6fa92 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -81,7 +81,7 @@ public class KeyDetector {
return mKeyboard;
}
- public boolean alwaysAllowsKeySelectionByDraggingFinger() {
+ public boolean alwaysAllowsSlidingInput() {
return false;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 02beb3f11..736f13ed6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -70,7 +70,8 @@ public final class KeyboardId {
public final int mElementId;
private final EditorInfo mEditorInfo;
public final boolean mClobberSettingsKey;
- public final boolean mSupportsSwitchingToShortcutIme;
+ public final boolean mShortcutKeyEnabled;
+ public final boolean mShortcutKeyOnSymbols;
public final boolean mLanguageSwitchKeyEnabled;
public final String mCustomActionLabel;
public final boolean mHasShortcutKey;
@@ -86,11 +87,17 @@ public final class KeyboardId {
mElementId = elementId;
mEditorInfo = params.mEditorInfo;
mClobberSettingsKey = params.mNoSettingsKey;
- mSupportsSwitchingToShortcutIme = params.mSupportsSwitchingToShortcutIme;
+ mShortcutKeyEnabled = params.mVoiceKeyEnabled;
+ mShortcutKeyOnSymbols = mShortcutKeyEnabled && !params.mVoiceKeyOnMain;
mLanguageSwitchKeyEnabled = params.mLanguageSwitchKeyEnabled;
mCustomActionLabel = (mEditorInfo.actionLabel != null)
? mEditorInfo.actionLabel.toString() : null;
- mHasShortcutKey = mSupportsSwitchingToShortcutIme && params.mShowsVoiceInputKey;
+ final boolean alphabetMayHaveShortcutKey = isAlphabetKeyboard(elementId)
+ && !mShortcutKeyOnSymbols;
+ final boolean symbolsMayHaveShortcutKey = (elementId == KeyboardId.ELEMENT_SYMBOLS)
+ && mShortcutKeyOnSymbols;
+ mHasShortcutKey = mShortcutKeyEnabled
+ && (alphabetMayHaveShortcutKey || symbolsMayHaveShortcutKey);
mHashCode = computeHashCode(this);
}
@@ -103,8 +110,8 @@ public final class KeyboardId {
id.mHeight,
id.passwordInput(),
id.mClobberSettingsKey,
- id.mSupportsSwitchingToShortcutIme,
- id.mHasShortcutKey,
+ id.mShortcutKeyEnabled,
+ id.mShortcutKeyOnSymbols,
id.mLanguageSwitchKeyEnabled,
id.isMultiLine(),
id.imeAction(),
@@ -124,8 +131,8 @@ public final class KeyboardId {
&& other.mHeight == mHeight
&& other.passwordInput() == passwordInput()
&& other.mClobberSettingsKey == mClobberSettingsKey
- && other.mSupportsSwitchingToShortcutIme == mSupportsSwitchingToShortcutIme
- && other.mHasShortcutKey == mHasShortcutKey
+ && other.mShortcutKeyEnabled == mShortcutKeyEnabled
+ && other.mShortcutKeyOnSymbols == mShortcutKeyOnSymbols
&& other.mLanguageSwitchKeyEnabled == mLanguageSwitchKeyEnabled
&& other.isMultiLine() == isMultiLine()
&& other.imeAction() == imeAction()
@@ -179,20 +186,21 @@ public final class KeyboardId {
@Override
public String toString() {
- return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]",
+ return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s %s%s%s%s%s%s%s%s%s]",
elementIdToName(mElementId),
mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
mWidth, mHeight,
modeName(mMode),
- actionName(imeAction()),
- (navigateNext() ? " navigateNext" : ""),
- (navigatePrevious() ? " navigatePrevious" : ""),
+ imeAction(),
+ (navigateNext() ? "navigateNext" : ""),
+ (navigatePrevious() ? "navigatePrevious" : ""),
(mClobberSettingsKey ? " clobberSettingsKey" : ""),
(passwordInput() ? " passwordInput" : ""),
- (mSupportsSwitchingToShortcutIme ? " supportsSwitchingToShortcutIme" : ""),
+ (mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""),
+ (mShortcutKeyOnSymbols ? " shortcutKeyOnSymbols" : ""),
(mHasShortcutKey ? " hasShortcutKey" : ""),
(mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""),
- (isMultiLine() ? " isMultiLine" : "")
+ (isMultiLine() ? "isMultiLine" : "")
);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index e5b814faf..1eccdf341 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -105,10 +105,10 @@ public final class KeyboardLayoutSet {
int mMode;
EditorInfo mEditorInfo;
boolean mDisableTouchPositionCorrectionDataForTest;
- boolean mIsPasswordField;
- boolean mSupportsSwitchingToShortcutIme;
- boolean mShowsVoiceInputKey;
- boolean mNoMicrophoneKey;
+ boolean mVoiceKeyEnabled;
+ // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show
+ // the voice input key on the symbol layout
+ boolean mVoiceKeyOnMain;
boolean mNoSettingsKey;
boolean mLanguageSwitchKeyEnabled;
InputMethodSubtype mSubtype;
@@ -221,24 +221,16 @@ public final class KeyboardLayoutSet {
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
- public Builder(final Context context, final EditorInfo ei) {
+ public Builder(final Context context, final EditorInfo editorInfo) {
mContext = context;
mPackageName = context.getPackageName();
mResources = context.getResources();
final Params params = mParams;
- final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO;
params.mMode = getKeyboardMode(editorInfo);
- params.mEditorInfo = editorInfo;
- params.mIsPasswordField = InputTypeUtils.isPasswordInputType(editorInfo.inputType);
- @SuppressWarnings("deprecation")
- final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
- null, NO_MICROPHONE_COMPAT, editorInfo);
- params.mNoMicrophoneKey = InputAttributes.inPrivateImeOptions(
- mPackageName, NO_MICROPHONE, editorInfo)
- || deprecatedNoMicrophone;
+ params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO;
params.mNoSettingsKey = InputAttributes.inPrivateImeOptions(
- mPackageName, NO_SETTINGS_KEY, editorInfo);
+ mPackageName, NO_SETTINGS_KEY, params.mEditorInfo);
}
public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHeight) {
@@ -269,11 +261,18 @@ public final class KeyboardLayoutSet {
return this;
}
- public Builder setOptions(final boolean isShortcutImeEnabled,
- final boolean showsVoiceInputKey, final boolean languageSwitchKeyEnabled) {
- mParams.mSupportsSwitchingToShortcutIme =
- isShortcutImeEnabled && !mParams.mNoMicrophoneKey && !mParams.mIsPasswordField;
- mParams.mShowsVoiceInputKey = showsVoiceInputKey;
+ // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show
+ // the voice input key on the symbol layout
+ public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain,
+ final boolean languageSwitchKeyEnabled) {
+ @SuppressWarnings("deprecation")
+ final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
+ null, NO_MICROPHONE_COMPAT, mParams.mEditorInfo);
+ final boolean noMicrophone = InputAttributes.inPrivateImeOptions(
+ mPackageName, NO_MICROPHONE, mParams.mEditorInfo)
+ || deprecatedNoMicrophone;
+ mParams.mVoiceKeyEnabled = voiceKeyEnabled && !noMicrophone;
+ mParams.mVoiceKeyOnMain = voiceKeyOnMain;
mParams.mLanguageSwitchKeyEnabled = languageSwitchKeyEnabled;
return this;
}
@@ -369,6 +368,9 @@ public final class KeyboardLayoutSet {
}
private static int getKeyboardMode(final EditorInfo editorInfo) {
+ if (editorInfo == null)
+ return KeyboardId.MODE_TEXT;
+
final int inputType = editorInfo.inputType;
final int variation = inputType & InputType.TYPE_MASK_VARIATION;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index fb84f1d73..5abc9ab38 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -154,8 +154,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
builder.setOptions(
- mSubtypeSwitcher.isShortcutImeEnabled(),
- settingsValues.mShowsVoiceInputKey,
+ settingsValues.isVoiceKeyEnabled(editorInfo),
+ true /* always show a voice key on the main keyboard */,
settingsValues.isLanguageSwitchKeyEnabled());
mKeyboardLayoutSet = builder.build();
try {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 422bd12a3..5578713a0 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -113,6 +113,9 @@ public class KeyboardView extends View {
private final Canvas mOffscreenCanvas = new Canvas();
private final Paint mPaint = new Paint();
private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
+ private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
+ private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
+
public KeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.keyboardViewStyle);
}
@@ -319,7 +322,7 @@ public class KeyboardView extends View {
params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
if (!key.isSpacer()) {
- onDrawKeyBackground(key, canvas, mKeyBackground);
+ onDrawKeyBackground(key, canvas);
}
onDrawKeyTopVisuals(key, canvas, paint, params);
@@ -327,14 +330,14 @@ public class KeyboardView extends View {
}
// Draw key background.
- protected void onDrawKeyBackground(final Key key, final Canvas canvas,
- final Drawable background) {
+ protected void onDrawKeyBackground(final Key key, final Canvas canvas) {
final Rect padding = mKeyBackgroundPadding;
final int bgWidth = key.getDrawWidth() + padding.left + padding.right;
final int bgHeight = key.getHeight() + padding.top + padding.bottom;
final int bgX = -padding.left;
final int bgY = -padding.top;
final int[] drawableState = key.getCurrentDrawableState();
+ final Drawable background = mKeyBackground;
background.setState(drawableState);
final Rect bounds = background.getBounds();
if (bgWidth != bounds.right || bgHeight != bounds.bottom) {
@@ -367,8 +370,10 @@ public class KeyboardView extends View {
if (label != null) {
paint.setTypeface(key.selectTypeface(params));
paint.setTextSize(key.selectTextSize(params));
- final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
- final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
+ final float labelCharHeight = TypefaceUtils.getCharHeight(
+ KEY_LABEL_REFERENCE_CHAR, paint);
+ final float labelCharWidth = TypefaceUtils.getCharWidth(
+ KEY_LABEL_REFERENCE_CHAR, paint);
// Vertical label text alignment.
final float baseline = centerY + labelCharHeight / 2.0f;
@@ -386,12 +391,12 @@ public class KeyboardView extends View {
positionX = centerX - labelCharWidth * 7.0f / 4.0f;
paint.setTextAlign(Align.LEFT);
} else if (key.hasLabelWithIconLeft() && icon != null) {
- labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth()
+ labelWidth = TypefaceUtils.getLabelWidth(label, paint) + icon.getIntrinsicWidth()
+ LABEL_ICON_MARGIN * keyWidth;
positionX = centerX + labelWidth / 2.0f;
paint.setTextAlign(Align.RIGHT);
} else if (key.hasLabelWithIconRight() && icon != null) {
- labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth()
+ labelWidth = TypefaceUtils.getLabelWidth(label, paint) + icon.getIntrinsicWidth()
+ LABEL_ICON_MARGIN * keyWidth;
positionX = centerX - labelWidth / 2.0f;
paint.setTextAlign(Align.LEFT);
@@ -399,15 +404,9 @@ public class KeyboardView extends View {
positionX = centerX;
paint.setTextAlign(Align.CENTER);
}
- if (key.needsAutoXScale()) {
- final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) /
- TypefaceUtils.getStringWidth(label, paint));
- if (key.needsAutoScale()) {
- final float autoSize = paint.getTextSize() * ratio;
- paint.setTextSize(autoSize);
- } else {
- paint.setTextScaleX(ratio);
- }
+ if (key.needsXScale()) {
+ paint.setTextScaleX(Math.min(1.0f,
+ (keyWidth * MAX_LABEL_RATIO) / TypefaceUtils.getLabelWidth(label, paint)));
}
paint.setColor(key.selectTextColor(params));
@@ -452,35 +451,36 @@ public class KeyboardView extends View {
// TODO: Should add a way to specify type face for hint letters
paint.setTypeface(Typeface.DEFAULT_BOLD);
blendAlpha(paint, params.mAnimAlpha);
- final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint);
- final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint);
- final KeyVisualAttributes visualAttr = key.getVisualAttributes();
- final float adjustmentY = (visualAttr == null) ? 0.0f
- : visualAttr.mHintLabelVerticalAdjustment * labelCharHeight;
final float hintX, hintY;
if (key.hasHintLabel()) {
// The hint label is placed just right of the key label. Used mainly on
// "phone number" layout.
// TODO: Generalize the following calculations.
- hintX = positionX + labelCharWidth * 2.0f;
- hintY = centerY + labelCharHeight / 2.0f;
+ hintX = positionX
+ + TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) * 2.0f;
+ hintY = centerY
+ + TypefaceUtils.getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f;
paint.setTextAlign(Align.LEFT);
} else if (key.hasShiftedLetterHint()) {
// The hint label is placed at top-right corner of the key. Used mainly on tablet.
- hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f;
+ hintX = keyWidth - mKeyShiftedLetterHintPadding
+ - TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f;
paint.getFontMetrics(mFontMetrics);
hintY = -mFontMetrics.top;
paint.setTextAlign(Align.CENTER);
} else { // key.hasHintLetter()
// The hint letter is placed at top-right corner of the key. Used mainly on phone.
- final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint);
- final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint);
+ final float keyNumericHintLabelReferenceCharWidth =
+ TypefaceUtils.getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint);
+ final float keyHintLabelStringWidth =
+ TypefaceUtils.getStringWidth(hintLabel, paint);
hintX = keyWidth - mKeyHintLetterPadding
- - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f;
+ - Math.max(keyNumericHintLabelReferenceCharWidth, keyHintLabelStringWidth)
+ / 2.0f;
hintY = -paint.ascent();
paint.setTextAlign(Align.CENTER);
}
- canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY + adjustmentY, paint);
+ canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY, paint);
if (LatinImeLogger.sVISUALDEBUG) {
final Paint line = new Paint();
@@ -530,7 +530,7 @@ public class KeyboardView extends View {
paint.setColor(params.mHintLabelColor);
paint.setTextAlign(Align.CENTER);
final float hintX = keyWidth - mKeyHintLetterPadding
- - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f;
+ - TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f;
final float hintY = keyHeight - mKeyPopupHintLetterPadding;
canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint);
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 1400e05c8..13db47004 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;
@@ -31,24 +28,27 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.os.Message;
+import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.keyboard.internal.DrawingHandler;
+import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
+import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText;
import com.android.inputmethod.keyboard.internal.GestureTrailsPreview;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
@@ -56,7 +56,6 @@ import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper;
import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview;
-import com.android.inputmethod.keyboard.internal.TimerHandler;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
@@ -65,15 +64,13 @@ import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.settings.DebugSettings;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import com.android.inputmethod.latin.utils.TypefaceUtils;
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;
/**
@@ -81,10 +78,9 @@ import java.util.WeakHashMap;
*
* @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
* @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
- * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio
- * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor
- * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor
- * @attr ref R.styleable#MainKeyboardView_spacebarBackground
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
* @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
* @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
* @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
@@ -92,7 +88,7 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
* @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
* @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
- * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger
+ * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
* @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
* @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
* @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
@@ -118,27 +114,26 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
* @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
*/
-public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy,
- MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks {
+public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
+ PointerTracker.DrawingProxy, MoreKeysPanel.Controller {
private static final String TAG = MainKeyboardView.class.getSimpleName();
/** Listener for {@link KeyboardActionListener}. */
private KeyboardActionListener mKeyboardActionListener;
- /* Space key and its icon and background. */
+ /* Space key and its icons */
private Key mSpaceKey;
- private Drawable mSpacebarIcon;
- private final Drawable mSpacebarBackground;
+ private Drawable mSpaceIcon;
// Stuff to draw language name on spacebar.
private final int mLanguageOnSpacebarFinalAlpha;
private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
private boolean mNeedsToDisplayLanguage;
private boolean mHasMultipleEnabledIMEsOrSubtypes;
private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
- private final float mLanguageOnSpacebarTextRatio;
- private float mLanguageOnSpacebarTextSize;
- private final int mLanguageOnSpacebarTextColor;
- private final int mLanguageOnSpacebarTextShadowColor;
+ private final float mSpacebarTextRatio;
+ private float mSpacebarTextSize;
+ private final int mSpacebarTextColor;
+ private final int mSpacebarTextShadowColor;
// The minimum x-scale to fit the language name on spacebar.
private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
// Stuff to draw auto correction LED on spacebar.
@@ -148,8 +143,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private static final int SPACE_LED_LENGTH_PERCENT = 80;
// Stuff to draw altCodeWhileTyping keys.
- private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
- private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
+ private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
+ private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
// Preview placer view
@@ -160,26 +155,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private final SlidingKeyInputPreview mSlidingKeyInputPreview;
// 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 SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
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();
// More keys keyboard
private final Paint mBackgroundDimAlphaPaint = new Paint();
@@ -196,43 +178,253 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
// TODO: Make this parameter customizable by user via settings.
private int mGestureFloatingPreviewTextLingerTimeout;
- private final KeyDetector mKeyDetector;
+ private KeyDetector mKeyDetector;
private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
- private final TimerHandler mKeyTimerHandler;
+ private final KeyTimerHandler mKeyTimerHandler;
private final int mLanguageOnSpacebarHorizontalMargin;
- private final DrawingHandler mDrawingHandler =
- new DrawingHandler(this);
+ private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
+ implements TimerProxy {
+ private static final int MSG_TYPING_STATE_EXPIRED = 0;
+ private static final int MSG_REPEAT_KEY = 1;
+ private static final int MSG_LONGPRESS_KEY = 2;
+ private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 3;
+ private static final int MSG_UPDATE_BATCH_INPUT = 4;
- public MainKeyboardView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.mainKeyboardViewStyle);
+ private final int mIgnoreAltCodeKeyTimeout;
+ private final int mGestureRecognitionUpdateTime;
+
+ public KeyTimerHandler(final MainKeyboardView outerInstance,
+ final TypedArray mainKeyboardViewAttr) {
+ super(outerInstance);
+
+ mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
+ mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
+ }
+
+ @Override
+ public void handleMessage(final Message msg) {
+ final MainKeyboardView keyboardView = getOuterInstance();
+ if (keyboardView == null) {
+ return;
+ }
+ final PointerTracker tracker = (PointerTracker) msg.obj;
+ switch (msg.what) {
+ case MSG_TYPING_STATE_EXPIRED:
+ startWhileTypingFadeinAnimation(keyboardView);
+ break;
+ case MSG_REPEAT_KEY:
+ tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
+ break;
+ case MSG_LONGPRESS_KEY:
+ keyboardView.onLongPress(tracker);
+ break;
+ case MSG_UPDATE_BATCH_INPUT:
+ tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
+ startUpdateBatchInputTimer(tracker);
+ break;
+ }
+ }
+
+ @Override
+ public void startKeyRepeatTimer(final PointerTracker tracker, final int repeatCount,
+ final int delay) {
+ final Key key = tracker.getKey();
+ if (key == null || delay == 0) {
+ return;
+ }
+ sendMessageDelayed(
+ obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay);
+ }
+
+ public void cancelKeyRepeatTimer() {
+ removeMessages(MSG_REPEAT_KEY);
+ }
+
+ // TODO: Suppress layout changes in key repeat mode
+ public boolean isInKeyRepeat() {
+ return hasMessages(MSG_REPEAT_KEY);
+ }
+
+ @Override
+ public void startLongPressTimer(final PointerTracker tracker, final int delay) {
+ cancelLongPressTimer();
+ if (delay <= 0) return;
+ sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
+ }
+
+ @Override
+ public void cancelLongPressTimer() {
+ removeMessages(MSG_LONGPRESS_KEY);
+ }
+
+ private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
+ final ObjectAnimator animatorToStart) {
+ if (animatorToCancel == null || animatorToStart == null) {
+ // TODO: Stop using null as a no-operation animator.
+ return;
+ }
+ float startFraction = 0.0f;
+ if (animatorToCancel.isStarted()) {
+ animatorToCancel.cancel();
+ startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
+ }
+ final long startTime = (long)(animatorToStart.getDuration() * startFraction);
+ animatorToStart.start();
+ animatorToStart.setCurrentPlayTime(startTime);
+ }
+
+ private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
+ cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
+ keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
+ }
+
+ private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
+ cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
+ keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
+ }
+
+ @Override
+ public void startTypingStateTimer(final Key typedKey) {
+ if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
+ return;
+ }
+
+ final boolean isTyping = isTypingState();
+ removeMessages(MSG_TYPING_STATE_EXPIRED);
+ final MainKeyboardView keyboardView = getOuterInstance();
+
+ // When user hits the space or the enter key, just cancel the while-typing timer.
+ final int typedCode = typedKey.getCode();
+ if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
+ if (isTyping) {
+ startWhileTypingFadeinAnimation(keyboardView);
+ }
+ return;
+ }
+
+ sendMessageDelayed(
+ obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
+ if (isTyping) {
+ return;
+ }
+ startWhileTypingFadeoutAnimation(keyboardView);
+ }
+
+ @Override
+ public boolean isTypingState() {
+ return hasMessages(MSG_TYPING_STATE_EXPIRED);
+ }
+
+ @Override
+ public void startDoubleTapShiftKeyTimer() {
+ sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY),
+ ViewConfiguration.getDoubleTapTimeout());
+ }
+
+ @Override
+ public void cancelDoubleTapShiftKeyTimer() {
+ removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
+ }
+
+ @Override
+ public boolean isInDoubleTapShiftKeyTimeout() {
+ return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
+ }
+
+ @Override
+ public void cancelKeyTimers() {
+ cancelKeyRepeatTimer();
+ cancelLongPressTimer();
+ }
+
+ @Override
+ public void startUpdateBatchInputTimer(final PointerTracker tracker) {
+ if (mGestureRecognitionUpdateTime <= 0) {
+ return;
+ }
+ removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
+ mGestureRecognitionUpdateTime);
+ }
+
+ @Override
+ public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
+ removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
+ }
+
+ @Override
+ public void cancelAllUpdateBatchInputTimers() {
+ removeMessages(MSG_UPDATE_BATCH_INPUT);
+ }
+
+ public void cancelAllMessages() {
+ cancelKeyTimers();
+ cancelAllUpdateBatchInputTimers();
+ }
}
- public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
- super(context, attrs, defStyle);
+ private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
- mPreviewPlacerView = new PreviewPlacerView(context, attrs);
+ public static class DrawingHandler extends StaticInnerHandlerWrapper<MainKeyboardView> {
+ private static final int MSG_DISMISS_KEY_PREVIEW = 0;
+ private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
- final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
- attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
- final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
- final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
- mKeyTimerHandler = new TimerHandler(
- this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime);
+ public DrawingHandler(final MainKeyboardView outerInstance) {
+ super(outerInstance);
+ }
- final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
- final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
- R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
- mKeyDetector = new KeyDetector(
- keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
+ @Override
+ public void handleMessage(final Message msg) {
+ final MainKeyboardView mainKeyboardView = getOuterInstance();
+ if (mainKeyboardView == null) return;
+ final PointerTracker tracker = (PointerTracker) msg.obj;
+ switch (msg.what) {
+ case MSG_DISMISS_KEY_PREVIEW:
+ final TextView previewText = mainKeyboardView.mKeyPreviewTexts.get(
+ tracker.mPointerId);
+ if (previewText != null) {
+ previewText.setVisibility(INVISIBLE);
+ }
+ break;
+ case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
+ mainKeyboardView.showGestureFloatingPreviewText(SuggestedWords.EMPTY);
+ break;
+ }
+ }
+
+ public void dismissKeyPreview(final long delay, final PointerTracker tracker) {
+ sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
+ }
- PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */,
- mKeyDetector);
+ public void cancelDismissKeyPreview(final PointerTracker tracker) {
+ removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
+ }
+
+ private void cancelAllDismissKeyPreviews() {
+ removeMessages(MSG_DISMISS_KEY_PREVIEW);
+ }
+ public void dismissGestureFloatingPreviewText(final long delay) {
+ sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
+ }
+
+ public void cancelAllMessages() {
+ cancelAllDismissKeyPreviews();
+ }
+ }
+
+ public MainKeyboardView(final Context context, final AttributeSet attrs) {
+ this(context, attrs, R.attr.mainKeyboardViewStyle);
+ }
+
+ public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+
+ PointerTracker.init(getResources());
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final boolean forceNonDistinctMultitouch = prefs.getBoolean(
DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false);
@@ -240,24 +432,26 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
&& !forceNonDistinctMultitouch;
mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null
- : new NonDistinctMultitouchHelper(PointerTracker.getPointerTracker(0));
+ : new NonDistinctMultitouchHelper();
+
+ mPreviewPlacerView = new PreviewPlacerView(context, attrs);
+ final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes(
+ attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
final int backgroundDimAlpha = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
mBackgroundDimAlphaPaint.setColor(Color.BLACK);
mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
- mSpacebarBackground = mainKeyboardViewAttr.getDrawable(
- R.styleable.MainKeyboardView_spacebarBackground);
mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean(
R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable(
R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
- mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
- R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
- mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0);
- mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
- R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0);
+ mSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
+ R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
+ mSpacebarTextColor = mainKeyboardViewAttr.getColor(
+ R.styleable.MainKeyboardView_spacebarTextColor, 0);
+ mSpacebarTextShadowColor = mainKeyboardViewAttr.getColor(
+ R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
Constants.Color.ALPHA_OPAQUE);
@@ -268,6 +462,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId(
R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
+ final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension(
+ R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f);
+ final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension(
+ R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f);
+ mKeyDetector = new KeyDetector(
+ keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
+ mKeyTimerHandler = new KeyTimerHandler(this, mainKeyboardViewAttr);
mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset(
R.styleable.MainKeyboardView_keyPreviewOffset, 0);
mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize(
@@ -279,10 +480,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
if (mKeyPreviewLayoutId == 0) {
mShowKeyPreviewPopup = false;
}
- mKeyPreviewZoomInDuration = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyPreviewZoomInDuration, 0);
- mKeyPreviewZoomOutDuration = mainKeyboardViewAttr.getInt(
- R.styleable.MainKeyboardView_keyPreviewZoomOutDuration, 0);
final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId(
R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0);
mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean(
@@ -290,6 +487,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
+ PointerTracker.setParameters(mainKeyboardViewAttr);
mGestureFloatingPreviewText = new GestureFloatingPreviewText(
mPreviewPlacerView, mainKeyboardViewAttr);
@@ -315,8 +513,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
- mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension(
- R.dimen.config_language_on_spacebar_horizontal_margin);
+ mLanguageOnSpacebarHorizontalMargin =
+ (int) getResources().getDimension(R.dimen.language_on_spacebar_horizontal_margin);
}
@Override
@@ -338,35 +536,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
return animator;
}
- private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
- final ObjectAnimator animatorToStart) {
- if (animatorToCancel == null || animatorToStart == null) {
- // TODO: Stop using null as a no-operation animator.
- return;
- }
- float startFraction = 0.0f;
- if (animatorToCancel.isStarted()) {
- animatorToCancel.cancel();
- startFraction = 1.0f - animatorToCancel.getAnimatedFraction();
- }
- final long startTime = (long)(animatorToStart.getDuration() * startFraction);
- animatorToStart.start();
- animatorToStart.setCurrentPlayTime(startTime);
- }
-
- // Implements {@link TimerHander.Callbacks} method.
- @Override
- public void startWhileTypingFadeinAnimation() {
- cancelAndStartAnimators(
- mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator);
- }
-
- @Override
- public void startWhileTypingFadeoutAnimation() {
- cancelAndStartAnimators(
- mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator);
- }
-
@ExternallyReferenced
public int getLanguageOnSpacebarAnimAlpha() {
return mLanguageOnSpacebarAnimAlpha;
@@ -404,16 +573,28 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
PointerTracker.setKeyboardActionListener(listener);
}
- // TODO: We should reconsider which coordinate system should be used to represent keyboard
- // event.
- public int getKeyX(final int x) {
- return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x;
+ /**
+ * Returns the {@link KeyboardActionListener} object.
+ * @return the listener attached to this keyboard
+ */
+ @Override
+ public KeyboardActionListener getKeyboardActionListener() {
+ return mKeyboardActionListener;
}
- // TODO: We should reconsider which coordinate system should be used to represent keyboard
- // event.
- public int getKeyY(final int y) {
- return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y;
+ @Override
+ public KeyDetector getKeyDetector() {
+ return mKeyDetector;
+ }
+
+ @Override
+ public DrawingProxy getDrawingProxy() {
+ return this;
+ }
+
+ @Override
+ public TimerProxy getTimerProxy() {
+ return mKeyTimerHandler;
}
/**
@@ -425,20 +606,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
*/
@Override
public void setKeyboard(final Keyboard keyboard) {
- // Remove any pending messages.
- mKeyTimerHandler.cancelAllKeyTimers();
+ // Remove any pending messages, except dismissing preview and key repeat.
+ mKeyTimerHandler.cancelLongPressTimer();
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
PointerTracker.setKeyDetector(mKeyDetector);
- PointerTracker.setKeyboardActionListener(mKeyboardActionListener);
mMoreKeysKeyboardCache.clear();
mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
- mSpacebarIcon = (mSpaceKey != null)
+ mSpaceIcon = (mSpaceKey != null)
? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
- mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio;
+ mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
final int orientation = getContext().getResources().getConfiguration().orientation;
ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation);
@@ -501,33 +681,34 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
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;
+ private void addKeyPreview(final TextView keyPreview) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.addView(
+ keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
+ }
+
+ private TextView getKeyPreviewText(final int pointerId) {
+ TextView previewText = mKeyPreviewTexts.get(pointerId);
+ if (previewText != null) {
+ return previewText;
}
final Context context = getContext();
if (mKeyPreviewLayoutId != 0) {
- previewTextView = (TextView)LayoutInflater.from(context)
- .inflate(mKeyPreviewLayoutId, null);
+ previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
} else {
- previewTextView = new TextView(context);
+ previewText = new TextView(context);
}
- locatePreviewPlacerView();
- mPreviewPlacerView.addView(
- previewTextView, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
- return previewTextView;
+ mKeyPreviewTexts.put(pointerId, previewText);
+ return previewText;
}
- // Implements {@link DrawingHandler.Callbacks} method.
- @Override
- public void dismissAllKeyPreviews() {
- for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) {
- dismissKeyPreviewWithoutDelay(key);
+ private void dismissAllKeyPreviews() {
+ final int pointerCount = mKeyPreviewTexts.size();
+ for (int id = 0; id < pointerCount; id++) {
+ final TextView previewText = mKeyPreviewTexts.get(id);
+ if (previewText != null) {
+ previewText.setVisibility(INVISIBLE);
+ }
}
PointerTracker.setReleasedKeyGraphicsToAllKeys();
}
@@ -553,16 +734,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
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.
- // Trying to show key preview while root window is closed causes
- // WindowManager.BadTokenException.
- if (key == null) {
- return;
- }
-
+ public void showKeyPreview(final PointerTracker tracker) {
final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
final Keyboard keyboard = getKeyboard();
if (!mShowKeyPreviewPopup) {
@@ -570,40 +743,54 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
return;
}
- final TextView previewTextView = getKeyPreviewTextView(key);
+ final TextView previewText = getKeyPreviewText(tracker.mPointerId);
+ // If the key preview has no parent view yet, add it to the ViewGroup which can place
+ // key preview absolutely in SoftInputWindow.
+ if (previewText.getParent() == null) {
+ addKeyPreview(previewText);
+ }
+
+ mDrawingHandler.cancelDismissKeyPreview(tracker);
+ final Key key = tracker.getKey();
+ // If key is invalid or IME is already closed, we must not show key preview.
+ // Trying to show key preview while root window is closed causes
+ // WindowManager.BadTokenException.
+ if (key == null) {
+ return;
+ }
+
final KeyDrawParams drawParams = mKeyDrawParams;
- previewTextView.setTextColor(drawParams.mPreviewTextColor);
- final Drawable background = previewTextView.getBackground();
+ previewText.setTextColor(drawParams.mPreviewTextColor);
+ final Drawable background = previewText.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,
+ previewText.setCompoundDrawables(null, null, null, null);
+ previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
key.selectPreviewTextSize(drawParams));
- previewTextView.setTypeface(key.selectPreviewTypeface(drawParams));
- previewTextView.setText(label);
+ previewText.setTypeface(key.selectPreviewTypeface(drawParams));
+ previewText.setText(label);
} else {
- previewTextView.setCompoundDrawables(null, null, null,
+ previewText.setCompoundDrawables(null, null, null,
key.getPreviewIcon(keyboard.mIconsSet));
- previewTextView.setText(null);
+ previewText.setText(null);
}
- previewTextView.measure(
+ previewText.measure(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final int keyDrawWidth = key.getDrawWidth();
- final int previewWidth = previewTextView.getMeasuredWidth();
+ final int previewWidth = previewText.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();
+ previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
+ - previewText.getPaddingRight();
+ previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
+ - previewText.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();
+ previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom();
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
@@ -630,132 +817,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
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();
+ previewText, previewX, previewY, previewWidth, previewHeight);
+ previewText.setVisibility(VISIBLE);
}
- // 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;
- }
-
- // 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);
- }
- // 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);
- return;
- }
- final Object tag = previewTextView.getTag();
- if (tag instanceof KeyPreviewAnimations) {
- final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag;
- animation.startZoomOut();
- }
+ public void dismissKeyPreview(final PointerTracker tracker) {
+ mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker);
}
public void setSlidingKeyInputPreviewEnabled(final boolean enabled) {
@@ -779,8 +847,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
mGestureTrailsPreview.setPreviewEnabled(isGestureTrailEnabled);
}
- // Implements {@link DrawingHandler.Callbacks} method.
- @Override
public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
locatePreviewPlacerView();
mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);
@@ -856,13 +922,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
return moreKeysKeyboardView;
}
- // Implements {@link TimerHandler.Callbacks} method.
/**
* Called when a key is long pressed.
* @param tracker the pointer tracker which pressed the parent key
*/
- @Override
- public void onLongPress(final PointerTracker tracker) {
+ private void onLongPress(final PointerTracker tracker) {
if (isShowingMoreKeysPanel()) {
return;
}
@@ -918,15 +982,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final int pointY = key.getY() + mKeyPreviewDrawParams.mPreviewVisibleOffset;
moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
tracker.onShowMoreKeysPanel(moreKeysPanel);
- // TODO: Implement zoom in animation of more keys panel.
- dismissKeyPreviewWithoutDelay(key);
}
- public boolean isInDraggingFinger() {
+ public boolean isInSlidingKeyInput() {
if (isShowingMoreKeysPanel()) {
return true;
}
- return PointerTracker.isAnyInDraggingFinger();
+ return PointerTracker.isAnyInSlidingKeyInput();
}
@Override
@@ -987,10 +1049,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
if (mNonDistinctMultitouchHelper != null) {
if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) {
// Key repeating timer will be canceled if 2 or more keys are in action.
- mKeyTimerHandler.cancelKeyRepeatTimers();
+ mKeyTimerHandler.cancelKeyRepeatTimer();
}
// Non distinct multitouch screen support
- mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector);
+ mNonDistinctMultitouchHelper.processMotionEvent(me, this);
return true;
}
return processMotionEvent(me);
@@ -1007,14 +1069,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final int index = me.getActionIndex();
final int id = me.getPointerId(index);
- final PointerTracker tracker = PointerTracker.getPointerTracker(id);
- // When a more keys panel is showing, we should ignore other fingers' single touch events
- // other than the finger that is showing the more keys panel.
- if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel()
- && PointerTracker.getActivePointerTrackerCount() == 1) {
- return true;
- }
- tracker.processMotionEvent(me, mKeyDetector);
+ final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
+ tracker.processMotionEvent(me, this);
return true;
}
@@ -1043,7 +1099,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
@Override
public boolean dispatchHoverEvent(final MotionEvent event) {
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- final PointerTracker tracker = PointerTracker.getPointerTracker(0);
+ final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
}
@@ -1113,30 +1169,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
}
- // Draw key background.
- @Override
- protected void onDrawKeyBackground(final Key key, final Canvas canvas,
- final Drawable background) {
- if (key.getCode() == Constants.CODE_SPACE) {
- super.onDrawKeyBackground(key, canvas, mSpacebarBackground);
- return;
- }
- super.onDrawKeyBackground(key, canvas, background);
- }
-
@Override
protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
final KeyDrawParams params) {
if (key.altCodeWhileTyping() && key.isEnabled()) {
params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
}
- // Don't draw key top letter when key preview is showing.
- if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED
- && mShowingKeyPreviewTextViews.containsKey(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;
- }
final int code = key.getCode();
if (code == Constants.CODE_SPACE) {
drawSpacebar(key, canvas, paint);
@@ -1155,7 +1193,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
paint.setTextScaleX(1.0f);
- final float textWidth = TypefaceUtils.getStringWidth(text, paint);
+ final float textWidth = TypefaceUtils.getLabelWidth(text, paint);
if (textWidth < width) {
return true;
}
@@ -1166,7 +1204,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
paint.setTextScaleX(scaleX);
- return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth;
+ return TypefaceUtils.getLabelWidth(text, paint) < maxTextWidth;
}
// Layout language name on spacebar.
@@ -1200,17 +1238,17 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
if (mNeedsToDisplayLanguage) {
paint.setTextAlign(Align.CENTER);
paint.setTypeface(Typeface.DEFAULT);
- paint.setTextSize(mLanguageOnSpacebarTextSize);
+ paint.setTextSize(mSpacebarTextSize);
final InputMethodSubtype subtype = getKeyboard().mId.mSubtype;
final String language = layoutLanguageOnSpacebar(paint, subtype, width);
// Draw language text with shadow
final float descent = paint.descent();
final float textHeight = -paint.ascent() + descent;
final float baseline = height / 2 + textHeight / 2;
- paint.setColor(mLanguageOnSpacebarTextShadowColor);
+ paint.setColor(mSpacebarTextShadowColor);
paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
canvas.drawText(language, width / 2, baseline - descent - 1, paint);
- paint.setColor(mLanguageOnSpacebarTextColor);
+ paint.setColor(mSpacebarTextColor);
paint.setAlpha(mLanguageOnSpacebarAnimAlpha);
canvas.drawText(language, width / 2, baseline - descent, paint);
}
@@ -1222,12 +1260,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
int x = (width - iconWidth) / 2;
int y = height - iconHeight;
drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
- } else if (mSpacebarIcon != null) {
- final int iconWidth = mSpacebarIcon.getIntrinsicWidth();
- final int iconHeight = mSpacebarIcon.getIntrinsicHeight();
+ } else if (mSpaceIcon != null) {
+ final int iconWidth = mSpaceIcon.getIntrinsicWidth();
+ final int iconHeight = mSpaceIcon.getIntrinsicHeight();
int x = (width - iconWidth) / 2;
int y = height - iconHeight;
- drawIcon(canvas, mSpacebarIcon, x, y, iconWidth, iconHeight);
+ drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight);
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index 81b8f0428..6b76e2461 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -28,7 +28,7 @@ public final class MoreKeysDetector extends KeyDetector {
}
@Override
- public boolean alwaysAllowsKeySelectionByDraggingFinger() {
+ public boolean alwaysAllowsSlidingInput() {
return true;
}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index 670524380..8256d4623 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -223,7 +223,7 @@ public final class MoreKeysKeyboard extends Keyboard {
}
public int getDefaultKeyCoordX() {
- return mLeftKeys * mColumnWidth + mLeftPadding;
+ return mLeftKeys * mColumnWidth;
}
public int getX(final int n, final int row) {
@@ -298,7 +298,7 @@ public final class MoreKeysKeyboard extends Keyboard {
height = keyPreviewDrawParams.mPreviewVisibleHeight + mParams.mVerticalGap;
} else {
final float padding = context.getResources().getDimension(
- R.dimen.config_more_keys_keyboard_key_horizontal_padding)
+ R.dimen.more_keys_keyboard_key_horizontal_padding)
+ (parentKey.hasLabelsInMoreKeys()
? mParams.mDefaultKeyWidth * LABEL_PADDING_RATIO : 0.0f);
width = getMaxKeyWidth(parentKey, mParams.mDefaultKeyWidth, padding,
@@ -327,7 +327,7 @@ public final class MoreKeysKeyboard extends Keyboard {
// If the label is single letter, minKeyWidth is enough to hold the label.
if (label != null && StringUtils.codePointCount(label) > 1) {
maxWidth = Math.max(maxWidth,
- (int)(TypefaceUtils.getStringWidth(label, paint) + padding));
+ (int)(TypefaceUtils.getLabelWidth(label, paint) + padding));
}
}
return maxWidth;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index a7c468538..973128d36 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -52,7 +52,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
final Resources res = context.getResources();
mKeyDetector = new MoreKeysDetector(
- res.getDimension(R.dimen.config_more_keys_keyboard_slide_allowance));
+ res.getDimension(R.dimen.more_keys_keyboard_slide_allowance));
}
@Override
@@ -81,13 +81,11 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
mListener = listener;
final View container = getContainerView();
// The coordinates of panel's left-top corner in parentView's coordinate system.
- // We need to consider background drawable paddings.
- final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft();
- final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom()
- + getPaddingBottom();
+ final int x = pointX - getDefaultCoordX() - container.getPaddingLeft();
+ final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom();
parentView.getLocationInWindow(mCoordinates);
- // Ensure the horizontal position of the panel does not extend past the parentView edges.
+ // Ensure the horizontal position of the panel does not extend past the screen edges.
final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth();
final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates);
final int panelY = y + CoordinateUtils.y(mCoordinates);
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index f9e78bfd9..52f190e77 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -55,10 +55,37 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private static boolean sGestureHandlingEnabledByInputField = false;
private static boolean sGestureHandlingEnabledByUser = false;
+ public interface KeyEventHandler {
+ /**
+ * Get KeyDetector object that is used for this PointerTracker.
+ * @return the KeyDetector object that is used for this PointerTracker
+ */
+ public KeyDetector getKeyDetector();
+
+ /**
+ * Get KeyboardActionListener object that is used to register key code and so on.
+ * @return the KeyboardActionListner for this PointerTracke
+ */
+ public KeyboardActionListener getKeyboardActionListener();
+
+ /**
+ * Get DrawingProxy object that is used for this PointerTracker.
+ * @return the DrawingProxy object that is used for this PointerTracker
+ */
+ public DrawingProxy getDrawingProxy();
+
+ /**
+ * Get TimerProxy object that handles key repeat and long press timer event for this
+ * PointerTracker.
+ * @return the TimerProxy object that handles key repeat and long press timer event.
+ */
+ public TimerProxy getTimerProxy();
+ }
+
public interface DrawingProxy {
public void invalidateKey(Key key);
- public void showKeyPreview(Key key);
- public void dismissKeyPreview(Key key);
+ public void showKeyPreview(PointerTracker tracker);
+ public void dismissKeyPreview(PointerTracker tracker);
public void showSlidingKeyInputPreview(PointerTracker tracker);
public void dismissSlidingKeyInputPreview();
public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText);
@@ -67,13 +94,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public interface TimerProxy {
public void startTypingStateTimer(Key typedKey);
public boolean isTypingState();
- public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay);
- public void startLongPressTimerOf(PointerTracker tracker, int delay);
- public void cancelLongPressTimerOf(PointerTracker tracker);
- public void cancelKeyTimersOf(PointerTracker tracker);
+ public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay);
+ public void startLongPressTimer(PointerTracker tracker, int delay);
+ public void cancelLongPressTimer();
public void startDoubleTapShiftKeyTimer();
public void cancelDoubleTapShiftKeyTimer();
public boolean isInDoubleTapShiftKeyTimeout();
+ public void cancelKeyTimers();
public void startUpdateBatchInputTimer(PointerTracker tracker);
public void cancelUpdateBatchInputTimer(PointerTracker tracker);
public void cancelAllUpdateBatchInputTimers();
@@ -84,13 +111,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
@Override
public boolean isTypingState() { return false; }
@Override
- public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay) {}
- @Override
- public void startLongPressTimerOf(PointerTracker tracker, int delay) {}
+ public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay) {}
@Override
- public void cancelLongPressTimerOf(PointerTracker tracker) {}
+ public void startLongPressTimer(PointerTracker tracker, int delay) {}
@Override
- public void cancelKeyTimersOf(PointerTracker tracker) {}
+ public void cancelLongPressTimer() {}
@Override
public void startDoubleTapShiftKeyTimer() {}
@Override
@@ -98,6 +123,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
@Override
public boolean isInDoubleTapShiftKeyTimeout() { return false; }
@Override
+ public void cancelKeyTimers() {}
+ @Override
public void startUpdateBatchInputTimer(PointerTracker tracker) {}
@Override
public void cancelUpdateBatchInputTimer(PointerTracker tracker) {}
@@ -107,7 +134,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
static final class PointerTrackerParams {
- public final boolean mKeySelectionByDraggingFinger;
+ public final boolean mSlidingKeyInputEnabled;
public final int mTouchNoiseThresholdTime;
public final int mTouchNoiseThresholdDistance;
public final int mSuppressKeyPreviewAfterBatchInputDuration;
@@ -115,9 +142,21 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public final int mKeyRepeatInterval;
public final int mLongPressShiftLockTimeout;
+ public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
+
+ private PointerTrackerParams() {
+ mSlidingKeyInputEnabled = false;
+ mTouchNoiseThresholdTime = 0;
+ mTouchNoiseThresholdDistance = 0;
+ mSuppressKeyPreviewAfterBatchInputDuration = 0;
+ mKeyRepeatStartTimeout = 0;
+ mKeyRepeatInterval = 0;
+ mLongPressShiftLockTimeout = 0;
+ }
+
public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
- mKeySelectionByDraggingFinger = mainKeyboardViewAttr.getBoolean(
- R.styleable.MainKeyboardView_keySelectionByDraggingFinger, false);
+ mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
+ R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
@@ -150,14 +189,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public final int mPointerId;
- private static DrawingProxy sDrawingProxy;
- private static TimerProxy sTimerProxy;
- private static KeyDetector sDefaultKeyDetector;
+ private DrawingProxy mDrawingProxy;
+ private TimerProxy mTimerProxy;
private KeyDetector mKeyDetector;
- private static KeyboardActionListener sListener = KeyboardActionListener.EMPTY_LISTENER;
+ private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER;
private Keyboard mKeyboard;
- private int mPhantomSuddenMoveThreshold;
+ private int mPhantonSuddenMoveThreshold;
private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
private boolean mIsDetectingGesture = false; // per PointerTracker.
@@ -303,16 +341,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private MoreKeysPanel mMoreKeysPanel;
private static final int MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT = 3;
- // true if this pointer is in the dragging finger mode.
- boolean mIsInDraggingFinger;
- // true if this pointer is sliding from a modifier key and in the sliding key input mode,
- // so that further modifier keys should be ignored.
+ // true if this pointer is in a sliding key input.
boolean mIsInSlidingKeyInput;
+ // true if this pointer is in a sliding key input from a modifier key,
+ // so that further modifier keys should be ignored.
+ boolean mIsInSlidingKeyInputFromModifier;
// if not a NOT_A_CODE, the key of this code is repeating
private int mCurrentRepeatingKeyCode = Constants.NOT_A_CODE;
- // true if dragging finger is allowed.
- private boolean mIsAllowedDraggingFinger;
+ // true if a sliding key input is allowed.
+ private boolean mIsAllowedSlidingKeyInput;
private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
@@ -339,26 +377,22 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return needsTheHack;
}
- // TODO: Add PointerTrackerFactory singleton and move some class static methods into it.
- public static void init(final TypedArray mainKeyboardViewAttr, final TimerProxy timerProxy,
- final DrawingProxy drawingProxy, final KeyDetector defaultKeyDetector) {
- sParams = new PointerTrackerParams(mainKeyboardViewAttr);
- sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
- sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
- sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
-
- final Resources res = mainKeyboardViewAttr.getResources();
+ public static void init(final Resources res) {
sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
ResourceUtils.getDeviceOverrideValue(
res, R.array.phantom_sudden_move_event_device_list));
sNeedsProximateBogusDownMoveUpEventHack = needsProximateBogusDownMoveUpEventHack(res);
+ sParams = PointerTrackerParams.DEFAULT;
sGestureStrokeParams = GestureStrokeParams.DEFAULT;
sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT;
sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
+ }
- sTimerProxy = timerProxy;
- sDrawingProxy = drawingProxy;
- sDefaultKeyDetector = defaultKeyDetector;
+ public static void setParameters(final TypedArray mainKeyboardViewAttr) {
+ sParams = new PointerTrackerParams(mainKeyboardViewAttr);
+ sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
+ sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr);
+ sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
}
private static void updateGestureHandlingMode() {
@@ -379,20 +413,20 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
updateGestureHandlingMode();
}
- public static PointerTracker getPointerTracker(final int id) {
+ public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
final ArrayList<PointerTracker> trackers = sTrackers;
// Create pointer trackers until we can get 'id+1'-th tracker, if needed.
for (int i = trackers.size(); i <= id; i++) {
- final PointerTracker tracker = new PointerTracker(i);
+ final PointerTracker tracker = new PointerTracker(i, handler);
trackers.add(tracker);
}
return trackers.get(id);
}
- public static boolean isAnyInDraggingFinger() {
- return sPointerTrackerQueue.isAnyInDraggingFinger();
+ public static boolean isAnyInSlidingKeyInput() {
+ return sPointerTrackerQueue.isAnyInSlidingKeyInput();
}
public static void cancelAllPointerTrackers() {
@@ -400,7 +434,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
public static void setKeyboardActionListener(final KeyboardActionListener listener) {
- sListener = listener;
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mListener = listener;
+ }
}
public static void setKeyDetector(final KeyDetector keyDetector) {
@@ -408,6 +446,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
for (int i = 0; i < trackersSize; ++i) {
final PointerTracker tracker = sTrackers.get(i);
tracker.setKeyDetectorInner(keyDetector);
+ // Mark that keyboard layout has been changed.
+ tracker.mKeyboardLayoutHasBeenChanged = true;
}
final Keyboard keyboard = keyDetector.getKeyboard();
sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
@@ -418,7 +458,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final int trackersSize = sTrackers.size();
for (int i = 0; i < trackersSize; ++i) {
final PointerTracker tracker = sTrackers.get(i);
- tracker.setReleasedKeyGraphics(tracker.getKey());
+ tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
}
}
@@ -426,15 +466,28 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final int trackersSize = sTrackers.size();
for (int i = 0; i < trackersSize; ++i) {
final PointerTracker tracker = sTrackers.get(i);
- tracker.dismissMoreKeysPanel();
+ if (tracker.isShowingMoreKeysPanel()) {
+ tracker.mMoreKeysPanel.dismissMoreKeysPanel();
+ tracker.mMoreKeysPanel = null;
+ }
}
}
- private PointerTracker(final int id) {
+ private PointerTracker(final int id, final KeyEventHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException();
+ }
mPointerId = id;
mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
id, sGestureStrokeParams, sGesturePreviewParams);
- setKeyDetectorInner(sDefaultKeyDetector);
+ setKeyEventHandler(handler);
+ }
+
+ private void setKeyEventHandler(final KeyEventHandler handler) {
+ setKeyDetectorInner(handler.getKeyDetector());
+ mListener = handler.getKeyboardActionListener();
+ mDrawingProxy = handler.getDrawingProxy();
+ mTimerProxy = handler.getTimerProxy();
}
// Returns true if keyboard has been changed by this callback.
@@ -447,7 +500,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
return false;
}
- final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
+ final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onPress : %s%s%s%s", mPointerId,
KeyDetector.printableCode(key),
@@ -459,10 +512,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return false;
}
if (key.isEnabled()) {
- sListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1);
+ mListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1);
final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
mKeyboardLayoutHasBeenChanged = false;
- sTimerProxy.startTypingStateTimer(key);
+ mTimerProxy.startTypingStateTimer(key);
return keyboardLayoutHasBeenChanged;
}
return false;
@@ -472,8 +525,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// primaryCode is different from {@link Key#mCode}.
private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
final int y, final long eventTime) {
- final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
- final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState();
+ final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
+ final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
final int code = altersCode ? key.getAltCode() : primaryCode;
if (DEBUG_LISTENER) {
final String output = code == Constants.CODE_OUTPUT_TEXT
@@ -493,9 +546,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (key.isEnabled() || altersCode) {
sTimeRecorder.onCodeInput(code, eventTime);
if (code == Constants.CODE_OUTPUT_TEXT) {
- sListener.onTextInput(key.getOutputText());
+ mListener.onTextInput(key.getOutputText());
} else if (code != Constants.CODE_UNSPECIFIED) {
- sListener.onCodeInput(code, x, y);
+ mListener.onCodeInput(code, x, y);
}
}
}
@@ -508,7 +561,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) {
return;
}
- final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier();
+ final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier();
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId,
Constants.printableCode(primaryCode),
@@ -523,7 +576,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return;
}
if (key.isEnabled()) {
- sListener.onReleaseKey(primaryCode, withSliding);
+ mListener.onReleaseKey(primaryCode, withSliding);
}
}
@@ -531,7 +584,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId));
}
- sListener.onFinishSlidingInput();
+ mListener.onFinishSlidingInput();
}
private void callListenerOnCancelInput() {
@@ -541,7 +594,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.pointerTracker_callListenerOnCancelInput();
}
- sListener.onCancelInput();
+ mListener.onCancelInput();
}
private void setKeyDetectorInner(final KeyDetector keyDetector) {
@@ -551,25 +604,23 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
- // Mark that keyboard layout has been changed.
- mKeyboardLayoutHasBeenChanged = true;
final int keyWidth = mKeyboard.mMostCommonKeyWidth;
final int keyHeight = mKeyboard.mMostCommonKeyHeight;
mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
if (newKey != mCurrentKey) {
- if (sDrawingProxy != null) {
+ if (mDrawingProxy != null) {
setReleasedKeyGraphics(mCurrentKey);
}
// Keep {@link #mCurrentKey} that comes from previous keyboard.
}
- mPhantomSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
+ mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
}
@Override
- public boolean isInDraggingFinger() {
- return mIsInDraggingFinger;
+ public boolean isInSlidingKeyInput() {
+ return mIsInSlidingKeyInput;
}
public Key getKey() {
@@ -586,7 +637,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
private void setReleasedKeyGraphics(final Key key) {
- sDrawingProxy.dismissKeyPreview(key);
+ mDrawingProxy.dismissKeyPreview(this);
if (key == null) {
return;
}
@@ -627,14 +678,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
// Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
- final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState();
+ final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
final boolean needsToUpdateGraphics = key.isEnabled() || altersCode;
if (!needsToUpdateGraphics) {
return;
}
if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
- sDrawingProxy.showKeyPreview(key);
+ mDrawingProxy.showKeyPreview(this);
}
updatePressKeyGraphics(key);
@@ -646,7 +697,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
- if (key.altCodeWhileTyping() && sTimerProxy.isTypingState()) {
+ if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
final int altCode = key.getAltCode();
final Key altKey = mKeyboard.getKey(altCode);
if (altKey != null) {
@@ -660,14 +711,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
- private static void updateReleaseKeyGraphics(final Key key) {
+ private void updateReleaseKeyGraphics(final Key key) {
key.onReleased();
- sDrawingProxy.invalidateKey(key);
+ mDrawingProxy.invalidateKey(key);
}
- private static void updatePressKeyGraphics(final Key key) {
+ private void updatePressKeyGraphics(final Key key) {
key.onPressed();
- sDrawingProxy.invalidateKey(key);
+ mDrawingProxy.invalidateKey(key);
}
public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
@@ -715,7 +766,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return newKey;
}
- /* package */ static int getActivePointerTrackerCount() {
+ private static int getActivePointerTrackerCount() {
return sPointerTrackerQueue.size();
}
@@ -738,12 +789,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
sAggregratedPointers.reset();
sLastRecognitionPointSize = 0;
sLastRecognitionTime = 0;
- sListener.onStartBatchInput();
+ mListener.onStartBatchInput();
dismissAllMoreKeysPanels();
}
- sTimerProxy.cancelLongPressTimerOf(this);
+ mTimerProxy.cancelLongPressTimer();
// A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
- sDrawingProxy.showGestureTrail(
+ mDrawingProxy.showGestureTrail(
this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
}
@@ -761,7 +812,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return;
}
// A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
- sDrawingProxy.showGestureTrail(
+ mDrawingProxy.showGestureTrail(
this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
}
@@ -776,8 +827,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
size));
}
- sTimerProxy.startUpdateBatchInputTimer(this);
- sListener.onUpdateBatchInput(sAggregratedPointers);
+ mTimerProxy.startUpdateBatchInputTimer(this);
+ mListener.onUpdateBatchInput(sAggregratedPointers);
// The listener may change the size of the pointers (when auto-committing
// for example), so we need to get the size from the pointers again.
sLastRecognitionPointSize = sAggregratedPointers.getPointerSize();
@@ -792,13 +843,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (getActivePointerTrackerCount() == 1) {
sInGesture = false;
sTimeRecorder.onEndBatchInput(eventTime);
- sTimerProxy.cancelAllUpdateBatchInputTimers();
+ mTimerProxy.cancelAllUpdateBatchInputTimers();
if (!mIsTrackingForActionDisabled) {
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
mPointerId, sAggregratedPointers.getPointerSize()));
}
- sListener.onEndBatchInput(sAggregratedPointers);
+ mListener.onEndBatchInput(sAggregratedPointers);
}
}
}
@@ -806,7 +857,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return;
}
// A gesture floating preview text will be shown at the oldest pointer/finger on the screen.
- sDrawingProxy.showGestureTrail(
+ mDrawingProxy.showGestureTrail(
this, isOldestTrackerInQueue() /* showsFloatingPreviewText */);
}
@@ -820,26 +871,19 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
}
- sListener.onCancelBatchInput();
+ mListener.onCancelBatchInput();
}
- public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
+ public void processMotionEvent(final MotionEvent me, final KeyEventHandler handler) {
final int action = me.getActionMasked();
final long eventTime = me.getEventTime();
if (action == MotionEvent.ACTION_MOVE) {
- // When this pointer is the only active pointer and is showing a more keys panel,
- // we should ignore other pointers' motion event.
- final boolean shouldIgnoreOtherPointers =
- isShowingMoreKeysPanel() && getActivePointerTrackerCount() == 1;
final int pointerCount = me.getPointerCount();
for (int index = 0; index < pointerCount; index++) {
final int id = me.getPointerId(index);
- if (shouldIgnoreOtherPointers && id != mPointerId) {
- continue;
- }
+ final PointerTracker tracker = getPointerTracker(id, handler);
final int x = (int)me.getX(index);
final int y = (int)me.getY(index);
- final PointerTracker tracker = getPointerTracker(id);
tracker.onMoveEvent(x, y, eventTime, me);
}
return;
@@ -850,7 +894,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
- onDownEvent(x, y, eventTime, keyDetector);
+ onDownEvent(x, y, eventTime, handler);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
@@ -863,11 +907,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
private void onDownEvent(final int x, final int y, final long eventTime,
- final KeyDetector keyDetector) {
+ final KeyEventHandler handler) {
if (DEBUG_EVENT) {
printTouchEvent("onDownEvent:", x, y, eventTime);
}
- setKeyDetectorInner(keyDetector);
+ setKeyEventHandler(handler);
// Naive up-to-down noise filter.
final long deltaT = eventTime - mUpTime;
if (deltaT < sParams.mTouchNoiseThresholdTime) {
@@ -910,29 +954,20 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
- /* package */ boolean isShowingMoreKeysPanel() {
+ private boolean isShowingMoreKeysPanel() {
return (mMoreKeysPanel != null);
}
- private void dismissMoreKeysPanel() {
- if (isShowingMoreKeysPanel()) {
- mMoreKeysPanel.dismissMoreKeysPanel();
- mMoreKeysPanel = null;
- }
- }
-
private void onDownEventInternal(final int x, final int y, final long eventTime) {
Key key = onDownKey(x, y, eventTime);
- // Key selection by dragging finger is allowed when 1) key selection by dragging finger is
- // enabled by configuration, 2) this pointer starts dragging from modifier key, or 3) this
- // pointer's KeyDetector always allows key selection by dragging finger, such as
- // {@link MoreKeysKeyboard}.
- mIsAllowedDraggingFinger = sParams.mKeySelectionByDraggingFinger
+ // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
+ // from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
+ mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
|| (key != null && key.isModifier())
- || mKeyDetector.alwaysAllowsKeySelectionByDraggingFinger();
+ || mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mIsTrackingForActionDisabled = false;
- resetKeySelectionByDraggingFinger();
+ resetSlidingKeyInput();
if (key != null) {
// This onPress call may have changed keyboard layout. Those cases are detected at
// {@link #setKeyboard}. In those cases, we should update key according to the new
@@ -947,17 +982,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
- private void startKeySelectionByDraggingFinger(final Key key) {
- if (!mIsInDraggingFinger) {
- mIsInSlidingKeyInput = key.isModifier();
+ private void startSlidingKeyInput(final Key key) {
+ if (!mIsInSlidingKeyInput) {
+ mIsInSlidingKeyInputFromModifier = key.isModifier();
}
- mIsInDraggingFinger = true;
+ mIsInSlidingKeyInput = true;
}
- private void resetKeySelectionByDraggingFinger() {
- mIsInDraggingFinger = false;
+ private void resetSlidingKeyInput() {
mIsInSlidingKeyInput = false;
- sDrawingProxy.dismissSlidingKeyInputPreview();
+ mIsInSlidingKeyInputFromModifier = false;
+ mDrawingProxy.dismissSlidingKeyInputPreview();
}
private void onGestureMoveEvent(final int x, final int y, final long eventTime,
@@ -968,7 +1003,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
x, y, gestureTime, isMajorEvent);
if (mGestureStrokeWithPreviewPoints.getLength() > beforeLength) {
- sTimerProxy.startUpdateBatchInputTimer(this);
+ mTimerProxy.startUpdateBatchInputTimer(this);
}
// If the move event goes out from valid batch input area, cancel batch input.
if (!onValidArea) {
@@ -1013,15 +1048,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final int translatedY = mMoreKeysPanel.translateY(y);
mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime);
onMoveKey(x, y);
- if (mIsInSlidingKeyInput) {
- sDrawingProxy.showSlidingKeyInputPreview(this);
+ if (mIsInSlidingKeyInputFromModifier) {
+ mDrawingProxy.showSlidingKeyInputPreview(this);
}
return;
}
onMoveEventInternal(x, y, eventTime);
}
- private void processDraggingFingerInToNewKey(final Key newKey, final int x, final int y,
+ private void processSlidingKeyInput(final Key newKey, final int x, final int y,
final long eventTime) {
// This onPress call may have changed keyboard layout. Those cases are detected
// at {@link #setKeyboard}. In those cases, we should update key according
@@ -1075,29 +1110,29 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
onDownEventInternal(x, y, eventTime);
}
- private void processDraggingFingerOutFromOldKey(final Key oldKey) {
+ private void processSildeOutFromOldKey(final Key oldKey) {
setReleasedKeyGraphics(oldKey);
callListenerOnRelease(oldKey, oldKey.getCode(), true /* withSliding */);
- startKeySelectionByDraggingFinger(oldKey);
- sTimerProxy.cancelKeyTimersOf(this);
+ startSlidingKeyInput(oldKey);
+ mTimerProxy.cancelKeyTimers();
}
- private void dragFingerFromOldKeyToNewKey(final Key key, final int x, final int y,
+ private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
final long eventTime, final Key oldKey, final int lastX, final int lastY) {
// The pointer has been slid in to the new key from the previous key, we must call
// onRelease() first to notify that the previous key has been released, then call
// onPress() to notify that the new key is being pressed.
- processDraggingFingerOutFromOldKey(oldKey);
+ processSildeOutFromOldKey(oldKey);
startRepeatKey(key);
- if (mIsAllowedDraggingFinger) {
- processDraggingFingerInToNewKey(key, x, y, eventTime);
+ if (mIsAllowedSlidingKeyInput) {
+ processSlidingKeyInput(key, x, y, eventTime);
}
// HACK: On some devices, quick successive touches may be reported as a sudden move by
// touch panel firmware. This hack detects such cases and translates the move event to
// successive up and down events.
// TODO: Should find a way to balance gesture detection and this hack.
else if (sNeedsPhantomSuddenMoveEventHack
- && getDistance(x, y, lastX, lastY) >= mPhantomSuddenMoveThreshold) {
+ && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
}
// HACK: On some devices, quick successive proximate touches may be reported as a bogus
@@ -1128,11 +1163,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
}
- private void dragFingerOutFromOldKey(final Key oldKey, final int x, final int y) {
+ private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
// The pointer has been slid out from the previous key, we must call onRelease() to
// notify that the previous key has been released.
- processDraggingFingerOutFromOldKey(oldKey);
- if (mIsAllowedDraggingFinger) {
+ processSildeOutFromOldKey(oldKey);
+ if (mIsAllowedSlidingKeyInput) {
onMoveToNewKey(null, x, y);
} else {
if (!mIsDetectingGesture) {
@@ -1159,19 +1194,19 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (newKey != null) {
if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
- dragFingerFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
+ slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
} else if (oldKey == null) {
// The pointer has been slid in to the new key, but the finger was not on any keys.
// In this case, we must call onPress() to notify that the new key is being pressed.
- processDraggingFingerInToNewKey(newKey, x, y, eventTime);
+ processSlidingKeyInput(newKey, x, y, eventTime);
}
} else { // newKey == null
if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
- dragFingerOutFromOldKey(oldKey, x, y);
+ slideOutFromOldKey(oldKey, x, y);
}
}
- if (mIsInSlidingKeyInput) {
- sDrawingProxy.showSlidingKeyInputPreview(this);
+ if (mIsInSlidingKeyInputFromModifier) {
+ mDrawingProxy.showSlidingKeyInputPreview(this);
}
}
@@ -1180,7 +1215,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
printTouchEvent("onUpEvent :", x, y, eventTime);
}
- sTimerProxy.cancelUpdateBatchInputTimer(this);
+ mTimerProxy.cancelUpdateBatchInputTimer(this);
if (!sInGesture) {
if (mCurrentKey != null && mCurrentKey.isModifier()) {
// Before processing an up event of modifier key, all pointers already being
@@ -1202,15 +1237,18 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (DEBUG_EVENT) {
printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
}
+ if (isShowingMoreKeysPanel()) {
+ return;
+ }
onUpEventInternal(mLastX, mLastY, eventTime);
cancelTrackingForAction();
}
private void onUpEventInternal(final int x, final int y, final long eventTime) {
- sTimerProxy.cancelKeyTimersOf(this);
- final boolean isInDraggingFinger = mIsInDraggingFinger;
+ mTimerProxy.cancelKeyTimers();
final boolean isInSlidingKeyInput = mIsInSlidingKeyInput;
- resetKeySelectionByDraggingFinger();
+ final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier;
+ resetSlidingKeyInput();
mIsDetectingGesture = false;
final Key currentKey = mCurrentKey;
mCurrentKey = null;
@@ -1242,11 +1280,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return;
}
if (currentKey != null && currentKey.isRepeatable()
- && (currentKey.getCode() == currentRepeatingKeyCode) && !isInDraggingFinger) {
+ && (currentKey.getCode() == currentRepeatingKeyCode) && !isInSlidingKeyInput) {
return;
}
detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
- if (isInSlidingKeyInput) {
+ if (isInSlidingKeyInputFromModifier) {
callListenerOnFinishSlidingInput();
}
}
@@ -1268,7 +1306,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
public void onLongPressed() {
- resetKeySelectionByDraggingFinger();
+ resetSlidingKeyInput();
cancelTrackingForAction();
setReleasedKeyGraphics(mCurrentKey);
sPointerTrackerQueue.remove(this);
@@ -1286,9 +1324,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
private void onCancelEventInternal() {
- sTimerProxy.cancelKeyTimersOf(this);
+ mTimerProxy.cancelKeyTimers();
setReleasedKeyGraphics(mCurrentKey);
- resetKeySelectionByDraggingFinger();
+ resetSlidingKeyInput();
if (isShowingMoreKeysPanel()) {
mMoreKeysPanel.dismissMoreKeysPanel();
mMoreKeysPanel = null;
@@ -1309,7 +1347,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
// Here curKey points to the different key from newKey.
final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
- mIsInSlidingKeyInput);
+ mIsInSlidingKeyInputFromModifier);
final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
if (DEBUG_MODE) {
@@ -1320,7 +1358,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
return true;
}
- if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedDraggingFinger
+ if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
&& sTimeRecorder.isInFastTyping(eventTime)
&& mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
if (DEBUG_MODE) {
@@ -1342,26 +1380,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (key == null) return;
if (!key.isLongPressEnabled()) return;
// Caveat: Please note that isLongPressEnabled() can be true even if the current key
- // doesn't have its more keys. (e.g. spacebar, globe key) If we are in the dragging finger
- // mode, we will disable long press timer of such key.
+ // doesn't have its more keys. (e.g. spacebar, globe key)
// We always need to start the long press timer if the key has its more keys regardless of
- // whether or not we are in the dragging finger mode.
- if (mIsInDraggingFinger && key.getMoreKeys() == null) return;
-
- final int delay = getLongPressTimeout(key.getCode());
- sTimerProxy.startLongPressTimerOf(this, delay);
- }
-
- private int getLongPressTimeout(final int code) {
- if (code == Constants.CODE_SHIFT) {
- return sParams.mLongPressShiftLockTimeout;
- }
- final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
- if (mIsInSlidingKeyInput) {
- // We use longer timeout for sliding finger input started from the modifier key.
- return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
+ // whether or not we are in the sliding input mode.
+ if (mIsInSlidingKeyInput && key.getMoreKeys() == null) return;
+ final int delay;
+ switch (key.getCode()) {
+ case Constants.CODE_SHIFT:
+ delay = sParams.mLongPressShiftLockTimeout;
+ break;
+ default:
+ final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout;
+ if (mIsInSlidingKeyInputFromModifier) {
+ // We use longer timeout for sliding finger input started from the modifier key.
+ delay = longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT;
+ } else {
+ delay = longpressTimeout;
+ }
+ break;
}
- return longpressTimeout;
+ mTimerProxy.startLongPressTimer(this, delay);
}
private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
@@ -1379,10 +1417,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (sInGesture) return;
if (key == null) return;
if (!key.isRepeatable()) return;
- // Don't start key repeat when we are in the dragging finger mode.
- if (mIsInDraggingFinger) return;
+ // Don't start key repeat when we are in sliding input mode.
+ if (mIsInSlidingKeyInput) return;
final int startRepeatCount = 1;
- startKeyRepeatTimer(startRepeatCount);
+ mTimerProxy.startKeyRepeatTimer(this, startRepeatCount, sParams.mKeyRepeatStartTimeout);
}
public void onKeyRepeat(final int code, final int repeatCount) {
@@ -1394,17 +1432,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mCurrentRepeatingKeyCode = code;
mIsDetectingGesture = false;
final int nextRepeatCount = repeatCount + 1;
- startKeyRepeatTimer(nextRepeatCount);
+ mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval);
callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
}
- private void startKeyRepeatTimer(final int repeatCount) {
- final int delay =
- (repeatCount == 1) ? sParams.mKeyRepeatStartTimeout : sParams.mKeyRepeatInterval;
- sTimerProxy.startKeyRepeatTimerOf(this, repeatCount, delay);
- }
-
private void printTouchEvent(final String title, final int x, final int y,
final long eventTime) {
final Key key = mKeyDetector.detectHitKey(x, y);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java b/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java
deleted file mode 100644
index f5cc45ca7..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.content.Context;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-
-/**
- * Custom view pager to prevent {@link ViewPager} from crashing while handling multi-touch
- * event.
- */
-public class CustomViewPager extends ViewPager {
- private static final String TAG = CustomViewPager.class.getSimpleName();
-
- public CustomViewPager(final Context context, final AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean onInterceptTouchEvent(final MotionEvent event) {
- // This only happens when you multi-touch, take the first finger off and move.
- // Unfortunately this causes {@link ViewPager} to crash, so we will ignore such events.
- if (event.getAction() == MotionEvent.ACTION_MOVE && event.getPointerId(0) != 0) {
- Log.w(TAG, "Ignored multi-touch move event to prevent ViewPager from crashing");
- return false;
- }
-
- return super.onInterceptTouchEvent(event);
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
deleted file mode 100644
index df82becae..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.os.Message;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-
-// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so.
-public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> {
- public interface Callbacks {
- public void dismissKeyPreviewWithoutDelay(Key key);
- public void dismissAllKeyPreviews();
- public void showGestureFloatingPreviewText(SuggestedWords suggestedWords);
- }
-
- private static final int MSG_DISMISS_KEY_PREVIEW = 0;
- private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
-
- public DrawingHandler(final Callbacks ownerInstance) {
- super(ownerInstance);
- }
-
- @Override
- public void handleMessage(final Message msg) {
- final Callbacks callbacks = getOwnerInstance();
- if (callbacks == null) {
- return;
- }
- switch (msg.what) {
- case MSG_DISMISS_KEY_PREVIEW:
- callbacks.dismissKeyPreviewWithoutDelay((Key)msg.obj);
- break;
- case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
- callbacks.showGestureFloatingPreviewText(SuggestedWords.EMPTY);
- break;
- }
- }
-
- public void dismissKeyPreview(final long delay, final Key key) {
- sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay);
- }
-
- private void cancelAllDismissKeyPreviews() {
- removeMessages(MSG_DISMISS_KEY_PREVIEW);
- final Callbacks callbacks = getOwnerInstance();
- if (callbacks == null) {
- return;
- }
- callbacks.dismissAllKeyPreviews();
- }
-
- public void dismissGestureFloatingPreviewText(final long delay) {
- sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay);
- }
-
- public void cancelAllMessages() {
- cancelAllDismissKeyPreviews();
- }
-}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index e2fd39017..3133e54be 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -25,7 +25,7 @@ import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.JsonUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -53,7 +53,7 @@ public class DynamicGridKeyboard extends Keyboard {
private Key[] mCachedGridKeys;
public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
- final int maxKeyCount, final int categoryId) {
+ final int maxKeyCount, final int categoryId, final int categoryPageId) {
super(templateKeyboard);
final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
@@ -124,7 +124,7 @@ public class DynamicGridKeyboard extends Keyboard {
final int keyY0 = getKeyY0(index);
final int keyX1 = getKeyX1(index);
final int keyY1 = getKeyY1(index);
- gridKey.updateCoordinates(keyX0, keyY0, keyX1, keyY1);
+ gridKey.updateCorrdinates(keyX0, keyY0, keyX1, keyY1);
index++;
}
}
@@ -139,48 +139,36 @@ public class DynamicGridKeyboard extends Keyboard {
keys.add(key.getCode());
}
}
- final String jsonStr = JsonUtils.listToJsonStr(keys);
+ final String jsonStr = StringUtils.listToJsonStr(keys);
Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
}
- private static Key getKeyByCode(final Collection<DynamicGridKeyboard> keyboards,
- final int code) {
- for (final DynamicGridKeyboard keyboard : keyboards) {
- final Key key = keyboard.getKey(code);
- if (key != null) {
- return key;
- }
- }
- return null;
- }
-
- private static Key getKeyByOutputText(final Collection<DynamicGridKeyboard> keyboards,
- final String outputText) {
+ private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) {
for (final DynamicGridKeyboard kbd : keyboards) {
- final Key key = kbd.getKeyFromOutputText(outputText);
- if (key != null) {
- return key;
+ if (o instanceof Integer) {
+ final int code = (Integer) o;
+ final Key key = kbd.getKey(code);
+ if (key != null) {
+ return key;
+ }
+ } else if (o instanceof String) {
+ final String outputText = (String) o;
+ final Key key = kbd.getKeyFromOutputText(outputText);
+ if (key != null) {
+ return key;
+ }
+ } else {
+ Log.w(TAG, "Invalid object: " + o);
}
}
return null;
}
- public void loadRecentKeys(final Collection<DynamicGridKeyboard> keyboards) {
+ public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
final String str = Settings.readEmojiRecentKeys(mPrefs);
- final List<Object> keys = JsonUtils.jsonStrToList(str);
+ final List<Object> keys = StringUtils.jsonStrToList(str);
for (final Object o : keys) {
- final Key key;
- if (o instanceof Integer) {
- final int code = (Integer)o;
- key = getKeyByCode(keyboards, code);
- } else if (o instanceof String) {
- final String outputText = (String)o;
- key = getKeyByOutputText(keyboards, outputText);
- } else {
- Log.w(TAG, "Invalid object: " + o);
- continue;
- }
- addKeyLast(key);
+ addKeyLast(getKey(keyboards, o));
}
}
@@ -229,7 +217,7 @@ public class DynamicGridKeyboard extends Keyboard {
super(originalKey);
}
- public void updateCoordinates(final int x0, final int y0, final int x1, final int y1) {
+ public void updateCorrdinates(final int x0, final int y0, final int x1, final int y1) {
mCurrentX = x0;
mCurrentY = y0;
getHitBox().set(x0, y0, x1, y1);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
index 8b413e5e2..19e995548 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java
@@ -31,7 +31,7 @@ import android.view.View;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.internal.GestureTrail.Params;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
+import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
/**
* Draw gesture trail preview graphics during gesture.
@@ -52,23 +52,21 @@ public final class GestureTrailsPreview extends AbstractDrawingPreview {
private final DrawingHandler mDrawingHandler;
private static final class DrawingHandler
- extends LeakGuardHandlerWrapper<GestureTrailsPreview> {
+ extends StaticInnerHandlerWrapper<GestureTrailsPreview> {
private static final int MSG_UPDATE_GESTURE_TRAIL = 0;
private final Params mGestureTrailParams;
- public DrawingHandler(final GestureTrailsPreview ownerInstance,
+ public DrawingHandler(final GestureTrailsPreview outerInstance,
final Params gestureTrailParams) {
- super(ownerInstance);
+ super(outerInstance);
mGestureTrailParams = gestureTrailParams;
}
@Override
public void handleMessage(final Message msg) {
- final GestureTrailsPreview preview = getOwnerInstance();
- if (preview == null) {
- return;
- }
+ final GestureTrailsPreview preview = getOuterInstance();
+ if (preview == null) return;
switch (msg.what) {
case MSG_UPDATE_GESTURE_TRAIL:
preview.getDrawingView().invalidate();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index accfaedcb..22f5b3dd1 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -78,10 +78,10 @@ public final class KeySpecParser {
* or has no key specifications.
*/
public static String[] splitKeySpecs(final String text) {
- if (TextUtils.isEmpty(text)) {
+ final int size = text.length();
+ if (size == 0) {
return null;
}
- final int size = text.length();
// Optimization for one-letter key specification.
if (size == 1) {
return text.charAt(0) == COMMA ? null : new String[] { text };
@@ -380,9 +380,6 @@ public final class KeySpecParser {
public static String resolveTextReference(final String rawText,
final KeyboardTextsSet textsSet) {
- if (TextUtils.isEmpty(rawText)) {
- return null;
- }
int level = 0;
String text = rawText;
StringBuilder sb;
@@ -395,7 +392,7 @@ public final class KeySpecParser {
final int prefixLen = PREFIX_TEXT.length();
final int size = text.length();
if (size < prefixLen) {
- return TextUtils.isEmpty(text) ? null : text;
+ return text;
}
sb = null;
@@ -424,7 +421,7 @@ public final class KeySpecParser {
text = sb.toString();
}
} while (sb != null);
- return TextUtils.isEmpty(text) ? null : text;
+ return text;
}
private static int searchTextNameEnd(final String text, final int start) {
@@ -486,7 +483,7 @@ public final class KeySpecParser {
public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
final Locale locale) {
if (!Constants.isLetterCode(code) || !needsToUpperCase) return code;
- final String text = StringUtils.newSingleCodePointString(code);
+ final String text = new String(new int[] { code } , 0, 1);
final String casedText = KeySpecParser.toUpperCaseOfStringForLocale(
text, needsToUpperCase, locale);
return StringUtils.codePointCount(casedText) == 1
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
index f7e43a6c2..05d855e31 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
@@ -27,7 +27,6 @@ import com.android.inputmethod.latin.utils.XmlParseUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import java.util.Arrays;
import java.util.HashMap;
public final class KeyStylesSet {
@@ -91,8 +90,7 @@ public final class KeyStylesSet {
}
final Object value = mStyleAttributes.get(index);
if (value != null) {
- final String[] array = (String[])value;
- return Arrays.copyOf(array, array.length);
+ return (String[])value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
index c3e0aa685..8bdad364c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -47,8 +47,6 @@ public final class KeyVisualAttributes {
public final int mShiftedLetterHintActivatedColor;
public final int mPreviewTextColor;
- public final float mHintLabelVerticalAdjustment;
-
private static final int[] VISUAL_ATTRIBUTE_IDS = {
R.styleable.Keyboard_Key_keyTypeface,
R.styleable.Keyboard_Key_keyLetterSize,
@@ -67,7 +65,6 @@ public final class KeyVisualAttributes {
R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor,
R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor,
R.styleable.Keyboard_Key_keyPreviewTextColor,
- R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment,
};
private static final SparseIntArray sVisualAttributeIds = new SparseIntArray();
private static final int ATTR_DEFINED = 1;
@@ -130,8 +127,5 @@ public final class KeyVisualAttributes {
mShiftedLetterHintActivatedColor = keyAttr.getColor(
R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0);
mPreviewTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyPreviewTextColor, 0);
-
- mHintLabelVerticalAdjustment = ResourceUtils.getFraction(keyAttr,
- R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment, 0.0f);
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index b31358f3c..c1ae65695 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -459,7 +459,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
final int x = (int)row.getKeyX(null);
final int y = row.getKeyY();
final Key key = new Key(mParams, label, null /* hintLabel */, 0 /* iconId */,
- code, outputText, x, y, (int)keyWidth, row.getRowHeight(),
+ code, outputText, x, y, (int)keyWidth, (int)row.getRowHeight(),
row.getDefaultKeyLabelFlags(), row.getDefaultBackgroundType());
endKey(key);
row.advanceXPos(keyWidth);
@@ -649,9 +649,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
- final boolean supportsSwitchingToShortcutImeMatched = matchBoolean(caseAttr,
- R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme,
- id.mSupportsSwitchingToShortcutIme);
+ final boolean shortcutKeyEnabledMatched = matchBoolean(caseAttr,
+ R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
+ final boolean shortcutKeyOnSymbolsMatched = matchBoolean(caseAttr,
+ R.styleable.Keyboard_Case_shortcutKeyOnSymbols, id.mShortcutKeyOnSymbols);
final boolean hasShortcutKeyMatched = matchBoolean(caseAttr,
R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr,
@@ -670,12 +671,13 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched
&& modeMatched && navigateNextMatched && navigatePreviousMatched
&& passwordInputMatched && clobberSettingsKeyMatched
- && supportsSwitchingToShortcutImeMatched && hasShortcutKeyMatched
- && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched
- && localeCodeMatched && languageCodeMatched && countryCodeMatched;
+ && shortcutKeyEnabledMatched && shortcutKeyOnSymbolsMatched
+ && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
+ && isMultiLineMatched && imeActionMatched && localeCodeMatched
+ && languageCodeMatched && countryCodeMatched;
if (DEBUG) {
- startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
+ startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
textAttr(caseAttr.getString(
R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"),
textAttr(caseAttr.getString(
@@ -692,9 +694,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
"clobberSettingsKey"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput,
"passwordInput"),
- booleanAttr(
- caseAttr, R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme,
- "supportsSwitchingToShortcutIme"),
+ booleanAttr(caseAttr, R.styleable.Keyboard_Case_shortcutKeyEnabled,
+ "shortcutKeyEnabled"),
+ booleanAttr(caseAttr, R.styleable.Keyboard_Case_shortcutKeyOnSymbols,
+ "shortcutKeyOnSymbols"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey,
"hasShortcutKey"),
booleanAttr(caseAttr, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 0ee935f60..336db186e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -48,6 +48,7 @@ public final class KeyboardIconsSet {
"search_key", R.styleable.Keyboard_iconSearchKey,
"tab_key", R.styleable.Keyboard_iconTabKey,
"shortcut_key", R.styleable.Keyboard_iconShortcutKey,
+ "shortcut_for_label", R.styleable.Keyboard_iconShortcutForLabel,
"space_key_for_number_layout", R.styleable.Keyboard_iconSpaceKeyForNumberLayout,
"shift_key_shifted", R.styleable.Keyboard_iconShiftKeyShifted,
"shortcut_key_disabled", R.styleable.Keyboard_iconShortcutKeyDisabled,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 1b722c5ce..c2a01b5e8 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -87,11 +87,11 @@ public final class KeyboardTextsSet {
return (text == null) ? LANGUAGE_DEFAULT[id] : text;
}
- // These texts' name should be aligned with the @string/<name> in
- // values*/strings-action-keys.xml.
private static final String[] RESOURCE_NAMES = {
+ // These texts' name should be aligned with the @string/<name> in values/strings.xml.
// Labels for action.
"label_go_key",
+ // "label_search_key",
"label_send_key",
"label_next_key",
"label_done_key",
@@ -147,118 +147,111 @@ public final class KeyboardTextsSet {
/* 42 */ "keylabel_for_south_slavic_row3_8",
/* 43 */ "more_keys_for_cyrillic_ie",
/* 44 */ "more_keys_for_cyrillic_i",
- /* 45 */ "keylabel_for_swiss_row1_11",
- /* 46 */ "keylabel_for_swiss_row2_10",
- /* 47 */ "keylabel_for_swiss_row2_11",
- /* 48 */ "more_keys_for_swiss_row1_11",
- /* 49 */ "more_keys_for_swiss_row2_10",
- /* 50 */ "more_keys_for_swiss_row2_11",
- /* 51 */ "label_to_alpha_key",
- /* 52 */ "single_quotes",
- /* 53 */ "double_quotes",
- /* 54 */ "single_angle_quotes",
- /* 55 */ "double_angle_quotes",
- /* 56 */ "more_keys_for_currency_dollar",
- /* 57 */ "keylabel_for_currency",
- /* 58 */ "more_keys_for_currency",
- /* 59 */ "more_keys_for_punctuation",
- /* 60 */ "more_keys_for_tablet_punctuation",
- /* 61 */ "more_keys_for_star",
- /* 62 */ "more_keys_for_bullet",
- /* 63 */ "more_keys_for_plus",
- /* 64 */ "more_keys_for_left_parenthesis",
- /* 65 */ "more_keys_for_right_parenthesis",
- /* 66 */ "more_keys_for_less_than",
- /* 67 */ "more_keys_for_greater_than",
- /* 68 */ "more_keys_for_arabic_diacritics",
- /* 69 */ "keylabel_for_symbols_1",
- /* 70 */ "keylabel_for_symbols_2",
- /* 71 */ "keylabel_for_symbols_3",
- /* 72 */ "keylabel_for_symbols_4",
- /* 73 */ "keylabel_for_symbols_5",
- /* 74 */ "keylabel_for_symbols_6",
- /* 75 */ "keylabel_for_symbols_7",
- /* 76 */ "keylabel_for_symbols_8",
- /* 77 */ "keylabel_for_symbols_9",
- /* 78 */ "keylabel_for_symbols_0",
- /* 79 */ "label_to_symbol_key",
- /* 80 */ "label_to_symbol_with_microphone_key",
- /* 81 */ "additional_more_keys_for_symbols_1",
- /* 82 */ "additional_more_keys_for_symbols_2",
- /* 83 */ "additional_more_keys_for_symbols_3",
- /* 84 */ "additional_more_keys_for_symbols_4",
- /* 85 */ "additional_more_keys_for_symbols_5",
- /* 86 */ "additional_more_keys_for_symbols_6",
- /* 87 */ "additional_more_keys_for_symbols_7",
- /* 88 */ "additional_more_keys_for_symbols_8",
- /* 89 */ "additional_more_keys_for_symbols_9",
- /* 90 */ "additional_more_keys_for_symbols_0",
- /* 91 */ "more_keys_for_symbols_1",
- /* 92 */ "more_keys_for_symbols_2",
- /* 93 */ "more_keys_for_symbols_3",
- /* 94 */ "more_keys_for_symbols_4",
- /* 95 */ "more_keys_for_symbols_5",
- /* 96 */ "more_keys_for_symbols_6",
- /* 97 */ "more_keys_for_symbols_7",
- /* 98 */ "more_keys_for_symbols_8",
- /* 99 */ "more_keys_for_symbols_9",
- /* 100 */ "more_keys_for_symbols_0",
- /* 101 */ "keylabel_for_comma",
- /* 102 */ "more_keys_for_comma",
- /* 103 */ "keylabel_for_tablet_comma",
- /* 104 */ "keyhintlabel_for_tablet_comma",
- /* 105 */ "more_keys_for_tablet_comma",
- /* 106 */ "keylabel_for_period",
+ /* 45 */ "label_to_alpha_key",
+ /* 46 */ "single_quotes",
+ /* 47 */ "double_quotes",
+ /* 48 */ "single_angle_quotes",
+ /* 49 */ "double_angle_quotes",
+ /* 50 */ "more_keys_for_currency_dollar",
+ /* 51 */ "keylabel_for_currency",
+ /* 52 */ "more_keys_for_currency",
+ /* 53 */ "more_keys_for_punctuation",
+ /* 54 */ "more_keys_for_star",
+ /* 55 */ "more_keys_for_bullet",
+ /* 56 */ "more_keys_for_plus",
+ /* 57 */ "more_keys_for_left_parenthesis",
+ /* 58 */ "more_keys_for_right_parenthesis",
+ /* 59 */ "more_keys_for_less_than",
+ /* 60 */ "more_keys_for_greater_than",
+ /* 61 */ "more_keys_for_arabic_diacritics",
+ /* 62 */ "keyhintlabel_for_arabic_diacritics",
+ /* 63 */ "keylabel_for_symbols_1",
+ /* 64 */ "keylabel_for_symbols_2",
+ /* 65 */ "keylabel_for_symbols_3",
+ /* 66 */ "keylabel_for_symbols_4",
+ /* 67 */ "keylabel_for_symbols_5",
+ /* 68 */ "keylabel_for_symbols_6",
+ /* 69 */ "keylabel_for_symbols_7",
+ /* 70 */ "keylabel_for_symbols_8",
+ /* 71 */ "keylabel_for_symbols_9",
+ /* 72 */ "keylabel_for_symbols_0",
+ /* 73 */ "label_to_symbol_key",
+ /* 74 */ "label_to_symbol_with_microphone_key",
+ /* 75 */ "additional_more_keys_for_symbols_1",
+ /* 76 */ "additional_more_keys_for_symbols_2",
+ /* 77 */ "additional_more_keys_for_symbols_3",
+ /* 78 */ "additional_more_keys_for_symbols_4",
+ /* 79 */ "additional_more_keys_for_symbols_5",
+ /* 80 */ "additional_more_keys_for_symbols_6",
+ /* 81 */ "additional_more_keys_for_symbols_7",
+ /* 82 */ "additional_more_keys_for_symbols_8",
+ /* 83 */ "additional_more_keys_for_symbols_9",
+ /* 84 */ "additional_more_keys_for_symbols_0",
+ /* 85 */ "more_keys_for_symbols_1",
+ /* 86 */ "more_keys_for_symbols_2",
+ /* 87 */ "more_keys_for_symbols_3",
+ /* 88 */ "more_keys_for_symbols_4",
+ /* 89 */ "more_keys_for_symbols_5",
+ /* 90 */ "more_keys_for_symbols_6",
+ /* 91 */ "more_keys_for_symbols_7",
+ /* 92 */ "more_keys_for_symbols_8",
+ /* 93 */ "more_keys_for_symbols_9",
+ /* 94 */ "more_keys_for_symbols_0",
+ /* 95 */ "keylabel_for_comma",
+ /* 96 */ "more_keys_for_comma",
+ /* 97 */ "keylabel_for_symbols_question",
+ /* 98 */ "keylabel_for_symbols_semicolon",
+ /* 99 */ "keylabel_for_symbols_percent",
+ /* 100 */ "more_keys_for_symbols_exclamation",
+ /* 101 */ "more_keys_for_symbols_question",
+ /* 102 */ "more_keys_for_symbols_semicolon",
+ /* 103 */ "more_keys_for_symbols_percent",
+ /* 104 */ "keylabel_for_tablet_comma",
+ /* 105 */ "keyhintlabel_for_tablet_comma",
+ /* 106 */ "more_keys_for_tablet_comma",
/* 107 */ "keyhintlabel_for_period",
/* 108 */ "more_keys_for_period",
- /* 109 */ "keylabel_for_tablet_period",
- /* 110 */ "keyhintlabel_for_tablet_period",
- /* 111 */ "more_keys_for_tablet_period",
- /* 112 */ "keylabel_for_symbols_question",
- /* 113 */ "keylabel_for_symbols_semicolon",
- /* 114 */ "keylabel_for_symbols_percent",
- /* 115 */ "more_keys_for_exclamation",
- /* 116 */ "more_keys_for_question",
- /* 117 */ "more_keys_for_symbols_semicolon",
- /* 118 */ "more_keys_for_symbols_percent",
- /* 119 */ "more_keys_for_q",
- /* 120 */ "more_keys_for_x",
- /* 121 */ "keylabel_for_q",
- /* 122 */ "keylabel_for_w",
- /* 123 */ "keylabel_for_y",
- /* 124 */ "keylabel_for_x",
- /* 125 */ "keylabel_for_spanish_row2_10",
- /* 126 */ "more_keys_for_am_pm",
- /* 127 */ "settings_as_more_key",
- /* 128 */ "shortcut_as_more_key",
- /* 129 */ "action_next_as_more_key",
- /* 130 */ "action_previous_as_more_key",
- /* 131 */ "label_to_more_symbol_key",
- /* 132 */ "label_to_more_symbol_for_tablet_key",
- /* 133 */ "label_tab_key",
- /* 134 */ "label_to_phone_numeric_key",
- /* 135 */ "label_to_phone_symbols_key",
- /* 136 */ "label_time_am",
- /* 137 */ "label_time_pm",
- /* 138 */ "keylabel_for_popular_domain",
- /* 139 */ "more_keys_for_popular_domain",
- /* 140 */ "more_keys_for_smiley",
- /* 141 */ "single_laqm_raqm",
- /* 142 */ "single_laqm_raqm_rtl",
- /* 143 */ "single_raqm_laqm",
- /* 144 */ "double_laqm_raqm",
- /* 145 */ "double_laqm_raqm_rtl",
- /* 146 */ "double_raqm_laqm",
- /* 147 */ "single_lqm_rqm",
- /* 148 */ "single_9qm_lqm",
- /* 149 */ "single_9qm_rqm",
- /* 150 */ "double_lqm_rqm",
- /* 151 */ "double_9qm_lqm",
- /* 152 */ "double_9qm_rqm",
- /* 153 */ "more_keys_for_single_quote",
- /* 154 */ "more_keys_for_double_quote",
- /* 155 */ "more_keys_for_tablet_double_quote",
- /* 156 */ "emoji_key_as_more_key",
+ /* 109 */ "keylabel_for_apostrophe",
+ /* 110 */ "keyhintlabel_for_apostrophe",
+ /* 111 */ "more_keys_for_apostrophe",
+ /* 112 */ "more_keys_for_q",
+ /* 113 */ "more_keys_for_x",
+ /* 114 */ "keylabel_for_q",
+ /* 115 */ "keylabel_for_w",
+ /* 116 */ "keylabel_for_y",
+ /* 117 */ "keylabel_for_x",
+ /* 118 */ "keylabel_for_spanish_row2_10",
+ /* 119 */ "more_keys_for_am_pm",
+ /* 120 */ "settings_as_more_key",
+ /* 121 */ "shortcut_as_more_key",
+ /* 122 */ "action_next_as_more_key",
+ /* 123 */ "action_previous_as_more_key",
+ /* 124 */ "label_to_more_symbol_key",
+ /* 125 */ "label_to_more_symbol_for_tablet_key",
+ /* 126 */ "label_tab_key",
+ /* 127 */ "label_to_phone_numeric_key",
+ /* 128 */ "label_to_phone_symbols_key",
+ /* 129 */ "label_time_am",
+ /* 130 */ "label_time_pm",
+ /* 131 */ "keylabel_for_popular_domain",
+ /* 132 */ "more_keys_for_popular_domain",
+ /* 133 */ "more_keys_for_smiley",
+ /* 134 */ "single_laqm_raqm",
+ /* 135 */ "single_laqm_raqm_rtl",
+ /* 136 */ "single_raqm_laqm",
+ /* 137 */ "double_laqm_raqm",
+ /* 138 */ "double_laqm_raqm_rtl",
+ /* 139 */ "double_raqm_laqm",
+ /* 140 */ "single_lqm_rqm",
+ /* 141 */ "single_9qm_lqm",
+ /* 142 */ "single_9qm_rqm",
+ /* 143 */ "double_lqm_rqm",
+ /* 144 */ "double_9qm_lqm",
+ /* 145 */ "double_9qm_rqm",
+ /* 146 */ "more_keys_for_single_quote",
+ /* 147 */ "more_keys_for_double_quote",
+ /* 148 */ "more_keys_for_tablet_double_quote",
+ /* 149 */ "emoji_key_as_more_key",
};
private static final String EMPTY = "";
@@ -269,148 +262,145 @@ public final class KeyboardTextsSet {
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~50 */
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+ /* ~44 */
// Label for "switch to alphabetic" key.
- /* 51 */ "ABC",
- /* 52 */ "!text/single_lqm_rqm",
- /* 53 */ "!text/double_lqm_rqm",
- /* 54 */ "!text/single_laqm_raqm",
- /* 55 */ "!text/double_laqm_raqm",
+ /* 45 */ "ABC",
+ /* 46 */ "!text/single_lqm_rqm",
+ /* 47 */ "!text/double_lqm_rqm",
+ /* 48 */ "!text/single_laqm_raqm",
+ /* 49 */ "!text/double_laqm_raqm",
// U+00A2: "¢" CENT SIGN
// U+00A3: "£" POUND SIGN
// U+20AC: "€" EURO SIGN
// U+00A5: "¥" YEN SIGN
// U+20B1: "₱" PESO SIGN
- /* 56 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
- /* 57 */ "$",
- /* 58 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1",
- /* 59 */ "!fixedColumnOrder!8,;,/,(,),#,!,\\,,?,&,\\%,+,\",-,:,',@",
- /* 60 */ "!fixedColumnOrder!7,;,/,(,),#,',\\,,&,\\%,+,\",-,:,@",
+ /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
+ /* 51 */ "$",
+ /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1",
+ /* 53 */ "!fixedColumnOrder!8,;,/,(,),#,!,\\,,?,&,\\%,+,\",-,:,',@",
// U+2020: "†" DAGGER
// U+2021: "‡" DOUBLE DAGGER
// U+2605: "★" BLACK STAR
- /* 61 */ "\u2020,\u2021,\u2605",
+ /* 54 */ "\u2020,\u2021,\u2605",
// U+266A: "♪" EIGHTH NOTE
// U+2665: "♥" BLACK HEART SUIT
// U+2660: "♠" BLACK SPADE SUIT
// U+2666: "♦" BLACK DIAMOND SUIT
// U+2663: "♣" BLACK CLUB SUIT
- /* 62 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
+ /* 55 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
// U+00B1: "±" PLUS-MINUS SIGN
- /* 63 */ "\u00B1",
+ /* 56 */ "\u00B1",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 64 */ "!fixedColumnOrder!3,<,{,[",
- /* 65 */ "!fixedColumnOrder!3,>,},]",
+ /* 57 */ "!fixedColumnOrder!3,<,{,[",
+ /* 58 */ "!fixedColumnOrder!3,>,},]",
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- /* 66 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
- /* 67 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
- /* 68 */ EMPTY,
- /* 69 */ "1",
- /* 70 */ "2",
- /* 71 */ "3",
- /* 72 */ "4",
- /* 73 */ "5",
- /* 74 */ "6",
- /* 75 */ "7",
- /* 76 */ "8",
- /* 77 */ "9",
- /* 78 */ "0",
+ /* 59 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
+ /* 60 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
+ /* 61 */ EMPTY,
+ /* 62 */ EMPTY,
+ /* 63 */ "1",
+ /* 64 */ "2",
+ /* 65 */ "3",
+ /* 66 */ "4",
+ /* 67 */ "5",
+ /* 68 */ "6",
+ /* 69 */ "7",
+ /* 70 */ "8",
+ /* 71 */ "9",
+ /* 72 */ "0",
// Label for "switch to symbols" key.
- /* 79 */ "?123",
+ /* 73 */ "?123",
// Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
// part because it'll be appended by the code.
- /* 80 */ "123",
- /* 81~ */
+ /* 74 */ "123",
+ /* 75~ */
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~90 */
+ /* ~84 */
// U+00B9: "¹" SUPERSCRIPT ONE
// U+00BD: "½" VULGAR FRACTION ONE HALF
// U+2153: "⅓" VULGAR FRACTION ONE THIRD
// U+00BC: "¼" VULGAR FRACTION ONE QUARTER
// U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
- /* 91 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
+ /* 85 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
// U+00B2: "²" SUPERSCRIPT TWO
// U+2154: "⅔" VULGAR FRACTION TWO THIRDS
- /* 92 */ "\u00B2,\u2154",
+ /* 86 */ "\u00B2,\u2154",
// U+00B3: "³" SUPERSCRIPT THREE
// U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
// U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
- /* 93 */ "\u00B3,\u00BE,\u215C",
+ /* 87 */ "\u00B3,\u00BE,\u215C",
// U+2074: "⁴" SUPERSCRIPT FOUR
- /* 94 */ "\u2074",
+ /* 88 */ "\u2074",
// U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
- /* 95 */ "\u215D",
- /* 96 */ EMPTY,
+ /* 89 */ "\u215D",
+ /* 90 */ EMPTY,
// U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
- /* 97 */ "\u215E",
- /* 98 */ EMPTY,
- /* 99 */ EMPTY,
+ /* 91 */ "\u215E",
+ /* 92 */ EMPTY,
+ /* 93 */ EMPTY,
// U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
// U+2205: "∅" EMPTY SET
- /* 100 */ "\u207F,\u2205",
- // Comma key
- /* 101 */ ",",
- /* 102 */ EMPTY,
- /* 103 */ ",",
- /* 104 */ EMPTY,
- /* 105 */ EMPTY,
- // Period key
- /* 106 */ ".",
- /* 107 */ EMPTY,
- /* 108 */ "!text/more_keys_for_punctuation",
- /* 109 */ ".",
- /* 110 */ EMPTY,
- /* 111 */ "!text/more_keys_for_tablet_punctuation",
- /* 112 */ "?",
- /* 113 */ ";",
- /* 114 */ "%",
+ /* 94 */ "\u207F,\u2205",
+ /* 95 */ ",",
+ /* 96 */ EMPTY,
+ /* 97 */ "?",
+ /* 98 */ ";",
+ /* 99 */ "%",
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 115 */ "\u00A1",
+ /* 100 */ "\u00A1",
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 116 */ "\u00BF",
- /* 117 */ EMPTY,
+ /* 101 */ "\u00BF",
+ /* 102 */ EMPTY,
// U+2030: "‰" PER MILLE SIGN
- /* 118 */ "\u2030",
- /* 119 */ EMPTY,
- /* 120 */ EMPTY,
- /* 121 */ "q",
- /* 122 */ "w",
- /* 123 */ "y",
- /* 124 */ "x",
- // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* 125 */ "\u00F1",
- /* 126 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
- /* 127 */ "!icon/settings_key|!code/key_settings",
- /* 128 */ "!icon/shortcut_key|!code/key_shortcut",
- /* 129 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
- /* 130 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
+ /* 103 */ "\u2030",
+ /* 104 */ ",",
+ /* 105~ */
+ EMPTY, EMPTY, EMPTY,
+ /* ~107 */
+ // U+2026: "…" HORIZONTAL ELLIPSIS
+ /* 108 */ "\u2026",
+ /* 109 */ "\'",
+ /* 110 */ "\"",
+ /* 111 */ "\"",
+ /* 112 */ EMPTY,
+ /* 113 */ EMPTY,
+ /* 114 */ "q",
+ /* 115 */ "w",
+ /* 116 */ "y",
+ /* 117 */ "x",
+ /* 118 */ EMPTY,
+ /* 119 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
+ /* 120 */ "!icon/settings_key|!code/key_settings",
+ /* 121 */ "!icon/shortcut_key|!code/key_shortcut",
+ /* 122 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
+ /* 123 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
// Label for "switch to more symbol" modifier key. Must be short to fit on key!
- /* 131 */ "= \\ <",
+ /* 124 */ "= \\ <",
// Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
- /* 132 */ "~ [ <",
+ /* 125 */ "~ [ <",
// Label for "Tab" key. Must be short to fit on key!
- /* 133 */ "Tab",
+ /* 126 */ "Tab",
// Label for "switch to phone numeric" key. Must be short to fit on key!
- /* 134 */ "123",
+ /* 127 */ "123",
// Label for "switch to phone symbols" key. Must be short to fit on key!
// U+FF0A: "*" FULLWIDTH ASTERISK
// U+FF03: "#" FULLWIDTH NUMBER SIGN
- /* 135 */ "\uFF0A\uFF03",
+ /* 128 */ "\uFF0A\uFF03",
// Key label for "ante meridiem"
- /* 136 */ "AM",
+ /* 129 */ "AM",
// Key label for "post meridiem"
- /* 137 */ "PM",
- /* 138 */ ".com",
+ /* 130 */ "PM",
+ /* 131 */ ".com",
// popular web domains for the locale - most popular, displayed on the keyboard
- /* 139 */ "!hasLabels!,.net,.org,.gov,.edu",
- /* 140 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
+ /* 132 */ "!hasLabels!,.net,.org,.gov,.edu",
+ /* 133 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -432,25 +422,25 @@ public final class KeyboardTextsSet {
// The following each quotation mark pair consist of
// <opening quotation mark>, <closing quotation mark>
// and is named after (single|double)_<opening quotation mark>_<closing quotation mark>.
- /* 141 */ "\u2039,\u203A",
- /* 142 */ "\u2039|\u203A,\u203A|\u2039",
- /* 143 */ "\u203A,\u2039",
- /* 144 */ "\u00AB,\u00BB",
- /* 145 */ "\u00AB|\u00BB,\u00BB|\u00AB",
- /* 146 */ "\u00BB,\u00AB",
+ /* 134 */ "\u2039,\u203A",
+ /* 135 */ "\u2039|\u203A,\u203A|\u2039",
+ /* 136 */ "\u203A,\u2039",
+ /* 137 */ "\u00AB,\u00BB",
+ /* 138 */ "\u00AB|\u00BB,\u00BB|\u00AB",
+ /* 139 */ "\u00BB,\u00AB",
// The following each quotation mark triplet consists of
// <another quotation mark>, <opening quotation mark>, <closing quotation mark>
// and is named after (single|double)_<opening quotation mark>_<closing quotation mark>.
- /* 147 */ "\u201A,\u2018,\u2019",
- /* 148 */ "\u2019,\u201A,\u2018",
- /* 149 */ "\u2018,\u201A,\u2019",
- /* 150 */ "\u201E,\u201C,\u201D",
- /* 151 */ "\u201D,\u201E,\u201C",
- /* 152 */ "\u201C,\u201E,\u201D",
- /* 153 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
- /* 154 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
- /* 155 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
- /* 156 */ "!icon/emoji_key|!code/key_emoji",
+ /* 140 */ "\u201A,\u2018,\u2019",
+ /* 141 */ "\u2019,\u201A,\u2018",
+ /* 142 */ "\u2018,\u201A,\u2019",
+ /* 143 */ "\u201E,\u201C,\u201D",
+ /* 144 */ "\u201D,\u201E,\u201C",
+ /* 145 */ "\u201C,\u201E,\u201D",
+ /* 146 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
+ /* 147 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
+ /* 148 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
+ /* 149 */ "!icon/emoji_key|!code/key_emoji",
};
/* Language af: Afrikaans */
@@ -512,43 +502,44 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0623: "ا" ARABIC LETTER ALEF
// U+200C: ZERO WIDTH NON-JOINER
// U+0628: "ب" ARABIC LETTER BEH
// U+062C: "پ" ARABIC LETTER PEH
- /* 51 */ "\u0623\u200C\u0628\u200C\u062C",
- /* 52 */ null,
- /* 53 */ null,
- /* 54 */ "!text/single_laqm_raqm_rtl",
- /* 55 */ "!text/double_laqm_raqm_rtl",
- /* 56~ */
+ /* 45 */ "\u0623\u200C\u0628\u200C\u062C",
+ /* 46 */ null,
+ /* 47 */ null,
+ /* 48 */ "!text/single_laqm_raqm_rtl",
+ /* 49 */ "!text/double_laqm_raqm_rtl",
+ /* 50~ */
null, null, null,
- /* ~58 */
- /* 59 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(",
- /* 60 */ null,
+ /* ~52 */
+ // U+061F: "؟" ARABIC QUESTION MARK
+ // U+060C: "،" ARABIC COMMA
+ // U+061B: "؛" ARABIC SEMICOLON
+ /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 61 */ "\u2605,\u066D",
+ /* 54 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 62 */ "\u266A",
- /* 63 */ null,
+ /* 55 */ "\u266A",
+ /* 56 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 64 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 65 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- /* 66 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 67 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0654: "ٔ" ARABIC HAMZA ABOVE
// U+0652: "ْ" ARABIC SUKUN
@@ -565,74 +556,70 @@ public final class KeyboardTextsSet {
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
// Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
- /* 68 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
+ /* 61 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
+ /* 62 */ "\u0651",
// U+0661: "١" ARABIC-INDIC DIGIT ONE
- /* 69 */ "\u0661",
+ /* 63 */ "\u0661",
// U+0662: "٢" ARABIC-INDIC DIGIT TWO
- /* 70 */ "\u0662",
+ /* 64 */ "\u0662",
// U+0663: "٣" ARABIC-INDIC DIGIT THREE
- /* 71 */ "\u0663",
+ /* 65 */ "\u0663",
// U+0664: "٤" ARABIC-INDIC DIGIT FOUR
- /* 72 */ "\u0664",
+ /* 66 */ "\u0664",
// U+0665: "٥" ARABIC-INDIC DIGIT FIVE
- /* 73 */ "\u0665",
+ /* 67 */ "\u0665",
// U+0666: "٦" ARABIC-INDIC DIGIT SIX
- /* 74 */ "\u0666",
+ /* 68 */ "\u0666",
// U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
- /* 75 */ "\u0667",
+ /* 69 */ "\u0667",
// U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
- /* 76 */ "\u0668",
+ /* 70 */ "\u0668",
// U+0669: "٩" ARABIC-INDIC DIGIT NINE
- /* 77 */ "\u0669",
+ /* 71 */ "\u0669",
// U+0660: "٠" ARABIC-INDIC DIGIT ZERO
- /* 78 */ "\u0660",
+ /* 72 */ "\u0660",
// Label for "switch to symbols" key.
// U+061F: "؟" ARABIC QUESTION MARK
- /* 79 */ "\u0663\u0662\u0661\u061F",
+ /* 73 */ "\u0663\u0662\u0661\u061F",
// Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
// part because it'll be appended by the code.
- /* 80 */ "\u0663\u0662\u0661",
- /* 81 */ "1",
- /* 82 */ "2",
- /* 83 */ "3",
- /* 84 */ "4",
- /* 85 */ "5",
- /* 86 */ "6",
- /* 87 */ "7",
- /* 88 */ "8",
- /* 89 */ "9",
+ /* 74 */ "\u0663\u0662\u0661",
+ /* 75 */ "1",
+ /* 76 */ "2",
+ /* 77 */ "3",
+ /* 78 */ "4",
+ /* 79 */ "5",
+ /* 80 */ "6",
+ /* 81 */ "7",
+ /* 82 */ "8",
+ /* 83 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 90 */ "0,\u066B,\u066C",
- /* 91~ */
+ /* 84 */ "0,\u066B,\u066C",
+ /* 85~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~100 */
+ /* ~94 */
// U+060C: "،" ARABIC COMMA
- /* 101 */ "\u060C",
- /* 102 */ "\\,",
- // U+061F: "؟" ARABIC QUESTION MARK
- // U+060C: "،" ARABIC COMMA
- // U+061B: "؛" ARABIC SEMICOLON
- /* 103 */ "\u060C",
- /* 104 */ "\u061F",
- /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\",\'",
- /* 106 */ null,
- // U+0651: "ّ" ARABIC SHADDA
- /* 107 */ "\u0651",
- /* 108 */ "!text/more_keys_for_arabic_diacritics",
- /* 109 */ null,
- /* 110 */ "\u0651",
- /* 111 */ "!text/more_keys_for_arabic_diacritics",
- /* 112 */ "\u061F",
- /* 113 */ "\u061B",
+ /* 95 */ "\u060C",
+ /* 96 */ "\\,",
+ /* 97 */ "\u061F",
+ /* 98 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
- /* 114 */ "\u066A",
- /* 115 */ null,
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* 116 */ "?,\u00BF",
- /* 117 */ ";",
+ /* 99 */ "\u066A",
+ /* 100 */ null,
+ /* 101 */ "?",
+ /* 102 */ ";",
// U+2030: "‰" PER MILLE SIGN
- /* 118 */ "\\%,\u2030",
+ /* 103 */ "\\%,\u2030",
+ /* 104~ */
+ null, null, null, null, null,
+ /* ~108 */
+ // U+060C: "،" ARABIC COMMA
+ // U+061B: "؛" ARABIC SEMICOLON
+ // U+061F: "؟" ARABIC QUESTION MARK
+ /* 109 */ "\u060C",
+ /* 110 */ "\u061F",
+ /* 111 */ "\u061F,\u061B,!,:,-,/,\',\"",
};
/* Language az: Azerbaijani */
@@ -681,8 +668,8 @@ public final class KeyboardTextsSet {
/* 15 */ "\u011F",
};
- /* Language be_BY: Belarusian (Belarus) */
- private static final String[] LANGUAGE_be_BY = {
+ /* Language be: Belarusian */
+ private static final String[] LANGUAGE_be = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null,
@@ -707,16 +694,14 @@ public final class KeyboardTextsSet {
/* ~42 */
// U+0451: "ё" CYRILLIC SMALL LETTER IO
/* 43 */ "\u0451",
- /* 44~ */
- null, null, null, null, null, null, null,
- /* ~50 */
+ /* 44 */ null,
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language bg: Bulgarian */
@@ -725,16 +710,15 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52 */ null,
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46 */ null,
// single_quotes of Bulgarian is default single_quotes_right_left.
- /* 53 */ "!text/double_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language ca: Catalan */
@@ -798,20 +782,22 @@ public final class KeyboardTextsSet {
/* 15~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~58 */
+ null, null, null, null, null, null, null, null,
+ /* ~52 */
// U+00B7: "·" MIDDLE DOT
- /* 59 */ "!fixedColumnOrder!9,;,/,(,),#,\u00B7,!,\\,,?,&,\\%,+,\",-,:,',@",
- /* 60 */ "!fixedColumnOrder!8,;,/,(,),#,\u00B7,',\\,,&,\\%,+,\",-,:,@",
- /* 61~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* 53 */ "!fixedColumnOrder!9,;,/,(,),#,\u00B7,!,\\,,?,&,\\%,+,\",-,:,',@",
+ /* 54~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null,
- /* ~124 */
+ null, null, null, null, null, null, null, null, null,
+ /* ~107 */
+ /* 108 */ "?,\u00B7",
+ /* 109~ */
+ null, null, null, null, null, null, null, null, null,
+ /* ~117 */
// U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- /* 125 */ "\u00E7",
+ /* 118 */ "\u00E7",
};
/* Language cs: Czech */
@@ -885,12 +871,12 @@ public final class KeyboardTextsSet {
/* 13~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language da: Danish */
@@ -954,12 +940,12 @@ public final class KeyboardTextsSet {
/* 24 */ "\u00F6",
/* 25~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language de: German */
@@ -1005,25 +991,12 @@ public final class KeyboardTextsSet {
/* 7~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null,
- /* ~44 */
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* 45 */ "\u00FC",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* 46 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* 47 */ "\u00E4",
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- /* 48 */ "\u00E8",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* 49 */ "\u00E9",
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- /* 50 */ "\u00E0",
- /* 51 */ null,
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null, null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language el: Greek */
@@ -1032,13 +1005,12 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0391: "Α" GREEK CAPITAL LETTER ALPHA
// U+0392: "Β" GREEK CAPITAL LETTER BETA
// U+0393: "Γ" GREEK CAPITAL LETTER GAMMA
- /* 51 */ "\u0391\u0392\u0393",
+ /* 45 */ "\u0391\u0392\u0393",
};
/* Language en: English */
@@ -1210,20 +1182,20 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~118 */
- /* 119 */ "q",
- /* 120 */ "x",
+ null, null,
+ /* ~111 */
+ /* 112 */ "q",
+ /* 113 */ "x",
// U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
- /* 121 */ "\u015D",
+ /* 114 */ "\u015D",
// U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
- /* 122 */ "\u011D",
+ /* 115 */ "\u011D",
// U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
- /* 123 */ "\u016D",
+ /* 116 */ "\u016D",
// U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
- /* 124 */ "\u0109",
+ /* 117 */ "\u0109",
// U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
- /* 125 */ "\u0135",
+ /* 118 */ "\u0135",
};
/* Language es: Spanish */
@@ -1282,15 +1254,33 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~58 */
+ /* ~52 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 59 */ "!fixedColumnOrder!9,\u00A1,;,/,(,),#,!,\\,,?,\u00BF,&,\\%,+,\",-,:,',@",
+ /* 53 */ "!fixedColumnOrder!4,;,!,\\,,?,:,\u00A1,@,\u00BF",
+ /* 54~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null,
+ /* ~105 */
+ // U+00A1: "¡" INVERTED EXCLAMATION MARK
+ /* 106 */ "!,\u00A1",
+ /* 107 */ null,
+ // U+00BF: "¿" INVERTED QUESTION MARK
+ /* 108 */ "?,\u00BF",
+ /* 109 */ "\"",
+ /* 110 */ "\'",
+ /* 111 */ "\'",
+ /* 112~ */
+ null, null, null, null, null, null,
+ /* ~117 */
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 118 */ "\u00F1",
};
- /* Language et_EE: Estonian (Estonia) */
- private static final String[] LANGUAGE_et_EE = {
+ /* Language et: Estonian */
+ private static final String[] LANGUAGE_et = {
// U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
// U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
// U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
@@ -1389,10 +1379,10 @@ public final class KeyboardTextsSet {
/* 23 */ "\u00F5",
/* 24~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ null, null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language fa: Persian */
@@ -1401,47 +1391,45 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0627: "ا" ARABIC LETTER ALEF
// U+200C: ZERO WIDTH NON-JOINER
// U+0628: "ب" ARABIC LETTER BEH
// U+067E: "پ" ARABIC LETTER PEH
- /* 51 */ "\u0627\u200C\u0628\u200C\u067E",
- /* 52 */ null,
- /* 53 */ null,
- /* 54 */ "!text/single_laqm_raqm_rtl",
- /* 55 */ "!text/double_laqm_raqm_rtl",
- /* 56 */ null,
+ /* 45 */ "\u0627\u200C\u0628\u200C\u067E",
+ /* 46 */ null,
+ /* 47 */ null,
+ /* 48 */ "!text/single_laqm_raqm_rtl",
+ /* 49 */ "!text/double_laqm_raqm_rtl",
+ /* 50 */ null,
// U+FDFC: "﷼" RIAL SIGN
- /* 57 */ "\uFDFC",
- /* 58 */ null,
+ /* 51 */ "\uFDFC",
+ /* 52 */ null,
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 59 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(",
- /* 60 */ null,
+ /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 61 */ "\u2605,\u066D",
+ /* 54 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 62 */ "\u266A",
- /* 63 */ null,
+ /* 55 */ "\u266A",
+ /* 56 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 64 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 65 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- /* 66 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
- /* 67 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
+ /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
+ /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0652: "ْ" ARABIC SUKUN
// U+0651: "ّ" ARABIC SHADDA
@@ -1458,75 +1446,74 @@ public final class KeyboardTextsSet {
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
// Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly.
- /* 68 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
+ /* 61 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640",
+ /* 62 */ "\u064B",
// U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
- /* 69 */ "\u06F1",
+ /* 63 */ "\u06F1",
// U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
- /* 70 */ "\u06F2",
+ /* 64 */ "\u06F2",
// U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
- /* 71 */ "\u06F3",
+ /* 65 */ "\u06F3",
// U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
- /* 72 */ "\u06F4",
+ /* 66 */ "\u06F4",
// U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
- /* 73 */ "\u06F5",
+ /* 67 */ "\u06F5",
// U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
- /* 74 */ "\u06F6",
+ /* 68 */ "\u06F6",
// U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
- /* 75 */ "\u06F7",
+ /* 69 */ "\u06F7",
// U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
- /* 76 */ "\u06F8",
+ /* 70 */ "\u06F8",
// U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
- /* 77 */ "\u06F9",
+ /* 71 */ "\u06F9",
// U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
- /* 78 */ "\u06F0",
+ /* 72 */ "\u06F0",
// Label for "switch to symbols" key.
// U+061F: "؟" ARABIC QUESTION MARK
- /* 79 */ "\u06F3\u06F2\u06F1\u061F",
+ /* 73 */ "\u06F3\u06F2\u06F1\u061F",
// Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
// part because it'll be appended by the code.
- /* 80 */ "\u06F3\u06F2\u06F1",
- /* 81 */ "1",
- /* 82 */ "2",
- /* 83 */ "3",
- /* 84 */ "4",
- /* 85 */ "5",
- /* 86 */ "6",
- /* 87 */ "7",
- /* 88 */ "8",
- /* 89 */ "9",
+ /* 74 */ "\u06F3\u06F2\u06F1",
+ /* 75 */ "1",
+ /* 76 */ "2",
+ /* 77 */ "3",
+ /* 78 */ "4",
+ /* 79 */ "5",
+ /* 80 */ "6",
+ /* 81 */ "7",
+ /* 82 */ "8",
+ /* 83 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 90 */ "0,\u066B,\u066C",
- /* 91~ */
+ /* 84 */ "0,\u066B,\u066C",
+ /* 85~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~100 */
+ /* ~94 */
// U+060C: "،" ARABIC COMMA
- /* 101 */ "\u060C",
- /* 102 */ "\\,",
+ /* 95 */ "\u060C",
+ /* 96 */ "\\,",
+ /* 97 */ "\u061F",
+ /* 98 */ "\u061B",
+ // U+066A: "٪" ARABIC PERCENT SIGN
+ /* 99 */ "\u066A",
+ /* 100 */ null,
+ /* 101 */ "?",
+ /* 102 */ ";",
+ // U+2030: "‰" PER MILLE SIGN
+ /* 103 */ "\\%,\u2030",
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
// U+061F: "؟" ARABIC QUESTION MARK
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- /* 103 */ "\u060C",
- /* 104 */ "\u061F",
- /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB",
- /* 106 */ null,
+ /* 104 */ "\u060C",
+ /* 105 */ "!",
+ /* 106 */ "!,\\,",
/* 107 */ "\u061F",
- /* 108 */ "!text/more_keys_for_arabic_diacritics",
- /* 109 */ null,
- /* 110 */ "\u064B",
- /* 111 */ "!text/more_keys_for_arabic_diacritics",
- /* 112 */ "\u061F",
- /* 113 */ "\u061B",
- // U+066A: "٪" ARABIC PERCENT SIGN
- /* 114 */ "\u066A",
- /* 115 */ null,
- // U+00BF: "¿" INVERTED QUESTION MARK
- /* 116 */ "?,\u00BF",
- /* 117 */ ";",
- // U+2030: "‰" PER MILLE SIGN
- /* 118 */ "\\%,\u2030",
+ /* 108 */ "\u061F,?",
+ /* 109 */ "\u060C",
+ /* 110 */ "\u061F",
+ /* 111 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB",
};
/* Language fi: Finnish */
@@ -1627,23 +1614,6 @@ public final class KeyboardTextsSet {
/* 7 */ "\u00E7,\u0107,\u010D",
// U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
/* 8 */ "%,\u00FF",
- /* 9~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~44 */
- // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- /* 45 */ "\u00E8",
- // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- /* 46 */ "\u00E9",
- // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- /* 47 */ "\u00E0",
- // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- /* 48 */ "\u00FC",
- // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- /* 49 */ "\u00F6",
- // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- /* 50 */ "\u00E4",
};
/* Language hi: Hindi */
@@ -1652,56 +1622,55 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0915: "क" DEVANAGARI LETTER KA
// U+0916: "ख" DEVANAGARI LETTER KHA
// U+0917: "ग" DEVANAGARI LETTER GA
- /* 51 */ "\u0915\u0916\u0917",
- /* 52~ */
+ /* 45 */ "\u0915\u0916\u0917",
+ /* 46~ */
null, null, null, null, null,
- /* ~56 */
+ /* ~50 */
// U+20B9: "₹" INDIAN RUPEE SIGN
- /* 57 */ "\u20B9",
- /* 58~ */
+ /* 51 */ "\u20B9",
+ /* 52~ */
null, null, null, null, null, null, null, null, null, null, null,
- /* ~68 */
+ /* ~62 */
// U+0967: "१" DEVANAGARI DIGIT ONE
- /* 69 */ "\u0967",
+ /* 63 */ "\u0967",
// U+0968: "२" DEVANAGARI DIGIT TWO
- /* 70 */ "\u0968",
+ /* 64 */ "\u0968",
// U+0969: "३" DEVANAGARI DIGIT THREE
- /* 71 */ "\u0969",
+ /* 65 */ "\u0969",
// U+096A: "४" DEVANAGARI DIGIT FOUR
- /* 72 */ "\u096A",
+ /* 66 */ "\u096A",
// U+096B: "५" DEVANAGARI DIGIT FIVE
- /* 73 */ "\u096B",
+ /* 67 */ "\u096B",
// U+096C: "६" DEVANAGARI DIGIT SIX
- /* 74 */ "\u096C",
+ /* 68 */ "\u096C",
// U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* 75 */ "\u096D",
+ /* 69 */ "\u096D",
// U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* 76 */ "\u096E",
+ /* 70 */ "\u096E",
// U+096F: "९" DEVANAGARI DIGIT NINE
- /* 77 */ "\u096F",
+ /* 71 */ "\u096F",
// U+0966: "०" DEVANAGARI DIGIT ZERO
- /* 78 */ "\u0966",
+ /* 72 */ "\u0966",
// Label for "switch to symbols" key.
- /* 79 */ "?\u0967\u0968\u0969",
+ /* 73 */ "?\u0967\u0968\u0969",
// Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
// part because it'll be appended by the code.
- /* 80 */ "\u0967\u0968\u0969",
- /* 81 */ "1",
- /* 82 */ "2",
- /* 83 */ "3",
- /* 84 */ "4",
- /* 85 */ "5",
- /* 86 */ "6",
- /* 87 */ "7",
- /* 88 */ "8",
- /* 89 */ "9",
- /* 90 */ "0",
+ /* 74 */ "\u0967\u0968\u0969",
+ /* 75 */ "1",
+ /* 76 */ "2",
+ /* 77 */ "3",
+ /* 78 */ "4",
+ /* 79 */ "5",
+ /* 80 */ "6",
+ /* 81 */ "7",
+ /* 82 */ "8",
+ /* 83 */ "9",
+ /* 84 */ "0",
};
/* Language hr: Croatian */
@@ -1732,12 +1701,12 @@ public final class KeyboardTextsSet {
/* 13~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_rqm",
- /* 53 */ "!text/double_9qm_rqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_rqm",
+ /* 47 */ "!text/double_9qm_rqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language hu: Hungarian */
@@ -1786,23 +1755,22 @@ public final class KeyboardTextsSet {
/* 5~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_rqm",
- /* 53 */ "!text/double_9qm_rqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null, null, null, null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_rqm",
+ /* 47 */ "!text/double_9qm_rqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
- /* Language hy_AM: Armenian (Armenia) */
- private static final String[] LANGUAGE_hy_AM = {
+ /* Language hy: Armenian */
+ private static final String[] LANGUAGE_hy = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~58 */
+ null, null, null, null, null, null, null, null,
+ /* ~52 */
// U+058A: "֊" ARMENIAN HYPHEN
// U+055C: "՜" ARMENIAN EXCLAMATION MARK
// U+055D: "՝" ARMENIAN COMMA
@@ -1811,36 +1779,19 @@ public final class KeyboardTextsSet {
// U+055A: "՚" ARMENIAN APOSTROPHE
// U+055B: "՛" ARMENIAN EMPHASIS MARK
// U+055F: "՟" ARMENIAN ABBREVIATION MARK
- /* 59 */ "!fixedColumnOrder!8,!,?,\\,,.,\u058A,\u055C,\u055D,\u055E,:,;,@,\u0559,\u055A,\u055B,\u055F",
- /* 60~ */
+ /* 53 */ "!fixedColumnOrder!8,!,?,\\,,.,\u058A,\u055C,\u055D,\u055E,:,;,@,\u0559,\u055A,\u055B,\u055F",
+ /* 54~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~102 */
- // U+058F: "֏" ARMENIAN DRAM SIGN
- // TODO: Enable this when we have glyph for the following letter
- // <string name="keylabel_for_currency">&#x058F;</string>
- //
- // U+055D: "՝" ARMENIAN COMMA
- /* 103 */ "\u055D",
- /* 104 */ null,
- /* 105 */ null,
- // U+0589: "։" ARMENIAN FULL STOP
- /* 106 */ "\u0589",
- /* 107 */ null,
- /* 108 */ null,
- /* 109 */ "\u0589",
- /* 110 */ null,
- /* 111 */ "!text/more_keys_for_punctuation",
- /* 112~ */
- null, null, null,
- /* ~114 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null,
+ /* ~99 */
// U+055C: "՜" ARMENIAN EXCLAMATION MARK
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 115 */ "\u055C,\u00A1",
+ /* 100 */ "\u055C,\u00A1",
// U+055E: "՞" ARMENIAN QUESTION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 116 */ "\u055E,\u00BF",
+ /* 101 */ "\u055E,\u00BF",
};
/* Language is: Icelandic */
@@ -1906,10 +1857,10 @@ public final class KeyboardTextsSet {
/* 22 */ "\u00FE",
/* 23~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ null, null, null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language it: Italian */
@@ -1963,13 +1914,12 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+05D0: "א" HEBREW LETTER ALEF
// U+05D1: "ב" HEBREW LETTER BET
// U+05D2: "ג" HEBREW LETTER GIMEL
- /* 51 */ "\u05D0\u05D1\u05D2",
+ /* 45 */ "\u05D0\u05D1\u05D2",
// The following characters don't need BIDI mirroring.
// U+2018: "‘" LEFT SINGLE QUOTATION MARK
// U+2019: "’" RIGHT SINGLE QUOTATION MARK
@@ -1977,51 +1927,58 @@ public final class KeyboardTextsSet {
// U+201C: "“" LEFT DOUBLE QUOTATION MARK
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
- /* 52 */ "\u2018,\u2019,\u201A",
- /* 53 */ "\u201C,\u201D,\u201E",
- /* 54 */ "!text/single_laqm_raqm_rtl",
- /* 55 */ "!text/double_laqm_raqm_rtl",
- /* 56 */ null,
+ /* 46 */ "\u2018,\u2019,\u201A",
+ /* 47 */ "\u201C,\u201D,\u201E",
+ /* 48 */ "!text/single_laqm_raqm_rtl",
+ /* 49 */ "!text/double_laqm_raqm_rtl",
+ /* 50 */ null,
// U+20AA: "₪" NEW SHEQEL SIGN
- /* 57 */ "\u20AA",
- /* 58 */ null,
- /* 59 */ "!fixedColumnOrder!8,;,/,(|),)|(,#,!,\\,,?,&,\\%,+,\",-,:,',@",
- /* 60 */ "!fixedColumnOrder!7,;,/,(|),)|(,#,',\\,,&,\\%,+,\",-,:,@",
+ /* 51 */ "\u20AA",
+ /* 52 */ null,
+ /* 53 */ "!fixedColumnOrder!8,;,/,(|),)|(,#,!,\\,,?,&,\\%,+,\",-,:,',@",
// U+2605: "★" BLACK STAR
- /* 61 */ "\u2605",
- /* 62 */ null,
+ /* 54 */ "\u2605",
+ /* 55 */ null,
// U+00B1: "±" PLUS-MINUS SIGN
// U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
- /* 63 */ "\u00B1,\uFB29",
+ /* 56 */ "\u00B1,\uFB29",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 64 */ "!fixedColumnOrder!3,<|>,{|},[|]",
- /* 65 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
+ /* 57 */ "!fixedColumnOrder!3,<|>,{|},[|]",
+ /* 58 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
- /* 66 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 67 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 61~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~104 */
+ /* 105 */ "!",
+ /* 106 */ "!",
+ /* 107 */ "?",
+ /* 108 */ "?",
};
- /* Language ka_GE: Georgian (Georgia) */
- private static final String[] LANGUAGE_ka_GE = {
+ /* Language ka: Georgian */
+ private static final String[] LANGUAGE_ka = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+10D0: "ა" GEORGIAN LETTER AN
// U+10D1: "ბ" GEORGIAN LETTER BAN
// U+10D2: "გ" GEORGIAN LETTER GAN
- /* 51 */ "\u10D0\u10D1\u10D2",
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ /* 45 */ "\u10D0\u10D1\u10D2",
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language kk: Kazakh */
@@ -2064,34 +2021,31 @@ public final class KeyboardTextsSet {
/* ~42 */
// U+0451: "ё" CYRILLIC SMALL LETTER IO
/* 43 */ "\u0451",
- /* 44~ */
- null, null, null, null, null, null, null,
- /* ~50 */
+ /* 44 */ null,
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
+ /* 45 */ "\u0410\u0411\u0412",
};
- /* Language km_KH: Khmer (Cambodia) */
- private static final String[] LANGUAGE_km_KH = {
+ /* Language km: Khmer */
+ private static final String[] LANGUAGE_km = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+1780: "ក" KHMER LETTER KA
// U+1781: "ខ" KHMER LETTER KHA
// U+1782: "គ" KHMER LETTER KO
- /* 51 */ "\u1780\u1781\u1782",
- /* 52~ */
+ /* 45 */ "\u1780\u1781\u1782",
+ /* 46~ */
null, null, null, null,
- /* ~55 */
+ /* ~49 */
// U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL
- /* 56 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
+ /* 50 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
};
/* Language ky: Kirghiz */
@@ -2127,34 +2081,31 @@ public final class KeyboardTextsSet {
/* ~42 */
// U+0451: "ё" CYRILLIC SMALL LETTER IO
/* 43 */ "\u0451",
- /* 44~ */
- null, null, null, null, null, null, null,
- /* ~50 */
+ /* 44 */ null,
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
+ /* 45 */ "\u0410\u0411\u0412",
};
- /* Language lo_LA: Lao (Laos) */
- private static final String[] LANGUAGE_lo_LA = {
+ /* Language lo: Lao */
+ private static final String[] LANGUAGE_lo = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0E81: "ກ" LAO LETTER KO
// U+0E82: "ຂ" LAO LETTER KHO SUNG
// U+0E84: "ຄ" LAO LETTER KHO TAM
- /* 51 */ "\u0E81\u0E82\u0E84",
- /* 52~ */
+ /* 45 */ "\u0E81\u0E82\u0E84",
+ /* 46~ */
null, null, null, null, null,
- /* ~56 */
+ /* ~50 */
// U+20AD: "₭" KIP SIGN
- /* 57 */ "\u20AD",
+ /* 51 */ "\u20AD",
};
/* Language lt: Lithuanian */
@@ -2248,10 +2199,9 @@ public final class KeyboardTextsSet {
/* 16~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language lv: Latvian */
@@ -2344,10 +2294,9 @@ public final class KeyboardTextsSet {
/* 16~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language mk: Macedonian */
@@ -2369,36 +2318,32 @@ public final class KeyboardTextsSet {
/* 43 */ "\u0450",
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
/* 44 */ "\u045D",
- /* 45~ */
- null, null, null, null, null, null,
- /* ~50 */
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
- /* Language mn_MN: Mongolian (Mongolia) */
- private static final String[] LANGUAGE_mn_MN = {
+ /* Language mn: Mongolian */
+ private static final String[] LANGUAGE_mn = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52~ */
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46~ */
null, null, null, null, null,
- /* ~56 */
+ /* ~50 */
// U+20AE: "₮" TUGRIK SIGN
- /* 57 */ "\u20AE",
+ /* 51 */ "\u20AE",
};
/* Language nb: Norwegian Bokmål */
@@ -2448,10 +2393,10 @@ public final class KeyboardTextsSet {
/* 24 */ "\u00E4",
/* 25~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_rqm",
- /* 53 */ "!text/double_9qm_rqm",
+ null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_rqm",
+ /* 47 */ "!text/double_9qm_rqm",
};
/* Language ne: Nepali */
@@ -2460,56 +2405,55 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0915: "क" DEVANAGARI LETTER KA
// U+0916: "ख" DEVANAGARI LETTER KHA
// U+0917: "ग" DEVANAGARI LETTER GA
- /* 51 */ "\u0915\u0916\u0917",
- /* 52~ */
+ /* 45 */ "\u0915\u0916\u0917",
+ /* 46~ */
null, null, null, null, null,
- /* ~56 */
+ /* ~50 */
// U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
- /* 57 */ "\u0930\u0941.",
- /* 58~ */
+ /* 51 */ "\u0930\u0941.",
+ /* 52~ */
null, null, null, null, null, null, null, null, null, null, null,
- /* ~68 */
+ /* ~62 */
// U+0967: "१" DEVANAGARI DIGIT ONE
- /* 69 */ "\u0967",
+ /* 63 */ "\u0967",
// U+0968: "२" DEVANAGARI DIGIT TWO
- /* 70 */ "\u0968",
+ /* 64 */ "\u0968",
// U+0969: "३" DEVANAGARI DIGIT THREE
- /* 71 */ "\u0969",
+ /* 65 */ "\u0969",
// U+096A: "४" DEVANAGARI DIGIT FOUR
- /* 72 */ "\u096A",
+ /* 66 */ "\u096A",
// U+096B: "५" DEVANAGARI DIGIT FIVE
- /* 73 */ "\u096B",
+ /* 67 */ "\u096B",
// U+096C: "६" DEVANAGARI DIGIT SIX
- /* 74 */ "\u096C",
+ /* 68 */ "\u096C",
// U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* 75 */ "\u096D",
+ /* 69 */ "\u096D",
// U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* 76 */ "\u096E",
+ /* 70 */ "\u096E",
// U+096F: "९" DEVANAGARI DIGIT NINE
- /* 77 */ "\u096F",
+ /* 71 */ "\u096F",
// U+0966: "०" DEVANAGARI DIGIT ZERO
- /* 78 */ "\u0966",
+ /* 72 */ "\u0966",
// Label for "switch to symbols" key.
- /* 79 */ "?\u0967\u0968\u0969",
+ /* 73 */ "?\u0967\u0968\u0969",
// Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
// part because it'll be appended by the code.
- /* 80 */ "\u0967\u0968\u0969",
- /* 81 */ "1",
- /* 82 */ "2",
- /* 83 */ "3",
- /* 84 */ "4",
- /* 85 */ "5",
- /* 86 */ "6",
- /* 87 */ "7",
- /* 88 */ "8",
- /* 89 */ "9",
- /* 90 */ "0",
+ /* 74 */ "\u0967\u0968\u0969",
+ /* 75 */ "1",
+ /* 76 */ "2",
+ /* 77 */ "3",
+ /* 78 */ "4",
+ /* 79 */ "5",
+ /* 80 */ "6",
+ /* 81 */ "7",
+ /* 82 */ "8",
+ /* 83 */ "9",
+ /* 84 */ "0",
};
/* Language nl: Dutch */
@@ -2564,10 +2508,10 @@ public final class KeyboardTextsSet {
/* 9~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_rqm",
- /* 53 */ "!text/double_9qm_rqm",
+ null, null, null, null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_rqm",
+ /* 47 */ "!text/double_9qm_rqm",
};
/* Language pl: Polish */
@@ -2625,10 +2569,10 @@ public final class KeyboardTextsSet {
/* 15~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_rqm",
- /* 53 */ "!text/double_9qm_rqm",
+ null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_rqm",
+ /* 47 */ "!text/double_9qm_rqm",
};
/* Language pt: Portuguese */
@@ -2731,10 +2675,10 @@ public final class KeyboardTextsSet {
/* 12~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_rqm",
- /* 53 */ "!text/double_9qm_rqm",
+ null, null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_rqm",
+ /* 47 */ "!text/double_9qm_rqm",
};
/* Language ru: Russian */
@@ -2763,16 +2707,14 @@ public final class KeyboardTextsSet {
/* ~42 */
// U+0451: "ё" CYRILLIC SMALL LETTER IO
/* 43 */ "\u0451",
- /* 44~ */
- null, null, null, null, null, null, null,
- /* ~50 */
+ /* 44 */ null,
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
};
/* Language sk: Slovak */
@@ -2866,12 +2808,11 @@ public final class KeyboardTextsSet {
/* 16~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language sl: Slovenian */
@@ -2895,12 +2836,12 @@ public final class KeyboardTextsSet {
/* 13~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null,
- /* ~51 */
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null,
+ /* ~45 */
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language sr: Serbian */
@@ -2940,19 +2881,16 @@ public final class KeyboardTextsSet {
/* 43 */ "\u0450",
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
/* 44 */ "\u045D",
- /* 45~ */
- null, null, null, null, null, null,
- /* ~50 */
// END: More keys definitions for Serbian (Cyrillic)
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language sv: Swedish */
@@ -3034,10 +2972,10 @@ public final class KeyboardTextsSet {
/* 24 */ "\u00E6",
/* 25~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~53 */
- /* 54 */ "!text/single_raqm_laqm",
- /* 55 */ "!text/double_raqm_laqm",
+ null, null, null, null, null, null, null, null,
+ /* ~47 */
+ /* 48 */ "!text/single_raqm_laqm",
+ /* 49 */ "!text/double_raqm_laqm",
};
/* Language sw: Swahili */
@@ -3097,18 +3035,17 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null,
- /* ~50 */
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0E01: "ก" THAI CHARACTER KO KAI
// U+0E02: "ข" THAI CHARACTER KHO KHAI
// U+0E04: "ค" THAI CHARACTER KHO KHWAI
- /* 51 */ "\u0E01\u0E02\u0E04",
- /* 52~ */
+ /* 45 */ "\u0E01\u0E02\u0E04",
+ /* 46~ */
null, null, null, null, null,
- /* ~56 */
+ /* ~50 */
// U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
- /* 57 */ "\u0E3F",
+ /* 51 */ "\u0E3F",
};
/* Language tl: Tagalog */
@@ -3238,20 +3175,20 @@ public final class KeyboardTextsSet {
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
/* 37 */ "\u044A",
/* 38~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~50 */
+ null, null, null, null, null, null, null,
+ /* ~44 */
// Label for "switch to alphabetic" key.
// U+0410: "А" CYRILLIC CAPITAL LETTER A
// U+0411: "Б" CYRILLIC CAPITAL LETTER BE
// U+0412: "В" CYRILLIC CAPITAL LETTER VE
- /* 51 */ "\u0410\u0411\u0412",
- /* 52 */ "!text/single_9qm_lqm",
- /* 53 */ "!text/double_9qm_lqm",
- /* 54~ */
+ /* 45 */ "\u0410\u0411\u0412",
+ /* 46 */ "!text/single_9qm_lqm",
+ /* 47 */ "!text/double_9qm_lqm",
+ /* 48~ */
null, null, null,
- /* ~56 */
+ /* ~50 */
// U+20B4: "₴" HRYVNIA SIGN
- /* 57 */ "\u20B4",
+ /* 51 */ "\u20B4",
};
/* Language vi: Vietnamese */
@@ -3336,11 +3273,10 @@ public final class KeyboardTextsSet {
/* 10~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null,
- /* ~56 */
+ null, null, null, null, null, null, null, null, null, null, null,
+ /* ~50 */
// U+20AB: "₫" DONG SIGN
- /* 57 */ "\u20AB",
+ /* 51 */ "\u20AB",
};
/* Language zu: Zulu */
@@ -3513,14 +3449,12 @@ public final class KeyboardTextsSet {
/* 19 */ "\u0175",
};
- // TODO: Use the language + "_" + region representation for the locale string key.
- // Currently we are dropping the region from the key.
private static final Object[] LANGUAGES_AND_TEXTS = {
"DEFAULT", LANGUAGE_DEFAULT, /* default */
"af", LANGUAGE_af, /* Afrikaans */
"ar", LANGUAGE_ar, /* Arabic */
"az", LANGUAGE_az, /* Azerbaijani */
- "be" /* "be_BY" */, LANGUAGE_be_BY, /* Belarusian (Belarus) */
+ "be", LANGUAGE_be, /* Belarusian */
"bg", LANGUAGE_bg, /* Bulgarian */
"ca", LANGUAGE_ca, /* Catalan */
"cs", LANGUAGE_cs, /* Czech */
@@ -3530,26 +3464,26 @@ public final class KeyboardTextsSet {
"en", LANGUAGE_en, /* English */
"eo", LANGUAGE_eo, /* Esperanto */
"es", LANGUAGE_es, /* Spanish */
- "et" /* "et_EE" */, LANGUAGE_et_EE, /* Estonian (Estonia) */
+ "et", LANGUAGE_et, /* Estonian */
"fa", LANGUAGE_fa, /* Persian */
"fi", LANGUAGE_fi, /* Finnish */
"fr", LANGUAGE_fr, /* French */
"hi", LANGUAGE_hi, /* Hindi */
"hr", LANGUAGE_hr, /* Croatian */
"hu", LANGUAGE_hu, /* Hungarian */
- "hy" /* "hy_AM" */, LANGUAGE_hy_AM, /* Armenian (Armenia) */
+ "hy", LANGUAGE_hy, /* Armenian */
"is", LANGUAGE_is, /* Icelandic */
"it", LANGUAGE_it, /* Italian */
"iw", LANGUAGE_iw, /* Hebrew */
- "ka" /* "ka_GE" */, LANGUAGE_ka_GE, /* Georgian (Georgia) */
+ "ka", LANGUAGE_ka, /* Georgian */
"kk", LANGUAGE_kk, /* Kazakh */
- "km" /* "km_KH" */, LANGUAGE_km_KH, /* Khmer (Cambodia) */
+ "km", LANGUAGE_km, /* Khmer */
"ky", LANGUAGE_ky, /* Kirghiz */
- "lo" /* "lo_LA" */, LANGUAGE_lo_LA, /* Lao (Laos) */
+ "lo", LANGUAGE_lo, /* Lao */
"lt", LANGUAGE_lt, /* Lithuanian */
"lv", LANGUAGE_lv, /* Latvian */
"mk", LANGUAGE_mk, /* Macedonian */
- "mn" /* "mn_MN" */, LANGUAGE_mn_MN, /* Mongolian (Mongolia) */
+ "mn", LANGUAGE_mn, /* Mongolian */
"nb", LANGUAGE_nb, /* Norwegian Bokmål */
"ne", LANGUAGE_ne, /* Nepali */
"nl", LANGUAGE_nl, /* Dutch */
diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
index 111eb6db6..a0935b985 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
@@ -20,24 +20,18 @@ import android.util.Log;
import android.view.MotionEvent;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler;
import com.android.inputmethod.latin.utils.CoordinateUtils;
public final class NonDistinctMultitouchHelper {
private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName();
- // Use only main (id=0) pointer tracker.
- private final PointerTracker mMainTracker;
private int mOldPointerCount = 1;
private Key mOldKey;
private int[] mLastCoords = CoordinateUtils.newInstance();
- public NonDistinctMultitouchHelper(final PointerTracker mainTracker) {
- mMainTracker = mainTracker;
- }
-
- public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) {
+ public void processMotionEvent(final MotionEvent me, final KeyEventHandler keyEventHandler) {
final int pointerCount = me.getPointerCount();
final int oldPointerCount = mOldPointerCount;
mOldPointerCount = pointerCount;
@@ -47,7 +41,8 @@ public final class NonDistinctMultitouchHelper {
return;
}
- final PointerTracker mainTracker = mMainTracker;
+ // Use only main (id=0) pointer tracker.
+ final PointerTracker mainTracker = PointerTracker.getPointerTracker(0, keyEventHandler);
final int action = me.getActionMasked();
final int index = me.getActionIndex();
final long eventTime = me.getEventTime();
@@ -56,12 +51,12 @@ public final class NonDistinctMultitouchHelper {
// In single-touch.
if (oldPointerCount == 1 && pointerCount == 1) {
if (me.getPointerId(index) == mainTracker.mPointerId) {
- mainTracker.processMotionEvent(me, keyDetector);
+ mainTracker.processMotionEvent(me, keyEventHandler);
return;
}
// Inject a copied event.
injectMotionEvent(action, me.getX(index), me.getY(index), downTime, eventTime,
- mainTracker, keyDetector);
+ mainTracker, keyEventHandler);
return;
}
@@ -75,7 +70,7 @@ public final class NonDistinctMultitouchHelper {
mOldKey = mainTracker.getKeyOn(x, y);
// Inject an artifact up event for the old key.
injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime,
- mainTracker, keyDetector);
+ mainTracker, keyEventHandler);
return;
}
@@ -90,11 +85,11 @@ public final class NonDistinctMultitouchHelper {
// Inject an artifact down event for the new key.
// An artifact up event for the new key will usually be injected as a single-touch.
injectMotionEvent(MotionEvent.ACTION_DOWN, x, y, downTime, eventTime,
- mainTracker, keyDetector);
+ mainTracker, keyEventHandler);
if (action == MotionEvent.ACTION_UP) {
// Inject an artifact up event for the new key also.
injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime,
- mainTracker, keyDetector);
+ mainTracker, keyEventHandler);
}
}
return;
@@ -106,11 +101,11 @@ public final class NonDistinctMultitouchHelper {
private static void injectMotionEvent(final int action, final float x, final float y,
final long downTime, final long eventTime, final PointerTracker tracker,
- final KeyDetector keyDetector) {
+ final KeyEventHandler handler) {
final MotionEvent me = MotionEvent.obtain(
downTime, eventTime, action, x, y, 0 /* metaState */);
try {
- tracker.processMotionEvent(me, keyDetector);
+ tracker.processMotionEvent(me, handler);
} finally {
me.recycle();
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 5ac34188c..7ee45e8f6 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -28,7 +28,7 @@ public final class PointerTrackerQueue {
public interface Element {
public boolean isModifier();
- public boolean isInDraggingFinger();
+ public boolean isInSlidingKeyInput();
public void onPhantomUpEvent(long eventTime);
public void cancelTrackingForAction();
}
@@ -193,13 +193,13 @@ public final class PointerTrackerQueue {
}
}
- public boolean isAnyInDraggingFinger() {
+ public boolean isAnyInSlidingKeyInput() {
synchronized (mExpandableArrayOfActivePointers) {
final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
final int arraySize = mArraySize;
for (int index = 0; index < arraySize; index++) {
final Element element = expandableArray.get(index);
- if (element.isInDraggingFinger()) {
+ if (element.isInSlidingKeyInput()) {
return true;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
index 5a996ff53..9cf68d43d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
@@ -20,21 +20,25 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
+import android.widget.ScrollView;
+import android.widget.Scroller;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
/**
- * This is an extended {@link KeyboardView} class that hosts an emoji page keyboard.
+ * This is an extended {@link KeyboardView} class that hosts a vertical scroll keyboard.
* Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
+ * TODO: Vertical scroll capability should be removed from this class because it's no longer used.
*/
// TODO: Implement key popup preview.
-public final class EmojiPageKeyboardView extends KeyboardView implements
- GestureDetector.OnGestureListener {
+public final class ScrollKeyboardView extends KeyboardView implements
+ ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener {
+ private static final boolean PAGINATION = false;
+
public interface OnKeyClickListener {
public void onKeyClick(Key key);
}
@@ -48,15 +52,63 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
private final GestureDetector mGestureDetector;
- public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
+ private final Scroller mScroller;
+ private ScrollViewWithNotifier mScrollView;
+
+ public ScrollKeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.keyboardViewStyle);
}
- public EmojiPageKeyboardView(final Context context, final AttributeSet attrs,
- final int defStyle) {
+ public ScrollKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
+ mScroller = new Scroller(context);
+ }
+
+ public void setScrollView(final ScrollViewWithNotifier scrollView) {
+ mScrollView = scrollView;
+ scrollView.setScrollListener(this);
+ }
+
+ private final Runnable mScrollTask = new Runnable() {
+ @Override
+ public void run() {
+ final Scroller scroller = mScroller;
+ final ScrollView scrollView = mScrollView;
+ scroller.computeScrollOffset();
+ scrollView.scrollTo(0, scroller.getCurrY());
+ if (!scroller.isFinished()) {
+ scrollView.post(this);
+ }
+ }
+ };
+
+ // {@link ScrollViewWithNotified#ScrollListener} methods.
+ @Override
+ public void notifyScrollChanged(final int scrollX, final int scrollY, final int oldX,
+ final int oldY) {
+ if (PAGINATION) {
+ mScroller.forceFinished(true /* finished */);
+ mScrollView.removeCallbacks(mScrollTask);
+ final int currentTop = mScrollView.getScrollY();
+ final int pageHeight = getKeyboard().mBaseHeight;
+ final int lastPageNo = currentTop / pageHeight;
+ final int lastPageTop = lastPageNo * pageHeight;
+ final int nextPageNo = lastPageNo + 1;
+ final int nextPageTop = Math.min(nextPageNo * pageHeight, getHeight() - pageHeight);
+ final int scrollTo = (currentTop - lastPageTop) < (nextPageTop - currentTop)
+ ? lastPageTop : nextPageTop;
+ final int deltaY = scrollTo - currentTop;
+ mScroller.startScroll(0, currentTop, 0, deltaY, 300);
+ mScrollView.post(mScrollTask);
+ }
+ }
+
+ @Override
+ public void notifyOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
+ final boolean clampedY) {
+ releaseCurrentKey();
}
public void setOnKeyClickListener(final OnKeyClickListener listener) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java
new file mode 100644
index 000000000..d1ccdc7b5
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ScrollView;
+
+/**
+ * This is an extended {@link ScrollView} that can notify
+ * {@link ScrollView#onScrollChanged(int,int,int,int} and
+ * {@link ScrollView#onOverScrolled(int,int,int,int)} to a content view.
+ */
+public class ScrollViewWithNotifier extends ScrollView {
+ private ScrollListener mScrollListener = EMPTY_LISTER;
+
+ public interface ScrollListener {
+ public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY);
+ public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
+ boolean clampedY);
+ }
+
+ private static final ScrollListener EMPTY_LISTER = new ScrollListener() {
+ @Override
+ public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY) {}
+ @Override
+ public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
+ boolean clampedY) {}
+ };
+
+ public ScrollViewWithNotifier(final Context context, final AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onScrollChanged(final int scrollX, final int scrollY, final int oldX,
+ final int oldY) {
+ super.onScrollChanged(scrollX, scrollY, oldX, oldY);
+ mScrollListener.notifyScrollChanged(scrollX, scrollY, oldX, oldY);
+ }
+
+ @Override
+ protected void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
+ final boolean clampedY) {
+ super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ mScrollListener.notifyOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ }
+
+ public void setScrollListener(final ScrollListener listener) {
+ mScrollListener = listener;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
deleted file mode 100644
index 88d0878d1..000000000
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.keyboard.internal;
-
-import android.os.Message;
-import android.os.SystemClock;
-import android.view.ViewConfiguration;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
-import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks;
-import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
-
-// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so.
-public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy {
- public interface Callbacks {
- public void startWhileTypingFadeinAnimation();
- public void startWhileTypingFadeoutAnimation();
- public void onLongPress(PointerTracker tracker);
- }
-
- private static final int MSG_TYPING_STATE_EXPIRED = 0;
- private static final int MSG_REPEAT_KEY = 1;
- private static final int MSG_LONGPRESS_KEY = 2;
- private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 3;
- private static final int MSG_UPDATE_BATCH_INPUT = 4;
-
- private final int mIgnoreAltCodeKeyTimeout;
- private final int mGestureRecognitionUpdateTime;
-
- public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout,
- final int gestureRecognitionUpdateTime) {
- super(ownerInstance);
- mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout;
- mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime;
- }
-
- @Override
- public void handleMessage(final Message msg) {
- final Callbacks callbacks = getOwnerInstance();
- if (callbacks == null) {
- return;
- }
- final PointerTracker tracker = (PointerTracker) msg.obj;
- switch (msg.what) {
- case MSG_TYPING_STATE_EXPIRED:
- callbacks.startWhileTypingFadeinAnimation();
- break;
- case MSG_REPEAT_KEY:
- tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
- break;
- case MSG_LONGPRESS_KEY:
- cancelLongPressTimers();
- callbacks.onLongPress(tracker);
- break;
- case MSG_UPDATE_BATCH_INPUT:
- tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
- startUpdateBatchInputTimer(tracker);
- break;
- }
- }
-
- @Override
- public void startKeyRepeatTimerOf(final PointerTracker tracker, final int repeatCount,
- final int delay) {
- final Key key = tracker.getKey();
- if (key == null || delay == 0) {
- return;
- }
- sendMessageDelayed(
- obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay);
- }
-
- private void cancelKeyRepeatTimerOf(final PointerTracker tracker) {
- removeMessages(MSG_REPEAT_KEY, tracker);
- }
-
- public void cancelKeyRepeatTimers() {
- removeMessages(MSG_REPEAT_KEY);
- }
-
- // TODO: Suppress layout changes in key repeat mode
- public boolean isInKeyRepeat() {
- return hasMessages(MSG_REPEAT_KEY);
- }
-
- @Override
- public void startLongPressTimerOf(final PointerTracker tracker, final int delay) {
- if (delay <= 0) {
- return;
- }
- sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay);
- }
-
- @Override
- public void cancelLongPressTimerOf(final PointerTracker tracker) {
- removeMessages(MSG_LONGPRESS_KEY, tracker);
- }
-
- private void cancelLongPressTimers() {
- removeMessages(MSG_LONGPRESS_KEY);
- }
-
- @Override
- public void startTypingStateTimer(final Key typedKey) {
- if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
- return;
- }
-
- final boolean isTyping = isTypingState();
- removeMessages(MSG_TYPING_STATE_EXPIRED);
- final Callbacks callbacks = getOwnerInstance();
- if (callbacks == null) {
- return;
- }
-
- // When user hits the space or the enter key, just cancel the while-typing timer.
- final int typedCode = typedKey.getCode();
- if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
- if (isTyping) {
- callbacks.startWhileTypingFadeinAnimation();
- }
- return;
- }
-
- sendMessageDelayed(
- obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
- if (isTyping) {
- return;
- }
- callbacks.startWhileTypingFadeoutAnimation();
- }
-
- @Override
- public boolean isTypingState() {
- return hasMessages(MSG_TYPING_STATE_EXPIRED);
- }
-
- @Override
- public void startDoubleTapShiftKeyTimer() {
- sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY),
- ViewConfiguration.getDoubleTapTimeout());
- }
-
- @Override
- public void cancelDoubleTapShiftKeyTimer() {
- removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
- }
-
- @Override
- public boolean isInDoubleTapShiftKeyTimeout() {
- return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY);
- }
-
- @Override
- public void cancelKeyTimersOf(final PointerTracker tracker) {
- cancelKeyRepeatTimerOf(tracker);
- cancelLongPressTimerOf(tracker);
- }
-
- public void cancelAllKeyTimers() {
- cancelKeyRepeatTimers();
- cancelLongPressTimers();
- }
-
- @Override
- public void startUpdateBatchInputTimer(final PointerTracker tracker) {
- if (mGestureRecognitionUpdateTime <= 0) {
- return;
- }
- removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
- sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
- mGestureRecognitionUpdateTime);
- }
-
- @Override
- public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
- removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
- }
-
- @Override
- public void cancelAllUpdateBatchInputTimers() {
- removeMessages(MSG_UPDATE_BATCH_INPUT);
- }
-
- public void cancelAllMessages() {
- cancelAllKeyTimers();
- cancelAllUpdateBatchInputTimers();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index e6fb9807e..463d09344 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -27,13 +27,15 @@ import java.io.File;
import java.io.IOException;
import java.util.Map;
-abstract public class AbstractDictionaryWriter {
+// TODO: Quit extending Dictionary after implementing dynamic binary dictionary.
+abstract public class AbstractDictionaryWriter extends Dictionary {
/** Used for Log actions from this class */
private static final String TAG = AbstractDictionaryWriter.class.getSimpleName();
private final Context mContext;
- public AbstractDictionaryWriter(final Context context) {
+ public AbstractDictionaryWriter(final Context context, final String dictType) {
+ super(dictType);
mContext = context;
}
@@ -53,16 +55,18 @@ abstract public class AbstractDictionaryWriter {
// TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
abstract public void addBigramWords(final String word0, final String word1,
- final int frequency, final boolean isValid, final long lastModifiedTime);
+ final int frequency, final boolean isValid,
+ final long lastModifiedTime);
abstract public void removeBigramWords(final String word0, final String word1);
abstract protected void writeDictionary(final DictEncoder dictEncoder,
final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException;
- public void write(final File file, final Map<String, String> attributeMap) {
- final String tempFilePath = file.getAbsolutePath() + ".temp";
- final File tempFile = new File(tempFilePath);
+ public void write(final String fileName, final Map<String, String> attributeMap) {
+ final String tempFileName = fileName + ".temp";
+ final File file = new File(mContext.getFilesDir(), fileName);
+ final File tempFile = new File(mContext.getFilesDir(), tempFileName);
try {
final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile);
writeDictionary(dictEncoder, attributeMap);
diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
index fd6c24dfe..875192554 100644
--- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java
+++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
@@ -16,8 +16,6 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.utils.FileUtils;
-
import java.io.File;
/**
@@ -54,12 +52,4 @@ public final class AssetFileAddress {
if (!f.isFile()) return null;
return new AssetFileAddress(filename, offset, length);
}
-
- public boolean pointsToPhysicalFile() {
- return 0 == mOffset;
- }
-
- public void deleteUnderlyingFile() {
- FileUtils.deleteRecursively(new File(mFilename));
- }
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index db4234c63..fd296988e 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -26,7 +26,6 @@ import com.android.inputmethod.latin.settings.NativeSuggestOptions;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.StringUtils;
-import com.android.inputmethod.latin.utils.UnigramProperty;
import java.io.File;
import java.util.ArrayList;
@@ -58,21 +57,6 @@ public final class BinaryDictionary extends Dictionary {
@UsedForTesting
public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
- public static final int NOT_A_VALID_TIMESTAMP = -1;
-
- // Format to get unigram flags from native side via getUnigramPropertyNative().
- private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT = 4;
- private static final int FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX = 0;
- private static final int FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX = 1;
- private static final int FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX = 2;
- private static final int FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX = 3;
-
- // Format to get unigram historical info from native side via getUnigramPropertyNative().
- private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT = 3;
- private static final int FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX = 0;
- private static final int FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX = 1;
- private static final int FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX = 2;
-
private long mNativeDict;
private final Locale mLocale;
private final long mDictSize;
@@ -139,13 +123,8 @@ public final class BinaryDictionary extends Dictionary {
private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
private static native void flushWithGCNative(long dict, String filePath);
private static native void closeNative(long dict);
- private static native int getFormatVersionNative(long dict);
private static native int getProbabilityNative(long dict, int[] word);
private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
- private static native void getUnigramPropertyNative(long dict, int[] word,
- int[] outCodePoints, boolean[] outFlags, int[] outProbability,
- int[] outHistoricalInfo, ArrayList<int[]> outShortcutTargets,
- ArrayList<Integer> outShortcutProbabilities);
private static native int getSuggestionsNative(long dict, long proximityInfo,
long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
@@ -154,14 +133,10 @@ public final class BinaryDictionary extends Dictionary {
int[] outputAutoCommitFirstWordConfidence);
private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
private static native int editDistanceNative(int[] before, int[] after);
- private static native void addUnigramWordNative(long dict, int[] word, int probability,
- int[] shortcutTarget, int shortcutProbability, boolean isNotAWord,
- boolean isBlacklisted, int timestamp);
+ private static native void addUnigramWordNative(long dict, int[] word, int probability);
private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
- int probability, int timestamp);
+ int probability);
private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
- private static native int addMultipleDictionaryEntriesNative(long dict,
- LanguageModelParam[] languageModelParams, int startIndex);
private static native int calculateProbabilityNative(long dict, int unigramProbability,
int bigramProbability);
private static native String getPropertyNative(long dict, String query);
@@ -260,10 +235,6 @@ public final class BinaryDictionary extends Dictionary {
return mNativeDict != 0;
}
- public int getFormatVersion() {
- return getFormatVersionNative(mNativeDict);
- }
-
public static float calcNormalizedScore(final String before, final String after,
final int score) {
return calcNormalizedScoreNative(StringUtils.toCodePointArray(before),
@@ -303,55 +274,23 @@ public final class BinaryDictionary extends Dictionary {
return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
}
- @UsedForTesting
- public UnigramProperty getUnigramProperty(final String word) {
- if (TextUtils.isEmpty(word)) {
- return null;
- }
- final int[] codePoints = StringUtils.toCodePointArray(word);
- final int[] outCodePoints = new int[MAX_WORD_LENGTH];
- final boolean[] outFlags = new boolean[FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT];
- final int[] outProbability = new int[1];
- final int[] outHistoricalInfo =
- new int[FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT];
- final ArrayList<int[]> outShortcutTargets = CollectionUtils.newArrayList();
- final ArrayList<Integer> outShortcutProbabilities = CollectionUtils.newArrayList();
- getUnigramPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbability,
- outHistoricalInfo, outShortcutTargets, outShortcutProbabilities);
- return new UnigramProperty(codePoints,
- outFlags[FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX],
- outFlags[FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX],
- outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX],
- outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX], outProbability[0],
- outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX],
- outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX],
- outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX],
- outShortcutTargets, outShortcutProbabilities);
- }
-
- // Add a unigram entry to binary dictionary with unigram attributes in native code.
- public void addUnigramWord(final String word, final int probability,
- final String shortcutTarget, final int shortcutProbability, final boolean isNotAWord,
- final boolean isBlacklisted, final int timestamp) {
+ // Add a unigram entry to binary dictionary in native code.
+ public void addUnigramWord(final String word, final int probability) {
if (TextUtils.isEmpty(word)) {
return;
}
final int[] codePoints = StringUtils.toCodePointArray(word);
- final int[] shortcutTargetCodePoints = (shortcutTarget != null) ?
- StringUtils.toCodePointArray(shortcutTarget) : null;
- addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
- shortcutProbability, isNotAWord, isBlacklisted, timestamp);
+ addUnigramWordNative(mNativeDict, codePoints, probability);
}
- // Add a bigram entry to binary dictionary with timestamp in native code.
- public void addBigramWords(final String word0, final String word1, final int probability,
- final int timestamp) {
+ // Add a bigram entry to binary dictionary in native code.
+ public void addBigramWords(final String word0, final String word1, final int probability) {
if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
return;
}
final int[] codePoints0 = StringUtils.toCodePointArray(word0);
final int[] codePoints1 = StringUtils.toCodePointArray(word1);
- addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability, timestamp);
+ addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability);
}
// Remove a bigram entry form binary dictionary in native code.
@@ -364,70 +303,10 @@ public final class BinaryDictionary extends Dictionary {
removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
}
- public static class LanguageModelParam {
- public final int[] mWord0;
- public final int[] mWord1;
- public final int[] mShortcutTarget;
- public final int mUnigramProbability;
- public final int mBigramProbability;
- public final int mShortcutProbability;
- public final boolean mIsNotAWord;
- public final boolean mIsBlacklisted;
- public final int mTimestamp;
-
- // Constructor for unigram.
- public LanguageModelParam(final String word, final int unigramProbability,
- final int timestamp) {
- mWord0 = null;
- mWord1 = StringUtils.toCodePointArray(word);
- mShortcutTarget = null;
- mUnigramProbability = unigramProbability;
- mBigramProbability = NOT_A_PROBABILITY;
- mShortcutProbability = NOT_A_PROBABILITY;
- mIsNotAWord = false;
- mIsBlacklisted = false;
- mTimestamp = timestamp;
- }
-
- // Constructor for unigram and bigram.
- public LanguageModelParam(final String word0, final String word1,
- final int unigramProbability, final int bigramProbability,
- final int timestamp) {
- mWord0 = StringUtils.toCodePointArray(word0);
- mWord1 = StringUtils.toCodePointArray(word1);
- mShortcutTarget = null;
- mUnigramProbability = unigramProbability;
- mBigramProbability = bigramProbability;
- mShortcutProbability = NOT_A_PROBABILITY;
- mIsNotAWord = false;
- mIsBlacklisted = false;
- mTimestamp = timestamp;
- }
- }
-
- public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) {
- if (!isValidDictionary()) return;
- int processedParamCount = 0;
- while (processedParamCount < languageModelParams.length) {
- if (needsToRunGC(true /* mindsBlockByGC */)) {
- flushWithGC();
- }
- processedParamCount = addMultipleDictionaryEntriesNative(mNativeDict,
- languageModelParams, processedParamCount);
- if (processedParamCount <= 0) {
- return;
- }
- }
-
- }
-
private void reopen() {
close();
final File dictFile = new File(mDictFilePath);
- // WARNING: Because we pass 0 as the offset and file.length() as the length, this can
- // only be called for actual files. Right now it's only called by the flush() family of
- // functions, which require an updatable dictionary, so it's okay. But beware.
- loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
+ mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */,
dictFile.length(), true /* isUpdatable */);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index b4382bc2c..722a82961 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -98,7 +98,7 @@ public final class BinaryDictionaryFileDumper {
* This creates a URI builder able to build a URI pointing to the dictionary
* pack content provider for a specific dictionary id.
*/
- public static Uri.Builder getProviderUriBuilder(final String path) {
+ private static Uri.Builder getProviderUriBuilder(final String path) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(DictionaryPackConstants.AUTHORITY).appendPath(path);
}
@@ -339,25 +339,15 @@ public final class BinaryDictionaryFileDumper {
Log.e(TAG, "Could not copy a word list. Will not be able to use it.");
// If we can't copy it we should warn the dictionary provider so that it can mark it
// as invalid.
- reportBrokenFileToDictionaryProvider(providerClient, clientId, wordlistId);
- }
-
- public static boolean reportBrokenFileToDictionaryProvider(
- final ContentProviderClient providerClient, final String clientId,
- final String wordlistId) {
+ wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT,
+ QUERY_PARAMETER_FAILURE);
try {
- final Uri.Builder wordListUriBuilder = getContentUriBuilderForType(clientId,
- providerClient, QUERY_PATH_DATAFILE, wordlistId /* extraPath */);
- wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT,
- QUERY_PARAMETER_FAILURE);
if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) {
- Log.e(TAG, "Unable to delete a word list.");
+ Log.e(TAG, "In addition, we were unable to delete it.");
}
} catch (RemoteException e) {
- Log.e(TAG, "Communication with the dictionary provider was cut", e);
- return false;
+ Log.e(TAG, "In addition, communication with the dictionary provider was cut", e);
}
- return true;
}
// Ideally the two following methods should be merged, but AssetFileDescriptor does not
@@ -442,9 +432,8 @@ public final class BinaryDictionaryFileDumper {
// Actually copy the file
final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE];
- for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) {
+ for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer))
output.write(buffer, 0, readBytes);
- }
input.close();
}
@@ -489,7 +478,8 @@ public final class BinaryDictionaryFileDumper {
* @param context the context for resources and providers.
* @param clientId the client ID to use.
*/
- public static void initializeClientRecordHelper(final Context context, final String clientId) {
+ public static void initializeClientRecordHelper(final Context context,
+ final String clientId) {
try {
final ContentProviderClient client = context.getContentResolver().
acquireContentProviderClient(getProviderUriBuilder("").build());
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index c260434d5..9a9653094 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -70,47 +70,38 @@ public final class Constants {
public static final class ExtraValue {
/**
- * The subtype extra value used to indicate that this subtype is capable of
- * entering ASCII characters.
+ * The subtype extra value used to indicate that the subtype keyboard layout is capable
+ * for typing ASCII characters.
*/
public static final String ASCII_CAPABLE = "AsciiCapable";
/**
- * The subtype extra value used to indicate that this subtype is enabled
- * when the default subtype is not marked as ascii capable.
- */
- public static final String ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
- "EnabledWhenDefaultIsNotAsciiCapable";
-
- /**
- * The subtype extra value used to indicate that this subtype is capable of
- * entering emoji characters.
+ * The subtype extra value used to indicate that the subtype keyboard layout is capable
+ * for typing EMOJI characters.
*/
public static final String EMOJI_CAPABLE = "EmojiCapable";
-
/**
- * The subtype extra value used to indicate that this subtype requires a network
- * connection to work.
+ * The subtype extra value used to indicate that the subtype require network connection
+ * to work.
*/
public static final String REQ_NETWORK_CONNECTIVITY = "requireNetworkConnectivity";
/**
- * The subtype extra value used to indicate that the display name of this subtype
- * contains a "%s" for printf-like replacement and it should be replaced by
- * this extra value.
+ * The subtype extra value used to indicate that the subtype display name contains "%s"
+ * for replacement mark and it should be replaced by this extra value.
* This extra value is supported on JellyBean and later.
*/
public static final String UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
"UntranslatableReplacementStringInSubtypeName";
/**
- * The subtype extra value used to indicate this subtype keyboard layout set name.
+ * The subtype extra value used to indicate that the subtype keyboard layout set name.
* This extra value is private to LatinIME.
*/
public static final String KEYBOARD_LAYOUT_SET = "KeyboardLayoutSet";
/**
- * The subtype extra value used to indicate that this subtype is an additional subtype
+ * The subtype extra value used to indicate that the subtype is additional subtype
* that the user defined. This extra value is private to LatinIME.
*/
public static final String IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype";
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index b6cfcd064..47891c6b7 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -44,8 +44,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
private static final String TAG = ContactsBinaryDictionary.class.getSimpleName();
private static final String NAME = "contacts";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_DUMP = false;
+ private static boolean DEBUG = false;
/**
* Frequency for contacts information into the dictionary
@@ -72,8 +71,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
private final boolean mUseFirstLastBigrams;
public ContactsBinaryDictionary(final Context context, final Locale locale) {
- super(context, getDictNameWithLocale(NAME, locale), locale,
- Dictionary.TYPE_CONTACTS, false /* isUpdatable */);
+ super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS,
+ false /* isUpdatable */);
mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context);
@@ -169,10 +168,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
if (isValidName(name)) {
addName(name);
++count;
- } else {
- if (DEBUG_DUMP) {
- Log.d(TAG, "Invalid name: " + name);
- }
}
cursor.moveToNext();
}
@@ -209,9 +204,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
if (Character.isLetter(name.codePointAt(i))) {
int end = getWordEndPosition(name, len, i);
String word = name.substring(i, end);
- if (DEBUG_DUMP) {
- Log.d(TAG, "addName word = " + word);
- }
i = end - 1;
// Don't add single letter words, possibly confuses
// capitalization of i.
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index e04524843..fa79f5af7 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -52,10 +52,13 @@ public abstract class Dictionary {
public static final String TYPE_CONTACTS = "contacts";
// User dictionary, the system-managed one.
public static final String TYPE_USER = "user";
- // User history dictionary internal to LatinIME.
+ // User history dictionary internal to LatinIME. This assumes bigram prediction for now.
public static final String TYPE_USER_HISTORY = "history";
- // Personalization dictionary.
+ // Personalization binary dictionary internal to LatinIME.
public static final String TYPE_PERSONALIZATION = "personalization";
+ // Personalization prediction dictionary internal to LatinIME's Java code.
+ public static final String TYPE_PERSONALIZATION_PREDICTION_IN_JAVA =
+ "personalization_prediction_in_java";
public final String mDictType;
public Dictionary(final String dictType) {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index e09c309ea..828e54f14 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -16,7 +16,6 @@
package com.android.inputmethod.latin;
-import android.content.ContentProviderClient;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
@@ -65,10 +64,6 @@ public final class DictionaryFactory {
useFullEditDistance, locale, Dictionary.TYPE_MAIN);
if (readOnlyBinaryDictionary.isValidDictionary()) {
dictList.add(readOnlyBinaryDictionary);
- } else {
- readOnlyBinaryDictionary.close();
- // Prevent this dictionary to do any further harm.
- killDictionary(context, f);
}
}
}
@@ -80,51 +75,6 @@ public final class DictionaryFactory {
}
/**
- * Kills a dictionary so that it is never used again, if possible.
- * @param context The context to contact the dictionary provider, if possible.
- * @param f A file address to the dictionary to kill.
- */
- private static void killDictionary(final Context context, final AssetFileAddress f) {
- if (f.pointsToPhysicalFile()) {
- f.deleteUnderlyingFile();
- // Warn the dictionary provider if the dictionary came from there.
- final ContentProviderClient providerClient;
- try {
- providerClient = context.getContentResolver().acquireContentProviderClient(
- BinaryDictionaryFileDumper.getProviderUriBuilder("").build());
- } catch (final SecurityException e) {
- Log.e(TAG, "No permission to communicate with the dictionary provider", e);
- return;
- }
- if (null == providerClient) {
- Log.e(TAG, "Can't establish communication with the dictionary provider");
- return;
- }
- final String wordlistId =
- DictionaryInfoUtils.getWordListIdFromFileName(new File(f.mFilename).getName());
- if (null != wordlistId) {
- // TODO: this is a reasonable last resort, but it is suboptimal.
- // The following will remove the entry for this dictionary with the dictionary
- // provider. When the metadata is downloaded again, we will try downloading it
- // again.
- // However, in the practice that will mean the user will find themselves without
- // the new dictionary. That's fine for languages where it's included in the APK,
- // but for other languages it will leave the user without a dictionary at all until
- // the next update, which may be a few days away.
- // Ideally, we would trigger a new download right away, and use increasing retry
- // delays for this particular id/version combination.
- // Then again, this is expected to only ever happen in case of human mistake. If
- // the wrong file is on the server, the following is still doing the right thing.
- // If it's a file left over from the last version however, it's not great.
- BinaryDictionaryFileDumper.reportBrokenFileToDictionaryProvider(
- providerClient,
- context.getString(R.string.dictionary_pack_client_id),
- wordlistId);
- }
- }
- }
-
- /**
* Initializes a main dictionary collection from a dictionary pack, with default flags.
*
* This searches for a content provider providing a dictionary pack for the specified
diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
index 89ef96d7f..3df2a2b63 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.latin;
import android.content.Context;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.makedict.DictEncoder;
import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.FusionDictionary;
@@ -35,14 +37,14 @@ import java.util.Map;
* An in memory dictionary for memorizing entries and writing a binary dictionary.
*/
public class DictionaryWriter extends AbstractDictionaryWriter {
- private static final int BINARY_DICT_VERSION = 2;
+ private static final int BINARY_DICT_VERSION = 3;
private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
- new FormatSpec.FormatOptions(BINARY_DICT_VERSION, false /* supportsDynamicUpdate */);
+ new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */);
private FusionDictionary mFusionDictionary;
- public DictionaryWriter(final Context context) {
- super(context);
+ public DictionaryWriter(final Context context, final String dictType) {
+ super(context, dictType);
clear();
}
@@ -50,7 +52,7 @@ public class DictionaryWriter extends AbstractDictionaryWriter {
public void clear() {
final HashMap<String, String> attributes = CollectionUtils.newHashMap();
mFusionDictionary = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(attributes));
+ new FusionDictionary.DictionaryOptions(attributes, false, false));
}
/**
@@ -90,4 +92,18 @@ public class DictionaryWriter extends AbstractDictionaryWriter {
}
dictEncoder.writeDictionary(mFusionDictionary, FORMAT_OPTIONS);
}
+
+ @Override
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final String prevWord, final ProximityInfo proximityInfo,
+ boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+ // This class doesn't support suggestion.
+ return null;
+ }
+
+ @Override
+ public boolean isValidWord(String word) {
+ // This class doesn't support dictionary retrieval.
+ return false;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 9f5cd162f..eb8650e6f 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -22,22 +22,18 @@ import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
-import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam;
import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -56,7 +52,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/** Whether to print debug output to log */
private static boolean DEBUG = false;
- private static final boolean DBG_STRESS_TEST = false;
+
+ // TODO: Remove.
+ /** Whether to call binary dictionary dynamically updating methods. */
+ public static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = true;
private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
@@ -65,19 +64,22 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
- private static final int DICTIONARY_FORMAT_VERSION = FormatSpec.VERSION4;
+ private static final int DICTIONARY_FORMAT_VERSION = 3;
+
+ private static final String SUPPORTS_DYNAMIC_UPDATE =
+ FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE;
/**
* A static map of update controllers, each of which records the time of accesses to a single
* binary dictionary file and tracks whether the file is regenerating. The key for this map is
- * the dictionary name and the value is the shared dictionary time recorder associated with
- * that dictionary name.
+ * the filename and the value is the shared dictionary time recorder associated with that
+ * filename.
*/
private static final ConcurrentHashMap<String, DictionaryUpdateController>
- sDictNameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
+ sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
- sDictNameExecutorMap = CollectionUtils.newConcurrentHashMap();
+ sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap();
/** The application context. */
protected final Context mContext;
@@ -93,24 +95,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected AbstractDictionaryWriter mDictionaryWriter;
/**
- * The name of this dictionary, used as a part of the filename for storing the binary
- * dictionary. Multiple dictionary instances with the same name is supported, with access
- * controlled by DictionaryUpdateController.
+ * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple
+ * dictionary instances with the same filename is supported, with access controlled by
+ * DictionaryTimeRecorder.
*/
- private final String mDictName;
-
- /** Dictionary locale */
- private final Locale mLocale;
+ private final String mFilename;
/** Whether to support dynamically updating the dictionary */
private final boolean mIsUpdatable;
- /** Dictionary file */
- private final File mDictFile;
-
// TODO: remove, once dynamic operations is serialized
/** Controls updating the shared binary dictionary file across multiple instances. */
- private final DictionaryUpdateController mDictNameDictionaryUpdateController;
+ private final DictionaryUpdateController mFilenameDictionaryUpdateController;
// TODO: remove, once dynamic operations is serialized
/** Controls updating the local binary dictionary for this instance. */
@@ -136,57 +132,45 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
protected abstract boolean hasContentChanged();
- protected boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) {
- // This class is using format 2 because it's used by the User and Contacts dictionary
- // only, which right now use format 2 (dicts using format 4 use Decaying*, which overrides
- // this method).
- // TODO: Migrate these dicts to ver4 format, and remove this function.
- return formatVersion == 2;
- }
-
- public boolean isValidDictionary() {
- return mBinaryDictionary.isValidDictionary();
- }
-
- private File getDictFile() {
- return mDictFile;
- }
-
/**
- * Gets the dictionary update controller for the given dictionary name.
+ * Gets the dictionary update controller for the given filename.
*/
private static DictionaryUpdateController getDictionaryUpdateController(
- final String dictName) {
- DictionaryUpdateController recorder = sDictNameDictionaryUpdateControllerMap.get(dictName);
+ String filename) {
+ DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename);
if (recorder == null) {
- synchronized(sDictNameDictionaryUpdateControllerMap) {
+ synchronized(sFilenameDictionaryUpdateControllerMap) {
recorder = new DictionaryUpdateController();
- sDictNameDictionaryUpdateControllerMap.put(dictName, recorder);
+ sFilenameDictionaryUpdateControllerMap.put(filename, recorder);
}
}
return recorder;
}
/**
- * Gets the executor for the given dictionary name.
+ * Gets the executor for the given filename.
*/
- private static PrioritizedSerialExecutor getExecutor(final String dictName) {
- PrioritizedSerialExecutor executor = sDictNameExecutorMap.get(dictName);
+ private static PrioritizedSerialExecutor getExecutor(final String filename) {
+ PrioritizedSerialExecutor executor = sFilenameExecutorMap.get(filename);
if (executor == null) {
- synchronized(sDictNameExecutorMap) {
+ synchronized(sFilenameExecutorMap) {
executor = new PrioritizedSerialExecutor();
- sDictNameExecutorMap.put(dictName, executor);
+ sFilenameExecutorMap.put(filename, executor);
}
}
return executor;
}
private static AbstractDictionaryWriter getDictionaryWriter(final Context context,
- final boolean isDynamicPersonalizationDictionary) {
+ final String dictType, final boolean isDynamicPersonalizationDictionary) {
if (isDynamicPersonalizationDictionary) {
- return null;
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ return null;
+ } else {
+ return new DynamicPersonalizationDictionaryWriter(context, dictType);
+ }
} else {
- return new DictionaryWriter(context);
+ return new DictionaryWriter(context, dictType);
}
}
@@ -194,37 +178,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* Creates a new expandable binary dictionary.
*
* @param context The application context of the parent.
- * @param dictName The name of the dictionary. Multiple instances with the same
- * name is supported.
- * @param locale the dictionary locale.
+ * @param filename The filename for this binary dictionary. Multiple dictionaries with the same
+ * filename is supported.
* @param dictType the dictionary type, as a human-readable string
* @param isUpdatable whether to support dynamically updating the dictionary. Please note that
* dynamic dictionary has negative effects on memory space and computation time.
*/
- public ExpandableBinaryDictionary(final Context context, final String dictName,
- final Locale locale, final String dictType, final boolean isUpdatable) {
- this(context, dictName, locale, dictType, isUpdatable,
- new File(context.getFilesDir(), dictName + DICT_FILE_EXTENSION));
- }
-
- // Creates an instance that uses a given dictionary file.
- public ExpandableBinaryDictionary(final Context context, final String dictName,
- final Locale locale, final String dictType, final boolean isUpdatable,
- final File dictFile) {
+ public ExpandableBinaryDictionary(final Context context, final String filename,
+ final String dictType, final boolean isUpdatable) {
super(dictType);
- mDictName = dictName;
+ mFilename = filename;
mContext = context;
- mLocale = locale;
mIsUpdatable = isUpdatable;
- mDictFile = dictFile;
mBinaryDictionary = null;
- mDictNameDictionaryUpdateController = getDictionaryUpdateController(dictName);
+ mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename);
// Currently, only dynamic personalization dictionary is updatable.
- mDictionaryWriter = getDictionaryWriter(context, isUpdatable);
+ mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
}
- protected static String getDictNameWithLocale(final String name, final Locale locale) {
- return name + "." + locale.toString();
+ protected static String getFilenameWithLocale(final String name, final String localeStr) {
+ return name + "." + localeStr + DICT_FILE_EXTENSION;
}
/**
@@ -232,20 +205,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
@Override
public void close() {
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
if (mBinaryDictionary!= null) {
mBinaryDictionary.close();
mBinaryDictionary = null;
}
+ if (mDictionaryWriter != null) {
+ mDictionaryWriter.close();
+ }
}
});
}
protected void closeBinaryDictionary() {
// Ensure that no other threads are accessing the local binary dictionary.
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
if (mBinaryDictionary != null) {
@@ -258,23 +234,19 @@ 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_ATTRIBUTE, mDictName);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString());
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
- String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+ attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
+ SUPPORTS_DYNAMIC_UPDATE);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFilename);
return attributeMap;
}
protected void clear() {
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
- if (mDictionaryWriter == null) {
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE && mDictionaryWriter == null) {
mBinaryDictionary.close();
- final File file = getDictFile();
- if (file.exists() && !FileUtils.deleteRecursively(file)) {
- Log.e(TAG, "Can't remove a file: " + file.getName());
- }
+ final File file = new File(mContext.getFilesDir(), mFilename);
BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
mBinaryDictionary = new BinaryDictionary(
@@ -314,7 +286,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* Check whether GC is needed and run GC if required.
*/
protected void runGCIfRequired(final boolean mindsBlockByGC) {
- getExecutor(mDictName).execute(new Runnable() {
+ if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return;
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
runGCIfRequiredInternalLocked(mindsBlockByGC);
@@ -323,17 +296,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) {
+ if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return;
// Calls to needsToRunGC() need to be serialized.
if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
- if (setProcessingLargeTaskIfNot()) {
+ if (setIsRegeneratingIfNotRegenerating()) {
// Run GC after currently existing time sensitive operations.
- getExecutor(mDictName).executePrioritized(new Runnable() {
+ getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
public void run() {
try {
mBinaryDictionary.flushWithGC();
} finally {
- mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
+ mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
}
}
});
@@ -344,19 +318,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/**
* Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
*/
- protected void addWordDynamically(final String word, final int frequency,
- final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
- final boolean isBlacklisted, final int timestamp) {
+ protected void addWordDynamically(final String word, final String shortcutTarget,
+ final int frequency, final int shortcutFreq, final boolean isNotAWord) {
if (!mIsUpdatable) {
- Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mDictName);
+ Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
return;
}
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
- runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
- mBinaryDictionary.addUnigramWord(word, frequency, shortcutTarget, shortcutFreq,
- isNotAWord, isBlacklisted, timestamp);
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
+ mBinaryDictionary.addUnigramWord(word, frequency);
+ } else {
+ // TODO: Remove.
+ mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq,
+ isNotAWord);
+ }
}
});
}
@@ -365,17 +343,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* Dynamically adds a word bigram in the dictionary. May overwrite an existing entry.
*/
protected void addBigramDynamically(final String word0, final String word1,
- final int frequency, final int timestamp) {
+ final int frequency, final boolean isValid) {
if (!mIsUpdatable) {
Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
- + mDictName);
+ + mFilename);
return;
}
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
- runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
- mBinaryDictionary.addBigramWords(word0, word1, frequency, timestamp);
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
+ mBinaryDictionary.addBigramWords(word0, word1, frequency);
+ } else {
+ // TODO: Remove.
+ mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid,
+ 0 /* lastTouchedTime */);
+ }
}
});
}
@@ -386,48 +370,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void removeBigramDynamically(final String word0, final String word1) {
if (!mIsUpdatable) {
Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: "
- + mDictName);
+ + mFilename);
return;
}
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
- runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
- mBinaryDictionary.removeBigramWords(word0, word1);
- }
- });
- }
-
- public interface AddMultipleDictionaryEntriesCallback {
- public void onFinished();
- }
-
- /**
- * Dynamically add multiple entries to the dictionary.
- */
- protected void addMultipleDictionaryEntriesDynamically(
- final ArrayList<LanguageModelParam> languageModelParams,
- final AddMultipleDictionaryEntriesCallback callback) {
- if (!mIsUpdatable) {
- Log.w(TAG, "addMultipleDictionaryEntriesDynamically is called for non-updatable " +
- "dictionary: " + mDictName);
- return;
- }
- getExecutor(mDictName).execute(new Runnable() {
- @Override
- public void run() {
- final boolean locked = setProcessingLargeTaskIfNot();
- try {
- mBinaryDictionary.addMultipleDictionaryEntries(
- languageModelParams.toArray(
- new LanguageModelParam[languageModelParams.size()]));
- } finally {
- if (callback != null) {
- callback.onFinished();
- }
- if (locked) {
- mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
- }
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
+ mBinaryDictionary.removeBigramWords(word0, word1);
+ } else {
+ // TODO: Remove.
+ mDictionaryWriter.removeBigramWords(word0, word1);
}
}
});
@@ -439,23 +393,49 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId) {
reloadDictionaryIfRequired();
- if (processingLargeTask()) {
+ if (isRegenerating()) {
return null;
}
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
- getExecutor(mDictName).executePrioritized(new Runnable() {
+ getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
public void run() {
- if (mBinaryDictionary == null) {
- holder.set(null);
- return;
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ if (mBinaryDictionary == null) {
+ holder.set(null);
+ return;
+ }
+ final ArrayList<SuggestedWordInfo> binarySuggestion =
+ mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
+ proximityInfo, blockOffensiveWords, additionalFeaturesOptions,
+ sessionId);
+ holder.set(binarySuggestion);
+ } else {
+ final ArrayList<SuggestedWordInfo> inMemDictSuggestion =
+ composer.isBatchMode() ? null :
+ mDictionaryWriter.getSuggestionsWithSessionId(composer,
+ prevWord, proximityInfo, blockOffensiveWords,
+ additionalFeaturesOptions, sessionId);
+ // TODO: Remove checking mIsUpdatable and use native suggestion.
+ if (mBinaryDictionary != null && !mIsUpdatable) {
+ final ArrayList<SuggestedWordInfo> binarySuggestion =
+ mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
+ proximityInfo, blockOffensiveWords,
+ additionalFeaturesOptions, sessionId);
+ if (inMemDictSuggestion == null) {
+ holder.set(binarySuggestion);
+ } else if (binarySuggestion == null) {
+ holder.set(inMemDictSuggestion);
+ } else {
+ binarySuggestion.addAll(inMemDictSuggestion);
+ holder.set(binarySuggestion);
+ }
+ } else {
+ holder.set(inMemDictSuggestion);
+ }
}
- final ArrayList<SuggestedWordInfo> binarySuggestion =
- mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
- proximityInfo, blockOffensiveWords, additionalFeaturesOptions,
- sessionId);
- holder.set(binarySuggestion);
}
});
return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
@@ -476,11 +456,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
protected boolean isValidWordInner(final String word) {
- if (processingLargeTask()) {
+ if (isRegenerating()) {
return false;
}
final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
- getExecutor(mDictName).executePrioritized(new Runnable() {
+ getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
public void run() {
holder.set(isValidWordLocked(word));
@@ -514,22 +494,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
private void loadBinaryDictionary() {
if (DEBUG) {
- Log.d(TAG, "Loading binary dictionary: " + mDictName + " request="
- + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update="
- + mDictNameDictionaryUpdateController.mLastUpdateTime);
- }
- if (DBG_STRESS_TEST) {
- // Test if this class does not cause problems when it takes long time to load binary
- // dictionary.
- try {
- Log.w(TAG, "Start stress in loading: " + mDictName);
- Thread.sleep(15000);
- Log.w(TAG, "End stress in loading");
- } catch (InterruptedException e) {
- }
+ Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
+ + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ + mFilenameDictionaryUpdateController.mLastUpdateTime);
}
- final File file = getDictFile();
+ final File file = new File(mContext.getFilesDir(), mFilename);
final String filename = file.getAbsolutePath();
final long length = file.length();
@@ -541,7 +511,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// swapping in the new one.
// TODO: Ensure multi-thread assignment of mBinaryDictionary.
final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
- getExecutor(mDictName).executePrioritized(new Runnable() {
+ getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
public void run() {
mBinaryDictionary = newBinaryDictionary;
@@ -563,31 +533,29 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
private void writeBinaryDictionary() {
if (DEBUG) {
- Log.d(TAG, "Generating binary dictionary: " + mDictName + " request="
- + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update="
- + mDictNameDictionaryUpdateController.mLastUpdateTime);
+ Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
+ + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ + mFilenameDictionaryUpdateController.mLastUpdateTime);
}
if (needsToReloadBeforeWriting()) {
mDictionaryWriter.clear();
loadDictionaryAsync();
- mDictionaryWriter.write(getDictFile(), getHeaderAttributeMap());
+ mDictionaryWriter.write(mFilename, getHeaderAttributeMap());
} else {
- if (mBinaryDictionary == null || !isValidDictionary()
- // TODO: remove the check below
- || !matchesExpectedBinaryDictFormatVersionForThisType(
- mBinaryDictionary.getFormatVersion())) {
- final File file = getDictFile();
- if (file.exists() && !FileUtils.deleteRecursively(file)) {
- Log.e(TAG, "Can't remove a file: " + file.getName());
- }
- BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
- DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
- } else {
- if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
- mBinaryDictionary.flushWithGC();
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ if (mBinaryDictionary == null || !mBinaryDictionary.isValidDictionary()) {
+ final File file = new File(mContext.getFilesDir(), mFilename);
+ BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
+ DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
} else {
- mBinaryDictionary.flush();
+ if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ mBinaryDictionary.flushWithGC();
+ } else {
+ mBinaryDictionary.flush();
+ }
}
+ } else {
+ mDictionaryWriter.write(mFilename, getHeaderAttributeMap());
}
}
}
@@ -602,10 +570,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void setRequiresReload(final boolean requiresRebuild) {
final long time = SystemClock.uptimeMillis();
mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time;
- mDictNameDictionaryUpdateController.mLastUpdateRequestTime = time;
+ mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time;
if (DEBUG) {
- Log.d(TAG, "Reload request: " + mDictName + ": request=" + time + " update="
- + mDictNameDictionaryUpdateController.mLastUpdateTime);
+ Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update="
+ + mFilenameDictionaryUpdateController.mLastUpdateTime);
}
}
@@ -614,7 +582,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
public final void reloadDictionaryIfRequired() {
if (!isReloadRequired()) return;
- if (setProcessingLargeTaskIfNot()) {
+ if (setIsRegeneratingIfNotRegenerating()) {
reloadDictionary();
}
}
@@ -626,14 +594,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.isOutOfDate();
}
- private boolean processingLargeTask() {
- return mDictNameDictionaryUpdateController.mProcessingLargeTask.get();
+ private boolean isRegenerating() {
+ return mFilenameDictionaryUpdateController.mIsRegenerating.get();
}
- // Returns whether the dictionary is being used for a large task. If true, we should not use
- // this dictionary for latency sensitive operations.
- private boolean setProcessingLargeTaskIfNot() {
- return mDictNameDictionaryUpdateController.mProcessingLargeTask.compareAndSet(
+ // Returns whether the dictionary can be regenerated.
+ private boolean setIsRegeneratingIfNotRegenerating() {
+ return mFilenameDictionaryUpdateController.mIsRegenerating.compareAndSet(
false /* expect */ , true /* update */);
}
@@ -644,13 +611,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private final void reloadDictionary() {
// Ensure that only one thread attempts to read or write to the shared binary dictionary
// file at the same time.
- getExecutor(mDictName).execute(new Runnable() {
+ getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
try {
final long time = SystemClock.uptimeMillis();
final boolean dictionaryFileExists = dictionaryFileExists();
- if (mDictNameDictionaryUpdateController.isOutOfDate()
+ if (mFilenameDictionaryUpdateController.isOutOfDate()
|| !dictionaryFileExists) {
// If the shared dictionary file does not exist or is out of date, the
// first instance that acquires the lock will generate a new one.
@@ -659,44 +626,31 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// rebuild the binary dictionary. Empty dictionaries are supported (in
// the case where loadDictionaryAsync() adds nothing) in order to
// provide a uniform framework.
- mDictNameDictionaryUpdateController.mLastUpdateTime = time;
+ mFilenameDictionaryUpdateController.mLastUpdateTime = time;
writeBinaryDictionary();
loadBinaryDictionary();
} else {
// If not, the reload request was unnecessary so revert
// LastUpdateRequestTime to LastUpdateTime.
- mDictNameDictionaryUpdateController.mLastUpdateRequestTime =
- mDictNameDictionaryUpdateController.mLastUpdateTime;
+ mFilenameDictionaryUpdateController.mLastUpdateRequestTime =
+ mFilenameDictionaryUpdateController.mLastUpdateTime;
}
} else if (mBinaryDictionary == null ||
mPerInstanceDictionaryUpdateController.mLastUpdateTime
- < mDictNameDictionaryUpdateController.mLastUpdateTime) {
+ < mFilenameDictionaryUpdateController.mLastUpdateTime) {
// Otherwise, if the local dictionary is older than the shared dictionary,
// load the shared dictionary.
loadBinaryDictionary();
}
- // If we just loaded the binary dictionary, then mBinaryDictionary is not
- // up-to-date yet so it's useless to test it right away. Schedule the check
- // for right after it's loaded instead.
- getExecutor(mDictName).executePrioritized(new Runnable() {
- @Override
- public void run() {
- if (mBinaryDictionary != null && !(isValidDictionary()
- // TODO: remove the check below
- && matchesExpectedBinaryDictFormatVersionForThisType(
- mBinaryDictionary.getFormatVersion()))) {
- // Binary dictionary or its format version is not valid. Regenerate
- // the dictionary file. writeBinaryDictionary will remove the
- // existing files if appropriate.
- mDictNameDictionaryUpdateController.mLastUpdateTime = time;
- writeBinaryDictionary();
- loadBinaryDictionary();
- }
- mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
- }
- });
+ if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
+ // Binary dictionary is not valid. Regenerate the dictionary file.
+ mFilenameDictionaryUpdateController.mLastUpdateTime = time;
+ writeBinaryDictionary();
+ loadBinaryDictionary();
+ }
+ mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
} finally {
- mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
+ mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
}
}
});
@@ -704,13 +658,28 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// TODO: cache the file's existence so that we avoid doing a disk access each time.
private boolean dictionaryFileExists() {
- return getDictFile().exists();
+ final File file = new File(mContext.getFilesDir(), mFilename);
+ return file.exists();
+ }
+
+ /**
+ * Load the dictionary to memory.
+ */
+ protected void asyncLoadDictionaryToMemory() {
+ getExecutor(mFilename).executePrioritized(new Runnable() {
+ @Override
+ public void run() {
+ if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ loadDictionaryAsync();
+ }
+ }
+ });
}
/**
* Generate binary dictionary using DictionaryWriter.
*/
- protected void asyncFlushBinaryDictionary() {
+ protected void asyncFlashAllBinaryDictionary() {
final Runnable newTask = new Runnable() {
@Override
public void run() {
@@ -718,32 +687,37 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
};
final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask);
- getExecutor(mDictName).replaceAndExecute(oldTask, newTask);
+ getExecutor(mFilename).replaceAndExecute(oldTask, newTask);
}
/**
- * For tracking whether the dictionary is out of date and the dictionary is used in a large
- * task. Can be shared across multiple dictionary instances that access the same filename.
+ * For tracking whether the dictionary is out of date and the dictionary is regenerating.
+ * Can be shared across multiple dictionary instances that access the same filename.
*/
private static class DictionaryUpdateController {
public volatile long mLastUpdateTime = 0;
public volatile long mLastUpdateRequestTime = 0;
- public volatile AtomicBoolean mProcessingLargeTask = new AtomicBoolean();
+ public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean();
public boolean isOutOfDate() {
return (mLastUpdateRequestTime > mLastUpdateTime);
}
}
- // TODO: Implement BinaryDictionary.isInDictionary().
+ // TODO: Implement native binary methods once the dynamic dictionary implementation is done.
@UsedForTesting
public boolean isInDictionaryForTests(final String word) {
final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
- getExecutor(mDictName).executePrioritized(new Runnable() {
+ getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
public void run() {
if (mDictType == Dictionary.TYPE_USER_HISTORY) {
- holder.set(mBinaryDictionary.isValidWord(word));
+ if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ holder.set(mBinaryDictionary.isValidWord(word));
+ } else {
+ holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter)
+ .isInBigramListForTests(word));
+ }
}
}
});
@@ -751,33 +725,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@UsedForTesting
- public void waitAllTasksForTests() {
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- getExecutor(mDictName).execute(new Runnable() {
- @Override
- public void run() {
- countDownLatch.countDown();
- }
- });
- try {
- countDownLatch.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e);
- }
+ public void shutdownExecutorForTests() {
+ getExecutor(mFilename).shutdown();
}
@UsedForTesting
- protected void runAfterGcForDebug(final Runnable r) {
- getExecutor(mDictName).executePrioritized(new Runnable() {
- @Override
- public void run() {
- try {
- mBinaryDictionary.flushWithGC();
- r.run();
- } finally {
- mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false);
- }
- }
- });
+ public boolean isTerminatedForTests() {
+ return getExecutor(mFilename).isTerminated();
}
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
new file mode 100644
index 000000000..95c9bcab9
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -0,0 +1,894 @@
+/*
+ * Copyright (C) 2009 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.text.TextUtils;
+import android.util.Log;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * Class for an in-memory dictionary that can grow dynamically and can
+ * be searched for suggestions and valid words.
+ */
+// TODO: Remove after binary dictionary supports dynamic update.
+public class ExpandableDictionary extends Dictionary {
+ private static final String TAG = ExpandableDictionary.class.getSimpleName();
+ /**
+ * The weight to give to a word if it's length is the same as the number of typed characters.
+ */
+ private static final int FULL_WORD_SCORE_MULTIPLIER = 2;
+
+ private char[] mWordBuilder = new char[Constants.DICTIONARY_MAX_WORD_LENGTH];
+ private int mMaxDepth;
+ private int mInputLength;
+
+ private static final class Node {
+ char mCode;
+ int mFrequency;
+ boolean mTerminal;
+ Node mParent;
+ NodeArray mChildren;
+ ArrayList<char[]> mShortcutTargets;
+ boolean mShortcutOnly;
+ LinkedList<NextWord> mNGrams; // Supports ngram
+ }
+
+ private static final class NodeArray {
+ Node[] mData;
+ int mLength = 0;
+ private static final int INCREMENT = 2;
+
+ NodeArray() {
+ mData = new Node[INCREMENT];
+ }
+
+ void add(final Node n) {
+ if (mLength + 1 > mData.length) {
+ Node[] tempData = new Node[mLength + INCREMENT];
+ if (mLength > 0) {
+ System.arraycopy(mData, 0, tempData, 0, mLength);
+ }
+ mData = tempData;
+ }
+ mData[mLength++] = n;
+ }
+ }
+
+ public interface NextWord {
+ public Node getWordNode();
+ public int getFrequency();
+ public ForgettingCurveParams getFcParams();
+ public int notifyTypedAgainAndGetFrequency();
+ }
+
+ private static final class NextStaticWord implements NextWord {
+ public final Node mWord;
+ private final int mFrequency;
+ public NextStaticWord(Node word, int frequency) {
+ mWord = word;
+ mFrequency = frequency;
+ }
+
+ @Override
+ public Node getWordNode() {
+ return mWord;
+ }
+
+ @Override
+ public int getFrequency() {
+ return mFrequency;
+ }
+
+ @Override
+ public ForgettingCurveParams getFcParams() {
+ return null;
+ }
+
+ @Override
+ public int notifyTypedAgainAndGetFrequency() {
+ return mFrequency;
+ }
+ }
+
+ private static final class NextHistoryWord implements NextWord {
+ public final Node mWord;
+ public final ForgettingCurveParams mFcp;
+
+ public NextHistoryWord(Node word, ForgettingCurveParams fcp) {
+ mWord = word;
+ mFcp = fcp;
+ }
+
+ @Override
+ public Node getWordNode() {
+ return mWord;
+ }
+
+ @Override
+ public int getFrequency() {
+ return mFcp.getFrequency();
+ }
+
+ @Override
+ public ForgettingCurveParams getFcParams() {
+ return mFcp;
+ }
+
+ @Override
+ public int notifyTypedAgainAndGetFrequency() {
+ return mFcp.notifyTypedAgainAndGetFrequency();
+ }
+ }
+
+ private NodeArray mRoots;
+
+ private int[][] mCodes;
+
+ public ExpandableDictionary(final String dictType) {
+ super(dictType);
+ clearDictionary();
+ mCodes = new int[Constants.DICTIONARY_MAX_WORD_LENGTH][];
+ }
+
+ public int getMaxWordLength() {
+ return Constants.DICTIONARY_MAX_WORD_LENGTH;
+ }
+
+ /**
+ * Add a word with an optional shortcut to the dictionary.
+ * @param word The word to add.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ */
+ public void addWord(final String word, final String shortcutTarget, final int frequency,
+ final int shortcutFreq) {
+ if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH) {
+ return;
+ }
+ addWordRec(mRoots, word, 0, shortcutTarget, frequency, shortcutFreq, null);
+ }
+
+ /**
+ * Add a word, recursively searching for its correct place in the trie tree.
+ * @param children The node to recursively search for addition. Initially, the root of the tree.
+ * @param word The word to add.
+ * @param depth The current depth in the tree.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ * @param parentNode The parent node, for up linking. Initially null, as the root has no parent.
+ */
+ private void addWordRec(final NodeArray children, final String word, final int depth,
+ final String shortcutTarget, final int frequency, final int shortcutFreq,
+ final Node parentNode) {
+ final int wordLength = word.length();
+ if (wordLength <= depth) return;
+ final char c = word.charAt(depth);
+ // Does children have the current character?
+ final int childrenLength = children.mLength;
+ Node childNode = null;
+ for (int i = 0; i < childrenLength; i++) {
+ final Node node = children.mData[i];
+ if (node.mCode == c) {
+ childNode = node;
+ break;
+ }
+ }
+ final boolean isShortcutOnly = (null != shortcutTarget);
+ if (childNode == null) {
+ childNode = new Node();
+ childNode.mCode = c;
+ childNode.mParent = parentNode;
+ childNode.mShortcutOnly = isShortcutOnly;
+ children.add(childNode);
+ }
+ if (wordLength == depth + 1) {
+ // Terminate this word
+ childNode.mTerminal = true;
+ if (isShortcutOnly) {
+ if (null == childNode.mShortcutTargets) {
+ childNode.mShortcutTargets = CollectionUtils.newArrayList();
+ }
+ childNode.mShortcutTargets.add(shortcutTarget.toCharArray());
+ } else {
+ childNode.mShortcutOnly = false;
+ }
+ childNode.mFrequency = Math.max(frequency, childNode.mFrequency);
+ if (childNode.mFrequency > 255) childNode.mFrequency = 255;
+ return;
+ }
+ if (childNode.mChildren == null) {
+ childNode.mChildren = new NodeArray();
+ }
+ addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, shortcutFreq,
+ childNode);
+ }
+
+ @Override
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final String prevWord, final ProximityInfo proximityInfo,
+ final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+ if (composer.size() > 1) {
+ if (composer.size() >= Constants.DICTIONARY_MAX_WORD_LENGTH) {
+ return null;
+ }
+ final ArrayList<SuggestedWordInfo> suggestions =
+ getWordsInner(composer, prevWord, proximityInfo);
+ return suggestions;
+ } else {
+ if (TextUtils.isEmpty(prevWord)) return null;
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ runBigramReverseLookUp(prevWord, suggestions);
+ return suggestions;
+ }
+ }
+
+ private ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ mInputLength = codes.size();
+ if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
+ final InputPointers ips = codes.getInputPointers();
+ final int[] xCoordinates = ips.getXCoordinates();
+ final int[] yCoordinates = ips.getYCoordinates();
+ // Cache the codes so that we don't have to lookup an array list
+ for (int i = 0; i < mInputLength; i++) {
+ // TODO: Calculate proximity info here.
+ if (mCodes[i] == null || mCodes[i].length < 1) {
+ mCodes[i] = new int[ProximityInfo.MAX_PROXIMITY_CHARS_SIZE];
+ }
+ final int x = xCoordinates != null && i < xCoordinates.length ?
+ xCoordinates[i] : Constants.NOT_A_COORDINATE;
+ final int y = xCoordinates != null && i < yCoordinates.length ?
+ yCoordinates[i] : Constants.NOT_A_COORDINATE;
+ proximityInfo.fillArrayWithNearestKeyCodes(x, y, codes.getCodeAt(i), mCodes[i]);
+ }
+ mMaxDepth = mInputLength * 3;
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, suggestions);
+ for (int i = 0; i < mInputLength; i++) {
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, suggestions);
+ }
+ return suggestions;
+ }
+
+ @Override
+ public synchronized boolean isValidWord(final String word) {
+ final Node node = searchNode(mRoots, word, 0, word.length());
+ // If node is null, we didn't find the word, so it's not valid.
+ // If node.mShortcutOnly is true, then it exists as a shortcut but not as a word,
+ // so that means it's not a valid word.
+ // If node.mShortcutOnly is false, then it exists as a word (it may also exist as
+ // a shortcut, but this does not matter), so it's a valid word.
+ return (node == null) ? false : !node.mShortcutOnly;
+ }
+
+ public boolean removeBigram(final String word0, final String word1) {
+ // Refer to addOrSetBigram() about word1.toLowerCase()
+ final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null);
+ final Node secondWord = searchWord(mRoots, word1, 0, null);
+ LinkedList<NextWord> bigrams = firstWord.mNGrams;
+ NextWord bigramNode = null;
+ if (bigrams == null || bigrams.size() == 0) {
+ return false;
+ } else {
+ for (NextWord nw : bigrams) {
+ if (nw.getWordNode() == secondWord) {
+ bigramNode = nw;
+ break;
+ }
+ }
+ }
+ if (bigramNode == null) {
+ return false;
+ }
+ return bigrams.remove(bigramNode);
+ }
+
+ /**
+ * Returns the word's frequency or -1 if not found
+ */
+ @UsedForTesting
+ public int getWordFrequency(final String word) {
+ // Case-sensitive search
+ final Node node = searchNode(mRoots, word, 0, word.length());
+ return (node == null) ? -1 : node.mFrequency;
+ }
+
+ public NextWord getBigramWord(final String word0, final String word1) {
+ // Refer to addOrSetBigram() about word0.toLowerCase()
+ final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null);
+ final Node secondWord = searchWord(mRoots, word1, 0, null);
+ LinkedList<NextWord> bigrams = firstWord.mNGrams;
+ if (bigrams == null || bigrams.size() == 0) {
+ return null;
+ } else {
+ for (NextWord nw : bigrams) {
+ if (nw.getWordNode() == secondWord) {
+ return nw;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static int computeSkippedWordFinalFreq(final int freq, final int snr,
+ final int inputLength) {
+ // The computation itself makes sense for >= 2, but the == 2 case returns 0
+ // anyway so we may as well test against 3 instead and return the constant
+ if (inputLength >= 3) {
+ return (freq * snr * (inputLength - 2)) / (inputLength - 1);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Helper method to add a word and its shortcuts.
+ *
+ * @param node the terminal node
+ * @param word the word to insert, as an array of code points
+ * @param depth the depth of the node in the tree
+ * @param finalFreq the frequency for this word
+ * @param suggestions the suggestion collection to add the suggestions to
+ * @return whether there is still space for more words.
+ */
+ private boolean addWordAndShortcutsFromNode(final Node node, final char[] word, final int depth,
+ final int finalFreq, final ArrayList<SuggestedWordInfo> suggestions) {
+ if (finalFreq > 0 && !node.mShortcutOnly) {
+ // Use KIND_CORRECTION always. This dictionary does not really have a notion of
+ // COMPLETION against CORRECTION; we could artificially add one by looking at
+ // the respective size of the typed word and the suggestion if it matters sometime
+ // in the future.
+ suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq,
+ SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */,
+ SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+ SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
+ if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false;
+ }
+ if (null != node.mShortcutTargets) {
+ final int length = node.mShortcutTargets.size();
+ for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) {
+ final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
+ suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length),
+ finalFreq, SuggestedWordInfo.KIND_SHORTCUT, this /* sourceDict */,
+ SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+ SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
+ if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Recursively traverse the tree for words that match the input. Input consists of
+ * a list of arrays. Each item in the list is one input character position. An input
+ * character is actually an array of multiple possible candidates. This function is not
+ * optimized for speed, assuming that the user dictionary will only be a few hundred words in
+ * size.
+ * @param roots node whose children have to be search for matches
+ * @param codes the input character codes
+ * @param word the word being composed as a possible match
+ * @param depth the depth of traversal - the length of the word being composed thus far
+ * @param completion whether the traversal is now in completion mode - meaning that we've
+ * exhausted the input and we're looking for all possible suffixes.
+ * @param snr current weight of the word being formed
+ * @param inputIndex position in the input characters. This can be off from the depth in
+ * case we skip over some punctuations such as apostrophe in the traversal. That is, if you type
+ * "wouldve", it could be matching "would've", so the depth will be one more than the
+ * inputIndex
+ * @param suggestions the list in which to add suggestions
+ */
+ // TODO: Share this routine with the native code for BinaryDictionary
+ private void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word,
+ final int depth, final boolean completion, final int snr, final int inputIndex,
+ final int skipPos, final ArrayList<SuggestedWordInfo> suggestions) {
+ final int count = roots.mLength;
+ final int codeSize = mInputLength;
+ // Optimization: Prune out words that are too long compared to how much was typed.
+ if (depth > mMaxDepth) {
+ return;
+ }
+ final int[] currentChars;
+ if (codeSize <= inputIndex) {
+ currentChars = null;
+ } else {
+ currentChars = mCodes[inputIndex];
+ }
+
+ for (int i = 0; i < count; i++) {
+ final Node node = roots.mData[i];
+ final char c = node.mCode;
+ final char lowerC = toLowerCase(c);
+ final boolean terminal = node.mTerminal;
+ final NodeArray children = node.mChildren;
+ final int freq = node.mFrequency;
+ if (completion || currentChars == null) {
+ word[depth] = c;
+ if (terminal) {
+ final int finalFreq;
+ if (skipPos < 0) {
+ finalFreq = freq * snr;
+ } else {
+ finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength);
+ }
+ if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, suggestions)) {
+ // No space left in the queue, bail out
+ return;
+ }
+ }
+ if (children != null) {
+ getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex,
+ skipPos, suggestions);
+ }
+ } else if ((c == Constants.CODE_SINGLE_QUOTE
+ && currentChars[0] != Constants.CODE_SINGLE_QUOTE) || depth == skipPos) {
+ // Skip the ' and continue deeper
+ word[depth] = c;
+ if (children != null) {
+ getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
+ skipPos, suggestions);
+ }
+ } else {
+ // Don't use alternatives if we're looking for missing characters
+ final int alternativesSize = skipPos >= 0 ? 1 : currentChars.length;
+ for (int j = 0; j < alternativesSize; j++) {
+ final int addedAttenuation = (j > 0 ? 1 : 2);
+ final int currentChar = currentChars[j];
+ if (currentChar == Constants.NOT_A_CODE) {
+ break;
+ }
+ if (currentChar == lowerC || currentChar == c) {
+ word[depth] = c;
+
+ if (codeSize == inputIndex + 1) {
+ if (terminal) {
+ final int finalFreq;
+ if (skipPos < 0) {
+ finalFreq = freq * snr * addedAttenuation
+ * FULL_WORD_SCORE_MULTIPLIER;
+ } else {
+ finalFreq = computeSkippedWordFinalFreq(freq,
+ snr * addedAttenuation, mInputLength);
+ }
+ if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq,
+ suggestions)) {
+ // No space left in the queue, bail out
+ return;
+ }
+ }
+ if (children != null) {
+ getWordsRec(children, codes, word, depth + 1,
+ true, snr * addedAttenuation, inputIndex + 1,
+ skipPos, suggestions);
+ }
+ } else if (children != null) {
+ getWordsRec(children, codes, word, depth + 1,
+ false, snr * addedAttenuation, inputIndex + 1,
+ skipPos, suggestions);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public int setBigramAndGetFrequency(final String word0, final String word1,
+ final int frequency) {
+ return setBigramAndGetFrequency(word0, word1, frequency, null /* unused */);
+ }
+
+ public int setBigramAndGetFrequency(final String word0, final String word1,
+ final ForgettingCurveParams fcp) {
+ return setBigramAndGetFrequency(word0, word1, 0 /* unused */, fcp);
+ }
+
+ /**
+ * Adds bigrams to the in-memory trie structure that is being used to retrieve any word
+ * @param word0 the first word of this bigram
+ * @param word1 the second word of this bigram
+ * @param frequency frequency for this bigram
+ * @param fcp an instance of ForgettingCurveParams to use for decay policy
+ * @return returns the final bigram frequency
+ */
+ private int setBigramAndGetFrequency(final String word0, final String word1,
+ final int frequency, final ForgettingCurveParams fcp) {
+ if (TextUtils.isEmpty(word0)) {
+ Log.e(TAG, "Invalid bigram previous word: " + word0);
+ return frequency;
+ }
+ // We don't want results to be different according to case of the looked up left hand side
+ // word. We do want however to return the correct case for the right hand side.
+ // So we want to squash the case of the left hand side, and preserve that of the right
+ // hand side word.
+ final String word0Lower = word0.toLowerCase();
+ if (TextUtils.isEmpty(word0Lower) || TextUtils.isEmpty(word1)) {
+ Log.e(TAG, "Invalid bigram pair: " + word0 + ", " + word0Lower + ", " + word1);
+ return frequency;
+ }
+ final Node firstWord = searchWord(mRoots, word0Lower, 0, null);
+ final Node secondWord = searchWord(mRoots, word1, 0, null);
+ LinkedList<NextWord> bigrams = firstWord.mNGrams;
+ if (bigrams == null || bigrams.size() == 0) {
+ firstWord.mNGrams = CollectionUtils.newLinkedList();
+ bigrams = firstWord.mNGrams;
+ } else {
+ for (NextWord nw : bigrams) {
+ if (nw.getWordNode() == secondWord) {
+ return nw.notifyTypedAgainAndGetFrequency();
+ }
+ }
+ }
+ if (fcp != null) {
+ // history
+ firstWord.mNGrams.add(new NextHistoryWord(secondWord, fcp));
+ } else {
+ firstWord.mNGrams.add(new NextStaticWord(secondWord, frequency));
+ }
+ return frequency;
+ }
+
+ /**
+ * Searches for the word and add the word if it does not exist.
+ * @return Returns the terminal node of the word we are searching for.
+ */
+ private Node searchWord(final NodeArray children, final String word, final int depth,
+ final Node parentNode) {
+ final int wordLength = word.length();
+ final char c = word.charAt(depth);
+ // Does children have the current character?
+ final int childrenLength = children.mLength;
+ Node childNode = null;
+ for (int i = 0; i < childrenLength; i++) {
+ final Node node = children.mData[i];
+ if (node.mCode == c) {
+ childNode = node;
+ break;
+ }
+ }
+ if (childNode == null) {
+ childNode = new Node();
+ childNode.mCode = c;
+ childNode.mParent = parentNode;
+ children.add(childNode);
+ }
+ if (wordLength == depth + 1) {
+ // Terminate this word
+ childNode.mTerminal = true;
+ return childNode;
+ }
+ if (childNode.mChildren == null) {
+ childNode.mChildren = new NodeArray();
+ }
+ return searchWord(childNode.mChildren, word, depth + 1, childNode);
+ }
+
+ private void runBigramReverseLookUp(final String previousWord,
+ final ArrayList<SuggestedWordInfo> suggestions) {
+ // Search for the lowercase version of the word only, because that's where bigrams
+ // store their sons.
+ final Node prevWord = searchNode(mRoots, previousWord.toLowerCase(), 0,
+ previousWord.length());
+ if (prevWord != null && prevWord.mNGrams != null) {
+ reverseLookUp(prevWord.mNGrams, suggestions);
+ }
+ }
+
+ // Local to reverseLookUp, but do not allocate each time.
+ private final char[] mLookedUpString = new char[Constants.DICTIONARY_MAX_WORD_LENGTH];
+
+ /**
+ * reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
+ * to the suggestions list passed as an argument.
+ * @param terminalNodes list of terminal nodes we want to add
+ * @param suggestions the suggestion collection to add the word to
+ */
+ private void reverseLookUp(final LinkedList<NextWord> terminalNodes,
+ final ArrayList<SuggestedWordInfo> suggestions) {
+ Node node;
+ int freq;
+ for (NextWord nextWord : terminalNodes) {
+ node = nextWord.getWordNode();
+ freq = nextWord.getFrequency();
+ int index = Constants.DICTIONARY_MAX_WORD_LENGTH;
+ do {
+ --index;
+ mLookedUpString[index] = node.mCode;
+ node = node.mParent;
+ } while (node != null && index > 0);
+
+ // If node is null, we have a word longer than MAX_WORD_LENGTH in the dictionary.
+ // It's a little unclear how this can happen, but just in case it does it's safer
+ // to ignore the word in this case.
+ if (freq >= 0 && node == null) {
+ suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
+ Constants.DICTIONARY_MAX_WORD_LENGTH - index),
+ freq, SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */,
+ SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+ SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
+ }
+ }
+ }
+
+ /**
+ * Recursively search for the terminal node of the word.
+ *
+ * One iteration takes the full word to search for and the current index of the recursion.
+ *
+ * @param children the node of the trie to search under.
+ * @param word the word to search for. Only read [offset..length] so there may be trailing chars
+ * @param offset the index in {@code word} this recursion should operate on.
+ * @param length the length of the input word.
+ * @return Returns the terminal node of the word if the word exists
+ */
+ private Node searchNode(final NodeArray children, final CharSequence word, final int offset,
+ final int length) {
+ final int count = children.mLength;
+ final char currentChar = word.charAt(offset);
+ for (int j = 0; j < count; j++) {
+ final Node node = children.mData[j];
+ if (node.mCode == currentChar) {
+ if (offset == length - 1) {
+ if (node.mTerminal) {
+ return node;
+ }
+ } else {
+ if (node.mChildren != null) {
+ Node returnNode = searchNode(node.mChildren, word, offset + 1, length);
+ if (returnNode != null) return returnNode;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void clearDictionary() {
+ mRoots = new NodeArray();
+ }
+
+ private static char toLowerCase(final char c) {
+ char baseChar = c;
+ if (c < BASE_CHARS.length) {
+ baseChar = BASE_CHARS[c];
+ }
+ if (baseChar >= 'A' && baseChar <= 'Z') {
+ return (char)(baseChar | 32);
+ } else if (baseChar > 127) {
+ return Character.toLowerCase(baseChar);
+ }
+ return baseChar;
+ }
+
+ /**
+ * Table mapping most combined Latin, Greek, and Cyrillic characters
+ * to their base characters. If c is in range, BASE_CHARS[c] == c
+ * if c is not a combined character, or the base character if it
+ * is combined.
+ *
+ * cf. native/jni/src/utils/char_utils.cpp
+ */
+ private static final char BASE_CHARS[] = {
+ /* U+0000 */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ /* U+0008 */ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ /* U+0010 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ /* U+0018 */ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ /* U+0020 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ /* U+0028 */ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ /* U+0030 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ /* U+0038 */ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ /* U+0040 */ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ /* U+0048 */ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+ /* U+0050 */ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ /* U+0058 */ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ /* U+0060 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ /* U+0068 */ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* U+0070 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ /* U+0078 */ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ /* U+0080 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ /* U+0088 */ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ /* U+0090 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ /* U+0098 */ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ /* U+00A0 */ 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ /* U+00A8 */ 0x0020, 0x00A9, 0x0061, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0020,
+ /* U+00B0 */ 0x00B0, 0x00B1, 0x0032, 0x0033, 0x0020, 0x03BC, 0x00B6, 0x00B7,
+ /* U+00B8 */ 0x0020, 0x0031, 0x006F, 0x00BB, 0x0031, 0x0031, 0x0033, 0x00BF,
+ /* U+00C0 */ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00C6, 0x0043,
+ /* U+00C8 */ 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049,
+ /* U+00D0 */ 0x00D0, 0x004E, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x00D7,
+ /* U+00D8 */ 0x004F, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00DE, 0x0073,
+ // U+00D8: Manually changed from 00D8 to 004F
+ // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O
+ // U+00DF: Manually changed from 00DF to 0073
+ /* U+00E0 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00E6, 0x0063,
+ /* U+00E8 */ 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069,
+ /* U+00F0 */ 0x00F0, 0x006E, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x00F7,
+ /* U+00F8 */ 0x006F, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00FE, 0x0079,
+ // U+00F8: Manually changed from 00F8 to 006F
+ // TODO: Check if it's really acceptable to consider ø a diacritical variant of o
+ /* U+0100 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063,
+ /* U+0108 */ 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064,
+ /* U+0110 */ 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065,
+ /* U+0118 */ 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067,
+ /* U+0120 */ 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127,
+ /* U+0128 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069,
+ /* U+0130 */ 0x0049, 0x0131, 0x0049, 0x0069, 0x004A, 0x006A, 0x004B, 0x006B,
+ /* U+0138 */ 0x0138, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C,
+ /* U+0140 */ 0x006C, 0x004C, 0x006C, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E,
+ // U+0141: Manually changed from 0141 to 004C
+ // U+0142: Manually changed from 0142 to 006C
+ /* U+0148 */ 0x006E, 0x02BC, 0x014A, 0x014B, 0x004F, 0x006F, 0x004F, 0x006F,
+ /* U+0150 */ 0x004F, 0x006F, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072,
+ /* U+0158 */ 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073,
+ /* U+0160 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167,
+ /* U+0168 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075,
+ /* U+0170 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079,
+ /* U+0178 */ 0x0059, 0x005A, 0x007A, 0x005A, 0x007A, 0x005A, 0x007A, 0x0073,
+ /* U+0180 */ 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187,
+ /* U+0188 */ 0x0188, 0x0189, 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F,
+ /* U+0190 */ 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197,
+ /* U+0198 */ 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F,
+ /* U+01A0 */ 0x004F, 0x006F, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7,
+ /* U+01A8 */ 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x0055,
+ /* U+01B0 */ 0x0075, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7,
+ /* U+01B8 */ 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF,
+ /* U+01C0 */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x0044, 0x0044, 0x0064, 0x004C,
+ /* U+01C8 */ 0x004C, 0x006C, 0x004E, 0x004E, 0x006E, 0x0041, 0x0061, 0x0049,
+ /* U+01D0 */ 0x0069, 0x004F, 0x006F, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055,
+ // U+01D5: Manually changed from 00DC to 0055
+ // U+01D6: Manually changed from 00FC to 0075
+ // U+01D7: Manually changed from 00DC to 0055
+ /* U+01D8 */ 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x01DD, 0x0041, 0x0061,
+ // U+01D8: Manually changed from 00FC to 0075
+ // U+01D9: Manually changed from 00DC to 0055
+ // U+01DA: Manually changed from 00FC to 0075
+ // U+01DB: Manually changed from 00DC to 0055
+ // U+01DC: Manually changed from 00FC to 0075
+ // U+01DE: Manually changed from 00C4 to 0041
+ // U+01DF: Manually changed from 00E4 to 0061
+ /* U+01E0 */ 0x0041, 0x0061, 0x00C6, 0x00E6, 0x01E4, 0x01E5, 0x0047, 0x0067,
+ // U+01E0: Manually changed from 0226 to 0041
+ // U+01E1: Manually changed from 0227 to 0061
+ /* U+01E8 */ 0x004B, 0x006B, 0x004F, 0x006F, 0x004F, 0x006F, 0x01B7, 0x0292,
+ // U+01EC: Manually changed from 01EA to 004F
+ // U+01ED: Manually changed from 01EB to 006F
+ /* U+01F0 */ 0x006A, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01F6, 0x01F7,
+ /* U+01F8 */ 0x004E, 0x006E, 0x0041, 0x0061, 0x00C6, 0x00E6, 0x004F, 0x006F,
+ // U+01FA: Manually changed from 00C5 to 0041
+ // U+01FB: Manually changed from 00E5 to 0061
+ // U+01FE: Manually changed from 00D8 to 004F
+ // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O
+ // U+01FF: Manually changed from 00F8 to 006F
+ // TODO: Check if it's really acceptable to consider ø a diacritical variant of o
+ /* U+0200 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065,
+ /* U+0208 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x004F, 0x006F, 0x004F, 0x006F,
+ /* U+0210 */ 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075,
+ /* U+0218 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x021C, 0x021D, 0x0048, 0x0068,
+ /* U+0220 */ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061,
+ /* U+0228 */ 0x0045, 0x0065, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F,
+ // U+022A: Manually changed from 00D6 to 004F
+ // U+022B: Manually changed from 00F6 to 006F
+ // U+022C: Manually changed from 00D5 to 004F
+ // U+022D: Manually changed from 00F5 to 006F
+ /* U+0230 */ 0x004F, 0x006F, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237,
+ // U+0230: Manually changed from 022E to 004F
+ // U+0231: Manually changed from 022F to 006F
+ /* U+0238 */ 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F,
+ /* U+0240 */ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247,
+ /* U+0248 */ 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F,
+ /* U+0250 */ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257,
+ /* U+0258 */ 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F,
+ /* U+0260 */ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267,
+ /* U+0268 */ 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F,
+ /* U+0270 */ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277,
+ /* U+0278 */ 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F,
+ /* U+0280 */ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287,
+ /* U+0288 */ 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F,
+ /* U+0290 */ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297,
+ /* U+0298 */ 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F,
+ /* U+02A0 */ 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7,
+ /* U+02A8 */ 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF,
+ /* U+02B0 */ 0x0068, 0x0266, 0x006A, 0x0072, 0x0279, 0x027B, 0x0281, 0x0077,
+ /* U+02B8 */ 0x0079, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF,
+ /* U+02C0 */ 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7,
+ /* U+02C8 */ 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF,
+ /* U+02D0 */ 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7,
+ /* U+02D8 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02DE, 0x02DF,
+ /* U+02E0 */ 0x0263, 0x006C, 0x0073, 0x0078, 0x0295, 0x02E5, 0x02E6, 0x02E7,
+ /* U+02E8 */ 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF,
+ /* U+02F0 */ 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7,
+ /* U+02F8 */ 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF,
+ /* U+0300 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+ /* U+0308 */ 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
+ /* U+0310 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+ /* U+0318 */ 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
+ /* U+0320 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+ /* U+0328 */ 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
+ /* U+0330 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+ /* U+0338 */ 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
+ /* U+0340 */ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347,
+ /* U+0348 */ 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
+ /* U+0350 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+ /* U+0358 */ 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
+ /* U+0360 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+ /* U+0368 */ 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
+ /* U+0370 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x02B9, 0x0375, 0x0376, 0x0377,
+ /* U+0378 */ 0x0378, 0x0379, 0x0020, 0x037B, 0x037C, 0x037D, 0x003B, 0x037F,
+ /* U+0380 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00A8, 0x0391, 0x00B7,
+ /* U+0388 */ 0x0395, 0x0397, 0x0399, 0x038B, 0x039F, 0x038D, 0x03A5, 0x03A9,
+ /* U+0390 */ 0x03CA, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+ /* U+0398 */ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+ /* U+03A0 */ 0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
+ /* U+03A8 */ 0x03A8, 0x03A9, 0x0399, 0x03A5, 0x03B1, 0x03B5, 0x03B7, 0x03B9,
+ /* U+03B0 */ 0x03CB, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ /* U+03B8 */ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* U+03C0 */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ /* U+03C8 */ 0x03C8, 0x03C9, 0x03B9, 0x03C5, 0x03BF, 0x03C5, 0x03C9, 0x03CF,
+ /* U+03D0 */ 0x03B2, 0x03B8, 0x03A5, 0x03D2, 0x03D2, 0x03C6, 0x03C0, 0x03D7,
+ /* U+03D8 */ 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,
+ /* U+03E0 */ 0x03E0, 0x03E1, 0x03E2, 0x03E3, 0x03E4, 0x03E5, 0x03E6, 0x03E7,
+ /* U+03E8 */ 0x03E8, 0x03E9, 0x03EA, 0x03EB, 0x03EC, 0x03ED, 0x03EE, 0x03EF,
+ /* U+03F0 */ 0x03BA, 0x03C1, 0x03C2, 0x03F3, 0x0398, 0x03B5, 0x03F6, 0x03F7,
+ /* U+03F8 */ 0x03F8, 0x03A3, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,
+ /* U+0400 */ 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406,
+ /* U+0408 */ 0x0408, 0x0409, 0x040A, 0x040B, 0x041A, 0x0418, 0x0423, 0x040F,
+ /* U+0410 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+ /* U+0418 */ 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+ // U+0419: Manually changed from 0418 to 0419
+ /* U+0420 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+ /* U+0428 */ 0x0428, 0x0429, 0x042C, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+ // U+042A: Manually changed from 042A to 042C
+ /* U+0430 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ /* U+0438 */ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ // U+0439: Manually changed from 0438 to 0439
+ /* U+0440 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ /* U+0448 */ 0x0448, 0x0449, 0x044C, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ // U+044A: Manually changed from 044A to 044C
+ /* U+0450 */ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456,
+ /* U+0458 */ 0x0458, 0x0459, 0x045A, 0x045B, 0x043A, 0x0438, 0x0443, 0x045F,
+ /* U+0460 */ 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467,
+ /* U+0468 */ 0x0468, 0x0469, 0x046A, 0x046B, 0x046C, 0x046D, 0x046E, 0x046F,
+ /* U+0470 */ 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475,
+ /* U+0478 */ 0x0478, 0x0479, 0x047A, 0x047B, 0x047C, 0x047D, 0x047E, 0x047F,
+ /* U+0480 */ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+ /* U+0488 */ 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,
+ /* U+0490 */ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497,
+ /* U+0498 */ 0x0498, 0x0499, 0x049A, 0x049B, 0x049C, 0x049D, 0x049E, 0x049F,
+ /* U+04A0 */ 0x04A0, 0x04A1, 0x04A2, 0x04A3, 0x04A4, 0x04A5, 0x04A6, 0x04A7,
+ /* U+04A8 */ 0x04A8, 0x04A9, 0x04AA, 0x04AB, 0x04AC, 0x04AD, 0x04AE, 0x04AF,
+ /* U+04B0 */ 0x04B0, 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7,
+ /* U+04B8 */ 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF,
+ /* U+04C0 */ 0x04C0, 0x0416, 0x0436, 0x04C3, 0x04C4, 0x04C5, 0x04C6, 0x04C7,
+ /* U+04C8 */ 0x04C8, 0x04C9, 0x04CA, 0x04CB, 0x04CC, 0x04CD, 0x04CE, 0x04CF,
+ /* U+04D0 */ 0x0410, 0x0430, 0x0410, 0x0430, 0x04D4, 0x04D5, 0x0415, 0x0435,
+ /* U+04D8 */ 0x04D8, 0x04D9, 0x04D8, 0x04D9, 0x0416, 0x0436, 0x0417, 0x0437,
+ /* U+04E0 */ 0x04E0, 0x04E1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041E, 0x043E,
+ /* U+04E8 */ 0x04E8, 0x04E9, 0x04E8, 0x04E9, 0x042D, 0x044D, 0x0423, 0x0443,
+ /* U+04F0 */ 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04F6, 0x04F7,
+ /* U+04F8 */ 0x042B, 0x044B, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,
+ };
+}
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index fcf043031..8caf6f17f 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -52,7 +52,8 @@ public final class InputAttributes {
} else if (inputClass == 0) {
// TODO: is this check still necessary?
Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x"
- + " imeOptions=0x%08x", inputType, editorInfo.imeOptions));
+ + " imeOptions=0x%08x",
+ inputType, editorInfo.imeOptions));
}
mIsSettingsSuggestionStripOn = false;
mInputTypeNoAutoCorrect = false;
@@ -203,7 +204,8 @@ public final class InputAttributes {
public static boolean inPrivateImeOptions(String packageName, String key,
EditorInfo editorInfo) {
if (editorInfo == null) return false;
- final String findingKey = (packageName != null) ? packageName + "." + key : key;
+ final String findingKey = (packageName != null) ? packageName + "." + key
+ : key;
return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions);
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6a10131b0..77d07019f 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -68,6 +68,7 @@ import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
import com.android.inputmethod.event.EventInterpreter;
+import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardId;
@@ -80,6 +81,7 @@ import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastRec
import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister;
import com.android.inputmethod.latin.personalization.PersonalizationHelper;
+import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsActivity;
@@ -95,8 +97,8 @@ import com.android.inputmethod.latin.utils.InputTypeUtils;
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.RecapitalizeStatus;
+import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
import com.android.inputmethod.latin.utils.TextRange;
@@ -108,7 +110,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Locale;
import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
/**
* Input method implementation for Qwerty'ish keyboard.
@@ -181,6 +182,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private boolean mIsMainDictionaryAvailable;
private UserBinaryDictionary mUserDictionary;
private UserHistoryDictionary mUserHistoryDictionary;
+ private PersonalizationPredictionDictionary mPersonalizationPredictionDictionary;
private PersonalizationDictionary mPersonalizationDictionary;
private boolean mIsUserDictionaryAvailable;
@@ -194,6 +196,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
+ // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
+ // "expect" it, it means the user actually moved the cursor.
+ private boolean mExpectingUpdateSelection;
private int mDeleteCount;
private long mLastKeyTime;
private final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet();
@@ -222,7 +227,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public final UIHandler mHandler = new UIHandler(this);
private InputUpdater mInputUpdater;
- public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> {
+ public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
private static final int MSG_UPDATE_SHIFT_STATE = 0;
private static final int MSG_PENDING_IMS_CALLBACK = 1;
private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
@@ -231,8 +236,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private static final int MSG_REOPEN_DICTIONARIES = 5;
private static final int MSG_ON_END_BATCH_INPUT = 6;
private static final int MSG_RESET_CACHES = 7;
- // Update this when adding new messages
- private static final int MSG_LAST = MSG_RESET_CACHES;
private static final int ARG1_NOT_GESTURE_INPUT = 0;
private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
@@ -245,12 +248,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private long mDoubleSpacePeriodTimeout;
private long mDoubleSpacePeriodTimerStart;
- public UIHandler(final LatinIME ownerInstance) {
- super(ownerInstance);
+ public UIHandler(final LatinIME outerInstance) {
+ super(outerInstance);
}
public void onCreate() {
- final Resources res = getOwnerInstance().getResources();
+ final Resources res = getOuterInstance().getResources();
mDelayUpdateSuggestions =
res.getInteger(R.integer.config_delay_update_suggestions);
mDelayUpdateShiftState =
@@ -261,7 +264,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void handleMessage(final Message msg) {
- final LatinIME latinIme = getOwnerInstance();
+ final LatinIME latinIme = getOuterInstance();
final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
switch (msg.what) {
case MSG_UPDATE_SUGGESTION_STRIP:
@@ -344,13 +347,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
removeMessages(MSG_UPDATE_SHIFT_STATE);
}
- @UsedForTesting
- public void removeAllMessages() {
- for (int i = 0; i <= MSG_LAST; ++i) {
- removeMessages(i);
- }
- }
-
public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
final boolean dismissGestureFloatingPreviewText) {
removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
@@ -405,7 +401,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
removeMessages(MSG_PENDING_IMS_CALLBACK);
resetPendingImsCallback();
mIsOrientationChanging = true;
- final LatinIME latinIme = getOwnerInstance();
+ final LatinIME latinIme = getOuterInstance();
if (latinIme.isInputViewShown()) {
latinIme.mKeyboardSwitcher.saveKeyboardState();
}
@@ -438,7 +434,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mIsOrientationChanging = false;
mPendingSuccessiveImsCallback = true;
}
- final LatinIME latinIme = getOwnerInstance();
+ final LatinIME latinIme = getOuterInstance();
executePendingImsCallback(latinIme, editorInfo, restarting);
latinIme.onStartInputInternal(editorInfo, restarting);
}
@@ -457,7 +453,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK),
PENDING_IMS_CALLBACK_DURATION);
}
- final LatinIME latinIme = getOwnerInstance();
+ final LatinIME latinIme = getOuterInstance();
executePendingImsCallback(latinIme, editorInfo, restarting);
latinIme.onStartInputViewInternal(editorInfo, restarting);
mAppliedEditorInfo = editorInfo;
@@ -469,7 +465,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Typically this is the first onFinishInputView after orientation changed.
mHasPendingFinishInputView = true;
} else {
- final LatinIME latinIme = getOwnerInstance();
+ final LatinIME latinIme = getOuterInstance();
latinIme.onFinishInputViewInternal(finishingInput);
mAppliedEditorInfo = null;
}
@@ -480,7 +476,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Typically this is the first onFinishInput after orientation changed.
mHasPendingFinishInput = true;
} else {
- final LatinIME latinIme = getOwnerInstance();
+ final LatinIME latinIme = getOuterInstance();
executePendingImsCallback(latinIme, null, false);
latinIme.onFinishInputInternal();
}
@@ -613,6 +609,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
final String switcherLocaleStr = switcherSubtypeLocale.toString();
final Locale subtypeLocale;
+ final String localeStr;
if (TextUtils.isEmpty(switcherLocaleStr)) {
// This happens in very rare corner cases - for example, immediately after a switch
// to LatinIME has been requested, about a frame later another switch happens. In this
@@ -622,8 +619,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// of knowing anyway.
Log.e(TAG, "System is reporting no current subtype.");
subtypeLocale = getResources().getConfiguration().locale;
+ localeStr = subtypeLocale.toString();
} else {
subtypeLocale = switcherSubtypeLocale;
+ localeStr = switcherLocaleStr;
}
final Suggest newSuggest = new Suggest(this /* Context */, subtypeLocale,
@@ -638,16 +637,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
ResearchLogger.getInstance().initSuggest(newSuggest);
}
- mUserDictionary = new UserBinaryDictionary(this, subtypeLocale);
+ mUserDictionary = new UserBinaryDictionary(this, localeStr);
mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
newSuggest.setUserDictionary(mUserDictionary);
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+
mUserHistoryDictionary = PersonalizationHelper.getUserHistoryDictionary(
- this, subtypeLocale);
+ this, localeStr, prefs);
newSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
- mPersonalizationDictionary =
- PersonalizationHelper.getPersonalizationDictionary(this, subtypeLocale);
+ mPersonalizationDictionary = PersonalizationHelper
+ .getPersonalizationDictionary(this, localeStr, prefs);
newSuggest.setPersonalizationDictionary(mPersonalizationDictionary);
+ mPersonalizationPredictionDictionary = PersonalizationHelper
+ .getPersonalizationPredictionDictionary(this, localeStr, prefs);
+ newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary);
final Suggest oldSuggest = mSuggest;
resetContactsDictionary(null != oldSuggest ? oldSuggest.getContactsDictionary() : null);
@@ -756,9 +760,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
.findViewById(android.R.id.extractArea);
mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
- if (mSuggestionStripView != null) {
+ if (mSuggestionStripView != null)
mSuggestionStripView.setListener(this, view);
- }
if (LatinImeLogger.sVISUALDEBUG) {
mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
}
@@ -904,7 +907,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// refresh later.
final boolean canReachInputConnection;
if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart,
- editorInfo.initialSelEnd, false /* shouldFinishComposition */)) {
+ false /* shouldFinishComposition */)) {
// We try resetting the caches up to 5 times before giving up.
mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
// mLastSelection{Start,End} are reset later in this method, don't need to do it here
@@ -1085,9 +1088,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ", ce=" + composingSpanEnd);
}
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ final boolean expectingUpdateSelectionFromLogger =
+ ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection();
ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
- composingSpanEnd, mConnection);
+ composingSpanEnd, mExpectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, mConnection);
+ if (expectingUpdateSelectionFromLogger) {
+ // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work
+ return;
+ }
}
final boolean selectionChanged = mLastSelectionStart != newSelStart
@@ -1106,8 +1116,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: revisit this when LatinIME supports hardware keyboards.
// NOTE: the test harness subclasses LatinIME and overrides isInputViewShown().
// TODO: find a better way to simulate actual execution.
- if (isInputViewShown() && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart,
- oldSelEnd, newSelEnd)) {
+ if (isInputViewShown() && !mExpectingUpdateSelection
+ && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
+ // TAKE CARE: there is a race condition when we enter this test even when the user
+ // did not explicitly move the cursor. This happens when typing fast, where two keys
+ // turn this flag on in succession and both onUpdateSelection() calls arrive after
+ // the second one - the first call successfully avoids this test, but the second one
+ // enters. For the moment we rely on noComposingSpan to further reduce the impact.
+
// TODO: the following is probably better done in resetEntireInputState().
// it should only happen when the cursor moved, and the very purpose of the
// test below is to narrow down whether this happened or not. Likewise with
@@ -1133,13 +1149,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Another option would be to send suggestions each time we set the composing
// text, but that is probably too expensive to do, so we decided to leave things
// as is.
- resetEntireInputState(newSelStart, newSelEnd);
+ resetEntireInputState(newSelStart);
} else {
- // resetEntireInputState calls resetCachesUponCursorMove, but forcing the
- // composition to end. But in all cases where we don't reset the entire input
- // state, we still want to tell the rich input connection about the new cursor
- // position so that it can update its caches.
- mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
+ // resetEntireInputState calls resetCachesUponCursorMove, but with the second
+ // argument as true. But in all cases where we don't reset the entire input state,
+ // we still want to tell the rich input connection about the new cursor position so
+ // that it can update its caches.
+ mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart,
false /* shouldFinishComposition */);
}
@@ -1152,6 +1168,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mRecapitalizeStatus.deactivate();
mKeyboardSwitcher.updateShiftState();
}
+ mExpectingUpdateSelection = false;
// Make a note of the cursor position
mLastSelectionStart = newSelStart;
@@ -1337,7 +1354,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public boolean onEvaluateFullscreenMode() {
// Reread resource value here, because this method is called by framework anytime as needed.
- final boolean isFullscreenModeAllowed = Settings.readUseFullscreenMode(getResources());
+ final boolean isFullscreenModeAllowed =
+ Settings.readUseFullscreenMode(getResources());
if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) {
// TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI
// implies NO_FULLSCREEN. However, the framework mistakenly does. i.e. NO_EXTRACT_UI
@@ -1362,7 +1380,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// This will reset the whole input state to the starting state. It will clear
// the composing word, reset the last composed word, tell the inputconnection about it.
- private void resetEntireInputState(final int newSelStart, final int newSelEnd) {
+ private void resetEntireInputState(final int newCursorPosition) {
final boolean shouldFinishComposition = mWordComposer.isComposingWord();
resetComposingState(true /* alsoResetLastComposedWord */);
final SettingsValues settingsValues = mSettings.getCurrent();
@@ -1371,15 +1389,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
setSuggestedWords(settingsValues.mSuggestPuncList, false);
}
- mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
+ mConnection.resetCachesUponCursorMoveAndReturnSuccess(newCursorPosition,
shouldFinishComposition);
}
private void resetComposingState(final boolean alsoResetLastComposedWord) {
mWordComposer.reset();
- if (alsoResetLastComposedWord) {
+ if (alsoResetLastComposedWord)
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- }
}
private void commitTyped(final String separatorString) {
@@ -1426,16 +1443,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
}
- if (0 != auto) {
- return WordComposer.CAPS_MODE_AUTO_SHIFTED;
- }
+ if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED;
return WordComposer.CAPS_MODE_OFF;
}
private void swapSwapperAndSpace() {
final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
// It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
- if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) {
+ if (lastTwo != null && lastTwo.length() == 2
+ && lastTwo.charAt(0) == Constants.CODE_SPACE) {
mConnection.deleteSurroundingText(2, 0);
final String text = lastTwo.charAt(1) + " ";
mConnection.commitText(text, 1);
@@ -1714,7 +1730,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
- resetEntireInputState(mLastSelectionStart, mLastSelectionEnd);
+ resetEntireInputState(mLastSelectionStart);
} else {
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
}
@@ -1730,6 +1746,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
handleCharacter(primaryCode, keyX, keyY, spaceState);
}
+ mExpectingUpdateSelection = true;
return didAutoCorrect;
}
@@ -1769,9 +1786,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputUpdater.onStartBatchInput();
mHandler.cancelUpdateSuggestionStrip();
mConnection.beginBatchEdit();
- final SettingsValues currentSettingsValues = mSettings.getCurrent();
+ final SettingsValues settingsValues = mSettings.getCurrent();
if (mWordComposer.isComposingWord()) {
- if (currentSettingsValues.mIsInternal) {
+ if (settingsValues.mIsInternal) {
if (mWordComposer.isBatchMode()) {
LatinImeLoggerUtils.onAutoCorrection(
"", mWordComposer.getTypedWord(), " ", mWordComposer);
@@ -1782,7 +1799,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the batch input at the current cursor position.
- resetEntireInputState(mLastSelectionStart, mLastSelectionEnd);
+ resetEntireInputState(mLastSelectionStart);
} else if (wordComposerSize <= 1) {
// We auto-correct the previous (typed, not gestured) string iff it's one character
// long. The reason for this is, even in the middle of gesture typing, you'll still
@@ -1795,17 +1812,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
}
+ mExpectingUpdateSelection = true;
}
final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
if (Character.isLetterOrDigit(codePointBeforeCursor)
- || currentSettingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
+ || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
mSpaceState = SPACE_STATE_PHANTOM;
}
mConnection.endBatchEdit();
- mKeyboardSwitcher.updateShiftState();
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(getActualCapsMode(),
- // Prev word is 1st word before cursor
- getNthPreviousWordForSuggestion(currentSettingsValues, 1 /* nthPreviousWord */));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
}
static final class InputUpdater implements Handler.Callback {
@@ -1978,8 +1993,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mConnection.commitText(commitParts[0], 0);
mSpaceState = SPACE_STATE_PHANTOM;
mKeyboardSwitcher.updateShiftState();
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- getActualCapsMode(), commitParts[0]);
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
++mAutoCommitSequenceNumber;
}
}
@@ -1989,7 +2003,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// This method must run in UI Thread.
public void onEndBatchInputAsyncInternal(final SuggestedWords suggestedWords) {
- final String batchInputText = suggestedWords.isEmpty() ? null : suggestedWords.getWord(0);
+ final String batchInputText = suggestedWords.isEmpty()
+ ? null : suggestedWords.getWord(0);
if (TextUtils.isEmpty(batchInputText)) {
return;
}
@@ -2011,6 +2026,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mWordComposer.setBatchInputWord(batchInputText);
mConnection.setComposingText(batchInputText, 1);
}
+ mExpectingUpdateSelection = true;
mConnection.endBatchEdit();
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords);
@@ -2064,7 +2080,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void handleBackspace(final int spaceState) {
+ // We revert these in this method if the deletion doesn't happen.
mDeleteCount++;
+ mExpectingUpdateSelection = true;
// In many cases, we may have to put the keyboard in auto-shift state again. However
// we want to wait a few milliseconds before doing it to avoid the keyboard flashing
@@ -2074,7 +2092,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can remove the character at the current cursor position.
- resetEntireInputState(mLastSelectionStart, mLastSelectionEnd);
+ resetEntireInputState(mLastSelectionStart);
// When we exit this if-clause, mWordComposer.isComposingWord() will return false.
}
if (mWordComposer.isComposingWord()) {
@@ -2145,10 +2163,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// later (typically, in a subsequent press on backspace).
mLastSelectionEnd = mLastSelectionStart;
mConnection.deleteSurroundingText(numCharsDeleted, 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace(numCharsDeleted,
- false /* shouldUncommitLogUnit */);
- }
} else {
// There is no selection, just delete one character.
if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
@@ -2171,27 +2185,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursor == Constants.NOT_A_CODE) {
- // Nothing to delete before the cursor.
+ // Nothing to delete before the cursor. We have to revert the deletion
+ // states that were updated at the beginning of this method.
+ mDeleteCount--;
+ mExpectingUpdateSelection = false;
return;
}
final int lengthToDelete =
Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDelete, 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace(lengthToDelete,
- true /* shouldUncommitLogUnit */);
- }
if (mDeleteCount > DELETE_ACCELERATE_AT) {
final int codePointBeforeCursorToDeleteAgain =
mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) {
final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
- codePointBeforeCursorToDeleteAgain) ? 2 : 1;
+ codePointBeforeCursorToDeleteAgain) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDeleteAgain, 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain,
- true /* shouldUncommitLogUnit */);
- }
}
}
}
@@ -2208,8 +2217,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
/*
* Strip a trailing space if necessary and returns whether it's a swap weak space situation.
*/
- private boolean maybeStripSpace(final int code, final int spaceState,
- final boolean isFromSuggestionStrip) {
+ private boolean maybeStripSpace(final int code,
+ final int spaceState, final boolean isFromSuggestionStrip) {
if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
mConnection.removeTrailingSpace();
return false;
@@ -2224,8 +2233,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return false;
}
- private void handleCharacter(final int primaryCode, final int x, final int y,
- final int spaceState) {
+ private void handleCharacter(final int primaryCode, final int x,
+ final int y, final int spaceState) {
// TODO: refactor this method to stop flipping isComposingWord around all the time, and
// make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
// which has the same name as other handle* methods but is not the same.
@@ -2245,7 +2254,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
- resetEntireInputState(mLastSelectionStart, mLastSelectionEnd);
+ resetEntireInputState(mLastSelectionStart);
isComposingWord = false;
}
// We want to find out whether to start composing a new word with this character. If so,
@@ -2275,24 +2284,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetComposingState(false /* alsoResetLastComposedWord */);
}
if (isComposingWord) {
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- // TODO: We should reconsider which coordinate system should be used to represent
- // keyboard event.
- final int keyX = mainKeyboardView.getKeyX(x);
- final int keyY = mainKeyboardView.getKeyY(y);
+ final int keyX, keyY;
+ if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) {
+ final KeyDetector keyDetector =
+ mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
+ keyX = keyDetector.getTouchX(x);
+ keyY = keyDetector.getTouchY(y);
+ } else {
+ keyX = x;
+ keyY = y;
+ }
mWordComposer.add(primaryCode, keyX, keyY);
// If it's the first letter, make note of auto-caps state
if (mWordComposer.size() == 1) {
- // We pass 1 to getPreviousWordForSuggestion because we were not composing a word
- // yet, so the word we want is the 1st word before the cursor.
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- getActualCapsMode(),
- getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord */));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
}
mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
- final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
- Constants.SUGGESTION_STRIP_COORDINATE == x);
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode,
+ spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
sendKeyCodePoint(primaryCode);
@@ -2356,7 +2366,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the separator at the current cursor position.
- resetEntireInputState(mLastSelectionStart, mLastSelectionEnd);
+ resetEntireInputState(mLastSelectionStart);
}
if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing
if (currentSettings.mCorrectionEnabled) {
@@ -2529,24 +2539,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- /**
- * Get the nth previous word before the cursor as context for the suggestion process.
- * @param currentSettings the current settings values.
- * @param nthPreviousWord reverse index of the word to get (1-indexed)
- * @return the nth previous word before the cursor.
- */
- private String getNthPreviousWordForSuggestion(final SettingsValues currentSettings,
- final int nthPreviousWord) {
- if (currentSettings.mCurrentLanguageHasSpaces) {
- // If we are typing in a language with spaces we can just look up the previous
- // word from textview.
- return mConnection.getNthPreviousWord(currentSettings, nthPreviousWord);
- } else {
- return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
- : mLastComposedWord.mCommittedWord;
- }
- }
-
private void getSuggestedWords(final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
@@ -2560,31 +2552,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// should just skip whitespace if any, so 1.
final SettingsValues currentSettings = mSettings.getCurrent();
final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues;
-
- final String previousWord;
- if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) {
- previousWord = mWordComposer.getPreviousWord();
- } else {
- // Not composing: this is for prediction.
- // TODO: read the previous word earlier for prediction, like we are doing for
- // normal suggestions.
- previousWord = getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord*/);
- }
- if (DEBUG) {
- // TODO: this is for checking consistency with older versions. Remove this when
- // we are confident this is stable.
- // We're checking the previous word in the text field against the memorized previous
- // word. If we are composing a word we should have the second word before the cursor
- // memorized, otherwise we should have the first.
- final String rereadPrevWord = getNthPreviousWordForSuggestion(currentSettings,
+ final String prevWord;
+ if (currentSettings.mCurrentLanguageHasSpaces) {
+ // If we are typing in a language with spaces we can just look up the previous
+ // word from textview.
+ prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators,
mWordComposer.isComposingWord() ? 2 : 1);
- if (!TextUtils.equals(previousWord, rereadPrevWord)) {
- throw new RuntimeException("Unexpected previous word: "
- + previousWord + " <> " + rereadPrevWord);
- }
+ } else {
+ prevWord = LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
+ : mLastComposedWord.mCommittedWord;
}
- suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWord(),
- keyboard.getProximityInfo(),
+ suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
additionalFeaturesOptions, sessionId, sequenceNumber, callback);
}
@@ -2701,6 +2679,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
separator, mWordComposer.isBatchMode(), suggestedWords);
}
+ mExpectingUpdateSelection = true;
commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
separator);
if (!typedWord.equals(autoCorrection)) {
@@ -2771,6 +2750,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// typed word.
final String replacedWord = mWordComposer.getTypedWord();
LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
+ mExpectingUpdateSelection = true;
commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
LastComposedWord.NOT_A_SEPARATOR);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -2853,7 +2833,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
if (userHistoryDictionary == null) return null;
- final String prevWord = mConnection.getNthPreviousWord(currentSettings, 2);
+ final String prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, 2);
final String secondWord;
if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
@@ -2865,8 +2845,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int maxFreq = AutoCorrectionUtils.getMaxFrequency(
suggest.getUnigramDictionaries(), suggestion);
if (maxFreq == 0) return null;
- userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0,
- (int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis())));
+ userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0);
return prevWord;
}
@@ -2922,13 +2901,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
}
- mWordComposer.setComposingWord(typedWord,
- getNthPreviousWordForSuggestion(currentSettings,
- // We want the previous word for suggestion. If we have chars in the word
- // before the cursor, then we want the word before that, hence 2; otherwise,
- // we want the word immediately before the cursor, hence 1.
- 0 == numberOfCharsInWordBeforeCursor ? 1 : 2),
- mKeyboardSwitcher.getKeyboard());
+ mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
mWordComposer.setCursorPositionWithinWord(
typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
mConnection.setComposingRegion(
@@ -3006,11 +2979,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void restartSuggestionsOnWordBeforeCursor(final String word) {
- mWordComposer.setComposingWord(word,
- // Previous word is the 2nd word before cursor because we are restarting on the
- // 1st word before cursor.
- getNthPreviousWordForSuggestion(mSettings.getCurrent(), 2 /* nthPreviousWord */),
- mKeyboardSwitcher.getKeyboard());
+ mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
final int length = word.length();
mConnection.deleteSurroundingText(length, 0);
mConnection.setComposingText(word, 1);
@@ -3028,8 +2997,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
* @param remainingTries How many times we may try again before giving up.
*/
private void retryResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
- if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart,
- mLastSelectionEnd, false)) {
+ if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, false)) {
if (0 < remainingTries) {
mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
return;
@@ -3056,7 +3024,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
throw new RuntimeException("revertCommit, but we are composing a word");
}
final CharSequence wordBeforeCursor =
- mConnection.getTextBeforeCursor(deleteLength, 0).subSequence(0, cancelLength);
+ mConnection.getTextBeforeCursor(deleteLength, 0)
+ .subSequence(0, cancelLength);
if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
throw new RuntimeException("revertCommit check failed: we thought we were "
+ "reverting \"" + committedWord
@@ -3076,8 +3045,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
// For languages without spaces, we revert the typed string but the cursor is flush
// with the typed word, so we need to resume suggestions right away.
- mWordComposer.setComposingWord(stringToCommit, previousWord,
- mKeyboardSwitcher.getKeyboard());
+ mWordComposer.setComposingWord(stringToCommit, mKeyboardSwitcher.getKeyboard());
mConnection.setComposingText(stringToCommit, 1);
}
if (mSettings.isInternal()) {
@@ -3127,8 +3095,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void hapticAndAudioFeedback(final int code, final int repeatCount) {
final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (keyboardView != null && keyboardView.isInDraggingFinger()) {
- // No need to feedback while finger is dragging.
+ if (keyboardView != null && keyboardView.isInSlidingKeyInput()) {
+ // No need to feedback while sliding input.
return;
}
if (repeatCount > 0) {
@@ -3258,8 +3226,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final Intent intent = IntentUtils.getInputLanguageSelectionIntent(
mRichImm.getInputMethodIdOfThisIme(),
Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
break;
case 1:
@@ -3268,8 +3236,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
};
- final AlertDialog.Builder builder =
- new AlertDialog.Builder(this).setItems(items, listener).setTitle(title);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setItems(items, listener)
+ .setTitle(title);
showOptionDialog(builder.create());
}
@@ -3330,13 +3299,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final Printer p = new PrintWriterPrinter(fout);
p.println("LatinIME state :");
- p.println(" VersionCode = " + ApplicationUtils.getVersionCode(this));
- p.println(" VersionName = " + ApplicationUtils.getVersionName(this));
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
p.println(" Keyboard mode = " + keyboardMode);
final SettingsValues settingsValues = mSettings.getCurrent();
- p.println(" mIsSuggestionsRequested = "
+ p.println(" mIsSuggestionsSuggestionsRequested = "
+ settingsValues.isSuggestionsRequested(mDisplayOrientation));
p.println(" mCorrectionEnabled=" + settingsValues.mCorrectionEnabled);
p.println(" isComposingWord=" + mWordComposer.isComposingWord());
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 37311acf2..673d1b4c2 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -57,19 +57,14 @@ public final class RichInputConnection {
private static final int INVALID_CURSOR_POSITION = -1;
/**
- * This variable contains an expected value for the selection start position. This is where the
- * cursor or selection start may end up after all the keyboard-triggered updates have passed. We
- * keep this to compare it to the actual selection start to guess whether the move was caused by
- * a keyboard command or not.
- * It's not really the selection start position: the selection start may not be there yet, and
- * in some cases, it may never arrive there.
+ * This variable contains an expected value for the cursor position. This is where the
+ * cursor may end up after all the keyboard-triggered updates have passed. We keep this to
+ * compare it to the actual cursor position to guess whether the move was caused by a
+ * keyboard command or not.
+ * It's not really the cursor position: the cursor may not be there yet, and it's also expected
+ * there be cases where it never actually comes to be there.
*/
- private int mExpectedSelStart = INVALID_CURSOR_POSITION; // in chars, not code points
- /**
- * The expected selection end. Only differs from mExpectedSelStart if a non-empty selection is
- * expected. The same caveats as mExpectedSelStart apply.
- */
- private int mExpectedSelEnd = INVALID_CURSOR_POSITION; // in chars, not code points
+ private int mExpectedCursorPosition = INVALID_CURSOR_POSITION; // in chars, not code points
/**
* This contains the committed text immediately preceding the cursor and the composing
* text if any. It is refreshed when the cursor moves by calling upon the TextView.
@@ -108,16 +103,16 @@ public final class RichInputConnection {
final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString()
: beforeCursor.subSequence(beforeCursor.length() - actualLength,
beforeCursor.length()).toString();
- if (et.selectionStart != mExpectedSelStart
+ if (et.selectionStart != mExpectedCursorPosition
|| !(reference.equals(internal.toString()))) {
- final String context = "Expected selection start = " + mExpectedSelStart
- + "\nActual selection start = " + et.selectionStart
+ final String context = "Expected cursor position = " + mExpectedCursorPosition
+ + "\nActual cursor position = " + et.selectionStart
+ "\nExpected text = " + internal.length() + " " + internal
+ "\nActual text = " + reference.length() + " " + reference;
((LatinIME)mParent).debugDumpStateAndCrashWithException(context);
} else {
Log.e(TAG, DebugLogUtils.getStackTrace(2));
- Log.e(TAG, "Exp <> Actual : " + mExpectedSelStart + " <> " + et.selectionStart);
+ Log.e(TAG, "Exp <> Actual : " + mExpectedCursorPosition + " <> " + et.selectionStart);
}
}
@@ -155,50 +150,16 @@ public final class RichInputConnection {
* data, so we empty the cache and note that we don't know the new cursor position, and we
* return false so that the caller knows about this and can retry later.
*
- * @param newSelStart the new position of the selection start, as received from the system.
- * @param newSelEnd the new position of the selection end, as received from the system.
- * @param shouldFinishComposition whether we should finish the composition in progress.
+ * @param newCursorPosition The new position of the cursor, as received from the system.
+ * @param shouldFinishComposition Whether we should finish the composition in progress.
* @return true if we were able to connect to the editor successfully, false otherwise. When
* this method returns false, the caches could not be correctly refreshed so they were only
* reset: the caller should try again later to return to normal operation.
*/
- public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart,
- final int newSelEnd, final boolean shouldFinishComposition) {
- mExpectedSelStart = newSelStart;
- mExpectedSelEnd = newSelEnd;
+ public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newCursorPosition,
+ final boolean shouldFinishComposition) {
+ mExpectedCursorPosition = newCursorPosition;
mComposingText.setLength(0);
- final boolean didReloadTextSuccessfully = reloadTextCache();
- if (!didReloadTextSuccessfully) {
- Log.d(TAG, "Will try to retrieve text later.");
- return false;
- }
- final int lengthOfTextBeforeCursor = mCommittedTextBeforeComposingText.length();
- if (lengthOfTextBeforeCursor > newSelStart
- || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
- && newSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
- // newSelStart and newSelEnd may be lying -- when rotating the device (probably a
- // framework bug). If we have less chars than we asked for, then we know how many chars
- // we have, and if we got more than newSelStart says, then we know it was lying. In both
- // cases the length is more reliable. Note that we only have to check newSelStart (not
- // newSelEnd) since if newSelEnd is wrong, the newSelStart will be wrong as well.
- mExpectedSelStart = lengthOfTextBeforeCursor;
- mExpectedSelEnd = lengthOfTextBeforeCursor;
- }
- if (null != mIC && shouldFinishComposition) {
- mIC.finishComposingText();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_finishComposingText();
- }
- }
- return true;
- }
-
- /**
- * Reload the cached text from the InputConnection.
- *
- * @return true if successful
- */
- private boolean reloadTextCache() {
mCommittedTextBeforeComposingText.setLength(0);
mIC = mParent.getCurrentInputConnection();
// Call upon the inputconnection directly since our own method is using the cache, and
@@ -208,12 +169,27 @@ public final class RichInputConnection {
if (null == textBeforeCursor) {
// For some reason the app thinks we are not connected to it. This looks like a
// framework bug... Fall back to ground state and return false.
- mExpectedSelStart = INVALID_CURSOR_POSITION;
- mExpectedSelEnd = INVALID_CURSOR_POSITION;
- Log.e(TAG, "Unable to connect to the editor to retrieve text.");
+ mExpectedCursorPosition = INVALID_CURSOR_POSITION;
+ Log.e(TAG, "Unable to connect to the editor to retrieve text... will retry later");
return false;
}
mCommittedTextBeforeComposingText.append(textBeforeCursor);
+ final int lengthOfTextBeforeCursor = textBeforeCursor.length();
+ if (lengthOfTextBeforeCursor > newCursorPosition
+ || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
+ && newCursorPosition < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
+ // newCursorPosition may be lying -- when rotating the device (probably a framework
+ // bug). If we have less chars than we asked for, then we know how many chars we have,
+ // and if we got more than newCursorPosition says, then we know it was lying. In both
+ // cases the length is more reliable
+ mExpectedCursorPosition = lengthOfTextBeforeCursor;
+ }
+ if (null != mIC && shouldFinishComposition) {
+ mIC.finishComposingText();
+ if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ ResearchLogger.richInputConnection_finishComposingText();
+ }
+ }
return true;
}
@@ -242,8 +218,7 @@ public final class RichInputConnection {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length() - mComposingText.length();
mComposingText.setLength(0);
if (null != mIC) {
mIC.commitText(text, i);
@@ -256,7 +231,7 @@ public final class RichInputConnection {
}
public boolean canDeleteCharacters() {
- return mExpectedSelStart > 0;
+ return mExpectedCursorPosition > 0;
}
/**
@@ -293,10 +268,11 @@ public final class RichInputConnection {
// heavy pressing of delete, for example DEFAULT_TEXT_CACHE_SIZE - 5 times or so.
// getCapsMode should be updated to be able to return a "not enough info" result so that
// we can get more context only when needed.
- if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedSelStart) {
- if (!reloadTextCache()) {
- Log.w(TAG, "Unable to connect to the editor. "
- + "Setting caps mode without knowing text.");
+ if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedCursorPosition) {
+ final CharSequence textBeforeCursor = getTextBeforeCursor(
+ Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
+ if (!TextUtils.isEmpty(textBeforeCursor)) {
+ mCommittedTextBeforeComposingText.append(textBeforeCursor);
}
}
// This never calls InputConnection#getCapsMode - in fact, it's a static method that
@@ -319,8 +295,8 @@ public final class RichInputConnection {
// However, if we don't have an expected cursor position, then we should always
// go fetch the cache again (as it happens, INVALID_CURSOR_POSITION < 0, so we need to
// test for this explicitly)
- if (INVALID_CURSOR_POSITION != mExpectedSelStart
- && (cachedLength >= n || cachedLength >= mExpectedSelStart)) {
+ if (INVALID_CURSOR_POSITION != mExpectedCursorPosition
+ && (cachedLength >= n || cachedLength >= mExpectedCursorPosition)) {
final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText);
// We call #toString() here to create a temporary object.
// In some situations, this method is called on a worker thread, and it's possible
@@ -360,14 +336,10 @@ public final class RichInputConnection {
+ remainingChars, 0);
mCommittedTextBeforeComposingText.setLength(len);
}
- if (mExpectedSelStart > beforeLength) {
- mExpectedSelStart -= beforeLength;
- mExpectedSelEnd -= beforeLength;
+ if (mExpectedCursorPosition > beforeLength) {
+ mExpectedCursorPosition -= beforeLength;
} else {
- // There are fewer characters before the cursor in the buffer than we are being asked to
- // delete. Only delete what is there.
- mExpectedSelStart = 0;
- mExpectedSelEnd -= mExpectedSelStart;
+ mExpectedCursorPosition = 0;
}
if (null != mIC) {
mIC.deleteSurroundingText(beforeLength, afterLength);
@@ -401,8 +373,7 @@ public final class RichInputConnection {
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
- mExpectedSelStart += 1;
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += 1;
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
@@ -414,24 +385,18 @@ public final class RichInputConnection {
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
- if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
- // TODO: Handle surrogate pairs.
- mExpectedSelStart -= 1;
- }
- mExpectedSelEnd = mExpectedSelStart;
+ if (mExpectedCursorPosition > 0) mExpectedCursorPosition -= 1;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
- mExpectedSelStart += keyEvent.getCharacters().length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += keyEvent.getCharacters().length();
}
break;
default:
- final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
+ final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1);
mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length();
break;
}
}
@@ -465,12 +430,10 @@ public final class RichInputConnection {
public void setComposingText(final CharSequence text, final int newCursorPosition) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length() - mComposingText.length();
mComposingText.setLength(0);
mComposingText.append(text);
- // TODO: support values of newCursorPosition != 1. At this time, this is never called with
- // newCursorPosition != 1.
+ // TODO: support values of i != 1. At this time, this is never called with i != 1.
if (null != mIC) {
mIC.setComposingText(text, newCursorPosition);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -480,31 +443,19 @@ public final class RichInputConnection {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
- /**
- * Set the selection of the text editor.
- *
- * Calls through to {@link InputConnection#setSelection(int, int)}.
- *
- * @param start the character index where the selection should start.
- * @param end the character index where the selection should end.
- * @return Returns true on success, false if the input connection is no longer valid either when
- * setting the selection or when retrieving the text cache at that point.
- */
- public boolean setSelection(final int start, final int end) {
+ public void setSelection(final int start, final int end) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- mExpectedSelStart = start;
- mExpectedSelEnd = end;
if (null != mIC) {
- final boolean isIcValid = mIC.setSelection(start, end);
- if (!isIcValid) {
- return false;
- }
+ mIC.setSelection(start, end);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.richInputConnection_setSelection(start, end);
}
}
- return reloadTextCache();
+ mExpectedCursorPosition = start;
+ mCommittedTextBeforeComposingText.setLength(0);
+ mCommittedTextBeforeComposingText.append(
+ getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0));
}
public void commitCorrection(final CorrectionInfo correctionInfo) {
@@ -525,8 +476,7 @@ public final class RichInputConnection {
// text should never be null, but just in case, it's better to insert nothing than to crash
if (null == text) text = "";
mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length() - mComposingText.length();
mComposingText.setLength(0);
if (null != mIC) {
mIC.commitCompletion(completionInfo);
@@ -538,7 +488,7 @@ public final class RichInputConnection {
}
@SuppressWarnings("unused")
- public String getNthPreviousWord(final SettingsValues currentSettingsValues, final int n) {
+ public String getNthPreviousWord(final String sentenceSeperators, final int n) {
mIC = mParent.getCurrentInputConnection();
if (null == mIC) return null;
final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
@@ -557,7 +507,7 @@ public final class RichInputConnection {
}
}
}
- return getNthPreviousWord(prev, currentSettingsValues, n);
+ return getNthPreviousWord(prev, sentenceSeperators, n);
}
private static boolean isSeparator(int code, String sep) {
@@ -581,7 +531,7 @@ public final class RichInputConnection {
// (n = 2) "abc |" -> null
// (n = 2) "abc. def|" -> null
public static String getNthPreviousWord(final CharSequence prev,
- final SettingsValues currentSettingsValues, final int n) {
+ final String sentenceSeperators, final int n) {
if (prev == null) return null;
final String[] w = spaceRegex.split(prev);
@@ -593,8 +543,7 @@ public final class RichInputConnection {
// If ends in a separator, return null
final char lastChar = nthPrevWord.charAt(length - 1);
- if (currentSettingsValues.isWordSeparator(lastChar)
- || currentSettingsValues.isWordConnector(lastChar)) return null;
+ if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
return nthPrevWord;
}
@@ -809,30 +758,20 @@ public final class RichInputConnection {
* this update and not the ones in-between. This is almost impossible to achieve even trying
* very very hard.
*
- * @param oldSelStart The value of the old selection in the update.
- * @param newSelStart The value of the new selection in the update.
- * @param oldSelEnd The value of the old selection end in the update.
- * @param newSelEnd The value of the new selection end in the update.
+ * @param oldSelStart The value of the old cursor position in the update.
+ * @param newSelStart The value of the new cursor position in the update.
* @return whether this is a belated expected update or not.
*/
- public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart,
- final int oldSelEnd, final int newSelEnd) {
- // This update is "belated" if we are expecting it. That is, mExpectedSelStart and
- // mExpectedSelEnd match the new values that the TextView is updating TO.
- if (mExpectedSelStart == newSelStart && mExpectedSelEnd == newSelEnd) return true;
- // This update is not belated if mExpectedSelStart and mExpeectedSelend match the old
- // values, and one of newSelStart or newSelEnd is updated to a different value. In this
- // case, there is likely something other than the IME that has moved the selection endpoint
- // to the new value.
- if (mExpectedSelStart == oldSelStart && mExpectedSelEnd == oldSelEnd
- && (oldSelStart != newSelStart || oldSelEnd != newSelEnd)) return false;
- // If nether of the above two cases holds, then the system may be having trouble keeping up
- // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart
- // and mExpectedSelStart, and 3) newSelEnd is between oldSelEnd and mExpectedSelEnd, then
- // assume a belated update.
- return (newSelStart == newSelEnd)
- && (newSelStart - oldSelStart) * (mExpectedSelStart - newSelStart) >= 0
- && (newSelEnd - oldSelEnd) * (mExpectedSelEnd - newSelEnd) >= 0;
+ public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) {
+ // If this is an update that arrives at our expected position, it's a belated update.
+ if (newSelStart == mExpectedCursorPosition) return true;
+ // If this is an update that moves the cursor from our expected position, it must be
+ // an explicit move.
+ if (oldSelStart == mExpectedCursorPosition) return false;
+ // The following returns true if newSelStart is between oldSelStart and
+ // mCurrentCursorPosition. We assume that if the updated position is between the old
+ // position and the expected position, then it must be a belated update.
+ return (newSelStart - oldSelStart) * (mExpectedCursorPosition - newSelStart) >= 0;
}
/**
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 379eaaa3d..cd9c89f04 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -32,9 +32,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.List;
@@ -58,34 +56,23 @@ public final class SubtypeSwitcher {
private InputMethodSubtype mEmojiSubtype;
private boolean mIsNetworkConnected;
- private static final String KEYBOARD_MODE = "keyboard";
// Dummy no language QWERTY subtype. See {@link R.xml.method}.
- private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
- private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE =
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
- + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
- private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE =
- InputMethodSubtypeCompatUtils.newInputMethodSubtype(
- R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
- SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
- EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE,
- false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
- SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE);
+ private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = new InputMethodSubtype(
+ R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
+ SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet="
+ + SubtypeLocaleUtils.QWERTY
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + ",EnabledWhenDefaultIsNotAsciiCapable,"
+ + Constants.Subtype.ExtraValue.EMOJI_CAPABLE,
+ false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */);
// Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
// Dummy Emoji subtype. See {@link R.xml.method}.
- private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0;
- private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE =
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
- private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE =
- InputMethodSubtypeCompatUtils.newInputMethodSubtype(
- R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
- SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
- EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE,
- false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
- SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE);
+ private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE = new InputMethodSubtype(
+ R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
+ SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet="
+ + SubtypeLocaleUtils.EMOJI + ","
+ + Constants.Subtype.ExtraValue.EMOJI_CAPABLE,
+ false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */);
static final class NeedsToDisplayLanguage {
private int mEnabledSubtypeCount;
@@ -269,23 +256,18 @@ public final class SubtypeSwitcher {
return mNeedsToDisplayLanguage.getValue();
}
- private static InputMethodSubtype sForcedSubtypeForTesting = null;
+ private static Locale sForcedLocaleForTesting = null;
@UsedForTesting
- void forceSubtype(final InputMethodSubtype subtype) {
- sForcedSubtypeForTesting = subtype;
+ void forceLocale(final Locale locale) {
+ sForcedLocaleForTesting = locale;
}
public Locale getCurrentSubtypeLocale() {
- if (null != sForcedSubtypeForTesting) {
- return LocaleUtils.constructLocaleFromString(sForcedSubtypeForTesting.getLocale());
- }
+ if (null != sForcedLocaleForTesting) return sForcedLocaleForTesting;
return SubtypeLocaleUtils.getSubtypeLocale(getCurrentSubtype());
}
public InputMethodSubtype getCurrentSubtype() {
- if (null != sForcedSubtypeForTesting) {
- return sForcedSubtypeForTesting;
- }
return mRichImm.getCurrentInputMethodSubtype(getNoLanguageSubtype());
}
@@ -297,8 +279,8 @@ public final class SubtypeSwitcher {
if (mNoLanguageSubtype != null) {
return mNoLanguageSubtype;
}
- Log.w(TAG, "Can't find any language with QWERTY subtype");
- Log.w(TAG, "No input method subtype found; returning dummy subtype: "
+ Log.w(TAG, "Can't find no lanugage with QWERTY subtype");
+ Log.w(TAG, "No input method subtype found; return dummy subtype: "
+ DUMMY_NO_LANGUAGE_SUBTYPE);
return DUMMY_NO_LANGUAGE_SUBTYPE;
}
@@ -311,9 +293,8 @@ public final class SubtypeSwitcher {
if (mEmojiSubtype != null) {
return mEmojiSubtype;
}
- Log.w(TAG, "Can't find emoji subtype");
- Log.w(TAG, "No input method subtype found; returning dummy subtype: "
- + DUMMY_EMOJI_SUBTYPE);
+ Log.w(TAG, "Can't find Emoji subtype");
+ Log.w(TAG, "No input method subtype found; return dummy subtype: " + DUMMY_EMOJI_SUBTYPE);
return DUMMY_EMOJI_SUBTYPE;
}
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 0ecb41100..0a4c7a55d 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -25,6 +25,7 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
+import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
@@ -89,6 +90,7 @@ public final class Suggest {
PreferenceManager.getDefaultSharedPreferences(context))) {
mOnlyDictionarySetForDebug = new HashSet<String>();
mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION);
+ mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA);
}
}
@@ -192,6 +194,12 @@ public final class Suggest {
addOrReplaceDictionaryInternal(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
+ public void setPersonalizationPredictionDictionary(
+ final PersonalizationPredictionDictionary personalizationPredictionDictionary) {
+ addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA,
+ personalizationPredictionDictionary);
+ }
+
public void setPersonalizationDictionary(
final PersonalizationDictionary personalizationDictionary) {
addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION,
@@ -424,8 +432,7 @@ public final class Suggest {
final String scoreInfoString;
if (normalizedScore > 0) {
scoreInfoString = String.format(
- Locale.ROOT, "%d (%4.2f), %s", cur.mScore, normalizedScore,
- cur.mSourceDict.mDictType);
+ Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore);
} else {
scoreInfoString = Integer.toString(cur.mScore);
}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 9cb2f5bc4..6405b5e46 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -22,15 +22,14 @@ import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.ArrayList;
-import java.util.Locale;
public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary {
- public SynchronouslyLoadedUserBinaryDictionary(final Context context, final Locale locale) {
+ public SynchronouslyLoadedUserBinaryDictionary(final Context context, final String locale) {
this(context, locale, false);
}
- public SynchronouslyLoadedUserBinaryDictionary(final Context context, final Locale locale,
+ public SynchronouslyLoadedUserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) {
super(context, locale, alsoUseMoreRestrictiveLocales);
}
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 1dc3c54bb..15b3d8d02 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
@@ -74,21 +75,20 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
final private String mLocale;
final private boolean mAlsoUseMoreRestrictiveLocales;
- public UserBinaryDictionary(final Context context, final Locale locale) {
+ public UserBinaryDictionary(final Context context, final String locale) {
this(context, locale, false);
}
- public UserBinaryDictionary(final Context context, final Locale locale,
+ public UserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) {
- super(context, getDictNameWithLocale(NAME, locale), locale, Dictionary.TYPE_USER,
+ super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER,
false /* isUpdatable */);
if (null == locale) throw new NullPointerException(); // Catch the error earlier
- final String localeStr = locale.toString();
- if (SubtypeLocaleUtils.NO_LANGUAGE.equals(localeStr)) {
+ if (SubtypeLocaleUtils.NO_LANGUAGE.equals(locale)) {
// If we don't have a locale, insert into the "all locales" user dictionary.
mLocale = USER_DICTIONARY_ALL_LANGUAGES;
} else {
- mLocale = localeStr;
+ mLocale = locale;
}
mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
// Perform a managed query. The Activity will handle closing and re-querying the cursor
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 2f81d15d5..039dadc66 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -48,10 +48,6 @@ public final class WordComposer {
// at any given time. However this is not limited in size, while mPrimaryKeyCodes is limited
// to MAX_WORD_LENGTH code points.
private final StringBuilder mTypedWord;
- // The previous word (before the composing word). Used as context for suggestions. May be null
- // after resetting and before starting a new composing word, or when there is no context like
- // at the start of text for example.
- private String mPreviousWord;
private String mAutoCorrection;
private boolean mIsResumed;
private boolean mIsBatchMode;
@@ -89,7 +85,6 @@ public final class WordComposer {
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
- mPreviousWord = null;
refreshSize();
}
@@ -106,7 +101,6 @@ public final class WordComposer {
mIsBatchMode = source.mIsBatchMode;
mCursorPositionWithinWord = source.mCursorPositionWithinWord;
mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion;
- mPreviousWord = source.mPreviousWord;
refreshSize();
}
@@ -124,7 +118,6 @@ public final class WordComposer {
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
- mPreviousWord = null;
refreshSize();
}
@@ -291,13 +284,8 @@ public final class WordComposer {
/**
* Set the currently composing word to the one passed as an argument.
* This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
- * @param word the char sequence to set as the composing word.
- * @param previousWord the previous word, to use as context for suggestions. Can be null if
- * the context is nil (typically, at start of text).
- * @param keyboard the keyboard this is typed on, for coordinate info/proximity.
*/
- public void setComposingWord(final CharSequence word, final String previousWord,
- final Keyboard keyboard) {
+ public void setComposingWord(final CharSequence word, final Keyboard keyboard) {
reset();
final int length = word.length();
for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
@@ -305,7 +293,6 @@ public final class WordComposer {
addKeyInfo(codePoint, keyboard);
}
mIsResumed = true;
- mPreviousWord = previousWord;
}
/**
@@ -356,10 +343,6 @@ public final class WordComposer {
return mTypedWord.toString();
}
- public String getPreviousWord() {
- return mPreviousWord;
- }
-
/**
* Whether or not the user typed a capital letter as the first letter in the word
* @return capitalization preference
@@ -405,21 +388,18 @@ public final class WordComposer {
}
/**
- * Saves the caps mode and the previous word at the start of composing.
+ * Saves the caps mode at the start of composing.
*
- * WordComposer needs to know about the caps mode for several reasons. The first is, we need
- * to know after the fact what the reason was, to register the correct form into the user
- * history dictionary: if the word was automatically capitalized, we should insert it in
- * all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
+ * WordComposer needs to know about this for several reasons. The first is, we need to know
+ * after the fact what the reason was, to register the correct form into the user history
+ * dictionary: if the word was automatically capitalized, we should insert it in all-lower
+ * case but if it's a manual pressing of shift, then it should be inserted as is.
* Also, batch input needs to know about the current caps mode to display correctly
* capitalized suggestions.
* @param mode the mode at the time of start
- * @param previousWord the previous word as context for suggestions. May be null if none.
*/
- public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
- final String previousWord) {
+ public void setCapitalizedModeAtStartComposingTime(final int mode) {
mCapitalizedMode = mode;
- mPreviousWord = previousWord;
}
/**
@@ -471,7 +451,6 @@ public final class WordComposer {
mCapsCount = 0;
mDigitsCount = 0;
mIsBatchMode = false;
- mPreviousWord = mTypedWord.toString();
mTypedWord.setLength(0);
mCodePointSize = 0;
mTrailingSingleQuotesCount = 0;
@@ -485,8 +464,7 @@ public final class WordComposer {
return lastComposedWord;
}
- public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
- final String previousWord) {
+ public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
mInputPointers.set(lastComposedWord.mInputPointers);
mTypedWord.setLength(0);
@@ -497,7 +475,6 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize;
mRejectedBatchModeSuggestion = null;
mIsResumed = true;
- mPreviousWord = previousWord;
}
public boolean isBatchMode() {
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
index f8fa68f45..fda97dafc 100644
--- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -32,35 +32,36 @@ import java.util.TreeMap;
* A base class of the binary dictionary decoder.
*/
public abstract class AbstractDictDecoder implements DictDecoder {
- private static final int SUCCESS = 0;
- private static final int ERROR_CANNOT_READ = 1;
- private static final int ERROR_WRONG_FORMAT = 2;
-
- protected FileHeader readHeader(final DictBuffer headerBuffer)
+ protected FileHeader readHeader(final DictBuffer dictBuffer)
throws IOException, UnsupportedFormatException {
- if (headerBuffer == null) {
+ if (dictBuffer == null) {
openDictBuffer();
}
- final int version = HeaderReader.readVersion(headerBuffer);
+ final int version = HeaderReader.readVersion(dictBuffer);
if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
|| version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
throw new UnsupportedFormatException("Unsupported version : " + version);
}
// TODO: Remove this field.
- final int optionsFlags = HeaderReader.readOptionFlags(headerBuffer);
- final int headerSize = HeaderReader.readHeaderSize(headerBuffer);
+ final int optionsFlags = HeaderReader.readOptionFlags(dictBuffer);
+
+ final int headerSize = HeaderReader.readHeaderSize(dictBuffer);
+
if (headerSize < 0) {
throw new UnsupportedFormatException("header size can't be negative.");
}
- final HashMap<String, String> attributes = HeaderReader.readAttributes(headerBuffer,
+ final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer,
headerSize);
final FileHeader header = new FileHeader(headerSize,
- new FusionDictionary.DictionaryOptions(attributes),
- new FormatOptions(version,
- 0 != (optionsFlags & FormatSpec.CONTAINS_TIMESTAMP_FLAG)));
+ new FusionDictionary.DictionaryOptions(attributes,
+ 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
+ 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
+ new FormatOptions(version,
+ 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE),
+ 0 != (optionsFlags & FormatSpec.CONTAINS_TIMESTAMP_FLAG)));
return header;
}
@@ -203,25 +204,4 @@ public abstract class AbstractDictDecoder implements DictDecoder {
return readLength;
}
}
-
- /**
- * Check whether the header contains the expected information. This is a no-error method,
- * that will return an error code and never throw a checked exception.
- * @return an error code, either ERROR_* or SUCCESS.
- */
- private int checkHeader() {
- try {
- readHeader();
- } catch (IOException e) {
- return ERROR_CANNOT_READ;
- } catch (UnsupportedFormatException e) {
- return ERROR_WRONG_FORMAT;
- }
- return SUCCESS;
- }
-
- @Override
- public boolean hasValidRawBinaryDictionary() {
- return checkHeader() == SUCCESS;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 7f0aa777f..216492b4d 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -24,9 +24,12 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
@@ -166,14 +169,6 @@ public final class BinaryDictDecoderUtils {
return size;
}
- static int getCharArraySize(final int[] chars, final int start, final int end) {
- int size = 0;
- for (int i = start; i < end; ++i) {
- size += getCharSize(chars[i]);
- }
- return size;
- }
-
/**
* Writes a char array to a byte buffer.
*
@@ -205,7 +200,8 @@ public final class BinaryDictDecoderUtils {
* @param word the string to write.
* @return the size written, in bytes.
*/
- static int writeString(final byte[] buffer, final int origin, final String word) {
+ static int writeString(final byte[] buffer, final int origin,
+ final String word) {
final int length = word.length();
int index = origin;
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
@@ -227,62 +223,22 @@ public final class BinaryDictDecoderUtils {
*
* This will also write the terminator byte.
*
- * @param stream the OutputStream to write to.
+ * @param buffer the OutputStream to write to.
* @param word the string to write.
- * @return the size written, in bytes.
*/
- static int writeString(final OutputStream stream, final String word) throws IOException {
+ static void writeString(final OutputStream buffer, final String word) throws IOException {
final int length = word.length();
- int written = 0;
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
final int codePoint = word.codePointAt(i);
- final int charSize = getCharSize(codePoint);
- if (1 == charSize) {
- stream.write((byte) codePoint);
- } else {
- stream.write((byte) (0xFF & (codePoint >> 16)));
- stream.write((byte) (0xFF & (codePoint >> 8)));
- stream.write((byte) (0xFF & codePoint));
- }
- written += charSize;
- }
- stream.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
- written += FormatSpec.PTNODE_TERMINATOR_SIZE;
- return written;
- }
-
- /**
- * Writes an array of code points with our character format to an OutputStream.
- *
- * This will also write the terminator byte.
- *
- * @param stream the OutputStream to write to.
- * @param codePoints the array of code points
- * @return the size written, in bytes.
- */
- // TODO: Merge this method with writeCharArray and rename the various write* methods to
- // make the difference clear.
- static int writeCodePoints(final OutputStream stream, final int[] codePoints,
- final int startIndex, final int endIndex)
- throws IOException {
- int written = 0;
- for (int i = startIndex; i < endIndex; ++i) {
- final int codePoint = codePoints[i];
- final int charSize = getCharSize(codePoint);
- if (1 == charSize) {
- stream.write((byte) codePoint);
+ if (1 == getCharSize(codePoint)) {
+ buffer.write((byte) codePoint);
} else {
- stream.write((byte) (0xFF & (codePoint >> 16)));
- stream.write((byte) (0xFF & (codePoint >> 8)));
- stream.write((byte) (0xFF & codePoint));
+ buffer.write((byte) (0xFF & (codePoint >> 16)));
+ buffer.write((byte) (0xFF & (codePoint >> 8)));
+ buffer.write((byte) (0xFF & codePoint));
}
- written += charSize;
}
- if (endIndex - startIndex > 1) {
- stream.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
- written += FormatSpec.PTNODE_TERMINATOR_SIZE;
- }
- return written;
+ buffer.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
}
/**
@@ -330,7 +286,7 @@ public final class BinaryDictDecoderUtils {
static int readChildrenAddress(final DictBuffer dictBuffer,
final int optionFlags, final FormatOptions options) {
- if (options.supportsDynamicUpdate()) {
+ if (options.mSupportsDynamicUpdate) {
final int address = dictBuffer.readUnsignedInt24();
if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
if ((address & FormatSpec.MSB24) != 0) {
@@ -540,11 +496,11 @@ public final class BinaryDictDecoderUtils {
}
// reach the end of the array.
- if (options.supportsDynamicUpdate()) {
+ if (options.mSupportsDynamicUpdate) {
final boolean hasValidForwardLink = dictDecoder.readAndFollowForwardLink();
if (!hasValidForwardLink) break;
}
- } while (options.supportsDynamicUpdate() && dictDecoder.hasNextPtNodeArray());
+ } while (options.mSupportsDynamicUpdate && dictDecoder.hasNextPtNodeArray());
final PtNodeArray nodeArray = new PtNodeArray(nodeArrayContents);
nodeArray.mCachedAddressBeforeUpdate = nodeArrayOriginPos;
@@ -600,7 +556,7 @@ public final class BinaryDictDecoderUtils {
Map<Integer, PtNodeArray> reverseNodeArrayMapping = new TreeMap<Integer, PtNodeArray>();
Map<Integer, PtNode> reversePtNodeMapping = new TreeMap<Integer, PtNode>();
- final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mBodyOffset,
+ final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mHeaderSize,
reverseNodeArrayMapping, reversePtNodeMapping, fileHeader.mFormatOptions);
FusionDictionary newDict = new FusionDictionary(root, fileHeader.mDictionaryOptions);
@@ -636,10 +592,32 @@ public final class BinaryDictDecoderUtils {
/**
* Basic test to find out whether the file is a binary dictionary or not.
*
+ * Concretely this only tests the magic number.
+ *
* @param file The file to test.
* @return true if it's a binary dictionary, false otherwise
*/
public static boolean isBinaryDictionary(final File file) {
- return FormatSpec.getDictDecoder(file).hasValidRawBinaryDictionary();
+ FileInputStream inStream = null;
+ try {
+ inStream = new FileInputStream(file);
+ final ByteBuffer buffer = inStream.getChannel().map(
+ FileChannel.MapMode.READ_ONLY, 0, file.length());
+ final int version = getFormatVersion(new ByteBufferDictBuffer(buffer));
+ return (version >= FormatSpec.MINIMUM_SUPPORTED_VERSION
+ && version <= FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+ } catch (FileNotFoundException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
}
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 8ba0797de..f761829de 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -17,9 +17,9 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
@@ -160,7 +160,7 @@ public class BinaryDictEncoderUtils {
node.mCachedSize = nodeSize;
size += nodeSize;
}
- if (options.supportsDynamicUpdate()) {
+ if (options.mSupportsDynamicUpdate) {
size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
}
ptNodeArray.mCachedSize = size;
@@ -245,26 +245,6 @@ public class BinaryDictEncoderUtils {
}
}
- static void writeUIntToDictBuffer(final DictBuffer dictBuffer, final int value,
- final int size) {
- switch(size) {
- case 4:
- dictBuffer.put((byte) ((value >> 24) & 0xFF));
- /* fall through */
- case 3:
- dictBuffer.put((byte) ((value >> 16) & 0xFF));
- /* fall through */
- case 2:
- dictBuffer.put((byte) ((value >> 8) & 0xFF));
- /* fall through */
- case 1:
- dictBuffer.put((byte) (value & 0xFF));
- break;
- default:
- /* nop */
- }
- }
-
// End utility methods
// This method is responsible for finding a nice ordering of the nodes that favors run-time
@@ -397,7 +377,7 @@ public class BinaryDictEncoderUtils {
nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
}
}
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
nodeSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
} else if (null != ptNode.mChildren) {
nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
@@ -417,7 +397,7 @@ public class BinaryDictEncoderUtils {
ptNode.mCachedSize = nodeSize;
size += nodeSize;
}
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
}
if (ptNodeArray.mCachedSize != size) {
@@ -533,7 +513,7 @@ public class BinaryDictEncoderUtils {
if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
} while (changesDone);
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
computeParentAddresses(flatNodes);
}
final PtNodeArray lastPtNodeArray = flatNodes.get(flatNodes.size() - 1);
@@ -642,7 +622,7 @@ public class BinaryDictEncoderUtils {
byte flags = 0;
if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
flags |= FormatSpec.FLAG_IS_NOT_MOVED;
} else if (true) {
switch (childrenAddressSize) {
@@ -710,13 +690,6 @@ public class BinaryDictEncoderUtils {
+ word + " is " + unigramFrequency);
bigramFrequency = unigramFrequency;
}
- bigramFlags += getBigramFrequencyDiff(unigramFrequency, bigramFrequency)
- & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
- return bigramFlags;
- }
-
- public static int getBigramFrequencyDiff(final int unigramFrequency,
- final int bigramFrequency) {
// We compute the difference between 255 (which means probability = 1) and the
// unigram score. We split this into a number of discrete steps.
// Now, the steps are numbered 0~15; 0 represents an increase of 1 step while 15
@@ -750,15 +723,22 @@ public class BinaryDictEncoderUtils {
// include this bigram in the dictionary. For now, register as 0, and live with the
// small over-estimation that we get in this case. TODO: actually remove this bigram
// if discretizedFrequency < 0.
- return discretizedFrequency > 0 ? discretizedFrequency : 0;
+ final int finalBigramFrequency = discretizedFrequency > 0 ? discretizedFrequency : 0;
+ bigramFlags += finalBigramFrequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
+ return bigramFlags;
}
/**
- * Makes the 2-byte value for options flags. Unused at the moment, and always 0.
+ * Makes the 2-byte value for options flags.
*/
- private static final int makeOptionsValue(final FormatOptions formatOptions) {
- // TODO: why doesn't this handle CONTAINS_TIMESTAMP_FLAG?
- return 0;
+ private static final int makeOptionsValue(final FusionDictionary dictionary,
+ final FormatOptions formatOptions) {
+ final DictionaryOptions options = dictionary.mOptions;
+ final boolean hasBigrams = dictionary.hasBigrams();
+ return (options.mFrenchLigatureProcessing ? FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG : 0)
+ + (options.mGermanUmlautProcessing ? FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG : 0)
+ + (hasBigrams ? FormatSpec.CONTAINS_BIGRAMS_FLAG : 0)
+ + (formatOptions.mSupportsDynamicUpdate ? FormatSpec.SUPPORTS_DYNAMIC_UPDATE : 0);
}
/**
@@ -846,7 +826,7 @@ public class BinaryDictEncoderUtils {
}
dictEncoder.writePtNode(ptNode, parentPosition, formatOptions, dict);
}
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
dictEncoder.writeForwardLinkAddress(FormatSpec.NO_FORWARD_LINK_ADDRESS);
}
if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate
@@ -947,7 +927,7 @@ public class BinaryDictEncoderUtils {
headerBuffer.write((byte) (0xFF & version));
// Options flags
- final int options = makeOptionsValue(formatOptions);
+ final int options = makeOptionsValue(dict, formatOptions);
headerBuffer.write((byte) (0xFF & (options >> 8)));
headerBuffer.write((byte) (0xFF & options));
final int headerSizeOffset = headerBuffer.size();
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 640d778bb..d5516ef46 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -62,7 +62,7 @@ public final class BinaryDictIOUtils {
* Retrieves all node arrays without recursive call.
*/
private static void readUnigramsAndBigramsBinaryInner(final DictDecoder dictDecoder,
- final int bodyOffset, final Map<Integer, String> words,
+ final int headerSize, final Map<Integer, String> words,
final Map<Integer, Integer> frequencies,
final Map<Integer, ArrayList<PendingAttribute>> bigrams,
final FormatOptions formatOptions) {
@@ -71,7 +71,7 @@ public final class BinaryDictIOUtils {
Stack<Position> stack = new Stack<Position>();
int index = 0;
- Position initPos = new Position(bodyOffset, 0);
+ Position initPos = new Position(headerSize, 0);
stack.push(initPos);
while (!stack.empty()) {
@@ -112,7 +112,7 @@ public final class BinaryDictIOUtils {
}
if (p.mPosition == p.mNumOfPtNode) {
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
final boolean hasValidForwardLinkAddress =
dictDecoder.readAndFollowForwardLink();
if (hasValidForwardLinkAddress && dictDecoder.hasNextPtNodeArray()) {
@@ -154,7 +154,7 @@ public final class BinaryDictIOUtils {
UnsupportedFormatException {
// Read header
final FileHeader header = dictDecoder.readHeader();
- readUnigramsAndBigramsBinaryInner(dictDecoder, header.mBodyOffset, words,
+ readUnigramsAndBigramsBinaryInner(dictDecoder, header.mHeaderSize, words,
frequencies, bigrams, header.mFormatOptions);
}
@@ -228,7 +228,7 @@ public final class BinaryDictIOUtils {
// a forward link address that we need to consult and possibly resume
// search on the next node array in the linked list.
if (foundNextPtNode) break;
- if (!header.mFormatOptions.supportsDynamicUpdate()) {
+ if (!header.mFormatOptions.mSupportsDynamicUpdate) {
return FormatSpec.NOT_VALID_WORD;
}
@@ -245,7 +245,8 @@ public final class BinaryDictIOUtils {
/**
* @return the size written, in bytes. Always 3 bytes.
*/
- static int writeSInt24ToBuffer(final DictBuffer dictBuffer, final int value) {
+ static int writeSInt24ToBuffer(final DictBuffer dictBuffer,
+ final int value) {
final int absValue = Math.abs(value);
dictBuffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
dictBuffer.put((byte)((absValue >> 8) & 0xFF));
@@ -300,6 +301,35 @@ public final class BinaryDictIOUtils {
}
/**
+ * Write a string to a stream.
+ *
+ * @param destination the stream to write.
+ * @param word the string to be written.
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int writeString(final OutputStream destination, final String word)
+ throws IOException {
+ int size = 0;
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (CharEncoding.getCharSize(codePoint) == 1) {
+ destination.write((byte)codePoint);
+ size++;
+ } else {
+ destination.write((byte)(0xFF & (codePoint >> 16)));
+ destination.write((byte)(0xFF & (codePoint >> 8)));
+ destination.write((byte)(0xFF & codePoint));
+ size += 3;
+ }
+ }
+ destination.write((byte)FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
+ size += FormatSpec.PTNODE_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
* Write a PtNode to an output stream from a PtNodeInfo.
* A PtNode is an in-memory representation of a node in the patricia trie.
* A PtNode info is a container for low-level information about how the
@@ -357,7 +387,7 @@ public final class BinaryDictIOUtils {
destination.write((byte)BinaryDictEncoderUtils.makeShortcutFlags(
shortcutIterator.hasNext(), target.mFrequency));
size++;
- size += CharEncoding.writeString(destination, target.mWord);
+ size += writeString(destination, target.mWord);
}
}
@@ -415,27 +445,6 @@ public final class BinaryDictIOUtils {
}
/**
- * Writes a PtNodeCount to the stream.
- *
- * @param destination the stream to write.
- * @param ptNodeCount the count.
- * @return the size written in bytes.
- */
- static int writePtNodeCount(final OutputStream destination, final int ptNodeCount)
- throws IOException {
- final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount);
- // the count must fit on one byte or two bytes.
- // Please see comments in FormatSpec.
- if (countSize != 1 && countSize != 2) {
- throw new RuntimeException("Strange size from getPtNodeCountSize : " + countSize);
- }
- final int encodedPtNodeCount = (countSize == 2) ?
- (ptNodeCount | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG) : ptNodeCount;
- BinaryDictEncoderUtils.writeUIntToStream(destination, encodedPtNodeCount, countSize);
- return countSize;
- }
-
- /**
* Write a node array to the stream.
*
* @param destination the stream to write.
@@ -445,7 +454,20 @@ public final class BinaryDictIOUtils {
*/
static int writeNodes(final OutputStream destination, final PtNodeInfo[] infos)
throws IOException {
- int size = writePtNodeCount(destination, infos.length);
+ int size = getPtNodeCountSize(infos.length);
+ switch (getPtNodeCountSize(infos.length)) {
+ case 1:
+ destination.write((byte)infos.length);
+ break;
+ case 2:
+ final int encodedPtNodeCount =
+ infos.length | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG;
+ destination.write((byte)(encodedPtNodeCount >> 8));
+ destination.write((byte)(encodedPtNodeCount & 0xFF));
+ break;
+ default:
+ throw new RuntimeException("Invalid node count size.");
+ }
for (final PtNodeInfo info : infos) size += writePtNode(destination, info);
writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS);
return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
@@ -507,7 +529,7 @@ public final class BinaryDictIOUtils {
* Helper method to check whether the node is moved.
*/
public static boolean isMovedPtNode(final int flags, final FormatOptions options) {
- return options.supportsDynamicUpdate()
+ return options.mSupportsDynamicUpdate
&& ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
}
@@ -516,14 +538,14 @@ public final class BinaryDictIOUtils {
*/
public static boolean supportsDynamicUpdate(final FormatOptions options) {
return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_DYNAMIC_UPDATE
- && options.supportsDynamicUpdate();
+ && options.mSupportsDynamicUpdate;
}
/**
* Helper method to check whether the node is deleted.
*/
public static boolean isDeletedPtNode(final int flags, final FormatOptions formatOptions) {
- return formatOptions.supportsDynamicUpdate()
+ return formatOptions.mSupportsDynamicUpdate
&& ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
}
@@ -546,7 +568,7 @@ public final class BinaryDictIOUtils {
static int getChildrenAddressSize(final int optionFlags,
final FormatOptions formatOptions) {
- if (formatOptions.supportsDynamicUpdate()) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+ if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
return 1;
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
index b4838f00f..3dbeee099 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -35,7 +35,6 @@ import java.util.TreeMap;
/**
* An interface of binary dictionary decoders.
*/
-// TODO: Straighten out responsibility for the buffer's file pointer.
public interface DictDecoder {
/**
@@ -44,7 +43,7 @@ public interface DictDecoder {
public FileHeader readHeader() throws IOException, UnsupportedFormatException;
/**
- * Reads PtNode from ptNodePos.
+ * Reads PtNode from nodeAddress.
* @param ptNodePos the position of PtNode.
* @param formatOptions the format options.
* @return PtNodeInfo.
@@ -128,8 +127,7 @@ public interface DictDecoder {
* Opens the dictionary file and makes DictBuffer.
*/
@UsedForTesting
- public void openDictBuffer() throws FileNotFoundException, IOException,
- UnsupportedFormatException;
+ public void openDictBuffer() throws FileNotFoundException, IOException;
@UsedForTesting
public boolean isDictBufferOpen();
@@ -230,9 +228,4 @@ public interface DictDecoder {
}
public void skipPtNode(final FormatOptions formatOptions);
-
- /**
- * @return whether this decoder has a valid binary dictionary that it can decode.
- */
- public boolean hasValidRawBinaryDictionary();
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
index ff03190a3..28da9ffdd 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
@@ -37,7 +37,7 @@ import java.util.Arrays;
@UsedForTesting
public final class DynamicBinaryDictIOUtils {
private static final boolean DBG = false;
- static final int MAX_JUMPS = 10000;
+ private static final int MAX_JUMPS = 10000;
private DynamicBinaryDictIOUtils() {
// This utility class is not publicly instantiable.
@@ -61,7 +61,7 @@ public final class DynamicBinaryDictIOUtils {
final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
final int originalPosition = dictBuffer.position();
dictBuffer.position(ptNodeOriginAddress);
- if (!formatOptions.supportsDynamicUpdate()) {
+ if (!formatOptions.mSupportsDynamicUpdate) {
throw new RuntimeException("this file format does not support parent addresses");
}
final int flags = dictBuffer.readUnsignedByte();
@@ -102,7 +102,7 @@ public final class DynamicBinaryDictIOUtils {
}
if (!dictUpdater.readAndFollowForwardLink()) break;
if (dictUpdater.getPosition() == FormatSpec.NO_FORWARD_LINK_ADDRESS) break;
- } while (formatOptions.supportsDynamicUpdate());
+ } while (formatOptions.mSupportsDynamicUpdate);
dictUpdater.setPosition(originalPosition);
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 20ddba836..b56234f6d 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -40,8 +40,12 @@ public final class FormatSpec {
* p | not used 3 bits
* t | each unigram and bigram entry has a time stamp?
* i | 1 bit, 1 = yes, 0 = no : CONTAINS_TIMESTAMP_FLAG
- * o |
- * nflags
+ * o | has bigrams ? 1 bit, 1 = yes, 0 = no : CONTAINS_BIGRAMS_FLAG
+ * n | FRENCH_LIGATURE_PROCESSING_FLAG
+ * f | supports dynamic updates ? 1 bit, 1 = yes, 0 = no : SUPPORTS_DYNAMIC_UPDATE
+ * l | GERMAN_UMLAUT_PROCESSING_FLAG
+ * a |
+ * gs
*
* h |
* e | size of the file header, 4bytes
@@ -78,36 +82,45 @@ public final class FormatSpec {
* s
*
* f |
- * o | forward link address, 3byte
- * r | 1 byte = bbbbbbbb match
- * w | case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte)
- * a | otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte
- * r |
- * dlinkaddress
+ * o | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header)
+ * r | forward link address, 3byte
+ * w | 1 byte = bbbbbbbb match
+ * a | case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte)
+ * r | otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte
+ * d |
+ * linkaddress
*/
/* Node (FusionDictionary.PtNode) layout is as follows:
- * | is moved ? 2 bits, 11 = no : FLAG_IS_NOT_MOVED
- * | This must be the same as FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
- * | 01 = yes : FLAG_IS_MOVED
- * f | the new address is stored in the same place as the parent address
- * l | is deleted? 10 = yes : FLAG_IS_DELETED
- * a | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
- * g | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
- * s | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
+ * | IF !SUPPORTS_DYNAMIC_UPDATE
+ * | addressType xx : mask with MASK_CHILDREN_ADDRESS_TYPE
+ * | 2 bits, 00 = no children : FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS
+ * f | 01 = 1 byte : FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE
+ * l | 10 = 2 bytes : FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES
+ * a | 11 = 3 bytes : FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
+ * g | ELSE
+ * s | is moved ? 2 bits, 11 = no : FLAG_IS_NOT_MOVED
+ * | This must be the same as FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
+ * | 01 = yes : FLAG_IS_MOVED
+ * | the new address is stored in the same place as the parent address
+ * | is deleted? 10 = yes : FLAG_IS_DELETED
+ * | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
+ * | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
+ * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
* | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS
* | is not a word ? 1 bit, 1 = yes, 0 = no : FLAG_IS_NOT_A_WORD
* | is blacklisted ? 1 bit, 1 = yes, 0 = no : FLAG_IS_BLACKLISTED
*
* p |
- * a | parent address, 3byte
- * r | 1 byte = bbbbbbbb match
- * e | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
- * n | otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte
- * t | This address is relative to the head of the PtNode.
- * a | If the node doesn't have a parent, this field is set to 0.
+ * a | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header)
+ * r | parent address, 3byte
+ * e | 1 byte = bbbbbbbb match
+ * n | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
+ * t | otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte
+ * a | This address is relative to the head of the PtNode.
+ * d | If the node doesn't have a parent, this field is set to 0.
* d |
- * dress
+ * ress
*
* c | IF FLAG_HAS_MULTIPLE_CHARS
* h | char, char, char, char n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers
@@ -121,16 +134,23 @@ public final class FormatSpec {
* e | frequency 1 byte
* q |
*
- * c |
- * h | children address, 3 bytes
- * i | 1 byte = bbbbbbbb match
- * l | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
- * d | otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte
- * r | if this node doesn't have children, this field is set to 0.
- * e | (see BinaryDictEncoderUtils#writeVariableSignedAddress)
- * n | This address is relative to the position of this field.
- * a |
- * ddress
+ * c | IF SUPPORTS_DYNAMIC_UPDATE
+ * h | children address, 3 bytes
+ * i | 1 byte = bbbbbbbb match
+ * l | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
+ * d | otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte
+ * r | if this node doesn't have children, this field is set to 0.
+ * e | (see BinaryDictEncoderUtils#writeVariableSignedAddress)
+ * n | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType
+ * a | // nothing
+ * d | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType
+ * d | children address, 1 byte
+ * r | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType
+ * e | children address, 2 bytes
+ * s | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType
+ * s | children address, 3 bytes
+ * | END
+ * | This address is relative to the position of this field.
*
* | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
* | shortcut string list
@@ -179,22 +199,20 @@ public final class FormatSpec {
*/
public static final int MAGIC_NUMBER = 0x9BC13AFE;
+ static final int MINIMUM_SUPPORTED_VERSION = 2;
+ static final int MAXIMUM_SUPPORTED_VERSION = 4;
static final int NOT_A_VERSION_NUMBER = -1;
static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3;
static final int FIRST_VERSION_WITH_TERMINAL_ID = 4;
-
- // These MUST have the same values as the relevant constants in format_utils.h.
- // From version 4 on, we use version * 100 + revision as a version number. That allows
- // us to change the format during development while having testing devices remove
- // older files with each upgrade, while still having a readable versioning scheme.
- public static final int VERSION2 = 2;
- public static final int VERSION3 = 3;
- public static final int VERSION4 = 400;
- static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
- static final int MAXIMUM_SUPPORTED_VERSION = VERSION4;
+ static final int VERSION3 = 3;
+ static final int VERSION4 = 4;
// These options need to be the same numeric values as the one in the native reading code.
+ static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
// TODO: Make the native reading code read this variable.
+ static final int SUPPORTS_DYNAMIC_UPDATE = 0x2;
+ static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
+ static final int CONTAINS_BIGRAMS_FLAG = 0x8;
static final int CONTAINS_TIMESTAMP_FLAG = 0x10;
// TODO: Make this value adaptative to content data, store it in the header, and
@@ -245,10 +263,8 @@ public final class FormatSpec {
static final int PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
static final int PTNODE_SHORTCUT_LIST_SIZE_SIZE = 2;
- // These values are used only by version 4 or later. They MUST match the definitions in
- // ver4_dict_constants.cpp.
+ // These values are used only by version 4 or later.
static final String TRIE_FILE_EXTENSION = ".trie";
- public static final String HEADER_FILE_EXTENSION = ".header";
static final String FREQ_FILE_EXTENSION = ".freq";
static final String UNIGRAM_TIMESTAMP_FILE_EXTENSION = ".timestamp";
// tat = Terminal Address Table
@@ -262,9 +278,9 @@ public final class FormatSpec {
static final int UNIGRAM_TIMESTAMP_SIZE = 4;
// With the English main dictionary as of October 2013, the size of bigram address table is
- // is 345KB with the block size being 16.
- // This is 54% of that of full address table.
- static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16;
+ // is 584KB with the block size being 4.
+ // This is 91% of that of full address table.
+ static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4;
static final int BIGRAM_CONTENT_COUNT = 2;
static final int BIGRAM_FREQ_CONTENT_INDEX = 0;
static final int BIGRAM_TIMESTAMP_CONTENT_INDEX = 1;
@@ -277,7 +293,7 @@ public final class FormatSpec {
static final int SHORTCUT_CONTENT_COUNT = 1;
static final int SHORTCUT_CONTENT_INDEX = 0;
// With the English main dictionary as of October 2013, the size of shortcut address table is
- // 26KB with the block size being 64.
+ // 29KB with the block size being 64.
// This is only 4.4% of that of full address table.
static final int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64;
static final String SHORTCUT_CONTENT_ID = "_shortcut";
@@ -315,36 +331,43 @@ public final class FormatSpec {
*/
public static final class FormatOptions {
public final int mVersion;
+ public final boolean mSupportsDynamicUpdate;
public final boolean mHasTerminalId;
public final boolean mHasTimestamp;
-
@UsedForTesting
public FormatOptions(final int version) {
- this(version, false /* hasTimestamp */);
+ this(version, false);
}
- public FormatOptions(final int version, final boolean hasTimestamp) {
+ @UsedForTesting
+ public FormatOptions(final int version, final boolean supportsDynamicUpdate) {
+ this(version, supportsDynamicUpdate, false /* hasTimestamp */);
+ }
+
+ public FormatOptions(final int version, final boolean supportsDynamicUpdate,
+ final boolean hasTimestamp) {
mVersion = version;
+ if (version < FIRST_VERSION_WITH_DYNAMIC_UPDATE && supportsDynamicUpdate) {
+ throw new RuntimeException("Dynamic updates are only supported with versions "
+ + FIRST_VERSION_WITH_DYNAMIC_UPDATE + " and ulterior.");
+ }
+ mSupportsDynamicUpdate = supportsDynamicUpdate;
mHasTerminalId = (version >= FIRST_VERSION_WITH_TERMINAL_ID);
mHasTimestamp = hasTimestamp;
}
-
- public boolean supportsDynamicUpdate() {
- return mVersion >= FIRST_VERSION_WITH_DYNAMIC_UPDATE;
- }
}
/**
* Class representing file header.
*/
public static final class FileHeader {
- public final int mBodyOffset;
+ public final int mHeaderSize;
public final DictionaryOptions mDictionaryOptions;
public final FormatOptions mFormatOptions;
// Note that these are corresponding definitions in native code in latinime::HeaderPolicy
// and latinime::HeaderReadWriteUtils.
+ public static final String SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE = "SUPPORTS_DYNAMIC_UPDATE";
public static final String USES_FORGETTING_CURVE_ATTRIBUTE = "USES_FORGETTING_CURVE";
- public static final String HAS_HISTORICAL_INFO_ATTRIBUTE = "HAS_HISTORICAL_INFO";
public static final String ATTRIBUTE_VALUE_TRUE = "1";
public static final String DICTIONARY_VERSION_ATTRIBUTE = "version";
@@ -353,18 +376,9 @@ public final class FormatSpec {
private static final String DICTIONARY_DESCRIPTION_ATTRIBUTE = "description";
public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
final FormatOptions formatOptions) {
+ mHeaderSize = headerSize;
mDictionaryOptions = dictionaryOptions;
mFormatOptions = formatOptions;
- mBodyOffset = formatOptions.mVersion < VERSION4 ? headerSize : 0;
- if (null == getLocaleString()) {
- throw new RuntimeException("Cannot create a FileHeader without a locale");
- }
- if (null == getVersion()) {
- throw new RuntimeException("Cannot create a FileHeader without a version");
- }
- if (null == getId()) {
- throw new RuntimeException("Cannot create a FileHeader without an ID");
- }
}
// Helper method to get the locale as a String
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index fdf2ae7b5..3bb218bea 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -303,9 +303,14 @@ public final class FusionDictionary implements Iterable<Word> {
* Options global to the dictionary.
*/
public static final class DictionaryOptions {
+ public final boolean mGermanUmlautProcessing;
+ public final boolean mFrenchLigatureProcessing;
public final HashMap<String, String> mAttributes;
- public DictionaryOptions(final HashMap<String, String> attributes) {
+ public DictionaryOptions(final HashMap<String, String> attributes,
+ final boolean germanUmlautProcessing, final boolean frenchLigatureProcessing) {
mAttributes = attributes;
+ mGermanUmlautProcessing = germanUmlautProcessing;
+ mFrenchLigatureProcessing = frenchLigatureProcessing;
}
@Override
public String toString() { // Convenience method
@@ -334,6 +339,14 @@ public final class FusionDictionary implements Iterable<Word> {
}
s.append("\n");
}
+ if (mGermanUmlautProcessing) {
+ s.append(indent);
+ s.append("Needs German umlaut processing\n");
+ }
+ if (mFrenchLigatureProcessing) {
+ s.append(indent);
+ s.append("Needs French ligature processing\n");
+ }
return s.toString();
}
}
@@ -688,6 +701,138 @@ public final class FusionDictionary implements Iterable<Word> {
}
/**
+ * Recursively count the number of nodes in a given branch of the trie.
+ *
+ * @param nodeArray the node array to count.
+ * @return the number of nodes in this branch.
+ */
+ public static int countNodeArrays(final PtNodeArray nodeArray) {
+ int size = 1;
+ for (int i = nodeArray.mData.size() - 1; i >= 0; --i) {
+ PtNode ptNode = nodeArray.mData.get(i);
+ if (null != ptNode.mChildren)
+ size += countNodeArrays(ptNode.mChildren);
+ }
+ return size;
+ }
+
+ // Recursively find out whether there are any bigrams.
+ // This can be pretty expensive especially if there aren't any (we return as soon
+ // as we find one, so it's much cheaper if there are bigrams)
+ private static boolean hasBigramsInternal(final PtNodeArray nodeArray) {
+ if (null == nodeArray) return false;
+ for (int i = nodeArray.mData.size() - 1; i >= 0; --i) {
+ PtNode ptNode = nodeArray.mData.get(i);
+ if (null != ptNode.mBigrams) return true;
+ if (hasBigramsInternal(ptNode.mChildren)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Finds out whether there are any bigrams in this dictionary.
+ *
+ * @return true if there is any bigram, false otherwise.
+ */
+ // TODO: this is expensive especially for large dictionaries without any bigram.
+ // The up side is, this is always accurate and correct and uses no memory. We should
+ // find a more efficient way of doing this, without compromising too much on memory
+ // and ease of use.
+ public boolean hasBigrams() {
+ return hasBigramsInternal(mRootNodeArray);
+ }
+
+ // Historically, the tails of the words were going to be merged to save space.
+ // However, that would prevent the code to search for a specific address in log(n)
+ // time so this was abandoned.
+ // The code is still of interest as it does add some compression to any dictionary
+ // that has no need for attributes. Implementations that does not read attributes should be
+ // able to read a dictionary with merged tails.
+ // Also, the following code does support frequencies, as in, it will only merges
+ // tails that share the same frequency. Though it would result in the above loss of
+ // performance while searching by address, it is still technically possible to merge
+ // tails that contain attributes, but this code does not take that into account - it does
+ // not compare attributes and will merge terminals with different attributes regardless.
+ public void mergeTails() {
+ MakedictLog.i("Do not merge tails");
+ return;
+
+// MakedictLog.i("Merging PtNodes. Number of PtNodes : " + countPtNodes(root));
+// MakedictLog.i("Number of PtNodes : " + countPtNodes(root));
+//
+// final HashMap<String, ArrayList<PtNodeArray>> repository =
+// new HashMap<String, ArrayList<PtNodeArray>>();
+// mergeTailsInner(repository, root);
+//
+// MakedictLog.i("Number of different pseudohashes : " + repository.size());
+// int size = 0;
+// for (ArrayList<PtNodeArray> a : repository.values()) {
+// size += a.size();
+// }
+// MakedictLog.i("Number of nodes after merge : " + (1 + size));
+// MakedictLog.i("Recursively seen nodes : " + countNodes(root));
+ }
+
+ // The following methods are used by the deactivated mergeTails()
+// private static boolean isEqual(PtNodeArray a, PtNodeArray b) {
+// if (null == a && null == b) return true;
+// if (null == a || null == b) return false;
+// if (a.data.size() != b.data.size()) return false;
+// final int size = a.data.size();
+// for (int i = size - 1; i >= 0; --i) {
+// PtNode aPtNode = a.data.get(i);
+// PtNode bPtNode = b.data.get(i);
+// if (aPtNode.frequency != bPtNode.frequency) return false;
+// if (aPtNode.alternates == null && bPtNode.alternates != null) return false;
+// if (aPtNode.alternates != null && !aPtNode.equals(bPtNode.alternates)) return false;
+// if (!Arrays.equals(aPtNode.chars, bPtNode.chars)) return false;
+// if (!isEqual(aPtNode.children, bPtNode.children)) return false;
+// }
+// return true;
+// }
+
+// static private HashMap<String, ArrayList<PtNodeArray>> mergeTailsInner(
+// final HashMap<String, ArrayList<PtNodeArray>> map, final PtNodeArray nodeArray) {
+// final ArrayList<PtNode> branches = nodeArray.data;
+// final int nodeSize = branches.size();
+// for (int i = 0; i < nodeSize; ++i) {
+// PtNode ptNode = branches.get(i);
+// if (null != ptNode.children) {
+// String pseudoHash = getPseudoHash(ptNode.children);
+// ArrayList<PtNodeArray> similarList = map.get(pseudoHash);
+// if (null == similarList) {
+// similarList = new ArrayList<PtNodeArray>();
+// map.put(pseudoHash, similarList);
+// }
+// boolean merged = false;
+// for (PtNodeArray similar : similarList) {
+// if (isEqual(ptNode.children, similar)) {
+// ptNode.children = similar;
+// merged = true;
+// break;
+// }
+// }
+// if (!merged) {
+// similarList.add(ptNode.children);
+// }
+// mergeTailsInner(map, ptNode.children);
+// }
+// }
+// return map;
+// }
+
+// private static String getPseudoHash(final PtNodeArray nodeArray) {
+// StringBuilder s = new StringBuilder();
+// for (PtNode ptNode : nodeArray.data) {
+// s.append(ptNode.frequency);
+// for (int ch : ptNode.chars) {
+// s.append(Character.toChars(ch));
+// }
+// }
+// return s.toString();
+// }
+
+ /**
* Iterator to walk through a dictionary.
*
* This is purely for convenience.
diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java
deleted file mode 100644
index 06088b651..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
-import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-/**
- * An auxiliary class for reading SparseTable and data written by SparseTableContentWriter.
- */
-public class SparseTableContentReader {
-
- /**
- * An interface of a function which is passed to SparseTableContentReader.read.
- */
- public interface SparseTableContentReaderInterface {
- /**
- * Reads data.
- *
- * @param buffer the DictBuffer. The position of the buffer is set to the head of data.
- */
- public void read(final DictBuffer buffer);
- }
-
- protected final int mContentCount;
- protected final int mBlockSize;
- protected final File mBaseDir;
- protected final File mLookupTableFile;
- protected final File[] mAddressTableFiles;
- protected final File[] mContentFiles;
- protected DictBuffer mLookupTableBuffer;
- protected final DictBuffer[] mAddressTableBuffers;
- private final DictBuffer[] mContentBuffers;
- protected final DictionaryBufferFactory mFactory;
-
- /**
- * Sole constructor of SparseTableContentReader.
- *
- * @param name the name of SparseTable.
- * @param blockSize the block size of the content table.
- * @param baseDir the directory which contains the files of the content table.
- * @param contentFilenames the file names of content files.
- * @param contentIds the ids of contents. These ids are used for a suffix of a name of
- * address files and content files.
- * @param factory the DictionaryBufferFactory which is used for opening the files.
- */
- public SparseTableContentReader(final String name, final int blockSize, final File baseDir,
- final String[] contentFilenames, final String[] contentIds,
- final DictionaryBufferFactory factory) {
- if (contentFilenames.length != contentIds.length) {
- throw new RuntimeException("The length of contentFilenames and the length of"
- + " contentIds are different " + contentFilenames.length + ", "
- + contentIds.length);
- }
- mBlockSize = blockSize;
- mBaseDir = baseDir;
- mFactory = factory;
- mContentCount = contentFilenames.length;
- mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
- mAddressTableFiles = new File[mContentCount];
- mContentFiles = new File[mContentCount];
- for (int i = 0; i < mContentCount; ++i) {
- mAddressTableFiles[i] = new File(mBaseDir,
- name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]);
- mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]);
- }
- mAddressTableBuffers = new DictBuffer[mContentCount];
- mContentBuffers = new DictBuffer[mContentCount];
- }
-
- public void openBuffers() throws FileNotFoundException, IOException {
- mLookupTableBuffer = mFactory.getDictionaryBuffer(mLookupTableFile);
- for (int i = 0; i < mContentCount; ++i) {
- mAddressTableBuffers[i] = mFactory.getDictionaryBuffer(mAddressTableFiles[i]);
- mContentBuffers[i] = mFactory.getDictionaryBuffer(mContentFiles[i]);
- }
- }
-
- protected void read(final int contentIndex, final int index,
- final SparseTableContentReaderInterface reader) {
- if (index < 0 || (index / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES
- >= mLookupTableBuffer.limit()) {
- return;
- }
-
- mLookupTableBuffer.position((index / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES);
- final int posInAddressTable = mLookupTableBuffer.readInt();
- if (posInAddressTable == SparseTable.NOT_EXIST) {
- return;
- }
-
- mAddressTableBuffers[contentIndex].position(
- (posInAddressTable + index % mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES);
- final int address = mAddressTableBuffers[contentIndex].readInt();
- if (address == SparseTable.NOT_EXIST) {
- return;
- }
-
- mContentBuffers[contentIndex].position(address);
- reader.read(mContentBuffers[contentIndex]);
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java
deleted file mode 100644
index 4518f21b9..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An auxiliary class for updating data associated with SparseTable.
- */
-public class SparseTableContentUpdater extends SparseTableContentReader {
- protected OutputStream mLookupTableOutStream;
- protected OutputStream[] mAddressTableOutStreams;
- protected OutputStream[] mContentOutStreams;
-
- public SparseTableContentUpdater(final String name, final int blockSize,
- final File baseDir, final String[] contentFilenames, final String[] contentIds,
- final DictionaryBufferFactory factory) {
- super(name, blockSize, baseDir, contentFilenames, contentIds, factory);
- mAddressTableOutStreams = new OutputStream[mContentCount];
- mContentOutStreams = new OutputStream[mContentCount];
- }
-
- protected void openStreamsAndBuffers() throws IOException {
- openBuffers();
- mLookupTableOutStream = new FileOutputStream(mLookupTableFile, true /* append */);
- for (int i = 0; i < mContentCount; ++i) {
- mAddressTableOutStreams[i] = new FileOutputStream(mAddressTableFiles[i],
- true /* append */);
- mContentOutStreams[i] = new FileOutputStream(mContentFiles[i], true /* append */);
- }
- }
-
- /**
- * Set the contentIndex-th elements of contentId-th table.
- *
- * @param contentId the id of the content table.
- * @param contentIndex the index where to set the valie.
- * @param value the value to set.
- */
- protected void setContentValue(final int contentId, final int contentIndex, final int value)
- throws IOException {
- if ((contentIndex / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES
- >= mLookupTableBuffer.limit()) {
- // Need to extend the lookup table
- final int currentSize = mLookupTableBuffer.limit()
- / SparseTable.SIZE_OF_INT_IN_BYTES;
- final int target = contentIndex / mBlockSize + 1;
- for (int i = currentSize; i < target; ++i) {
- BinaryDictEncoderUtils.writeUIntToStream(mLookupTableOutStream,
- SparseTable.NOT_EXIST, SparseTable.SIZE_OF_INT_IN_BYTES);
- }
- // We need to reopen the byte buffer of the lookup table because a MappedByteBuffer in
- // Java isn't expanded automatically when the underlying file is expanded.
- reopenLookupTable();
- }
-
- mLookupTableBuffer.position((contentIndex / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES);
- int posInAddressTable = mLookupTableBuffer.readInt();
- if (posInAddressTable == SparseTable.NOT_EXIST) {
- // Need to extend the address table
- mLookupTableBuffer.position(mLookupTableBuffer.position()
- - SparseTable.SIZE_OF_INT_IN_BYTES);
- posInAddressTable = mAddressTableBuffers[0].limit() / mBlockSize;
- BinaryDictEncoderUtils.writeUIntToDictBuffer(mLookupTableBuffer,
- posInAddressTable, SparseTable.SIZE_OF_INT_IN_BYTES);
- for (int i = 0; i < mContentCount; ++i) {
- for (int j = 0; j < mBlockSize; ++j) {
- BinaryDictEncoderUtils.writeUIntToStream(mAddressTableOutStreams[i],
- SparseTable.NOT_EXIST, SparseTable.SIZE_OF_INT_IN_BYTES);
- }
- }
- // We need to reopen the byte buffers of the address tables because a MappedByteBuffer
- // in Java isn't expanded automatically when the underlying file is expanded.
- reopenAddressTables();
- }
- posInAddressTable += (contentIndex % mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES;
-
- mAddressTableBuffers[contentId].position(posInAddressTable);
- BinaryDictEncoderUtils.writeUIntToDictBuffer(mAddressTableBuffers[contentId],
- value, SparseTable.SIZE_OF_INT_IN_BYTES);
- }
-
- private void reopenLookupTable() throws IOException {
- mLookupTableOutStream.flush();
- mLookupTableBuffer = mFactory.getDictionaryBuffer(mLookupTableFile);
- }
-
- private void reopenAddressTables() throws IOException {
- for (int i = 0; i < mContentCount; ++i) {
- mAddressTableOutStreams[i].flush();
- mAddressTableBuffers[i] = mFactory.getDictionaryBuffer(mAddressTableFiles[i]);
- }
- }
-
- protected void close() throws IOException {
- mLookupTableOutStream.close();
- for (final OutputStream stream : mAddressTableOutStreams) {
- stream.close();
- }
- for (final OutputStream stream : mContentOutStreams) {
- stream.close();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java
deleted file mode 100644
index 49f0fd624..000000000
--- a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An auxiliary class for writing data associated with SparseTable to files.
- */
-public class SparseTableContentWriter {
- public interface SparseTableContentWriterInterface {
- public void write(final OutputStream outStream) throws IOException;
- }
-
- private final int mContentCount;
- private final SparseTable mSparseTable;
- private final File mLookupTableFile;
- protected final File mBaseDir;
- private final File[] mAddressTableFiles;
- private final File[] mContentFiles;
- protected final OutputStream[] mContentOutStreams;
-
- /**
- * Sole constructor of SparseTableContentWriter.
- *
- * @param name the name of SparseTable.
- * @param initialCapacity the initial capacity of SparseTable.
- * @param blockSize the block size of the content table.
- * @param baseDir the directory which contains the files of the content table.
- * @param contentFilenames the file names of content files.
- * @param contentIds the ids of contents. These ids are used for a suffix of a name of address
- * files and content files.
- */
- public SparseTableContentWriter(final String name, final int initialCapacity,
- final int blockSize, final File baseDir, final String[] contentFilenames,
- final String[] contentIds) {
- if (contentFilenames.length != contentIds.length) {
- throw new RuntimeException("The length of contentFilenames and the length of"
- + " contentIds are different " + contentFilenames.length + ", "
- + contentIds.length);
- }
- mContentCount = contentFilenames.length;
- mSparseTable = new SparseTable(initialCapacity, blockSize, mContentCount);
- mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
- mAddressTableFiles = new File[mContentCount];
- mContentFiles = new File[mContentCount];
- mBaseDir = baseDir;
- for (int i = 0; i < mContentCount; ++i) {
- mAddressTableFiles[i] = new File(mBaseDir,
- name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]);
- mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]);
- }
- mContentOutStreams = new OutputStream[mContentCount];
- }
-
- public void openStreams() throws FileNotFoundException {
- for (int i = 0; i < mContentCount; ++i) {
- mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]);
- }
- }
-
- protected void write(final int contentIndex, final int index,
- final SparseTableContentWriterInterface writer) throws IOException {
- mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length());
- writer.write(mContentOutStreams[contentIndex]);
- mContentOutStreams[contentIndex].flush();
- }
-
- public void closeStreams() throws IOException {
- mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles);
- for (int i = 0; i < mContentCount; ++i) {
- mContentOutStreams[i].close();
- }
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
index 92eb861d6..5da34534e 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
@@ -169,7 +169,7 @@ public class Ver3DictEncoder implements DictEncoder {
private void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) {
final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
mPosition += BinaryDictEncoderUtils.writeSignedChildrenPosition(mBuffer, mPosition,
childrenPos);
} else {
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index 3be62f066..734223ec2 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -40,52 +40,26 @@ import java.util.Arrays;
public class Ver4DictDecoder extends AbstractDictDecoder {
private static final String TAG = Ver4DictDecoder.class.getSimpleName();
- protected static final int FILETYPE_TRIE = 1;
- protected static final int FILETYPE_FREQUENCY = 2;
- protected static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3;
- protected static final int FILETYPE_BIGRAM_FREQ = 4;
- protected static final int FILETYPE_SHORTCUT = 5;
- protected static final int FILETYPE_HEADER = 6;
-
- protected final File mDictDirectory;
- protected final DictionaryBufferFactory mBufferFactory;
+ private static final int FILETYPE_TRIE = 1;
+ private static final int FILETYPE_FREQUENCY = 2;
+ private static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3;
+ private static final int FILETYPE_BIGRAM_FREQ = 4;
+ private static final int FILETYPE_SHORTCUT = 5;
+
+ private final File mDictDirectory;
+ private final DictionaryBufferFactory mBufferFactory;
protected DictBuffer mDictBuffer;
- protected DictBuffer mHeaderBuffer;
- protected DictBuffer mFrequencyBuffer;
- protected DictBuffer mTerminalAddressTableBuffer;
- private BigramContentReader mBigramReader;
- private ShortcutContentReader mShortcutReader;
-
- /**
- * Raw PtNode info straight out of a trie file in version 4 dictionary.
- */
- protected static final class Ver4PtNodeInfo {
- public final int mFlags;
- public final int[] mCharacters;
- public final int mTerminalId;
- public final int mChildrenPos;
- public final int mParentPos;
- public final int mNodeSize;
- public int mStartIndexOfCharacters;
- public int mEndIndexOfCharacters; // exclusive
-
- public Ver4PtNodeInfo(final int flags, final int[] characters, final int terminalId,
- final int childrenPos, final int parentPos, final int nodeSize) {
- mFlags = flags;
- mCharacters = characters;
- mTerminalId = terminalId;
- mChildrenPos = childrenPos;
- mParentPos = parentPos;
- mNodeSize = nodeSize;
- mStartIndexOfCharacters = 0;
- mEndIndexOfCharacters = characters.length;
- }
- }
+ private DictBuffer mFrequencyBuffer;
+ private DictBuffer mTerminalAddressTableBuffer;
+ private DictBuffer mBigramBuffer;
+ private DictBuffer mShortcutBuffer;
+ private SparseTable mBigramAddressTable;
+ private SparseTable mShortcutAddressTable;
@UsedForTesting
/* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) {
mDictDirectory = dictDirectory;
- mDictBuffer = mHeaderBuffer = mFrequencyBuffer = null;
+ mDictBuffer = mFrequencyBuffer = null;
if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
@@ -102,16 +76,13 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
/* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) {
mDictDirectory = dictDirectory;
mBufferFactory = factory;
- mDictBuffer = mHeaderBuffer = mFrequencyBuffer = null;
+ mDictBuffer = mFrequencyBuffer = null;
}
- protected File getFile(final int fileType) throws UnsupportedFormatException {
+ private File getFile(final int fileType) {
if (fileType == FILETYPE_TRIE) {
return new File(mDictDirectory,
mDictDirectory.getName() + FormatSpec.TRIE_FILE_EXTENSION);
- } else if (fileType == FILETYPE_HEADER) {
- return new File(mDictDirectory,
- mDictDirectory.getName() + FormatSpec.HEADER_FILE_EXTENSION);
} else if (fileType == FILETYPE_FREQUENCY) {
return new File(mDictDirectory,
mDictDirectory.getName() + FormatSpec.FREQ_FILE_EXTENSION);
@@ -127,27 +98,20 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
mDictDirectory.getName() + FormatSpec.SHORTCUT_FILE_EXTENSION
+ FormatSpec.SHORTCUT_CONTENT_ID);
} else {
- throw new UnsupportedFormatException("Unsupported kind of file : " + fileType);
+ throw new RuntimeException("Unsupported kind of file : " + fileType);
}
}
@Override
- public void openDictBuffer() throws FileNotFoundException, IOException,
- UnsupportedFormatException {
- if (!mDictDirectory.isDirectory()) {
- throw new UnsupportedFormatException("Format 4 dictionary needs a directory");
- }
- mHeaderBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_HEADER));
+ public void openDictBuffer() throws FileNotFoundException, IOException {
mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE));
mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY));
mTerminalAddressTableBuffer = mBufferFactory.getDictionaryBuffer(
getFile(FILETYPE_TERMINAL_ADDRESS_TABLE));
- mBigramReader = new BigramContentReader(mDictDirectory.getName(),
- mDictDirectory, mBufferFactory, false);
- mBigramReader.openBuffers();
- mShortcutReader = new ShortcutContentReader(mDictDirectory.getName(), mDictDirectory,
- mBufferFactory);
- mShortcutReader.openBuffers();
+ mBigramBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_BIGRAM_FREQ));
+ loadBigramAddressSparseTable();
+ mShortcutBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_SHORTCUT));
+ loadShortcutAddressSparseTable();
}
@Override
@@ -155,134 +119,46 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
return mDictBuffer != null;
}
- @UsedForTesting
- /* package */ DictBuffer getHeaderBuffer() {
- return mHeaderBuffer;
- }
-
- @UsedForTesting
/* package */ DictBuffer getDictBuffer() {
return mDictBuffer;
}
@Override
public FileHeader readHeader() throws IOException, UnsupportedFormatException {
- if (mHeaderBuffer == null) {
+ if (mDictBuffer == null) {
openDictBuffer();
}
- mHeaderBuffer.position(0);
- final FileHeader header = super.readHeader(mHeaderBuffer);
+ final FileHeader header = super.readHeader(mDictBuffer);
final int version = header.mFormatOptions.mVersion;
- if (version != FormatSpec.VERSION4) {
+ if (version != 4) {
throw new UnsupportedFormatException("File header has a wrong version : " + version);
}
return header;
}
- /**
- * An auxiliary class for reading bigrams.
- */
- protected static class BigramContentReader extends SparseTableContentReader {
- public BigramContentReader(final String name, final File baseDir,
- final DictionaryBufferFactory factory, final boolean hasTimestamp) {
- super(name + FormatSpec.BIGRAM_FILE_EXTENSION,
- FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
- getContentFilenames(name, hasTimestamp), getContentIds(hasTimestamp), factory);
- }
-
- // TODO: Consolidate this method and BigramContentWriter.getContentFilenames.
- protected static String[] getContentFilenames(final String name,
- final boolean hasTimestamp) {
- final String[] contentFilenames;
- if (hasTimestamp) {
- contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION,
- name + FormatSpec.BIGRAM_FILE_EXTENSION };
- } else {
- contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION };
- }
- return contentFilenames;
- }
-
- // TODO: Consolidate this method and BigramContentWriter.getContentIds.
- protected static String[] getContentIds(final boolean hasTimestamp) {
- final String[] contentIds;
- if (hasTimestamp) {
- contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID,
- FormatSpec.BIGRAM_TIMESTAMP_CONTENT_ID };
- } else {
- contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID };
- }
- return contentIds;
- }
-
- public ArrayList<PendingAttribute> readTargetsAndFrequencies(final int terminalId,
- final DictBuffer terminalAddressTableBuffer) {
- final ArrayList<PendingAttribute> bigrams = CollectionUtils.newArrayList();
- read(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId,
- new SparseTableContentReaderInterface() {
- @Override
- public void read(final DictBuffer buffer) {
- while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE,
- // remaining bigram entries are ignored.
- final int bigramFlags = buffer.readUnsignedByte();
- final int targetTerminalId = buffer.readUnsignedInt24();
- terminalAddressTableBuffer.position(targetTerminalId
- * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
- final int targetAddress =
- terminalAddressTableBuffer.readUnsignedInt24();
- bigrams.add(new PendingAttribute(bigramFlags
- & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
- targetAddress));
- if (0 == (bigramFlags
- & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) {
- break;
- }
- }
- if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- throw new RuntimeException("Too many bigrams in a PtNode ("
- + bigrams.size() + " but max is "
- + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")");
- }
- }
- });
- if (bigrams.isEmpty()) return null;
- return bigrams;
- }
+ private void loadBigramAddressSparseTable() throws IOException {
+ final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
+ final File freqsFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
+ + FormatSpec.BIGRAM_FREQ_CONTENT_ID);
+ mBigramAddressTable = SparseTable.readFromFiles(lookupIndexFile, new File[] { freqsFile },
+ FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE);
}
- /**
- * An auxiliary class for reading shortcuts.
- */
- protected static class ShortcutContentReader extends SparseTableContentReader {
- public ShortcutContentReader(final String name, final File baseDir,
- final DictionaryBufferFactory factory) {
- super(name + FormatSpec.SHORTCUT_FILE_EXTENSION,
- FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
- new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION },
- new String[] { FormatSpec.SHORTCUT_CONTENT_ID }, factory);
- }
-
- public ArrayList<WeightedString> readShortcuts(final int terminalId) {
- final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList();
- read(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId,
- new SparseTableContentReaderInterface() {
- @Override
- public void read(final DictBuffer buffer) {
- while (true) {
- final int flags = buffer.readUnsignedByte();
- final String word = CharEncoding.readString(buffer);
- shortcuts.add(new WeightedString(word,
- flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
- if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) {
- break;
- }
- }
- }
- });
- if (shortcuts.isEmpty()) return null;
- return shortcuts;
- }
+ // TODO: Let's have something like SparseTableContentsReader in this class.
+ private void loadShortcutAddressSparseTable() throws IOException {
+ final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
+ final File contentFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
+ + FormatSpec.SHORTCUT_CONTENT_ID);
+ final File timestampsFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
+ + FormatSpec.SHORTCUT_CONTENT_ID);
+ mShortcutAddressTable = SparseTable.readFromFiles(lookupIndexFile,
+ new File[] { contentFile, timestampsFile },
+ FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE);
}
protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader {
@@ -296,82 +172,102 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
}
}
- private final int[] mCharacterBufferForReadingVer4PtNodeInfo
- = new int[FormatSpec.MAX_WORD_LENGTH];
+ private ArrayList<WeightedString> readShortcuts(final int terminalId) {
+ if (mShortcutAddressTable.get(0, terminalId) == SparseTable.NOT_EXIST) return null;
+
+ final ArrayList<WeightedString> ret = CollectionUtils.newArrayList();
+ final int posOfShortcuts = mShortcutAddressTable.get(FormatSpec.SHORTCUT_CONTENT_INDEX,
+ terminalId);
+ mShortcutBuffer.position(posOfShortcuts);
+ while (true) {
+ final int flags = mShortcutBuffer.readUnsignedByte();
+ final String word = CharEncoding.readString(mShortcutBuffer);
+ ret.add(new WeightedString(word,
+ flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
+ if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ return ret;
+ }
- /**
- * Reads PtNode from ptNodePos in the trie file and returns Ver4PtNodeInfo.
- *
- * @param ptNodePos the position of PtNode.
- * @param options the format options.
- * @return Ver4PtNodeInfo.
- */
// TODO: Make this buffer thread safe.
// TODO: Support words longer than FormatSpec.MAX_WORD_LENGTH.
- protected Ver4PtNodeInfo readVer4PtNodeInfo(final int ptNodePos, final FormatOptions options) {
- int readingPos = ptNodePos;
+ private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
+ @Override
+ public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) {
+ int addressPointer = ptNodePos;
final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
- readingPos += FormatSpec.PTNODE_FLAGS_SIZE;
+ addressPointer += FormatSpec.PTNODE_FLAGS_SIZE;
- final int parentPos = PtNodeReader.readParentAddress(mDictBuffer, options);
+ final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options);
if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
- readingPos += FormatSpec.PARENT_ADDRESS_SIZE;
+ addressPointer += FormatSpec.PARENT_ADDRESS_SIZE;
}
final int characters[];
if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
int index = 0;
int character = CharEncoding.readChar(mDictBuffer);
- readingPos += CharEncoding.getCharSize(character);
+ addressPointer += CharEncoding.getCharSize(character);
while (FormatSpec.INVALID_CHARACTER != character
&& index < FormatSpec.MAX_WORD_LENGTH) {
- mCharacterBufferForReadingVer4PtNodeInfo[index++] = character;
+ mCharacterBuffer[index++] = character;
character = CharEncoding.readChar(mDictBuffer);
- readingPos += CharEncoding.getCharSize(character);
+ addressPointer += CharEncoding.getCharSize(character);
}
- characters = Arrays.copyOfRange(mCharacterBufferForReadingVer4PtNodeInfo, 0, index);
+ characters = Arrays.copyOfRange(mCharacterBuffer, 0, index);
} else {
final int character = CharEncoding.readChar(mDictBuffer);
- readingPos += CharEncoding.getCharSize(character);
+ addressPointer += CharEncoding.getCharSize(character);
characters = new int[] { character };
}
final int terminalId;
if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
terminalId = PtNodeReader.readTerminalId(mDictBuffer);
- readingPos += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+ addressPointer += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
} else {
terminalId = PtNode.NOT_A_TERMINAL;
}
- int childrenPos = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options);
- if (childrenPos != FormatSpec.NO_CHILDREN_ADDRESS) {
- childrenPos += readingPos;
- }
- readingPos += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
-
- return new Ver4PtNodeInfo(flags, characters, terminalId, childrenPos, parentPos,
- readingPos - ptNodePos);
- }
-
- @Override
- public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) {
- final Ver4PtNodeInfo nodeInfo = readVer4PtNodeInfo(ptNodePos, options);
-
final int frequency;
- if (0 != (FormatSpec.FLAG_IS_TERMINAL & nodeInfo.mFlags)) {
- frequency = PtNodeReader.readFrequency(mFrequencyBuffer, nodeInfo.mTerminalId);
+ if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
+ frequency = PtNodeReader.readFrequency(mFrequencyBuffer, terminalId);
} else {
frequency = PtNode.NOT_A_TERMINAL;
}
-
- final ArrayList<WeightedString> shortcutTargets = mShortcutReader.readShortcuts(
- nodeInfo.mTerminalId);
- final ArrayList<PendingAttribute> bigrams = mBigramReader.readTargetsAndFrequencies(
- nodeInfo.mTerminalId, mTerminalAddressTableBuffer);
-
- return new PtNodeInfo(ptNodePos, ptNodePos + nodeInfo.mNodeSize, nodeInfo.mFlags,
- nodeInfo.mCharacters, frequency, nodeInfo.mParentPos, nodeInfo.mChildrenPos,
- shortcutTargets, bigrams);
+ int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options);
+ if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ childrenAddress += addressPointer;
+ }
+ addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
+ final ArrayList<WeightedString> shortcutTargets = readShortcuts(terminalId);
+
+ final ArrayList<PendingAttribute> bigrams;
+ if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
+ bigrams = new ArrayList<PendingAttribute>();
+ final int posOfBigrams = mBigramAddressTable.get(0 /* contentTableIndex */, terminalId);
+ mBigramBuffer.position(posOfBigrams);
+ while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+ // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE,
+ // remaining bigram entries are ignored.
+ final int bigramFlags = mBigramBuffer.readUnsignedByte();
+ final int targetTerminalId = mBigramBuffer.readUnsignedInt24();
+ mTerminalAddressTableBuffer.position(
+ targetTerminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
+ final int targetAddress = mTerminalAddressTableBuffer.readUnsignedInt24();
+ bigrams.add(new PendingAttribute(
+ bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
+ targetAddress));
+ if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+ throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size()
+ + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")");
+ }
+ } else {
+ bigrams = null;
+ }
+ return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency,
+ parentAddress, childrenAddress, shortcutTargets, bigrams);
}
private void deleteDictFiles() {
@@ -422,14 +318,10 @@ public class Ver4DictDecoder extends AbstractDictDecoder {
@Override
public boolean readAndFollowForwardLink() {
- final int forwardLinkPos = mDictBuffer.position();
- int nextRelativePos = BinaryDictDecoderUtils.readSInt24(mDictBuffer);
- if (nextRelativePos != FormatSpec.NO_FORWARD_LINK_ADDRESS) {
- final int nextPos = forwardLinkPos + nextRelativePos;
- if (nextPos >= 0 && nextPos < mDictBuffer.limit()) {
- mDictBuffer.position(nextPos);
- return true;
- }
+ final int nextAddress = mDictBuffer.readUnsignedInt24();
+ if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) {
+ mDictBuffer.position(nextAddress);
+ return true;
}
return false;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 8b80ebe63..8d5b48a9b 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -25,8 +25,6 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
@@ -34,8 +32,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Iterator;
/**
@@ -46,8 +42,8 @@ public class Ver4DictEncoder implements DictEncoder {
private final File mDictPlacedDir;
private byte[] mTrieBuf;
private int mTriePos;
+ private int mHeaderSize;
private OutputStream mTrieOutStream;
- private OutputStream mHeaderOutStream;
private OutputStream mFreqOutStream;
private OutputStream mUnigramTimestampOutStream;
private OutputStream mTerminalAddressTableOutStream;
@@ -61,6 +57,62 @@ public class Ver4DictEncoder implements DictEncoder {
mDictPlacedDir = dictPlacedDir;
}
+ private interface SparseTableContentWriterInterface {
+ public void write(final OutputStream outStream) throws IOException;
+ }
+
+ private static class SparseTableContentWriter {
+ private final int mContentCount;
+ private final SparseTable mSparseTable;
+ private final File mLookupTableFile;
+ protected final File mBaseDir;
+ private final File[] mAddressTableFiles;
+ private final File[] mContentFiles;
+ protected final OutputStream[] mContentOutStreams;
+
+ public SparseTableContentWriter(final String name, final int initialCapacity,
+ final int blockSize, final File baseDir, final String[] contentFilenames,
+ final String[] contentIds) {
+ if (contentFilenames.length != contentIds.length) {
+ throw new RuntimeException("The length of contentFilenames and the length of"
+ + " contentIds are different " + contentFilenames.length + ", "
+ + contentIds.length);
+ }
+ mContentCount = contentFilenames.length;
+ mSparseTable = new SparseTable(initialCapacity, blockSize, mContentCount);
+ mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
+ mAddressTableFiles = new File[mContentCount];
+ mContentFiles = new File[mContentCount];
+ mBaseDir = baseDir;
+ for (int i = 0; i < mContentCount; ++i) {
+ mAddressTableFiles[i] = new File(mBaseDir,
+ name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]);
+ mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]);
+ }
+ mContentOutStreams = new OutputStream[mContentCount];
+ }
+
+ public void openStreams() throws FileNotFoundException {
+ for (int i = 0; i < mContentCount; ++i) {
+ mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]);
+ }
+ }
+
+ protected void write(final int contentIndex, final int index,
+ final SparseTableContentWriterInterface writer) throws IOException {
+ mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length());
+ writer.write(mContentOutStreams[contentIndex]);
+ mContentOutStreams[contentIndex].flush();
+ }
+
+ public void closeStreams() throws IOException {
+ mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles);
+ for (int i = 0; i < mContentCount; ++i) {
+ mContentOutStreams[i].close();
+ }
+ }
+ }
+
private static class BigramContentWriter extends SparseTableContentWriter {
private final boolean mWriteTimestamp;
@@ -186,21 +238,16 @@ public class Ver4DictEncoder implements DictEncoder {
mBaseFilename = header.getId() + "." + header.getVersion();
mDictDir = new File(mDictPlacedDir, mBaseFilename);
final File trieFile = new File(mDictDir, mBaseFilename + FormatSpec.TRIE_FILE_EXTENSION);
- final File headerFile = new File(mDictDir,
- mBaseFilename + FormatSpec.HEADER_FILE_EXTENSION);
final File freqFile = new File(mDictDir, mBaseFilename + FormatSpec.FREQ_FILE_EXTENSION);
final File timestampFile = new File(mDictDir,
mBaseFilename + FormatSpec.UNIGRAM_TIMESTAMP_FILE_EXTENSION);
final File terminalAddressTableFile = new File(mDictDir,
mBaseFilename + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
if (!mDictDir.isDirectory()) {
- if (mDictDir.exists()) {
- FileUtils.deleteRecursively(mDictDir);
- }
+ if (mDictDir.exists()) mDictDir.delete();
mDictDir.mkdirs();
}
mTrieOutStream = new FileOutputStream(trieFile);
- mHeaderOutStream = new FileOutputStream(headerFile);
mFreqOutStream = new FileOutputStream(freqFile);
mTerminalAddressTableOutStream = new FileOutputStream(terminalAddressTableFile);
if (formatOptions.mHasTimestamp) {
@@ -213,9 +260,6 @@ public class Ver4DictEncoder implements DictEncoder {
if (mTrieOutStream != null) {
mTrieOutStream.close();
}
- if (mHeaderOutStream != null) {
- mHeaderOutStream.close();
- }
if (mFreqOutStream != null) {
mFreqOutStream.close();
}
@@ -227,7 +271,6 @@ public class Ver4DictEncoder implements DictEncoder {
}
} finally {
mTrieOutStream = null;
- mHeaderOutStream = null;
mFreqOutStream = null;
mTerminalAddressTableOutStream = null;
}
@@ -248,34 +291,16 @@ public class Ver4DictEncoder implements DictEncoder {
openStreams(formatOptions, dict.mOptions);
}
- BinaryDictEncoderUtils.writeDictionaryHeader(mHeaderOutStream, dict, formatOptions);
+ mHeaderSize = BinaryDictEncoderUtils.writeDictionaryHeader(mTrieOutStream, dict,
+ formatOptions);
MakedictLog.i("Flattening the tree...");
ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
int terminalCount = 0;
- final ArrayList<PtNode> nodes = CollectionUtils.newArrayList();
for (final PtNodeArray array : flatNodes) {
for (final PtNode node : array.mData) {
- if (node.isTerminal()) {
- nodes.add(node);
- node.mTerminalId = terminalCount++;
- }
- }
- }
- Collections.sort(nodes, new Comparator<PtNode>() {
- @Override
- public int compare(final PtNode lhs, final PtNode rhs) {
- if (lhs.mFrequency != rhs.mFrequency) {
- return lhs.mFrequency < rhs.mFrequency ? -1 : 1;
- }
- if (lhs.mTerminalId < rhs.mTerminalId) return -1;
- if (lhs.mTerminalId > rhs.mTerminalId) return 1;
- return 0;
+ if (node.isTerminal()) node.mTerminalId = terminalCount++;
}
- });
- int count = 0;
- for (final PtNode node : nodes) {
- node.mTerminalId = count++;
}
MakedictLog.i("Computing addresses...");
@@ -312,7 +337,7 @@ public class Ver4DictEncoder implements DictEncoder {
@Override
public void setPosition(int position) {
- if (mTrieBuf == null || position < 0 || position > mTrieBuf.length) return;
+ if (mTrieBuf == null || position < 0 || position >- mTrieBuf.length) return;
mTriePos = position;
}
@@ -365,7 +390,7 @@ public class Ver4DictEncoder implements DictEncoder {
private void writeChildrenPosition(PtNode ptNode, FormatOptions formatOptions) {
final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
- if (formatOptions.supportsDynamicUpdate()) {
+ if (formatOptions.mSupportsDynamicUpdate) {
mTriePos += BinaryDictEncoderUtils.writeSignedChildrenPosition(mTrieBuf,
mTriePos, childrenPos);
} else {
@@ -432,7 +457,7 @@ public class Ver4DictEncoder implements DictEncoder {
ptNode.mFrequency, FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
BinaryDictEncoderUtils.writeUIntToBuffer(terminalAddressTableBuf,
ptNode.mTerminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE,
- ptNode.mCachedAddressAfterUpdate,
+ ptNode.mCachedAddressAfterUpdate + mHeaderSize,
FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
}
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
index c46bc36bb..3d8f186ba 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
@@ -17,130 +17,29 @@
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.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;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-
-import android.util.Log;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
/**
* An implementation of DictUpdater for version 4 binary dictionary.
*/
@UsedForTesting
public class Ver4DictUpdater extends Ver4DictDecoder implements DictUpdater {
- private static final String TAG = Ver4DictUpdater.class.getSimpleName();
-
- private OutputStream mDictStream;
- private final File mFrequencyFile;
@UsedForTesting
- public Ver4DictUpdater(final File dictDirectory, final int factoryType)
- throws UnsupportedFormatException {
+ public Ver4DictUpdater(final File dictDirectory, final int factoryType) {
// DictUpdater must have an updatable DictBuffer.
super(dictDirectory, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY)
? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER);
- mFrequencyFile = getFile(FILETYPE_FREQUENCY);
- }
-
- private static class BigramContentUpdater extends SparseTableContentUpdater {
- public BigramContentUpdater(final String name, final File baseDir,
- final boolean hasTimestamp) {
- super(name + FormatSpec.BIGRAM_FILE_EXTENSION,
- FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
- BigramContentReader.getContentFilenames(name, hasTimestamp),
- BigramContentReader.getContentIds(hasTimestamp),
- new DictionaryBufferFromWritableByteBufferFactory());
- }
-
- public void insertBigramEntries(final int terminalId, final int frequency,
- final ArrayList<PendingAttribute> entries) throws IOException {
- if (terminalId < 0) {
- throw new RuntimeException("Invalid terminal id : " + terminalId);
- }
- openStreamsAndBuffers();
-
- if (entries == null || entries.isEmpty()) {
- setContentValue(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId,
- SparseTable.NOT_EXIST);
- return;
- }
- final int positionOfEntries =
- (int) mContentFiles[FormatSpec.BIGRAM_FREQ_CONTENT_INDEX].length();
- setContentValue(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, positionOfEntries);
-
- final Iterator<PendingAttribute> bigramIterator = entries.iterator();
- while (bigramIterator.hasNext()) {
- final PendingAttribute entry = bigramIterator.next();
- final int flags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(),
- 0 /* offset */, entry.mFrequency, frequency, "" /* word */);
- BinaryDictEncoderUtils.writeUIntToStream(
- mContentOutStreams[FormatSpec.BIGRAM_FREQ_CONTENT_INDEX], flags,
- FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
- BinaryDictEncoderUtils.writeUIntToStream(
- mContentOutStreams[FormatSpec.BIGRAM_FREQ_CONTENT_INDEX], entry.mAddress,
- FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE);
- }
- close();
- }
- }
-
- private static class ShortcutContentUpdater extends SparseTableContentUpdater {
- public ShortcutContentUpdater(final String name, final File baseDir) {
- super(name + FormatSpec.SHORTCUT_FILE_EXTENSION,
- FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
- new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION },
- new String[] { FormatSpec.SHORTCUT_CONTENT_ID },
- new DictionaryBufferFromWritableByteBufferFactory());
- }
-
- public void insertShortcuts(final int terminalId,
- final ArrayList<WeightedString> shortcuts) throws IOException {
- if (terminalId < 0) {
- throw new RuntimeException("Invalid terminal id : " + terminalId);
- }
- openStreamsAndBuffers();
- if (shortcuts == null || shortcuts.isEmpty()) {
- setContentValue(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId,
- SparseTable.NOT_EXIST);
- return;
- }
-
- final int positionOfShortcuts =
- (int) mContentFiles[FormatSpec.SHORTCUT_CONTENT_INDEX].length();
- setContentValue(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId, positionOfShortcuts);
-
- final Iterator<WeightedString> shortcutIterator = shortcuts.iterator();
- while (shortcutIterator.hasNext()) {
- final WeightedString target = shortcutIterator.next();
- final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
- shortcutIterator.hasNext(), target.mFrequency);
- BinaryDictEncoderUtils.writeUIntToStream(
- mContentOutStreams[FormatSpec.SHORTCUT_CONTENT_INDEX], shortcutFlags,
- FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
- CharEncoding.writeString(mContentOutStreams[FormatSpec.SHORTCUT_CONTENT_INDEX],
- target.mWord);
- }
- close();
- }
}
@Override
public void deleteWord(final String word) throws IOException, UnsupportedFormatException {
- if (mDictBuffer == null) {
- openDictBuffer();
- readHeader();
- }
+ if (mDictBuffer == null) openDictBuffer();
+ readHeader();
final int wordPos = getTerminalPosition(word);
if (wordPos != FormatSpec.NOT_VALID_WORD) {
mDictBuffer.position(wordPos);
@@ -150,623 +49,11 @@ public class Ver4DictUpdater extends Ver4DictDecoder implements DictUpdater {
}
}
- private int getNewTerminalId() {
- // The size of frequency file is FormatSpec.FREQUENCY_AND_FLAGS_SIZE * number of terminals
- // because each terminal always has a frequency.
- // So we can get a fresh terminal id by this logic.
- // CAVEAT: we are reading the file size from the disk each time: beware of race conditions,
- // even on one thread.
- return (int) (mFrequencyFile.length() / FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
- }
-
- private void updateParentPosIfNotMoved(final int nodePos, final int newParentPos,
- final FormatOptions formatOptions) {
- final int originalPos = getPosition();
- setPosition(nodePos);
- final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
- if (!BinaryDictIOUtils.isMovedPtNode(flags, formatOptions)) {
- final int parentOffset = newParentPos - nodePos;
- BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, parentOffset);
- }
- setPosition(originalPos);
- }
-
- private void updateParentPositions(final int nodeArrayPos, final int newParentPos,
- final FormatOptions formatOptions) {
- final int originalPos = mDictBuffer.position();
- mDictBuffer.position(nodeArrayPos);
- int jumpCount = 0;
- do {
- final int count = readPtNodeCount();
- for (int i = 0; i < count; ++i) {
- updateParentPosIfNotMoved(getPosition(), newParentPos, formatOptions);
- skipPtNode(formatOptions);
- }
- if (!readAndFollowForwardLink()) break;
- } while (jumpCount++ < DynamicBinaryDictIOUtils.MAX_JUMPS);
- setPosition(originalPos);
- }
-
- private void updateChildrenPos(final int nodePos, final int newChildrenPos,
- final FormatOptions options) {
- final int originalPos = getPosition();
- setPosition(nodePos);
- final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
- PtNodeReader.readParentAddress(mDictBuffer, options);
- BinaryDictIOUtils.skipString(mDictBuffer,
- (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
- if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readTerminalId(mDictBuffer);
- final int basePos = getPosition();
- BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, newChildrenPos - basePos);
- setPosition(originalPos);
- }
-
- private void updateTerminalPosition(final int terminalId, final int position) {
- if (terminalId == PtNode.NOT_A_TERMINAL
- || terminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE
- >= mTerminalAddressTableBuffer.limit()) return;
- mTerminalAddressTableBuffer.position(terminalId
- * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
- BinaryDictEncoderUtils.writeUIntToDictBuffer(mTerminalAddressTableBuffer, position,
- FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
- }
-
- private void updateForwardLink(final int nodeArrayPos, final int newForwardLink,
- final FormatOptions formatOptions) {
- final int originalPos = getPosition();
- setPosition(nodeArrayPos);
- int jumpCount = 0;
- while (jumpCount++ < DynamicBinaryDictIOUtils.MAX_JUMPS) {
- final int ptNodeCount = readPtNodeCount();
- for (int i = 0; i < ptNodeCount; ++i) {
- skipPtNode(formatOptions);
- }
- final int forwardLinkPos = getPosition();
- if (!readAndFollowForwardLink()) {
- setPosition(forwardLinkPos);
- BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, newForwardLink - forwardLinkPos);
- break;
- }
- }
- setPosition(originalPos);
- }
-
- private void markPtNodeAsMoved(final int nodePos, final int newNodePos,
- final FormatOptions options) {
- final int originalPos = getPosition();
- updateParentPosIfNotMoved(nodePos, newNodePos, options);
- setPosition(nodePos);
- final int currentFlags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
- setPosition(nodePos);
- mDictBuffer.put((byte) (FormatSpec.FLAG_IS_MOVED
- | (currentFlags & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
- final int offset = newNodePos - nodePos;
- BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, offset);
- setPosition(originalPos);
- }
-
- /**
- * Writes a PtNode to an output stream from a Ver4PtNodeInfo.
- *
- * @param nodePos the position of the head of the PtNode.
- * @param info the PtNode info to be written.
- * @return the size written, in bytes.
- */
- private int writePtNode(final int nodePos, final Ver4PtNodeInfo info) throws IOException {
- int written = 0;
-
- // Write flags.
- mDictStream.write((byte) (info.mFlags & 0xFF));
- written += FormatSpec.PTNODE_FLAGS_SIZE;
-
- // Write the parent position.
- final int parentOffset = info.mParentPos == FormatSpec.NO_PARENT_ADDRESS ?
- FormatSpec.NO_PARENT_ADDRESS : info.mParentPos - nodePos;
- BinaryDictIOUtils.writeSInt24ToStream(mDictStream, parentOffset);
- written += FormatSpec.PARENT_ADDRESS_SIZE;
-
- // Write a string.
- if (((info.mFlags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0)
- != (info.mEndIndexOfCharacters - info.mStartIndexOfCharacters > 1)) {
- throw new RuntimeException("Inconsistent flags : hasMultipleChars = "
- + ((info.mFlags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0) + ", length = "
- + (info.mEndIndexOfCharacters - info.mStartIndexOfCharacters));
- }
- written += CharEncoding.writeCodePoints(mDictStream, info.mCharacters,
- info.mStartIndexOfCharacters, info.mEndIndexOfCharacters);
-
- // Write the terminal id.
- if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
- BinaryDictEncoderUtils.writeUIntToStream(mDictStream, info.mTerminalId,
- FormatSpec.PTNODE_TERMINAL_ID_SIZE);
- written += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
- }
-
- // Write the children position.
- final int childrenOffset = info.mChildrenPos == FormatSpec.NO_CHILDREN_ADDRESS
- ? 0 : info.mChildrenPos - (nodePos + written);
- BinaryDictIOUtils.writeSInt24ToStream(mDictStream, childrenOffset);
- written += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
-
- return written;
- }
-
- /**
- * Helper method to split and move PtNode.
- *
- * @param ptNodeArrayPos the position of PtNodeArray which contains the split and moved PtNode.
- * @param splittedPtNodeToMovePos the position of the split and moved PtNode.
- * @param newParent the parent PtNode after splitting.
- * @param newChildren the children PtNodes after splitting.
- * @param newParentStartPos where to write the new parent.
- * @param formatOptions the format options.
- */
- private void writeSplittedPtNodes(final int ptNodeArrayPos, final int splittedPtNodeToMovePos,
- final Ver4PtNodeInfo newParent, final Ver4PtNodeInfo[] newChildren,
- final int newParentStartPos,
- final FormatOptions formatOptions) throws IOException {
- updateTerminalPosition(newParent.mTerminalId,
- newParentStartPos + 1 /* size of PtNodeCount */);
- int written = writePtNodeArray(newParentStartPos, new Ver4PtNodeInfo[] { newParent },
- FormatSpec.NO_FORWARD_LINK_ADDRESS);
- final int childrenStartPos = newParentStartPos + written;
- writePtNodeArray(childrenStartPos, newChildren, FormatSpec.NO_FORWARD_LINK_ADDRESS);
- int childrenNodePos = childrenStartPos + 1 /* size of PtNodeCount */;
- for (final Ver4PtNodeInfo info : newChildren) {
- updateTerminalPosition(info.mTerminalId, childrenNodePos);
- childrenNodePos += computePtNodeSize(info.mCharacters, info.mStartIndexOfCharacters,
- info.mEndIndexOfCharacters,
- (info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0);
- }
-
- // Mark as moved.
- markPtNodeAsMoved(splittedPtNodeToMovePos, newParentStartPos + 1 /* size of PtNodeCount */,
- formatOptions);
- updateForwardLink(ptNodeArrayPos, newParentStartPos, formatOptions);
- }
-
- /**
- * Writes a node array to the stream.
- *
- * @param nodeArrayPos the position of the head of the node array.
- * @param infos an array of Ver4PtNodeInfo to be written.
- * @return the written length in bytes.
- */
- private int writePtNodeArray(final int nodeArrayPos, final Ver4PtNodeInfo[] infos,
- final int forwardLink) throws IOException {
- int written = BinaryDictIOUtils.writePtNodeCount(mDictStream, infos.length);
- for (int i = 0; i < infos.length; ++i) {
- written += writePtNode(nodeArrayPos + written, infos[i]);
- }
- BinaryDictIOUtils.writeSInt24ToStream(mDictStream, forwardLink);
- written += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
- return written;
- }
-
- private int computePtNodeSize(final int[] codePoints, final int startIndex, final int endIndex,
- final boolean isTerminal) {
- return FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
- + CharEncoding.getCharArraySize(codePoints, startIndex, endIndex)
- + (endIndex - startIndex > 1 ? FormatSpec.PTNODE_TERMINATOR_SIZE : 0)
- + (isTerminal ? FormatSpec.PTNODE_TERMINAL_ID_SIZE : 0)
- + FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
- }
-
- private void writeNewSinglePtNodeWithAttributes(final int[] codePoints,
- final boolean hasShortcuts, final int terminalId, final boolean hasBigrams,
- final boolean isNotAWord, final boolean isBlackListEntry, final int parentPos,
- final FormatOptions formatOptions) throws IOException {
- final int newNodeArrayPos = mDictBuffer.limit();
- final int newNodeFlags = BinaryDictEncoderUtils.makePtNodeFlags(codePoints.length > 1,
- terminalId != PtNode.NOT_A_TERMINAL, FormatSpec.FLAG_IS_NOT_MOVED, hasShortcuts,
- hasBigrams, isNotAWord, isBlackListEntry, formatOptions);
- final Ver4PtNodeInfo info = new Ver4PtNodeInfo(newNodeFlags, codePoints, terminalId,
- FormatSpec.NO_CHILDREN_ADDRESS, parentPos, 0 /* nodeSize */);
- writePtNodeArray(newNodeArrayPos, new Ver4PtNodeInfo[] { info },
- FormatSpec.NO_FORWARD_LINK_ADDRESS);
- }
-
- private int setMultipleCharsInFlags(final int currentFlags, final boolean hasMultipleChars) {
- final int flags;
- if (hasMultipleChars) {
- flags = currentFlags | FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
- } else {
- flags = currentFlags & (~FormatSpec.FLAG_HAS_MULTIPLE_CHARS);
- }
- return flags;
- }
-
- private int setIsNotAWordInFlags(final int currentFlags, final boolean isNotAWord) {
- final int flags;
- if (isNotAWord) {
- flags = currentFlags | FormatSpec.FLAG_IS_NOT_A_WORD;
- } else {
- flags = currentFlags & (~FormatSpec.FLAG_IS_NOT_A_WORD);
- }
- return flags;
- }
-
- private int setIsBlackListEntryInFlags(final int currentFlags, final boolean isBlackListEntry) {
- final int flags;
- if (isBlackListEntry) {
- flags = currentFlags | FormatSpec.FLAG_IS_BLACKLISTED;
- } else {
- flags = currentFlags & (~FormatSpec.FLAG_IS_BLACKLISTED);
- }
- return flags;
- }
-
- /**
- * Splits a PtNode.
- *
- * abcd - ef
- *
- * -> inserting "abc"
- *
- * abc - d - ef
- *
- * @param nodeArrayToSplitPos the position of PtNodeArray which contains the PtNode to split.
- * @param nodeToSplitPos the position of the PtNode to split.
- * @param nodeToSplitInfo the information of the PtNode to split.
- * @param indexToSplit the index where to split in the code points array.
- * @param parentOfNodeToSplitPos the absolute position of a parent of the node to split.
- * @param newTerminalId the terminal id of the inserted node (corresponds to "d").
- * @param hasShortcuts whether the inserted word should have shortcuts.
- * @param hasBigrams whether the inserted word should have bigrams.
- * @param isNotAWord whether the inserted word should be not a word.
- * @param isBlackListEntry whether the inserted word should be a black list entry.
- * @param formatOptions the format options.
- */
- private void splitOnly(final int nodeArrayToSplitPos, final int nodeToSplitPos,
- final Ver4PtNodeInfo nodeToSplitInfo, final int indexToSplit,
- final int parentOfNodeToSplitPos, final int newTerminalId, final boolean hasShortcuts,
- final boolean hasBigrams, final boolean isNotAWord, final boolean isBlackListEntry,
- final FormatOptions formatOptions) throws IOException {
- final int parentNodeArrayStartPos = mDictBuffer.limit();
- final int parentNodeStartPos = parentNodeArrayStartPos + 1 /* size of PtNodeCount */;
- final int parentFlags = BinaryDictEncoderUtils.makePtNodeFlags(indexToSplit > 1,
- true /* isTerminal */, FormatSpec.FLAG_IS_NOT_MOVED, hasShortcuts, hasBigrams,
- isNotAWord, isBlackListEntry, formatOptions);
- final Ver4PtNodeInfo parentInfo = new Ver4PtNodeInfo(parentFlags,
- nodeToSplitInfo.mCharacters, newTerminalId, parentNodeStartPos
- + computePtNodeSize(nodeToSplitInfo.mCharacters, 0, indexToSplit, true)
- + FormatSpec.FORWARD_LINK_ADDRESS_SIZE,
- parentOfNodeToSplitPos, 0 /* nodeSize */);
- parentInfo.mStartIndexOfCharacters = 0;
- parentInfo.mEndIndexOfCharacters = indexToSplit;
-
- // Write the child.
- final int childrenFlags = setMultipleCharsInFlags(nodeToSplitInfo.mFlags,
- nodeToSplitInfo.mCharacters.length - indexToSplit > 1);
- final Ver4PtNodeInfo childrenInfo = new Ver4PtNodeInfo(childrenFlags,
- nodeToSplitInfo.mCharacters, nodeToSplitInfo.mTerminalId,
- nodeToSplitInfo.mChildrenPos, parentNodeStartPos, 0 /* nodeSize */);
- childrenInfo.mStartIndexOfCharacters = indexToSplit;
- childrenInfo.mEndIndexOfCharacters = nodeToSplitInfo.mCharacters.length;
- if (nodeToSplitInfo.mChildrenPos != FormatSpec.NO_CHILDREN_ADDRESS) {
- updateParentPositions(nodeToSplitInfo.mChildrenPos,
- parentInfo.mChildrenPos + 1 /* size of PtNodeCount */, formatOptions);
- }
-
- writeSplittedPtNodes(nodeArrayToSplitPos, nodeToSplitPos, parentInfo,
- new Ver4PtNodeInfo[] { childrenInfo }, parentNodeArrayStartPos, formatOptions);
- }
-
- /**
- * Split and branch a PtNode.
- *
- * ab - cd
- *
- * -> inserting "ac"
- *
- * a - b - cd
- * |
- * - c
- *
- * @param nodeArrayToSplitPos the position of PtNodeArray which contains the PtNode to split.
- * @param nodeToSplitPos the position of the PtNode to split.
- * @param nodeToSplitInfo the information of the PtNode to split.
- * @param indexToSplit the index where to split in the code points array.
- * @param parentOfNodeToSplitPos the absolute position of parent of the node to split.
- * @param newWordSuffixCodePoints the suffix of the newly inserted word (corresponds to "c").
- * @param startIndexOfNewWordSuffixCodePoints the start index in newWordSuffixCodePoints where
- * the suffix starts.
- * @param newTerminalId the terminal id of the inserted node (correspond to "c").
- * @param hasShortcuts whether the inserted word should have shortcuts.
- * @param hasBigrams whether the inserted word should have bigrams.
- * @param isNotAWord whether the inserted word should be not a word.
- * @param isBlackListEntry whether the inserted word should be a black list entry.
- * @param formatOptions the format options.
- */
- private void splitAndBranch(final int nodeArrayToSplitPos, final int nodeToSplitPos,
- final Ver4PtNodeInfo nodeToSplitInfo, final int indexToSplit,
- final int parentOfNodeToSplitPos, final int[] newWordSuffixCodePoints,
- final int startIndexOfNewWordSuffixCodePoints,
- final int newTerminalId,
- final boolean hasShortcuts, final boolean hasBigrams, final boolean isNotAWord,
- final boolean isBlackListEntry, final FormatOptions formatOptions) throws IOException {
- final int parentNodeArrayStartPos = mDictBuffer.limit();
- final int parentNodeStartPos = parentNodeArrayStartPos + 1 /* size of PtNodeCount */;
- final int parentFlags = BinaryDictEncoderUtils.makePtNodeFlags(
- indexToSplit > 1,
- false /* isTerminal */, FormatSpec.FLAG_IS_NOT_MOVED,
- false /* hasShortcut */, false /* hasBigrams */,
- false /* isNotAWord */, false /* isBlackListEntry */, formatOptions);
- final Ver4PtNodeInfo parentInfo = new Ver4PtNodeInfo(parentFlags,
- nodeToSplitInfo.mCharacters, PtNode.NOT_A_TERMINAL,
- parentNodeStartPos
- + computePtNodeSize(nodeToSplitInfo.mCharacters, 0, indexToSplit, false)
- + FormatSpec.FORWARD_LINK_ADDRESS_SIZE,
- parentOfNodeToSplitPos, 0 /* nodeSize */);
- parentInfo.mStartIndexOfCharacters = 0;
- parentInfo.mEndIndexOfCharacters = indexToSplit;
-
- final int childrenNodeArrayStartPos = parentNodeStartPos
- + computePtNodeSize(nodeToSplitInfo.mCharacters, 0, indexToSplit, false)
- + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
- final int firstChildrenFlags = BinaryDictEncoderUtils.makePtNodeFlags(
- newWordSuffixCodePoints.length - startIndexOfNewWordSuffixCodePoints > 1,
- true /* isTerminal */, FormatSpec.FLAG_IS_NOT_MOVED, hasShortcuts, hasBigrams,
- isNotAWord, isBlackListEntry, formatOptions);
- final Ver4PtNodeInfo firstChildrenInfo = new Ver4PtNodeInfo(firstChildrenFlags,
- newWordSuffixCodePoints, newTerminalId,
- FormatSpec.NO_CHILDREN_ADDRESS, parentNodeStartPos,
- 0 /* nodeSize */);
- firstChildrenInfo.mStartIndexOfCharacters = startIndexOfNewWordSuffixCodePoints;
- firstChildrenInfo.mEndIndexOfCharacters = newWordSuffixCodePoints.length;
-
- final int secondChildrenStartPos = childrenNodeArrayStartPos + 1 /* size of ptNodeCount */
- + computePtNodeSize(newWordSuffixCodePoints, startIndexOfNewWordSuffixCodePoints,
- newWordSuffixCodePoints.length, true /* isTerminal */);
- final int secondChildrenFlags = setMultipleCharsInFlags(nodeToSplitInfo.mFlags,
- nodeToSplitInfo.mCharacters.length - indexToSplit > 1);
- final Ver4PtNodeInfo secondChildrenInfo = new Ver4PtNodeInfo(secondChildrenFlags,
- nodeToSplitInfo.mCharacters, nodeToSplitInfo.mTerminalId,
- nodeToSplitInfo.mChildrenPos, parentNodeStartPos, 0 /* nodeSize */);
- secondChildrenInfo.mStartIndexOfCharacters = indexToSplit;
- secondChildrenInfo.mEndIndexOfCharacters = nodeToSplitInfo.mCharacters.length;
- if (nodeToSplitInfo.mChildrenPos != FormatSpec.NO_CHILDREN_ADDRESS) {
- updateParentPositions(nodeToSplitInfo.mChildrenPos, secondChildrenStartPos,
- formatOptions);
- }
-
- writeSplittedPtNodes(nodeArrayToSplitPos, nodeToSplitPos, parentInfo,
- new Ver4PtNodeInfo[] { firstChildrenInfo, secondChildrenInfo },
- parentNodeArrayStartPos, formatOptions);
- }
-
- /**
- * Inserts a word into the trie file and returns the position of inserted terminal node.
- * If the insertion is failed, returns FormatSpec.NOT_VALID_WORD.
- */
- @UsedForTesting
- private int insertWordToTrie(final String word, final int newTerminalId,
- final boolean isNotAWord, final boolean isBlackListEntry, final boolean hasBigrams,
- final boolean hasShortcuts) throws IOException, UnsupportedFormatException {
- setPosition(0);
- final FileHeader header = readHeader();
-
- final int[] codePoints = FusionDictionary.getCodePoints(word);
- final int wordLen = codePoints.length;
-
- int wordPos = 0;
- for (int depth = 0; depth < FormatSpec.MAX_WORD_LENGTH; /* nop */) {
- final int nodeArrayPos = getPosition();
- final int ptNodeCount = readPtNodeCount();
- boolean goToChildren = false;
- int parentPos = FormatSpec.NO_PARENT_ADDRESS;
- for (int i = 0; i < ptNodeCount; ++i) {
- final int nodePos = getPosition();
- final Ver4PtNodeInfo nodeInfo = readVer4PtNodeInfo(nodePos, header.mFormatOptions);
- if (BinaryDictIOUtils.isMovedPtNode(nodeInfo.mFlags, header.mFormatOptions)) {
- continue;
- }
- if (nodeInfo.mParentPos != FormatSpec.NO_PARENT_ADDRESS) {
- parentPos = nodePos + nodeInfo.mParentPos;
- }
-
- final boolean firstCharacterMatched =
- codePoints[wordPos] == nodeInfo.mCharacters[0];
- boolean allCharactersMatched = true;
- int firstDifferentCharacterIndex = -1;
- for (int p = 0; p < nodeInfo.mCharacters.length; ++p) {
- if (wordPos + p >= codePoints.length) break;
- if (codePoints[wordPos + p] != nodeInfo.mCharacters[p]) {
- if (firstDifferentCharacterIndex == -1) {
- firstDifferentCharacterIndex = p;
- }
- allCharactersMatched = false;
- }
- }
-
- if (!firstCharacterMatched) {
- // Go to the next sibling node.
- continue;
- }
-
- if (!allCharactersMatched) {
- final int parentNodeArrayStartPos = mDictBuffer.limit();
- splitAndBranch(nodeArrayPos, nodePos, nodeInfo, firstDifferentCharacterIndex,
- parentPos, codePoints, wordPos + firstDifferentCharacterIndex,
- newTerminalId, hasShortcuts, hasBigrams, isNotAWord,
- isBlackListEntry, header.mFormatOptions);
-
- return parentNodeArrayStartPos + computePtNodeSize(codePoints, wordPos,
- wordPos + firstDifferentCharacterIndex, false)
- + FormatSpec.FORWARD_LINK_ADDRESS_SIZE + 1 /* size of PtNodeCount */;
- }
-
- if (wordLen - wordPos < nodeInfo.mCharacters.length) {
- final int parentNodeArrayStartPos = mDictBuffer.limit();
- splitOnly(nodeArrayPos, nodePos, nodeInfo, wordLen - wordPos, parentPos,
- newTerminalId, hasShortcuts, hasBigrams, isNotAWord, isBlackListEntry,
- header.mFormatOptions);
-
- // Return the position of the inserted word.
- return parentNodeArrayStartPos + 1 /* size of PtNodeCount */;
- }
-
- wordPos += nodeInfo.mCharacters.length;
- if (wordPos == wordLen) {
- // This dictionary already contains the word.
- Log.e(TAG, "Something went wrong. If the word is already contained, "
- + " there is no need to insert new PtNode.");
- return FormatSpec.NOT_VALID_WORD;
- }
- if (nodeInfo.mChildrenPos == FormatSpec.NO_CHILDREN_ADDRESS) {
- // There are no children.
- // We need to add a new node as a child of this node.
- final int newNodeArrayPos = mDictBuffer.limit();
- final int[] newNodeCodePoints = Arrays.copyOfRange(codePoints, wordPos,
- codePoints.length);
- writeNewSinglePtNodeWithAttributes(newNodeCodePoints, hasShortcuts,
- newTerminalId, hasBigrams, isNotAWord, isBlackListEntry, nodePos,
- header.mFormatOptions);
- updateChildrenPos(nodePos, newNodeArrayPos, header.mFormatOptions);
- return newNodeArrayPos + 1 /* size of PtNodeCount */;
- } else {
- // Found the matched node.
- // Go to the children of this node.
- setPosition(nodeInfo.mChildrenPos);
- goToChildren = true;
- depth++;
- break;
- }
- }
-
- if (goToChildren) continue;
- if (!readAndFollowForwardLink()) {
- // Add a new node that contains [wordPos, word.length()-1].
- // and update the forward link.
- final int newNodeArrayPos = mDictBuffer.limit();
- final int[] newCodePoints = Arrays.copyOfRange(codePoints, wordPos,
- codePoints.length);
- writeNewSinglePtNodeWithAttributes(newCodePoints, hasShortcuts, newTerminalId,
- hasBigrams, isNotAWord, isBlackListEntry, parentPos, header.mFormatOptions);
- updateForwardLink(nodeArrayPos, newNodeArrayPos, header.mFormatOptions);
- return newNodeArrayPos + 1 /* size of PtNodeCount */;
- }
- }
- return FormatSpec.NOT_VALID_WORD;
- }
-
- private void updateFrequency(final int terminalId, final int frequency) {
- mFrequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
- BinaryDictEncoderUtils.writeUIntToDictBuffer(mFrequencyBuffer, frequency,
- FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
- }
-
- private void insertFrequency(final int frequency) throws IOException {
- final OutputStream frequencyStream = new FileOutputStream(mFrequencyFile,
- true /* append */);
- BinaryDictEncoderUtils.writeUIntToStream(frequencyStream, frequency,
- FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
- frequencyStream.close();
- }
-
- private void insertTerminalPosition(final int posOfTerminal) throws IOException,
- UnsupportedFormatException {
- final OutputStream terminalPosStream = new FileOutputStream(
- getFile(FILETYPE_TERMINAL_ADDRESS_TABLE), true /* append */);
- BinaryDictEncoderUtils.writeUIntToStream(terminalPosStream, posOfTerminal,
- FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE);
- terminalPosStream.close();
- }
-
- private void insertBigrams(final int terminalId, final int frequency,
- final ArrayList<PendingAttribute> bigramAddresses)
- throws IOException, UnsupportedFormatException {
- openDictBuffer();
- final BigramContentUpdater updater = new BigramContentUpdater(mDictDirectory.getName(),
- mDictDirectory, false);
-
- // Convert addresses to terminal ids.
- final ArrayList<PendingAttribute> bigrams = CollectionUtils.newArrayList();
- mDictBuffer.position(0);
- final FileHeader header = readHeader();
- for (PendingAttribute attr : bigramAddresses) {
- mDictBuffer.position(attr.mAddress);
- final Ver4PtNodeInfo info = readVer4PtNodeInfo(attr.mAddress, header.mFormatOptions);
- if (info.mTerminalId == PtNode.NOT_A_TERMINAL) {
- throw new RuntimeException("We can't have a bigram target that's not a terminal.");
- }
- bigrams.add(new PendingAttribute(frequency, info.mTerminalId));
- }
- updater.insertBigramEntries(terminalId, frequency, bigrams);
- close();
- }
-
- private void insertShortcuts(final int terminalId, final ArrayList<WeightedString> shortcuts)
- throws IOException {
- final ShortcutContentUpdater updater = new ShortcutContentUpdater(mDictDirectory.getName(),
- mDictDirectory);
- updater.insertShortcuts(terminalId, shortcuts);
- }
-
- private void openBuffersAndStream() throws IOException, UnsupportedFormatException {
- openDictBuffer();
- mDictStream = new FileOutputStream(getFile(FILETYPE_TRIE), true /* append */);
- }
-
- private void close() throws IOException {
- if (mDictStream != null) {
- mDictStream.close();
- mDictStream = null;
- }
- mDictBuffer = null;
- mFrequencyBuffer = null;
- mTerminalAddressTableBuffer = null;
- }
-
- private void updateAttributes(final int posOfWord, final int frequency,
- final ArrayList<WeightedString> bigramStrings,
- final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
- final boolean isBlackListEntry) throws IOException, UnsupportedFormatException {
- mDictBuffer.position(0);
- final FileHeader header = readHeader();
- mDictBuffer.position(posOfWord);
- final Ver4PtNodeInfo info = readVer4PtNodeInfo(posOfWord, header.mFormatOptions);
- final int terminalId = info.mTerminalId;
-
- // Update the flags.
- final int newFlags = setIsNotAWordInFlags(
- setIsBlackListEntryInFlags(info.mFlags, isBlackListEntry), isNotAWord);
- mDictBuffer.position(posOfWord);
- mDictBuffer.put((byte) newFlags);
-
- updateFrequency(terminalId, frequency);
- insertBigrams(terminalId, frequency,
- DynamicBinaryDictIOUtils.resolveBigramPositions(this, bigramStrings));
- insertShortcuts(terminalId, shortcuts);
- }
-
- @Override @UsedForTesting
+ @Override
public void insertWord(final String word, final int frequency,
final ArrayList<WeightedString> bigramStrings, final ArrayList<WeightedString> shortcuts,
final boolean isNotAWord, final boolean isBlackListEntry)
throws IOException, UnsupportedFormatException {
- final int newTerminalId = getNewTerminalId();
-
- openBuffersAndStream();
- final int posOfWord = getTerminalPosition(word);
- if (posOfWord != FormatSpec.NOT_VALID_WORD) {
- // The word is already contained in the dictionary.
- updateAttributes(posOfWord, frequency, bigramStrings, shortcuts, isNotAWord,
- isBlackListEntry);
- close();
- return;
- }
-
- // Insert new PtNode into trie.
- final int posOfTerminal = insertWordToTrie(word, newTerminalId, isNotAWord,
- isBlackListEntry, bigramStrings != null && !bigramStrings.isEmpty(),
- shortcuts != null && !shortcuts.isEmpty());
- insertFrequency(frequency);
- insertTerminalPosition(posOfTerminal);
- close();
-
- insertBigrams(newTerminalId, frequency,
- DynamicBinaryDictIOUtils.resolveBigramPositions(this, bigramStrings));
- insertShortcuts(newTerminalId, shortcuts);
+ // TODO: Implement this method.
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 9b573b4b8..1de15a333 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -17,16 +17,18 @@
package com.android.inputmethod.latin.personalization;
import android.content.Context;
+import android.content.SharedPreferences;
import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+import com.android.inputmethod.latin.LatinImeLogger;
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.settings.Settings;
+import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
@@ -34,9 +36,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Locale;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
/**
* This class is a base class of a dictionary that supports decaying for the personalized language
@@ -45,7 +45,8 @@ 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;
+ private static final boolean DBG_STRESS_TEST = false;
+ private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
/** Any pair being typed or picked */
public static final int FREQUENCY_FOR_TYPED = 2;
@@ -53,56 +54,52 @@ 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 = 4;
-
/** Locale for which this user history dictionary is storing words */
- private final Locale mLocale;
+ private final String mLocale;
- private final String mDictName;
+ private final String mFileName;
- /* package */ DecayingExpandableBinaryDictionaryBase(final Context context,
- final Locale locale, final String dictionaryType, final String dictName) {
- super(context, dictName, locale, dictionaryType, true);
- mLocale = locale;
- mDictName = dictName;
- if (mLocale != null && mLocale.toString().length() > 1) {
- reloadDictionaryIfRequired();
- }
- }
+ private final SharedPreferences mPrefs;
+
+ private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
+ CollectionUtils.newArrayList();
+
+ // Should always be false except when we use this class for test
+ @UsedForTesting boolean mIsTest = false;
- // Creates an instance that uses a given dictionary file for testing.
- @UsedForTesting
/* package */ DecayingExpandableBinaryDictionaryBase(final Context context,
- final Locale locale, final String dictionaryType, final String dictName,
- final File dictFile) {
- super(context, dictName, locale, dictionaryType, true, dictFile);
+ final String locale, final SharedPreferences sp, final String dictionaryType,
+ final String fileName) {
+ super(context, fileName, dictionaryType, true);
mLocale = locale;
- mDictName = dictName;
- if (mLocale != null && mLocale.toString().length() > 1) {
+ mFileName = fileName;
+ mPrefs = sp;
+ if (mLocale != null && mLocale.length() > 1) {
+ asyncLoadDictionaryToMemory();
reloadDictionaryIfRequired();
}
}
@Override
public void close() {
- if (DBG_DUMP_ON_CLOSE) {
- dumpAllWordsForDebug();
+ if (!ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ closeBinaryDictionary();
}
// Flush pending writes.
- asyncFlushBinaryDictionary();
+ // TODO: Remove after this class become to use a dynamic binary dictionary.
+ asyncFlashAllBinaryDictionary();
+ Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale);
}
@Override
protected Map<String, String> getHeaderAttributeMap() {
HashMap<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString());
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
- String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFileName);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale);
return attributeMap;
}
@@ -116,25 +113,6 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
return false;
}
- @Override
- protected boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) {
- // This class is using format 4 because it's used by all version 4 dictionaries.
- // TODO: remove this when all dynamically generated dicts use version 4.
- return formatVersion == REQUIRED_BINARY_DICTIONARY_VERSION;
- }
-
- public void addMultipleDictionaryEntriesToDictionary(
- final ArrayList<LanguageModelParam> languageModelParams,
- final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
- if (languageModelParams == null || languageModelParams.isEmpty()) {
- if (callback != null) {
- callback.onFinished();
- }
- return;
- }
- addMultipleDictionaryEntriesDynamically(languageModelParams, callback);
- }
-
/**
* Pair will be added to the decaying dictionary.
*
@@ -143,63 +121,70 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
* context, as in beginning of a sentence for example.
* The second word may not be null (a NullPointerException would be thrown).
*/
- public void addToDictionary(final String word0, final String word1, final boolean isValid,
- final int timestamp) {
+ public void addToDictionary(final String word0, final String word1, final boolean isValid) {
if (word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
(word0 != null && word0.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
return;
}
- final int frequency = isValid ?
- FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS;
- addWordDynamically(word1, frequency, null /* shortcutTarget */, 0 /* shortcutFreq */,
- false /* isNotAWord */, false /* isBlacklisted */, timestamp);
+ final int frequency = ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
+ (isValid ? FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS) :
+ FREQUENCY_FOR_TYPED;
+ addWordDynamically(word1, null /* shortcutTarget */, frequency, 0 /* shortcutFreq */,
+ false /* isNotAWord */);
// Do not insert a word as a bigram of itself
if (word1.equals(word0)) {
return;
}
if (null != word0) {
- addBigramDynamically(word0, word1, frequency, timestamp);
+ addBigramDynamically(word0, word1, frequency, isValid);
}
}
- @Override
- protected void loadDictionaryAsync() {
- // Never loaded to memory in Java side.
+ public void cancelAddingUserHistory(final String word0, final String word1) {
+ removeBigramDynamically(word0, word1);
}
- @UsedForTesting
- public void dumpAllWordsForDebug() {
- runAfterGcForDebug(new Runnable() {
- @Override
- public void run() {
- dumpAllWordsForDebugLocked();
+ @Override
+ protected void loadDictionaryAsync() {
+ final int[] profTotalCount = { 0 };
+ final String locale = getLocale();
+ if (DBG_STRESS_TEST) {
+ try {
+ Log.w(TAG, "Start stress in loading: " + locale);
+ Thread.sleep(15000);
+ Log.w(TAG, "End stress in loading");
+ } catch (InterruptedException e) {
}
- });
- }
-
- private void dumpAllWordsForDebugLocked() {
- Log.d(TAG, "dumpAllWordsForDebug started.");
+ }
+ final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale);
+ final long now = System.currentTimeMillis();
+ final ExpandableBinaryDictionary dictionary = this;
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);
+ if (DBG_SAVE_RESTORE) {
+ Log.d(TAG, "load unigram: " + word + "," + frequency);
+ }
+ addWord(word, shortcutTarget, frequency, shortcutFreq, false /* isNotAWord */);
+ ++profTotalCount[0];
}
@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);
+ if (DBG_SAVE_RESTORE) {
+ Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency);
+ }
+ ++profTotalCount[0];
+ addBigram(word0, word1, frequency, last);
}
}
};
// Load the dictionary from binary file
- final File dictFile = new File(mContext.getFilesDir(), mDictName);
+ final File dictFile = new File(mContext.getFilesDir(), mFileName);
final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile,
DictDecoder.USE_BYTEARRAY);
if (dictDecoder == null) {
@@ -213,17 +198,35 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
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);
+ } finally {
+ if (PROFILE_SAVE_RESTORE) {
+ final long diff = System.currentTimeMillis() - now;
+ Log.d(TAG, "PROF: Load UserHistoryDictionary: "
+ + locale + ", " + diff + "ms. load " + profTotalCount[0] + "entries.");
+ }
}
}
+ protected String getLocale() {
+ return mLocale;
+ }
+
+ public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
+ session.setPredictionDictionary(this);
+ mSessions.add(session);
+ session.onDictionaryReady();
+ }
+
+ public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) {
+ mSessions.remove(session);
+ }
+
@UsedForTesting
public void clearAndFlushDictionary() {
// Clear the node structure on memory
clear();
// Then flush the cleared state of the dictionary on disk.
- asyncFlushBinaryDictionary();
+ asyncFlashAllBinaryDictionary();
}
/* package */ void decayIfNeeded() {
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
new file mode 100644
index 000000000..6f152bb91
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import android.content.Context;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.compat.ActivityManagerCompatUtils;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.AbstractDictionaryWriter;
+import com.android.inputmethod.latin.ExpandableDictionary;
+import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.ExpandableDictionary.NextWord;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+
+// Currently this class is used to implement dynamic prodiction dictionary.
+// TODO: Move to native code.
+public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWriter {
+ private static final String TAG = DynamicPersonalizationDictionaryWriter.class.getSimpleName();
+ /** Maximum number of pairs. Pruning will start when databases goes above this number. */
+ public static final int DEFAULT_MAX_HISTORY_BIGRAMS = 10000;
+ public static final int LOW_MEMORY_MAX_HISTORY_BIGRAMS = 2000;
+
+ /** Any pair being typed or picked */
+ private static final int FREQUENCY_FOR_TYPED = 2;
+
+ private static final int BINARY_DICT_VERSION = 3;
+ private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+ new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */);
+
+ private final UserHistoryDictionaryBigramList mBigramList =
+ new UserHistoryDictionaryBigramList();
+ private final ExpandableDictionary mExpandableDictionary;
+ private final int mMaxHistoryBigrams;
+
+ public DynamicPersonalizationDictionaryWriter(final Context context, final String dictType) {
+ super(context, dictType);
+ mExpandableDictionary = new ExpandableDictionary(dictType);
+ final boolean isLowRamDevice = ActivityManagerCompatUtils.isLowRamDevice(context);
+ mMaxHistoryBigrams = isLowRamDevice ?
+ LOW_MEMORY_MAX_HISTORY_BIGRAMS : DEFAULT_MAX_HISTORY_BIGRAMS;
+ }
+
+ @Override
+ public void clear() {
+ mBigramList.evictAll();
+ mExpandableDictionary.clearDictionary();
+ }
+
+ /**
+ * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes
+ * are done to update the binary dictionary.
+ * @param word The word to add.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ * @param isNotAWord true if this is not a word, i.e. shortcut only.
+ */
+ @Override
+ public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
+ final int shortcutFreq, final boolean isNotAWord) {
+ if (mBigramList.size() > mMaxHistoryBigrams * 2) {
+ // Too many entries: just stop adding new vocabulary and wait next refresh.
+ return;
+ }
+ mExpandableDictionary.addWord(word, shortcutTarget, frequency, shortcutFreq);
+ mBigramList.addBigram(null, word, (byte)frequency);
+ }
+
+ @Override
+ public void addBigramWords(final String word0, final String word1, final int frequency,
+ final boolean isValid, final long lastModifiedTime) {
+ if (mBigramList.size() > mMaxHistoryBigrams * 2) {
+ // Too many entries: just stop adding new vocabulary and wait next refresh.
+ return;
+ }
+ if (lastModifiedTime > 0) {
+ mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
+ new ForgettingCurveParams(frequency, System.currentTimeMillis(),
+ lastModifiedTime));
+ mBigramList.addBigram(word0, word1, (byte)frequency);
+ } else {
+ mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
+ new ForgettingCurveParams(isValid));
+ mBigramList.addBigram(word0, word1, (byte)frequency);
+ }
+ }
+
+ @Override
+ public void removeBigramWords(final String word0, final String word1) {
+ if (mBigramList.removeBigram(word0, word1)) {
+ mExpandableDictionary.removeBigram(word0, word1);
+ }
+ }
+
+ @Override
+ protected void writeDictionary(final DictEncoder dictEncoder,
+ final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException {
+ UserHistoryDictIOUtils.writeDictionary(dictEncoder,
+ new FrequencyProvider(mBigramList, mExpandableDictionary, mMaxHistoryBigrams),
+ mBigramList, FORMAT_OPTIONS);
+ }
+
+ private static class FrequencyProvider implements BigramDictionaryInterface {
+ private final UserHistoryDictionaryBigramList mBigramList;
+ private final ExpandableDictionary mExpandableDictionary;
+ private final int mMaxHistoryBigrams;
+
+ public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList,
+ final ExpandableDictionary expandableDictionary, final int maxHistoryBigrams) {
+ mBigramList = bigramList;
+ mExpandableDictionary = expandableDictionary;
+ mMaxHistoryBigrams = maxHistoryBigrams;
+ }
+
+ @Override
+ public int getFrequency(final String word0, final String word1) {
+ final int freq;
+ if (word0 == null) { // unigram
+ freq = FREQUENCY_FOR_TYPED;
+ } else { // bigram
+ final NextWord nw = mExpandableDictionary.getBigramWord(word0, word1);
+ if (nw != null) {
+ final ForgettingCurveParams forgettingCurveParams = nw.getFcParams();
+ final byte prevFc = mBigramList.getBigrams(word0).get(word1);
+ final byte fc = forgettingCurveParams.getFc();
+ final boolean isValid = forgettingCurveParams.isValid();
+ if (prevFc > 0 && prevFc == fc) {
+ freq = fc & 0xFF;
+ } else if (UserHistoryForgettingCurveUtils.
+ needsToSave(fc, isValid, mBigramList.size() <= mMaxHistoryBigrams)) {
+ freq = fc & 0xFF;
+ } else {
+ // Delete this entry
+ freq = -1;
+ }
+ } else {
+ // Delete this entry
+ freq = -1;
+ }
+ }
+ return freq;
+ }
+ }
+
+ @Override
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final String prevWord, final ProximityInfo proximityInfo,
+ boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+ return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo,
+ blockOffensiveWords, additionalFeaturesOptions);
+ }
+
+ @Override
+ public boolean isValidWord(final String word) {
+ return mExpandableDictionary.isValidWord(word);
+ }
+
+ @UsedForTesting
+ public boolean isInBigramListForTests(final String word) {
+ // TODO: Use native method to determine whether the word is in dictionary or not
+ return mBigramList.containsKey(word) || mBigramList.getBigrams(null).containsKey(word);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index 596562f1d..f257165cb 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -16,37 +16,53 @@
package com.android.inputmethod.latin.personalization;
-import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Locale;
-
import android.content.Context;
+import android.content.SharedPreferences;
-public class PersonalizationDictionary extends DecayingExpandableBinaryDictionaryBase {
- private static final String NAME = PersonalizationDictionary.class.getSimpleName();
+import java.util.ArrayList;
+/**
+ * This class is a dictionary for the personalized language model that uses binary dictionary.
+ */
+public class PersonalizationDictionary extends ExpandableBinaryDictionary {
+ private static final String NAME = "personalization";
private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
CollectionUtils.newArrayList();
- /* package */ PersonalizationDictionary(final Context context, final Locale locale) {
- super(context, locale, Dictionary.TYPE_PERSONALIZATION,
- getDictNameWithLocale(NAME, locale));
+ /** Locale for which this user history dictionary is storing words */
+ private final String mLocale;
+
+ public PersonalizationDictionary(final Context context, final String locale,
+ final SharedPreferences prefs) {
+ // TODO: Make isUpdatable true.
+ super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION,
+ false /* isUpdatable */);
+ mLocale = locale;
+ // TODO: Restore last updated time
+ loadDictionary();
+ }
+
+ @Override
+ protected void loadDictionaryAsync() {
+ // TODO: Implement
+ }
+
+ @Override
+ protected boolean hasContentChanged() {
+ return false;
}
- // Creates an instance that uses a given dictionary file for testing.
- @UsedForTesting
- public PersonalizationDictionary(final Context context, final Locale locale,
- final File dictFile) {
- super(context, locale, Dictionary.TYPE_PERSONALIZATION, getDictNameWithLocale(NAME, locale),
- dictFile);
+ @Override
+ protected boolean needsToReloadBeforeWriting() {
+ return false;
}
public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
- session.setPredictionDictionary(this);
+ session.setDictionary(this);
mSessions.add(session);
session.onDictionaryReady();
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java
index 542bda621..c1833ff14 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java
@@ -20,18 +20,18 @@ import android.content.Context;
import android.content.res.Configuration;
public class PersonalizationDictionarySessionRegister {
- public static void init(final Context context) {
+ public static void init(Context context) {
}
public static void onConfigurationChanged(final Context context, final Configuration conf) {
}
- public static void onUpdateData(final Context context, final String type) {
+ public static void onUpdateData(Context context, String type) {
}
- public static void onRemoveData(final Context context, final String type) {
+ public static void onRemoveData(Context context, String type) {
}
- public static void onDestroy(final Context context) {
+ public static void onDestroy(Context context) {
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
index 61354762b..a86f6e584 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
@@ -18,37 +18,61 @@ package com.android.inputmethod.latin.personalization;
import android.content.Context;
-import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam;
-import com.android.inputmethod.latin.ExpandableBinaryDictionary;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Locale;
/**
* This class is a session where a data provider can communicate with a personalization
* dictionary.
*/
public abstract class PersonalizationDictionaryUpdateSession {
- public WeakReference<PersonalizationDictionary> mDictionary;
- public final Locale mSystemLocale;
+ /**
+ * This class is a parameter for a new unigram or bigram word which will be added
+ * to the personalization dictionary.
+ */
+ public static class PersonalizationLanguageModelParam {
+ public final String mWord0;
+ public final String mWord1;
+ public final boolean mIsValid;
+ public final int mFrequency;
+ public PersonalizationLanguageModelParam(String word0, String word1, boolean isValid,
+ int frequency) {
+ mWord0 = word0;
+ mWord1 = word1;
+ mIsValid = isValid;
+ mFrequency = frequency;
+ }
+ }
- public PersonalizationDictionaryUpdateSession(final Locale locale) {
+ // TODO: Use a dynamic binary dictionary instead
+ public WeakReference<PersonalizationDictionary> mDictionary;
+ public WeakReference<DecayingExpandableBinaryDictionaryBase> mPredictionDictionary;
+ public final String mSystemLocale;
+ public PersonalizationDictionaryUpdateSession(String locale) {
mSystemLocale = locale;
}
public abstract void onDictionaryReady();
- public abstract void onDictionaryClosed(final Context context);
+ public abstract void onDictionaryClosed(Context context);
- public void setPredictionDictionary(final PersonalizationDictionary dictionary) {
+ public void setDictionary(PersonalizationDictionary dictionary) {
mDictionary = new WeakReference<PersonalizationDictionary>(dictionary);
}
+ public void setPredictionDictionary(DecayingExpandableBinaryDictionaryBase dictionary) {
+ mPredictionDictionary =
+ new WeakReference<DecayingExpandableBinaryDictionaryBase>(dictionary);
+ }
+
protected PersonalizationDictionary getDictionary() {
return mDictionary == null ? null : mDictionary.get();
}
+ protected DecayingExpandableBinaryDictionaryBase getPredictionDictionary() {
+ return mPredictionDictionary == null ? null : mPredictionDictionary.get();
+ }
+
private void unsetDictionary() {
final PersonalizationDictionary dictionary = getDictionary();
if (dictionary == null) {
@@ -57,30 +81,48 @@ public abstract class PersonalizationDictionaryUpdateSession {
dictionary.unRegisterUpdateSession(this);
}
- public void clearAndFlushDictionary(final Context context) {
- final PersonalizationDictionary dictionary = getDictionary();
+ private void unsetPredictionDictionary() {
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
+ if (dictionary == null) {
+ return;
+ }
+ dictionary.unRegisterUpdateSession(this);
+ }
+
+ public void clearAndFlushPredictionDictionary(Context context) {
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
if (dictionary == null) {
return;
}
dictionary.clearAndFlushDictionary();
}
- public void closeSession(final Context context) {
+ public void closeSession(Context context) {
unsetDictionary();
+ unsetPredictionDictionary();
onDictionaryClosed(context);
}
- // TODO: Support multi locale.
- public void addMultipleDictionaryEntriesToDictionary(
- final ArrayList<LanguageModelParam> languageModelParams,
- final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
- final PersonalizationDictionary dictionary = getDictionary();
+ // TODO: Support multi locale to add bigram
+ public void addBigramToPersonalizationDictionary(String word0, String word1, boolean isValid,
+ int frequency) {
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
if (dictionary == null) {
- if (callback != null) {
- callback.onFinished();
- }
return;
}
- dictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams, callback);
+ dictionary.addToDictionary(word0, word1, isValid);
+ }
+
+ // Bulk import
+ // TODO: Support multi locale to add bigram
+ public void addBigramsToPersonalizationDictionary(
+ final ArrayList<PersonalizationLanguageModelParam> lmParams) {
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
+ if (dictionary == null) {
+ return;
+ }
+ for (final PersonalizationLanguageModelParam lmParam : lmParams) {
+ dictionary.addToDictionary(lmParam.mWord0, lmParam.mWord1, lmParam.mIsValid);
+ }
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index d55cae132..221ddeeba 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -19,10 +19,11 @@ package com.android.inputmethod.latin.personalization;
import com.android.inputmethod.latin.utils.CollectionUtils;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
import android.util.Log;
import java.lang.ref.SoftReference;
-import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
public class PersonalizationHelper {
@@ -30,16 +31,21 @@ public class PersonalizationHelper {
private static final boolean DEBUG = false;
private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap();
+
private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>>
sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap();
+ private static final ConcurrentHashMap<String,
+ SoftReference<PersonalizationPredictionDictionary>>
+ sLangPersonalizationPredictionDictCache =
+ CollectionUtils.newConcurrentHashMap();
+
public static UserHistoryDictionary getUserHistoryDictionary(
- final Context context, final Locale locale) {
- final String localeStr = locale.toString();
+ final Context context, final String locale, final SharedPreferences sp) {
synchronized (sLangUserHistoryDictCache) {
- if (sLangUserHistoryDictCache.containsKey(localeStr)) {
+ if (sLangUserHistoryDictCache.containsKey(locale)) {
final SoftReference<UserHistoryDictionary> ref =
- sLangUserHistoryDictCache.get(localeStr);
+ sLangUserHistoryDictCache.get(locale);
final UserHistoryDictionary dict = ref == null ? null : ref.get();
if (dict != null) {
if (DEBUG) {
@@ -49,9 +55,8 @@ public class PersonalizationHelper {
return dict;
}
}
- final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale);
- sLangUserHistoryDictCache.put(localeStr,
- new SoftReference<UserHistoryDictionary>(dict));
+ final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale, sp);
+ sLangUserHistoryDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict));
return dict;
}
}
@@ -69,30 +74,57 @@ public class PersonalizationHelper {
}
public static void registerPersonalizationDictionaryUpdateSession(final Context context,
- final PersonalizationDictionaryUpdateSession session, final Locale locale) {
- final PersonalizationDictionary personalizationDictionary =
- getPersonalizationDictionary(context, locale);
- personalizationDictionary.registerUpdateSession(session);
+ final PersonalizationDictionaryUpdateSession session, String locale) {
+ final PersonalizationPredictionDictionary predictionDictionary =
+ getPersonalizationPredictionDictionary(context, locale,
+ PreferenceManager.getDefaultSharedPreferences(context));
+ predictionDictionary.registerUpdateSession(session);
+ final PersonalizationDictionary dictionary =
+ getPersonalizationDictionary(context, locale,
+ PreferenceManager.getDefaultSharedPreferences(context));
+ dictionary.registerUpdateSession(session);
}
public static PersonalizationDictionary getPersonalizationDictionary(
- final Context context, final Locale locale) {
- final String localeStr = locale.toString();
+ final Context context, final String locale, final SharedPreferences sp) {
synchronized (sLangPersonalizationDictCache) {
- if (sLangPersonalizationDictCache.containsKey(localeStr)) {
+ if (sLangPersonalizationDictCache.containsKey(locale)) {
final SoftReference<PersonalizationDictionary> ref =
- sLangPersonalizationDictCache.get(localeStr);
+ sLangPersonalizationDictCache.get(locale);
final PersonalizationDictionary dict = ref == null ? null : ref.get();
if (dict != null) {
if (DEBUG) {
- Log.w(TAG, "Use cached PersonalizationDictionary for " + locale);
+ Log.w(TAG, "Use cached PersonalizationDictCache for " + locale);
}
return dict;
}
}
- final PersonalizationDictionary dict = new PersonalizationDictionary(context, locale);
+ final PersonalizationDictionary dict =
+ new PersonalizationDictionary(context, locale, sp);
sLangPersonalizationDictCache.put(
- localeStr, new SoftReference<PersonalizationDictionary>(dict));
+ locale, new SoftReference<PersonalizationDictionary>(dict));
+ return dict;
+ }
+ }
+
+ public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
+ final Context context, final String locale, final SharedPreferences sp) {
+ synchronized (sLangPersonalizationPredictionDictCache) {
+ if (sLangPersonalizationPredictionDictCache.containsKey(locale)) {
+ final SoftReference<PersonalizationPredictionDictionary> ref =
+ sLangPersonalizationPredictionDictCache.get(locale);
+ final PersonalizationPredictionDictionary dict = ref == null ? null : ref.get();
+ if (dict != null) {
+ if (DEBUG) {
+ Log.w(TAG, "Use cached PersonalizationPredictionDictionary for " + locale);
+ }
+ return dict;
+ }
+ }
+ final PersonalizationPredictionDictionary dict =
+ new PersonalizationPredictionDictionary(context, locale, sp);
+ sLangPersonalizationPredictionDictCache.put(
+ locale, new SoftReference<PersonalizationPredictionDictionary>(dict));
return dict;
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
new file mode 100644
index 000000000..432954453
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class PersonalizationPredictionDictionary extends DecayingExpandableBinaryDictionaryBase {
+ private static final String NAME = PersonalizationPredictionDictionary.class.getSimpleName();
+
+ /* package */ PersonalizationPredictionDictionary(final Context context, final String locale,
+ final SharedPreferences sp) {
+ super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA,
+ getDictionaryFileName(locale));
+ }
+
+ private static String getDictionaryFileName(final String locale) {
+ return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 868f21cbc..a60226d7e 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -16,13 +16,11 @@
package com.android.inputmethod.latin.personalization;
-import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Dictionary;
-
-import java.io.File;
-import java.util.Locale;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import android.content.Context;
+import android.content.SharedPreferences;
/**
* Locally gathers stats about the words user types and various other signals like auto-correction
@@ -31,19 +29,12 @@ import android.content.Context;
public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase {
/* package for tests */ static final String NAME =
UserHistoryDictionary.class.getSimpleName();
- /* package */ UserHistoryDictionary(final Context context, final Locale locale) {
- super(context, locale, Dictionary.TYPE_USER_HISTORY, getDictNameWithLocale(NAME, locale));
- }
-
- // Creates an instance that uses a given dictionary file for testing.
- @UsedForTesting
- public UserHistoryDictionary(final Context context, final Locale locale,
- final File dictFile) {
- super(context, locale, Dictionary.TYPE_USER_HISTORY, getDictNameWithLocale(NAME, locale),
- dictFile);
+ /* package */ UserHistoryDictionary(final Context context, final String locale,
+ final SharedPreferences sp) {
+ super(context, locale, sp, Dictionary.TYPE_USER_HISTORY, getDictionaryFileName(locale));
}
- public void cancelAddingUserHistory(final String word0, final String word1) {
- removeBigramDynamically(word0, word1);
+ private static String getDictionaryFileName(final String locale) {
+ return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
}
}
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index d060485bd..da1fb73fe 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -112,7 +112,8 @@ public final class DebugSettings extends PreferenceFragment
updateDebugMode();
mServiceNeedsRestart = true;
}
- } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) {
+ } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)
+ || key.equals(PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG)) {
mServiceNeedsRestart = true;
}
}
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 714c3a97a..df2c6907f 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -27,10 +27,12 @@ import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.LocaleUtils;
import com.android.inputmethod.latin.utils.ResourceUtils;
import com.android.inputmethod.latin.utils.RunInLocale;
import com.android.inputmethod.latin.utils.StringUtils;
+import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.locks.ReentrantLock;
@@ -51,6 +53,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
public static final String PREF_MISC_SETTINGS = "misc_settings";
+ public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME =
+ "last_user_dictionary_write_time";
public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
@@ -225,15 +229,16 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
res.getBoolean(R.bool.config_default_phrase_gesture_enabled));
}
- public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) {
- return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option);
+ public static boolean readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(
+ final Resources res) {
+ return res.getBoolean(R.bool.config_enable_show_option_of_key_preview_popup);
}
public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs,
final Resources res) {
final boolean defaultKeyPreviewPopup = res.getBoolean(
R.bool.config_default_key_preview_popup);
- if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
+ if (!readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(res)) {
return defaultKeyPreviewPopup;
}
return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup);
@@ -328,6 +333,25 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true);
}
+ public static long readLastUserHistoryWriteTime(final SharedPreferences prefs,
+ final String locale) {
+ final String str = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
+ final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(str);
+ if (map.containsKey(locale)) {
+ return map.get(locale);
+ }
+ return 0;
+ }
+
+ public static void writeLastUserHistoryWriteTime(final SharedPreferences prefs,
+ final String locale) {
+ final String oldStr = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
+ final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(oldStr);
+ map.put(locale, System.currentTimeMillis());
+ final String newStr = LocaleUtils.localeAndTimeHashMapToStr(map);
+ prefs.edit().putString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
+ }
+
public static boolean readUseFullscreenMode(final Resources res) {
return res.getBoolean(R.bool.config_use_fullscreen_mode);
}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index d7a3e95b3..5c60a7350 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -48,6 +48,7 @@ import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.FeedbackUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+import com.android.inputmethod.research.ResearchLogger;
import com.android.inputmethodcommon.InputMethodSettingsFragment;
import java.util.TreeSet;
@@ -60,6 +61,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment
DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS
|| Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */;
+ private CheckBoxPreference mVoiceInputKeyPreference;
+ private ListPreference mShowCorrectionSuggestionsPreference;
+ private ListPreference mAutoCorrectionThresholdPreference;
+ private ListPreference mKeyPreviewPopupDismissDelay;
+ // Use bigrams to predict the next word when there is no input for it yet
+ private CheckBoxPreference mBigramPrediction;
+
private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) {
final Preference preference = findPreference(preferenceKey);
if (preference != null) {
@@ -67,18 +75,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment
}
}
- private void updateListPreferenceSummaryToCurrentValue(final String prefKey) {
- // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before
- // KitKat, we need to update the summary programmatically.
- final ListPreference listPreference = (ListPreference)findPreference(prefKey);
- if (listPreference == null) {
- return;
- }
- final CharSequence entries[] = listPreference.getEntries();
- final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue());
- listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]);
- }
-
private static void removePreference(final String preferenceKey, final PreferenceGroup parent) {
if (parent == null) {
return;
@@ -111,9 +107,16 @@ public final class SettingsFragment extends InputMethodSettingsFragment
SubtypeLocaleUtils.init(context);
AudioAndHapticFeedbackManager.init(context);
+ mVoiceInputKeyPreference =
+ (CheckBoxPreference) findPreference(Settings.PREF_VOICE_INPUT_KEY);
+ mShowCorrectionSuggestionsPreference =
+ (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
+ mAutoCorrectionThresholdPreference =
+ (ListPreference) findPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD);
+ mBigramPrediction = (CheckBoxPreference) findPreference(Settings.PREF_BIGRAM_PREDICTIONS);
ensureConsistencyOfAutoCorrectionSettings();
final PreferenceGroup generalSettings =
@@ -140,7 +143,12 @@ public final class SettingsFragment extends InputMethodSettingsFragment
feedbackSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference pref) {
- FeedbackUtils.showFeedbackForm(getActivity());
+ if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ // Use development-only feedback mechanism
+ ResearchLogger.getInstance().presentFeedbackDialogFromSettings();
+ } else {
+ FeedbackUtils.showFeedbackForm(getActivity());
+ }
return true;
}
});
@@ -159,7 +167,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment
final boolean showVoiceKeyOption = res.getBoolean(
R.bool.config_enable_show_voice_key_option);
if (!showVoiceKeyOption) {
- removePreference(Settings.PREF_VOICE_INPUT_KEY, generalSettings);
+ generalSettings.removePreference(mVoiceInputKeyPreference);
}
final PreferenceGroup advancedSettings =
@@ -169,27 +177,26 @@ public final class SettingsFragment extends InputMethodSettingsFragment
removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings);
}
- if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
+ mKeyPreviewPopupDismissDelay =
+ (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+ if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(res)) {
removePreference(Settings.PREF_POPUP_ON, generalSettings);
removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedSettings);
} else {
- // TODO: Cleanup this setup.
- final ListPreference keyPreviewPopupDismissDelay =
- (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
R.integer.config_key_preview_linger_timeout));
- keyPreviewPopupDismissDelay.setEntries(new String[] {
+ mKeyPreviewPopupDismissDelay.setEntries(new String[] {
res.getString(R.string.key_preview_popup_dismiss_no_delay),
res.getString(R.string.key_preview_popup_dismiss_default_delay),
});
- keyPreviewPopupDismissDelay.setEntryValues(new String[] {
+ mKeyPreviewPopupDismissDelay.setEntryValues(new String[] {
"0",
popupDismissDelayDefaultValue
});
- if (null == keyPreviewPopupDismissDelay.getValue()) {
- keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
+ if (null == mKeyPreviewPopupDismissDelay.getValue()) {
+ mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
}
- keyPreviewPopupDismissDelay.setEnabled(
+ mKeyPreviewPopupDismissDelay.setEnabled(
Settings.readKeyPreviewPopupEnabled(prefs, res));
}
@@ -236,19 +243,20 @@ public final class SettingsFragment extends InputMethodSettingsFragment
@Override
public void onResume() {
super.onResume();
- final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
- final Resources res = getResources();
final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
- setPreferenceEnabled(Settings.PREF_VOICE_INPUT_KEY, isShortcutImeEnabled);
+ if (!isShortcutImeEnabled) {
+ getPreferenceScreen().removePreference(mVoiceInputKeyPreference);
+ }
+ final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
final CheckBoxPreference showSetupWizardIcon =
(CheckBoxPreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON);
if (showSetupWizardIcon != null) {
showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity()));
}
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT);
- updateCustomInputStylesSummary(prefs, res);
+ updateShowCorrectionSuggestionsSummary();
+ updateKeyPreviewPopupDelaySummary();
+ updateColorSchemeSummary(prefs, getResources());
+ updateCustomInputStylesSummary();
}
@Override
@@ -279,26 +287,50 @@ public final class SettingsFragment extends InputMethodSettingsFragment
LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity());
}
ensureConsistencyOfAutoCorrectionSettings();
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT);
+ updateShowCorrectionSuggestionsSummary();
+ updateKeyPreviewPopupDelaySummary();
+ updateColorSchemeSummary(prefs, res);
refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
}
private void ensureConsistencyOfAutoCorrectionSettings() {
final String autoCorrectionOff = getResources().getString(
R.string.auto_correction_threshold_mode_index_off);
- final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference(
- Settings.PREF_AUTO_CORRECTION_THRESHOLD);
- final String currentSetting = autoCorrectionThresholdPref.getValue();
- setPreferenceEnabled(
- Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff));
+ final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
+ mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
}
- private void updateCustomInputStylesSummary(final SharedPreferences prefs,
- final Resources res) {
+ private void updateShowCorrectionSuggestionsSummary() {
+ mShowCorrectionSuggestionsPreference.setSummary(
+ getResources().getStringArray(R.array.prefs_suggestion_visibilities)
+ [mShowCorrectionSuggestionsPreference.findIndexOfValue(
+ mShowCorrectionSuggestionsPreference.getValue())]);
+ }
+
+ private void updateColorSchemeSummary(final SharedPreferences prefs, final Resources res) {
+ // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before
+ // KitKat, we need to update the summary by code.
+ final Preference preference = findPreference(Settings.PREF_KEYBOARD_LAYOUT);
+ if (!(preference instanceof ListPreference)) {
+ Log.w(TAG, "Can't find Keyboard Color Scheme preference");
+ return;
+ }
+ final ListPreference colorSchemePreference = (ListPreference)preference;
+ final int themeIndex = Settings.readKeyboardThemeIndex(prefs, res);
+ int entryIndex = colorSchemePreference.findIndexOfValue(Integer.toString(themeIndex));
+ if (entryIndex < 0) {
+ final int defaultThemeIndex = Settings.resetAndGetDefaultKeyboardThemeIndex(prefs, res);
+ entryIndex = colorSchemePreference.findIndexOfValue(
+ Integer.toString(defaultThemeIndex));
+ }
+ colorSchemePreference.setSummary(colorSchemePreference.getEntries()[entryIndex]);
+ }
+
+ private void updateCustomInputStylesSummary() {
final PreferenceScreen customInputStyles =
(PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES);
+ final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ final Resources res = getResources();
final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res);
final InputMethodSubtype[] subtypes =
AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype);
@@ -310,6 +342,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment
customInputStyles.setSummary(styles);
}
+ private void updateKeyPreviewPopupDelaySummary() {
+ final ListPreference lp = mKeyPreviewPopupDismissDelay;
+ final CharSequence[] entries = lp.getEntries();
+ if (entries == null || entries.length <= 0) return;
+ lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
+ }
+
private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
final SharedPreferences sp, final Resources res) {
setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS,
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 06406c19b..f331c78e5 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -67,7 +67,7 @@ public final class SettingsValues {
public final boolean mVibrateOn;
public final boolean mSoundOn;
public final boolean mKeyPreviewPopupOn;
- public final boolean mShowsVoiceInputKey;
+ private final boolean mShowsVoiceInputKey;
public final boolean mIncludesOtherImesInLanguageSwitchList;
public final boolean mShowsLanguageSwitchKey;
public final boolean mUseContactsDict;
@@ -271,6 +271,13 @@ public final class SettingsValues {
return mInputAttributes.mShouldInsertSpacesAutomatically;
}
+ public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) {
+ final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+ final int inputType = (editorInfo != null) ? editorInfo.inputType : 0;
+ return shortcutImeEnabled && mShowsVoiceInputKey
+ && !InputTypeUtils.isPasswordInputType(inputType);
+ }
+
public boolean isLanguageSwitchKeyEnabled() {
if (!mShowsLanguageSwitchKey) {
return false;
@@ -367,20 +374,16 @@ public final class SettingsValues {
return autoCorrectionThreshold;
}
- private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs,
- final Resources res) {
- if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) {
- // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
- // {@link Settings#PREF_VOICE_INPUT_KEY}.
- final String voiceModeMain = res.getString(R.string.voice_mode_main);
- final String voiceMode = prefs.getString(
- Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
- final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode);
- prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply();
- }
- // Remove the obsolete preference if exists.
- if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
- prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply();
+ private static boolean needsToShowVoiceInputKey(SharedPreferences prefs, Resources res) {
+ final String voiceModeMain = res.getString(R.string.voice_mode_main);
+ final String voiceMode = prefs.getString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
+ final boolean showsVoiceInputKey = voiceMode == null || voiceMode.equals(voiceModeMain);
+ if (!showsVoiceInputKey) {
+ // Migrate settings from PREF_VOICE_MODE_OBSOLETE to PREF_VOICE_INPUT_KEY
+ // Set voiceModeMain as a value of obsolete voice mode settings.
+ prefs.edit().putString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain).apply();
+ // Disable voice input key.
+ prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, false).apply();
}
return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index 5072fabd6..c4a813c24 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -38,7 +38,7 @@ import com.android.inputmethod.compat.ViewCompatUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.settings.SettingsActivity;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
+import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
import java.util.ArrayList;
@@ -74,21 +74,21 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
private SettingsPoolingHandler mHandler;
private static final class SettingsPoolingHandler
- extends LeakGuardHandlerWrapper<SetupWizardActivity> {
+ extends StaticInnerHandlerWrapper<SetupWizardActivity> {
private static final int MSG_POLLING_IME_SETTINGS = 0;
private static final long IME_SETTINGS_POLLING_INTERVAL = 200;
private final InputMethodManager mImmInHandler;
- public SettingsPoolingHandler(final SetupWizardActivity ownerInstance,
+ public SettingsPoolingHandler(final SetupWizardActivity outerInstance,
final InputMethodManager imm) {
- super(ownerInstance);
+ super(outerInstance);
mImmInHandler = imm;
}
@Override
public void handleMessage(final Message msg) {
- final SetupWizardActivity setupWizardActivity = getOwnerInstance();
+ final SetupWizardActivity setupWizardActivity = getOuterInstance();
if (setupWizardActivity == null) {
return;
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index c108b20ed..503b18b1b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -428,7 +428,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
final String localeStr = locale.toString();
UserBinaryDictionary userDictionary = mUserDictionaries.get(localeStr);
if (null == userDictionary) {
- userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, locale, true);
+ userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true);
mUserDictionaries.put(localeStr, userDictionary);
}
dictionaryCollection.addDictionary(userDictionary);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index b7a5a4026..a0aed2829 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -49,7 +49,6 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> {
final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList();
private final static DictAndKeyboard dummyDict = new DictAndKeyboard(
new Dictionary(Dictionary.TYPE_MAIN) {
- // TODO: this dummy dictionary should be a singleton in the Dictionary class.
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
final String prevWord, final ProximityInfo proximityInfo,
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 52012c846..acd47450b 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -66,8 +66,7 @@ public final class MoreSuggestions extends Keyboard {
clearKeys();
mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
mDividerWidth = mDivider.getIntrinsicWidth();
- final float padding = res.getDimension(
- R.dimen.config_more_suggestions_key_horizontal_padding);
+ final float padding = res.getDimension(R.dimen.more_suggestions_key_horizontal_padding);
int row = 0;
int index = fromIndex;
@@ -76,7 +75,7 @@ public final class MoreSuggestions extends Keyboard {
while (index < size) {
final String word = suggestedWords.getWord(index);
// TODO: Should take care of text x-scaling.
- mWidths[index] = (int)(TypefaceUtils.getStringWidth(word, paint) + padding);
+ mWidths[index] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding);
final int numColumn = index - rowStartIndex + 1;
final int columnWidth =
(maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 549ff0d9d..0ebe37782 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -54,7 +54,7 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView {
public void adjustVerticalCorrectionForModalMode() {
// Set vertical correction to zero (Reset more keys keyboard sliding allowance
- // {@link R#dimen.config_more_keys_keyboard_slide_allowance}).
+ // {@link R#dimen.more_keys_keyboard_slide_allowance}).
mKeyDetector.setKeyboard(getKeyboard(), -getPaddingLeft(), -getPaddingTop());
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 72281e62c..faa5560e4 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -119,8 +119,7 @@ final class SuggestionStripLayoutHelper {
mDividerWidth = dividerView.getMeasuredWidth();
final Resources res = wordView.getResources();
- mSuggestionsStripHeight = res.getDimensionPixelSize(
- R.dimen.config_suggestions_strip_height);
+ mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripView);
@@ -146,17 +145,15 @@ final class SuggestionStripLayoutHelper {
a.recycle();
mMoreSuggestionsHint = getMoreSuggestionsHint(res,
- res.getDimension(R.dimen.config_more_suggestions_hint_text_size),
- mColorAutoCorrect);
+ res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect);
mCenterPositionInStrip = mSuggestionsCountInStrip / 2;
// Assuming there are at least three suggestions. Also, note that the suggestions are
// laid out according to script direction, so this is left of the center for LTR scripts
// and right of the center for RTL scripts.
mTypedWordPositionWhenAutocorrect = mCenterPositionInStrip - 1;
mMoreSuggestionsBottomGap = res.getDimensionPixelOffset(
- R.dimen.config_more_suggestions_bottom_gap);
- mMoreSuggestionsRowHeight = res.getDimensionPixelSize(
- R.dimen.config_more_suggestions_row_height);
+ R.dimen.more_suggestions_bottom_gap);
+ mMoreSuggestionsRowHeight = res.getDimensionPixelSize(R.dimen.more_suggestions_row_height);
final LayoutInflater inflater = LayoutInflater.from(context);
mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index aa87affa2..75f17c559 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -112,7 +112,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
final Resources res = context.getResources();
mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
- R.dimen.config_more_suggestions_modal_tolerance);
+ R.dimen.more_suggestions_modal_tolerance);
mMoreSuggestionsSlidingDetector = new GestureDetector(
context, mMoreSuggestionsSlidingListener);
}
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index ef1d0f42c..d87f6f3c4 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -17,25 +17,21 @@
package com.android.inputmethod.latin.utils;
import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
-import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE;
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME;
import android.os.Build;
import android.text.TextUtils;
-import android.util.Log;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import java.util.ArrayList;
-import java.util.Arrays;
public final class AdditionalSubtypeUtils {
- private static final String TAG = AdditionalSubtypeUtils.class.getSimpleName();
-
private static final InputMethodSubtype[] EMPTY_SUBTYPE_ARRAY = new InputMethodSubtype[0];
private AdditionalSubtypeUtils() {
@@ -47,11 +43,6 @@ public final class AdditionalSubtypeUtils {
}
private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":";
- private static final int INDEX_OF_LOCALE = 0;
- private static final int INDEX_OF_KEYBOARD_LAYOUT = 1;
- private static final int INDEX_OF_EXTRA_VALUE = 2;
- private static final int LENGTH_WITHOUT_EXTRA_VALUE = (INDEX_OF_KEYBOARD_LAYOUT + 1);
- private static final int LENGTH_WITH_EXTRA_VALUE = (INDEX_OF_EXTRA_VALUE + 1);
private static final String PREF_SUBTYPE_SEPARATOR = ";";
public static InputMethodSubtype createAdditionalSubtype(final String localeString,
@@ -88,6 +79,17 @@ public final class AdditionalSubtypeUtils {
: basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue;
}
+ public static InputMethodSubtype createAdditionalSubtype(final String prefSubtype) {
+ final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
+ if (elems.length < 2 || elems.length > 3) {
+ throw new RuntimeException("Unknown additional subtype specified: " + prefSubtype);
+ }
+ final String localeString = elems[0];
+ final String keyboardLayoutSetName = elems[1];
+ final String extraValue = (elems.length == 3) ? elems[2] : null;
+ return createAdditionalSubtype(localeString, keyboardLayoutSetName, extraValue);
+ }
+
public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) {
if (TextUtils.isEmpty(prefSubtypes)) {
return EMPTY_SUBTYPE_ARRAY;
@@ -96,19 +98,7 @@ public final class AdditionalSubtypeUtils {
final ArrayList<InputMethodSubtype> subtypesList =
CollectionUtils.newArrayList(prefSubtypeArray.length);
for (final String prefSubtype : prefSubtypeArray) {
- final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
- if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE
- && elems.length != LENGTH_WITH_EXTRA_VALUE) {
- Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype + " in "
- + prefSubtypes);
- continue;
- }
- final String localeString = elems[INDEX_OF_LOCALE];
- final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT];
- final String extraValue = (elems.length == LENGTH_WITH_EXTRA_VALUE)
- ? elems[INDEX_OF_EXTRA_VALUE] : null;
- final InputMethodSubtype subtype = createAdditionalSubtype(
- localeString, keyboardLayoutSetName, extraValue);
+ final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype);
if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) {
// Skip unknown keyboard layout subtype. This may happen when predefined keyboard
// layout has been removed.
@@ -147,36 +137,31 @@ public final class AdditionalSubtypeUtils {
return sb.toString();
}
- private static InputMethodSubtype buildInputMethodSubtype(final int nameId,
- final String localeString, final String layoutExtraValue,
- final String additionalSubtypeExtraValue) {
- // To preserve additional subtype settings and user's selection across OS updates, subtype
- // id shouldn't be changed. New attributes, such as emojiCapable, are carefully excluded
- // from the calculation of subtype id.
- final String compatibleExtraValue = StringUtils.joinCommaSplittableText(
- layoutExtraValue, additionalSubtypeExtraValue);
- final int compatibleSubtypeId = getInputMethodSubtypeId(localeString, compatibleExtraValue);
+ private static InputMethodSubtype buildInputMethodSubtype(int nameId, String localeString,
+ String layoutExtraValue, String additionalSubtypeExtraValue) {
+ // CAVEAT! If you want to change subtypeId after changing the extra values,
+ // you must change "getInputMethodSubtypeId". But it will remove the additional keyboard
+ // from the current users. So, you should be really careful to change it.
+ final int subtypeId = getInputMethodSubtypeId(nameId, localeString, layoutExtraValue,
+ additionalSubtypeExtraValue);
final String extraValue;
- // Color Emoji is supported from KitKat.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- extraValue = StringUtils.appendToCommaSplittableTextIfNotExists(
- EMOJI_CAPABLE, compatibleExtraValue);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ extraValue = layoutExtraValue + "," + additionalSubtypeExtraValue
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
} else {
- extraValue = compatibleExtraValue;
+ extraValue = layoutExtraValue + "," + additionalSubtypeExtraValue;
}
return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId,
R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, extraValue,
- false, false, compatibleSubtypeId);
+ false, false, subtypeId);
}
- private static int getInputMethodSubtypeId(final String localeString, final String extraValue) {
- // From the compatibility point of view, the calculation of subtype id has been copied from
- // {@link InputMethodSubtype} of JellyBean MR2.
- return Arrays.hashCode(new Object[] {
- localeString,
- KEYBOARD_MODE,
- extraValue,
- false /* isAuxiliary */,
- false /* overrideImplicitlyEnabledSubtype */ });
+ private static int getInputMethodSubtypeId(int nameId, String localeString,
+ String layoutExtraValue, String additionalSubtypeExtraValue) {
+ // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19.
+ return (new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark,
+ localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue,
+ false, false)).hashCode();
}
}
diff --git a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java
index e521ec807..08a2a8c5a 100644
--- a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java
@@ -62,22 +62,4 @@ public final class ApplicationUtils {
}
return "";
}
-
- /**
- * A utility method to get the application's PackageInfo.versionCode
- * @return the application's PackageInfo.versionCode
- */
- public static int getVersionCode(final Context context) {
- try {
- if (context == null) {
- return 0;
- }
- final String packageName = context.getPackageName();
- final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
- return info.versionCode;
- } catch (final NameNotFoundException e) {
- Log.e(TAG, "Could not find version info.", e);
- }
- return 0;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
index d12aad639..c2e97a36f 100644
--- a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
+++ b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
@@ -20,7 +20,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
- * This class is a holder of the result of an asynchronous computation.
+ * This class is a holder of a result of asynchronous computation.
*
* @param <E> the type of the result.
*/
@@ -36,9 +36,9 @@ public class AsyncResultHolder<E> {
}
/**
- * Sets the result value of this holder.
+ * Sets the result value to this holder.
*
- * @param result the value to set.
+ * @param result the value which is set.
*/
public void set(final E result) {
synchronized(mLock) {
@@ -54,12 +54,12 @@ public class AsyncResultHolder<E> {
* Causes the current thread to wait unless the value is set or the specified time is elapsed.
*
* @param defaultValue the default value.
- * @param timeOut the maximum time to wait.
- * @return if the result is set before the time limit then the result, otherwise defaultValue.
+ * @param timeOut the time to wait.
+ * @return if the result is set until the time limit then the result, otherwise defaultValue.
*/
public E get(final E defaultValue, final long timeOut) {
try {
- if (mLatch.await(timeOut, TimeUnit.MILLISECONDS)) {
+ if(mLatch.await(timeOut, TimeUnit.MILLISECONDS)) {
return mResult;
} else {
return defaultValue;
diff --git a/java/src/com/android/inputmethod/latin/utils/FileUtils.java b/java/src/com/android/inputmethod/latin/utils/FileUtils.java
deleted file mode 100644
index 83c1e7c4d..000000000
--- a/java/src/com/android/inputmethod/latin/utils/FileUtils.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import java.io.File;
-
-/**
- * A simple class to help with removing directories recursively.
- */
-public class FileUtils {
- public static boolean deleteRecursively(final File path) {
- if (path.isDirectory()) {
- for (final File child : path.listFiles()) {
- deleteRecursively(child);
- }
- }
- return path.delete();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
deleted file mode 100644
index 764ef72ce..000000000
--- a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.utils;
-
-import android.util.JsonReader;
-import android.util.JsonWriter;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public final class JsonUtils {
- private static final String TAG = JsonUtils.class.getSimpleName();
-
- private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName();
- private static final String STRING_CLASS_NAME = String.class.getSimpleName();
-
- private static final String EMPTY_STRING = "";
-
- public static List<Object> jsonStrToList(final String s) {
- final ArrayList<Object> list = CollectionUtils.newArrayList();
- final JsonReader reader = new JsonReader(new StringReader(s));
- try {
- reader.beginArray();
- while (reader.hasNext()) {
- reader.beginObject();
- while (reader.hasNext()) {
- final String name = reader.nextName();
- if (name.equals(INTEGER_CLASS_NAME)) {
- list.add(reader.nextInt());
- } else if (name.equals(STRING_CLASS_NAME)) {
- list.add(reader.nextString());
- } else {
- Log.w(TAG, "Invalid name: " + name);
- reader.skipValue();
- }
- }
- reader.endObject();
- }
- reader.endArray();
- return list;
- } catch (final IOException e) {
- } finally {
- close(reader);
- }
- return Collections.<Object>emptyList();
- }
-
- public static String listToJsonStr(final List<Object> list) {
- if (list == null || list.isEmpty()) {
- return EMPTY_STRING;
- }
- final StringWriter sw = new StringWriter();
- final JsonWriter writer = new JsonWriter(sw);
- try {
- writer.beginArray();
- for (final Object o : list) {
- writer.beginObject();
- if (o instanceof Integer) {
- writer.name(INTEGER_CLASS_NAME).value((Integer)o);
- } else if (o instanceof String) {
- writer.name(STRING_CLASS_NAME).value((String)o);
- }
- writer.endObject();
- }
- writer.endArray();
- return sw.toString();
- } catch (final IOException e) {
- } finally {
- close(writer);
- }
- return EMPTY_STRING;
- }
-
- private static void close(final Closeable closeable) {
- try {
- if (closeable != null) {
- closeable.close();
- }
- } catch (final IOException e) {
- // Ignore
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java
index d14ba508b..e958a7e71 100644
--- a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java
@@ -35,7 +35,7 @@ public final class LatinImeLoggerUtils {
public static void onSeparator(final int code, final int x, final int y) {
// Helper method to log a single code point separator
// TODO: cache this mapping of a code point to a string in a sparse array in StringUtils
- onSeparator(StringUtils.newSingleCodePointString(code), x, y);
+ onSeparator(new String(new int[]{code}, 0, 1), x, y);
}
public static void onSeparator(final String separator, final int x, final int y) {
diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
index 0c55484b4..22045aa38 100644
--- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
@@ -30,6 +30,9 @@ import java.util.Locale;
* dictionary pack.
*/
public final class LocaleUtils {
+ private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap();
+ private static final String LOCALE_AND_TIME_STR_SEPARATER = ",";
+
private LocaleUtils() {
// Intentional empty constructor for utility class.
}
@@ -165,14 +168,12 @@ public final class LocaleUtils {
* Creates a locale from a string specification.
*/
public static Locale constructLocaleFromString(final String localeStr) {
- if (localeStr == null) {
+ if (localeStr == null)
return null;
- }
synchronized (sLocaleCache) {
- Locale retval = sLocaleCache.get(localeStr);
- if (retval != null) {
- return retval;
- }
+ if (sLocaleCache.containsKey(localeStr))
+ return sLocaleCache.get(localeStr);
+ Locale retval = null;
String[] localeParams = localeStr.split("_", 3);
if (localeParams.length == 1) {
retval = new Locale(localeParams[0]);
@@ -187,4 +188,38 @@ public final class LocaleUtils {
return retval;
}
}
+
+ public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return EMPTY_LT_HASH_MAP;
+ }
+ final String[] ss = str.split(LOCALE_AND_TIME_STR_SEPARATER);
+ final int N = ss.length;
+ if (N < 2 || N % 2 != 0) {
+ return EMPTY_LT_HASH_MAP;
+ }
+ final HashMap<String, Long> retval = CollectionUtils.newHashMap();
+ for (int i = 0; i < N / 2; ++i) {
+ final String localeStr = ss[i * 2];
+ final long time = Long.valueOf(ss[i * 2 + 1]);
+ retval.put(localeStr, time);
+ }
+ return retval;
+ }
+
+ public static String localeAndTimeHashMapToStr(HashMap<String, Long> map) {
+ if (map == null || map.isEmpty()) {
+ return "";
+ }
+ final StringBuilder builder = new StringBuilder();
+ for (String localeStr : map.keySet()) {
+ if (builder.length() > 0) {
+ builder.append(LOCALE_AND_TIME_STR_SEPARATER);
+ }
+ final Long time = map.get(localeStr);
+ builder.append(localeStr).append(LOCALE_AND_TIME_STR_SEPARATER);
+ builder.append(String.valueOf(time));
+ }
+ return builder.toString();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
index deb28a08d..22c92446a 100644
--- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
@@ -227,19 +227,19 @@ public final class ResourceUtils {
final String keyboardHeightString = getDeviceOverrideValue(res, R.array.keyboard_heights);
final float keyboardHeight;
if (TextUtils.isEmpty(keyboardHeightString)) {
- keyboardHeight = res.getDimension(R.dimen.config_default_keyboard_height);
+ keyboardHeight = res.getDimension(R.dimen.keyboardHeight);
} else {
keyboardHeight = Float.parseFloat(keyboardHeightString) * dm.density;
}
final float maxKeyboardHeight = res.getFraction(
- R.fraction.config_max_keyboard_height, dm.heightPixels, dm.heightPixels);
+ R.fraction.maxKeyboardHeight, dm.heightPixels, dm.heightPixels);
float minKeyboardHeight = res.getFraction(
- R.fraction.config_min_keyboard_height, dm.heightPixels, dm.heightPixels);
+ R.fraction.minKeyboardHeight, dm.heightPixels, dm.heightPixels);
if (minKeyboardHeight < 0.0f) {
// Specified fraction was negative, so it should be calculated against display
// width.
minKeyboardHeight = -res.getFraction(
- R.fraction.config_min_keyboard_height, dm.widthPixels, dm.widthPixels);
+ R.fraction.minKeyboardHeight, dm.widthPixels, dm.widthPixels);
}
// Keyboard height will not exceed maxKeyboardHeight and will not be less than
// minKeyboardHeight.
diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java
index 8469c87b0..44e5d17b4 100644
--- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
+++ b/java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java
@@ -21,22 +21,22 @@ import android.os.Looper;
import java.lang.ref.WeakReference;
-public class LeakGuardHandlerWrapper<T> extends Handler {
- private final WeakReference<T> mOwnerInstanceRef;
+public class StaticInnerHandlerWrapper<T> extends Handler {
+ private final WeakReference<T> mOuterInstanceRef;
- public LeakGuardHandlerWrapper(final T ownerInstance) {
- this(ownerInstance, Looper.myLooper());
+ public StaticInnerHandlerWrapper(final T outerInstance) {
+ this(outerInstance, Looper.myLooper());
}
- public LeakGuardHandlerWrapper(final T ownerInstance, final Looper looper) {
+ public StaticInnerHandlerWrapper(final T outerInstance, final Looper looper) {
super(looper);
- if (ownerInstance == null) {
- throw new NullPointerException("ownerInstance is null");
+ if (outerInstance == null) {
+ throw new NullPointerException("outerInstance is null");
}
- mOwnerInstanceRef = new WeakReference<T>(ownerInstance);
+ mOuterInstanceRef = new WeakReference<T>(outerInstance);
}
- public T getOwnerInstance() {
- return mOwnerInstanceRef.get();
+ public T getOuterInstance() {
+ return mOuterInstanceRef.get();
}
}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index df420417d..a36548392 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -16,15 +16,20 @@
package com.android.inputmethod.latin.utils;
-import android.text.TextUtils;
-import android.util.Log;
-
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.settings.SettingsValues;
+import android.text.TextUtils;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+
import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -34,8 +39,6 @@ public final class StringUtils {
public static final int CAPITALIZE_FIRST = 1; // First only
public static final int CAPITALIZE_ALL = 2; // All caps
- private static final String EMPTY_STRING = "";
-
private StringUtils() {
// This utility class is not publicly instantiable.
}
@@ -77,20 +80,6 @@ public final class StringUtils {
return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT));
}
- public static String joinCommaSplittableText(final String head, final String tail) {
- if (TextUtils.isEmpty(head) && TextUtils.isEmpty(tail)) {
- return EMPTY_STRING;
- }
- // Here either head or tail is not null.
- if (TextUtils.isEmpty(head)) {
- return tail;
- }
- if (TextUtils.isEmpty(tail)) {
- return head;
- }
- return head + SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT + tail;
- }
-
public static String appendToCommaSplittableTextIfNotExists(final String text,
final String extraValues) {
if (TextUtils.isEmpty(extraValues)) {
@@ -105,7 +94,7 @@ public final class StringUtils {
public static String removeFromCommaSplittableTextIfExists(final String text,
final String extraValues) {
if (TextUtils.isEmpty(extraValues)) {
- return EMPTY_STRING;
+ return "";
}
final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT);
if (!containsInArray(text, elements)) {
@@ -378,7 +367,7 @@ public final class StringUtils {
return false;
}
- public static boolean isEmptyStringOrWhiteSpaces(final String s) {
+ public static boolean isEmptyStringOrWhiteSpaces(String s) {
final int N = codePointCount(s);
for (int i = 0; i < N; ++i) {
if (!Character.isWhitespace(s.codePointAt(i))) {
@@ -389,9 +378,9 @@ public final class StringUtils {
}
@UsedForTesting
- public static String byteArrayToHexString(final byte[] bytes) {
+ public static String byteArrayToHexString(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
- return EMPTY_STRING;
+ return "";
}
final StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
@@ -404,7 +393,7 @@ public final class StringUtils {
* Convert hex string to byte array. The string length must be an even number.
*/
@UsedForTesting
- public static byte[] hexStringToByteArray(final String hexString) {
+ public static byte[] hexStringToByteArray(String hexString) {
if (TextUtils.isEmpty(hexString)) {
return null;
}
@@ -420,4 +409,67 @@ public final class StringUtils {
}
return bytes;
}
+
+ public static List<Object> jsonStrToList(String s) {
+ final ArrayList<Object> retval = CollectionUtils.newArrayList();
+ final JsonReader reader = new JsonReader(new StringReader(s));
+ try {
+ reader.beginArray();
+ while(reader.hasNext()) {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ final String name = reader.nextName();
+ if (name.equals(Integer.class.getSimpleName())) {
+ retval.add(reader.nextInt());
+ } else if (name.equals(String.class.getSimpleName())) {
+ retval.add(reader.nextString());
+ } else {
+ Log.w(TAG, "Invalid name: " + name);
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ }
+ reader.endArray();
+ return retval;
+ } catch (IOException e) {
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ }
+ }
+ return Collections.<Object>emptyList();
+ }
+
+ public static String listToJsonStr(List<Object> list) {
+ if (list == null || list.isEmpty()) {
+ return "";
+ }
+ final StringWriter sw = new StringWriter();
+ final JsonWriter writer = new JsonWriter(sw);
+ try {
+ writer.beginArray();
+ for (final Object o : list) {
+ writer.beginObject();
+ if (o instanceof Integer) {
+ writer.name(Integer.class.getSimpleName()).value((Integer)o);
+ } else if (o instanceof String) {
+ writer.name(String.class.getSimpleName()).value((String)o);
+ }
+ writer.endObject();
+ }
+ writer.endArray();
+ return sw.toString();
+ } catch (IOException e) {
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ return "";
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index fdbe81ab6..102a41b4e 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -197,9 +197,7 @@ public final class SubtypeLocaleUtils {
// es_US spanish F Español (EE.UU.) exception
// fr azerty F Français
// fr_CA qwerty F Français (Canada)
- // fr_CH swiss F Français (Suisse)
// de qwertz F Deutsch
- // de_CH swiss T Deutsch (Schweiz)
// zz qwerty F No language (QWERTY) in system locale
// fr qwertz T Français (QWERTZ)
// de qwerty T Deutsch (QWERTY)
@@ -300,9 +298,7 @@ public final class SubtypeLocaleUtils {
// es_US spanish F Es Español Español (EE.UU.) exception
// fr azerty F Fr Français Français
// fr_CA qwerty F Fr Français Français (Canada)
- // fr_CH swiss F Fr Français Français (Suisse)
// de qwertz F De Deutsch Deutsch
- // de_CH swiss T De Deutsch Deutsch (Schweiz)
// zz qwerty F QWERTY QWERTY
// fr qwertz T Fr Français Français
// de qwerty T De Deutsch Deutsch
diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
index 087a7f255..47ea1ea75 100644
--- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
@@ -22,9 +22,6 @@ import android.graphics.Typeface;
import android.util.SparseArray;
public final class TypefaceUtils {
- private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
- private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
-
private TypefaceUtils() {
// This utility class is not publicly instantiable.
}
@@ -34,7 +31,7 @@ public final class TypefaceUtils {
// Working variable for the following method.
private static final Rect sTextHeightBounds = new Rect();
- private static float getCharHeight(final char[] referenceChar, final Paint paint) {
+ public static float getCharHeight(final char[] referenceChar, final Paint paint) {
final int key = getCharGeometryCacheKey(referenceChar[0], paint);
synchronized (sTextHeightCache) {
final Float cachedValue = sTextHeightCache.get(key);
@@ -54,7 +51,7 @@ public final class TypefaceUtils {
// Working variable for the following method.
private static final Rect sTextWidthBounds = new Rect();
- private static float getCharWidth(final char[] referenceChar, final Paint paint) {
+ public static float getCharWidth(final char[] referenceChar, final Paint paint) {
final int key = getCharGeometryCacheKey(referenceChar[0], paint);
synchronized (sTextWidthCache) {
final Float cachedValue = sTextWidthCache.get(key);
@@ -69,6 +66,11 @@ public final class TypefaceUtils {
}
}
+ public static float getStringWidth(final String string, final Paint paint) {
+ paint.getTextBounds(string, 0, string.length(), sTextWidthBounds);
+ return sTextWidthBounds.width();
+ }
+
private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
final int labelSize = (int)paint.getTextSize();
final Typeface face = paint.getTypeface();
@@ -84,25 +86,9 @@ public final class TypefaceUtils {
}
}
- public static float getReferenceCharHeight(final Paint paint) {
- return getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint);
- }
-
- public static float getReferenceCharWidth(final Paint paint) {
- return getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint);
- }
-
- public static float getReferenceDigitWidth(final Paint paint) {
- return getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint);
- }
-
- // Working variable for the following method.
- private static final Rect sStringWidthBounds = new Rect();
-
- public static float getStringWidth(final String string, final Paint paint) {
- synchronized (sStringWidthBounds) {
- paint.getTextBounds(string, 0, string.length(), sStringWidthBounds);
- return sStringWidthBounds.width();
- }
+ public static float getLabelWidth(final String label, final Paint paint) {
+ final Rect textBounds = new Rect();
+ paint.getTextBounds(label, 0, label.length(), textBounds);
+ return textBounds.width();
}
}
diff --git a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java b/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java
deleted file mode 100644
index 4feee4393..000000000
--- a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.inputmethod.latin.utils;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-
-import java.util.ArrayList;
-
-// This has information that belong to a unigram. This class has some detailed attributes such as
-// historical information but they have to be checked only for testing purpose.
-@UsedForTesting
-public class UnigramProperty {
- public final String mCodePoints;
- public final boolean mIsNotAWord;
- public final boolean mIsBlacklisted;
- public final boolean mHasBigrams;
- public final boolean mHasShortcuts;
- 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 final ArrayList<WeightedString> mShortcutTargets = CollectionUtils.newArrayList();
-
- private static int getCodePointCount(final int[] codePoints) {
- for (int i = 0; i < codePoints.length; i++) {
- if (codePoints[i] == 0) {
- return i;
- }
- }
- return codePoints.length;
- }
-
- // This represents invalid unigram when the probability is BinaryDictionary.NOT_A_PROBABILITY.
- public UnigramProperty(final int[] codePoints, final boolean isNotAWord,
- final boolean isBlacklisted, final boolean hasBigram,
- final boolean hasShortcuts, final int probability, final int timestamp,
- final int level, final int count, final ArrayList<int[]> shortcutTargets,
- final ArrayList<Integer> shortcutProbabilities) {
- mCodePoints = new String(codePoints, 0 /* offset */, getCodePointCount(codePoints));
- mIsNotAWord = isNotAWord;
- mIsBlacklisted = isBlacklisted;
- mHasBigrams = hasBigram;
- mHasShortcuts = hasShortcuts;
- mProbability = probability;
- mTimestamp = timestamp;
- mLevel = level;
- mCount = count;
- 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));
- mShortcutTargets.add(
- new WeightedString(shortcutTargetString, shortcutProbabilities.get(i)));
- }
- }
-
- @UsedForTesting
- public boolean isValid() {
- return mProbability != BinaryDictionary.NOT_A_PROBABILITY;
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
index db628fe18..635afe7cc 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
@@ -70,11 +70,10 @@ public final class UserHistoryDictIOUtils {
/**
* 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);
+ final FormatOptions formatOptions) {
+ final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams);
fusionDict.addOptionAttribute(USES_FORGETTING_CURVE_KEY, USES_FORGETTING_CURVE_VALUE);
fusionDict.addOptionAttribute(LAST_UPDATED_TIME_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
@@ -92,10 +91,11 @@ public final class UserHistoryDictIOUtils {
* Constructs a new FusionDictionary from BigramDictionaryInterface.
*/
@UsedForTesting
- static FusionDictionary constructFusionDictionary(final BigramDictionaryInterface dict,
- final UserHistoryDictionaryBigramList bigrams, final HashMap<String, String> options) {
+ static FusionDictionary constructFusionDictionary(
+ final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
final FusionDictionary fusionDict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(options));
+ new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
+ false));
int profTotal = 0;
for (final String word1 : bigrams.keySet()) {
final HashMap<String, Byte> word1Bigrams = bigrams.getBigrams(word1);
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java
index 677035ed6..1992b2f5d 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java
@@ -20,9 +20,6 @@ import android.util.Log;
import java.util.concurrent.TimeUnit;
-import com.android.inputmethod.annotations.UsedForTesting;
-
-@UsedForTesting
public final class UserHistoryForgettingCurveUtils {
private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -121,22 +118,18 @@ public final class UserHistoryForgettingCurveUtils {
}
}
- @UsedForTesting
/* package */ static int fcToElapsedTime(byte fc) {
return fc & 0x0F;
}
- @UsedForTesting
/* package */ static int fcToCount(byte fc) {
return (fc >> 4) & 0x03;
}
- @UsedForTesting
/* package */ static int fcToLevel(byte fc) {
return (fc >> 6) & 0x03;
}
- @UsedForTesting
private static int calcFreq(int elapsedTime, int count, int level) {
if (level <= 0) {
// Reserved words, just return -1
@@ -165,7 +158,6 @@ public final class UserHistoryForgettingCurveUtils {
return calcFreq(elapsedTime, count, level);
}
- @UsedForTesting
public static byte pushElapsedTime(byte fc) {
int elapsedTime = fcToElapsedTime(fc);
int count = fcToCount(fc);
@@ -181,7 +173,6 @@ public final class UserHistoryForgettingCurveUtils {
return calcFc(elapsedTime, count, level);
}
- @UsedForTesting
public static byte pushCount(byte fc, boolean isValid) {
final int elapsedTime = fcToElapsedTime(fc);
int count = fcToCount(fc);
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 28a647b02..da9c61103 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -102,6 +102,10 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
&& ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG;
private static final boolean DEBUG_REPLAY_AFTER_FEEDBACK = false
&& ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG;
+ // Whether the TextView contents are logged at the end of the session. true will disclose
+ // private info.
+ private static final boolean LOG_FULL_TEXTVIEW_CONTENTS = false
+ && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG;
// Whether the feedback dialog preserves the editable text across invocations. Should be false
// for normal research builds so users do not have to delete the same feedback string they
// entered earlier. Should be true for builds internal to a development team so when the text
@@ -164,6 +168,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// U+E001 is in the "private-use area"
/* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001";
protected static final int SUSPEND_DURATION_IN_MINUTES = 1;
+ // set when LatinIME should ignore an onUpdateSelection() callback that
+ // arises from operations in this class
+ private static boolean sLatinIMEExpectingUpdateSelection = false;
// used to check whether words are not unique
private Suggest mSuggest;
@@ -1119,6 +1126,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
new Object[] { applicationSpecifiedCompletions });
}
+ public static boolean getAndClearLatinIMEExpectingUpdateSelection() {
+ boolean returnValue = sLatinIMEExpectingUpdateSelection;
+ sLatinIMEExpectingUpdateSelection = false;
+ return returnValue;
+ }
+
/**
* The IME is finishing; it is either being destroyed, or is about to be hidden.
*
@@ -1136,12 +1149,51 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
// if called from finishViews(), which is called from hideWindow() and onDestroy(). These
// are the situations in which we want to finish up the researchLog.
if (ic != null && !finishingInput) {
+ final boolean isTextTruncated;
+ final String text;
+ if (LOG_FULL_TEXTVIEW_CONTENTS) {
+ // Capture the TextView contents. This will trigger onUpdateSelection(), so we
+ // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called,
+ // it can tell that it was generated by the logging code, and not by the user, and
+ // therefore keep user-visible state as is.
+ ic.beginBatchEdit();
+ ic.performContextMenuAction(android.R.id.selectAll);
+ CharSequence charSequence = ic.getSelectedText(0);
+ if (savedSelectionStart != -1 && savedSelectionEnd != -1) {
+ ic.setSelection(savedSelectionStart, savedSelectionEnd);
+ }
+ ic.endBatchEdit();
+ sLatinIMEExpectingUpdateSelection = true;
+ if (TextUtils.isEmpty(charSequence)) {
+ isTextTruncated = false;
+ text = "";
+ } else {
+ if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) {
+ int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE;
+ // do not cut in the middle of a supplementary character
+ final char c = charSequence.charAt(length - 1);
+ if (Character.isHighSurrogate(c)) {
+ length--;
+ }
+ final CharSequence truncatedCharSequence = charSequence.subSequence(0,
+ length);
+ isTextTruncated = true;
+ text = truncatedCharSequence.toString();
+ } else {
+ isTextTruncated = false;
+ text = charSequence.toString();
+ }
+ }
+ } else {
+ isTextTruncated = true;
+ text = "";
+ }
final ResearchLogger researchLogger = getInstance();
// Assume that OUTPUT_ENTIRE_BUFFER is only true when we don't care about privacy (e.g.
// during a live user test), so the normal isPotentiallyPrivate and
// isPotentiallyRevealing flags do not apply
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONFINISHINPUTVIEWINTERNAL,
- true /* isTextTruncated */, "" /* text */);
+ isTextTruncated, text);
researchLogger.commitCurrentLogUnit();
getInstance().stop();
}
@@ -1161,7 +1213,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
public static void latinIME_onUpdateSelection(final int lastSelectionStart,
final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
final int newSelStart, final int newSelEnd, final int composingSpanStart,
- final int composingSpanEnd, final RichInputConnection connection) {
+ final int composingSpanEnd, final boolean expectingUpdateSelection,
+ final boolean expectingUpdateSelectionFromLogger,
+ final RichInputConnection connection) {
String word = "";
if (connection != null) {
TextRange range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1);
@@ -1173,8 +1227,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final String scrubbedWord = researchLogger.scrubWord(word);
researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONUPDATESELECTION, lastSelectionStart,
lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd,
- composingSpanStart, composingSpanEnd, false /* expectingUpdateSelection */,
- false /* expectingUpdateSelectionFromLogger */, scrubbedWord);
+ composingSpanStart, composingSpanEnd, expectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, scrubbedWord);
}
/**
@@ -1357,9 +1411,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD =
new LogStatement("MainKeyboardViewSetKeyboard", false, false, "elementId", "locale",
"orientation", "width", "modeName", "action", "navigateNext",
- "navigatePrevious", "clobberSettingsKey", "passwordInput",
- "supportsSwitchingToShortcutIme", "hasShortcutKey", "languageSwitchKeyEnabled",
- "isMultiLine", "tw", "th",
+ "navigatePrevious", "clobberSettingsKey", "passwordInput", "shortcutKeyEnabled",
+ "hasShortcutKey", "languageSwitchKeyEnabled", "isMultiLine", "tw", "th",
"keys");
public static void mainKeyboardView_setKeyboard(final Keyboard keyboard,
final int orientation) {
@@ -1372,7 +1425,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
orientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(),
kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey,
- isPasswordView, kid.mSupportsSwitchingToShortcutIme, kid.mHasShortcutKey,
+ isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey,
kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth,
keyboard.mOccupiedHeight, keyboard.getKeys());
}