Home | History | Annotate | Download | only in contacts
      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 android.provider.ContactsContract.FullNameStyle;
     20 import android.util.SparseArray;
     21 
     22 import com.android.providers.contacts.HanziToPinyin.Token;
     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 {
     40         public String getSortKey(String displayName) {
     41             return displayName;
     42         }
     43         @SuppressWarnings("unused")
     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
     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
     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 
    139     private ContactLocaleUtils() {
    140         setLocale(null);
    141     }
    142 
    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 
    151     public String getSortKey(String displayName, int nameStyle) {
    152         return getForSort(Integer.valueOf(nameStyle)).getSortKey(displayName);
    153     }
    154 
    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      */
    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 
    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      */
    193     private ContactLocaleUtilsBase getForSort(Integer nameStyle) {
    194         return get(Integer.valueOf(getAdjustedStyle(nameStyle.intValue())));
    195     }
    196 
    197     public static synchronized ContactLocaleUtils getIntance() {
    198         if (sSingleton == null) {
    199             sSingleton = new ContactLocaleUtils();
    200         }
    201         return sSingleton;
    202     }
    203 
    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