1ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka/*
27674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen * Copyright (C) 2015 The Android Open Source Project
3ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka *
4ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
5ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * you may not use this file except in compliance with the License.
6ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * You may obtain a copy of the License at
7ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka *
8ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka *
10ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
11ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
12ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * See the License for the specific language governing permissions and
14ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka * limitations under the License.
15ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka */
16ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
179f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokapackage com.android.inputmethod.keyboard.emoji;
18ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
19ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport android.content.SharedPreferences;
20ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport android.content.res.Resources;
2127ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaokaimport android.content.res.TypedArray;
227674a653678b542d795b0bd05c241654ffe1015aChieu Nguyenimport android.graphics.Paint;
23ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport android.graphics.Rect;
24ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport android.os.Build;
25ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport android.util.Log;
26ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport android.util.Pair;
27ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
2878cff10f8d317641dd3531cce04e74502be8ad44Tadashi G. Takaokaimport com.android.inputmethod.compat.BuildCompatUtils;
29ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.Key;
30ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.Keyboard;
31ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardId;
32ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport com.android.inputmethod.keyboard.KeyboardLayoutSet;
33ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport com.android.inputmethod.latin.R;
34ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport com.android.inputmethod.latin.settings.Settings;
35ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
36ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport java.util.ArrayList;
37ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport java.util.Collections;
38ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport java.util.Comparator;
39ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport java.util.HashMap;
40ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport java.util.List;
41ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaokaimport java.util.concurrent.ConcurrentHashMap;
42ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
439f4d62cc42ab66f72ecb23996ffc2f8b039c8c4aTadashi G. Takaokafinal class EmojiCategory {
44ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private final String TAG = EmojiCategory.class.getSimpleName();
45ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
46ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_UNSPECIFIED = -1;
47ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public static final int ID_RECENTS = 0;
48ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_PEOPLE = 1;
49ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_OBJECTS = 2;
50ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_NATURE = 3;
51ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_PLACES = 4;
52ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_SYMBOLS = 5;
53ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int ID_EMOTICONS = 6;
547674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_FLAGS = 7;
557674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_SMILEY_PEOPLE = 8;
567674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_ANIMALS_NATURE = 9;
577674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_FOOD_DRINK = 10;
587674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_TRAVEL_PLACES = 11;
597674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_ACTIVITY = 12;
607674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_OBJECTS = 13;
617674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_SYMBOLS = 14;
627674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_FLAGS = 15;
637674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static final int ID_EIGHT_SMILEY_PEOPLE_BORING = 16;
64ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
65ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public final class CategoryProperties {
66ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        public final int mCategoryId;
67ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        public final int mPageCount;
68ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        public CategoryProperties(final int categoryId, final int pageCount) {
69ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            mCategoryId = categoryId;
70ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            mPageCount = pageCount;
71ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
72ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
73ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
74ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final String[] sCategoryName = {
75ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            "recents",
76ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            "people",
77ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            "objects",
78ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            "nature",
79ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            "places",
80ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            "symbols",
817674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "emoticons",
827674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "flags",
837674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "smiley & people",
847674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "animals & nature",
857674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "food & drink",
867674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "travel & places",
877674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "activity",
887674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "objects2",
897674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "symbols2",
907674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "flags2",
917674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            "smiley & people2" };
92ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
9327ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka    private static final int[] sCategoryTabIconAttr = {
9427ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            R.styleable.EmojiPalettesView_iconEmojiRecentsTab,
9527ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            R.styleable.EmojiPalettesView_iconEmojiCategory1Tab,
9627ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            R.styleable.EmojiPalettesView_iconEmojiCategory2Tab,
9727ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            R.styleable.EmojiPalettesView_iconEmojiCategory3Tab,
9827ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            R.styleable.EmojiPalettesView_iconEmojiCategory4Tab,
9927ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            R.styleable.EmojiPalettesView_iconEmojiCategory5Tab,
1007674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory6Tab,
1017674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory7Tab,
1027674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory8Tab,
1037674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory9Tab,
1047674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory10Tab,
1057674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory11Tab,
1067674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory12Tab,
1077674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory13Tab,
1087674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory14Tab,
1097674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory15Tab,
1107674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.styleable.EmojiPalettesView_iconEmojiCategory16Tab };
111ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
112ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int[] sAccessibilityDescriptionResourceIdsForCategories = {
113ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            R.string.spoken_descrption_emoji_category_recents,
114ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            R.string.spoken_descrption_emoji_category_people,
115ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            R.string.spoken_descrption_emoji_category_objects,
116ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            R.string.spoken_descrption_emoji_category_nature,
117ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            R.string.spoken_descrption_emoji_category_places,
118ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            R.string.spoken_descrption_emoji_category_symbols,
1197674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_emoticons,
1207674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_flags,
1217674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_eight_smiley_people,
1227674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_eight_animals_nature,
1237674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_eight_food_drink,
1247674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_eight_travel_places,
1257674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_eight_activity,
1267674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_objects,
1277674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_symbols,
1287674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_flags,
1297674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            R.string.spoken_descrption_emoji_category_eight_smiley_people };
130ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
131ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final int[] sCategoryElementId = {
132ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            KeyboardId.ELEMENT_EMOJI_RECENTS,
133ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            KeyboardId.ELEMENT_EMOJI_CATEGORY1,
134ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            KeyboardId.ELEMENT_EMOJI_CATEGORY2,
135ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            KeyboardId.ELEMENT_EMOJI_CATEGORY3,
136ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            KeyboardId.ELEMENT_EMOJI_CATEGORY4,
137ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            KeyboardId.ELEMENT_EMOJI_CATEGORY5,
1387674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY6,
1397674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY7,
1407674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY8,
1417674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY9,
1427674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY10,
1437674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY11,
1447674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY12,
1457674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY13,
1467674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY14,
1477674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY15,
1487674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            KeyboardId.ELEMENT_EMOJI_CATEGORY16 };
149ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
150ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private final SharedPreferences mPrefs;
151ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private final Resources mRes;
152ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private final int mMaxPageKeyCount;
153ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private final KeyboardLayoutSet mLayoutSet;
154a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final HashMap<String, Integer> mCategoryNameToIdMap = new HashMap<>();
15527ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka    private final int[] mCategoryTabIconId = new int[sCategoryName.length];
156a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final ArrayList<CategoryProperties> mShownCategories = new ArrayList<>();
157a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka    private final ConcurrentHashMap<Long, DynamicGridKeyboard> mCategoryKeyboardMap =
158a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka            new ConcurrentHashMap<>();
159ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
160ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private int mCurrentCategoryId = EmojiCategory.ID_UNSPECIFIED;
161ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private int mCurrentCategoryPageId = 0;
162ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
163ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public EmojiCategory(final SharedPreferences prefs, final Resources res,
16427ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            final KeyboardLayoutSet layoutSet, final TypedArray emojiPaletteViewAttr) {
165ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mPrefs = prefs;
166ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mRes = res;
167ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count);
168ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mLayoutSet = layoutSet;
169ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (int i = 0; i < sCategoryName.length; ++i) {
170ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            mCategoryNameToIdMap.put(sCategoryName[i], i);
17127ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka            mCategoryTabIconId[i] = emojiPaletteViewAttr.getResourceId(
17227ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka                    sCategoryTabIconAttr[i], 0);
173ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
17428e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov
17528e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov        int defaultCategoryId = EmojiCategory.ID_SYMBOLS;
176ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        addShownCategoryId(EmojiCategory.ID_RECENTS);
17778cff10f8d317641dd3531cce04e74502be8ad44Tadashi G. Takaoka        if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.KITKAT) {
1787674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            if (canShowUnicodeEightEmoji()) {
1797674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                defaultCategoryId = EmojiCategory.ID_EIGHT_SMILEY_PEOPLE;
1807674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_SMILEY_PEOPLE);
1817674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_ANIMALS_NATURE);
1827674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_FOOD_DRINK);
1837674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_TRAVEL_PLACES);
1847674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_ACTIVITY);
1857674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_OBJECTS);
1867674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_EIGHT_SYMBOLS);
1877674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_FLAGS); // Exclude combinations without glyphs.
1887674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            } else {
1897674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                defaultCategoryId = EmojiCategory.ID_PEOPLE;
1907674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_PEOPLE);
1917674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_OBJECTS);
1927674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_NATURE);
1937674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_PLACES);
1947674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                addShownCategoryId(EmojiCategory.ID_SYMBOLS);
1957674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                if (canShowFlagEmoji()) {
1967674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                    addShownCategoryId(EmojiCategory.ID_FLAGS);
1977674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                }
1987674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            }
199a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        } else {
200a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov            addShownCategoryId(EmojiCategory.ID_SYMBOLS);
201ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
202ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        addShownCategoryId(EmojiCategory.ID_EMOTICONS);
20328e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov
20428e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov        DynamicGridKeyboard recentsKbd =
2057674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                getKeyboard(EmojiCategory.ID_RECENTS, 0 /* categoryPageId */);
20628e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov        recentsKbd.loadRecentKeys(mCategoryKeyboardMap.values());
20728e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov
20828e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov        mCurrentCategoryId = Settings.readLastShownEmojiCategoryId(mPrefs, defaultCategoryId);
209a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        Log.i(TAG, "Last Emoji category id is " + mCurrentCategoryId);
210a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        if (!isShownCategoryId(mCurrentCategoryId)) {
211a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov            Log.i(TAG, "Last emoji category " + mCurrentCategoryId +
212a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov                    " is invalid, starting in " + defaultCategoryId);
213a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov            mCurrentCategoryId = defaultCategoryId;
214a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        } else if (mCurrentCategoryId == EmojiCategory.ID_RECENTS &&
21528e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov                recentsKbd.getSortedKeys().isEmpty()) {
216a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov            Log.i(TAG, "No recent emojis found, starting in category " + defaultCategoryId);
21728e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov            mCurrentCategoryId = defaultCategoryId;
21828e65858d9f7e8f94e0b298275624703fd74af05Dmitry Torokhov        }
219ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
220ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
221ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private void addShownCategoryId(final int categoryId) {
222ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        // Load a keyboard of categoryId
2237674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        getKeyboard(categoryId, 0 /* categoryPageId */);
224ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final CategoryProperties properties =
225ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                new CategoryProperties(categoryId, getCategoryPageCount(categoryId));
226ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mShownCategories.add(properties);
227ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
228ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
229a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov    private boolean isShownCategoryId(final int categoryId) {
230a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        for (final CategoryProperties prop : mShownCategories) {
231a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov            if (prop.mCategoryId == categoryId) {
232a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov                return true;
233a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov            }
234a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        }
235a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov        return false;
236a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov    }
237a2a057a991d825c85dd07d4d353ddbca4f9ad92dDmitry Torokhov
2385f00fe09e9a611b647592188316e5999465df4d3Tadashi G. Takaoka    public static String getCategoryName(final int categoryId, final int categoryPageId) {
239ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return sCategoryName[categoryId] + "-" + categoryPageId;
240ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
241ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
242ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getCategoryId(final String name) {
243ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final String[] strings = name.split("-");
244ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return mCategoryNameToIdMap.get(strings[0]);
245ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
246ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
24727ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka    public int getCategoryTabIcon(final int categoryId) {
24827ca9f1bf17c1d275faed939d529f07bb57e28f3Tadashi G. Takaoka        return mCategoryTabIconId[categoryId];
249ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
250ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
251ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public String getAccessibilityDescription(final int categoryId) {
252ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return mRes.getString(sAccessibilityDescriptionResourceIdsForCategories[categoryId]);
253ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
254ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
255ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public ArrayList<CategoryProperties> getShownCategories() {
256ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return mShownCategories;
257ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
258ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
259ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getCurrentCategoryId() {
260ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return mCurrentCategoryId;
261ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
262ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
263ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getCurrentCategoryPageSize() {
264ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return getCategoryPageSize(mCurrentCategoryId);
265ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
266ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
267ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getCategoryPageSize(final int categoryId) {
268ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (final CategoryProperties prop : mShownCategories) {
269ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (prop.mCategoryId == categoryId) {
270ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return prop.mPageCount;
271ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
272ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
273ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        Log.w(TAG, "Invalid category id: " + categoryId);
274ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        // Should not reach here.
275ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return 0;
276ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
277ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
278ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public void setCurrentCategoryId(final int categoryId) {
279ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mCurrentCategoryId = categoryId;
280ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId);
281ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
282ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
283ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public void setCurrentCategoryPageId(final int id) {
284ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        mCurrentCategoryPageId = id;
285ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
286ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
287ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getCurrentCategoryPageId() {
288ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return mCurrentCategoryPageId;
289ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
290ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
291ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public void saveLastTypedCategoryPage() {
292ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        Settings.writeLastTypedEmojiCategoryPageId(
293ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                mPrefs, mCurrentCategoryId, mCurrentCategoryPageId);
294ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
295ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
296ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public boolean isInRecentTab() {
297ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return mCurrentCategoryId == EmojiCategory.ID_RECENTS;
298ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
299ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
300ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getTabIdFromCategoryId(final int categoryId) {
301ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (int i = 0; i < mShownCategories.size(); ++i) {
302ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (mShownCategories.get(i).mCategoryId == categoryId) {
303ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return i;
304ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
305ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
306ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        Log.w(TAG, "categoryId not found: " + categoryId);
307ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return 0;
308ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
309ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
310ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    // Returns the view pager's page position for the categoryId
311ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getPageIdFromCategoryId(final int categoryId) {
312ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final int lastSavedCategoryPageId =
313ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId);
314ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        int sum = 0;
315ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (int i = 0; i < mShownCategories.size(); ++i) {
316ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            final CategoryProperties props = mShownCategories.get(i);
317ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (props.mCategoryId == categoryId) {
318ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return sum + lastSavedCategoryPageId;
319ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
320ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            sum += props.mPageCount;
321ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
322ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        Log.w(TAG, "categoryId not found: " + categoryId);
323ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return 0;
324ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
325ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
326ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getRecentTabId() {
327ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return getTabIdFromCategoryId(EmojiCategory.ID_RECENTS);
328ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
329ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
330ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private int getCategoryPageCount(final int categoryId) {
331ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
332ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return (keyboard.getSortedKeys().size() - 1) / mMaxPageKeyCount + 1;
333ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
334ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
335ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    // Returns a pair of the category id and the category page id from the view pager's page
336ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    // position. The category page id is numbered in each category. And the view page position
337ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    // is the position of the current shown page in the view pager which contains all pages of
338ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    // all categories.
339ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(final int position) {
340ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        int sum = 0;
341ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (final CategoryProperties properties : mShownCategories) {
342ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            final int temp = sum;
343ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            sum += properties.mPageCount;
344ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (sum > position) {
345a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka                return new Pair<>(properties.mCategoryId, position - temp);
346ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
347ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
348ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return null;
349ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
350ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
351ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    // Returns a keyboard from the view pager's page position.
352ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public DynamicGridKeyboard getKeyboardFromPagePosition(final int position) {
353ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final Pair<Integer, Integer> categoryAndId =
354ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                getCategoryIdAndPageIdFromPagePosition(position);
355ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        if (categoryAndId != null) {
356ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            return getKeyboard(categoryAndId.first, categoryAndId.second);
357ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
358ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return null;
359ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
360ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
361ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) {
36223a357d71d5d1a404975375fd376618a4fd2a596Ken Wakasa        return (((long) categoryId) << Integer.SIZE) | id;
363ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
364ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
365ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) {
366ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        synchronized (mCategoryKeyboardMap) {
3677674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            final Long categoryKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id);
3687674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            if (mCategoryKeyboardMap.containsKey(categoryKeyboardMapKey)) {
3697674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                return mCategoryKeyboardMap.get(categoryKeyboardMapKey);
370ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
371ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
372ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (categoryId == EmojiCategory.ID_RECENTS) {
373ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs,
374ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                        mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
375ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                        mMaxPageKeyCount, categoryId);
3767674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen                mCategoryKeyboardMap.put(categoryKeyboardMapKey, kbd);
377ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return kbd;
378ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
379ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
380ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
381ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            final Key[][] sortedKeys = sortKeysIntoPages(
382ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                    keyboard.getSortedKeys(), mMaxPageKeyCount);
383ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            for (int pageId = 0; pageId < sortedKeys.length; ++pageId) {
384ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs,
385ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                        mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
386ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                        mMaxPageKeyCount, categoryId);
387ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                for (final Key emojiKey : sortedKeys[pageId]) {
388ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                    if (emojiKey == null) {
389ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                        break;
390ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                    }
391ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                    tempKeyboard.addKeyLast(emojiKey);
392ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                }
393ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                mCategoryKeyboardMap.put(
394ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                        getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard);
395ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
3967674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            return mCategoryKeyboardMap.get(categoryKeyboardMapKey);
397ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
398ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
399ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
400ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    public int getTotalPageCountOfAllCategories() {
401ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        int sum = 0;
402ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (CategoryProperties properties : mShownCategories) {
403ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            sum += properties.mPageCount;
404ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
405ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return sum;
406ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
407ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
408ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static Comparator<Key> EMOJI_KEY_COMPARATOR = new Comparator<Key>() {
409ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        @Override
410ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        public int compare(final Key lhs, final Key rhs) {
411ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            final Rect lHitBox = lhs.getHitBox();
412ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            final Rect rHitBox = rhs.getHitBox();
413ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (lHitBox.top < rHitBox.top) {
414ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return -1;
415ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            } else if (lHitBox.top > rHitBox.top) {
416ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return 1;
417ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
418ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (lHitBox.left < rHitBox.left) {
419ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return -1;
420ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            } else if (lHitBox.left > rHitBox.left) {
421ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return 1;
422ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
423ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            if (lhs.getCode() == rhs.getCode()) {
424ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka                return 0;
425ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            }
426ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            return lhs.getCode() < rhs.getCode() ? -1 : 1;
427ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
428ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    };
429ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka
430ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    private static Key[][] sortKeysIntoPages(final List<Key> inKeys, final int maxPageCount) {
431a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        final ArrayList<Key> keys = new ArrayList<>(inKeys);
432ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        Collections.sort(keys, EMOJI_KEY_COMPARATOR);
433ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final int pageCount = (keys.size() - 1) / maxPageCount + 1;
434ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        final Key[][] retval = new Key[pageCount][maxPageCount];
435ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        for (int i = 0; i < keys.size(); ++i) {
436ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka            retval[i / maxPageCount][i % maxPageCount] = keys.get(i);
437ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        }
438ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka        return retval;
439ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka    }
4407674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen
4417674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static boolean canShowFlagEmoji() {
4427674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        Paint paint = new Paint();
4437674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        String switzerland = "\uD83C\uDDE8\uD83C\uDDED"; //  U+1F1E8 U+1F1ED Flag for Switzerland
4447674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        try {
4457674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            return paint.hasGlyph(switzerland);
4467674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        } catch (NoSuchMethodError e) {
4477674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            // Compare display width of single-codepoint emoji to width of flag emoji to determine
4487674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            // whether flag is rendered as single glyph or two adjacent regional indicator symbols.
4497674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            float flagWidth = paint.measureText(switzerland);
4507674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            float standardWidth = paint.measureText("\uD83D\uDC27"); //  U+1F427 Penguin
4517674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            return flagWidth < standardWidth * 1.25;
4527674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            // This assumes that a valid glyph for the flag emoji must be less than 1.25 times
4537674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            // the width of the penguin.
4547674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        }
4557674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    }
4567674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen
4577674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    private static boolean canShowUnicodeEightEmoji() {
4587674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        Paint paint = new Paint();
4597674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        String cheese = "\uD83E\uDDC0"; //  U+1F9C0 Cheese wedge
4607674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        try {
4617674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            return paint.hasGlyph(cheese);
4627674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        } catch (NoSuchMethodError e) {
4637674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            float cheeseWidth = paint.measureText(cheese);
4647674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            float tofuWidth = paint.measureText("\uFFFE");
4657674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            return cheeseWidth > tofuWidth;
4667674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            // This assumes that a valid glyph for the cheese wedge must be greater than the width
4677674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen            // of the noncharacter.
4687674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen        }
4697674a653678b542d795b0bd05c241654ffe1015aChieu Nguyen    }
470ead058b00216339a8688c604886645fce42fee4aTadashi G. Takaoka}
471