• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.app;
18 
19 import com.android.internal.R;
20 
21 import android.annotation.UnsupportedAppUsage;
22 import android.app.ActivityManager;
23 import android.app.IActivityManager;
24 import android.app.ListFragment;
25 import android.app.backup.BackupManager;
26 import android.content.Context;
27 import android.content.res.Configuration;
28 import android.content.res.Resources;
29 import android.os.Bundle;
30 import android.os.LocaleList;
31 import android.os.RemoteException;
32 import android.provider.Settings;
33 import android.util.Log;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.ArrayAdapter;
38 import android.widget.ListView;
39 import android.widget.TextView;
40 
41 import java.text.Collator;
42 import java.util.Collections;
43 import java.util.List;
44 import java.util.Locale;
45 import java.util.ArrayList;
46 
47 public class LocalePicker extends ListFragment {
48     private static final String TAG = "LocalePicker";
49     private static final boolean DEBUG = false;
50     private static final String[] pseudoLocales = { "en-XA", "ar-XB" };
51 
52     public static interface LocaleSelectionListener {
53         // You can add any argument if you really need it...
onLocaleSelected(Locale locale)54         public void onLocaleSelected(Locale locale);
55     }
56 
57     LocaleSelectionListener mListener;  // default to null
58 
59     public static class LocaleInfo implements Comparable<LocaleInfo> {
60         static final Collator sCollator = Collator.getInstance();
61 
62         String label;
63         final Locale locale;
64 
LocaleInfo(String label, Locale locale)65         public LocaleInfo(String label, Locale locale) {
66             this.label = label;
67             this.locale = locale;
68         }
69 
getLabel()70         public String getLabel() {
71             return label;
72         }
73 
74         @UnsupportedAppUsage
getLocale()75         public Locale getLocale() {
76             return locale;
77         }
78 
79         @Override
toString()80         public String toString() {
81             return this.label;
82         }
83 
84         @Override
compareTo(LocaleInfo another)85         public int compareTo(LocaleInfo another) {
86             return sCollator.compare(this.label, another.label);
87         }
88     }
89 
getSystemAssetLocales()90     public static String[] getSystemAssetLocales() {
91         return Resources.getSystem().getAssets().getLocales();
92     }
93 
getSupportedLocales(Context context)94     public static String[] getSupportedLocales(Context context) {
95         return context.getResources().getStringArray(R.array.supported_locales);
96     }
97 
getAllAssetLocales(Context context, boolean isInDeveloperMode)98     public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
99         final Resources resources = context.getResources();
100 
101         final String[] locales = getSystemAssetLocales();
102         List<String> localeList = new ArrayList<String>(locales.length);
103         Collections.addAll(localeList, locales);
104 
105         Collections.sort(localeList);
106         final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
107         final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
108 
109         final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
110         for (String locale : localeList) {
111             final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
112             if (l == null || "und".equals(l.getLanguage())
113                     || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
114                 continue;
115             }
116             // Don't show the pseudolocales unless we're in developer mode. http://b/17190407.
117             if (!isInDeveloperMode && LocaleList.isPseudoLocale(l)) {
118                 continue;
119             }
120 
121             if (localeInfos.isEmpty()) {
122                 if (DEBUG) {
123                     Log.v(TAG, "adding initial "+ toTitleCase(l.getDisplayLanguage(l)));
124                 }
125                 localeInfos.add(new LocaleInfo(toTitleCase(l.getDisplayLanguage(l)), l));
126             } else {
127                 // check previous entry:
128                 //  same lang and a country -> upgrade to full name and
129                 //    insert ours with full name
130                 //  diff lang -> insert ours with lang-only name
131                 final LocaleInfo previous = localeInfos.get(localeInfos.size() - 1);
132                 if (previous.locale.getLanguage().equals(l.getLanguage()) &&
133                         !previous.locale.getLanguage().equals("zz")) {
134                     if (DEBUG) {
135                         Log.v(TAG, "backing up and fixing " + previous.label + " to " +
136                                 getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
137                     }
138                     previous.label = toTitleCase(getDisplayName(
139                             previous.locale, specialLocaleCodes, specialLocaleNames));
140                     if (DEBUG) {
141                         Log.v(TAG, "  and adding "+ toTitleCase(
142                                 getDisplayName(l, specialLocaleCodes, specialLocaleNames)));
143                     }
144                     localeInfos.add(new LocaleInfo(toTitleCase(
145                             getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l));
146                 } else {
147                     String displayName = toTitleCase(l.getDisplayLanguage(l));
148                     if (DEBUG) {
149                         Log.v(TAG, "adding "+displayName);
150                     }
151                     localeInfos.add(new LocaleInfo(displayName, l));
152                 }
153             }
154         }
155 
156         Collections.sort(localeInfos);
157         return localeInfos;
158     }
159 
160     /**
161      * Constructs an Adapter object containing Locale information. Content is sorted by
162      * {@link LocaleInfo#label}.
163      */
constructAdapter(Context context)164     public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
165         return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
166     }
167 
constructAdapter(Context context, final int layoutId, final int fieldId)168     public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
169             final int layoutId, final int fieldId) {
170         boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
171                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
172         final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
173 
174         final LayoutInflater inflater =
175                 (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
176         return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
177             @Override
178             public View getView(int position, View convertView, ViewGroup parent) {
179                 View view;
180                 TextView text;
181                 if (convertView == null) {
182                     view = inflater.inflate(layoutId, parent, false);
183                     text = (TextView) view.findViewById(fieldId);
184                     view.setTag(text);
185                 } else {
186                     view = convertView;
187                     text = (TextView) view.getTag();
188                 }
189                 LocaleInfo item = getItem(position);
190                 text.setText(item.toString());
191                 text.setTextLocale(item.getLocale());
192 
193                 return view;
194             }
195         };
196     }
197 
198     private static String toTitleCase(String s) {
199         if (s.length() == 0) {
200             return s;
201         }
202 
203         return Character.toUpperCase(s.charAt(0)) + s.substring(1);
204     }
205 
206     private static String getDisplayName(
207             Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
208         String code = l.toString();
209 
210         for (int i = 0; i < specialLocaleCodes.length; i++) {
211             if (specialLocaleCodes[i].equals(code)) {
212                 return specialLocaleNames[i];
213             }
214         }
215 
216         return l.getDisplayName(l);
217     }
218 
219     @Override
220     public void onActivityCreated(final Bundle savedInstanceState) {
221         super.onActivityCreated(savedInstanceState);
222         final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
223         setListAdapter(adapter);
224     }
225 
226     public void setLocaleSelectionListener(LocaleSelectionListener listener) {
227         mListener = listener;
228     }
229 
230     @Override
231     public void onResume() {
232         super.onResume();
233         getListView().requestFocus();
234     }
235 
236     /**
237      * Each listener needs to call {@link #updateLocale(Locale)} to actually change the locale.
238      *
239      * We don't call {@link #updateLocale(Locale)} automatically, as it halt the system for
240      * a moment and some callers won't want it.
241      */
242     @Override
243     public void onListItemClick(ListView l, View v, int position, long id) {
244         if (mListener != null) {
245             final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
246             mListener.onLocaleSelected(locale);
247         }
248     }
249 
250     /**
251      * Requests the system to update the system locale. Note that the system looks halted
252      * for a while during the Locale migration, so the caller need to take care of it.
253      *
254      * @see #updateLocales(LocaleList)
255      */
256     @UnsupportedAppUsage
257     public static void updateLocale(Locale locale) {
258         updateLocales(new LocaleList(locale));
259     }
260 
261     /**
262      * Requests the system to update the list of system locales.
263      * Note that the system looks halted for a while during the Locale migration,
264      * so the caller need to take care of it.
265      */
266     @UnsupportedAppUsage
267     public static void updateLocales(LocaleList locales) {
268         try {
269             final IActivityManager am = ActivityManager.getService();
270             final Configuration config = am.getConfiguration();
271 
272             config.setLocales(locales);
273             config.userSetLocale = true;
274 
275             am.updatePersistentConfiguration(config);
276             // Trigger the dirty bit for the Settings Provider.
277             BackupManager.dataChanged("com.android.providers.settings");
278         } catch (RemoteException e) {
279             // Intentionally left blank
280         }
281     }
282 
283     /**
284      * Get the locale list.
285      *
286      * @return The locale list.
287      */
288     @UnsupportedAppUsage
289     public static LocaleList getLocales() {
290         try {
291             return ActivityManager.getService()
292                     .getConfiguration().getLocales();
293         } catch (RemoteException e) {
294             // If something went wrong
295             return LocaleList.getDefault();
296         }
297     }
298 }
299