Home | History | Annotate | Download | only in latin
      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 
     52     public ContactsDictionary(final Context context, final int dicTypeId) {
     53         super(context, dicTypeId);
     54         registerObserver(context);
     55         loadDictionary();
     56     }
     57 
     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 
     72     public void reopen(final Context context) {
     73         registerObserver(context);
     74     }
     75 
     76     @Override
     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
     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
     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
    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 
    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