18ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka/*
28ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * Copyright (C) 2013 The Android Open Source Project
38ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka *
48ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * You may obtain a copy of the License at
78ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka *
88ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
98ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka *
108ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
118ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * See the License for the specific language governing permissions and
148ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * limitations under the License.
158ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka */
168ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
179f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokapackage com.android.inputmethod.keyboard.emoji;
188ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
199342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport static com.android.inputmethod.latin.common.Constants.NOT_A_COORDINATE;
208ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
218ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.content.Context;
228ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.content.res.Resources;
238ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.content.res.TypedArray;
2400ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovicimport android.graphics.Color;
25f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataokaimport android.preference.PreferenceManager;
268ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.support.v4.view.ViewPager;
278ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.util.AttributeSet;
285dfbc8af2439152ca6b6759309aced5bb879b518Satoshi Kataokaimport android.util.Pair;
29fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaokaimport android.util.TypedValue;
308ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.view.LayoutInflater;
319204d11525b26be49fc6a4bc6f785d326b1b5984Satoshi Kataokaimport android.view.MotionEvent;
328ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.view.View;
33c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaokaimport android.widget.ImageButton;
348ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.widget.ImageView;
358ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.widget.LinearLayout;
368ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.widget.TabHost;
378ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.widget.TabHost.OnTabChangeListener;
382782f9792708148e4481aa41912239500825d819Tadashi G. Takaokaimport android.widget.TabWidget;
398ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport android.widget.TextView;
408ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
419f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key;
429f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardActionListener;
439f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardLayoutSet;
449f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardView;
45d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyDrawParams;
46d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
47c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
48874a600dc805b6ab9fe92fccddfe4765b8d29a35Tadashi G. Takaokaimport com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
498ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport com.android.inputmethod.latin.R;
502a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaokaimport com.android.inputmethod.latin.RichInputMethodSubtype;
519342484e8d573a40f470b6a593df31c602fa4076Ken Wakasaimport com.android.inputmethod.latin.common.Constants;
528ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaokaimport com.android.inputmethod.latin.utils.ResourceUtils;
538ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
548ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka/**
55d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka * View class to implement Emoji palettes.
5627ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka * The Emoji keyboard consists of group of views layout/emoji_palettes_view.
578ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * <ol>
588ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * <li> Emoji category tabs.
598ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * <li> Delete button.
608ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * <li> Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab.
618ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * <li> Back to main keyboard button and enter button.
628ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * </ol>
638ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka * Because of the above reasons, this class doesn't extend {@link KeyboardView}.
648ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka */
65d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataokapublic final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
660e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        ViewPager.OnPageChangeListener, View.OnClickListener, View.OnTouchListener,
67df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        EmojiPageKeyboardView.OnKeyEventListener {
68c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka    private final int mFunctionalKeyBackgroundId;
69c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka    private final int mSpacebarBackgroundId;
702782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka    private final boolean mCategoryIndicatorEnabled;
712782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka    private final int mCategoryIndicatorDrawableResId;
722782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka    private final int mCategoryIndicatorBackgroundResId;
732782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka    private final int mCategoryPageIndicatorColor;
742782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka    private final int mCategoryPageIndicatorBackground;
75d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka    private EmojiPalettesAdapter mEmojiPalettesAdapter;
762fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa    private final EmojiLayoutParams mEmojiLayoutParams;
7700ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic    private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener;
788ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
79c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka    private ImageButton mDeleteKey;
80fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka    private TextView mAlphabetKeyLeft;
81fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka    private TextView mAlphabetKeyRight;
823558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka    private View mSpacebar;
833558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka    // TODO: Remove this workaround.
843558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka    private View mSpacebarIcon;
858ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    private TabHost mTabHost;
868ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    private ViewPager mEmojiPager;
87f981fe7ed23b269676a95f953728d74f82f68e4bSatoshi Kataoka    private int mCurrentPagerPosition = 0;
88250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka    private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
898ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
908ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
918ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
92b0bf7e729b5a3ec6dc481d72f04d7dad0e12672aSatoshi Kataoka    private final EmojiCategory mEmojiCategory;
938ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
94d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka    public EmojiPalettesView(final Context context, final AttributeSet attrs) {
95d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka        this(context, attrs, R.attr.emojiPalettesViewStyle);
968ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
978ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
98d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka    public EmojiPalettesView(final Context context, final AttributeSet attrs, final int defStyle) {
998ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        super(context, attrs, defStyle);
1008ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
1018ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka                R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
102c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka        final int keyBackgroundId = keyboardViewAttr.getResourceId(
1038ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka                R.styleable.KeyboardView_keyBackground, 0);
104c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka        mFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId(
105c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka                R.styleable.KeyboardView_functionalKeyBackground, keyBackgroundId);
106c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka        mSpacebarBackgroundId = keyboardViewAttr.getResourceId(
107c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka                R.styleable.KeyboardView_spacebarBackground, keyBackgroundId);
1088ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        keyboardViewAttr.recycle();
1098ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
1108ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka                context, null /* editorInfo */);
1118ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final Resources res = context.getResources();
1122fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mEmojiLayoutParams = new EmojiLayoutParams(res);
1132a7da0ab87db1166c62c171858b589da3d9c2ca7Tadashi G. Takaoka        builder.setSubtype(RichInputMethodSubtype.getEmojiSubtype());
114be819dbe2b7a5c418530a2fa1182766a4d31d6d5Tadashi G. Takaoka        builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res),
115be819dbe2b7a5c418530a2fa1182766a4d31d6d5Tadashi G. Takaoka                mEmojiLayoutParams.mEmojiKeyboardHeight);
11627ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        final KeyboardLayoutSet layoutSet = builder.build();
11727ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs,
11827ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka                R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView);
119f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context),
12027ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka                res, layoutSet, emojiPalettesViewAttr);
1212782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        mCategoryIndicatorEnabled = emojiPalettesViewAttr.getBoolean(
1222782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka                R.styleable.EmojiPalettesView_categoryIndicatorEnabled, false);
1232782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        mCategoryIndicatorDrawableResId = emojiPalettesViewAttr.getResourceId(
1242782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka                R.styleable.EmojiPalettesView_categoryIndicatorDrawable, 0);
1252782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        mCategoryIndicatorBackgroundResId = emojiPalettesViewAttr.getResourceId(
1262782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka                R.styleable.EmojiPalettesView_categoryIndicatorBackground, 0);
1272782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        mCategoryPageIndicatorColor = emojiPalettesViewAttr.getColor(
1282782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka                R.styleable.EmojiPalettesView_categoryPageIndicatorColor, 0);
1292782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        mCategoryPageIndicatorBackground = emojiPalettesViewAttr.getColor(
1302782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka                R.styleable.EmojiPalettesView_categoryPageIndicatorBackground, 0);
13127ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        emojiPalettesViewAttr.recycle();
13200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener();
1338ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
1348ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
1358ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
1368ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
1378ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1388ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final Resources res = getContext().getResources();
1398ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        // The main keyboard expands to the entire this {@link KeyboardView}.
140be819dbe2b7a5c418530a2fa1182766a4d31d6d5Tadashi G. Takaoka        final int width = ResourceUtils.getDefaultKeyboardWidth(res)
1418ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka                + getPaddingLeft() + getPaddingRight();
1428ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final int height = ResourceUtils.getDefaultKeyboardHeight(res)
1432fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa                + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height)
1448ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka                + getPaddingTop() + getPaddingBottom();
1458ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        setMeasuredDimension(width, height);
1468ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
1478ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
148f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka    private void addTab(final TabHost host, final int categoryId) {
1495f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka        final String tabId = EmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */);
1508ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final TabHost.TabSpec tspec = host.newTabSpec(tabId);
1518ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        tspec.setContent(R.id.emoji_keyboard_dummy);
15227ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate(
15327ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka                R.layout.emoji_keyboard_tab_icon, null);
15460835872a15992e239cf9bd038b6ee33b405f1d3Chieu Nguyen        // TODO: Replace background color with its own setting rather than using the
15560835872a15992e239cf9bd038b6ee33b405f1d3Chieu Nguyen        //       category page indicator background as a workaround.
15660835872a15992e239cf9bd038b6ee33b405f1d3Chieu Nguyen        iconView.setBackgroundColor(mCategoryPageIndicatorBackground);
15727ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        iconView.setImageResource(mEmojiCategory.getCategoryTabIcon(categoryId));
15827ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        iconView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId));
15927ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        tspec.setIndicator(iconView);
1608ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        host.addTab(tspec);
1618ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
1628ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
1638ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
1648ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    protected void onFinishInflate() {
1658ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost);
1668ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mTabHost.setup();
167ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (final EmojiCategory.CategoryProperties properties
168ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                : mEmojiCategory.getShownCategories()) {
169f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka            addTab(mTabHost, properties.mCategoryId);
17036ccb030ed1c19669116f0bb17d57b3b204fbf04Satoshi Kataoka        }
1718ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mTabHost.setOnTabChangedListener(this);
1722782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        final TabWidget tabWidget = mTabHost.getTabWidget();
1732782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        tabWidget.setStripEnabled(mCategoryIndicatorEnabled);
1742782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        if (mCategoryIndicatorEnabled) {
1752782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka            // On TabWidget's strip, what looks like an indicator is actually a background.
1762782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka            // And what looks like a background are actually left and right drawables.
1772782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka            tabWidget.setBackgroundResource(mCategoryIndicatorDrawableResId);
1782782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka            tabWidget.setLeftStripDrawable(mCategoryIndicatorBackgroundResId);
1792782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka            tabWidget.setRightStripDrawable(mCategoryIndicatorBackgroundResId);
1802782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        }
1818ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
1822fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this);
183dd072e32014935da120fab855a0d5c07541c51c3Satoshi Kataoka
1848ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager);
185d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka        mEmojiPager.setAdapter(mEmojiPalettesAdapter);
1868ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mEmojiPager.setOnPageChangeListener(this);
1878ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mEmojiPager.setOffscreenPageLimit(0);
1882fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mEmojiPager.setPersistentDrawingCache(PERSISTENT_NO_CACHE);
1892fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mEmojiLayoutParams.setPagerProperties(mEmojiPager);
190250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka
191250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        mEmojiCategoryPageIndicatorView =
192250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view);
1932782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka        mEmojiCategoryPageIndicatorView.setColors(
1942782f9792708148e4481aa41912239500825d819Tadashi G. Takaoka                mCategoryPageIndicatorColor, mCategoryPageIndicatorBackground);
1952fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
1968ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
197f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */);
1988ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
1992d3d415826fab9e8ebde0f752f4fb1508bb307daSatoshi Kataoka        final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar);
2002fa3693c264a4c150ac307d9bb7f6f8f18cc4ffcKen Wakasa        mEmojiLayoutParams.setActionBarProperties(actionBar);
2012d3d415826fab9e8ebde0f752f4fb1508bb307daSatoshi Kataoka
2020e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        // deleteKey depends only on OnTouchListener.
203c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mDeleteKey = (ImageButton)findViewById(R.id.emoji_keyboard_delete);
204c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mDeleteKey.setBackgroundResource(mFunctionalKeyBackgroundId);
205c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mDeleteKey.setTag(Constants.CODE_DELETE);
20600ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        mDeleteKey.setOnTouchListener(mDeleteKeyOnTouchListener);
2070e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa
208fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // {@link #mAlphabetKeyLeft}, {@link #mAlphabetKeyRight, and spaceKey depend on
209fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // {@link View.OnClickListener} as well as {@link View.OnTouchListener}.
210fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // {@link View.OnTouchListener} is used as the trigger of key-press, while
211fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // {@link View.OnClickListener} is used as the trigger of key-release which does not occur
212fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // if the event is canceled by moving off the finger from the view.
213fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // The text on alphabet keys are set at
214fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        // {@link #startEmojiPalettes(String,int,float,Typeface)}.
215fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyLeft = (TextView)findViewById(R.id.emoji_keyboard_alphabet_left);
216c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka        mAlphabetKeyLeft.setBackgroundResource(mFunctionalKeyBackgroundId);
217fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyLeft.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
218fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyLeft.setOnTouchListener(this);
219fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyLeft.setOnClickListener(this);
220fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyRight = (TextView)findViewById(R.id.emoji_keyboard_alphabet_right);
221c1e6100bdea95872cb66a64b7ee14ab0ae46476fTadashi G. Takaoka        mAlphabetKeyRight.setBackgroundResource(mFunctionalKeyBackgroundId);
222fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyRight.setTag(Constants.CODE_ALPHA_FROM_EMOJI);
223fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyRight.setOnTouchListener(this);
224fe39aec08a295ff6bd4622207ebacdbaa746c300Tadashi G. Takaoka        mAlphabetKeyRight.setOnClickListener(this);
2253558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka        mSpacebar = findViewById(R.id.emoji_keyboard_space);
226c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mSpacebar.setBackgroundResource(mSpacebarBackgroundId);
227c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mSpacebar.setTag(Constants.CODE_SPACE);
228c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mSpacebar.setOnTouchListener(this);
229c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mSpacebar.setOnClickListener(this);
230c1094d69134740ee0209f1ddfb25dd770b37ab25Tadashi G. Takaoka        mEmojiLayoutParams.setKeyProperties(mSpacebar);
2313558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka        mSpacebarIcon = findViewById(R.id.emoji_keyboard_space_icon);
2328ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
2338ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
2348ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
235b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka    public boolean dispatchTouchEvent(final MotionEvent ev) {
236b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka        // Add here to the stack trace to nail down the {@link IllegalArgumentException} exception
237b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka        // in MotionEvent that sporadically happens.
238b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka        // TODO: Remove this override method once the issue has been addressed.
239b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka        return super.dispatchTouchEvent(ev);
240b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka    }
241b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka
242b6c70f4ed31410eb33cd43e316a53067517c1fe3Tadashi G. Takaoka    @Override
2438ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    public void onTabChanged(final String tabId) {
244874a600dc805b6ab9fe92fccddfe4765b8d29a35Tadashi G. Takaoka        AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
245874a600dc805b6ab9fe92fccddfe4765b8d29a35Tadashi G. Takaoka                Constants.CODE_UNSPECIFIED, this);
246f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        final int categoryId = mEmojiCategory.getCategoryId(tabId);
247f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        setCurrentCategoryId(categoryId, false /* force */);
248250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        updateEmojiCategoryPageIdView();
2498ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
2508ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
2518ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
2528ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    public void onPageSelected(final int position) {
253f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        final Pair<Integer, Integer> newPos =
254f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka                mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position);
255f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        setCurrentCategoryId(newPos.first /* categoryId */, false /* force */);
256f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */);
257250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        updateEmojiCategoryPageIdView();
258f981fe7ed23b269676a95f953728d74f82f68e4bSatoshi Kataoka        mCurrentPagerPosition = position;
2598ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
2608ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
2618ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
2628ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    public void onPageScrollStateChanged(final int state) {
2638ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        // Ignore this message. Only want the actual page selected.
2648ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
2658ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
2668ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
2678ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    public void onPageScrolled(final int position, final float positionOffset,
26823574d1231296e090e6efb41a50c7885a116ddb4Dan Zivkovic                               final int positionOffsetPixels) {
2694c259866c418c4bbb1348ffd122a805e86ec8bbdYohei Yukawa        mEmojiPalettesAdapter.onPageScrolled();
270250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        final Pair<Integer, Integer> newPos =
271250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position);
272250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        final int newCategoryId = newPos.first;
273250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        final int newCategorySize = mEmojiCategory.getCategoryPageSize(newCategoryId);
274250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        final int currentCategoryId = mEmojiCategory.getCurrentCategoryId();
275250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        final int currentCategoryPageId = mEmojiCategory.getCurrentCategoryPageId();
276250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageSize();
277250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        if (newCategoryId == currentCategoryId) {
278250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka            mEmojiCategoryPageIndicatorView.setCategoryPageId(
279250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                    newCategorySize, newPos.second, positionOffset);
280250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        } else if (newCategoryId > currentCategoryId) {
281250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka            mEmojiCategoryPageIndicatorView.setCategoryPageId(
282250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                    currentCategorySize, currentCategoryPageId, positionOffset);
283250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        } else if (newCategoryId < currentCategoryId) {
284250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka            mEmojiCategoryPageIndicatorView.setCategoryPageId(
285250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                    currentCategorySize, currentCategoryPageId, positionOffset - 1);
286250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        }
2878ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
2888ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
2890e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    /**
2900e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnTouchListener}
2910e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * interface to handle touch events from View-based elements such as the space bar.
2920e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * Note that this method is used only for observing {@link MotionEvent#ACTION_DOWN} to trigger
2930e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * {@link KeyboardActionListener#onPressKey}. {@link KeyboardActionListener#onReleaseKey} will
2940e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * be covered by {@link #onClick} as long as the event is not canceled.
2950e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     */
2968ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
297df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa    public boolean onTouch(final View v, final MotionEvent event) {
2980e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        if (event.getActionMasked() != MotionEvent.ACTION_DOWN) {
2990e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa            return false;
3000e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        }
301df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        final Object tag = v.getTag();
302df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        if (!(tag instanceof Integer)) {
303df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa            return false;
304df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        }
305df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        final int code = (Integer) tag;
3060e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        mKeyboardActionListener.onPressKey(
3070e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa                code, 0 /* repeatCount */, true /* isSinglePointer */);
3080e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        // It's important to return false here. Otherwise, {@link #onClick} and touch-down visual
3090e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        // feedback stop working.
310df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        return false;
3118ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
3128ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
3130e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    /**
3140e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnClickListener}
3150e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * interface to handle non-canceled touch-up events from View-based elements such as the space
3160e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * bar.
3170e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     */
3180e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    @Override
3190e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    public void onClick(View v) {
3200e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        final Object tag = v.getTag();
3210e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        if (!(tag instanceof Integer)) {
3220e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa            return;
3230e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        }
3240e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        final int code = (Integer) tag;
3252282e8520a2c1984989a14fb09896536f5033b26Jean Chalard        mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE,
3262282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                false /* isKeyRepeat */);
3270e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa        mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
3280e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    }
3290e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa
3300e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    /**
3310e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * Called from {@link EmojiPageKeyboardView} through
3329f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaoka     * {@link com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener}
3330e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * interface to handle touch events from non-View-based elements such as Emoji buttons.
3340e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     */
335df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa    @Override
336df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa    public void onPressKey(final Key key) {
337df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        final int code = key.getCode();
3388ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */);
3398ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
3408ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
3410e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa    /**
3420e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * Called from {@link EmojiPageKeyboardView} through
3439f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaoka     * {@link com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener}
3440e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     * interface to handle touch events from non-View-based elements such as Emoji buttons.
3450e04c24a0501d3a7ba7b24622ca3866027a21c3dYohei Yukawa     */
3468ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    @Override
347df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa    public void onReleaseKey(final Key key) {
348d76b55297940a65bb9479020a9ed58aa978a0aeaSatoshi Kataoka        mEmojiPalettesAdapter.addRecentKey(key);
349f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        mEmojiCategory.saveLastTypedCategoryPage();
3508ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        final int code = key.getCode();
3518ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        if (code == Constants.CODE_OUTPUT_TEXT) {
3528ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka            mKeyboardActionListener.onTextInput(key.getOutputText());
353df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        } else {
3542282e8520a2c1984989a14fb09896536f5033b26Jean Chalard            mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE,
3552282e8520a2c1984989a14fb09896536f5033b26Jean Chalard                    false /* isKeyRepeat */);
3568ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        }
357df10aeb1da87f980e5d0512133261e2c92c688a7Yohei Yukawa        mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
3588ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
3598ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
3608ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
361e289441f928eb34463aa823a78fb9690a4d7be72Ken Wakasa        if (!enabled) return;
362e289441f928eb34463aa823a78fb9690a4d7be72Ken Wakasa        // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
363e289441f928eb34463aa823a78fb9690a4d7be72Ken Wakasa        setLayerType(LAYER_TYPE_HARDWARE, null);
3648ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
3658ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
366d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka    private static void setupAlphabetKey(final TextView alphabetKey, final String label,
36723574d1231296e090e6efb41a50c7885a116ddb4Dan Zivkovic                                         final KeyDrawParams params) {
368d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        alphabetKey.setText(label);
36927ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        alphabetKey.setTextColor(params.mFunctionalTextColor);
370d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        alphabetKey.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mLabelSize);
371d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        alphabetKey.setTypeface(params.mTypeface);
372d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka    }
373d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka
374d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka    public void startEmojiPalettes(final String switchToAlphaLabel,
37500ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                                   final KeyVisualAttributes keyVisualAttr,
37600ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                                   final KeyboardIconsSet iconSet) {
377761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka        final int deleteIconResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_DELETE_KEY);
378761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka        if (deleteIconResId != 0) {
379761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka            mDeleteKey.setImageResource(deleteIconResId);
380761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka        }
381761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka        final int spacebarResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_SPACE_KEY);
382761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka        if (spacebarResId != 0) {
3833558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka            // TODO: Remove this workaround to place the spacebar icon.
3843558505bee4c5254ee31cb0101b0a5d8d44a46cdTadashi G. Takaoka            mSpacebarIcon.setBackgroundResource(spacebarResId);
385761a6812312c48d04bcb91e5c4448364c4925731Tadashi G. Takaoka        }
386d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        final KeyDrawParams params = new KeyDrawParams();
387d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr);
388d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params);
389d227b00a72a68c59b8d2b2243a68c0f59d919b25Tadashi G. Takaoka        setupAlphabetKey(mAlphabetKeyRight, switchToAlphaLabel, params);
3900e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka        mEmojiPager.setAdapter(mEmojiPalettesAdapter);
391f981fe7ed23b269676a95f953728d74f82f68e4bSatoshi Kataoka        mEmojiPager.setCurrentItem(mCurrentPagerPosition);
3920e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka    }
3930e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka
3940e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka    public void stopEmojiPalettes() {
3950380421bf7aea2fd3c39f5e3cf13e1593c0da02eTadashi G. Takaoka        mEmojiPalettesAdapter.releaseCurrentKey(true /* withKeyRegistering */);
396f981fe7ed23b269676a95f953728d74f82f68e4bSatoshi Kataoka        mEmojiPalettesAdapter.flushPendingRecentKeys();
3970e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka        mEmojiPager.setAdapter(null);
3980e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka    }
3990e01df9baf3d9e2fc7b9a51b683ce6df6ff547ddSatoshi Kataoka
4008ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    public void setKeyboardActionListener(final KeyboardActionListener listener) {
4018ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        mKeyboardActionListener = listener;
40200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        mDeleteKeyOnTouchListener.setKeyboardActionListener(listener);
4038ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
4048ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
405250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka    private void updateEmojiCategoryPageIdView() {
406250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        if (mEmojiCategoryPageIndicatorView == null) {
407250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka            return;
408250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        }
409250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka        mEmojiCategoryPageIndicatorView.setCategoryPageId(
410250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                mEmojiCategory.getCurrentCategoryPageSize(),
411250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka                mEmojiCategory.getCurrentCategoryPageId(), 0.0f /* offset */);
412250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka    }
413250a12f6c216f65f3b6459b9fa620d20a6b94475Satoshi Kataoka
414f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka    private void setCurrentCategoryId(final int categoryId, final boolean force) {
415e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka        final int oldCategoryId = mEmojiCategory.getCurrentCategoryId();
416e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka        if (oldCategoryId == categoryId && !force) {
4178ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka            return;
4188ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        }
4198ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka
420ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        if (oldCategoryId == EmojiCategory.ID_RECENTS) {
421e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka            // Needs to save pending updates for recent keys when we get out of the recents
422e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka            // category because we don't want to move the recent emojis around while the user
423e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka            // is in the recents category.
424e6e4c70a237440ad94e4d1590e98f12d53c1cf2bSatoshi Kataoka            mEmojiPalettesAdapter.flushPendingRecentKeys();
425e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka        }
426e7ed5cae44b52f22d866e60f5e3c7e87f1375a1fSatoshi Kataoka
427f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        mEmojiCategory.setCurrentCategoryId(categoryId);
428f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId);
429f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka        final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId);
4305dfbc8af2439152ca6b6759309aced5bb879b518Satoshi Kataoka        if (force || mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(
431f3f00006cbe2046abbad3a901b436d67497a40f9Satoshi Kataoka                mEmojiPager.getCurrentItem()).first != categoryId) {
43244220e727105c23fad8eaf3777390e65747c1401Satoshi Kataoka            mEmojiPager.setCurrentItem(newCategoryPageId, false /* smoothScroll */);
4338ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        }
434dd072e32014935da120fab855a0d5c07541c51c3Satoshi Kataoka        if (force || mTabHost.getCurrentTab() != newTabId) {
435dd072e32014935da120fab855a0d5c07541c51c3Satoshi Kataoka            mTabHost.setCurrentTab(newTabId);
4368ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka        }
4378ac0eb59e13bce327007ba2cf2f1a7e767eebe0aTadashi G. Takaoka    }
43800ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic
43900ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic    private static class DeleteKeyOnTouchListener implements OnTouchListener {
44000ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        private KeyboardActionListener mKeyboardActionListener =
44100ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                KeyboardActionListener.EMPTY_LISTENER;
44200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic
44300ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        public void setKeyboardActionListener(final KeyboardActionListener listener) {
44400ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            mKeyboardActionListener = listener;
44500ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        }
44600ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic
44700ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        @Override
44800ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        public boolean onTouch(final View v, final MotionEvent event) {
44900ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            switch (event.getActionMasked()) {
45000ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                case MotionEvent.ACTION_DOWN:
45100ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    onTouchDown(v);
45200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    return true;
45300ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                case MotionEvent.ACTION_MOVE:
45400ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    final float x = event.getX();
45500ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    final float y = event.getY();
45600ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    if (x < 0.0f || v.getWidth() < x || y < 0.0f || v.getHeight() < y) {
45700ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                        // Stop generating key events once the finger moves away from the view area.
45800ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                        onTouchCanceled(v);
45900ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    }
46000ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    return true;
46100ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                case MotionEvent.ACTION_CANCEL:
46200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                case MotionEvent.ACTION_UP:
46300ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    onTouchUp(v);
46400ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    return true;
46500ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            }
46600ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            return false;
46700ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        }
46800ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic
46900ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        private void onTouchDown(final View v) {
47000ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            mKeyboardActionListener.onPressKey(Constants.CODE_DELETE,
47100ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    0 /* repeatCount */, true /* isSinglePointer */);
47200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            v.setPressed(true /* pressed */);
47300ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        }
47400ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic
47500ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        private void onTouchUp(final View v) {
47600ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            mKeyboardActionListener.onCodeInput(Constants.CODE_DELETE,
47700ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic                    NOT_A_COORDINATE, NOT_A_COORDINATE, false /* isKeyRepeat */);
47800ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            mKeyboardActionListener.onReleaseKey(Constants.CODE_DELETE, false /* withSliding */);
47900ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            v.setPressed(false /* pressed */);
48000ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        }
48100ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic
48200ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        private void onTouchCanceled(final View v) {
48300ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic            v.setBackgroundColor(Color.TRANSPARENT);
48400ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic        }
48500ae43316ed33df2b845023069b0b2b578982fc8Dan Zivkovic    }
48623574d1231296e090e6efb41a50c7885a116ddb4Dan Zivkovic}