14cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao/* 24cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * Copyright (C) 2010 The Android Open Source Project 34cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * 44cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * Licensed under the Apache License, Version 2.0 (the "License"); 54cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * you may not use this file except in compliance with the License. 64cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * You may obtain a copy of the License at 74cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * 84cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * http://www.apache.org/licenses/LICENSE-2.0 94cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * 104cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * Unless required by applicable law or agreed to in writing, software 114cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * distributed under the License is distributed on an "AS IS" BASIS, 124cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * See the License for the specific language governing permissions and 144cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * limitations under the License 154cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao */ 164cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 174cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taopackage com.android.providers.contacts; 184cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 1971340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawaimport android.provider.ContactsContract.FullNameStyle; 20dd1cc45e540b41e9a4b824410ca792dd8360b70eDaniel Lehmannimport android.util.SparseArray; 2171340347b4862d4b1368a5d69d1667e2245952e4Daisuke Miyakawa 2238210445730ee04c351c7cc1b3800cfe23e34325Makoto Onukiimport com.android.providers.contacts.HanziToPinyin.Token; 2338210445730ee04c351c7cc1b3800cfe23e34325Makoto Onuki 244cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taoimport java.util.ArrayList; 254cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taoimport java.util.HashSet; 264cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taoimport java.util.Iterator; 27cdd03b2ba03718a7fa85663a2438136284a1557cBai Taoimport java.util.Locale; 284cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 294cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao/** 304cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * This utility class provides customized sort key and name lookup key according the locale. 314cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao */ 324cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Taopublic class ContactLocaleUtils { 334cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 344cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao /** 354cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * This class is the default implementation. 364cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * <p> 374cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao * It should be the base class for other locales' implementation. 384cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao */ 39cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao public class ContactLocaleUtilsBase { 404cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao public String getSortKey(String displayName) { 414cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao return displayName; 424cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 43dd1cc45e540b41e9a4b824410ca792dd8360b70eDaniel Lehmann @SuppressWarnings("unused") 444cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao public Iterator<String> getNameLookupKeys(String name) { 454cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao return null; 464cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 474cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 484cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 49cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao /** 50cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * The classes to generate the Chinese style sort and search keys. 51cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * <p> 52cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * The sorting key is generated as each Chinese character' pinyin proceeding with 53cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * space and character itself. If the character's pinyin unable to find, the character 54cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * itself will be used. 55cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * <p> 56cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * The below additional name lookup keys will be generated. 57cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * a. Chinese character's pinyin and pinyin's initial character. 58cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * b. Latin word and the initial character for Latin word. 59cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * The name lookup keys are generated to make sure the name can be found by from any 60cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * initial character. 61cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao */ 62cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private class ChineseContactUtils extends ContactLocaleUtilsBase { 634cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao @Override 644cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao public String getSortKey(String displayName) { 654cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao ArrayList<Token> tokens = HanziToPinyin.getInstance().get(displayName); 664cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (tokens != null && tokens.size() > 0) { 674cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao StringBuilder sb = new StringBuilder(); 684cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao for (Token token : tokens) { 694cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // Put Chinese character's pinyin, then proceed with the 704cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // character itself. 714cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (Token.PINYIN == token.type) { 724cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (sb.length() > 0) { 734cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao sb.append(' '); 744cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 754cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao sb.append(token.target); 764cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao sb.append(' '); 774cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao sb.append(token.source); 784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } else { 794cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (sb.length() > 0) { 804cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao sb.append(' '); 814cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 824cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao sb.append(token.source); 834cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 844cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 854cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao return sb.toString(); 864cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 874cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao return super.getSortKey(displayName); 884cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 894cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 904cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao @Override 914cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao public Iterator<String> getNameLookupKeys(String name) { 924cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // TODO : Reduce the object allocation. 934cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao HashSet<String> keys = new HashSet<String>(); 944cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao ArrayList<Token> tokens = HanziToPinyin.getInstance().get(name); 954cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao final int tokenCount = tokens.size(); 964cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao final StringBuilder keyPinyin = new StringBuilder(); 974cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao final StringBuilder keyInitial = new StringBuilder(); 984cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // There is no space among the Chinese Characters, the variant name 994cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // lookup key wouldn't work for Chinese. The keyOrignal is used to 1004cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // build the lookup keys for itself. 1014cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao final StringBuilder keyOrignal = new StringBuilder(); 1024cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao for (int i = tokenCount - 1; i >= 0; i--) { 1034cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao final Token token = tokens.get(i); 1044cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (Token.PINYIN == token.type) { 1054cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyPinyin.insert(0, token.target); 1064cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyInitial.insert(0, token.target.charAt(0)); 1074cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } else if (Token.LATIN == token.type) { 1084cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao // Avoid adding space at the end of String. 1094cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (keyPinyin.length() > 0) { 1104cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyPinyin.insert(0, ' '); 1114cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1124cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (keyOrignal.length() > 0) { 1134cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyOrignal.insert(0, ' '); 1144cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1154cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyPinyin.insert(0, token.source); 1164cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyInitial.insert(0, token.source.charAt(0)); 1174cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1184cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keyOrignal.insert(0, token.source); 1194cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keys.add(keyOrignal.toString()); 1204cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keys.add(keyPinyin.toString()); 1214cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao keys.add(keyInitial.toString()); 1224cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1234cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao return keys.iterator(); 1244cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1254cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1264cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 127cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private static final String CHINESE_LANGUAGE = Locale.CHINESE.getLanguage().toLowerCase(); 128cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private static final String JAPANESE_LANGUAGE = Locale.JAPANESE.getLanguage().toLowerCase(); 129cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private static final String KOREAN_LANGUAGE = Locale.KOREAN.getLanguage().toLowerCase(); 130cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 131cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private static ContactLocaleUtils sSingleton; 132dd1cc45e540b41e9a4b824410ca792dd8360b70eDaniel Lehmann private final SparseArray<ContactLocaleUtilsBase> mUtils = 133dd1cc45e540b41e9a4b824410ca792dd8360b70eDaniel Lehmann new SparseArray<ContactLocaleUtilsBase>(); 134cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 135dd1cc45e540b41e9a4b824410ca792dd8360b70eDaniel Lehmann private final ContactLocaleUtilsBase mBase = new ContactLocaleUtilsBase(); 136cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 137cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private String mLanguage; 138cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 139cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private ContactLocaleUtils() { 140cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao setLocale(null); 141cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 142cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 143cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao public void setLocale(Locale currentLocale) { 144cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao if (currentLocale == null) { 145cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao mLanguage = Locale.getDefault().getLanguage().toLowerCase(); 146cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } else { 147cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao mLanguage = currentLocale.getLanguage().toLowerCase(); 148cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 149cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 1504cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 151cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao public String getSortKey(String displayName, int nameStyle) { 152cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return getForSort(Integer.valueOf(nameStyle)).getSortKey(displayName); 1534cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1544cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 155cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao public Iterator<String> getNameLookupKeys(String name, int nameStyle) { 156cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return getForNameLookup(Integer.valueOf(nameStyle)).getNameLookupKeys(name); 1574cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1584cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao 159cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao /** 160cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * Determine which utility should be used for generating NameLookupKey. 161cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * <p> 162cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * a. For Western style name, if the current language is Chinese, the 163cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * ChineseContactUtils should be used. 164cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * b. For Chinese and CJK style name if current language is neither Japanese or Korean, 165cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * the ChineseContactUtils should be used. 166cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao */ 167cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private ContactLocaleUtilsBase getForNameLookup(Integer nameStyle) { 168cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao int nameStyleInt = nameStyle.intValue(); 169cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao Integer adjustedUtil = Integer.valueOf(getAdjustedStyle(nameStyleInt)); 170cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao if (CHINESE_LANGUAGE.equals(mLanguage) && nameStyleInt == FullNameStyle.WESTERN) { 171cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao adjustedUtil = Integer.valueOf(FullNameStyle.CHINESE); 172cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 173cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return get(adjustedUtil); 174cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 175cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 176cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private synchronized ContactLocaleUtilsBase get(Integer nameStyle) { 1774cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao ContactLocaleUtilsBase utils = mUtils.get(nameStyle); 1784cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (utils == null) { 1794cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao if (nameStyle.intValue() == FullNameStyle.CHINESE) { 1804cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao utils = new ChineseContactUtils(); 1814cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao mUtils.put(nameStyle, utils); 1824cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 1834cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 184dd1cc45e540b41e9a4b824410ca792dd8360b70eDaniel Lehmann return (utils == null) ? mBase : utils; 1854cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao } 186cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 187cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao /** 188cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * Determine the which utility should be used for generating sort key. 189cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * <p> 190cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * For Chinese and CJK style name if current language is neither Japanese or Korean, 191cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao * the ChineseContactUtils should be used. 192cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao */ 193cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private ContactLocaleUtilsBase getForSort(Integer nameStyle) { 194cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return get(Integer.valueOf(getAdjustedStyle(nameStyle.intValue()))); 195cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 196cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 197cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao public static synchronized ContactLocaleUtils getIntance() { 198cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao if (sSingleton == null) { 199cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao sSingleton = new ContactLocaleUtils(); 200cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 201cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return sSingleton; 202cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 203cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao 204cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao private int getAdjustedStyle(int nameStyle) { 205cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao if (nameStyle == FullNameStyle.CJK && !JAPANESE_LANGUAGE.equals(mLanguage) && 206cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao !KOREAN_LANGUAGE.equals(mLanguage)) { 207cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return FullNameStyle.CHINESE; 208cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } else { 209cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao return nameStyle; 210cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 211cdd03b2ba03718a7fa85663a2438136284a1557cBai Tao } 2124cd13c4266d8e476e1a49c4b6bcd5b18c33d0de3Bai Tao} 213