Merge "Provider side changes for exposing data usage stats" into jb-mr2-dev
diff --git a/src/com/android/providers/contacts/ContactLocaleUtils.java b/src/com/android/providers/contacts/ContactLocaleUtils.java
index b53dab4..c63f7fc 100644
--- a/src/com/android/providers/contacts/ContactLocaleUtils.java
+++ b/src/com/android/providers/contacts/ContactLocaleUtils.java
@@ -17,6 +17,8 @@
package com.android.providers.contacts;
import android.provider.ContactsContract.FullNameStyle;
+import android.provider.ContactsContract.PhoneticNameStyle;
+import android.text.TextUtils;
import android.util.Log;
import com.android.providers.contacts.HanziToPinyin.Token;
@@ -35,6 +37,7 @@
import libcore.icu.AlphabeticIndex;
import libcore.icu.AlphabeticIndex.ImmutableIndex;
+import libcore.icu.Transliterator;
/**
* This utility class provides specialized handling for locale specific
@@ -43,6 +46,13 @@
public class ContactLocaleUtils {
public static final String TAG = "ContactLocale";
+ public static final Locale LOCALE_ARABIC = new Locale("ar");
+ public static final Locale LOCALE_GREEK = new Locale("el");
+ public static final Locale LOCALE_HEBREW = new Locale("he");
+ // Ukrainian labels are superset of Russian
+ public static final Locale LOCALE_UKRAINIAN = new Locale("uk");
+ public static final Locale LOCALE_THAI = new Locale("th");
+
/**
* This class is the default implementation and should be the base class
* for other locales.
@@ -61,8 +71,28 @@
private final int mNumberBucketIndex;
public ContactLocaleUtilsBase(Locale locale) {
+ // AlphabeticIndex.getBucketLabel() uses a binary search across
+ // the entire label set so care should be taken about growing this
+ // set too large. The following set determines for which locales
+ // we will show labels other than your primary locale. General rules
+ // of thumb for adding a locale: should be a supported locale; and
+ // should not be included if from a name it is not deterministic
+ // which way to label it (so eg Chinese cannot be added because
+ // the labeling of a Chinese character varies between Simplified,
+ // Traditional, and Japanese locales). Use English only for all
+ // Latin based alphabets. Ukrainian is chosen for Cyrillic because
+ // its alphabet is a superset of Russian.
mAlphabeticIndex = new AlphabeticIndex(locale)
- .addLabels(Locale.US).getImmutableIndex();
+ .setMaxLabelCount(300)
+ .addLabels(Locale.ENGLISH)
+ .addLabels(Locale.JAPANESE)
+ .addLabels(Locale.KOREAN)
+ .addLabels(LOCALE_THAI)
+ .addLabels(LOCALE_ARABIC)
+ .addLabels(LOCALE_HEBREW)
+ .addLabels(LOCALE_GREEK)
+ .addLabels(LOCALE_UKRAINIAN)
+ .getImmutableIndex();
mAlphabeticIndexBucketCount = mAlphabeticIndex.getBucketCount();
mNumberBucketIndex = mAlphabeticIndexBucketCount - 1;
}
@@ -138,7 +168,7 @@
}
@SuppressWarnings("unused")
- public Iterator<String> getNameLookupKeys(String name) {
+ public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
return null;
}
@@ -248,11 +278,56 @@
}
return super.getBucketLabel(bucketIndex);
}
+
+ @Override
+ public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
+ // Hiragana and Katakana will be positively identified as Japanese.
+ if (nameStyle == PhoneticNameStyle.JAPANESE) {
+ return getRomajiNameLookupKeys(name);
+ }
+ return null;
+ }
+
+ private static boolean mInitializedTransliterator;
+ private static Transliterator mJapaneseTransliterator;
+
+ private static Transliterator getJapaneseTransliterator() {
+ synchronized(JapaneseContactUtils.class) {
+ if (!mInitializedTransliterator) {
+ mInitializedTransliterator = true;
+ Transliterator t = null;
+ try {
+ t = new Transliterator("Hiragana-Latin; Katakana-Latin;"
+ + " Latin-Ascii");
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Hiragana/Katakana-Latin transliterator data"
+ + " is missing");
+ }
+ mJapaneseTransliterator = t;
+ }
+ return mJapaneseTransliterator;
+ }
+ }
+
+ public static Iterator<String> getRomajiNameLookupKeys(String name) {
+ final Transliterator t = getJapaneseTransliterator();
+ if (t == null) {
+ return null;
+ }
+ final String romajiName = t.transliterate(name);
+ if (TextUtils.isEmpty(romajiName) ||
+ TextUtils.equals(name, romajiName)) {
+ return null;
+ }
+ final HashSet<String> keys = new HashSet<String>();
+ keys.add(romajiName);
+ return keys.iterator();
+ }
}
/**
- * Chinese specific locale overrides. Uses ICU Transliterator for
- * generating pinyin transliteration.
+ * Simplified Chinese specific locale overrides. Uses ICU Transliterator
+ * for generating pinyin transliteration.
*
* sortKey: unchanged (same as name)
* nameLookupKeys: adds additional name lookup keys
@@ -260,17 +335,20 @@
* - Latin word and initial character.
* labels: unchanged
* Simplified Chinese labels are the same as English: [A-Z], #, " "
- * Traditional Chinese labels are stroke count, then English labels:
- * [1-33, 35, 36, 48]劃, [A-Z], #, " "
*/
- private static class ChineseContactUtils extends ContactLocaleUtilsBase {
- public ChineseContactUtils(Locale locale) {
+ private static class SimplifiedChineseContactUtils
+ extends ContactLocaleUtilsBase {
+ public SimplifiedChineseContactUtils(Locale locale) {
super(locale);
}
@Override
- public Iterator<String> getNameLookupKeys(String name) {
- return getPinyinNameLookupKeys(name);
+ public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
+ if (nameStyle != FullNameStyle.JAPANESE &&
+ nameStyle != FullNameStyle.KOREAN) {
+ return getPinyinNameLookupKeys(name);
+ }
+ return null;
}
public static Iterator<String> getPinyinNameLookupKeys(String name) {
@@ -286,6 +364,9 @@
final StringBuilder keyOriginal = new StringBuilder();
for (int i = tokenCount - 1; i >= 0; i--) {
final Token token = tokens.get(i);
+ if (Token.UNKNOWN == token.type) {
+ continue;
+ }
if (Token.PINYIN == token.type) {
keyPinyin.insert(0, token.target);
keyInitial.insert(0, token.target.charAt(0));
@@ -328,8 +409,8 @@
mLanguage = mLocale.getLanguage().toLowerCase();
if (mLanguage.equals(JAPANESE_LANGUAGE)) {
mUtils = new JapaneseContactUtils(mLocale);
- } else if (mLanguage.equals(CHINESE_LANGUAGE)) {
- mUtils = new ChineseContactUtils(mLocale);
+ } else if (mLocale.equals(Locale.CHINA)) {
+ mUtils = new SimplifiedChineseContactUtils(mLocale);
} else {
mUtils = new ContactLocaleUtilsBase(mLocale);
}
@@ -382,22 +463,21 @@
* Determine which utility should be used for generating NameLookupKey.
* (ie, whether we generate Pinyin lookup keys or not)
*
- * a. For unclassified CJK name, if current locale language is neither
- * Japanese nor Korean, use ChineseContactUtils.
- * b. If we're sure this is a Chinese name, always use ChineseContactUtils.
- * c. Otherwise, use whichever ContactUtils are appropriate for the locale
- * (so, Western names in Chinese locale will use ChineseContactUtils)
+ * Hiragana and Katakana are tagged as JAPANESE; Kanji is unclassified
+ * and tagged as CJK. For Hiragana/Katakana names, generate Romaji
+ * lookup keys when not in a Chinese or Korean locale.
+ *
+ * Otherwise, use the default behavior of that locale:
+ * a. For Japan, generate Romaji lookup keys for Hiragana/Katakana.
+ * b. For Simplified Chinese locale, generate Pinyin lookup keys.
*/
public Iterator<String> getNameLookupKeys(String name, int nameStyle) {
- if (nameStyle == FullNameStyle.CJK &&
- !JAPANESE_LANGUAGE.equals(mLanguage) &&
- !KOREAN_LANGUAGE.equals(mLanguage)) {
- return ChineseContactUtils.getPinyinNameLookupKeys(name);
+ if (nameStyle == FullNameStyle.JAPANESE &&
+ !CHINESE_LANGUAGE.equals(mLanguage) &&
+ !KOREAN_LANGUAGE.equals(mLanguage)) {
+ return JapaneseContactUtils.getRomajiNameLookupKeys(name);
}
- if (nameStyle == FullNameStyle.CHINESE) {
- return ChineseContactUtils.getPinyinNameLookupKeys(name);
- }
- return mUtils.getNameLookupKeys(name);
+ return mUtils.getNameLookupKeys(name, nameStyle);
}
}
diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
index 260d05f..25b8a00 100644
--- a/src/com/android/providers/contacts/ContactsDatabaseHelper.java
+++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java
@@ -109,7 +109,7 @@
* 700-799 Jelly Bean
* </pre>
*/
- static final int DATABASE_VERSION = 708;
+ static final int DATABASE_VERSION = 709;
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -996,6 +996,9 @@
RawContacts.DISPLAY_NAME_SOURCE + " INTEGER NOT NULL DEFAULT " +
DisplayNameSources.UNDEFINED + "," +
RawContacts.PHONETIC_NAME + " TEXT," +
+ // TODO: PHONETIC_NAME_STYLE should be INTEGER. There is a
+ // mismatch between how the column is created here (TEXT) and
+ // how it is created in upgradeToVersion205 (INTEGER).
RawContacts.PHONETIC_NAME_STYLE + " TEXT," +
RawContacts.SORT_KEY_PRIMARY + " TEXT COLLATE " +
ContactsProvider2.PHONEBOOK_COLLATOR_NAME + "," +
@@ -2453,6 +2456,12 @@
upgradeLocaleSpecificData = true;
oldVersion = 708;
}
+ if (oldVersion < 709) {
+ // Added secondary locale phonebook labels; changed Japanese
+ // and Chinese sort keys.
+ upgradeLocaleSpecificData = true;
+ oldVersion = 709;
+ }
if (upgradeViewsAndTriggers) {
createContactsViews(db);
@@ -5271,6 +5280,7 @@
bestPhoneticNameStyle = mNameSplitter.guessPhoneticNameStyle(bestPhoneticName);
}
} else {
+ bestPhoneticNameStyle = PhoneticNameStyle.UNDEFINED;
if (displayNameStyle == FullNameStyle.UNDEFINED) {
displayNameStyle = mNameSplitter.guessFullNameStyle(bestDisplayName);
if (displayNameStyle == FullNameStyle.UNDEFINED
diff --git a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
index 01ee1ba..8781d2f 100644
--- a/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
+++ b/src/com/android/providers/contacts/DataRowHandlerForStructuredName.java
@@ -21,6 +21,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.FullNameStyle;
+import android.provider.ContactsContract.PhoneticNameStyle;
import android.text.TextUtils;
import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
@@ -141,6 +142,7 @@
name.fromValues(augmented);
// As the name could be changed, let's guess the name style again.
name.fullNameStyle = FullNameStyle.UNDEFINED;
+ name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
mSplitter.guessNameStyle(name);
int unadjustedFullNameStyle = name.fullNameStyle;
name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
@@ -155,8 +157,11 @@
mSplitter.guessFullNameStyle(unstruct));
}
if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
- update.put(StructuredName.PHONETIC_NAME_STYLE,
- mSplitter.guessPhoneticNameStyle(unstruct));
+ NameSplitter.Name name = new NameSplitter.Name();
+ name.fromValues(update);
+ name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
+ mSplitter.guessNameStyle(name);
+ update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
}
}
}
@@ -219,7 +224,14 @@
builder.appendName(phoneticGiven);
mSb.append(phoneticGiven);
}
- builder.appendName(mSb.toString().trim());
+ final String phoneticName = mSb.toString().trim();
+ int phoneticNameStyle = builder.getInt(StructuredName.PHONETIC_NAME_STYLE);
+ if (phoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
+ phoneticNameStyle = mSplitter.guessPhoneticNameStyle(phoneticName);
+ }
+ builder.appendName(phoneticName);
+ mNameLookupBuilder.appendNameShorthandLookup(builder, phoneticName,
+ phoneticNameStyle);
}
}
}
diff --git a/src/com/android/providers/contacts/HanziToPinyin.java b/src/com/android/providers/contacts/HanziToPinyin.java
index 161bb58..0c35e21 100644
--- a/src/com/android/providers/contacts/HanziToPinyin.java
+++ b/src/com/android/providers/contacts/HanziToPinyin.java
@@ -34,7 +34,8 @@
private static final String TAG = "HanziToPinyin";
private static HanziToPinyin sInstance;
- private final Transliterator mPinyinTransliterator;
+ private Transliterator mPinyinTransliterator;
+ private Transliterator mAsciiTransliterator;
public static class Token {
/**
@@ -71,14 +72,13 @@
}
private HanziToPinyin() {
- Transliterator t = null;
try {
- t = new Transliterator("Han-Latin/Names; Latin-Ascii; Any-Upper");
+ mPinyinTransliterator = new Transliterator("Han-Latin/Names; Latin-Ascii; Any-Upper");
+ mAsciiTransliterator = new Transliterator("Latin-Ascii");
} catch (RuntimeException e) {
Log.w(TAG, "Han-Latin/Names transliterator data is missing,"
+ " HanziToPinyin is disabled");
}
- mPinyinTransliterator = t;
}
public boolean hasChineseTransliterator() {
@@ -94,14 +94,22 @@
}
}
- private Token getToken(char character) {
- Token token = new Token();
+ private void tokenize(char character, Token token) {
token.source = Character.toString(character);
- if (character < 256) {
+ // ASCII
+ if (character < 128) {
token.type = Token.LATIN;
token.target = token.source;
- return token;
+ return;
+ }
+
+ // Extended Latin. Transcode these to ASCII equivalents
+ if (character < 0x250 || (0x1e00 <= character && character < 0x1eff)) {
+ token.type = Token.LATIN;
+ token.target = mAsciiTransliterator == null ? token.source :
+ mAsciiTransliterator.transliterate(token.source);
+ return;
}
token.type = Token.PINYIN;
@@ -111,7 +119,6 @@
token.type = Token.UNKNOWN;
token.target = token.source;
}
- return token;
}
/**
@@ -125,40 +132,37 @@
// return empty tokens.
return tokens;
}
+
final int inputLength = input.length();
final StringBuilder sb = new StringBuilder();
int tokenType = Token.LATIN;
+ Token token = new Token();
+
// Go through the input, create a new token when
// a. Token type changed
// b. Get the Pinyin of current charater.
// c. current character is space.
for (int i = 0; i < inputLength; i++) {
final char character = input.charAt(i);
- if (character == ' ') {
+ if (Character.isSpaceChar(character)) {
if (sb.length() > 0) {
addToken(sb, tokens, tokenType);
}
- } else if (character < 256) {
- if (tokenType != Token.LATIN && sb.length() > 0) {
- addToken(sb, tokens, tokenType);
- }
- tokenType = Token.LATIN;
- sb.append(character);
} else {
- Token t = getToken(character);
- if (t.type == Token.PINYIN) {
+ tokenize(character, token);
+ if (token.type == Token.PINYIN) {
if (sb.length() > 0) {
addToken(sb, tokens, tokenType);
}
- tokens.add(t);
- tokenType = Token.PINYIN;
+ tokens.add(token);
+ token = new Token();
} else {
- if (tokenType != t.type && sb.length() > 0) {
+ if (tokenType != token.type && sb.length() > 0) {
addToken(sb, tokens, tokenType);
}
- tokenType = t.type;
- sb.append(character);
+ sb.append(token.target);
}
+ tokenType = token.type;
}
}
if (sb.length() > 0) {
diff --git a/src/com/android/providers/contacts/NameLookupBuilder.java b/src/com/android/providers/contacts/NameLookupBuilder.java
index 67dae46..fb266da 100644
--- a/src/com/android/providers/contacts/NameLookupBuilder.java
+++ b/src/com/android/providers/contacts/NameLookupBuilder.java
@@ -319,7 +319,12 @@
}
}
- private void appendNameShorthandLookup(IndexBuilder builder, String name, int fullNameStyle) {
+ /**
+ * Insert more name indexes according to locale specifies for those locales
+ * for which we have alternative shorthand name methods (eg, Pinyin for
+ * Chinese, Romaji for Japanese).
+ */
+ public void appendNameShorthandLookup(IndexBuilder builder, String name, int fullNameStyle) {
Iterator<String> it =
ContactLocaleUtils.getInstance().getNameLookupKeys(name, fullNameStyle);
if (it != null) {
diff --git a/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java b/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java
index 637c4e3..ea4ef0f 100644
--- a/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java
+++ b/tests/src/com/android/providers/contacts/ContactLocaleUtilsTest.java
@@ -25,6 +25,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Locale;
@SmallTest
@@ -160,9 +161,7 @@
assertEquals("D", getLabel(CHINESE_LATIN_MIX_NAME_1));
assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK));
- Iterator<String> keys = getNameLookupKeys(CHINESE_NAME,
- FullNameStyle.CHINESE);
- verifyKeys(keys, CHINESE_NAME_KEY);
+ assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE));
// Following two tests are broken with ICU 50
verifyLabels(getLabels(), LABELS_JA_JP);
@@ -213,14 +212,12 @@
}
ContactLocaleUtils.setLocale(Locale.ENGLISH);
- Iterator<String> keys = getNameLookupKeys(CHINESE_NAME,
- FullNameStyle.CHINESE);
- verifyKeys(keys, CHINESE_NAME_KEY);
- keys = getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK);
- verifyKeys(keys, CHINESE_NAME_KEY);
+ assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CHINESE));
+ assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK));
- ContactLocaleUtils.setLocale(Locale.CHINESE);
- keys = getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK);
+ ContactLocaleUtils.setLocale(Locale.CHINA);
+ Iterator<String> keys = getNameLookupKeys(CHINESE_NAME,
+ FullNameStyle.CJK);
verifyKeys(keys, CHINESE_NAME_KEY);
keys = getNameLookupKeys(LATIN_NAME, FullNameStyle.WESTERN);
verifyKeys(keys, LATIN_NAME_KEY);
@@ -228,8 +225,7 @@
verifyKeys(keys, LATIN_NAME_KEY_2);
ContactLocaleUtils.setLocale(Locale.TRADITIONAL_CHINESE);
- keys = getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK);
- verifyKeys(keys, CHINESE_NAME_KEY);
+ assertNull(getNameLookupKeys(CHINESE_NAME, FullNameStyle.CJK));
}
public void testKoreanContactLocaleUtils() throws Exception {
@@ -278,10 +274,14 @@
assertEquals(new HashSet<String>(Arrays.asList(expectedKeys)), allKeys);
}
+ // Verify that the initial set of resultLabels matches the expectedLabels.
+ // Ignore the (large) number of secondary locale labels that make up the
+ // tail labels in the result set right before the final "#" and "" buckets.
private void verifyLabels(final ArrayList<String> resultLabels,
- final String[] expectedLabels)
- throws Exception {
- assertEquals(new ArrayList<String>(Arrays.asList(expectedLabels)),
- resultLabels);
+ final String[] expectedLabels) throws Exception {
+ final List<String> expectedLabelList = Arrays.asList(expectedLabels);
+ final int numLabels = expectedLabelList.size() - 2;
+ assertEquals(expectedLabelList.subList(0, numLabels),
+ resultLabels.subList(0, numLabels));
}
}
diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
index df8e6d6..3f8b001 100644
--- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
+++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java
@@ -3383,6 +3383,27 @@
assertStoredValues(dataUri, values);
}
+ public void testJapaneseNameContactInEnglishLocale() {
+ // Need Japanese locale data for transliteration
+ if (!hasJapaneseCollator()) {
+ return;
+ }
+ ContactLocaleUtils.setLocale(Locale.US);
+ long rawContactId = createRawContact(null);
+
+ ContentValues values = new ContentValues();
+ values.put(StructuredName.GIVEN_NAME, "\u7A7A\u6D77");
+ values.put(StructuredName.PHONETIC_GIVEN_NAME, "\u304B\u3044\u304F\u3046");
+ insertStructuredName(rawContactId, values);
+
+ long contactId = queryContactId(rawContactId);
+ // en_US should behave same as ja_JP (match on Hiragana and Romaji
+ // but not Pinyin)
+ assertContactFilter(contactId, "\u304B\u3044\u304F\u3046");
+ assertContactFilter(contactId, "kaiku");
+ assertContactFilterNoResult("kong");
+ }
+
public void testContactWithJapaneseName() {
if (!hasJapaneseCollator()) {
return;
@@ -3426,6 +3447,12 @@
// The same values should be available through a join with Data
assertStoredValues(dataUri, values);
+
+ long contactId = queryContactId(rawContactId);
+ // ja_JP should match on Hiragana and Romaji but not Pinyin
+ assertContactFilter(contactId, "\u304B\u3044\u304F\u3046");
+ assertContactFilter(contactId, "kaiku");
+ assertContactFilterNoResult("kong");
}
public void testDisplayNameUpdate() {
@@ -3616,8 +3643,8 @@
}
private void assertContactFilterNoResult(String filter) {
- Uri filterUri4 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, filter);
- assertEquals(0, getCount(filterUri4, null, null));
+ Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
+ assertEquals(0, getCount(filterUri, null, null));
}
public void testSearchSnippetOrganization() throws Exception {
diff --git a/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java b/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
index 3abdc3f..ac6e5e3 100644
--- a/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
+++ b/tests/src/com/android/providers/contacts/SearchIndexManagerTest.java
@@ -87,6 +87,7 @@
if (!Arrays.asList(Collator.getAvailableLocales()).contains(Locale.CHINA)) {
return;
}
+ ContactLocaleUtils.setLocale(Locale.SIMPLIFIED_CHINESE);
long rawContactId = createRawContact();
ContentValues values = new ContentValues();