diff options
Diffstat (limited to 'java/src')
30 files changed, 1016 insertions, 220 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java new file mode 100644 index 000000000..25ff8d0ce --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java @@ -0,0 +1,370 @@ +/* + * 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; + +import static com.android.inputmethod.latin.Constants.NOT_A_COORDINATE; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TabHost; +import android.widget.TabHost.OnTabChangeListener; +import android.widget.TextView; + +import com.android.inputmethod.keyboard.internal.RecentsKeyboard; +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; +import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.ResourceUtils; + +import java.util.HashMap; + +/** + * View class to implement Emoji keyboards. + * The Emoji keyboard consists of group of views {@link R.layout#emoji_keyboard_view}. + * <ol> + * <li> Emoji category tabs. + * <li> Delete button. + * <li> Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab. + * <li> Back to main keyboard button and enter button. + * </ol> + * Because of the above reasons, this class doesn't extend {@link KeyboardView}. + */ +public final class EmojiKeyboardView extends LinearLayout implements OnTabChangeListener, + ViewPager.OnPageChangeListener, View.OnClickListener, + ScrollKeyboardView.OnKeyClickListener { + private final int mKeyBackgroundId; + private final ColorStateList mTabLabelColor; + private final EmojiKeyboardAdapter mEmojiKeyboardAdapter; + + private TabHost mTabHost; + private ViewPager mEmojiPager; + + private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; + + private int mCurrentCategory = CATEGORY_UNSPECIFIED; + private static final int CATEGORY_UNSPECIFIED = -1; + private static final int CATEGORY_RECENTS = 0; + private static final int CATEGORY_PEOPLE = 1; + private static final int CATEGORY_OBJECTS = 2; + private static final int CATEGORY_NATURE = 3; + private static final int CATEGORY_PLACES = 4; + private static final int CATEGORY_SYMBOLS = 5; + private static final int CATEGORY_EMOTICONS = 6; + private static final HashMap<String, Integer> sCategoryNameToIdMap = + CollectionUtils.newHashMap(); + private static final String[] sCategoryName = { + "recents", "people", "objects", "nature", "places", "symbols", "emoticons" + }; + private static final int[] sCategoryIcon = new int[] { + R.drawable.ic_emoji_recent_light, + R.drawable.ic_emoji_people_light, + R.drawable.ic_emoji_objects_light, + R.drawable.ic_emoji_nature_light, + R.drawable.ic_emoji_places_light, + R.drawable.ic_emoji_symbols_light, + 0 + }; + private static final String[] sCategoryLabel = { + null, null, null, null, null, null, + ":-)" + }; + private static final int[] sCategoryElementId = { + KeyboardId.ELEMENT_EMOJI_RECENTS, + KeyboardId.ELEMENT_EMOJI_CATEGORY1, + KeyboardId.ELEMENT_EMOJI_CATEGORY2, + KeyboardId.ELEMENT_EMOJI_CATEGORY3, + KeyboardId.ELEMENT_EMOJI_CATEGORY4, + KeyboardId.ELEMENT_EMOJI_CATEGORY5, + KeyboardId.ELEMENT_EMOJI_CATEGORY6, + }; + + public EmojiKeyboardView(final Context context, final AttributeSet attrs) { + this(context, attrs, R.attr.emojiKeyboardViewStyle); + } + + public EmojiKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, + R.styleable.KeyboardView, defStyle, R.style.KeyboardView); + mKeyBackgroundId = keyboardViewAttr.getResourceId( + R.styleable.KeyboardView_keyBackground, 0); + keyboardViewAttr.recycle(); + final TypedArray emojiKeyboardViewAttr = context.obtainStyledAttributes(attrs, + R.styleable.EmojiKeyboardView, defStyle, R.style.EmojiKeyboardView); + mTabLabelColor = emojiKeyboardViewAttr.getColorStateList( + R.styleable.EmojiKeyboardView_emojiTabLabelColor); + emojiKeyboardViewAttr.recycle(); + final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( + context, null /* editorInfo */); + final Resources res = context.getResources(); + builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); + // TODO: Make Keyboard height variable. + builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), + (int)(ResourceUtils.getDefaultKeyboardHeight(res) + - res.getDimension(R.dimen.suggestions_strip_height))); + builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */); + final KeyboardLayoutSet layoutSet = builder.build(); + mEmojiKeyboardAdapter = new EmojiKeyboardAdapter(layoutSet, this); + // TODO: Save/restore recent keys from/to preferences. + } + + @Override + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + final Resources res = getContext().getResources(); + // The main keyboard expands to the entire this {@link KeyboardView}. + final int width = ResourceUtils.getDefaultKeyboardWidth(res) + + getPaddingLeft() + getPaddingRight(); + final int height = ResourceUtils.getDefaultKeyboardHeight(res) + + res.getDimensionPixelSize(R.dimen.suggestions_strip_height) + + getPaddingTop() + getPaddingBottom(); + setMeasuredDimension(width, height); + } + + private void addTab(final TabHost host, final int category) { + final String tabId = sCategoryName[category]; + sCategoryNameToIdMap.put(tabId, category); + final TabHost.TabSpec tspec = host.newTabSpec(tabId); + tspec.setContent(R.id.emoji_keyboard_dummy); + if (sCategoryIcon[category] != 0) { + final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate( + R.layout.emoji_keyboard_tab_icon, null); + iconView.setImageResource(sCategoryIcon[category]); + tspec.setIndicator(iconView); + } + if (sCategoryLabel[category] != null) { + final TextView textView = (TextView)LayoutInflater.from(getContext()).inflate( + R.layout.emoji_keyboard_tab_label, null); + textView.setText(sCategoryLabel[category]); + textView.setTextColor(mTabLabelColor); + textView.setBackgroundResource(mKeyBackgroundId); + tspec.setIndicator(textView); + } + host.addTab(tspec); + } + + @Override + protected void onFinishInflate() { + mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost); + mTabHost.setup(); + addTab(mTabHost, CATEGORY_RECENTS); + addTab(mTabHost, CATEGORY_PEOPLE); + addTab(mTabHost, CATEGORY_OBJECTS); + addTab(mTabHost, CATEGORY_NATURE); + addTab(mTabHost, CATEGORY_PLACES); + addTab(mTabHost, CATEGORY_SYMBOLS); + addTab(mTabHost, CATEGORY_EMOTICONS); + mTabHost.setOnTabChangedListener(this); + mTabHost.getTabWidget().setStripEnabled(true); + + mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager); + mEmojiPager.setAdapter(mEmojiKeyboardAdapter); + mEmojiPager.setOnPageChangeListener(this); + mEmojiPager.setOffscreenPageLimit(0); + final ViewGroup.LayoutParams lp = mEmojiPager.getLayoutParams(); + final Resources res = getResources(); + lp.height = ResourceUtils.getDefaultKeyboardHeight(res) + - res.getDimensionPixelSize(R.dimen.suggestions_strip_height); + mEmojiPager.setLayoutParams(lp); + + // TODO: Record current category. + final int category = CATEGORY_PEOPLE; + setCurrentCategory(category, true /* force */); + + // TODO: Implement auto repeat, using View.OnTouchListener? + final View deleteKey = findViewById(R.id.emoji_keyboard_delete); + deleteKey.setBackgroundResource(mKeyBackgroundId); + deleteKey.setTag(Constants.CODE_DELETE); + deleteKey.setOnClickListener(this); + final View alphabetKey = findViewById(R.id.emoji_keyboard_alphabet); + alphabetKey.setBackgroundResource(mKeyBackgroundId); + alphabetKey.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL); + alphabetKey.setOnClickListener(this); + final View sendKey = findViewById(R.id.emoji_keyboard_send); + sendKey.setBackgroundResource(mKeyBackgroundId); + sendKey.setTag(Constants.CODE_ENTER); + sendKey.setOnClickListener(this); + } + + @Override + public void onTabChanged(final String tabId) { + final int category = sCategoryNameToIdMap.get(tabId); + setCurrentCategory(category, false /* force */); + } + + + @Override + public void onPageSelected(final int position) { + setCurrentCategory(position, false /* force */); + } + + @Override + public void onPageScrollStateChanged(final int state) { + // Ignore this message. Only want the actual page selected. + } + + @Override + public void onPageScrolled(final int position, final float positionOffset, + final int positionOffsetPixels) { + // Ignore this message. Only want the actual page selected. + } + + @Override + public void onClick(final View v) { + if (v.getTag() instanceof Integer) { + final int code = (Integer)v.getTag(); + registerCode(code); + return; + } + } + + private void registerCode(final int code) { + mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */); + mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE); + mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); + } + + @Override + public void onKeyClick(final Key key) { + mEmojiKeyboardAdapter.addRecentKey(key); + final int code = key.getCode(); + if (code == Constants.CODE_OUTPUT_TEXT) { + mKeyboardActionListener.onTextInput(key.getOutputText()); + return; + } + registerCode(code); + } + + public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { + // TODO: + } + + public void setKeyboardActionListener(final KeyboardActionListener listener) { + mKeyboardActionListener = listener; + } + + private void setCurrentCategory(final int category, final boolean force) { + if (mCurrentCategory == category && !force) { + return; + } + + mCurrentCategory = category; + if (force || mEmojiPager.getCurrentItem() != category) { + mEmojiPager.setCurrentItem(category, true /* smoothScroll */); + } + if (force || mTabHost.getCurrentTab() != category) { + mTabHost.setCurrentTab(category); + } + // TODO: Record current category + } + + private static class EmojiKeyboardAdapter extends PagerAdapter { + private final ScrollKeyboardView.OnKeyClickListener mListener; + private final KeyboardLayoutSet mLayoutSet; + private final RecentsKeyboard mRecentsKeyboard; + private final SparseArray<ScrollKeyboardView> mActiveKeyboardView = + CollectionUtils.newSparseArray(); + private int mActivePosition = CATEGORY_UNSPECIFIED; + + public EmojiKeyboardAdapter(final KeyboardLayoutSet layoutSet, + final ScrollKeyboardView.OnKeyClickListener listener) { + mListener = listener; + mLayoutSet = layoutSet; + mRecentsKeyboard = new RecentsKeyboard( + layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS)); + } + + public void addRecentKey(final Key key) { + if (mActivePosition == CATEGORY_RECENTS) { + return; + } + mRecentsKeyboard.addRecentKey(key); + final KeyboardView recentKeyboardView = mActiveKeyboardView.get(CATEGORY_RECENTS); + if (recentKeyboardView != null) { + recentKeyboardView.invalidateAllKeys(); + } + } + + @Override + public int getCount() { + return sCategoryName.length; + } + + @Override + public void setPrimaryItem(final View container, final int position, final Object object) { + if (mActivePosition == position) { + return; + } + final ScrollKeyboardView oldKeyboardView = mActiveKeyboardView.get(mActivePosition); + if (oldKeyboardView != null) { + oldKeyboardView.releaseCurrentKey(); + oldKeyboardView.deallocateMemory(); + } + mActivePosition = position; + } + + @Override + public Object instantiateItem(final ViewGroup container, final int position) { + final int elementId = sCategoryElementId[position]; + final Keyboard keyboard = (elementId == KeyboardId.ELEMENT_EMOJI_RECENTS) + ? mRecentsKeyboard : mLayoutSet.getKeyboard(elementId); + final LayoutInflater inflater = LayoutInflater.from(container.getContext()); + 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); + final ScrollViewWithNotifier scrollView = (ScrollViewWithNotifier)view.findViewById( + R.id.emoji_keyboard_scroller); + keyboardView.setScrollView(scrollView); + container.addView(view); + mActiveKeyboardView.put(position, keyboardView); + return view; + } + + @Override + public boolean isViewFromObject(final View view, final Object object) { + return view == object; + } + + @Override + public void destroyItem(final ViewGroup container, final int position, + final Object object) { + final ScrollKeyboardView keyboardView = mActiveKeyboardView.get(position); + if (keyboardView != null) { + keyboardView.deallocateMemory(); + mActiveKeyboardView.remove(position); + } + container.removeView(keyboardView); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 143c6e848..3ea68806b 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -764,8 +764,9 @@ public class Key implements Comparable<Key> { } public final int getDrawX() { + final int x = getX(); final OptionalAttributes attrs = mOptionalAttributes; - return (attrs == null) ? mX : mX + attrs.mVisualInsetsLeft; + return (attrs == null) ? x : x + attrs.mVisualInsetsLeft; } public final int getDrawWidth() { @@ -823,9 +824,9 @@ public class Key implements Comparable<Key> { * @return the square of the distance of the point from the nearest edge of the key */ public int squaredDistanceToEdge(final int x, final int y) { - final int left = mX; + final int left = getX(); final int right = left + mWidth; - final int top = mY; + final int top = getY(); final int bottom = top + mHeight; final int edgeX = x < left ? left : (x > right ? right : x); final int edgeY = y < top ? top : (y > bottom ? bottom : y); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 098c8b3df..c319c57a1 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -66,7 +66,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private SharedPreferences mPrefs; private InputView mCurrentInputView; + private View mMainKeyboardFrame; private MainKeyboardView mKeyboardView; + private EmojiKeyboardView mEmojiKeyboardView; private LatinIME mLatinIME; private Resources mResources; @@ -167,6 +169,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } private void setKeyboard(final Keyboard keyboard) { + // Make {@link MainKeyboardView} visible and hide {@link EmojiKeyboardView}. + setMainKeyboardFrame(); final MainKeyboardView keyboardView = mKeyboardView; final Keyboard oldKeyboard = keyboardView.getKeyboard(); keyboardView.setKeyboard(keyboard); @@ -253,6 +257,18 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); } + private void setMainKeyboardFrame() { + mMainKeyboardFrame.setVisibility(View.VISIBLE); + mEmojiKeyboardView.setVisibility(View.GONE); + } + + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setEmojiKeyboard() { + mMainKeyboardFrame.setVisibility(View.GONE); + mEmojiKeyboardView.setVisibility(View.VISIBLE); + } + // Implements {@link KeyboardState.SwitchActions}. @Override public void requestUpdatingShiftState() { @@ -292,6 +308,20 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState()); } + public boolean isShowingMoreKeysPanel() { + if (mEmojiKeyboardView.getVisibility() == View.VISIBLE) { + return false; + } + return mKeyboardView.isShowingMoreKeysPanel(); + } + + public View getVisibleKeyboardView() { + if (mEmojiKeyboardView.getVisibility() == View.VISIBLE) { + return mEmojiKeyboardView; + } + return mKeyboardView; + } + public MainKeyboardView getMainKeyboardView() { return mKeyboardView; } @@ -304,10 +334,16 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { setContextThemeWrapper(mLatinIME, mKeyboardTheme); mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( R.layout.input_view, null); + mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); + mEmojiKeyboardView = (EmojiKeyboardView)mCurrentInputView.findViewById( + R.id.emoji_keyboard_view); mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); mKeyboardView.setKeyboardActionListener(mLatinIME); + mEmojiKeyboardView.setHardwareAcceleratedDrawingEnabled( + isHardwareAcceleratedDrawingEnabled); + mEmojiKeyboardView.setKeyboardActionListener(mLatinIME); // This always needs to be set since the accessibility state can // potentially change without the input view being re-created. diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index c0c02f10a..cd127c760 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -342,7 +342,9 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get for (int centerY = yStart; centerY <= yEnd; centerY += mCellHeight) { int index = baseIndexOfCurrentRow; for (int centerX = xStart; centerX <= xEnd; centerX += mCellWidth) { - if (key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { + // TODO: Remove "index < neighborCountPerCell.length" below. + if (index < neighborCountPerCell.length + && key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { neighborsFlatBuffer[index * keyCount + neighborCountPerCell[index]] = key; ++neighborCountPerCell[index]; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java index b21ea3f71..05d855e31 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java @@ -124,10 +124,11 @@ public final class KeyStylesSet { @Override public int getFlags(final TypedArray a, final int index) { + final int parentFlags = mStyles.get(mParentStyleName).getFlags(a, index); final Integer value = (Integer)mStyleAttributes.get(index); - final int flags = a.getInt(index, (value != null) ? value : 0); - final KeyStyle parentStyle = mStyles.get(mParentStyleName); - return flags | parentStyle.getFlags(a, index); + final int styleFlags = (value != null) ? value : 0; + final int flags = a.getInt(index, 0); + return flags | styleFlags | parentFlags; } public void readKeyAttributes(final TypedArray keyAttr) { @@ -163,7 +164,8 @@ public final class KeyStylesSet { private void readFlags(final TypedArray a, final int index) { if (a.hasValue(index)) { final Integer value = (Integer)mStyleAttributes.get(index); - mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0)); + final int styleFlags = value != null ? value : 0; + mStyleAttributes.put(index, a.getInt(index, 0) | styleFlags); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 8c70389ba..22f7a83fc 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -625,6 +625,9 @@ public class KeyboardBuilder<KP extends KeyboardParams> { final AttributeSet attr = Xml.asAttributeSet(parser); final TypedArray caseAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Case); try { + final boolean keyboardLayoutSetMatched = matchString(caseAttr, + R.styleable.Keyboard_Case_keyboardLayoutSet, + SubtypeLocaleUtils.getKeyboardLayoutSetName(id.mSubtype)); final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr, R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId, KeyboardId.elementIdToName(id.mElementId)); @@ -657,15 +660,18 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage()); final boolean countryCodeMatched = matchString(caseAttr, R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); - final boolean selected = keyboardLayoutSetElementMatched && modeMatched - && navigateNextMatched && navigatePreviousMatched && passwordInputMatched - && clobberSettingsKeyMatched && shortcutKeyEnabledMatched - && shortcutKeyOnSymbolsMatched && hasShortcutKeyMatched - && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched - && localeCodeMatched && languageCodeMatched && countryCodeMatched; + final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched + && modeMatched && navigateNextMatched && navigatePreviousMatched + && passwordInputMatched && clobberSettingsKeyMatched + && 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( R.styleable.Keyboard_Case_keyboardLayoutSetElement), "keyboardLayoutSetElement"), diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 0b10a1d1a..089db12a2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -46,6 +46,7 @@ public final class KeyboardState { public void setAlphabetShiftLockedKeyboard(); public void setAlphabetShiftLockShiftedKeyboard(); public void setSymbolsKeyboard(); + public void setEmojiKeyboard(); /** * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}. @@ -71,7 +72,10 @@ public final class KeyboardState { private static final int SWITCH_STATE_MOMENTARY_ALPHA_SHIFT = 5; private int mSwitchState = SWITCH_STATE_ALPHA; + // TODO: Consolidate these two mode booleans into one integer to distinguish between alphabet, + // symbols, and emoji mode. private boolean mIsAlphabetMode; + private boolean mIsEmojiMode; private AlphabetShiftState mAlphabetShiftState = new AlphabetShiftState(); private boolean mPrevMainKeyboardWasShiftLocked; private int mRecapitalizeMode; @@ -86,6 +90,7 @@ public final class KeyboardState { public boolean mIsValid; public boolean mIsAlphabetMode; public boolean mIsAlphabetShiftLocked; + public boolean mIsEmojiMode; public int mShiftMode; @Override @@ -94,6 +99,8 @@ public final class KeyboardState { if (mIsAlphabetMode) { if (mIsAlphabetShiftLocked) return "ALPHABET_SHIFT_LOCKED"; return "ALPHABET_" + shiftModeToString(mShiftMode); + } else if (mIsEmojiMode) { + return "EMOJI"; } else { return "SYMBOLS"; } @@ -125,6 +132,7 @@ public final class KeyboardState { public void onSaveKeyboardState() { final SavedKeyboardState state = mSavedKeyboardState; state.mIsAlphabetMode = mIsAlphabetMode; + state.mIsEmojiMode = mIsEmojiMode; if (mIsAlphabetMode) { state.mIsAlphabetShiftLocked = mAlphabetShiftState.isShiftLocked(); state.mShiftMode = mAlphabetShiftState.isAutomaticShifted() ? AUTOMATIC_SHIFT @@ -145,6 +153,8 @@ public final class KeyboardState { } if (!state.mIsValid || state.mIsAlphabetMode) { setAlphabetKeyboard(); + } else if (state.mIsEmojiMode) { + setEmojiKeyboard(); } else { setSymbolsKeyboard(); } @@ -254,6 +264,7 @@ public final class KeyboardState { mSwitchActions.setAlphabetKeyboard(); mIsAlphabetMode = true; + mIsEmojiMode = false; mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; mSwitchState = SWITCH_STATE_ALPHA; mSwitchActions.requestUpdatingShiftState(); @@ -270,6 +281,15 @@ public final class KeyboardState { mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; } + private void setEmojiKeyboard() { + if (DEBUG_ACTION) { + Log.d(TAG, "setEmojiKeyboard"); + } + mIsAlphabetMode = false; + mIsEmojiMode = true; + mSwitchActions.setEmojiKeyboard(); + } + public void onPressKey(final int code, final boolean isSinglePointer, final int autoCaps) { if (DEBUG_EVENT) { Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) @@ -547,6 +567,8 @@ public final class KeyboardState { // If the code is a letter, update keyboard shift state. if (Constants.isLetterCode(code)) { updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); + } else if (code == Constants.CODE_EMOJI) { + setEmojiKeyboard(); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index c6d652c0e..4d3bdb0ca 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -275,7 +275,7 @@ public final class KeyboardTextsSet { /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", /* 51 */ "$", /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", - /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", + /* 53 */ "!fixedColumnOrder!3,!,\\,,?,:,;,@", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER // U+2605: "★" BLACK STAR @@ -357,10 +357,9 @@ public final class KeyboardTextsSet { // U+2030: "‰" PER MILLE SIGN /* 103 */ "\u2030", /* 104 */ ",", - /* 105 */ "!", - /* 106 */ "!", - /* 107 */ "?", - /* 108 */ "?", + /* 105~ */ + EMPTY, EMPTY, EMPTY, EMPTY, + /* ~108 */ /* 109 */ "\'", /* 110 */ "\"", /* 111 */ "\"", @@ -775,7 +774,7 @@ public final class KeyboardTextsSet { null, null, null, null, null, null, null, null, /* ~52 */ // U+00B7: "·" MIDDLE DOT - /* 53 */ "!fixedColumnOrder!9,\u00B7,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)", + /* 53 */ "!fixedColumnOrder!4,\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, @@ -1247,7 +1246,7 @@ public final class KeyboardTextsSet { /* ~52 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK - /* 53 */ "!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, @@ -1753,6 +1752,25 @@ public final class KeyboardTextsSet { /* 49 */ "!text/double_raqm_laqm", }; + /* 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, + /* ~52 */ + // U+055E: "՞" ARMENIAN QUESTION MARK + /* 53 */ "!fixedColumnOrder!4,\u055E,!,\\,,?,:,;,@", + /* 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, null, null, + /* ~107 */ + /* 108 */ "\u055E,?", + }; + /* Language is: Icelandic */ private static final String[] LANGUAGE_is = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -1894,7 +1912,7 @@ public final class KeyboardTextsSet { // U+20AA: "₪" NEW SHEQEL SIGN /* 51 */ "\u20AA", /* 52 */ null, - /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(|),)|(", + /* 53 */ null, // U+2605: "★" BLACK STAR /* 54 */ "\u2605", /* 55 */ null, @@ -1912,6 +1930,15 @@ public final class KeyboardTextsSet { // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK /* 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: Georgian */ @@ -3288,6 +3315,7 @@ public final class KeyboardTextsSet { "hi", LANGUAGE_hi, /* Hindi */ "hr", LANGUAGE_hr, /* Croatian */ "hu", LANGUAGE_hu, /* Hungarian */ + "hy", LANGUAGE_hy, /* Armenian */ "is", LANGUAGE_is, /* Icelandic */ "it", LANGUAGE_it, /* Italian */ "iw", LANGUAGE_iw, /* Hebrew */ diff --git a/java/src/com/android/inputmethod/keyboard/internal/RecentsKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/RecentsKeyboard.java new file mode 100644 index 000000000..629c60460 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/RecentsKeyboard.java @@ -0,0 +1,157 @@ +/* + * 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.text.TextUtils; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.utils.CollectionUtils; + +import java.util.ArrayDeque; +import java.util.Random; + +/** + * This is a Keyboard class to host recently used keys. + */ +// TODO: Save/restore recent keys from/to preferences. +public class RecentsKeyboard extends Keyboard { + private static final int TEMPLATE_KEY_CODE_0 = 0x30; + private static final int TEMPLATE_KEY_CODE_1 = 0x31; + + private final int mLeftPadding; + private final int mHorizontalStep; + private final int mVerticalStep; + private final int mColumnsNum; + private final int mMaxRecentKeyCount; + private final ArrayDeque<RecentKey> mRecentKeys = CollectionUtils.newArrayDeque(); + + private Key[] mCachedRecentKeys; + + public RecentsKeyboard(final Keyboard templateKeyboard) { + super(templateKeyboard); + final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0); + final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1); + mLeftPadding = key0.getX(); + mHorizontalStep = Math.abs(key1.getX() - key0.getX()); + mVerticalStep = key0.getHeight() + mVerticalGap; + mColumnsNum = mBaseWidth / mHorizontalStep; + final int rowsNum = mBaseHeight / mVerticalStep; + mMaxRecentKeyCount = mColumnsNum * rowsNum; + } + + private Key getTemplateKey(final int code) { + for (final Key key : super.getKeys()) { + if (key.getCode() == code) { + return key; + } + } + throw new RuntimeException("Can't find template key: code=" + code); + } + + private final Random random = new Random(); + + public void addRecentKey(final Key usedKey) { + synchronized (mRecentKeys) { + mCachedRecentKeys = null; + final RecentKey key = (usedKey instanceof RecentKey) + ? (RecentKey)usedKey : new RecentKey(usedKey); + while (mRecentKeys.remove(key)) { + // Remove duplicate keys. + } + mRecentKeys.addFirst(key); + while (mRecentKeys.size() > mMaxRecentKeyCount) { + mRecentKeys.removeLast(); + } + int index = 0; + for (final RecentKey recentKey : mRecentKeys) { + final int keyX = getKeyX(index); + final int keyY = getKeyY(index); + final int x = keyX+random.nextInt(recentKey.getWidth()); + final int y = keyY+random.nextInt(recentKey.getHeight()); + recentKey.updateCorrdinates(keyX, keyY); + index++; + } + } + } + + private int getKeyX(final int index) { + final int column = index % mColumnsNum; + return column * mHorizontalStep + mLeftPadding; + } + + private int getKeyY(final int index) { + final int row = index / mColumnsNum; + return row * mVerticalStep + mTopPadding; + } + + @Override + public Key[] getKeys() { + synchronized (mRecentKeys) { + if (mCachedRecentKeys != null) { + return mCachedRecentKeys; + } + mCachedRecentKeys = mRecentKeys.toArray(new Key[mRecentKeys.size()]); + return mCachedRecentKeys; + } + } + + @Override + public Key[] getNearestKeys(final int x, final int y) { + // TODO: Calculate the nearest key index in mRecentKeys from x and y. + return getKeys(); + } + + static final class RecentKey extends Key { + private int mCurrentX; + private int mCurrentY; + + public RecentKey(final Key originalKey) { + super(originalKey); + } + + public void updateCorrdinates(final int x, final int y) { + mCurrentX = x; + mCurrentY = y; + getHitBox().offsetTo(x, y); + } + + @Override + public int getX() { + return mCurrentX; + } + + @Override + public int getY() { + return mCurrentY; + } + + @Override + public boolean equals(final Object o) { + if (!(o instanceof Key)) return false; + final Key key = (Key)o; + if (getCode() != key.getCode()) return false; + if (!TextUtils.equals(getLabel(), key.getLabel())) return false; + return TextUtils.equals(getOutputText(), key.getOutputText()); + } + + @Override + public String toString() { + return "RecentKey: " + super.toString(); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java index 2628f59a8..b8ee976e8 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java @@ -33,6 +33,7 @@ import com.android.inputmethod.latin.R; * This is an extended {@link KeyboardView} class that hosts a scroll keyboard. * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support. */ +// TODO: Implement key popup preview. public final class ScrollKeyboardView extends KeyboardView implements ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener { private static final boolean PAGINATION = false; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index eb19ef932..e8b06570f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -21,7 +21,6 @@ import android.util.SparseArray; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils; import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; @@ -120,15 +119,16 @@ public final class BinaryDictionary extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, - 0 /* sessionId */); + additionalFeaturesOptions, 0 /* sessionId */); } @Override public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int sessionId) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, + final int sessionId) { if (!isValidDictionary()) return null; Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); @@ -148,8 +148,7 @@ public final class BinaryDictionary extends Dictionary { final InputPointers ips = composer.getInputPointers(); final int inputSize = isGesture ? ips.getPointerSize() : composerSize; mNativeSuggestOptions.setIsGesture(isGesture); - mNativeSuggestOptions.setAdditionalFeaturesOptions( - AdditionalFeaturesSettingUtils.getAdditionalNativeSuggestOptions()); + mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); // proximityInfo and/or prevWordForBigrams may not be null. final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index d9ded7ceb..8a3a88438 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -72,20 +72,23 @@ public abstract class Dictionary { * @param prevWord the previous word, or null if none * @param proximityInfo the object for key proximity. May be ignored by some implementations. * @param blockOffensiveWords whether to block potentially offensive words + * @param additionalFeaturesOptions options about additional features used for the suggestion. * @return the list of suggestions (possibly null if none) */ // TODO: pass more context than just the previous word, to enable better suggestions (n-gram // and more) abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords); + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions); // The default implementation of this method ignores sessionId. // Subclasses that want to use sessionId need to override this method. public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int sessionId) { - return getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords); + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, + final int sessionId) { + return getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions); } /** @@ -156,7 +159,7 @@ public abstract class Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { return null; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index d05bb1e55..bf075140e 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -58,18 +58,18 @@ public final class DictionaryCollection extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries; if (dictionaries.isEmpty()) return null; // To avoid creating unnecessary objects, we get the list out of the first // dictionary and add the rest to it if not null, hence the get(0) ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer, - prevWord, proximityInfo, blockOffensiveWords); + prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions); if (null == suggestions) suggestions = CollectionUtils.newArrayList(); final int length = dictionaries.size(); for (int i = 1; i < length; ++ i) { final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer, - prevWord, proximityInfo, blockOffensiveWords); + prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions); if (null != sugg) suggestions.addAll(sugg); } return suggestions; diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java index a97e053d0..5a453dde5 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java @@ -92,7 +92,7 @@ public class DictionaryWriter extends AbstractDictionaryWriter { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - boolean blockOffensiveWords) { + boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { // This class doesn't support suggestion. return null; } diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 939c2a03b..b92283c5b 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -272,19 +272,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { asyncReloadDictionaryIfRequired(); // Write lock because getSuggestions in native updates session status. if (mLocalDictionaryController.writeLock().tryLock()) { try { final ArrayList<SuggestedWordInfo> inMemDictSuggestion = mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords); + blockOffensiveWords, additionalFeaturesOptions); // TODO: Remove checking mIsUpdatable and use native suggestion. if (mBinaryDictionary != null && !mIsUpdatable) { final ArrayList<SuggestedWordInfo> binarySuggestion = mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords); + blockOffensiveWords, additionalFeaturesOptions); if (inMemDictSuggestion == null) { return binarySuggestion; } else if (binarySuggestion == null) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index f5fa5d0d7..342dcfc5e 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -210,7 +210,7 @@ public class ExpandableDictionary extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { if (composer.size() > 1) { if (composer.size() >= Constants.DICTIONARY_MAX_WORD_LENGTH) { return null; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 85001c30c..6be9ded5c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -31,6 +31,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.net.ConnectivityManager; @@ -50,6 +51,7 @@ import android.util.Printer; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; @@ -71,6 +73,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.personalization.PersonalizationDictionary; @@ -104,6 +107,8 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Locale; import java.util.TreeSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Input method implementation for Qwerty'ish keyboard. @@ -126,6 +131,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2; + // TODO: Set this value appropriately. + private static final int GET_SUGGESTED_WORDS_TIMEOUT = 200; + /** * The name of the scheme used by the Package Manager to warn of a new package installation, * replacement or removal. @@ -153,8 +161,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final Settings mSettings; - private View mInputView; - private int mInputViewMinHeight; + private View mExtractArea; + private View mKeyPreviewBackingView; private SuggestionStripView mSuggestionStripView; // Never null private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; @@ -218,6 +226,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final boolean mIsHardwareAcceleratedDrawingEnabled; public final UIHandler mHandler = new UIHandler(this); + private InputUpdater mInputUpdater; public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> { private static final int MSG_UPDATE_SHIFT_STATE = 0; @@ -226,8 +235,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3; private static final int MSG_RESUME_SUGGESTIONS = 4; private static final int MSG_REOPEN_DICTIONARIES = 5; + private static final int MSG_ON_END_BATCH_INPUT = 6; + private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; + private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -260,8 +272,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen switcher.updateShiftState(); break; case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: - latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords)msg.obj, - msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); + if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) { + latinIme.showSuggestionStrip((SuggestedWords) msg.obj); + } else { + latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj, + msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); + } break; case MSG_RESUME_SUGGESTIONS: latinIme.restartSuggestionsOnWordTouchedByCursor(); @@ -273,6 +289,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // get any suggestions. Wait one frame. postUpdateSuggestionStrip(); break; + case MSG_ON_END_BATCH_INPUT: + latinIme.onEndBatchInputAsyncInternal((SuggestedWords) msg.obj); + break; } } @@ -314,11 +333,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean dismissGestureFloatingPreviewText) { removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); final int arg1 = dismissGestureFloatingPreviewText - ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : 0; + ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT + : ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT; obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords) .sendToTarget(); } + public void showSuggestionStrip(final SuggestedWords suggestedWords) { + removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); + obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, + ARG1_NOT_GESTURE_INPUT, 0, suggestedWords).sendToTarget(); + } + + public void onEndBatchInput(final SuggestedWords suggestedWords) { + obtainMessage(MSG_ON_END_BATCH_INPUT, suggestedWords).sendToTarget(); + } + public void startDoubleSpacePeriodTimer() { mDoubleSpacePeriodTimerStart = SystemClock.uptimeMillis(); } @@ -511,6 +541,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final IntentFilter newDictFilter = new IntentFilter(); newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); registerReceiver(mDictionaryPackInstallReceiver, newDictFilter); + + mInputUpdater = new InputUpdater(this); } // Has to be package-visible for unit tests @@ -647,6 +679,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen PersonalizationDictionarySessionRegister.onDestroy(this); LatinImeLogger.commit(); LatinImeLogger.onDestroy(); + if (mInputUpdater != null) { + mInputUpdater.onDestroy(); + mInputUpdater = null; + } super.onDestroy(); } @@ -673,25 +709,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled); } - private void setInputViewMinHeight(final int minHeight) { - if (mInputView != null && mInputViewMinHeight != minHeight) { - mInputView.setMinimumHeight(minHeight); - mInputViewMinHeight = minHeight; - } - } - @Override - public void setInputView(final View inputView) { - super.setInputView(inputView); - mInputView = inputView; - setInputViewMinHeight(0); - mSuggestionStripView = (SuggestionStripView)inputView.findViewById( - R.id.suggestion_strip_view); - if (mSuggestionStripView != null) { - mSuggestionStripView.setListener(this, inputView); - } + public void setInputView(final View view) { + super.setInputView(view); + mExtractArea = getWindow().getWindow().getDecorView() + .findViewById(android.R.id.extractArea); + mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); + mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); + if (mSuggestionStripView != null) + mSuggestionStripView.setListener(this, view); if (LatinImeLogger.sVISUALDEBUG) { - inputView.setBackgroundColor(0x10FF0000); + mKeyPreviewBackingView.setBackgroundColor(0x10FF0000); } } @@ -1169,11 +1197,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggestionStripView.setVisibility( shouldShowSuggestions ? View.VISIBLE : View.INVISIBLE); } - if (shouldShowSuggestions && mainKeyboardView != null) { - final int remainingHeight = getWindow().getWindow().getDecorView().getHeight() - - mainKeyboardView.getHeight() - mSuggestionStripView.getHeight(); - mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight); - } } } @@ -1181,37 +1204,66 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setSuggestionStripShownInternal(shown, /* needsInputViewShown */true); } + private int getAdjustedBackingViewHeight() { + final int currentHeight = mKeyPreviewBackingView.getHeight(); + if (currentHeight > 0) { + return currentHeight; + } + + final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView(); + if (visibleKeyboardView == null) { + return 0; + } + // TODO: !!!!!!!!!!!!!!!!!!!! Handle different backing view heights between the main !!! + // keyboard and the emoji keyboard. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + final int keyboardHeight = visibleKeyboardView.getHeight(); + final int suggestionsHeight = mSuggestionStripView.getHeight(); + final int displayHeight = getResources().getDisplayMetrics().heightPixels; + final Rect rect = new Rect(); + mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect); + final int notificationBarHeight = rect.top; + final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight + - keyboardHeight; + + final LayoutParams params = mKeyPreviewBackingView.getLayoutParams(); + params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight); + mKeyPreviewBackingView.setLayoutParams(params); + return params.height; + } + @Override public void onComputeInsets(final InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); - final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (mainKeyboardView == null || mSuggestionStripView == null) { + final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView(); + if (visibleKeyboardView == null || mSuggestionStripView == null) { return; } - // This method is never called when in fullscreen mode. - // The contentTop is the top coordinate of the keyboard. The application behind will be - // resized/panned above this coordibnate to be able to show an input field. - final int contentTop = mInputView.getHeight() - mainKeyboardView.getHeight(); - final int suggestionsHeight = (mSuggestionStripView.getVisibility() == View.VISIBLE) - ? mSuggestionStripView.getHeight() : 0; - // The visibleTop is the top coordinates of the visible part of this IME. The application - // behind will never be resized, but may be panned or scrolled. - final int visibleTop = mainKeyboardView.isShowingMoreKeysPanel() ? 0 - : contentTop - suggestionsHeight; - outInsets.contentTopInsets = contentTop; - outInsets.visibleTopInsets = visibleTop; + final int adjustedBackingHeight = getAdjustedBackingViewHeight(); + final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE); + final int backingHeight = backingGone ? 0 : adjustedBackingHeight; + // In fullscreen mode, the height of the extract area managed by InputMethodService should + // be considered. + // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}. + final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0; + final int suggestionsHeight = (mSuggestionStripView.getVisibility() == View.GONE) ? 0 + : mSuggestionStripView.getHeight(); + final int extraHeight = extractHeight + backingHeight + suggestionsHeight; + int visibleTopY = extraHeight; // Need to set touchable region only if input view is being shown - if (mainKeyboardView.isShown()) { - final int touchLeft = 0; - final int touchTop = visibleTop; - final int touchRight = touchLeft + mainKeyboardView.getWidth(); - final int touchBottom = contentTop + mainKeyboardView.getHeight() + if (visibleKeyboardView.isShown()) { + if (mSuggestionStripView.getVisibility() == View.VISIBLE) { + visibleTopY -= suggestionsHeight; + } + final int touchY = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY; + final int touchWidth = visibleKeyboardView.getWidth(); + final int touchHeight = visibleKeyboardView.getHeight() + extraHeight // Extend touchable region below the keyboard. + EXTENDED_TOUCHABLE_REGION_HEIGHT; - // The touch event on touchableRegion will be delivered to this IME. - outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom); outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION; + outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight); } + outInsets.contentTopInsets = visibleTopY; + outInsets.visibleTopInsets = visibleTopY; } @Override @@ -1234,11 +1286,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void updateFullscreenMode() { super.updateFullscreenMode(); - if (!isFullscreenMode()) { - // Expand the input view to cover entire display to be able to show key previews and - // more suggestions view that may be displayed above the keyboard. - setInputViewMinHeight(getResources().getDisplayMetrics().heightPixels); - } + + if (mKeyPreviewBackingView == null) return; + // In fullscreen mode, no need to have extra space to show the key preview. + // If not, we should have extra space above the keyboard to show the key preview. + mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE); } // This will reset the whole input state to the starting state. It will clear @@ -1532,7 +1584,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen handleLanguageSwitchKey(); break; case Constants.CODE_EMOJI: - // TODO: Implement emoji keyboard switch. + // Note: Switching emoji keyboard is being handled in + // {@link KeyboardState#onCodeInput(int,int)}. break; case Constants.CODE_ENTER: final EditorInfo editorInfo = getCurrentInputEditorInfo(); @@ -1648,7 +1701,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onStartBatchInput() { - BatchInputUpdater.getInstance().onStartBatchInput(this); + mInputUpdater.onStartBatchInput(); mHandler.cancelUpdateSuggestionStrip(); mConnection.beginBatchEdit(); final SettingsValues settingsValues = mSettings.getCurrent(); @@ -1688,46 +1741,41 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); } - private static final class BatchInputUpdater implements Handler.Callback { + private static final class InputUpdater implements Handler.Callback { private final Handler mHandler; - private LatinIME mLatinIme; + private final LatinIME mLatinIme; private final Object mLock = new Object(); private boolean mInBatchInput; // synchronized using {@link #mLock}. - private BatchInputUpdater() { + private InputUpdater(final LatinIME latinIme) { final HandlerThread handlerThread = new HandlerThread( - BatchInputUpdater.class.getSimpleName()); + InputUpdater.class.getSimpleName()); handlerThread.start(); mHandler = new Handler(handlerThread.getLooper(), this); - } - - // Initialization-on-demand holder - private static final class OnDemandInitializationHolder { - public static final BatchInputUpdater sInstance = new BatchInputUpdater(); - } - - public static BatchInputUpdater getInstance() { - return OnDemandInitializationHolder.sInstance; + mLatinIme = latinIme; } private static final int MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 1; + private static final int MSG_GET_SUGGESTED_WORDS = 2; @Override public boolean handleMessage(final Message msg) { switch (msg.what) { - case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: - updateBatchInput((InputPointers)msg.obj); - break; + case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: + updateBatchInput((InputPointers)msg.obj); + break; + case MSG_GET_SUGGESTED_WORDS: + mLatinIme.getSuggestedWords(msg.arg1, (OnGetSuggestedWordsCallback) msg.obj); + break; } return true; } // Run in the UI thread. - public void onStartBatchInput(final LatinIME latinIme) { + public void onStartBatchInput() { synchronized (mLock) { mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); mInBatchInput = true; - mLatinIme = latinIme; mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip( SuggestedWords.EMPTY, false /* dismissGestureFloatingPreviewText */); } @@ -1740,9 +1788,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Batch input has ended or canceled while the message was being delivered. return; } - final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers); - mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip( - suggestedWords, false /* dismissGestureFloatingPreviewText */); + + getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(final SuggestedWords suggestedWords) { + mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip( + suggestedWords, false /* dismissGestureFloatingPreviewText */); + } + }); } } @@ -1765,35 +1818,57 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Run in the UI thread. - public SuggestedWords onEndBatchInput(final InputPointers batchPointers) { - synchronized (mLock) { - mInBatchInput = false; - final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers); - mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip( - suggestedWords, true /* dismissGestureFloatingPreviewText */); - return suggestedWords; + public void onEndBatchInput(final InputPointers batchPointers) { + synchronized(mLock) { + getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(final SuggestedWords suggestedWords) { + mInBatchInput = false; + mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWords, + true /* dismissGestureFloatingPreviewText */); + mLatinIme.mHandler.onEndBatchInput(suggestedWords); + } + }); } } // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to // be synchronized. - private SuggestedWords getSuggestedWordsGestureLocked(final InputPointers batchPointers) { + private void getSuggestedWordsGestureLocked(final InputPointers batchPointers, + final OnGetSuggestedWordsCallback callback) { mLatinIme.mWordComposer.setBatchInputPointers(batchPointers); - final SuggestedWords suggestedWords = - mLatinIme.getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_GESTURE); - final int suggestionCount = suggestedWords.size(); - if (suggestionCount <= 1) { - final String mostProbableSuggestion = (suggestionCount == 0) ? null - : suggestedWords.getWord(0); - return mLatinIme.getOlderSuggestions(mostProbableSuggestion); - } - return suggestedWords; + mLatinIme.getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_GESTURE, + new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(SuggestedWords suggestedWords) { + final int suggestionCount = suggestedWords.size(); + if (suggestionCount <= 1) { + final String mostProbableSuggestion = (suggestionCount == 0) ? null + : suggestedWords.getWord(0); + callback.onGetSuggestedWords( + mLatinIme.getOlderSuggestions(mostProbableSuggestion)); + } + callback.onGetSuggestedWords(suggestedWords); + } + }); + } + + public void getSuggestedWords(final int sessionId, + final OnGetSuggestedWordsCallback callback) { + mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, 0, callback).sendToTarget(); + } + + private void onDestroy() { + mHandler.removeMessages(MSG_GET_SUGGESTED_WORDS); + mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); + mHandler.getLooper().quit(); } } + // This method must run in UI Thread. private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { - showSuggestionStrip(suggestedWords, null); + showSuggestionStrip(suggestedWords); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); mainKeyboardView.showGestureFloatingPreviewText(suggestedWords); if (dismissGestureFloatingPreviewText) { @@ -1809,13 +1884,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: implement auto-commit } } - BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers); + mInputUpdater.onUpdateBatchInput(batchPointers); } - @Override - public void onEndBatchInput(final InputPointers batchPointers) { - final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput( - batchPointers); + // This method must run in UI Thread. + public void onEndBatchInputAsyncInternal(final SuggestedWords suggestedWords) { final String batchInputText = suggestedWords.isEmpty() ? null : suggestedWords.getWord(0); if (TextUtils.isEmpty(batchInputText)) { @@ -1837,6 +1910,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mKeyboardSwitcher.updateShiftState(); } + @Override + public void onEndBatchInput(final InputPointers batchPointers) { + mInputUpdater.onEndBatchInput(batchPointers); + } + private String specificTldProcessingOnTextInput(final String text) { if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD || !Character.isLetter(text.charAt(1))) { @@ -1872,7 +1950,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onCancelBatchInput() { - BatchInputUpdater.getInstance().onCancelBatchInput(); + mInputUpdater.onCancelBatchInput(); } private void handleBackspace(final int spaceState) { @@ -2307,22 +2385,47 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - final SuggestedWords suggestedWords = - getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_TYPING); - final String typedWord = mWordComposer.getTypedWord(); - showSuggestionStrip(suggestedWords, typedWord); + final CountDownLatch latch = new CountDownLatch(1); + final SuggestedWords[] suggestedWordsArray = new SuggestedWords[1]; + getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING, + new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(final SuggestedWords suggestedWords) { + suggestedWordsArray[0] = suggestedWords; + latch.countDown(); + } + } + ); + + // TODO: Quit blocking the main thread. + try { + // Wait for the result of getSuggestedWords + // We set the time out to avoid ANR. + latch.await(GET_SUGGESTED_WORDS_TIMEOUT, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // TODO: Cancel all pending "getSuggestedWords" tasks when it failed. We may want to add + // "onGetSuggestionFailed" to "OnGetSuggestedWordsCallback". + Log.e(TAG, "InterruptedException while waiting for getSuggestedWords.", e); + return; + } + if (suggestedWordsArray[0] != null) { + showSuggestionStrip(suggestedWordsArray[0]); + } } - private SuggestedWords getSuggestedWords(final int sessionId) { + private void getSuggestedWords(final int sessionId, + final OnGetSuggestedWordsCallback callback) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final Suggest suggest = mSuggest; if (keyboard == null || suggest == null) { - return SuggestedWords.EMPTY; + callback.onGetSuggestedWords(SuggestedWords.EMPTY); + return; } // Get the word on which we should search the bigrams. If we are composing a word, it's // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we // should just skip whitespace if any, so 1. final SettingsValues currentSettings = mSettings.getCurrent(); + final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues; final String prevWord; if (currentSettings.mCurrentLanguageHasSpaces) { // If we are typing in a language with spaces we can just look up the previous @@ -2333,14 +2436,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen prevWord = LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null : mLastComposedWord.mCommittedWord; } - return suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), - currentSettings.mBlockPotentiallyOffensive, - currentSettings.mCorrectionEnabled, sessionId); + suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), + currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled, + additionalFeaturesOptions, sessionId, callback); } - private SuggestedWords getSuggestedWordsOrOlderSuggestions(final int sessionId) { - return maybeRetrieveOlderSuggestions(mWordComposer.getTypedWord(), - getSuggestedWords(sessionId)); + private void getSuggestedWordsOrOlderSuggestionsAsync(final int sessionId, + final OnGetSuggestedWordsCallback callback) { + mInputUpdater.getSuggestedWords(sessionId, new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(SuggestedWords suggestedWords) { + callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions( + mWordComposer.getTypedWord(), suggestedWords)); + } + }); } private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord, @@ -2380,16 +2489,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen false /* isPrediction */); } - private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) { + private void showSuggestionStrip(final SuggestedWords suggestedWords) { if (suggestedWords.isEmpty()) { clearSuggestionStrip(); return; } final String autoCorrection; if (suggestedWords.mWillAutoCorrect) { - autoCorrection = suggestedWords.getWord(1); + autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); } else { - autoCorrection = typedWord; + autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD); } mWordComposer.setAutoCorrection(autoCorrection); final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); @@ -2638,41 +2747,52 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.setComposingRegion( mLastSelectionStart - numberOfCharsInWordBeforeCursor, mLastSelectionEnd + range.getNumberOfCharsInWordAfterCursor()); - final SuggestedWords suggestedWords; if (suggestions.isEmpty()) { // We come here if there weren't any suggestion spans on this word. We will try to // compute suggestions for it instead. - final SuggestedWords suggestedWordsIncludingTypedWord = - getSuggestedWords(Suggest.SESSION_TYPING); - if (suggestedWordsIncludingTypedWord.size() > 1) { - // We were able to compute new suggestions for this word. - // Remove the typed word, since we don't want to display it in this case. - // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to false. - suggestedWords = - suggestedWordsIncludingTypedWord.getSuggestedWordsExcludingTypedWord(); - } else { - // No saved suggestions, and we were unable to compute any good one either. - // Rather than displaying an empty suggestion strip, we'll display the original - // word alone in the middle. - // Since there is only one word, willAutoCorrect is false. - suggestedWords = suggestedWordsIncludingTypedWord; - } + mInputUpdater.getSuggestedWords(Suggest.SESSION_TYPING, + new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords( + final SuggestedWords suggestedWordsIncludingTypedWord) { + final SuggestedWords suggestedWords; + if (suggestedWordsIncludingTypedWord.size() > 1) { + // We were able to compute new suggestions for this word. + // Remove the typed word, since we don't want to display it in this case. + // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to + // false. + suggestedWords = suggestedWordsIncludingTypedWord + .getSuggestedWordsExcludingTypedWord(); + } else { + // No saved suggestions, and we were unable to compute any good one either. + // Rather than displaying an empty suggestion strip, we'll display the + // original word alone in the middle. + // Since there is only one word, willAutoCorrect is false. + suggestedWords = suggestedWordsIncludingTypedWord; + } + unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords); + }}); } else { // We found suggestion spans in the word. We'll create the SuggestedWords out of // them, and make willAutoCorrect false. - suggestedWords = new SuggestedWords(suggestions, + final SuggestedWords suggestedWords = new SuggestedWords(suggestions, true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, false /* isPrediction */); + unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords); } + } + public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip( + final SuggestedWords suggestedWords) { // Note that it's very important here that suggestedWords.mWillAutoCorrect is false. - // We never want to auto-correct on a resumed suggestion. Please refer to the three - // places above where suggestedWords is affected. We also need to reset - // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it. - // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition) + // We never want to auto-correct on a resumed suggestion. Please refer to the three places + // above in restartSuggestionsOnWordTouchedByCursor() where suggestedWords is affected. + // We also need to unset mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching + // the text to adapt it. + // TODO: remove mIsAutoCorrectionIndicatorOn (see comment on definition) mIsAutoCorrectionIndicatorOn = false; - showSuggestionStrip(suggestedWords, typedWord); + mHandler.showSuggestionStrip(suggestedWords); } /** diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index be03d4ae5..0889f22ca 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -53,12 +53,22 @@ public final class SubtypeSwitcher { private InputMethodInfo mShortcutInputMethodInfo; private InputMethodSubtype mShortcutSubtype; private InputMethodSubtype mNoLanguageSubtype; + private InputMethodSubtype mEmojiSubtype; private boolean mIsNetworkConnected; // Dummy no language QWERTY subtype. See {@link R.xml.method}. private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = new InputMethodSubtype( - R.string.subtype_no_language_qwerty, R.drawable.ic_subtype_keyboard, "zz", "keyboard", - "KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable", + R.string.subtype_no_language_qwerty, R.drawable.ic_subtype_keyboard, + SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", + "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY + + ",AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable", + 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 InputMethodSubtype DUMMY_EMOJI_SUBTYPE = new InputMethodSubtype( + R.string.subtype_emoji, R.drawable.ic_subtype_keyboard, + SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", + "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI, false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */); static final class NeedsToDisplayLanguage { @@ -271,4 +281,17 @@ public final class SubtypeSwitcher { + DUMMY_NO_LANGUAGE_SUBTYPE); return DUMMY_NO_LANGUAGE_SUBTYPE; } + + public InputMethodSubtype getEmojiSubtype() { + if (mEmojiSubtype == null) { + mEmojiSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI); + } + if (mEmojiSubtype != null) { + return mEmojiSubtype; + } + 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 c8a151a6c..18ba15872 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -211,24 +211,31 @@ public final class Suggest { mAutoCorrectionThreshold = threshold; } - public SuggestedWords getSuggestedWords(final WordComposer wordComposer, + public interface OnGetSuggestedWordsCallback { + public void onGetSuggestedWords(final SuggestedWords suggestedWords); + } + + public void getSuggestedWords(final WordComposer wordComposer, final String prevWordForBigram, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final boolean isCorrectionEnabled, - final int sessionId) { + final int[] additionalFeaturesOptions, final int sessionId, + final OnGetSuggestedWordsCallback callback) { LatinImeLogger.onStartSuggestion(prevWordForBigram); if (wordComposer.isBatchMode()) { - return getSuggestedWordsForBatchInput( - wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId); + getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId, callback); } else { - return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, - blockOffensiveWords, isCorrectionEnabled); + getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, + blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions, callback); } } - // Retrieves suggestions for the typing input. - private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer, + // Retrieves suggestions for the typing input + // and calls the callback function with the suggestions. + private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, final String prevWordForBigram, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final boolean isCorrectionEnabled) { + final boolean blockOffensiveWords, final boolean isCorrectionEnabled, + final int[] additionalFeaturesOptions, final OnGetSuggestedWordsCallback callback) { final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS); @@ -251,8 +258,9 @@ public final class Suggest { for (final String key : mDictionaries.keySet()) { final Dictionary dictionary = mDictionaries.get(key); - suggestionsSet.addAll(dictionary.getSuggestions( - wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords)); + suggestionsSet.addAll(dictionary.getSuggestions(wordComposerForLookup, + prevWordForBigram, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions)); } final String whitelistedWord; @@ -329,7 +337,7 @@ public final class Suggest { suggestionsList = suggestionsContainer; } - return new SuggestedWords(suggestionsList, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, // TODO: this first argument is lying. If this is a whitelisted word which is an // actual word, it says typedWordValid = false, which looks wrong. We should either // rename the attribute or change the value. @@ -337,13 +345,15 @@ public final class Suggest { hasAutoCorrection, /* willAutoCorrect */ false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, - !wordComposer.isComposingWord() /* isPrediction */); + !wordComposer.isComposingWord() /* isPrediction */)); } - // Retrieves suggestions for the batch input. - private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer, + // Retrieves suggestions for the batch input + // and calls the callback function with the suggestions. + private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, final String prevWordForBigram, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int sessionId) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, + final int sessionId, final OnGetSuggestedWordsCallback callback) { final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, MAX_SUGGESTIONS); @@ -357,7 +367,8 @@ public final class Suggest { } final Dictionary dictionary = mDictionaries.get(key); suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer, - prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId)); + prevWordForBigram, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions, sessionId)); } for (SuggestedWordInfo wordInfo : suggestionsSet) { @@ -396,12 +407,12 @@ public final class Suggest { // In the batch input mode, the most relevant suggested word should act as a "typed word" // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). - return new SuggestedWords(suggestionsContainer, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, - false /* isPrediction */); + false /* isPrediction */)); } private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java index 92f96c027..67ef538ac 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java @@ -34,9 +34,10 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB @Override public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, final String prevWordForBigrams, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { syncReloadDictionaryIfRequired(); - return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords); + return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions); } @Override diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java index 33fe89611..bea522320 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java @@ -37,9 +37,10 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic @Override public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, final String prevWordForBigrams, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { syncReloadDictionaryIfRequired(); - return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords); + return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions); } @Override diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java index 7f4f5e74a..d44660623 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java @@ -147,9 +147,9 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - boolean blockOffensiveWords) { + boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords); + blockOffensiveWords, additionalFeaturesOptions); } @Override diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java index be3a9f2f1..a08145b33 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java @@ -63,7 +63,7 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableBinaryDi /* package */ DynamicPredictionDictionaryBase(final Context context, final String locale, final SharedPreferences sp, final String dictionaryType, final String fileName) { - super(context, locale, dictionaryType, true); + super(context, fileName, dictionaryType, true); mLocale = locale; mFileName = fileName; mPrefs = sp; diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java index 139f5e290..6543003e8 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java +++ b/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java @@ -40,8 +40,4 @@ public class AdditionalFeaturesSettingUtils { final SharedPreferences prefs, final int[] additionalFeaturesPreferences) { // do nothing. } - - public static int[] getAdditionalNativeSuggestOptions() { - return Settings.getInstance().getCurrent().mAdditionalFeaturesSettingValues; - } } diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java index 878c505bd..cd726c969 100644 --- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java +++ b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java @@ -34,6 +34,9 @@ public class NativeSuggestOptions { } public void setAdditionalFeaturesOptions(final int[] additionalOptions) { + if (additionalOptions == null) { + return; + } for (int i = 0; i < additionalOptions.length; i++) { setIntegerOption(OPTIONS_SIZE + i, additionalOptions[i]); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 6719e98da..69f9a467f 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -301,12 +301,14 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { // TODO: make a spell checker option to block offensive words or not final ArrayList<SuggestedWordInfo> suggestions = dictInfo.mDictionary.getSuggestions(composer, prevWord, - dictInfo.getProximityInfo(), - true /* blockOffensiveWords */); - for (final SuggestedWordInfo suggestion : suggestions) { - final String suggestionStr = suggestion.mWord; - suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0, - suggestionStr.length(), suggestion.mScore); + dictInfo.getProximityInfo(), true /* blockOffensiveWords */, + null /* additionalFeaturesOptions */); + if (suggestions != null) { + for (final SuggestedWordInfo suggestion : suggestions) { + final String suggestionStr = suggestion.mWord; + suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0, + suggestionStr.length(), suggestion.mScore); + } } isInDict = isInDictForAnyCapitalization(dictInfo.mDictionary, text, capitalizeType); } finally { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index ac8f68781..a0aed2829 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -52,7 +52,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { return noSuggestions; } @Override diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index aca249240..8d2689a7d 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -165,9 +165,20 @@ final class SuggestionStripLayoutHelper { return mMaxMoreSuggestionsRow; } - public void setMoreSuggestionsHeight(final int remainingHeight) { + private int getMoreSuggestionsHeight() { + return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; + } + + public int setMoreSuggestionsHeight(final int remainingHeight) { + final int currentHeight = getMoreSuggestionsHeight(); + if (currentHeight <= remainingHeight) { + return currentHeight; + } + mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) / mMoreSuggestionsRowHeight; + final int newHeight = getMoreSuggestionsHeight(); + return newHeight; } private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index badc942b9..75f17c559 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -135,8 +135,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } } - public void setMoreSuggestionsHeight(final int remainingHeight) { - mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); + public int setMoreSuggestionsHeight(final int remainingHeight) { + return mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); } public boolean isShowingAddToDictionaryHint() { diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 16728092d..102a41b4e 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -40,6 +40,7 @@ public final class SubtypeLocaleUtils { // Special language code to represent "no language". public static final String NO_LANGUAGE = "zz"; public static final String QWERTY = "qwerty"; + public static final String EMOJI = "emoji"; public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic; private static boolean sInitialized = false; |