blob: 75174246cd99ba4c8fa1030b7332f55a2bf82424 [file] [log] [blame]
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -07001/*
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
Daisuke Miyakawa88b73492010-09-15 08:31:54 -070019import com.android.internal.R;
20
Andrei Onea15884392019-03-22 17:28:11 +000021import android.annotation.UnsupportedAppUsage;
Sudheer Shankadc589ac2016-11-10 15:30:17 -080022import android.app.ActivityManager;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070023import android.app.IActivityManager;
24import android.app.ListFragment;
25import android.app.backup.BackupManager;
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070026import android.content.Context;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070027import android.content.res.Configuration;
28import android.content.res.Resources;
29import android.os.Bundle;
Yohei Yukawa23cbe852016-05-17 16:42:58 -070030import android.os.LocaleList;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070031import android.os.RemoteException;
Elliott Hughesdee5cde2014-08-22 11:45:33 -070032import android.provider.Settings;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070033import android.util.Log;
Victoria Lease02033412012-08-20 17:16:09 -070034import android.view.LayoutInflater;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070035import android.view.View;
Victoria Lease02033412012-08-20 17:16:09 -070036import android.view.ViewGroup;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070037import android.widget.ArrayAdapter;
38import android.widget.ListView;
Victoria Lease02033412012-08-20 17:16:09 -070039import android.widget.TextView;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070040
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070041import java.text.Collator;
Narayan Kamathb27c1372014-07-01 10:56:20 +010042import java.util.Collections;
43import java.util.List;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070044import java.util.Locale;
Benjamin Poiesz39f96f92013-04-04 16:07:33 -070045import java.util.ArrayList;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070046
47public class LocalePicker extends ListFragment {
48 private static final String TAG = "LocalePicker";
49 private static final boolean DEBUG = false;
Mihai Nita45137602015-12-22 23:13:43 -080050 private static final String[] pseudoLocales = { "en-XA", "ar-XB" };
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070051
52 public static interface LocaleSelectionListener {
53 // You can add any argument if you really need it...
Daisuke Miyakawa88b73492010-09-15 08:31:54 -070054 public void onLocaleSelected(Locale locale);
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070055 }
56
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070057 LocaleSelectionListener mListener; // default to null
58
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070059 public static class LocaleInfo implements Comparable<LocaleInfo> {
60 static final Collator sCollator = Collator.getInstance();
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070061
62 String label;
Mihai Nita45137602015-12-22 23:13:43 -080063 final Locale locale;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070064
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070065 public LocaleInfo(String label, Locale locale) {
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070066 this.label = label;
67 this.locale = locale;
68 }
69
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070070 public String getLabel() {
71 return label;
72 }
73
Andrei Onea15884392019-03-22 17:28:11 +000074 @UnsupportedAppUsage
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070075 public Locale getLocale() {
76 return locale;
77 }
78
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070079 @Override
80 public String toString() {
81 return this.label;
82 }
83
84 @Override
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070085 public int compareTo(LocaleInfo another) {
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -070086 return sCollator.compare(this.label, another.label);
87 }
88 }
89
Mihai Nita45137602015-12-22 23:13:43 -080090 public static String[] getSystemAssetLocales() {
91 return Resources.getSystem().getAssets().getLocales();
92 }
93
94 public static String[] getSupportedLocales(Context context) {
95 return context.getResources().getStringArray(R.array.supported_locales);
96 }
97
Narayan Kamath37197df2014-07-14 16:28:24 +010098 public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
Daisuke Miyakawa53daead2010-10-26 15:53:04 -070099 final Resources resources = context.getResources();
Benjamin Poiesz39f96f92013-04-04 16:07:33 -0700100
Mihai Nita45137602015-12-22 23:13:43 -0800101 final String[] locales = getSystemAssetLocales();
Narayan Kamathb27c1372014-07-01 10:56:20 +0100102 List<String> localeList = new ArrayList<String>(locales.length);
103 Collections.addAll(localeList, locales);
Elliott Hughesdee5cde2014-08-22 11:45:33 -0700104
Narayan Kamathb27c1372014-07-01 10:56:20 +0100105 Collections.sort(localeList);
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700106 final String[] specialLocaleCodes = resources.getStringArray(R.array.special_locale_codes);
107 final String[] specialLocaleNames = resources.getStringArray(R.array.special_locale_names);
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700108
Narayan Kamathb27c1372014-07-01 10:56:20 +0100109 final ArrayList<LocaleInfo> localeInfos = new ArrayList<LocaleInfo>(localeList.size());
110 for (String locale : localeList) {
111 final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
Narayan Kamath80b57412014-07-03 13:26:07 +0100112 if (l == null || "und".equals(l.getLanguage())
113 || l.getLanguage().isEmpty() || l.getCountry().isEmpty()) {
Narayan Kamathb27c1372014-07-01 10:56:20 +0100114 continue;
115 }
Igor Viarheichyk025402c2017-10-06 12:46:49 -0700116 // Don't show the pseudolocales unless we're in developer mode. http://b/17190407.
117 if (!isInDeveloperMode && LocaleList.isPseudoLocale(l)) {
118 continue;
119 }
Narayan Kamathb27c1372014-07-01 10:56:20 +0100120
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")) {
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700134 if (DEBUG) {
Narayan Kamathb27c1372014-07-01 10:56:20 +0100135 Log.v(TAG, "backing up and fixing " + previous.label + " to " +
136 getDisplayName(previous.locale, specialLocaleCodes, specialLocaleNames));
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700137 }
Narayan Kamathb27c1372014-07-01 10:56:20 +0100138 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));
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700146 } else {
Elliott Hughesdee5cde2014-08-22 11:45:33 -0700147 String displayName = toTitleCase(l.getDisplayLanguage(l));
Narayan Kamathb27c1372014-07-01 10:56:20 +0100148 if (DEBUG) {
149 Log.v(TAG, "adding "+displayName);
150 }
151 localeInfos.add(new LocaleInfo(displayName, l));
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700152 }
153 }
154 }
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700155
Narayan Kamathb27c1372014-07-01 10:56:20 +0100156 Collections.sort(localeInfos);
Narayan Kamath37197df2014-07-14 16:28:24 +0100157 return localeInfos;
158 }
159
Elliott Hughesdee5cde2014-08-22 11:45:33 -0700160 /**
161 * Constructs an Adapter object containing Locale information. Content is sorted by
162 * {@link LocaleInfo#label}.
163 */
164 public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) {
165 return constructAdapter(context, R.layout.locale_picker_item, R.id.locale);
166 }
167
Narayan Kamath37197df2014-07-14 16:28:24 +0100168 public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
Elliott Hughesdee5cde2014-08-22 11:45:33 -0700169 final int layoutId, final int fieldId) {
170 boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(),
171 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
Narayan Kamath37197df2014-07-14 16:28:24 +0100172 final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
173
Victoria Lease02033412012-08-20 17:16:09 -0700174 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 };
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700196 }
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
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700206 private static String getDisplayName(
207 Locale l, String[] specialLocaleCodes, String[] specialLocaleNames) {
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700208 String code = l.toString();
209
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700210 for (int i = 0; i < specialLocaleCodes.length; i++) {
211 if (specialLocaleCodes[i].equals(code)) {
212 return specialLocaleNames[i];
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700213 }
214 }
215
216 return l.getDisplayName(l);
217 }
218
219 @Override
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700220 public void onActivityCreated(final Bundle savedInstanceState) {
221 super.onActivityCreated(savedInstanceState);
Elliott Hughesdee5cde2014-08-22 11:45:33 -0700222 final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity());
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700223 setListAdapter(adapter);
224 }
225
226 public void setLocaleSelectionListener(LocaleSelectionListener listener) {
227 mListener = listener;
228 }
229
230 @Override
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700231 public void onResume() {
232 super.onResume();
233 getListView().requestFocus();
234 }
235
Daisuke Miyakawa88b73492010-09-15 08:31:54 -0700236 /**
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 */
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700242 @Override
243 public void onListItemClick(ListView l, View v, int position, long id) {
Daisuke Miyakawa88b73492010-09-15 08:31:54 -0700244 if (mListener != null) {
Daisuke Miyakawa53daead2010-10-26 15:53:04 -0700245 final Locale locale = ((LocaleInfo)getListAdapter().getItem(position)).locale;
246 mListener.onLocaleSelected(locale);
Daisuke Miyakawa88b73492010-09-15 08:31:54 -0700247 }
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.
Mihai Nita45137602015-12-22 23:13:43 -0800253 *
254 * @see #updateLocales(LocaleList)
Daisuke Miyakawa88b73492010-09-15 08:31:54 -0700255 */
Andrei Onea15884392019-03-22 17:28:11 +0000256 @UnsupportedAppUsage
Daisuke Miyakawa88b73492010-09-15 08:31:54 -0700257 public static void updateLocale(Locale locale) {
Mihai Nita45137602015-12-22 23:13:43 -0800258 updateLocales(new LocaleList(locale));
259 }
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700260
Mihai Nita45137602015-12-22 23:13:43 -0800261 /**
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 */
Andrei Onea15884392019-03-22 17:28:11 +0000266 @UnsupportedAppUsage
Mihai Nita45137602015-12-22 23:13:43 -0800267 public static void updateLocales(LocaleList locales) {
268 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800269 final IActivityManager am = ActivityManager.getService();
Mihai Nita45137602015-12-22 23:13:43 -0800270 final Configuration config = am.getConfiguration();
271
272 config.setLocales(locales);
Narayan Kamath70392812015-03-23 17:13:18 +0000273 config.userSetLocale = true;
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700274
Seigo Nonakabd5cbdd2016-01-25 19:40:28 +0900275 am.updatePersistentConfiguration(config);
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700276 // Trigger the dirty bit for the Settings Provider.
277 BackupManager.dataChanged("com.android.providers.settings");
278 } catch (RemoteException e) {
279 // Intentionally left blank
280 }
Daisuke Miyakawaa43b74a2010-08-31 13:43:32 -0700281 }
Mihai Nita45137602015-12-22 23:13:43 -0800282
283 /**
284 * Get the locale list.
285 *
286 * @return The locale list.
287 */
Andrei Onea15884392019-03-22 17:28:11 +0000288 @UnsupportedAppUsage
Mihai Nita45137602015-12-22 23:13:43 -0800289 public static LocaleList getLocales() {
290 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800291 return ActivityManager.getService()
Mihai Nita45137602015-12-22 23:13:43 -0800292 .getConfiguration().getLocales();
293 } catch (RemoteException e) {
294 // If something went wrong
295 return LocaleList.getDefault();
296 }
297 }
Victoria Lease02033412012-08-20 17:16:09 -0700298}