| /* | |
| * Copyright (C) 2009 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.android.customlocale2; | |
| import java.util.ArrayList; | |
| import java.util.Collections; | |
| import java.util.Comparator; | |
| import java.util.Locale; | |
| import android.app.ActivityManager; | |
| import android.app.AlertDialog; | |
| import android.app.Dialog; | |
| import android.app.IActivityManager; | |
| import android.app.ListActivity; | |
| import android.content.DialogInterface; | |
| import android.content.Intent; | |
| import android.content.SharedPreferences; | |
| import android.content.res.Configuration; | |
| import android.os.Bundle; | |
| import android.os.RemoteException; | |
| import android.util.Log; | |
| import android.view.ContextMenu; | |
| import android.view.View; | |
| import android.view.ContextMenu.ContextMenuInfo; | |
| import android.widget.ArrayAdapter; | |
| import android.widget.Button; | |
| import android.widget.ListAdapter; | |
| import android.widget.ListView; | |
| import android.widget.TextView; | |
| import android.widget.Toast; | |
| import android.widget.AdapterView.AdapterContextMenuInfo; | |
| /** | |
| * Displays the list of system locales as well as maintain a custom list of user | |
| * locales. The user can select a locale and apply it or it can create or remove | |
| * a custom locale. | |
| */ | |
| public class CustomLocaleActivity extends ListActivity { | |
| private static final String TAG = "CustomLocale"; | |
| private static final boolean DEBUG = true; | |
| private static final int DLG_REMOVE_ID = 0; | |
| private static final String CUSTOM_LOCALES_SEP = " "; | |
| private static final String CUSTOM_LOCALES = "custom_locales"; | |
| /** Request code returned when the NewLocaleDialog activity finishes. */ | |
| private static final int UPDATE_LIST = 42; | |
| /** Menu item id for applying a locale */ | |
| private static final int MENU_APPLY = 43; | |
| /** Menu item id for removing a custom locale */ | |
| private static final int MENU_REMOVE = 44; | |
| /** List view displaying system and custom locales. */ | |
| private ListView mListView; | |
| /** Textview used to display current locale */ | |
| private TextView mCurrentLocaleTextView; | |
| /** Private shared preferences of this activity. */ | |
| private SharedPreferences mPrefs; | |
| private Button mRemoveLocaleButton; | |
| private Button mSelectLocaleButton; | |
| @Override | |
| protected void onCreate(Bundle savedInstanceState) { | |
| super.onCreate(savedInstanceState); | |
| setContentView(R.layout.main); | |
| mPrefs = getPreferences(MODE_PRIVATE); | |
| Button addLocaleButton = (Button) findViewById(R.id.add_new_locale_button); | |
| mRemoveLocaleButton = (Button) findViewById(R.id.remove_locale_button); | |
| mSelectLocaleButton = (Button) findViewById(R.id.select_locale_button); | |
| addLocaleButton.setOnClickListener(new View.OnClickListener() { | |
| public void onClick(View v) { | |
| onAddNewLocale(); | |
| } | |
| }); | |
| mRemoveLocaleButton.setOnClickListener(new View.OnClickListener() { | |
| @Override | |
| public void onClick(View v) { | |
| showDialog(DLG_REMOVE_ID); | |
| } | |
| }); | |
| mSelectLocaleButton.setOnClickListener(new View.OnClickListener() { | |
| @Override | |
| public void onClick(View v) { | |
| onSelectLocale(); | |
| } | |
| }); | |
| mListView = (ListView) findViewById(android.R.id.list); | |
| mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); | |
| mListView.setItemsCanFocus(false); | |
| mListView.setFocusable(true); | |
| mListView.setFocusableInTouchMode(true); | |
| mListView.requestFocus(); | |
| setupLocaleList(); | |
| mCurrentLocaleTextView = (TextView) findViewById(R.id.current_locale); | |
| displayCurrentLocale(); | |
| } | |
| @Override | |
| protected void onResume() { | |
| super.onResume(); | |
| updateLocaleButtons(); | |
| } | |
| @Override | |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |
| super.onActivityResult(requestCode, resultCode, data); | |
| if (requestCode == UPDATE_LIST && resultCode == RESULT_OK && data != null) { | |
| String locale = data.getExtras().getString(NewLocaleDialog.INTENT_EXTRA_LOCALE); | |
| if (locale != null && locale.length() > 0) { | |
| // Get current custom locale list | |
| String customLocales = mPrefs.getString(CUSTOM_LOCALES, null); | |
| // Update | |
| if (customLocales == null) { | |
| customLocales = locale; | |
| } else { | |
| customLocales += CUSTOM_LOCALES_SEP + locale; | |
| } | |
| // Save prefs | |
| if (DEBUG) { | |
| Log.d(TAG, "add/customLocales: " + customLocales); | |
| } | |
| mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit(); | |
| Toast.makeText(this, | |
| getString(R.string.added_custom_locale_1s, locale), | |
| Toast.LENGTH_SHORT) | |
| .show(); | |
| // Update list view | |
| setupLocaleList(); | |
| // Find the item to select it in the list view | |
| checkLocaleInList(locale); | |
| if (data.getExtras().getBoolean(NewLocaleDialog.INTENT_EXTRA_SELECT)) { | |
| changeSystemLocale(locale); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| protected void onListItemClick(ListView l, View v, int position, long id) { | |
| super.onListItemClick(l, v, position, id); | |
| updateLocaleButtons(); | |
| } | |
| @Override | |
| public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { | |
| super.onCreateContextMenu(menu, v, menuInfo); | |
| if (menuInfo instanceof AdapterContextMenuInfo) { | |
| int position = ((AdapterContextMenuInfo) menuInfo).position; | |
| Object o = mListView.getItemAtPosition(position); | |
| if (o instanceof LocaleInfo) { | |
| if (((LocaleInfo) o).isCustom()) { | |
| menu.setHeaderTitle("System Locale"); | |
| menu.add(0, MENU_APPLY, 0, "Apply"); | |
| } else { | |
| menu.setHeaderTitle("Custom Locale"); | |
| menu.add(0, MENU_APPLY, 0, "Apply"); | |
| menu.add(0, MENU_REMOVE, 0, "Remove"); | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| protected Dialog onCreateDialog(int id) { | |
| if (id == DLG_REMOVE_ID) { | |
| return createRemoveLocaleDialog(); | |
| } | |
| return super.onCreateDialog(id); | |
| } | |
| //--- private parts --- | |
| private void setupLocaleList() { | |
| if (DEBUG) { | |
| Log.d(TAG, "Update locate list"); | |
| } | |
| ArrayList<LocaleInfo> data = new ArrayList<LocaleInfo>(); | |
| // Insert all system locales | |
| String[] locales = getAssets().getLocales(); | |
| for (String locale : locales) { | |
| if (locale != null && locale.length() > 0) { | |
| Locale loc = new Locale(locale); | |
| data.add(new LocaleInfo(locale, loc.getDisplayName())); | |
| } | |
| } | |
| locales = null; | |
| // Insert all custom locales | |
| String customLocales = mPrefs.getString(CUSTOM_LOCALES, ""); | |
| if (DEBUG) { | |
| Log.d(TAG, "customLocales: " + customLocales); | |
| } | |
| for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) { | |
| if (locale != null && locale.length() > 0) { | |
| Locale loc = new Locale(locale); | |
| data.add(new LocaleInfo( | |
| locale, | |
| loc.getDisplayName(), | |
| true /*custom*/)); | |
| } | |
| } | |
| // Sort all locales by code | |
| Collections.sort(data, new Comparator<LocaleInfo>() { | |
| public int compare(LocaleInfo lhs, LocaleInfo rhs) { | |
| return lhs.getLocale().compareTo(rhs.getLocale()); | |
| } | |
| }); | |
| // Update the list view adapter | |
| mListView.setAdapter(new ArrayAdapter<LocaleInfo>( | |
| this, | |
| android.R.layout.simple_list_item_single_choice, | |
| data)); | |
| updateLocaleButtons(); | |
| } | |
| private void changeSystemLocale(String locale) { | |
| if (ChangeLocale.changeSystemLocale(locale)) { | |
| Toast.makeText(this, | |
| getString(R.string.select_locale_1s, locale), | |
| Toast.LENGTH_SHORT) | |
| .show(); | |
| } | |
| } | |
| private void displayCurrentLocale() { | |
| try { | |
| IActivityManager am = ActivityManager.getService(); | |
| Configuration config = am.getConfiguration(); | |
| if (config.locale != null) { | |
| String text = String.format("%1$s - %2$s", | |
| config.locale.toString(), | |
| config.locale.getDisplayName()); | |
| mCurrentLocaleTextView.setText(text); | |
| checkLocaleInList(config.locale.toString()); | |
| } | |
| } catch (RemoteException e) { | |
| if (DEBUG) { | |
| Log.e(TAG, "get current locale failed", e); | |
| } | |
| } | |
| } | |
| /** Find the locale by code to select it in the list view */ | |
| private void checkLocaleInList(String locale) { | |
| ListAdapter a = mListView.getAdapter(); | |
| for (int i = 0; i < a.getCount(); i++) { | |
| Object o = a.getItem(i); | |
| if (o instanceof LocaleInfo) { | |
| String code = ((LocaleInfo) o).getLocale(); | |
| if (code != null && code.equals(locale)) { | |
| mListView.setSelection(i); | |
| mListView.clearChoices(); | |
| mListView.setItemChecked(i, true); | |
| updateLocaleButtons(); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| private LocaleInfo getCheckedLocale() { | |
| int pos = mListView.getCheckedItemPosition(); | |
| ListAdapter a = mListView.getAdapter(); | |
| int n = a.getCount(); | |
| if (pos >= 0 && pos < n) { | |
| Object o = a.getItem(pos); | |
| if (o instanceof LocaleInfo) { | |
| return (LocaleInfo) o; | |
| } | |
| } | |
| return null; | |
| } | |
| /** Update the Select/Remove buttons based on the currently checked locale. */ | |
| private void updateLocaleButtons() { | |
| LocaleInfo info = getCheckedLocale(); | |
| if (info != null) { | |
| // Enable it | |
| mSelectLocaleButton.setEnabled(true); | |
| mSelectLocaleButton.setText( | |
| getString(R.string.select_locale_1s_button, info.getLocale())); | |
| // Enable the remove button only for custom locales and set the tag to the locale | |
| mRemoveLocaleButton.setEnabled(info.isCustom()); | |
| } else { | |
| // If nothing is selected, disable the buttons | |
| mSelectLocaleButton.setEnabled(false); | |
| mSelectLocaleButton.setText(R.string.select_locale_button); | |
| mRemoveLocaleButton.setEnabled(false); | |
| } | |
| } | |
| /** Invoked by the button "Add new..." */ | |
| private void onAddNewLocale() { | |
| Intent i = new Intent(CustomLocaleActivity.this, NewLocaleDialog.class); | |
| startActivityForResult(i, UPDATE_LIST); | |
| } | |
| /** Invoked by the button Select / mSelectLocaleButton */ | |
| private void onSelectLocale() { | |
| LocaleInfo info = getCheckedLocale(); | |
| if (info != null) { | |
| changeSystemLocale(info.getLocale()); | |
| } | |
| } | |
| /** | |
| * Invoked by the button Remove / mRemoveLocaleButton. | |
| * Creates a dialog to ask for confirmation before actually remove the custom locale. | |
| */ | |
| private Dialog createRemoveLocaleDialog() { | |
| LocaleInfo info = getCheckedLocale(); | |
| final String localeToRemove = info == null ? "<error>" : info.getLocale(); | |
| AlertDialog.Builder b = new AlertDialog.Builder(this); | |
| b.setMessage(getString(R.string.confirm_remove_locale_1s, localeToRemove)); | |
| b.setCancelable(false); | |
| b.setPositiveButton(R.string.confirm_remove_locale_yes, | |
| new DialogInterface.OnClickListener() { | |
| @Override | |
| public void onClick(DialogInterface dialog, int which) { | |
| removeCustomLocale(localeToRemove); | |
| dismissDialog(DLG_REMOVE_ID); | |
| } | |
| }); | |
| b.setNegativeButton(R.string.confirm_remove_locale_no, | |
| new DialogInterface.OnClickListener() { | |
| @Override | |
| public void onClick(DialogInterface dialog, int which) { | |
| dismissDialog(DLG_REMOVE_ID); | |
| } | |
| }); | |
| return b.create(); | |
| } | |
| private void removeCustomLocale(String localeToRemove) { | |
| // Get current custom locale list | |
| String oldLocales = mPrefs.getString(CUSTOM_LOCALES, ""); | |
| if (DEBUG) { | |
| Log.d(TAG, "Remove " + localeToRemove + " from custom locales: " + oldLocales); | |
| } | |
| // Update | |
| StringBuilder sb = new StringBuilder(); | |
| for (String locale : oldLocales.split(CUSTOM_LOCALES_SEP)) { | |
| if (locale != null && locale.length() > 0 && !locale.equals(localeToRemove)) { | |
| if (sb.length() > 0) { | |
| sb.append(CUSTOM_LOCALES_SEP); | |
| } | |
| sb.append(locale); | |
| } | |
| } | |
| String newLocales = sb.toString(); | |
| if (!newLocales.equals(oldLocales)) { | |
| // Save prefs | |
| boolean ok = mPrefs.edit().putString(CUSTOM_LOCALES, newLocales).commit(); | |
| if (DEBUG) { | |
| Log.d(TAG, "Prefs commit:" + Boolean.toString(ok) + ". Saved: " + newLocales); | |
| } | |
| Toast.makeText(this, | |
| getString(R.string.removed_custom_locale_1s, localeToRemove), | |
| Toast.LENGTH_SHORT) | |
| .show(); | |
| // Update list view | |
| setupLocaleList(); | |
| } | |
| } | |
| /** | |
| * Immutable structure that holds the information displayed by a list view item. | |
| */ | |
| private static class LocaleInfo { | |
| private final String mLocale; | |
| private final String mDisplayName; | |
| private final boolean mIsCustom; | |
| public LocaleInfo(String locale, String displayName, boolean isCustom) { | |
| mLocale = locale; | |
| mDisplayName = displayName; | |
| mIsCustom = isCustom; | |
| } | |
| public LocaleInfo(String locale, String displayName) { | |
| this(locale, displayName, false /*custom*/); | |
| } | |
| public String getLocale() { | |
| return mLocale; | |
| } | |
| public boolean isCustom() { | |
| return mIsCustom; | |
| } | |
| @Override | |
| public String toString() { | |
| StringBuilder sb = new StringBuilder(); | |
| sb.append(mLocale) | |
| .append(" - ") | |
| .append(mDisplayName); | |
| if (mIsCustom) { | |
| sb.append(" [Custom]"); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| } |