LocalePicker.java revision 53daead5718a95953fc009782299bd5e544bf4e5
1/*
2 * Copyright (C) 2010 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.internal.app;
18
19import com.android.internal.R;
20
21import android.app.ActivityManagerNative;
22import android.app.IActivityManager;
23import android.app.ListFragment;
24import android.app.backup.BackupManager;
25import android.content.Context;
26import android.content.res.Configuration;
27import android.content.res.Resources;
28import android.os.Bundle;
29import android.os.RemoteException;
30import android.util.Log;
31import android.view.View;
32import android.widget.ArrayAdapter;
33import android.widget.ListView;
34
35import java.text.Collator;
36import java.util.Arrays;
37import java.util.Locale;
38
39public class LocalePicker extends ListFragment {
40    private static final String TAG = "LocalePicker";
41    private static final boolean DEBUG = false;
42
43    public static interface LocaleSelectionListener {
44        // You can add any argument if you really need it...
45        public void onLocaleSelected(Locale locale);
46    }
47
48    LocaleSelectionListener mListener;  // default to null
49
50    public static class LocaleInfo implements Comparable<LocaleInfo> {
51        static final Collator sCollator = Collator.getInstance();
52
53        String label;
54        Locale locale;
55
56        public LocaleInfo(String label, Locale locale) {
57            this.label = label;
58            this.locale = locale;
59        }
60
61        public String getLabel() {
62            return label;
63        }
64
65        public Locale getLocale() {
66            return locale;
67        }
68
69        @Override
70        public String toString() {
71            return this.label;
72        }
73
74        @Override
75        public int compareTo(LocaleInfo another) {
76            return sCollator.compare(this.label, another.label);
77        }
78    }
79
80    /**
81     * Constructs an Adapter object containing Locale information. Content is sorted by
82     * {@link LocaleInfo#label}.
83     */
84    public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
85        final Resources resources = context.getResources();
86        final String[] locales = context.getAssets().getLocales();
87        final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
88        final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
89        Arrays.sort(locales);
90        final int origSize = locales.length;
91        final LocaleInfo[] preprocess = new LocaleInfo[origSize];
92        int finalSize = 0;
93        for (int i = 0 ; i < origSize; i++ ) {
94            final String s = locales[i];
95            final int len = s.length();
96            if (len == 5) {
97                String language = s.substring(0, 2);
98                String country = s.substring(3, 5);
99                final Locale l = new Locale(language, country);
100
101                if (finalSize == 0) {
102                    if (DEBUG) {
103                        Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
104                    }
105                    preprocess[finalSize++] =
106                            new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l);
107                } else {
108                    // check previous entry:
109                    //  same lang and a country -> upgrade to full name and
110                    //    insert ours with full name
111                    //  diff lang -> insert ours with lang-only name
112                    if (preprocess[finalSize-1].locale.getLanguage().equals(
113                            language)) {
114                        if (DEBUG) {
115                            Log.v(TAG, "backing up and fixing "+
116                                    preprocess[finalSize-1].label+" to "+
117                                    getDisplayName(preprocess[finalSize-1].locale,
118                                            specialLocaleCodes, specialLocaleNames));
119                        }
120                        preprocess[finalSize-1].label = toTitleCase(
121                                getDisplayName(preprocess[finalSize-1].locale,
122                                        specialLocaleCodes, specialLocaleNames));
123                        if (DEBUG) {
124                            Log.v(TAG, "  and adding "+ toTitleCase(
125                                    getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
126                        }
127                        preprocess[finalSize++] =
128                                new LocaleInfo(toTitleCase(
129                                        getDisplayName(
130                                                l, specialLocaleCodes, specialLocaleNames)), l);
131                    } else {
132                        String displayName;
133                        if (s.equals("zz_ZZ")) {
134                            displayName = "Pseudo...";
135                        } else {
136                            displayName = toTitleCase(l.getDisplayLanguage(l));
137                        }
138                        if (DEBUG) {
139                            Log.v(TAG, "adding "+displayName);
140                        }
141                        preprocess[finalSize++] = new LocaleInfo(displayName, l);
142                    }
143                }
144            }
145        }
146
147        final LocaleInfo[] localeInfos = new LocaleInfo[finalSize];
148        for (int i = 0; i < finalSize; i++) {
149            localeInfos[i] = preprocess[i];
150        }
151        Arrays.sort(localeInfos);
152        final int layoutId = R.layout.locale_picker_item;
153        final int fieldId = R.id.locale;
154        return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos);
155    }
156
157    private static String toTitleCase(String s) {
158        if (s.length() == 0) {
159            return s;
160        }
161
162        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
163    }
164
165    private static String getDisplayName(
166            Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
167        String code = l.toString();
168
169        for (int i = 0; i < specialLocaleCodes.length; i++) {
170            if (specialLocaleCodes[i].equals(code)) {
171                return specialLocaleNames[i];
172            }
173        }
174
175        return l.getDisplayName(l);
176    }
177
178    @Override
179    public void onActivityCreated(final Bundle savedInstanceState) {
180        super.onActivityCreated(savedInstanceState);
181        final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
182        setListAdapter(adapter);
183    }
184
185    public void setLocaleSelectionListener(LocaleSelectionListener listener) {
186        mListener = listener;
187    }
188
189    @Override
190    public void onResume() {
191        super.onResume();
192        getListView().requestFocus();
193    }
194
195    /**
196     * Each listener needs to call {@link #updateLocale(Locale)} to actually change the locale.
197     *
198     * We don't call {@link #updateLocale(Locale)} automatically, as it halt the system for
199     * a moment and some callers won't want it.
200     */
201    @Override
202    public void onListItemClick(ListView l, View v, int position, long id) {
203        if (mListener != null) {
204            final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
205            mListener.onLocaleSelected(locale);
206        }
207    }
208
209    /**
210     * Requests the system to update the system locale. Note that the system looks halted
211     * for a while during the Locale migration, so the caller need to take care of it.
212     */
213    public static void updateLocale(Locale locale) {
214        try {
215            IActivityManager am = ActivityManagerNative.getDefault();
216            Configuration config = am.getConfiguration();
217
218            config.locale = locale;
219
220            // indicate this isn't some passing default - the user wants this remembered
221            config.userSetLocale = true;
222
223            am.updateConfiguration(config);
224            // Trigger the dirty bit for the Settings Provider.
225            BackupManager.dataChanged("com.android.providers.settings");
226        } catch (RemoteException e) {
227            // Intentionally left blank
228        }
229    }
230}