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