再談Contacts中姓氏多音字排序錯誤問題

說到中國人的名字,那就不得不考慮多音字的問題,好比'單',在做爲姓氏時應該讀做'shan'而不是'dan'.可是在Contacts程序中卻使用的是'D'來做爲bucket label!
這是爲何?如何解決這種多音字姓氏的問題?

從4.3版本開始,HanziToPinyin.java(ContactsProvider中)改成直接調用ICU的Transliterator來對漢字進行transliterate(詳見類中mPinyinTransliterator變量的使用).咱們知道,ICU中的Han_Latin_Names.txt中保存了一些漢字在做爲姓氏時的讀音,若是在建立Transliterator對象時使用Han-Latin/Names做爲id(可參考mPinyinTransliterator變量的初始化),將能夠正確的得到漢字做爲姓氏時的讀音(僅限於Han_Latin_Names.txt中包含的漢字).也就是說,HanziToPinyin這個類在處理做爲姓氏的多音字時是能夠得到正確得到其讀音的.如在使用HanziToPinyin獲取'單田芳'的讀音爲'shan tian fang'.就算這樣姓名爲'單田芳'的聯繫人依然被排在'D'下面!這是爲何呢?
經過解析代碼能夠發現,聯繫人排在哪一個字母下面是由raw_contacts表中的phonebook_label的值來決定的,而數據庫中phonebook_label的值確實是'D',主要是由於在得到phonebook_label時使用的是AlphabeticIndex$ImmutableIndex的getBucketLabel函數,而這個函數是沒有對姓氏多音字作任何特殊處理的,因此最後獲得的是'D'(phonebook_label的處理流程見附一).

目前,若是想比較省力的解決這個問題,能夠在獲取phonebook_label時避免經過使用AlphabeticIndex$ImmutableIndex的getBucketLabel函數來獲取,而是改成用HanziToPinyin去得到sortKeyPrimary的第一個字符的拼音(sortKeyAlternative暫不考慮).

貼一下代碼修改(基於4.4.4_r2)
-----------------------------------------------------------------------------
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -78,6 +78,7 @@ import android.text.util.Rfc822Tokenizer;
 import android.util.Log;
 
 import com.android.common.content.SyncStateContentProviderHelper;
+import com.android.providers.contacts.HanziToPinyin.Token;
 import com.android.providers.contacts.aggregation.util.CommonNicknameCache;
 import com.android.providers.contacts.database.ContactsTableUtil;
 import com.android.providers.contacts.database.DeletedContactsTableUtil;
@@ -85,6 +86,7 @@ import com.android.providers.contacts.database.MoreDatabaseUtils;
 import com.android.providers.contacts.util.NeededForTesting;
 import com.google.android.collect.Sets;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Set;
@@ -5388,8 +5390,25 @@ public class ContactsDatabaseHelper extends SQLiteOpenHelper {
         ContactLocaleUtils localeUtils = ContactLocaleUtils.getInstance();
 
         if (sortKeyPrimary != null) {
-            phonebookBucketPrimary = localeUtils.getBucketIndex(sortKeyPrimary);
-            phonebookLabelPrimary = localeUtils.getBucketLabel(phonebookBucketPrimary);
+            boolean flag = false;
+            if (displayNameStyle == FullNameStyle.CHINESE
+                    && HanziToPinyin.getInstance().hasChineseTransliterator()) {
+                String target = sortKeyPrimary.substring(0, 1);
+                ArrayList<Token> tokens = HanziToPinyin.getInstance().get(target);
+                if (tokens != null && tokens.size() > 0) {
+                    char label = tokens.get(0).target.charAt(0);
+                    if (label >= 'A' && label <= 'Z') {
+                        phonebookLabelPrimary = String.valueOf(label);
+                        phonebookBucketPrimary = localeUtils.getBucketIndex(phonebookLabelPrimary);
+                        flag = true;
+                    }
+                }
+            }
+
+            if (!flag) {
+                phonebookBucketPrimary = localeUtils.getBucketIndex(sortKeyPrimary);
+                phonebookLabelPrimary = localeUtils.getBucketLabel(phonebookBucketPrimary);
+            }
         }
         if (sortKeyAlternative != null) {
             phonebookBucketAlternative = localeUtils.getBucketIndex(sortKeyAlternative);java

-----------------------------------------------------------------------------
經過上面的分析能夠發現一個問題,那就是在Contacts主頁面用'dan tian fang'搜索聯繫人時是搜不到的,只能用'shan tian fang'來搜索,可是在顯示時確是將該聯繫人放在了'D'分組下面!根本緣由就是NameLookupBuilder在調用 appendNameShorthandLookup時實際上使用了HanziToPinyin來獲取讀音,這時獲得的是做爲姓氏時的讀音,而 phonebook_label是使用AlphabeticIndex$ImmutableIndex的getBucketLabel函數來獲取的,而它 沒有對姓氏多音字作任何特殊處理,這就致使了前面說的問題.
另外,用HanziToPinyin的一個問題是全部的可做爲姓氏的漢字的讀音都是姓氏的讀音,如'單位單'的拼音是'shan wei shan'.如何解決這個問題呢?其實現有條件下沒有一個好的辦法(加詞庫的方法除外),只能儘可能規避.個人方法是中文狀況下名字中的第一個漢字做爲姓氏去獲取讀音(使用Han-Latin/Names; Latin-Ascii; Any-Upper建立的Transliterator對象),而其它漢字則做爲普通漢字去獲取讀音(使用Han-Latin; Latin-Ascii; Any-Upper建立的Transliterator對象,需在HanziToPinyin中再新建一個Transliterator對象).

附一
raw_contacts表中的phonebook_label的獲取流程:
raw_contacts表中的phonebook_label是經過調用ContactsDatabaseHelper的updateRawContactDisplayName函數來完成插入/更新的.使用的是phonebookLabelPrimary變量的值.而phonebookLabelPrimary是ContactLocaleUtils中getBucketLabel得到的,getBucketLabel中實際調用的是ICU中ImmutableIndex的getBucketLabel來完成的().android

相關文章
相關標籤/搜索