ContactsDictionary.java revision 4556de4b4540b18d059759c88cd8254ae6a42fa7
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
292bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischerpublic class ContactsDictionary extends ExpandableDictionary {
3041fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
312bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    private static final String[] PROJECTION = {
32e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        BaseColumns._ID,
3341fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov        Contacts.DISPLAY_NAME,
342bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    };
3541fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
3678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa    private static final String TAG = "ContactsDictionary";
3778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa
38979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    /**
39979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     * Frequency for contacts information into the dictionary
40979f8690967ff5409fe18f5085858ccdb8e0ccf1satok     */
41979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private static final int FREQUENCY_FOR_CONTACTS = 128;
42979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
43979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
442bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    private static final int INDEX_NAME = 1;
4541fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
462bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    private ContentObserver mObserver;
4741fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
48b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani    private long mLastLoadedContacts;
49b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani
50979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public ContactsDictionary(Context context, int dicTypeId) {
51979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        super(context, dicTypeId);
522bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        // Perform a managed query. The Activity will handle closing and requerying the cursor
532bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        // when needed.
542bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        ContentResolver cres = context.getContentResolver();
5541fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
56979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        cres.registerContentObserver(
57979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                Contacts.CONTENT_URI, true,mObserver = new ContentObserver(null) {
58979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    @Override
59979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    public void onChange(boolean self) {
60979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                        setRequiresReload(true);
61979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    }
62979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                });
63283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani        loadDictionary();
642bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
6541fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
66979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
672bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    public synchronized void close() {
682bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        if (mObserver != null) {
692bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer            getContext().getContentResolver().unregisterContentObserver(mObserver);
702bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer            mObserver = null;
712bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        }
72283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani        super.close();
732bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
7441fc8f4a183a5be8070fe28b7956b1d03a5dd8ceDmitri Plotnikov
75283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani    @Override
76283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani    public void startDictionaryLoadingTaskLocked() {
77718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasani        long now = SystemClock.uptimeMillis();
78b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani        if (mLastLoadedContacts == 0
79b3f6d58b6ec716c26df38b584eda061265437cf4Amith Yamasani                || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
80283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani            super.startDictionaryLoadingTaskLocked();
81718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasani        }
822bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
832bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer
842bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    @Override
85283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani    public void loadDictionaryAsync() {
8678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa        try {
8778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            Cursor cursor = getContext().getContentResolver()
8878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                    .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
8978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            if (cursor != null) {
9078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                addWords(cursor);
9178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            }
9278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa        } catch(IllegalStateException e) {
9378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            Log.e(TAG, "Contacts DB is having problems");
94718e813fbdb905b083562c0a6cd087463bacc261Amith Yamasani        }
95283a77f633e92ed7dbe96b083c921fc244bbe880Amith Yamasani        mLastLoadedContacts = SystemClock.uptimeMillis();
962bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
972bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer
984556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard    @Override
994556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard    public void getBigrams(final WordComposer codes, final CharSequence previousWord,
1004556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard            final WordCallback callback) {
1014556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard        // Do not return bigrams from Contacts when nothing was typed.
1024556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard        if (codes.size() <= 0) return;
1034556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard        super.getBigrams(codes, previousWord, callback);
1044556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard    }
1054556de4b4540b18d059759c88cd8254ae6a42fa7Jean Chalard
1062bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    private void addWords(Cursor cursor) {
1072bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        clearDictionary();
1082bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer
1092bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        final int maxWordLength = getMaxWordLength();
11078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa        try {
11178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            if (cursor.moveToFirst()) {
11278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                while (!cursor.isAfterLast()) {
11378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                    String name = cursor.getString(INDEX_NAME);
11478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa
11578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                    if (name != null) {
11678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                        int len = name.length();
11778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                        String prevWord = null;
11878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa
11978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                        // TODO: Better tokenization for non-Latin writing systems
12078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                        for (int i = 0; i < len; i++) {
12178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                            if (Character.isLetter(name.charAt(i))) {
12278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                int j;
12378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                for (j = i + 1; j < len; j++) {
12478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    char c = name.charAt(j);
12578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa
12678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    if (!(c == '-' || c == '\'' ||
12778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                          Character.isLetter(c))) {
12878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                        break;
12978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    }
130e5c7f0981d869806bc2ea7d58379a3138e0a0186Eric Fischer                                }
1312bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer
13278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                String word = name.substring(i, j);
13378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                i = j - 1;
13478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa
13578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                // Safeguard against adding really long words. Stack
13678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                // may overflow due to recursion
13778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                // Also don't add single letter words, possibly confuses
13878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                // capitalization of i.
13978c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                final int wordLen = word.length();
14078c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                if (wordLen < maxWordLength && wordLen > 1) {
14178c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    super.addWord(word, FREQUENCY_FOR_CONTACTS);
14278c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    if (!TextUtils.isEmpty(prevWord)) {
14378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                        // TODO Do not add email address
14478c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                        // Not so critical
14578c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                        super.setBigram(prevWord, word,
14678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                                FREQUENCY_FOR_CONTACTS_BIGRAM);
14778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    }
14878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                                    prevWord = word;
149979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                                }
150e5c7f0981d869806bc2ea7d58379a3138e0a0186Eric Fischer                            }
1512bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer                        }
1522bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer                    }
15378c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa                    cursor.moveToNext();
1542bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer                }
1552bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer            }
15678c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            cursor.close();
15778c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa        } catch(IllegalStateException e) {
15878c4611719f10b4a53ade54ab5eeb390061737a3Ken Wakasa            Log.e(TAG, "Contacts DB is having problems");
1592bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer        }
1602bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer    }
1612bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer}
162