ContactsBinaryDictionary.java revision 1ed017ef0e271ed3f3c212def6cc6ba95b14e780
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 5518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private ContentObserver mObserver; 5618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 5718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang /** 5818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Whether to use "firstname lastname" in bigram predictions. 5918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang */ 6018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private final boolean mUseFirstLastBigrams; 6118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 6218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) { 63f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId); 6418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); 6518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang registerObserver(context); 6618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 6718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // Load the current binary dictionary from internal storage. If no binary dictionary exists, 6818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // loadDictionary will start a new thread to generate one asynchronously. 6918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang loadDictionary(); 7018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 7118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 7218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private synchronized void registerObserver(final Context context) { 7318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // Perform a managed query. The Activity will handle closing and requerying the cursor 7418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // when needed. 7518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (mObserver != null) return; 7618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang ContentResolver cres = context.getContentResolver(); 7718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang cres.registerContentObserver(Contacts.CONTENT_URI, true, mObserver = 7818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang new ContentObserver(null) { 7918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 8018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void onChange(boolean self) { 8118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang setRequiresReload(true); 8218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 8318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang }); 8418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 8518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 8618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void reopen(final Context context) { 8718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang registerObserver(context); 8818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 8918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 9018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 9118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public synchronized void close() { 9218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (mObserver != null) { 9318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang mContext.getContentResolver().unregisterContentObserver(mObserver); 9418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang mObserver = null; 9518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 9618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang super.close(); 9718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 9818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 9918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 10018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void loadDictionaryAsync() { 10118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang try { 10218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang Cursor cursor = mContext.getContentResolver() 10318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang .query(Contacts.CONTENT_URI, PROJECTION, null, null, null); 10418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (cursor != null) { 10518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang try { 10618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (cursor.moveToFirst()) { 1074d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang sContactCountAtLastRebuild = getContactCount(); 10818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang addWords(cursor); 10918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } finally { 11118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang cursor.close(); 11218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } catch (IllegalStateException e) { 11518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang Log.e(TAG, "Contacts DB is having problems"); 11618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 11818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 11918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang @Override 12018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang public void getBigrams(final WordComposer codes, final CharSequence previousWord, 12118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang final WordCallback callback) { 12218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang super.getBigrams(codes, previousWord, callback); 12318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 12418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 12518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private boolean useFirstLastBigramsForLocale(Locale locale) { 12618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // TODO: Add firstname/lastname bigram rules for other languages. 12718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) { 12818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang return true; 12918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 13018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang return false; 13118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 13218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 13318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private void addWords(Cursor cursor) { 13418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang clearFusionDictionary(); 1354d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int count = 0; 1364d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) { 13718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang String name = cursor.getString(INDEX_NAME); 1384d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (isValidName(name)) { 13918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang addName(name); 1404d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang ++count; 14118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 14218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang cursor.moveToNext(); 14318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 14418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 14518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang 1464d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private int getContactCount() { 1474d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: consider switching to a rawQuery("select count(*)...") on the database if 1484d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // performance is a bottleneck. 1494d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final Cursor cursor = mContext.getContentResolver().query( 1504d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null); 1514d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (cursor != null) { 1522798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang try { 1532798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang return cursor.getCount(); 1542798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang } finally { 1552798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang cursor.close(); 1562798c85c0f77fdf4f12eccfe241f84ddec3de994Tom Ouyang } 1574d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 1584d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return 0; 1594d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 1604d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 16118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang /** 16218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their 16318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang * bigrams depending on locale. 16418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang */ 16518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang private void addName(String name) { 16618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang int len = name.codePointCount(0, name.length()); 16718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang String prevWord = null; 16818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // TODO: Better tokenization for non-Latin writing systems 16918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang for (int i = 0; i < len; i++) { 17018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (Character.isLetter(name.codePointAt(i))) { 1714d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int end = getWordEndPosition(name, len, i); 1724d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String word = name.substring(i, end); 1734d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang i = end - 1; 17418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // Don't add single letter words, possibly confuses 17518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang // capitalization of i. 17618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang final int wordLen = word.codePointCount(0, word.length()); 17718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { 178f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS); 17918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (!TextUtils.isEmpty(prevWord)) { 18018222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang if (mUseFirstLastBigrams) { 18118222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang super.setBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM); 18218222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18318222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18418222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang prevWord = word; 18518222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18618222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18718222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 18818222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang } 1894d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 1904d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** 1914d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * Returns the index of the last letter in the word, starting from position startIndex. 1924d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang */ 1934d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static int getWordEndPosition(String string, int len, int startIndex) { 1944d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int end; 1954d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int cp = 0; 1964d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang for (end = startIndex + 1; end < len; end += Character.charCount(cp)) { 1974d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang cp = string.codePointAt(end); 1984d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!(cp == Keyboard.CODE_DASH || cp == Keyboard.CODE_SINGLE_QUOTE 1994d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang || Character.isLetter(cp))) { 2004d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang break; 2014d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2024d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2034d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return end; 2044d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2054d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 2064d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang @Override 2074d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang protected boolean hasContentChanged() { 2084d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final long startTime = SystemClock.uptimeMillis(); 2094d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final int contactCount = getContactCount(); 2104d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (contactCount > MAX_CONTACT_COUNT) { 2114d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // If there are too many contacts then return false. In this rare case it is impossible 2124d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // to include all of them anyways and the cost of rebuilding the dictionary is too high. 2134d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: Sort and check only the MAX_CONTACT_COUNT most recent contacts? 2144d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2154d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2164d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (contactCount != sContactCountAtLastRebuild) { 2171ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang if (DEBUG) { 2181ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang Log.d(TAG, "Contact count changed: " + sContactCountAtLastRebuild + " to " 2191ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang + contactCount); 2201ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang } 2214d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2224d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2234d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // Check all contacts since it's not possible to find out which names have changed. 2244d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // This is needed because it's possible to receive extraneous onChange events even when no 2254d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // name has changed. 2264d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Cursor cursor = mContext.getContentResolver().query( 2274d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Contacts.CONTENT_URI, PROJECTION, null, null, null); 2284d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (cursor != null) { 2294d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang try { 2304d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (cursor.moveToFirst()) { 2314d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang while (!cursor.isAfterLast()) { 2324d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String name = cursor.getString(INDEX_NAME); 2334d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (isValidName(name) && !isNameInDictionary(name)) { 2344d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (DEBUG) { 2354d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Log.d(TAG, "Contact name missing: " + name + " (runtime = " 2364d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang + (SystemClock.uptimeMillis() - startTime) + " ms)"); 2374d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2384d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2394d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2404d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang cursor.moveToNext(); 2414d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2424d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2434d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } finally { 2444d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang cursor.close(); 2454d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2464d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2474d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (DEBUG) { 2484d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang Log.d(TAG, "No contacts changed. (runtime = " + (SystemClock.uptimeMillis() - startTime) 2494d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang + " ms)"); 2504d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2514d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2524d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2534d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 2544d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private static boolean isValidName(String name) { 2554d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (name != null && -1 == name.indexOf('@')) { 2564d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2574d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2584d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2594d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2604d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 2614d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** 2624d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * Checks if the words in a name are in the current binary dictionary. 2634d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang */ 2644d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang private boolean isNameInDictionary(String name) { 2654d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int len = name.codePointCount(0, name.length()); 2664d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String prevWord = null; 2674d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang for (int i = 0; i < len; i++) { 2684d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (Character.isLetter(name.codePointAt(i))) { 2694d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang int end = getWordEndPosition(name, len, i); 2704d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang String word = name.substring(i, end); 2714d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang i = end - 1; 2724d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final int wordLen = word.codePointCount(0, word.length()); 2734d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { 2744d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) { 2754d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!super.isValidBigramLocked(prevWord, word)) { 2764d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2774d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2784d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } else { 2794d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (!super.isValidWordLocked(word)) { 2804d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return false; 2814d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2824d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2834d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang prevWord = word; 2844d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2854d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2864d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 2874d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return true; 2884d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 28918222f8c863e509538857b1fafca9c696fae2f55Tom Ouyang} 290