• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.content.Context;
20 import android.content.Intent;
21 import android.database.Cursor;
22 import android.provider.UserDictionary;
23 import android.text.TextUtils;
24 import android.view.inputmethod.InputMethodInfo;
25 import android.view.inputmethod.InputMethodManager;
26 import android.view.inputmethod.InputMethodSubtype;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.Preference;
31 import androidx.preference.PreferenceScreen;
32 
33 import com.android.settings.R;
34 import com.android.settings.Utils;
35 import com.android.settings.core.BasePreferenceController;
36 import com.android.settingslib.core.lifecycle.LifecycleObserver;
37 import com.android.settingslib.core.lifecycle.events.OnStart;
38 
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.TreeSet;
42 
43 public class UserDictionaryListPreferenceController extends BasePreferenceController implements
44         LifecycleObserver, OnStart {
45 
46     public static final String USER_DICTIONARY_SETTINGS_INTENT_ACTION =
47             "android.settings.USER_DICTIONARY_SETTINGS";
48     private final String KEY_ALL_LANGUAGE = "all_languages";
49     private String mLocale;
50     private PreferenceScreen mScreen;
51 
UserDictionaryListPreferenceController(Context context, String key)52     public UserDictionaryListPreferenceController(Context context, String key) {
53         super(context, key);
54     }
55 
setLocale(String locale)56     public void setLocale(String locale) {
57         mLocale = locale;
58     }
59 
60     @Override
getAvailabilityStatus()61     public int getAvailabilityStatus() {
62         return AVAILABLE;
63     }
64 
65     @Override
displayPreference(PreferenceScreen screen)66     public void displayPreference(PreferenceScreen screen) {
67         super.displayPreference(screen);
68         // This is to make newly inserted languages being sorted alphabetically when updating
69         // the existing preferenceScreen, and for "For all languages" to be always on the top.
70         screen.setOrderingAsAdded(false);
71         mScreen = screen;
72     }
73 
74     @Override
onStart()75     public void onStart() {
76         createUserDictSettings();
77     }
78 
79     @NonNull
getUserDictionaryLocalesSet(Context context)80     public static TreeSet<String> getUserDictionaryLocalesSet(Context context) {
81         final Cursor cursor = context.getContentResolver().query(
82                 UserDictionary.Words.CONTENT_URI, new String[]{UserDictionary.Words.LOCALE},
83                 null, null, null);
84         final TreeSet<String> localeSet = new TreeSet<>();
85         if (cursor == null) {
86             // The user dictionary service is not present or disabled. Return empty set.
87             return localeSet;
88         }
89         try {
90             if (cursor.moveToFirst()) {
91                 final int columnIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
92                 do {
93                     final String locale = cursor.getString(columnIndex);
94                     localeSet.add(null != locale ? locale : "");
95                 } while (cursor.moveToNext());
96             }
97         } finally {
98             cursor.close();
99         }
100 
101         // CAVEAT: Keep this for consistency of the implementation between Keyboard and Settings
102         // if (!UserDictionarySettings.IS_SHORTCUT_API_SUPPORTED) {
103         //     // For ICS, we need to show "For all languages" in case that the keyboard locale
104         //     // is different from the system locale
105         //     localeSet.add("");
106         // }
107 
108         final InputMethodManager imm =
109                 (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
110         final List<InputMethodInfo> imis = imm.getEnabledInputMethodList();
111         for (final InputMethodInfo imi : imis) {
112             final List<InputMethodSubtype> subtypes =
113                     imm.getEnabledInputMethodSubtypeList(
114                             imi, true /* allowsImplicitlySelectedSubtypes */);
115             for (InputMethodSubtype subtype : subtypes) {
116                 final String locale = subtype.getLocale();
117                 if (!TextUtils.isEmpty(locale)) {
118                     localeSet.add(locale);
119                 }
120             }
121         }
122 
123         // We come here after we have collected locales from existing user dictionary entries and
124         // enabled subtypes. If we already have the locale-without-country version of the system
125         // locale, we don't add the system locale to avoid confusion even though it's technically
126         // correct to add it.
127         if (!localeSet.contains(Locale.getDefault().getLanguage())) {
128             localeSet.add(Locale.getDefault().toString());
129         }
130 
131         return localeSet;
132     }
133 
134     @VisibleForTesting
getUserDictLocalesSet(Context context)135     TreeSet<String> getUserDictLocalesSet(Context context) {
136         return getUserDictionaryLocalesSet(context);
137     }
138 
139     /**
140      * Creates the entries that allow the user to go into the user dictionary for each locale.
141      */
createUserDictSettings()142     private void createUserDictSettings() {
143         final TreeSet<String> localeSet = getUserDictLocalesSet(mContext);
144         final int prefCount = mScreen.getPreferenceCount();
145         String prefKey;
146 
147         if (mLocale != null) {
148             // If the caller explicitly specify empty string as a locale, we'll show "all languages"
149             // in the list.
150             localeSet.add(mLocale);
151         }
152         if (localeSet.size() > 1) {
153             // Have an "All languages" entry in the languages list if there are two or more active
154             // languages
155             localeSet.add("");
156         }
157 
158         // Update the existing preferenceScreen according to the corresponding data set.
159         if (prefCount > 0) {
160             for (int i = prefCount - 1; i >= 0; i--) {
161                 prefKey = mScreen.getPreference(i).getKey();
162                 if (TextUtils.isEmpty(prefKey) || TextUtils.equals(KEY_ALL_LANGUAGE, prefKey)) {
163                     continue;
164                 }
165                 if (!localeSet.isEmpty() && localeSet.contains(prefKey)) {
166                     localeSet.remove(prefKey);
167                     continue;
168                 }
169                 mScreen.removePreference(mScreen.findPreference(prefKey));
170             }
171         }
172 
173         if (localeSet.isEmpty() && prefCount == 0) {
174             mScreen.addPreference(createUserDictionaryPreference(null));
175         } else {
176             for (String locale : localeSet) {
177                 final Preference pref = createUserDictionaryPreference(locale);
178                 if (mScreen.findPreference(pref.getKey()) == null) {
179                     mScreen.addPreference(pref);
180                 }
181             }
182         }
183     }
184 
185     /**
186      * Create a single User Dictionary Preference object, with its parameters set.
187      *
188      * @param locale The locale for which this user dictionary is for.
189      * @return The corresponding preference.
190      */
createUserDictionaryPreference(String locale)191     private Preference createUserDictionaryPreference(String locale) {
192         final String KEY_LOCALE = "locale";
193         final Preference newPref = new Preference(mScreen.getContext());
194         final Intent intent = new Intent(USER_DICTIONARY_SETTINGS_INTENT_ACTION);
195         if (locale == null) {
196             newPref.setTitle(Locale.getDefault().getDisplayName());
197             newPref.setKey(Locale.getDefault().toString());
198         } else {
199             if (TextUtils.isEmpty(locale)) {
200                 newPref.setTitle(mContext.getString(R.string.user_dict_settings_all_languages));
201                 newPref.setKey(KEY_ALL_LANGUAGE);
202                 newPref.setOrder(0);
203             } else {
204                 newPref.setTitle(Utils.createLocaleFromString(locale).getDisplayName());
205                 newPref.setKey(locale);
206             }
207             intent.putExtra(KEY_LOCALE, locale);
208             newPref.getExtras().putString(KEY_LOCALE, locale);
209         }
210         newPref.setIntent(intent);
211         newPref.setFragment(UserDictionarySettings.class.getName());
212         return newPref;
213     }
214 }
215