• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.settings.inputmethod;
18 
19 import android.app.Activity;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.database.Cursor;
23 import android.os.Bundle;
24 import android.provider.UserDictionary;
25 import android.text.TextUtils;
26 import android.view.View;
27 import android.widget.EditText;
28 
29 import com.android.settings.R;
30 import com.android.settings.Utils;
31 
32 import java.util.ArrayList;
33 import java.util.Locale;
34 import java.util.TreeSet;
35 
36 /**
37  * A container class to factor common code to UserDictionaryAddWordFragment
38  * and UserDictionaryAddWordActivity.
39  */
40 public class UserDictionaryAddWordContents {
41     public static final String EXTRA_MODE = "mode";
42     public static final String EXTRA_WORD = "word";
43     public static final String EXTRA_SHORTCUT = "shortcut";
44     public static final String EXTRA_LOCALE = "locale";
45     public static final String EXTRA_ORIGINAL_WORD = "originalWord";
46     public static final String EXTRA_ORIGINAL_SHORTCUT = "originalShortcut";
47 
48     public static final int MODE_EDIT = 0;
49     public static final int MODE_INSERT = 1;
50 
51     private static final int FREQUENCY_FOR_USER_DICTIONARY_ADDS = 250;
52 
53     private final int mMode; // Either MODE_EDIT or MODE_INSERT
54     private final EditText mWordEditText;
55     private final EditText mShortcutEditText;
56     private String mLocale;
57     private final String mOldWord;
58     private final String mOldShortcut;
59     private String mSavedWord;
60     private String mSavedShortcut;
61 
UserDictionaryAddWordContents(final View view, final Bundle args)62     /* package */ UserDictionaryAddWordContents(final View view, final Bundle args) {
63         mWordEditText = (EditText) view.findViewById(R.id.user_dictionary_add_word_text);
64         mShortcutEditText = (EditText) view.findViewById(R.id.user_dictionary_add_shortcut);
65         final String word = args.getString(EXTRA_WORD);
66         if (null != word) {
67             mWordEditText.setText(word);
68             // Use getText in case the edit text modified the text we set. This happens when
69             // it's too long to be edited.
70             mWordEditText.setSelection(mWordEditText.getText().length());
71         }
72         final String shortcut = args.getString(EXTRA_SHORTCUT);
73         if (null != shortcut && null != mShortcutEditText) {
74             mShortcutEditText.setText(shortcut);
75         }
76         mMode = args.getInt(EXTRA_MODE); // default return value for #getInt() is 0 = MODE_EDIT
77         mOldWord = args.getString(EXTRA_WORD);
78         mOldShortcut = args.getString(EXTRA_SHORTCUT);
79         updateLocale(args.getString(EXTRA_LOCALE));
80     }
81 
UserDictionaryAddWordContents(final View view, final UserDictionaryAddWordContents oldInstanceToBeEdited)82     /* package */ UserDictionaryAddWordContents(final View view,
83             final UserDictionaryAddWordContents oldInstanceToBeEdited) {
84         mWordEditText = (EditText) view.findViewById(R.id.user_dictionary_add_word_text);
85         mShortcutEditText = (EditText) view.findViewById(R.id.user_dictionary_add_shortcut);
86         mMode = MODE_EDIT;
87         mOldWord = oldInstanceToBeEdited.mSavedWord;
88         mOldShortcut = oldInstanceToBeEdited.mSavedShortcut;
89         updateLocale(oldInstanceToBeEdited.getCurrentUserDictionaryLocale());
90     }
91 
92     // locale may be null, this means default locale
93     // It may also be the empty string, which means "all locales"
updateLocale(final String locale)94     /* package */ void updateLocale(final String locale) {
95         mLocale = null == locale ? Locale.getDefault().toString() : locale;
96     }
97 
saveStateIntoBundle(final Bundle outState)98     /* package */ void saveStateIntoBundle(final Bundle outState) {
99         outState.putString(EXTRA_WORD, mWordEditText.getText().toString());
100         outState.putString(EXTRA_ORIGINAL_WORD, mOldWord);
101         if (null != mShortcutEditText) {
102             outState.putString(EXTRA_SHORTCUT, mShortcutEditText.getText().toString());
103         }
104         if (null != mOldShortcut) {
105             outState.putString(EXTRA_ORIGINAL_SHORTCUT, mOldShortcut);
106         }
107         outState.putString(EXTRA_LOCALE, mLocale);
108     }
109 
delete(final Context context)110     /* package */ void delete(final Context context) {
111         if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
112             // Mode edit: remove the old entry.
113             final ContentResolver resolver = context.getContentResolver();
114             UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
115         }
116         // If we are in add mode, nothing was added, so we don't need to do anything.
117     }
118 
apply(final Context context, final Bundle outParameters)119     /* package */ int apply(final Context context, final Bundle outParameters) {
120         if (null != outParameters) saveStateIntoBundle(outParameters);
121         final ContentResolver resolver = context.getContentResolver();
122         if (MODE_EDIT == mMode && !TextUtils.isEmpty(mOldWord)) {
123             // Mode edit: remove the old entry.
124             UserDictionarySettings.deleteWord(mOldWord, mOldShortcut, resolver);
125         }
126         final String newWord = mWordEditText.getText().toString();
127         final String newShortcut;
128         if (null == mShortcutEditText) {
129             newShortcut = null;
130         } else {
131             final String tmpShortcut = mShortcutEditText.getText().toString();
132             if (TextUtils.isEmpty(tmpShortcut)) {
133                 newShortcut = null;
134             } else {
135                 newShortcut = tmpShortcut;
136             }
137         }
138         if (TextUtils.isEmpty(newWord)) {
139             // If the word is somehow empty, don't insert it.
140             return UserDictionaryAddWordActivity.CODE_CANCEL;
141         }
142         mSavedWord = newWord;
143         mSavedShortcut = newShortcut;
144         // If there is no shortcut, and the word already exists in the database, then we
145         // should not insert, because either A. the word exists with no shortcut, in which
146         // case the exact same thing we want to insert is already there, or B. the word
147         // exists with at least one shortcut, in which case it has priority on our word.
148         if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) {
149             return UserDictionaryAddWordActivity.CODE_ALREADY_PRESENT;
150         }
151 
152         // Disallow duplicates. If the same word with no shortcut is defined, remove it; if
153         // the same word with the same shortcut is defined, remove it; but we don't mind if
154         // there is the same word with a different, non-empty shortcut.
155         UserDictionarySettings.deleteWord(newWord, null, resolver);
156         if (!TextUtils.isEmpty(newShortcut)) {
157             // If newShortcut is empty we just deleted this, no need to do it again
158             UserDictionarySettings.deleteWord(newWord, newShortcut, resolver);
159         }
160 
161         // In this class we use the empty string to represent 'all locales' and mLocale cannot
162         // be null. However the addWord method takes null to mean 'all locales'.
163         UserDictionary.Words.addWord(context, newWord.toString(),
164                 FREQUENCY_FOR_USER_DICTIONARY_ADDS, newShortcut,
165                 TextUtils.isEmpty(mLocale) ? null : Utils.createLocaleFromString(mLocale));
166 
167         return UserDictionaryAddWordActivity.CODE_WORD_ADDED;
168     }
169 
170     private static final String[] HAS_WORD_PROJECTION = {UserDictionary.Words.WORD};
171     private static final String HAS_WORD_SELECTION_ONE_LOCALE = UserDictionary.Words.WORD
172             + "=? AND " + UserDictionary.Words.LOCALE + "=?";
173     private static final String HAS_WORD_SELECTION_ALL_LOCALES = UserDictionary.Words.WORD
174             + "=? AND " + UserDictionary.Words.LOCALE + " is null";
175 
hasWord(final String word, final Context context)176     private boolean hasWord(final String word, final Context context) {
177         final Cursor cursor;
178         // mLocale == "" indicates this is an entry for all languages. Here, mLocale can't
179         // be null at all (it's ensured by the updateLocale method).
180         if ("".equals(mLocale)) {
181             cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
182                     HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ALL_LOCALES,
183                     new String[] {word}, null /* sort order */);
184         } else {
185             cursor = context.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
186                     HAS_WORD_PROJECTION, HAS_WORD_SELECTION_ONE_LOCALE,
187                     new String[] {word, mLocale}, null /* sort order */);
188         }
189         try {
190             if (null == cursor) return false;
191             return cursor.getCount() > 0;
192         } finally {
193             if (null != cursor) cursor.close();
194         }
195     }
196 
197     public static class LocaleRenderer {
198         private final String mLocaleString;
199         private final String mDescription;
200 
201         // LocaleString may NOT be null.
LocaleRenderer(final Context context, final String localeString)202         public LocaleRenderer(final Context context, final String localeString) {
203             mLocaleString = localeString;
204             if (null == localeString) {
205                 mDescription = context.getString(R.string.user_dict_settings_more_languages);
206             } else if ("".equals(localeString)) {
207                 mDescription = context.getString(R.string.user_dict_settings_all_languages);
208             } else {
209                 mDescription = Utils.createLocaleFromString(localeString).getDisplayName();
210             }
211         }
212 
213         @Override
toString()214         public String toString() {
215             return mDescription;
216         }
217 
getLocaleString()218         public String getLocaleString() {
219             return mLocaleString;
220         }
221 
222         // "More languages..." is null ; "All languages" is the empty string.
isMoreLanguages()223         public boolean isMoreLanguages() {
224             return null == mLocaleString;
225         }
226     }
227 
addLocaleDisplayNameToList(final Context context, final ArrayList<LocaleRenderer> list, final String locale)228     private static void addLocaleDisplayNameToList(final Context context,
229             final ArrayList<LocaleRenderer> list, final String locale) {
230         if (null != locale) {
231             list.add(new LocaleRenderer(context, locale));
232         }
233     }
234 
235     // Helper method to get the list of locales to display for this word
getLocalesList(final Activity activity)236     public ArrayList<LocaleRenderer> getLocalesList(final Activity activity) {
237         final TreeSet<String> locales =
238                 UserDictionaryListPreferenceController.getUserDictionaryLocalesSet(activity);
239         // Remove our locale if it's in, because we're always gonna put it at the top
240         locales.remove(mLocale); // mLocale may not be null
241         final String systemLocale = Locale.getDefault().toString();
242         // The system locale should be inside. We want it at the 2nd spot.
243         locales.remove(systemLocale); // system locale may not be null
244         locales.remove(""); // Remove the empty string if it's there
245         final ArrayList<LocaleRenderer> localesList = new ArrayList<LocaleRenderer>();
246         // Add the passed locale, then the system locale at the top of the list. Add an
247         // "all languages" entry at the bottom of the list.
248         addLocaleDisplayNameToList(activity, localesList, mLocale);
249         if (!systemLocale.equals(mLocale)) {
250             addLocaleDisplayNameToList(activity, localesList, systemLocale);
251         }
252         for (final String l : locales) {
253             // TODO: sort in unicode order
254             addLocaleDisplayNameToList(activity, localesList, l);
255         }
256         if (!"".equals(mLocale)) {
257             // If mLocale is "", then we already inserted the "all languages" item, so don't do it
258             addLocaleDisplayNameToList(activity, localesList, ""); // meaning: all languages
259         }
260         localesList.add(new LocaleRenderer(activity, null)); // meaning: select another locale
261         return localesList;
262     }
263 
getCurrentUserDictionaryLocale()264     public String getCurrentUserDictionaryLocale() {
265         return mLocale;
266     }
267 }
268