ContactsDictionary.java revision 0730bbfbf5e37bbcb5c287aeff71b304c833a36e
12bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer/* 22bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * Copyright (C) 2009 The Android Open Source Project 32bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * 42bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * Licensed under the Apache License, Version 2.0 (the "License"); 52bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * you may not use this file except in compliance with the License. 62bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * You may obtain a copy of the License at 72bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * 82bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * http://www.apache.org/licenses/LICENSE-2.0 92bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * 102bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * Unless required by applicable law or agreed to in writing, software 112bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * distributed under the License is distributed on an "AS IS" BASIS, 122bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * See the License for the specific language governing permissions and 142bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer * limitations under the License. 152bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer */ 162bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 172bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerpackage com.android.inputmethod.latin; 182bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 192bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerimport android.content.ContentResolver; 202bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerimport android.content.Context; 212bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerimport android.database.ContentObserver; 222bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerimport android.database.Cursor; 23718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasaniimport android.os.SystemClock; 24e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaokaimport android.provider.BaseColumns; 2541fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikovimport android.provider.ContactsContract.Contacts; 26979f8690967ff5409fe18f5085858ccdb8e0ccf1satokimport android.text.TextUtils; 27979f8690967ff5409fe18f5085858ccdb8e0ccf1satokimport android.util.Log; 282bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 290730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalardimport com.android.inputmethod.keyboard.Keyboard; 300730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard 312bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerpublic class ContactsDictionary extends ExpandableDictionary { 3241fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 332bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer private static final String[] PROJECTION = { 34e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka BaseColumns._ID, 3541fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov Contacts.DISPLAY_NAME, 362bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer }; 3741fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 3878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa private static final String TAG = "ContactsDictionary"; 3978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa 40979f8690967ff5409fe18f5085858ccdb8e0ccf1satok /** 41979f8690967ff5409fe18f5085858ccdb8e0ccf1satok * Frequency for contacts information into the dictionary 42979f8690967ff5409fe18f5085858ccdb8e0ccf1satok */ 43979f8690967ff5409fe18f5085858ccdb8e0ccf1satok private static final int FREQUENCY_FOR_CONTACTS = 128; 44979f8690967ff5409fe18f5085858ccdb8e0ccf1satok private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90; 45979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 462bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer private static final int INDEX_NAME = 1; 4741fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 482bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer private ContentObserver mObserver; 4941fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 50b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani private long mLastLoadedContacts; 51b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani 52979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public ContactsDictionary(Context context, int dicTypeId) { 53979f8690967ff5409fe18f5085858ccdb8e0ccf1satok super(context, dicTypeId); 542bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer // Perform a managed query. The Activity will handle closing and requerying the cursor 552bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer // when needed. 562bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer ContentResolver cres = context.getContentResolver(); 5741fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 58979f8690967ff5409fe18f5085858ccdb8e0ccf1satok cres.registerContentObserver( 59979f8690967ff5409fe18f5085858ccdb8e0ccf1satok Contacts.CONTENT_URI, true,mObserver = new ContentObserver(null) { 60979f8690967ff5409fe18f5085858ccdb8e0ccf1satok @Override 61979f8690967ff5409fe18f5085858ccdb8e0ccf1satok public void onChange(boolean self) { 62979f8690967ff5409fe18f5085858ccdb8e0ccf1satok setRequiresReload(true); 63979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 64979f8690967ff5409fe18f5085858ccdb8e0ccf1satok }); 65283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani loadDictionary(); 662bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 6741fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 68979f8690967ff5409fe18f5085858ccdb8e0ccf1satok @Override 692bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer public synchronized void close() { 702bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer if (mObserver != null) { 712bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer getContext().getContentResolver().unregisterContentObserver(mObserver); 722bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer mObserver = null; 732bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 74283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani super.close(); 752bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 7641fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov 77283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani @Override 78283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani public void startDictionaryLoadingTaskLocked() { 79718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasani long now = SystemClock.uptimeMillis(); 80b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani if (mLastLoadedContacts == 0 81b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) { 82283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani super.startDictionaryLoadingTaskLocked(); 83718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasani } 842bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 852bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 862bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer @Override 87283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani public void loadDictionaryAsync() { 8878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa try { 8978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa Cursor cursor = getContext().getContentResolver() 9078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa .query(Contacts.CONTENT_URI, PROJECTION, null, null, null); 9178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa if (cursor != null) { 9278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa addWords(cursor); 9378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa } 9478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa } catch(IllegalStateException e) { 9578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa Log.e(TAG, "Contacts DB is having problems"); 96718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasani } 97283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani mLastLoadedContacts = SystemClock.uptimeMillis(); 982bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 992bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 1004556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard @Override 1014556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard public void getBigrams(final WordComposer codes, final CharSequence previousWord, 1024556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard final WordCallback callback) { 1034556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard // Do not return bigrams from Contacts when nothing was typed. 1044556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard if (codes.size() <= 0) return; 1054556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard super.getBigrams(codes, previousWord, callback); 1064556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard } 1074556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard 1082bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer private void addWords(Cursor cursor) { 1092bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer clearDictionary(); 1102bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 1112bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer final int maxWordLength = getMaxWordLength(); 11278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa try { 11378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa if (cursor.moveToFirst()) { 11478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa while (!cursor.isAfterLast()) { 11578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa String name = cursor.getString(INDEX_NAME); 11678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa 11778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa if (name != null) { 11878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa int len = name.length(); 11978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa String prevWord = null; 12078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa 12178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // TODO: Better tokenization for non-Latin writing systems 12278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa for (int i = 0; i < len; i++) { 12378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa if (Character.isLetter(name.charAt(i))) { 12478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa int j; 12578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa for (j = i + 1; j < len; j++) { 12678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa char c = name.charAt(j); 12778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa 1280730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard if (!(c == Keyboard.CODE_DASH 1290730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard || c == Keyboard.CODE_SINGLE_QUOTE 1300730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard || Character.isLetter(c))) { 13178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa break; 13278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa } 133e5c7f0981d869806bc2ea7d58379a3138e0a0186Eric Fischer } 1342bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 13578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa String word = name.substring(i, j); 13678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa i = j - 1; 13778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa 13878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // Safeguard against adding really long words. Stack 13978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // may overflow due to recursion 14078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // Also don't add single letter words, possibly confuses 14178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // capitalization of i. 14278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa final int wordLen = word.length(); 14378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa if (wordLen < maxWordLength && wordLen > 1) { 14478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa super.addWord(word, FREQUENCY_FOR_CONTACTS); 14578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa if (!TextUtils.isEmpty(prevWord)) { 14678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // TODO Do not add email address 14778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa // Not so critical 14878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa super.setBigram(prevWord, word, 14978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa FREQUENCY_FOR_CONTACTS_BIGRAM); 15078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa } 15178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa prevWord = word; 152979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 153e5c7f0981d869806bc2ea7d58379a3138e0a0186Eric Fischer } 1542bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 1552bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 15678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa cursor.moveToNext(); 1572bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 1582bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 15978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa cursor.close(); 16078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa } catch(IllegalStateException e) { 16178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa Log.e(TAG, "Contacts DB is having problems"); 1622bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 1632bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 1642bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer} 165