diff options
Diffstat (limited to 'java/src')
6 files changed, 430 insertions, 113 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java index 9996a6d6a..d28b5088c 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java @@ -19,14 +19,18 @@ package com.android.inputmethod.keyboard; import static com.android.inputmethod.latin.Constants.NOT_A_COORDINATE; import android.content.Context; +import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Rect; import android.os.Build; +import android.preference.PreferenceManager; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; +import android.util.Pair; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -43,11 +47,15 @@ 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.settings.Settings; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; /** * View class to implement Emoji keyboards. @@ -75,16 +83,25 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; + private static final int CATEGORY_ID_UNSPECIFIED = -1; + public static final int CATEGORY_ID_RECENTS = 0; + public static final int CATEGORY_ID_PEOPLE = 1; + public static final int CATEGORY_ID_OBJECTS = 2; + public static final int CATEGORY_ID_NATURE = 3; + public static final int CATEGORY_ID_PLACES = 4; + public static final int CATEGORY_ID_SYMBOLS = 5; + public static final int CATEGORY_ID_EMOTICONS = 6; + + private static class CategoryProperties { + public int mCategoryId; + public int mPageCount; + public CategoryProperties(final int categoryId, final int pageCount) { + mCategoryId = categoryId; + mPageCount = pageCount; + } + } + private static class EmojiCategory { - 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 String[] sCategoryName = { "recents", "people", @@ -110,87 +127,231 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange KeyboardId.ELEMENT_EMOJI_CATEGORY3, KeyboardId.ELEMENT_EMOJI_CATEGORY4, KeyboardId.ELEMENT_EMOJI_CATEGORY5, - KeyboardId.ELEMENT_EMOJI_CATEGORY6, }; + KeyboardId.ELEMENT_EMOJI_CATEGORY6 }; + private final SharedPreferences mPrefs; + private final int mMaxPageKeyCount; + private final KeyboardLayoutSet mLayoutSet; private final HashMap<String, Integer> mCategoryNameToIdMap = CollectionUtils.newHashMap(); - private final ArrayList<Integer> mShownCategories = new ArrayList<Integer>(); - - public EmojiCategory() { + private final ArrayList<CategoryProperties> mShownCategories = + CollectionUtils.newArrayList(); + private final ConcurrentHashMap<Long, DynamicGridKeyboard> + mCategoryKeyboardMap = new ConcurrentHashMap<Long, DynamicGridKeyboard>(); + + private int mCurrentCategoryId = CATEGORY_ID_UNSPECIFIED; + private int mCurrentCategoryPageId = 0; + + public EmojiCategory(final SharedPreferences prefs, final Resources res, + final KeyboardLayoutSet layoutSet) { + mPrefs = prefs; + mMaxPageKeyCount = res.getInteger(R.integer.emoji_keyboard_max_key_count); + mLayoutSet = layoutSet; for (int i = 0; i < sCategoryName.length; ++i) { mCategoryNameToIdMap.put(sCategoryName[i], i); } - mShownCategories.add(CATEGORY_RECENTS); + addShownCategoryId(CATEGORY_ID_RECENTS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - mShownCategories.add(CATEGORY_PEOPLE); - mShownCategories.add(CATEGORY_OBJECTS); - mShownCategories.add(CATEGORY_NATURE); - mShownCategories.add(CATEGORY_PLACES); - // TODO: Restore last saved category - mCurrentCategory = CATEGORY_PEOPLE; + addShownCategoryId(CATEGORY_ID_PEOPLE); + addShownCategoryId(CATEGORY_ID_OBJECTS); + addShownCategoryId(CATEGORY_ID_NATURE); + addShownCategoryId(CATEGORY_ID_PLACES); + mCurrentCategoryId = CATEGORY_ID_PEOPLE; } else { - // TODO: Restore last saved category - mCurrentCategory = CATEGORY_SYMBOLS; + mCurrentCategoryId = CATEGORY_ID_SYMBOLS; } - mShownCategories.add(CATEGORY_SYMBOLS); - mShownCategories.add(CATEGORY_EMOTICONS); + addShownCategoryId(CATEGORY_ID_SYMBOLS); + addShownCategoryId(CATEGORY_ID_EMOTICONS); + getKeyboard(CATEGORY_ID_RECENTS, 0 /* cagetoryPageId */) + .loadRecentKeys(mCategoryKeyboardMap.values()); + } + + private void addShownCategoryId(int categoryId) { + // Load a keyboard of categoryId + getKeyboard(categoryId, 0 /* cagetoryPageId */); + final CategoryProperties properties = + new CategoryProperties(categoryId, getCategoryPageCount(categoryId)); + mShownCategories.add(properties); } - public String getCategoryName(int category) { - return sCategoryName[category]; + public String getCategoryName(int categoryId, int categoryPageId) { + return sCategoryName[categoryId] + "-" + categoryPageId; } public int getCategoryId(String name) { - return mCategoryNameToIdMap.get(name); + final String[] strings = name.split("-"); + return mCategoryNameToIdMap.get(strings[0]); } - public int getCategoryIcon(int category) { - return sCategoryIcon[category]; + public int getCategoryIcon(int categoryId) { + return sCategoryIcon[categoryId]; } - public String getCategoryLabel(int category) { - return sCategoryLabel[category]; + public String getCategoryLabel(int categoryId) { + return sCategoryLabel[categoryId]; } - public ArrayList<Integer> getShownCategories() { + public ArrayList<CategoryProperties> getShownCategories() { return mShownCategories; } - public int getCurrentCategory() { - // TODO: Record current category. - return mCurrentCategory; + public int getCurrentCategoryId() { + return mCurrentCategoryId; + } + + public void setCurrentCategoryId(int categoryId) { + mCurrentCategoryId = categoryId; } - public void setCurrentCategory(int category) { - mCurrentCategory = category; + public void setCurrentCategoryPageId(int id) { + mCurrentCategoryPageId = id; + } + + public void saveLastTypedCategoryPage() { + Settings.writeEmojiCategoryLastTypedId( + mPrefs, mCurrentCategoryId, mCurrentCategoryPageId); } public boolean isInRecentTab() { - return mCurrentCategory == CATEGORY_RECENTS; + return mCurrentCategoryId == CATEGORY_ID_RECENTS; } - public int getTabIdFromCategory(int category) { + public int getTabIdFromCategoryId(int categoryId) { for (int i = 0; i < mShownCategories.size(); ++i) { - if (mShownCategories.get(i) == category) { + if (mShownCategories.get(i).mCategoryId == categoryId) { return i; } } - Log.w(TAG, "category not found: " + category); + Log.w(TAG, "categoryId not found: " + categoryId); + return 0; + } + + // Returns the view pager's page position for the categoryId + public int getPageIdFromCategoryId(int categoryId) { + final int lastSavedCategoryPageId = + Settings.readEmojiCategoryLastTypedId(mPrefs, categoryId); + int sum = 0; + for (int i = 0; i < mShownCategories.size(); ++i) { + final CategoryProperties props = mShownCategories.get(i); + if (props.mCategoryId == categoryId) { + return sum + lastSavedCategoryPageId; + } + sum += props.mPageCount; + } + Log.w(TAG, "categoryId not found: " + categoryId); return 0; } public int getRecentTabId() { - return getTabIdFromCategory(CATEGORY_RECENTS); + return getTabIdFromCategoryId(CATEGORY_ID_RECENTS); } - public int getCategoryFromTabId(int tabId) { - return mShownCategories.get(tabId); + private int getCategoryPageCount(int categoryId) { + final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); + return (keyboard.getKeys().length - 1) / mMaxPageKeyCount + 1; } - public int getElementIdFromTabId(int tabId) { - return sCategoryElementId[getCategoryFromTabId(tabId)]; + // Returns a pair of the category id and the category page id from the view pager's page + // position. The category page id is numbered in each category. And the view page position + // is the position of the current shown page in the view pager which contains all pages of + // all categories. + public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(int position) { + int sum = 0; + for (CategoryProperties properties : mShownCategories) { + final int temp = sum; + sum += properties.mPageCount; + if (sum > position) { + return new Pair<Integer, Integer>(properties.mCategoryId, position - temp); + } + } + return null; + } + + // Returns a keyboard from the view pager's page position. + public DynamicGridKeyboard getKeyboardFromPagePosition(int position) { + final Pair<Integer, Integer> categoryAndId = + getCategoryIdAndPageIdFromPagePosition(position); + if (categoryAndId != null) { + return getKeyboard(categoryAndId.first, categoryAndId.second); + } + return null; + } + + public DynamicGridKeyboard getKeyboard(int categoryId, int id) { + synchronized(mCategoryKeyboardMap) { + final long key = (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id; + final DynamicGridKeyboard kbd; + if (!mCategoryKeyboardMap.containsKey(key)) { + if (categoryId != CATEGORY_ID_RECENTS) { + final Keyboard keyboard = + mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); + final Key[][] sortedKeys = sortKeys(keyboard.getKeys(), mMaxPageKeyCount); + for (int i = 0; i < sortedKeys.length; ++i) { + final DynamicGridKeyboard tempKbd = new DynamicGridKeyboard(mPrefs, + mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), + mMaxPageKeyCount, categoryId, i /* categoryPageId */); + for (Key emojiKey : sortedKeys[i]) { + if (emojiKey == null) { + break; + } + tempKbd.addKeyLast(emojiKey); + } + mCategoryKeyboardMap.put((((long) categoryId) + << Constants.MAX_INT_BIT_COUNT) | i, tempKbd); + } + kbd = mCategoryKeyboardMap.get(key); + } else { + kbd = new DynamicGridKeyboard(mPrefs, + mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), + mMaxPageKeyCount, categoryId, 0 /* categoryPageId */); + mCategoryKeyboardMap.put(key, kbd); + } + } else { + kbd = mCategoryKeyboardMap.get(key); + } + return kbd; + } + } + + public int getTotalPageCountOfAllCategories() { + int sum = 0; + for (CategoryProperties properties : mShownCategories) { + sum += properties.mPageCount; + } + return sum; + } + + private Key[][] sortKeys(Key[] inKeys, int maxPageCount) { + Key[] keys = Arrays.copyOf(inKeys, inKeys.length); + Arrays.sort(keys, 0, keys.length, new Comparator<Key>() { + @Override + public int compare(Key lhs, Key rhs) { + final Rect lHitBox = lhs.getHitBox(); + final Rect rHitBox = rhs.getHitBox(); + if (lHitBox.top < rHitBox.top) { + return -1; + } else if (lHitBox.top > rHitBox.top) { + return 1; + } + if (lHitBox.left < rHitBox.left) { + return -1; + } else if (lHitBox.left > rHitBox.left) { + return 1; + } + if (lhs.getCode() == rhs.getCode()) { + return 0; + } + return lhs.getCode() < rhs.getCode() ? -1 : 1; + } + }); + final int pageCount = (keys.length - 1) / maxPageCount + 1; + final Key[][] retval = new Key[pageCount][maxPageCount]; + for (int i = 0; i < keys.length; ++i) { + retval[i / maxPageCount][i % maxPageCount] = keys[i]; + } + return retval; } } - private final EmojiCategory mEmojiCategory = new EmojiCategory(); + private final EmojiCategory mEmojiCategory; public EmojiKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.emojiKeyboardViewStyle); @@ -213,13 +374,14 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( context, null /* editorInfo */); final Resources res = context.getResources(); + final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res); builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), - (int)ResourceUtils.getDefaultKeyboardHeight(res) - + res.getDimensionPixelSize(R.dimen.suggestions_strip_height)); + emojiLp.mEmojiKeyboardHeight); builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */); mLayoutSet = builder.build(); - // TODO: Save/restore recent keys from/to preferences. + mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context), + context.getResources(), builder.build()); } @Override @@ -235,20 +397,20 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange setMeasuredDimension(width, height); } - private void addTab(final TabHost host, final int category) { - final String tabId = mEmojiCategory.getCategoryName(category); + private void addTab(final TabHost host, final int categoryId) { + final String tabId = mEmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */); final TabHost.TabSpec tspec = host.newTabSpec(tabId); tspec.setContent(R.id.emoji_keyboard_dummy); - if (mEmojiCategory.getCategoryIcon(category) != 0) { + if (mEmojiCategory.getCategoryIcon(categoryId) != 0) { final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate( R.layout.emoji_keyboard_tab_icon, null); - iconView.setImageResource(mEmojiCategory.getCategoryIcon(category)); + iconView.setImageResource(mEmojiCategory.getCategoryIcon(categoryId)); tspec.setIndicator(iconView); } - if (mEmojiCategory.getCategoryLabel(category) != null) { + if (mEmojiCategory.getCategoryLabel(categoryId) != null) { final TextView textView = (TextView)LayoutInflater.from(getContext()).inflate( R.layout.emoji_keyboard_tab_label, null); - textView.setText(mEmojiCategory.getCategoryLabel(category)); + textView.setText(mEmojiCategory.getCategoryLabel(categoryId)); textView.setTextColor(mTabLabelColor); tspec.setIndicator(textView); } @@ -259,8 +421,8 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange protected void onFinishInflate() { mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost); mTabHost.setup(); - for (final int i : mEmojiCategory.getShownCategories()) { - addTab(mTabHost, i); + for (final CategoryProperties properties : mEmojiCategory.getShownCategories()) { + addTab(mTabHost, properties.mCategoryId); } mTabHost.setOnTabChangedListener(this); mTabHost.getTabWidget().setStripEnabled(true); @@ -275,7 +437,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res); emojiLp.setPagerProps(mEmojiPager); - setCurrentCategory(mEmojiCategory.getCurrentCategory(), true /* force */); + setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */); final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar); emojiLp.setActionBarProps(actionBar); @@ -302,14 +464,17 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public void onTabChanged(final String tabId) { - final int category = mEmojiCategory.getCategoryId(tabId); - setCurrentCategory(category, false /* force */); + final int categoryId = mEmojiCategory.getCategoryId(tabId); + setCurrentCategoryId(categoryId, false /* force */); } @Override public void onPageSelected(final int position) { - setCurrentCategory(mEmojiCategory.getCategoryFromTabId(position), false /* force */); + final Pair<Integer, Integer> newPos = + mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); + setCurrentCategoryId(newPos.first /* categoryId */, false /* force */); + mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */); } @Override @@ -341,6 +506,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public void onKeyClick(final Key key) { mEmojiKeyboardAdapter.addRecentKey(key); + mEmojiCategory.saveLastTypedCategoryPage(); final int code = key.getCode(); if (code == Constants.CODE_OUTPUT_TEXT) { mKeyboardActionListener.onTextInput(key.getOutputText()); @@ -357,25 +523,25 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mKeyboardActionListener = listener; } - private void setCurrentCategory(final int category, final boolean force) { - if (mEmojiCategory.getCurrentCategory() == category && !force) { + private void setCurrentCategoryId(final int categoryId, final boolean force) { + if (mEmojiCategory.getCurrentCategoryId() == categoryId && !force) { return; } - mEmojiCategory.setCurrentCategory(category); - final int newTabId = mEmojiCategory.getTabIdFromCategory(category); - if (force || mEmojiPager.getCurrentItem() != newTabId) { - mEmojiPager.setCurrentItem(newTabId, true /* smoothScroll */); + mEmojiCategory.setCurrentCategoryId(categoryId); + final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId); + final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId); + if (force || mEmojiCategory.getCategoryIdAndPageIdFromPagePosition( + mEmojiPager.getCurrentItem()).first != categoryId) { + mEmojiPager.setCurrentItem(newCategoryPageId, true /* smoothScroll */); } if (force || mTabHost.getCurrentTab() != newTabId) { mTabHost.setCurrentTab(newTabId); } - // TODO: Record current category } private static class EmojiKeyboardAdapter extends PagerAdapter { private final ScrollKeyboardView.OnKeyClickListener mListener; - private final KeyboardLayoutSet mLayoutSet; private final DynamicGridKeyboard mRecentsKeyboard; private final SparseArray<ScrollKeyboardView> mActiveKeyboardView = CollectionUtils.newSparseArray(); @@ -387,16 +553,14 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange final ScrollKeyboardView.OnKeyClickListener listener) { mEmojiCategory = emojiCategory; mListener = listener; - mLayoutSet = layoutSet; - mRecentsKeyboard = new DynamicGridKeyboard( - layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS)); + mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0); } public void addRecentKey(final Key key) { if (mEmojiCategory.isInRecentTab()) { return; } - mRecentsKeyboard.addRecentKey(key); + mRecentsKeyboard.addKeyFirst(key); final KeyboardView recentKeyboardView = mActiveKeyboardView.get(mEmojiCategory.getRecentTabId()); if (recentKeyboardView != null) { @@ -406,7 +570,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public int getCount() { - return mEmojiCategory.getShownCategories().size(); + return mEmojiCategory.getTotalPageCountOfAllCategories(); } @Override @@ -424,9 +588,8 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public Object instantiateItem(final ViewGroup container, final int position) { - final int elementId = mEmojiCategory.getElementIdFromTabId(position); - final Keyboard keyboard = (elementId == KeyboardId.ELEMENT_EMOJI_RECENTS) - ? mRecentsKeyboard : mLayoutSet.getKeyboard(elementId); + final Keyboard keyboard = + mEmojiCategory.getKeyboardFromPagePosition(position); final LayoutInflater inflater = LayoutInflater.from(container.getContext()); final View view = inflater.inflate( R.layout.emoji_keyboard_page, container, false /* attachToRoot */); diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java index 6486fc9db..5570d594d 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java @@ -27,6 +27,8 @@ import android.widget.LinearLayout; public class EmojiLayoutParams { private static final int DEFAULT_KEYBOARD_ROWS = 4; + public final int mEmojiPagerHeight; + private final int mEmojiPagerBottomMargin; public final int mEmojiKeyboardHeight; public final int mEmojiActionBarHeight; public final int mKeyVerticalGap; @@ -49,13 +51,15 @@ public class EmojiLayoutParams { + mKeyVerticalGap; mEmojiActionBarHeight = ((int) baseheight) / DEFAULT_KEYBOARD_ROWS - (mKeyVerticalGap - mBottomPadding) / 2; - mEmojiKeyboardHeight = defaultKeyboardHeight - mEmojiActionBarHeight; + mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight; + mEmojiPagerBottomMargin = mKeyVerticalGap / 2; + mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1; } public void setPagerProps(ViewPager vp) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams(); - lp.height = mEmojiKeyboardHeight - mKeyVerticalGap / 2; - lp.bottomMargin = mKeyVerticalGap / 2; + lp.height = mEmojiPagerHeight - mEmojiPagerBottomMargin; + lp.bottomMargin = mEmojiPagerBottomMargin; vp.setLayoutParams(lp); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java index a226891b4..f203eb7d7 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java @@ -16,33 +16,41 @@ package com.android.inputmethod.keyboard.internal; +import android.content.SharedPreferences; import android.text.TextUtils; +import com.android.inputmethod.keyboard.EmojiKeyboardView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayDeque; -import java.util.Random; +import java.util.Collection; /** * This is a Keyboard class where you can add keys dynamically shown in a grid layout */ -// TODO: Save/restore recent keys from/to preferences. public class DynamicGridKeyboard extends Keyboard { private static final int TEMPLATE_KEY_CODE_0 = 0x30; private static final int TEMPLATE_KEY_CODE_1 = 0x31; + // Recent codes are saved as an integer array, so we use comma as a separater. + private static final String RECENT_KEY_SEPARATOR = Constants.STRING_COMMA; + private final SharedPreferences mPrefs; 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 final int mMaxKeyCount; + private final boolean mIsRecents; + private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque(); - private Key[] mCachedRecentKeys; + private Key[] mCachedGridKeys; - public DynamicGridKeyboard(final Keyboard templateKeyboard) { + public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard, + final int maxKeyCount, final int categoryId, final int categoryPageId) { super(templateKeyboard); final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0); final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1); @@ -50,8 +58,9 @@ public class DynamicGridKeyboard extends Keyboard { mHorizontalStep = Math.abs(key1.getX() - key0.getX()); mVerticalStep = key0.getHeight() + mVerticalGap; mColumnsNum = mBaseWidth / mHorizontalStep; - final int rowsNum = mBaseHeight / mVerticalStep; - mMaxRecentKeyCount = mColumnsNum * rowsNum; + mMaxKeyCount = maxKeyCount; + mIsRecents = categoryId == EmojiKeyboardView.CATEGORY_ID_RECENTS; + mPrefs = prefs; } private Key getTemplateKey(final int code) { @@ -63,32 +72,67 @@ public class DynamicGridKeyboard extends Keyboard { throw new RuntimeException("Can't find template key: code=" + code); } - private final Random random = new Random(); + public void addKeyFirst(final Key usedKey) { + addKey(usedKey, true); + if (mIsRecents) { + saveRecentKeys(); + } + } + + public void addKeyLast(final Key usedKey) { + addKey(usedKey, false); + } - 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)) { + private void addKey(final Key usedKey, final boolean addFirst) { + synchronized (mGridKeys) { + mCachedGridKeys = null; + final GridKey key = new GridKey(usedKey); + while (mGridKeys.remove(key)) { // Remove duplicate keys. } - mRecentKeys.addFirst(key); - while (mRecentKeys.size() > mMaxRecentKeyCount) { - mRecentKeys.removeLast(); + if (addFirst) { + mGridKeys.addFirst(key); + } else { + mGridKeys.addLast(key); + } + while (mGridKeys.size() > mMaxKeyCount) { + mGridKeys.removeLast(); } int index = 0; - for (final RecentKey recentKey : mRecentKeys) { + for (final GridKey gridKey : mGridKeys) { 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); + gridKey.updateCorrdinates(keyX, keyY); index++; } } } + private void saveRecentKeys() { + final StringBuilder sb = new StringBuilder(); + for (final Key key : mGridKeys) { + sb.append(key.getCode()).append(RECENT_KEY_SEPARATOR); + } + Settings.writeEmojiRecentKeys(mPrefs, sb.toString()); + } + + public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) { + final String str = Settings.readEmojiRecentKeys(mPrefs); + for (String s : str.split(RECENT_KEY_SEPARATOR)) { + if (TextUtils.isEmpty(s)) { + continue; + } + final int code = Integer.valueOf(s); + for (DynamicGridKeyboard kbd : keyboards) { + final Key key = kbd.getKey(code); + if (key != null) { + addKeyLast(key); + break; + } + } + } + } + private int getKeyX(final int index) { final int column = index % mColumnsNum; return column * mHorizontalStep + mLeftPadding; @@ -101,26 +145,26 @@ public class DynamicGridKeyboard extends Keyboard { @Override public Key[] getKeys() { - synchronized (mRecentKeys) { - if (mCachedRecentKeys != null) { - return mCachedRecentKeys; + synchronized (mGridKeys) { + if (mCachedGridKeys != null) { + return mCachedGridKeys; } - mCachedRecentKeys = mRecentKeys.toArray(new Key[mRecentKeys.size()]); - return mCachedRecentKeys; + mCachedGridKeys = mGridKeys.toArray(new Key[mGridKeys.size()]); + return mCachedGridKeys; } } @Override public Key[] getNearestKeys(final int x, final int y) { - // TODO: Calculate the nearest key index in mRecentKeys from x and y. + // TODO: Calculate the nearest key index in mGridKeys from x and y. return getKeys(); } - static final class RecentKey extends Key { + static final class GridKey extends Key { private int mCurrentX; private int mCurrentY; - public RecentKey(final Key originalKey) { + public GridKey(final Key originalKey) { super(originalKey); } @@ -151,7 +195,7 @@ public class DynamicGridKeyboard extends Keyboard { @Override public String toString() { - return "RecentKey: " + super.toString(); + return "GridKey: " + super.toString(); } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 7008b0619..a72595f7c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -383,7 +383,7 @@ public final class KeyboardTextsSet { // Label for "switch to more symbol" modifier key. Must be short to fit on key! /* 124 */ "= \\ <", // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! - /* 125 */ "~ [ {", + /* 125 */ "~ [ <", // Label for "Tab" key. Must be short to fit on key! /* 126 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! @@ -2056,6 +2056,25 @@ public final class KeyboardTextsSet { /* 45 */ "\u0410\u0411\u0412", }; + /* Language lo: Lao */ + private static final String[] LANGUAGE_lo = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ + // Label for "switch to alphabetic" key. + // U+0E81: "ກ" LAO LETTER KO + // U+0E82: "ຂ" LAO LETTER KHO SUNG + // U+0E84: "ຄ" LAO LETTER KHO TAM + /* 45 */ "\u0E81\u0E82\u0E84", + /* 46~ */ + null, null, null, null, null, + /* ~50 */ + // U+20AD: "₭" KIP SIGN + /* 51 */ "\u20AD", + }; + /* Language lt: Lithuanian */ private static final String[] LANGUAGE_lt = { // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK @@ -2347,6 +2366,63 @@ public final class KeyboardTextsSet { /* 47 */ "!text/double_9qm_rqm", }; + /* Language ne: Nepali */ + private static final String[] LANGUAGE_ne = { + /* 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, + /* ~44 */ + // Label for "switch to alphabetic" key. + // U+0915: "क" DEVANAGARI LETTER KA + // U+0916: "ख" DEVANAGARI LETTER KHA + // U+0917: "ग" DEVANAGARI LETTER GA + /* 45 */ "\u0915\u0916\u0917", + /* 46~ */ + null, null, null, null, null, + /* ~50 */ + // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN + /* 51 */ "\u0930\u0941.", + /* 52~ */ + null, null, null, null, null, null, null, null, null, null, null, + /* ~62 */ + // U+0967: "१" DEVANAGARI DIGIT ONE + /* 63 */ "\u0967", + // U+0968: "२" DEVANAGARI DIGIT TWO + /* 64 */ "\u0968", + // U+0969: "३" DEVANAGARI DIGIT THREE + /* 65 */ "\u0969", + // U+096A: "४" DEVANAGARI DIGIT FOUR + /* 66 */ "\u096A", + // U+096B: "५" DEVANAGARI DIGIT FIVE + /* 67 */ "\u096B", + // U+096C: "६" DEVANAGARI DIGIT SIX + /* 68 */ "\u096C", + // U+096D: "७" DEVANAGARI DIGIT SEVEN + /* 69 */ "\u096D", + // U+096E: "८" DEVANAGARI DIGIT EIGHT + /* 70 */ "\u096E", + // U+096F: "९" DEVANAGARI DIGIT NINE + /* 71 */ "\u096F", + // U+0966: "०" DEVANAGARI DIGIT ZERO + /* 72 */ "\u0966", + // Label for "switch to symbols" key. + /* 73 */ "?\u0967\u0968\u0969", + // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" + // part because it'll be appended by the code. + /* 74 */ "\u0967\u0968\u0969", + /* 75 */ "1", + /* 76 */ "2", + /* 77 */ "3", + /* 78 */ "4", + /* 79 */ "5", + /* 80 */ "6", + /* 81 */ "7", + /* 82 */ "8", + /* 83 */ "9", + /* 84 */ "0", + }; + /* Language nl: Dutch */ private static final String[] LANGUAGE_nl = { // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE @@ -3332,11 +3408,13 @@ public final class KeyboardTextsSet { "ka", LANGUAGE_ka, /* Georgian */ "kk", LANGUAGE_kk, /* Kazakh */ "ky", LANGUAGE_ky, /* Kirghiz */ + "lo", LANGUAGE_lo, /* Lao */ "lt", LANGUAGE_lt, /* Lithuanian */ "lv", LANGUAGE_lv, /* Latvian */ "mk", LANGUAGE_mk, /* Macedonian */ "mn", LANGUAGE_mn, /* Mongolian */ "nb", LANGUAGE_nb, /* Norwegian Bokmål */ + "ne", LANGUAGE_ne, /* Nepali */ "nl", LANGUAGE_nl, /* Dutch */ "pl", LANGUAGE_pl, /* Polish */ "pt", LANGUAGE_pt, /* Portuguese */ diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 8aec03f71..029ba02ed 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -220,7 +220,11 @@ public final class Constants { } } + public static final int MAX_INT_BIT_COUNT = 32; + public static final String STRING_COMMA = ","; + private Constants() { // This utility class is not publicly instantiable. } + } diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 686c8d5f5..1a0fecc62 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -98,6 +98,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_SEND_FEEDBACK = "send_feedback"; public static final String PREF_ABOUT_KEYBOARD = "about_keyboard"; + // Emoji + public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys"; + public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id"; + private Resources mRes; private SharedPreferences mPrefs; private SettingsValues mSettingsValues; @@ -370,4 +374,24 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null); return StringUtils.hexStringToByteArray(tokenStr); } + + public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) { + prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply(); + } + + public static String readEmojiRecentKeys(final SharedPreferences prefs) { + return prefs.getString(PREF_EMOJI_RECENT_KEYS, ""); + } + + public static void writeEmojiCategoryLastTypedId( + final SharedPreferences prefs, final int category, final int id) { + final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + category; + prefs.edit().putInt(key, id).apply(); + } + + public static int readEmojiCategoryLastTypedId( + final SharedPreferences prefs, final int category) { + final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + category; + return prefs.getInt(key, 0); + } } |