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.customlocale2; 18 19 20 import java.util.ArrayList; 21 import java.util.Collections; 22 import java.util.Comparator; 23 import java.util.Locale; 24 25 import android.app.ActivityManager; 26 import android.app.AlertDialog; 27 import android.app.Dialog; 28 import android.app.IActivityManager; 29 import android.app.ListActivity; 30 import android.content.DialogInterface; 31 import android.content.Intent; 32 import android.content.SharedPreferences; 33 import android.content.res.Configuration; 34 import android.os.Bundle; 35 import android.os.RemoteException; 36 import android.util.Log; 37 import android.view.ContextMenu; 38 import android.view.View; 39 import android.view.ContextMenu.ContextMenuInfo; 40 import android.widget.ArrayAdapter; 41 import android.widget.Button; 42 import android.widget.ListAdapter; 43 import android.widget.ListView; 44 import android.widget.TextView; 45 import android.widget.Toast; 46 import android.widget.AdapterView.AdapterContextMenuInfo; 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 TAG = "CustomLocale"; 56 private static final boolean DEBUG = true; 57 58 private static final int DLG_REMOVE_ID = 0; 59 60 private static final String CUSTOM_LOCALES_SEP = " "; 61 private static final String CUSTOM_LOCALES = "custom_locales"; 62 63 /** Request code returned when the NewLocaleDialog activity finishes. */ 64 private static final int UPDATE_LIST = 42; 65 /** Menu item id for applying a locale */ 66 private static final int MENU_APPLY = 43; 67 /** Menu item id for removing a custom locale */ 68 private static final int MENU_REMOVE = 44; 69 70 /** List view displaying system and custom locales. */ 71 private ListView mListView; 72 /** Textview used to display current locale */ 73 private TextView mCurrentLocaleTextView; 74 /** Private shared preferences of this activity. */ 75 private SharedPreferences mPrefs; 76 77 private Button mRemoveLocaleButton; 78 private Button mSelectLocaleButton; 79 80 @Override onCreate(Bundle savedInstanceState)81 protected void onCreate(Bundle savedInstanceState) { 82 super.onCreate(savedInstanceState); 83 setContentView(R.layout.main); 84 85 mPrefs = getPreferences(MODE_PRIVATE); 86 87 Button addLocaleButton = (Button) findViewById(R.id.add_new_locale_button); 88 mRemoveLocaleButton = (Button) findViewById(R.id.remove_locale_button); 89 mSelectLocaleButton = (Button) findViewById(R.id.select_locale_button); 90 91 addLocaleButton.setOnClickListener(new View.OnClickListener() { 92 public void onClick(View v) { 93 onAddNewLocale(); 94 } 95 }); 96 97 mRemoveLocaleButton.setOnClickListener(new View.OnClickListener() { 98 @Override 99 public void onClick(View v) { 100 showDialog(DLG_REMOVE_ID); 101 } 102 }); 103 104 mSelectLocaleButton.setOnClickListener(new View.OnClickListener() { 105 @Override 106 public void onClick(View v) { 107 onSelectLocale(); 108 } 109 }); 110 111 mListView = (ListView) findViewById(android.R.id.list); 112 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 113 mListView.setItemsCanFocus(false); 114 mListView.setFocusable(true); 115 mListView.setFocusableInTouchMode(true); 116 mListView.requestFocus(); 117 setupLocaleList(); 118 119 mCurrentLocaleTextView = (TextView) findViewById(R.id.current_locale); 120 displayCurrentLocale(); 121 } 122 123 @Override onResume()124 protected void onResume() { 125 super.onResume(); 126 updateLocaleButtons(); 127 } 128 129 @Override onActivityResult(int requestCode, int resultCode, Intent data)130 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 131 super.onActivityResult(requestCode, resultCode, data); 132 133 if (requestCode == UPDATE_LIST && resultCode == RESULT_OK && data != null) { 134 String locale = data.getExtras().getString(NewLocaleDialog.INTENT_EXTRA_LOCALE); 135 if (locale != null && locale.length() > 0) { 136 // Get current custom locale list 137 String customLocales = mPrefs.getString(CUSTOM_LOCALES, null); 138 139 // Update 140 if (customLocales == null) { 141 customLocales = locale; 142 } else { 143 customLocales += CUSTOM_LOCALES_SEP + locale; 144 } 145 146 // Save prefs 147 if (DEBUG) { 148 Log.d(TAG, "add/customLocales: " + customLocales); 149 } 150 mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit(); 151 152 Toast.makeText(this, 153 getString(R.string.added_custom_locale_1s, locale), 154 Toast.LENGTH_SHORT) 155 .show(); 156 157 // Update list view 158 setupLocaleList(); 159 160 // Find the item to select it in the list view 161 checkLocaleInList(locale); 162 163 if (data.getExtras().getBoolean(NewLocaleDialog.INTENT_EXTRA_SELECT)) { 164 changeSystemLocale(locale); 165 } 166 } 167 } 168 } 169 170 @Override onListItemClick(ListView l, View v, int position, long id)171 protected void onListItemClick(ListView l, View v, int position, long id) { 172 super.onListItemClick(l, v, position, id); 173 updateLocaleButtons(); 174 } 175 176 @Override onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)177 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 178 super.onCreateContextMenu(menu, v, menuInfo); 179 180 if (menuInfo instanceof AdapterContextMenuInfo) { 181 int position = ((AdapterContextMenuInfo) menuInfo).position; 182 Object o = mListView.getItemAtPosition(position); 183 if (o instanceof LocaleInfo) { 184 if (((LocaleInfo) o).isCustom()) { 185 menu.setHeaderTitle("System Locale"); 186 menu.add(0, MENU_APPLY, 0, "Apply"); 187 } else { 188 menu.setHeaderTitle("Custom Locale"); 189 menu.add(0, MENU_APPLY, 0, "Apply"); 190 menu.add(0, MENU_REMOVE, 0, "Remove"); 191 } 192 } 193 } 194 } 195 196 @Override onCreateDialog(int id)197 protected Dialog onCreateDialog(int id) { 198 if (id == DLG_REMOVE_ID) { 199 return createRemoveLocaleDialog(); 200 } 201 return super.onCreateDialog(id); 202 } 203 204 //--- private parts --- 205 setupLocaleList()206 private void setupLocaleList() { 207 if (DEBUG) { 208 Log.d(TAG, "Update locate list"); 209 } 210 211 ArrayList<LocaleInfo> data = new ArrayList<LocaleInfo>(); 212 213 // Insert all system locales 214 String[] locales = getAssets().getLocales(); 215 for (String locale : locales) { 216 if (locale != null && locale.length() > 0) { 217 Locale loc = new Locale(locale); 218 data.add(new LocaleInfo(locale, loc.getDisplayName())); 219 } 220 } 221 locales = null; 222 223 // Insert all custom locales 224 String customLocales = mPrefs.getString(CUSTOM_LOCALES, ""); 225 if (DEBUG) { 226 Log.d(TAG, "customLocales: " + customLocales); 227 } 228 for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) { 229 if (locale != null && locale.length() > 0) { 230 Locale loc = new Locale(locale); 231 data.add(new LocaleInfo( 232 locale, 233 loc.getDisplayName(), 234 true /*custom*/)); 235 } 236 } 237 238 // Sort all locales by code 239 Collections.sort(data, new Comparator<LocaleInfo>() { 240 public int compare(LocaleInfo lhs, LocaleInfo rhs) { 241 return lhs.getLocale().compareTo(rhs.getLocale()); 242 } 243 }); 244 245 // Update the list view adapter 246 mListView.setAdapter(new ArrayAdapter<LocaleInfo>( 247 this, 248 android.R.layout.simple_list_item_single_choice, 249 data)); 250 updateLocaleButtons(); 251 } 252 changeSystemLocale(String locale)253 private void changeSystemLocale(String locale) { 254 if (ChangeLocale.changeSystemLocale(locale)) { 255 Toast.makeText(this, 256 getString(R.string.select_locale_1s, locale), 257 Toast.LENGTH_SHORT) 258 .show(); 259 } 260 } 261 displayCurrentLocale()262 private void displayCurrentLocale() { 263 try { 264 IActivityManager am = ActivityManager.getService(); 265 Configuration config = am.getConfiguration(); 266 267 if (config.locale != null) { 268 String text = String.format("%1$s - %2$s", 269 config.locale.toString(), 270 config.locale.getDisplayName()); 271 mCurrentLocaleTextView.setText(text); 272 273 checkLocaleInList(config.locale.toString()); 274 } 275 } catch (RemoteException e) { 276 if (DEBUG) { 277 Log.e(TAG, "get current locale failed", e); 278 } 279 } 280 } 281 282 /** Find the locale by code to select it in the list view */ checkLocaleInList(String locale)283 private void checkLocaleInList(String locale) { 284 ListAdapter a = mListView.getAdapter(); 285 for (int i = 0; i < a.getCount(); i++) { 286 Object o = a.getItem(i); 287 if (o instanceof LocaleInfo) { 288 String code = ((LocaleInfo) o).getLocale(); 289 if (code != null && code.equals(locale)) { 290 mListView.setSelection(i); 291 mListView.clearChoices(); 292 mListView.setItemChecked(i, true); 293 updateLocaleButtons(); 294 break; 295 } 296 } 297 } 298 } 299 getCheckedLocale()300 private LocaleInfo getCheckedLocale() { 301 int pos = mListView.getCheckedItemPosition(); 302 ListAdapter a = mListView.getAdapter(); 303 int n = a.getCount(); 304 if (pos >= 0 && pos < n) { 305 Object o = a.getItem(pos); 306 if (o instanceof LocaleInfo) { 307 return (LocaleInfo) o; 308 } 309 } 310 311 return null; 312 } 313 314 /** Update the Select/Remove buttons based on the currently checked locale. */ updateLocaleButtons()315 private void updateLocaleButtons() { 316 LocaleInfo info = getCheckedLocale(); 317 if (info != null) { 318 // Enable it 319 mSelectLocaleButton.setEnabled(true); 320 mSelectLocaleButton.setText( 321 getString(R.string.select_locale_1s_button, info.getLocale())); 322 323 // Enable the remove button only for custom locales and set the tag to the locale 324 mRemoveLocaleButton.setEnabled(info.isCustom()); 325 } else { 326 // If nothing is selected, disable the buttons 327 mSelectLocaleButton.setEnabled(false); 328 mSelectLocaleButton.setText(R.string.select_locale_button); 329 330 mRemoveLocaleButton.setEnabled(false); 331 } 332 } 333 334 /** Invoked by the button "Add new..." */ onAddNewLocale()335 private void onAddNewLocale() { 336 Intent i = new Intent(CustomLocaleActivity.this, NewLocaleDialog.class); 337 startActivityForResult(i, UPDATE_LIST); 338 } 339 340 /** Invoked by the button Select / mSelectLocaleButton */ onSelectLocale()341 private void onSelectLocale() { 342 LocaleInfo info = getCheckedLocale(); 343 if (info != null) { 344 changeSystemLocale(info.getLocale()); 345 } 346 } 347 348 /** 349 * Invoked by the button Remove / mRemoveLocaleButton. 350 * Creates a dialog to ask for confirmation before actually remove the custom locale. 351 */ createRemoveLocaleDialog()352 private Dialog createRemoveLocaleDialog() { 353 354 LocaleInfo info = getCheckedLocale(); 355 final String localeToRemove = info == null ? "<error>" : info.getLocale(); 356 357 AlertDialog.Builder b = new AlertDialog.Builder(this); 358 b.setMessage(getString(R.string.confirm_remove_locale_1s, localeToRemove)); 359 b.setCancelable(false); 360 b.setPositiveButton(R.string.confirm_remove_locale_yes, 361 new DialogInterface.OnClickListener() { 362 @Override 363 public void onClick(DialogInterface dialog, int which) { 364 removeCustomLocale(localeToRemove); 365 dismissDialog(DLG_REMOVE_ID); 366 } 367 }); 368 b.setNegativeButton(R.string.confirm_remove_locale_no, 369 new DialogInterface.OnClickListener() { 370 @Override 371 public void onClick(DialogInterface dialog, int which) { 372 dismissDialog(DLG_REMOVE_ID); 373 } 374 }); 375 376 return b.create(); 377 } 378 removeCustomLocale(String localeToRemove)379 private void removeCustomLocale(String localeToRemove) { 380 // Get current custom locale list 381 String oldLocales = mPrefs.getString(CUSTOM_LOCALES, ""); 382 383 if (DEBUG) { 384 Log.d(TAG, "Remove " + localeToRemove + " from custom locales: " + oldLocales); 385 } 386 387 // Update 388 StringBuilder sb = new StringBuilder(); 389 for (String locale : oldLocales.split(CUSTOM_LOCALES_SEP)) { 390 if (locale != null && locale.length() > 0 && !locale.equals(localeToRemove)) { 391 if (sb.length() > 0) { 392 sb.append(CUSTOM_LOCALES_SEP); 393 } 394 sb.append(locale); 395 } 396 } 397 398 String newLocales = sb.toString(); 399 if (!newLocales.equals(oldLocales)) { 400 // Save prefs 401 boolean ok = mPrefs.edit().putString(CUSTOM_LOCALES, newLocales).commit(); 402 if (DEBUG) { 403 Log.d(TAG, "Prefs commit:" + Boolean.toString(ok) + ". Saved: " + newLocales); 404 } 405 406 Toast.makeText(this, 407 getString(R.string.removed_custom_locale_1s, localeToRemove), 408 Toast.LENGTH_SHORT) 409 .show(); 410 411 // Update list view 412 setupLocaleList(); 413 } 414 } 415 416 /** 417 * Immutable structure that holds the information displayed by a list view item. 418 */ 419 private static class LocaleInfo { 420 private final String mLocale; 421 private final String mDisplayName; 422 private final boolean mIsCustom; 423 LocaleInfo(String locale, String displayName, boolean isCustom)424 public LocaleInfo(String locale, String displayName, boolean isCustom) { 425 mLocale = locale; 426 mDisplayName = displayName; 427 mIsCustom = isCustom; 428 } 429 LocaleInfo(String locale, String displayName)430 public LocaleInfo(String locale, String displayName) { 431 this(locale, displayName, false /*custom*/); 432 } 433 getLocale()434 public String getLocale() { 435 return mLocale; 436 } 437 isCustom()438 public boolean isCustom() { 439 return mIsCustom; 440 } 441 442 @Override toString()443 public String toString() { 444 StringBuilder sb = new StringBuilder(); 445 sb.append(mLocale) 446 .append(" - ") 447 .append(mDisplayName); 448 if (mIsCustom) { 449 sb.append(" [Custom]"); 450 } 451 return sb.toString(); 452 } 453 } 454 } 455