1/*
2 * Copyright (C) 2008-2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.inputmethod.deprecated.languageswitcher;
18
19import com.android.inputmethod.compat.SharedPreferencesCompat;
20import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
21import com.android.inputmethod.latin.DictionaryFactory;
22import com.android.inputmethod.latin.LocaleUtils;
23import com.android.inputmethod.latin.R;
24import com.android.inputmethod.latin.Settings;
25import com.android.inputmethod.latin.Utils;
26
27import org.xmlpull.v1.XmlPullParserException;
28
29import android.content.SharedPreferences;
30import android.content.SharedPreferences.Editor;
31import android.content.res.Resources;
32import android.os.Bundle;
33import android.preference.CheckBoxPreference;
34import android.preference.PreferenceActivity;
35import android.preference.PreferenceGroup;
36import android.preference.PreferenceManager;
37import android.text.TextUtils;
38import android.util.Pair;
39
40import java.io.IOException;
41import java.text.Collator;
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.HashMap;
45import java.util.Locale;
46import java.util.Map.Entry;
47import java.util.TreeMap;
48
49public class InputLanguageSelection extends PreferenceActivity {
50
51    private SharedPreferences mPrefs;
52    private String mSelectedLanguages;
53    private HashMap<CheckBoxPreference, Locale> mLocaleMap =
54            new HashMap<CheckBoxPreference, Locale>();
55
56    private static class LocaleEntry implements Comparable<Object> {
57        private static Collator sCollator = Collator.getInstance();
58
59        private String mLabel;
60        public final Locale mLocale;
61
62        public LocaleEntry(String label, Locale locale) {
63            this.mLabel = label;
64            this.mLocale = locale;
65        }
66
67        @Override
68        public String toString() {
69            return this.mLabel;
70        }
71
72        @Override
73        public int compareTo(Object o) {
74            return sCollator.compare(this.mLabel, ((LocaleEntry) o).mLabel);
75        }
76    }
77
78    @Override
79    protected void onCreate(Bundle icicle) {
80        super.onCreate(icicle);
81        addPreferencesFromResource(R.xml.language_prefs);
82        // Get the settings preferences
83        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
84        mSelectedLanguages = mPrefs.getString(Settings.PREF_SELECTED_LANGUAGES, "");
85        String[] languageList = mSelectedLanguages.split(",");
86        ArrayList<LocaleEntry> availableLanguages = getUniqueLocales();
87        PreferenceGroup parent = getPreferenceScreen();
88        final HashMap<Long, LocaleEntry> dictionaryIdLocaleMap = new HashMap<Long, LocaleEntry>();
89        final TreeMap<LocaleEntry, Boolean> localeHasDictionaryMap =
90                new TreeMap<LocaleEntry, Boolean>();
91        for (int i = 0; i < availableLanguages.size(); i++) {
92            LocaleEntry loc = availableLanguages.get(i);
93            Locale locale = loc.mLocale;
94            final Pair<Long, Boolean> hasDictionaryOrLayout = hasDictionaryOrLayout(locale);
95            final Long dictionaryId = hasDictionaryOrLayout.first;
96            final boolean hasLayout = hasDictionaryOrLayout.second;
97            final boolean hasDictionary = dictionaryId != null;
98            // Add this locale to the supported list if:
99            // 1) this locale has a layout/ 2) this locale has a dictionary
100            // If some locales have no layout but have a same dictionary, the shortest locale
101            // will be added to the supported list.
102            if (!hasLayout && !hasDictionary) {
103                continue;
104            }
105            if (hasLayout) {
106                localeHasDictionaryMap.put(loc, hasDictionary);
107            }
108            if (!hasDictionary) {
109                continue;
110            }
111            if (dictionaryIdLocaleMap.containsKey(dictionaryId)) {
112                final String newLocale = locale.toString();
113                final String oldLocale =
114                        dictionaryIdLocaleMap.get(dictionaryId).mLocale.toString();
115                // Check if this locale is more appropriate to be the candidate of the input locale.
116                if (oldLocale.length() <= newLocale.length() && !hasLayout) {
117                    // Don't add this new locale to the map<dictionary id, locale> if:
118                    // 1) the new locale's name is longer than the existing one, and
119                    // 2) the new locale doesn't have its layout
120                    continue;
121                }
122            }
123            dictionaryIdLocaleMap.put(dictionaryId, loc);
124        }
125
126        for (LocaleEntry localeEntry : dictionaryIdLocaleMap.values()) {
127            if (!localeHasDictionaryMap.containsKey(localeEntry)) {
128                localeHasDictionaryMap.put(localeEntry, true);
129            }
130        }
131
132        for (Entry<LocaleEntry, Boolean> entry : localeHasDictionaryMap.entrySet()) {
133            final LocaleEntry localeEntry = entry.getKey();
134            final Locale locale = localeEntry.mLocale;
135            final Boolean hasDictionary = entry.getValue();
136            CheckBoxPreference pref = new CheckBoxPreference(this);
137            pref.setTitle(localeEntry.mLabel);
138            boolean checked = isLocaleIn(locale, languageList);
139            pref.setChecked(checked);
140            if (hasDictionary) {
141                pref.setSummary(R.string.has_dictionary);
142            }
143            mLocaleMap.put(pref, locale);
144            parent.addPreference(pref);
145        }
146    }
147
148    private boolean isLocaleIn(Locale locale, String[] list) {
149        String lang = get5Code(locale);
150        for (int i = 0; i < list.length; i++) {
151            if (lang.equalsIgnoreCase(list[i])) return true;
152        }
153        return false;
154    }
155
156    private Pair<Long, Boolean> hasDictionaryOrLayout(Locale locale) {
157        if (locale == null) return new Pair<Long, Boolean>(null, false);
158        final Resources res = getResources();
159        final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale);
160        final Long dictionaryId = DictionaryFactory.getDictionaryId(this, locale);
161        boolean hasLayout = false;
162
163        try {
164            final String localeStr = locale.toString();
165            final String[] layoutCountryCodes = KeyboardBuilder.parseKeyboardLocale(
166                    this, R.xml.kbd_qwerty).split(",", -1);
167            if (!TextUtils.isEmpty(localeStr) && layoutCountryCodes.length > 0) {
168                for (String s : layoutCountryCodes) {
169                    if (s.equals(localeStr)) {
170                        hasLayout = true;
171                        break;
172                    }
173                }
174            }
175        } catch (XmlPullParserException e) {
176        } catch (IOException e) {
177        }
178        LocaleUtils.setSystemLocale(res, saveLocale);
179        return new Pair<Long, Boolean>(dictionaryId, hasLayout);
180    }
181
182    private String get5Code(Locale locale) {
183        String country = locale.getCountry();
184        return locale.getLanguage()
185                + (TextUtils.isEmpty(country) ? "" : "_" + country);
186    }
187
188    @Override
189    protected void onResume() {
190        super.onResume();
191    }
192
193    @Override
194    protected void onPause() {
195        super.onPause();
196        // Save the selected languages
197        String checkedLanguages = "";
198        PreferenceGroup parent = getPreferenceScreen();
199        int count = parent.getPreferenceCount();
200        for (int i = 0; i < count; i++) {
201            CheckBoxPreference pref = (CheckBoxPreference) parent.getPreference(i);
202            if (pref.isChecked()) {
203                checkedLanguages += get5Code(mLocaleMap.get(pref)) + ",";
204            }
205        }
206        if (checkedLanguages.length() < 1) checkedLanguages = null; // Save null
207        Editor editor = mPrefs.edit();
208        editor.putString(Settings.PREF_SELECTED_LANGUAGES, checkedLanguages);
209        SharedPreferencesCompat.apply(editor);
210    }
211
212    public ArrayList<LocaleEntry> getUniqueLocales() {
213        String[] locales = getAssets().getLocales();
214        Arrays.sort(locales);
215        ArrayList<LocaleEntry> uniqueLocales = new ArrayList<LocaleEntry>();
216
217        final int origSize = locales.length;
218        LocaleEntry[] preprocess = new LocaleEntry[origSize];
219        int finalSize = 0;
220        for (int i = 0 ; i < origSize; i++ ) {
221            String s = locales[i];
222            int len = s.length();
223            String language = "";
224            String country = "";
225            if (len == 5) {
226                language = s.substring(0, 2);
227                country = s.substring(3, 5);
228            } else if (len < 5) {
229                language = s;
230            }
231            Locale l = new Locale(language, country);
232
233            // Exclude languages that are not relevant to LatinIME
234            if (TextUtils.isEmpty(language)) {
235                continue;
236            }
237
238            if (finalSize == 0) {
239                preprocess[finalSize++] =
240                        new LocaleEntry(Utils.getFullDisplayName(l, false), l);
241            } else {
242                if (s.equals("zz_ZZ")) {
243                    // ignore this locale
244                } else {
245                    final String displayName = Utils.getFullDisplayName(l, false);
246                    preprocess[finalSize++] = new LocaleEntry(displayName, l);
247                }
248            }
249        }
250        for (int i = 0; i < finalSize ; i++) {
251            uniqueLocales.add(preprocess[i]);
252        }
253        return uniqueLocales;
254    }
255}
256