• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.latin;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.database.ContentObserver;
22 import android.database.Cursor;
23 import android.os.SystemClock;
24 import android.provider.BaseColumns;
25 import android.provider.ContactsContract.Contacts;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import com.android.inputmethod.keyboard.Keyboard;
30 
31 public class ContactsDictionary extends ExpandableDictionary {
32 
33     private static final String[] PROJECTION = {
34         BaseColumns._ID,
35         Contacts.DISPLAY_NAME,
36     };
37 
38     private static final String TAG = "ContactsDictionary";
39 
40     /**
41      * Frequency for contacts information into the dictionary
42      */
43     private static final int FREQUENCY_FOR_CONTACTS = 40;
44     private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
45 
46     private static final int INDEX_NAME = 1;
47 
48     private ContentObserver mObserver;
49 
50     private long mLastLoadedContacts;
51 
ContactsDictionary(final Context context, final int dicTypeId)52     public ContactsDictionary(final Context context, final int dicTypeId) {
53         super(context, dicTypeId);
54         registerObserver(context);
55         loadDictionary();
56     }
57 
registerObserver(final Context context)58     private synchronized void registerObserver(final Context context) {
59         // Perform a managed query. The Activity will handle closing and requerying the cursor
60         // when needed.
61         if (mObserver != null) return;
62         ContentResolver cres = context.getContentResolver();
63         cres.registerContentObserver(
64                 Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
65                     @Override
66                     public void onChange(boolean self) {
67                         setRequiresReload(true);
68                     }
69                 });
70     }
71 
reopen(final Context context)72     public void reopen(final Context context) {
73         registerObserver(context);
74     }
75 
76     @Override
close()77     public synchronized void close() {
78         if (mObserver != null) {
79             getContext().getContentResolver().unregisterContentObserver(mObserver);
80             mObserver = null;
81         }
82         super.close();
83     }
84 
85     @Override
startDictionaryLoadingTaskLocked()86     public void startDictionaryLoadingTaskLocked() {
87         long now = SystemClock.uptimeMillis();
88         if (mLastLoadedContacts == 0
89                 || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
90             super.startDictionaryLoadingTaskLocked();
91         }
92     }
93 
94     @Override
loadDictionaryAsync()95     public void loadDictionaryAsync() {
96         try {
97             Cursor cursor = getContext().getContentResolver()
98                     .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
99             if (cursor != null) {
100                 addWords(cursor);
101             }
102         } catch(IllegalStateException e) {
103             Log.e(TAG, "Contacts DB is having problems");
104         }
105         mLastLoadedContacts = SystemClock.uptimeMillis();
106     }
107 
108     @Override
getBigrams(final WordComposer codes, final CharSequence previousWord, final WordCallback callback)109     public void getBigrams(final WordComposer codes, final CharSequence previousWord,
110             final WordCallback callback) {
111         // Do not return bigrams from Contacts when nothing was typed.
112         if (codes.size() <= 0) return;
113         super.getBigrams(codes, previousWord, callback);
114     }
115 
addWords(Cursor cursor)116     private void addWords(Cursor cursor) {
117         clearDictionary();
118 
119         final int maxWordLength = getMaxWordLength();
120         try {
121             if (cursor.moveToFirst()) {
122                 while (!cursor.isAfterLast()) {
123                     String name = cursor.getString(INDEX_NAME);
124 
125                     if (name != null && -1 == name.indexOf('@')) {
126                         int len = name.length();
127                         String prevWord = null;
128 
129                         // TODO: Better tokenization for non-Latin writing systems
130                         for (int i = 0; i < len; i++) {
131                             if (Character.isLetter(name.charAt(i))) {
132                                 int j;
133                                 for (j = i + 1; j < len; j++) {
134                                     char c = name.charAt(j);
135 
136                                     if (!(c == Keyboard.CODE_DASH
137                                             || c == Keyboard.CODE_SINGLE_QUOTE
138                                             || Character.isLetter(c))) {
139                                         break;
140                                     }
141                                 }
142 
143                                 String word = name.substring(i, j);
144                                 i = j - 1;
145 
146                                 // Safeguard against adding really long words. Stack
147                                 // may overflow due to recursion
148                                 // Also don't add single letter words, possibly confuses
149                                 // capitalization of i.
150                                 final int wordLen = word.length();
151                                 if (wordLen < maxWordLength && wordLen > 1) {
152                                     super.addWord(word, FREQUENCY_FOR_CONTACTS);
153                                     if (!TextUtils.isEmpty(prevWord)) {
154                                         super.setBigram(prevWord, word,
155                                                 FREQUENCY_FOR_CONTACTS_BIGRAM);
156                                     }
157                                     prevWord = word;
158                                 }
159                             }
160                         }
161                     }
162                     cursor.moveToNext();
163                 }
164             }
165             cursor.close();
166         } catch(IllegalStateException e) {
167             Log.e(TAG, "Contacts DB is having problems");
168         }
169     }
170 }
171