Merge "Use DynamicGridKeyboard for EmojiPager"

main
Satoshi Kataoka 2013-09-13 12:20:14 +00:00 committed by Android (Google) Code Review
commit cdc2ce8c89
2 changed files with 127 additions and 49 deletions

View File

@ -22,6 +22,7 @@ import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
@ -37,7 +38,9 @@ import android.widget.TabHost;
import android.widget.TabHost.OnTabChangeListener; import android.widget.TabHost.OnTabChangeListener;
import android.widget.TextView; import android.widget.TextView;
import com.android.inputmethod.keyboard.internal.CodesArrayParser;
import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard; import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.ScrollKeyboardView; import com.android.inputmethod.keyboard.internal.ScrollKeyboardView;
import com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier; import com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier;
import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Constants;
@ -47,7 +50,10 @@ import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.ResourceUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* View class to implement Emoji keyboards. * View class to implement Emoji keyboards.
@ -75,8 +81,6 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
private static class EmojiCategory {
private int mCurrentCategory = CATEGORY_UNSPECIFIED;
private static final int CATEGORY_UNSPECIFIED = -1; private static final int CATEGORY_UNSPECIFIED = -1;
private static final int CATEGORY_RECENTS = 0; private static final int CATEGORY_RECENTS = 0;
private static final int CATEGORY_PEOPLE = 1; private static final int CATEGORY_PEOPLE = 1;
@ -85,6 +89,9 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
private static final int CATEGORY_PLACES = 4; private static final int CATEGORY_PLACES = 4;
private static final int CATEGORY_SYMBOLS = 5; private static final int CATEGORY_SYMBOLS = 5;
private static final int CATEGORY_EMOTICONS = 6; private static final int CATEGORY_EMOTICONS = 6;
private static class EmojiCategory {
private static final int DEFAULT_MAX_ROW_SIZE = 3;
private static final String[] sCategoryName = { private static final String[] sCategoryName = {
"recents", "recents",
"people", "people",
@ -111,10 +118,18 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
KeyboardId.ELEMENT_EMOJI_CATEGORY4, KeyboardId.ELEMENT_EMOJI_CATEGORY4,
KeyboardId.ELEMENT_EMOJI_CATEGORY5, KeyboardId.ELEMENT_EMOJI_CATEGORY5,
KeyboardId.ELEMENT_EMOJI_CATEGORY6, }; KeyboardId.ELEMENT_EMOJI_CATEGORY6, };
private Resources mRes;
private final KeyboardLayoutSet mLayoutSet;
private final HashMap<String, Integer> mCategoryNameToIdMap = CollectionUtils.newHashMap(); private final HashMap<String, Integer> mCategoryNameToIdMap = CollectionUtils.newHashMap();
private final ArrayList<Integer> mShownCategories = new ArrayList<Integer>(); private final ArrayList<Integer> mShownCategories = new ArrayList<Integer>();
private final ConcurrentHashMap<Long, DynamicGridKeyboard>
mCategoryKeyboardMap = new ConcurrentHashMap<Long, DynamicGridKeyboard>();
public EmojiCategory() { private int mCurrentCategory = CATEGORY_UNSPECIFIED;
public EmojiCategory(final Resources res, final KeyboardLayoutSet layoutSet) {
mRes = res;
mLayoutSet = layoutSet;
for (int i = 0; i < sCategoryName.length; ++i) { for (int i = 0; i < sCategoryName.length; ++i) {
mCategoryNameToIdMap.put(sCategoryName[i], i); mCategoryNameToIdMap.put(sCategoryName[i], i);
} }
@ -185,12 +200,71 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
return mShownCategories.get(tabId); return mShownCategories.get(tabId);
} }
public int getElementIdFromTabId(int tabId) { public DynamicGridKeyboard getKeyboard(int category, int id) {
return sCategoryElementId[getCategoryFromTabId(tabId)]; synchronized(mCategoryKeyboardMap) {
final long key = (((long) category) << 32) | id;
final DynamicGridKeyboard kbd;
if (!mCategoryKeyboardMap.containsKey(key)) {
if (category != CATEGORY_RECENTS) {
kbd = new DynamicGridKeyboard(
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
DEFAULT_MAX_ROW_SIZE);
final Keyboard keyboard =
mLayoutSet.getKeyboard(sCategoryElementId[category]);
// TODO: Calculate maxPageCount dynamically
final Key[][] sortedKeys = sortKeys(keyboard.getKeys(), 21);
for (Key emojiKey : sortedKeys[0]) {
if (emojiKey == null) {
break;
}
kbd.addKeyLast(emojiKey);
}
} else {
kbd = new DynamicGridKeyboard(
mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
DEFAULT_MAX_ROW_SIZE);
}
mCategoryKeyboardMap.put(key, kbd);
} else {
kbd = mCategoryKeyboardMap.get(key);
}
return kbd;
} }
} }
private final EmojiCategory mEmojiCategory = new EmojiCategory(); 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;
public EmojiKeyboardView(final Context context, final AttributeSet attrs) { public EmojiKeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.emojiKeyboardViewStyle); this(context, attrs, R.attr.emojiKeyboardViewStyle);
@ -219,6 +293,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
+ res.getDimensionPixelSize(R.dimen.suggestions_strip_height)); + res.getDimensionPixelSize(R.dimen.suggestions_strip_height));
builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */); builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */);
mLayoutSet = builder.build(); mLayoutSet = builder.build();
mEmojiCategory = new EmojiCategory(context.getResources(), builder.build());
// TODO: Save/restore recent keys from/to preferences. // TODO: Save/restore recent keys from/to preferences.
} }
@ -388,15 +463,14 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
mEmojiCategory = emojiCategory; mEmojiCategory = emojiCategory;
mListener = listener; mListener = listener;
mLayoutSet = layoutSet; mLayoutSet = layoutSet;
mRecentsKeyboard = new DynamicGridKeyboard( mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_RECENTS, 0);
layoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS));
} }
public void addRecentKey(final Key key) { public void addRecentKey(final Key key) {
if (mEmojiCategory.isInRecentTab()) { if (mEmojiCategory.isInRecentTab()) {
return; return;
} }
mRecentsKeyboard.addRecentKey(key); mRecentsKeyboard.addKeyFirst(key);
final KeyboardView recentKeyboardView = final KeyboardView recentKeyboardView =
mActiveKeyboardView.get(mEmojiCategory.getRecentTabId()); mActiveKeyboardView.get(mEmojiCategory.getRecentTabId());
if (recentKeyboardView != null) { if (recentKeyboardView != null) {
@ -424,9 +498,8 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
@Override @Override
public Object instantiateItem(final ViewGroup container, final int position) { public Object instantiateItem(final ViewGroup container, final int position) {
final int elementId = mEmojiCategory.getElementIdFromTabId(position); final Keyboard keyboard =
final Keyboard keyboard = (elementId == KeyboardId.ELEMENT_EMOJI_RECENTS) mEmojiCategory.getKeyboard(mEmojiCategory.getCategoryFromTabId(position), 0);
? mRecentsKeyboard : mLayoutSet.getKeyboard(elementId);
final LayoutInflater inflater = LayoutInflater.from(container.getContext()); final LayoutInflater inflater = LayoutInflater.from(container.getContext());
final View view = inflater.inflate( final View view = inflater.inflate(
R.layout.emoji_keyboard_page, container, false /* attachToRoot */); R.layout.emoji_keyboard_page, container, false /* attachToRoot */);

View File

@ -23,7 +23,6 @@ import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Random;
/** /**
* This is a Keyboard class where you can add keys dynamically shown in a grid layout * This is a Keyboard class where you can add keys dynamically shown in a grid layout
@ -37,12 +36,12 @@ public class DynamicGridKeyboard extends Keyboard {
private final int mHorizontalStep; private final int mHorizontalStep;
private final int mVerticalStep; private final int mVerticalStep;
private final int mColumnsNum; private final int mColumnsNum;
private final int mMaxRecentKeyCount; private final int mMaxKeyCount;
private final ArrayDeque<RecentKey> mRecentKeys = CollectionUtils.newArrayDeque(); private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque();
private Key[] mCachedRecentKeys; private Key[] mCachedGridKeys;
public DynamicGridKeyboard(final Keyboard templateKeyboard) { public DynamicGridKeyboard(final Keyboard templateKeyboard, final int maxRows) {
super(templateKeyboard); super(templateKeyboard);
final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0); final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1); final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
@ -50,8 +49,7 @@ public class DynamicGridKeyboard extends Keyboard {
mHorizontalStep = Math.abs(key1.getX() - key0.getX()); mHorizontalStep = Math.abs(key1.getX() - key0.getX());
mVerticalStep = key0.getHeight() + mVerticalGap; mVerticalStep = key0.getHeight() + mVerticalGap;
mColumnsNum = mBaseWidth / mHorizontalStep; mColumnsNum = mBaseWidth / mHorizontalStep;
final int rowsNum = mBaseHeight / mVerticalStep; mMaxKeyCount = mColumnsNum * maxRows;
mMaxRecentKeyCount = mColumnsNum * rowsNum;
} }
private Key getTemplateKey(final int code) { private Key getTemplateKey(final int code) {
@ -63,27 +61,34 @@ public class DynamicGridKeyboard extends Keyboard {
throw new RuntimeException("Can't find template key: code=" + code); 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);
}
public void addRecentKey(final Key usedKey) { public void addKeyLast(final Key usedKey) {
synchronized (mRecentKeys) { addKey(usedKey, false);
mCachedRecentKeys = null; }
final RecentKey key = (usedKey instanceof RecentKey)
? (RecentKey)usedKey : new RecentKey(usedKey); private void addKey(final Key usedKey, final boolean addFirst) {
while (mRecentKeys.remove(key)) { synchronized (mGridKeys) {
mCachedGridKeys = null;
final GridKey key = new GridKey(usedKey);
while (mGridKeys.remove(key)) {
// Remove duplicate keys. // Remove duplicate keys.
} }
mRecentKeys.addFirst(key); if (addFirst) {
while (mRecentKeys.size() > mMaxRecentKeyCount) { mGridKeys.addFirst(key);
mRecentKeys.removeLast(); } else {
mGridKeys.addLast(key);
}
while (mGridKeys.size() > mMaxKeyCount) {
mGridKeys.removeLast();
} }
int index = 0; int index = 0;
for (final RecentKey recentKey : mRecentKeys) { for (final GridKey gridKey : mGridKeys) {
final int keyX = getKeyX(index); final int keyX = getKeyX(index);
final int keyY = getKeyY(index); final int keyY = getKeyY(index);
final int x = keyX+random.nextInt(recentKey.getWidth()); gridKey.updateCorrdinates(keyX, keyY);
final int y = keyY+random.nextInt(recentKey.getHeight());
recentKey.updateCorrdinates(keyX, keyY);
index++; index++;
} }
} }
@ -101,26 +106,26 @@ public class DynamicGridKeyboard extends Keyboard {
@Override @Override
public Key[] getKeys() { public Key[] getKeys() {
synchronized (mRecentKeys) { synchronized (mGridKeys) {
if (mCachedRecentKeys != null) { if (mCachedGridKeys != null) {
return mCachedRecentKeys; return mCachedGridKeys;
} }
mCachedRecentKeys = mRecentKeys.toArray(new Key[mRecentKeys.size()]); mCachedGridKeys = mGridKeys.toArray(new Key[mGridKeys.size()]);
return mCachedRecentKeys; return mCachedGridKeys;
} }
} }
@Override @Override
public Key[] getNearestKeys(final int x, final int y) { 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(); return getKeys();
} }
static final class RecentKey extends Key { static final class GridKey extends Key {
private int mCurrentX; private int mCurrentX;
private int mCurrentY; private int mCurrentY;
public RecentKey(final Key originalKey) { public GridKey(final Key originalKey) {
super(originalKey); super(originalKey);
} }
@ -151,7 +156,7 @@ public class DynamicGridKeyboard extends Keyboard {
@Override @Override
public String toString() { public String toString() {
return "RecentKey: " + super.toString(); return "GridKey: " + super.toString();
} }
} }
} }