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