118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang/* 218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Copyright (C) 2012 The Android Open Source Project 318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * 418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * in compliance with the License. You may obtain a copy of the License at 618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * 718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * http://www.apache.org/licenses/LICENSE-2.0 818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * 918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Unless required by applicable law or agreed to in writing, software distributed under the License 1018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 1118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * or implied. See the License for the specific language governing permissions and limitations under 1218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * the License. 1318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang */ 1418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 1518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangpackage com.android.inputmethod.latin; 1618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 1718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.content.ContentResolver; 1818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.content.Context; 1918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.database.ContentObserver; 2018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.database.Cursor; 214d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyangimport android.os.SystemClock; 2218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.provider.BaseColumns; 2318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.provider.ContactsContract.Contacts; 2418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.text.TextUtils; 2518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport android.util.Log; 2618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 2718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport com.android.inputmethod.keyboard.Keyboard; 2818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 2918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangimport java.util.Locale; 3018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 3118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyangpublic class ContactsBinaryDictionary extends ExpandableBinaryDictionary { 3218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 3318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private static final String[] PROJECTION = {BaseColumns._ID, Contacts.DISPLAY_NAME,}; 344d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static final String[] PROJECTION_ID_ONLY = {BaseColumns._ID}; 3518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 3618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private static final String TAG = ContactsBinaryDictionary.class.getSimpleName(); 3718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private static final String NAME = "contacts"; 3818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 394d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static boolean DEBUG = false; 404d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 4118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang /** 4218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Frequency for contacts information into the dictionary 4318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang */ 4418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private static final int FREQUENCY_FOR_CONTACTS = 40; 4518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90; 4618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 474d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** The maximum number of contacts that this dictionary supports. */ 484d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static final int MAX_CONTACT_COUNT = 10000; 494d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 5018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private static final int INDEX_NAME = 1; 5118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 524d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** The number of contacts in the most recent dictionary rebuild. */ 534d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang static private int sContactCountAtLastRebuild = 0; 544d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 552e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang /** The locale for this contacts dictionary. Controls name bigram predictions. */ 562e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang public final Locale mLocale; 572e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang 5818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private ContentObserver mObserver; 5918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 6018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang /** 6118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Whether to use "firstname lastname" in bigram predictions. 6218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang */ 6318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private final boolean mUseFirstLastBigrams; 6418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 6505efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard public ContactsBinaryDictionary(final Context context, Locale locale) { 66d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS); 672e8aa0600293875c620ba7b650010cb30ec023c1Tom Ouyang mLocale = locale; 6818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); 6918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang registerObserver(context); 7018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 7118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // Load the current binary dictionary from internal storage. If no binary dictionary exists, 7218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // loadDictionary will start a new thread to generate one asynchronously. 7318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang loadDictionary(); 7418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 7518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 7618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private synchronized void registerObserver(final Context context) { 7718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // Perform a managed query. The Activity will handle closing and requerying the cursor 7818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // when needed. 7918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (mObserver != null) return; 8018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang ContentResolver cres = context.getContentResolver(); 8118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang cres.registerContentObserver(Contacts.CONTENT_URI, true, mObserver = 8218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang new ContentObserver(null) { 8318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 8418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void onChange(boolean self) { 8518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang setRequiresReload(true); 8618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 8718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang }); 8818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 8918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 9018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void reopen(final Context context) { 9118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang registerObserver(context); 9218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 9318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 9418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 9518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public synchronized void close() { 9618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (mObserver != null) { 9718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang mContext.getContentResolver().unregisterContentObserver(mObserver); 9818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang mObserver = null; 9918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 10018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang super.close(); 10118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 10218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 10318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 10418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void loadDictionaryAsync() { 10518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang try { 10618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang Cursor cursor = mContext.getContentResolver() 10718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang .query(Contacts.CONTENT_URI, PROJECTION, null, null, null); 10818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (cursor != null) { 10918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang try { 11018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (cursor.moveToFirst()) { 1114d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang sContactCountAtLastRebuild = getContactCount(); 11218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang addWords(cursor); 11318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } finally { 11518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang cursor.close(); 11618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } catch (IllegalStateException e) { 11918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang Log.e(TAG, "Contacts DB is having problems"); 12018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 12118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 12218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 12318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private boolean useFirstLastBigramsForLocale(Locale locale) { 12418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // TODO: Add firstname/lastname bigram rules for other languages. 12518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { 12618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang return true; 12718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 12818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang return false; 12918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 13018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 13118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private void addWords(Cursor cursor) { 13218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang clearFusionDictionary(); 1334d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int count = 0; 1344d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) { 13518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang String name = cursor.getString(INDEX_NAME); 1364d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (isValidName(name)) { 13718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang addName(name); 1384d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang ++count; 13918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 14018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang cursor.moveToNext(); 14118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 14218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 14318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 1444d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private int getContactCount() { 1454d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: consider switching to a rawQuery("select count(*)...") on the database if 1464d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // performance is a bottleneck. 1474d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final Cursor cursor = mContext.getContentResolver().query( 1484d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null); 1494d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (cursor != null) { 1502798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang try { 1512798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang return cursor.getCount(); 1522798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang } finally { 1532798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang cursor.close(); 1542798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang } 1554d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 1564d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return 0; 1574d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 1584d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 15918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang /** 16018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their 16118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * bigrams depending on locale. 16218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang */ 16318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private void addName(String name) { 164e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa int len = StringUtils.codePointCount(name); 16518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang String prevWord = null; 16618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // TODO: Better tokenization for non-Latin writing systems 16718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang for (int i = 0; i < len; i++) { 16818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (Character.isLetter(name.codePointAt(i))) { 1694d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int end = getWordEndPosition(name, len, i); 1704d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String word = name.substring(i, end); 1714d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang i = end - 1; 17218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // Don't add single letter words, possibly confuses 17318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // capitalization of i. 174e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa final int wordLen = StringUtils.codePointCount(word); 17518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { 176f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS); 17718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (!TextUtils.isEmpty(prevWord)) { 17818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (mUseFirstLastBigrams) { 17918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang super.setBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM); 18018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang prevWord = word; 18318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 1874d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 1884d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** 1894d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * Returns the index of the last letter in the word, starting from position startIndex. 1904d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang */ 1914d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static int getWordEndPosition(String string, int len, int startIndex) { 1924d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int end; 1934d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int cp = 0; 1944d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang for (end = startIndex + 1; end < len; end += Character.charCount(cp)) { 1954d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang cp = string.codePointAt(end); 1964d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!(cp == Keyboard.CODE_DASH || cp == Keyboard.CODE_SINGLE_QUOTE 1974d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang || Character.isLetter(cp))) { 1984d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang break; 1994d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2004d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2014d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return end; 2024d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2034d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 2044d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang @Override 2054d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang protected boolean hasContentChanged() { 2064d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final long startTime = SystemClock.uptimeMillis(); 2074d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final int contactCount = getContactCount(); 2084d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (contactCount > MAX_CONTACT_COUNT) { 2094d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // If there are too many contacts then return false. In this rare case it is impossible 2104d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // to include all of them anyways and the cost of rebuilding the dictionary is too high. 2114d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: Sort and check only the MAX_CONTACT_COUNT most recent contacts? 2124d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2134d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2144d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (contactCount != sContactCountAtLastRebuild) { 2151ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang if (DEBUG) { 2161ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang Log.d(TAG, "Contact count changed: " + sContactCountAtLastRebuild + " to " 2171ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang + contactCount); 2181ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang } 2194d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2204d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2214d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // Check all contacts since it's not possible to find out which names have changed. 2224d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // This is needed because it's possible to receive extraneous onChange events even when no 2234d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // name has changed. 2244d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Cursor cursor = mContext.getContentResolver().query( 2254d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Contacts.CONTENT_URI, PROJECTION, null, null, null); 2264d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (cursor != null) { 2274d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang try { 2284d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (cursor.moveToFirst()) { 2294d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang while (!cursor.isAfterLast()) { 2304d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String name = cursor.getString(INDEX_NAME); 2314d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (isValidName(name) && !isNameInDictionary(name)) { 2324d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (DEBUG) { 2334d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Log.d(TAG, "Contact name missing: " + name + " (runtime = " 2344d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang + (SystemClock.uptimeMillis() - startTime) + " ms)"); 2354d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2364d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2374d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2384d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang cursor.moveToNext(); 2394d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2404d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2414d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } finally { 2424d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang cursor.close(); 2434d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2444d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2454d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (DEBUG) { 2464d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Log.d(TAG, "No contacts changed. (runtime = " + (SystemClock.uptimeMillis() - startTime) 2474d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang + " ms)"); 2484d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2494d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2504d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2514d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 2524d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static boolean isValidName(String name) { 2534d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (name != null && -1 == name.indexOf('@')) { 2544d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2554d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2564d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2574d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2584d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 2594d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** 2604d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * Checks if the words in a name are in the current binary dictionary. 2614d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang */ 2624d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private boolean isNameInDictionary(String name) { 263e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa int len = StringUtils.codePointCount(name); 2644d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String prevWord = null; 2654d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang for (int i = 0; i < len; i++) { 2664d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (Character.isLetter(name.codePointAt(i))) { 2674d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int end = getWordEndPosition(name, len, i); 2684d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String word = name.substring(i, end); 2694d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang i = end - 1; 270e55c23e4b0b8d9d66349a3b275d0fa1540d7450aKen Wakasa final int wordLen = StringUtils.codePointCount(word); 2714d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { 2724d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) { 2734d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!super.isValidBigramLocked(prevWord, word)) { 2744d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2754d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2764d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } else { 2774d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!super.isValidWordLocked(word)) { 2784d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2794d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2804d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2814d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang prevWord = word; 2824d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2834d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2844d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2854d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2864d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 28718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang} 288