1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.providers.contacts; 18 19 import com.android.providers.contacts.HanziToPinyin.Token; 20 21 import android.provider.ContactsContract.FullNameStyle; 22 import android.util.SparseArray; 23 24 import java.util.ArrayList; 25 import java.util.HashSet; 26 import java.util.Iterator; 27 import java.util.Locale; 28 29 /** 30 * This utility class provides customized sort key and name lookup key according the locale. 31 */ 32 public class ContactLocaleUtils { 33 34 /** 35 * This class is the default implementation. 36 * <p> 37 * It should be the base class for other locales' implementation. 38 */ 39 public class ContactLocaleUtilsBase { getSortKey(String displayName)40 public String getSortKey(String displayName) { 41 return displayName; 42 } 43 @SuppressWarnings("unused") getNameLookupKeys(String name)44 public Iterator<String> getNameLookupKeys(String name) { 45 return null; 46 } 47 } 48 49 /** 50 * The classes to generate the Chinese style sort and search keys. 51 * <p> 52 * The sorting key is generated as each Chinese character' pinyin proceeding with 53 * space and character itself. If the character's pinyin unable to find, the character 54 * itself will be used. 55 * <p> 56 * The below additional name lookup keys will be generated. 57 * a. Chinese character's pinyin and pinyin's initial character. 58 * b. Latin word and the initial character for Latin word. 59 * The name lookup keys are generated to make sure the name can be found by from any 60 * initial character. 61 */ 62 private class ChineseContactUtils extends ContactLocaleUtilsBase { 63 @Override getSortKey(String displayName)64 public String getSortKey(String displayName) { 65 ArrayList<Token> tokens = HanziToPinyin.getInstance().get(displayName); 66 if (tokens != null && tokens.size() > 0) { 67 StringBuilder sb = new StringBuilder(); 68 for (Token token : tokens) { 69 // Put Chinese character's pinyin, then proceed with the 70 // character itself. 71 if (Token.PINYIN == token.type) { 72 if (sb.length() > 0) { 73 sb.append(' '); 74 } 75 sb.append(token.target); 76 sb.append(' '); 77 sb.append(token.source); 78 } else { 79 if (sb.length() > 0) { 80 sb.append(' '); 81 } 82 sb.append(token.source); 83 } 84 } 85 return sb.toString(); 86 } 87 return super.getSortKey(displayName); 88 } 89 90 @Override getNameLookupKeys(String name)91 public Iterator<String> getNameLookupKeys(String name) { 92 // TODO : Reduce the object allocation. 93 HashSet<String> keys = new HashSet<String>(); 94 ArrayList<Token> tokens = HanziToPinyin.getInstance().get(name); 95 final int tokenCount = tokens.size(); 96 final StringBuilder keyPinyin = new StringBuilder(); 97 final StringBuilder keyInitial = new StringBuilder(); 98 // There is no space among the Chinese Characters, the variant name 99 // lookup key wouldn't work for Chinese. The keyOrignal is used to 100 // build the lookup keys for itself. 101 final StringBuilder keyOrignal = new StringBuilder(); 102 for (int i = tokenCount - 1; i >= 0; i--) { 103 final Token token = tokens.get(i); 104 if (Token.PINYIN == token.type) { 105 keyPinyin.insert(0, token.target); 106 keyInitial.insert(0, token.target.charAt(0)); 107 } else if (Token.LATIN == token.type) { 108 // Avoid adding space at the end of String. 109 if (keyPinyin.length() > 0) { 110 keyPinyin.insert(0, ' '); 111 } 112 if (keyOrignal.length() > 0) { 113 keyOrignal.insert(0, ' '); 114 } 115 keyPinyin.insert(0, token.source); 116 keyInitial.insert(0, token.source.charAt(0)); 117 } 118 keyOrignal.insert(0, token.source); 119 keys.add(keyOrignal.toString()); 120 keys.add(keyPinyin.toString()); 121 keys.add(keyInitial.toString()); 122 } 123 return keys.iterator(); 124 } 125 } 126 127 private static final String CHINESE_LANGUAGE = Locale.CHINESE.getLanguage().toLowerCase(); 128 private static final String JAPANESE_LANGUAGE = Locale.JAPANESE.getLanguage().toLowerCase(); 129 private static final String KOREAN_LANGUAGE = Locale.KOREAN.getLanguage().toLowerCase(); 130 131 private static ContactLocaleUtils sSingleton; 132 private final SparseArray<ContactLocaleUtilsBase> mUtils = 133 new SparseArray<ContactLocaleUtilsBase>(); 134 135 private final ContactLocaleUtilsBase mBase = new ContactLocaleUtilsBase(); 136 137 private String mLanguage; 138 ContactLocaleUtils()139 private ContactLocaleUtils() { 140 setLocale(null); 141 } 142 setLocale(Locale currentLocale)143 public void setLocale(Locale currentLocale) { 144 if (currentLocale == null) { 145 mLanguage = Locale.getDefault().getLanguage().toLowerCase(); 146 } else { 147 mLanguage = currentLocale.getLanguage().toLowerCase(); 148 } 149 } 150 getSortKey(String displayName, int nameStyle)151 public String getSortKey(String displayName, int nameStyle) { 152 return getForSort(Integer.valueOf(nameStyle)).getSortKey(displayName); 153 } 154 getNameLookupKeys(String name, int nameStyle)155 public Iterator<String> getNameLookupKeys(String name, int nameStyle) { 156 return getForNameLookup(Integer.valueOf(nameStyle)).getNameLookupKeys(name); 157 } 158 159 /** 160 * Determine which utility should be used for generating NameLookupKey. 161 * <p> 162 * a. For Western style name, if the current language is Chinese, the 163 * ChineseContactUtils should be used. 164 * b. For Chinese and CJK style name if current language is neither Japanese or Korean, 165 * the ChineseContactUtils should be used. 166 */ getForNameLookup(Integer nameStyle)167 private ContactLocaleUtilsBase getForNameLookup(Integer nameStyle) { 168 int nameStyleInt = nameStyle.intValue(); 169 Integer adjustedUtil = Integer.valueOf(getAdjustedStyle(nameStyleInt)); 170 if (CHINESE_LANGUAGE.equals(mLanguage) && nameStyleInt == FullNameStyle.WESTERN) { 171 adjustedUtil = Integer.valueOf(FullNameStyle.CHINESE); 172 } 173 return get(adjustedUtil); 174 } 175 get(Integer nameStyle)176 private synchronized ContactLocaleUtilsBase get(Integer nameStyle) { 177 ContactLocaleUtilsBase utils = mUtils.get(nameStyle); 178 if (utils == null) { 179 if (nameStyle.intValue() == FullNameStyle.CHINESE) { 180 utils = new ChineseContactUtils(); 181 mUtils.put(nameStyle, utils); 182 } 183 } 184 return (utils == null) ? mBase : utils; 185 } 186 187 /** 188 * Determine the which utility should be used for generating sort key. 189 * <p> 190 * For Chinese and CJK style name if current language is neither Japanese or Korean, 191 * the ChineseContactUtils should be used. 192 */ getForSort(Integer nameStyle)193 private ContactLocaleUtilsBase getForSort(Integer nameStyle) { 194 return get(Integer.valueOf(getAdjustedStyle(nameStyle.intValue()))); 195 } 196 getIntance()197 public static synchronized ContactLocaleUtils getIntance() { 198 if (sSingleton == null) { 199 sSingleton = new ContactLocaleUtils(); 200 } 201 return sSingleton; 202 } 203 getAdjustedStyle(int nameStyle)204 private int getAdjustedStyle(int nameStyle) { 205 if (nameStyle == FullNameStyle.CJK && !JAPANESE_LANGUAGE.equals(mLanguage) && 206 !KOREAN_LANGUAGE.equals(mLanguage)) { 207 return FullNameStyle.CHINESE; 208 } else { 209 return nameStyle; 210 } 211 } 212 } 213