1 /* 2 * Copyright (C) 2009 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.customlocale; 18 19 20 import android.app.ActivityManagerNative; 21 import android.app.IActivityManager; 22 import android.app.ListActivity; 23 import android.content.Intent; 24 import android.content.SharedPreferences; 25 import android.content.res.Configuration; 26 import android.os.Bundle; 27 import android.os.RemoteException; 28 import android.util.Log; 29 import android.view.ContextMenu; 30 import android.view.MenuItem; 31 import android.view.View; 32 import android.view.ContextMenu.ContextMenuInfo; 33 import android.widget.Button; 34 import android.widget.ListAdapter; 35 import android.widget.ListView; 36 import android.widget.SimpleAdapter; 37 import android.widget.TextView; 38 import android.widget.Toast; 39 import android.widget.AdapterView.AdapterContextMenuInfo; 40 41 import java.util.ArrayList; 42 import java.util.Collections; 43 import java.util.Comparator; 44 import java.util.HashMap; 45 import java.util.Locale; 46 import java.util.Map; 47 48 /** 49 * Displays the list of system locales as well as maintain a custom list of user 50 * locales. The user can select a locale and apply it or it can create or remove 51 * a custom locale. 52 */ 53 public class CustomLocaleActivity extends ListActivity { 54 55 private static final String CUSTOM_LOCALES_SEP = " "; 56 private static final String CUSTOM_LOCALES = "custom_locales"; 57 private static final String KEY_CUSTOM = "custom"; 58 private static final String KEY_NAME = "name"; 59 private static final String KEY_CODE = "code"; 60 61 private static final String TAG = "LocaleSetup"; 62 private static final boolean DEBUG = true; 63 64 /** Request code returned when the NewLocaleDialog activity finishes. */ 65 private static final int UPDATE_LIST = 42; 66 /** Menu item id for applying a locale */ 67 private static final int MENU_APPLY = 43; 68 /** Menu item id for removing a custom locale */ 69 private static final int MENU_REMOVE = 44; 70 71 /** List view displaying system and custom locales. */ 72 private ListView mListView; 73 /** Textview used to display current locale */ 74 private TextView mCurrentLocaleTextView; 75 /** Private shared preferences of this activity. */ 76 private SharedPreferences mPrefs; 77 78 @Override 79 protected void onCreate(Bundle savedInstanceState) { 80 super.onCreate(savedInstanceState); 81 setContentView(R.layout.main); 82 83 mPrefs = getPreferences(MODE_PRIVATE); 84 85 Button newLocaleButton = (Button) findViewById(R.id.new_locale); 86 87 newLocaleButton.setOnClickListener(new View.OnClickListener() { 88 public void onClick(View v) { 89 Intent i = new Intent(CustomLocaleActivity.this, NewLocaleDialog.class); 90 startActivityForResult(i, UPDATE_LIST); 91 } 92 }); 93 94 mListView = (ListView) findViewById(android.R.id.list); 95 mListView.setFocusable(true); 96 mListView.setFocusableInTouchMode(true); 97 mListView.requestFocus(); 98 registerForContextMenu(mListView); 99 setupLocaleList(); 100 101 mCurrentLocaleTextView = (TextView) findViewById(R.id.current_locale); 102 displayCurrentLocale(); 103 } 104 105 @SuppressWarnings("unchecked") 106 @Override 107 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 108 super.onActivityResult(requestCode, resultCode, data); 109 110 if (requestCode == UPDATE_LIST && resultCode == RESULT_OK && data != null) { 111 String locale = data.getExtras().getString(NewLocaleDialog.INTENT_EXTRA_LOCALE); 112 if (locale != null && locale.length() > 0) { 113 // Get current custom locale list 114 String customLocales = mPrefs.getString(CUSTOM_LOCALES, null); 115 116 // Update 117 if (customLocales == null) { 118 customLocales = locale; 119 } else { 120 customLocales += CUSTOM_LOCALES_SEP + locale; 121 } 122 123 // Save prefs 124 if (DEBUG) { 125 Log.d(TAG, "add/customLocales: " + customLocales); 126 } 127 mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit(); 128 129 Toast.makeText(this, "Added custom locale: " + locale, Toast.LENGTH_SHORT).show(); 130 131 // Update list view 132 setupLocaleList(); 133 134 // Find the item to select it in the list view 135 ListAdapter a = mListView.getAdapter(); 136 for (int i = 0; i < a.getCount(); i++) { 137 Object o = a.getItem(i); 138 if (o instanceof Map<?, ?>) { 139 String code = ((Map<String, String>) o).get(KEY_CODE); 140 if (code != null && code.equals(locale)) { 141 mListView.setSelection(i); 142 break; 143 } 144 } 145 } 146 147 if (data.getExtras().getBoolean(NewLocaleDialog.INTENT_EXTRA_SELECT)) { 148 selectLocale(locale); 149 } 150 } 151 } 152 } 153 154 private void setupLocaleList() { 155 if (DEBUG) { 156 Log.d(TAG, "Update locate list"); 157 } 158 159 ArrayList<Map<String, String>> data = new ArrayList<Map<String, String>>(); 160 161 // Insert all system locales 162 String[] locales = getAssets().getLocales(); 163 for (String locale : locales) { 164 Locale loc = new Locale(locale); 165 166 Map<String, String> map = new HashMap<String, String>(1); 167 map.put(KEY_CODE, locale); 168 map.put(KEY_NAME, loc.getDisplayName()); 169 data.add(map); 170 } 171 locales = null; 172 173 // Insert all custom locales 174 String customLocales = mPrefs.getString(CUSTOM_LOCALES, ""); 175 if (DEBUG) { 176 Log.d(TAG, "customLocales: " + customLocales); 177 } 178 for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) { 179 if (locale != null && locale.length() > 0) { 180 Locale loc = new Locale(locale); 181 182 Map<String, String> map = new HashMap<String, String>(1); 183 map.put(KEY_CODE, locale); 184 map.put(KEY_NAME, loc.getDisplayName() + " [Custom]"); 185 // the presence of the "custom" key marks it as custom. 186 map.put(KEY_CUSTOM, ""); 187 data.add(map); 188 } 189 } 190 191 // Sort all locales by code 192 Collections.sort(data, new Comparator<Map<String, String>>() { 193 public int compare(Map<String, String> lhs, Map<String, String> rhs) { 194 return lhs.get(KEY_CODE).compareTo(rhs.get(KEY_CODE)); 195 } 196 }); 197 198 // Update the list view adapter 199 mListView.setAdapter(new SimpleAdapter(this, data, R.layout.list_item, new String[] { 200 KEY_CODE, KEY_NAME}, new int[] {R.id.locale_code, R.id.locale_name})); 201 } 202 203 @SuppressWarnings("unchecked") 204 @Override 205 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 206 super.onCreateContextMenu(menu, v, menuInfo); 207 208 if (menuInfo instanceof AdapterContextMenuInfo) { 209 int position = ((AdapterContextMenuInfo) menuInfo).position; 210 Object o = mListView.getItemAtPosition(position); 211 if (o instanceof Map<?, ?>) { 212 String locale = ((Map<String, String>) o).get(KEY_CODE); 213 String custom = ((Map<String, String>) o).get(KEY_CUSTOM); 214 215 if (custom == null) { 216 menu.setHeaderTitle("System Locale"); 217 menu.add(0, MENU_APPLY, 0, "Apply"); 218 } else { 219 menu.setHeaderTitle("Custom Locale"); 220 menu.add(0, MENU_APPLY, 0, "Apply"); 221 menu.add(0, MENU_REMOVE, 0, "Remove"); 222 } 223 } 224 } 225 } 226 227 @SuppressWarnings("unchecked") 228 @Override 229 public boolean onContextItemSelected(MenuItem item) { 230 231 String pendingLocale = null; 232 boolean is_custom = false; 233 234 ContextMenuInfo menuInfo = item.getMenuInfo(); 235 if (menuInfo instanceof AdapterContextMenuInfo) { 236 int position = ((AdapterContextMenuInfo) menuInfo).position; 237 Object o = mListView.getItemAtPosition(position); 238 if (o instanceof Map<?, ?>) { 239 pendingLocale = ((Map<String, String>) o).get(KEY_CODE); 240 is_custom = ((Map<String, String>) o).get(KEY_CUSTOM) != null; 241 } 242 } 243 244 if (pendingLocale == null) { 245 // should never happen 246 return super.onContextItemSelected(item); 247 } 248 249 if (item.getItemId() == MENU_REMOVE) { 250 // Get current custom locale list 251 String customLocales = mPrefs.getString(CUSTOM_LOCALES, ""); 252 253 if (DEBUG) { 254 Log.d(TAG, "Remove " + pendingLocale + " from custom locales: " + customLocales); 255 } 256 257 // Update 258 StringBuilder sb = new StringBuilder(); 259 for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) { 260 if (locale != null && locale.length() > 0 && !locale.equals(pendingLocale)) { 261 if (sb.length() > 0) { 262 sb.append(CUSTOM_LOCALES_SEP); 263 } 264 sb.append(locale); 265 } 266 } 267 String newLocales = sb.toString(); 268 if (!newLocales.equals(customLocales)) { 269 // Save prefs 270 mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit(); 271 272 Toast.makeText(this, "Removed custom locale: " + pendingLocale, Toast.LENGTH_SHORT) 273 .show(); 274 } 275 276 } else if (item.getItemId() == MENU_APPLY) { 277 selectLocale(pendingLocale); 278 } 279 280 return super.onContextItemSelected(item); 281 } 282 283 private void selectLocale(String locale) { 284 if (DEBUG) { 285 Log.d(TAG, "Select locale " + locale); 286 } 287 288 try { 289 IActivityManager am = ActivityManagerNative.getDefault(); 290 Configuration config = am.getConfiguration(); 291 292 Locale loc = null; 293 294 String[] langCountry = locale.split("_"); 295 if (langCountry.length == 2) { 296 loc = new Locale(langCountry[0], langCountry[1]); 297 } else { 298 loc = new Locale(locale); 299 } 300 301 config.locale = loc; 302 303 // indicate this isn't some passing default - the user wants this 304 // remembered 305 config.userSetLocale = true; 306 307 am.updateConfiguration(config); 308 309 Toast.makeText(this, "Select locale: " + locale, Toast.LENGTH_SHORT).show(); 310 } catch (RemoteException e) { 311 if (DEBUG) { 312 Log.e(TAG, "Select locale failed", e); 313 } 314 } 315 } 316 317 private void displayCurrentLocale() { 318 try { 319 IActivityManager am = ActivityManagerNative.getDefault(); 320 Configuration config = am.getConfiguration(); 321 322 if (config.locale != null) { 323 String text = String.format("%s - %s", 324 config.locale.toString(), 325 config.locale.getDisplayName()); 326 mCurrentLocaleTextView.setText(text); 327 } 328 } catch (RemoteException e) { 329 if (DEBUG) { 330 Log.e(TAG, "get current locale failed", e); 331 } 332 } 333 } 334 } 335