1/*
2 * Copyright (C) 2008 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
17package com.android.inputmethod.latin;
18
19import android.content.ContentResolver;
20import android.content.ContentValues;
21import android.content.Context;
22import android.database.ContentObserver;
23import android.database.Cursor;
24import android.provider.UserDictionary.Words;
25
26public class UserDictionary extends ExpandableDictionary {
27
28    private static final String[] PROJECTION = {
29        Words._ID,
30        Words.WORD,
31        Words.FREQUENCY
32    };
33
34    private static final int INDEX_WORD = 1;
35    private static final int INDEX_FREQUENCY = 2;
36
37    private ContentObserver mObserver;
38    private String mLocale;
39
40    public UserDictionary(Context context, String locale) {
41        super(context);
42        mLocale = locale;
43        // Perform a managed query. The Activity will handle closing and requerying the cursor
44        // when needed.
45        ContentResolver cres = context.getContentResolver();
46
47        cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
48            @Override
49            public void onChange(boolean self) {
50                setRequiresReload(true);
51            }
52        });
53
54        loadDictionary();
55    }
56
57    public synchronized void close() {
58        if (mObserver != null) {
59            getContext().getContentResolver().unregisterContentObserver(mObserver);
60            mObserver = null;
61        }
62        super.close();
63    }
64
65    @Override
66    public void loadDictionaryAsync() {
67        Cursor cursor = getContext().getContentResolver()
68                .query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)",
69                        new String[] { mLocale }, null);
70        addWords(cursor);
71    }
72
73    /**
74     * Adds a word to the dictionary and makes it persistent.
75     * @param word the word to add. If the word is capitalized, then the dictionary will
76     * recognize it as a capitalized word when searched.
77     * @param frequency the frequency of occurrence of the word. A frequency of 255 is considered
78     * the highest.
79     * @TODO use a higher or float range for frequency
80     */
81    @Override
82    public synchronized void addWord(String word, int frequency) {
83        // Force load the dictionary here synchronously
84        if (getRequiresReload()) loadDictionaryAsync();
85        // Safeguard against adding long words. Can cause stack overflow.
86        if (word.length() >= getMaxWordLength()) return;
87
88        super.addWord(word, frequency);
89
90        // Update the user dictionary provider
91        ContentValues values = new ContentValues(5);
92        values.put(Words.WORD, word);
93        values.put(Words.FREQUENCY, frequency);
94        values.put(Words.LOCALE, mLocale);
95        values.put(Words.APP_ID, 0);
96
97        getContext().getContentResolver().insert(Words.CONTENT_URI, values);
98        // In case the above does a synchronous callback of the change observer
99        setRequiresReload(false);
100    }
101
102    @Override
103    public synchronized void getWords(final WordComposer codes, final WordCallback callback,
104            int[] nextLettersFrequencies) {
105        super.getWords(codes, callback, nextLettersFrequencies);
106    }
107
108    @Override
109    public synchronized boolean isValidWord(CharSequence word) {
110        return super.isValidWord(word);
111    }
112
113    private void addWords(Cursor cursor) {
114        clearDictionary();
115
116        final int maxWordLength = getMaxWordLength();
117        if (cursor.moveToFirst()) {
118            while (!cursor.isAfterLast()) {
119                String word = cursor.getString(INDEX_WORD);
120                int frequency = cursor.getInt(INDEX_FREQUENCY);
121                // Safeguard against adding really long words. Stack may overflow due
122                // to recursion
123                if (word.length() < maxWordLength) {
124                    super.addWord(word, frequency);
125                }
126                cursor.moveToNext();
127            }
128        }
129        cursor.close();
130    }
131}
132