diff --git a/java/res/layout/emoji_keyboard_page.xml b/java/res/layout/emoji_keyboard_page.xml
new file mode 100644
index 000000000..e0b752b32
--- /dev/null
+++ b/java/res/layout/emoji_keyboard_page.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
diff --git a/java/res/layout/emoji_keyboard_tab_icon.xml b/java/res/layout/emoji_keyboard_tab_icon.xml
new file mode 100644
index 000000000..d79276eb9
--- /dev/null
+++ b/java/res/layout/emoji_keyboard_tab_icon.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/java/res/layout/emoji_keyboard_tab_label.xml b/java/res/layout/emoji_keyboard_tab_label.xml
new file mode 100644
index 000000000..62c552dd8
--- /dev/null
+++ b/java/res/layout/emoji_keyboard_tab_label.xml
@@ -0,0 +1,26 @@
+
+
+
+
diff --git a/java/res/layout/emoji_keyboard_view.xml b/java/res/layout/emoji_keyboard_view.xml
new file mode 100644
index 000000000..ccbcfdc6f
--- /dev/null
+++ b/java/res/layout/emoji_keyboard_view.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/res/xml/kbd_emoji_category6.xml b/java/res/xml/kbd_emoji_category6.xml
index a07966b07..838f3f52c 100644
--- a/java/res/xml/kbd_emoji_category6.xml
+++ b/java/res/xml/kbd_emoji_category6.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="@fraction/emoji_keyboard_key_width"
latin:keyLetterSize="90%p"
+ latin:keyLabelSize="60%p"
>
+ * Emoji category tabs.
+ * Delete button.
+ * Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab.
+ * Back to main keyboard button and enter button.
+ *
+ * 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 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 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);
+ }
+ }
+}