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}