diff options
| author | Raphael Moll <ralf@android.com> | 2011-01-30 17:05:10 -0800 |
|---|---|---|
| committer | Raphael Moll <ralf@android.com> | 2011-01-31 15:22:56 -0800 |
| commit | bc6fa8a82b436912501baabc6cb1b93b6202eb7d (patch) | |
| tree | d38d34f57def81bc0266cb5836411068ae97f617 /apps/CustomLocale | |
| parent | d9af3ac3e2f9f61d541e5c15c3d47bd8e0e6def5 (diff) | |
| download | android_development-bc6fa8a82b436912501baabc6cb1b93b6202eb7d.tar.gz android_development-bc6fa8a82b436912501baabc6cb1b93b6202eb7d.tar.bz2 android_development-bc6fa8a82b436912501baabc6cb1b93b6202eb7d.zip | |
Revamped CustomLocale for the SDK.
- Revamped the list UI: replaced the long-press actions by
some stateful buttons. Uses the listview's single choice mode.
- Added a broadcast receiver to be able to change the system
locale using an intent, e.g. sent by adb shell am. This is
convenient for automated testing scripts.
- Changed package namespace to v2. This makes it easier for
an automated testing script to figure that an emulator has the
old version and install the new one side-by-side.
Change-Id: I1849d2b9d55441490b013cfefcca5d8fa48e22bd
Diffstat (limited to 'apps/CustomLocale')
| -rw-r--r-- | apps/CustomLocale/AndroidManifest.xml | 31 | ||||
| -rw-r--r-- | apps/CustomLocale/res/layout/list_item.xml | 35 | ||||
| -rw-r--r-- | apps/CustomLocale/res/layout/main.xml | 74 | ||||
| -rw-r--r-- | apps/CustomLocale/res/values-fr/strings.xml | 33 | ||||
| -rw-r--r-- | apps/CustomLocale/res/values/strings.xml | 35 | ||||
| -rw-r--r-- | apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java | 334 | ||||
| -rwxr-xr-x | apps/CustomLocale/src/com/android/customlocale2/ChangeLocale.java | 78 | ||||
| -rw-r--r-- | apps/CustomLocale/src/com/android/customlocale2/CustomLocaleActivity.java | 454 | ||||
| -rwxr-xr-x | apps/CustomLocale/src/com/android/customlocale2/CustomLocaleReceiver.java | 82 | ||||
| -rw-r--r-- | apps/CustomLocale/src/com/android/customlocale2/NewLocaleDialog.java (renamed from apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java) | 8 |
10 files changed, 754 insertions, 410 deletions
diff --git a/apps/CustomLocale/AndroidManifest.xml b/apps/CustomLocale/AndroidManifest.xml index 9dfa89630..0a446de3e 100644 --- a/apps/CustomLocale/AndroidManifest.xml +++ b/apps/CustomLocale/AndroidManifest.xml @@ -13,25 +13,40 @@ See the License for the specific language governing permissions and limitations under the License. --> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" - android:versionName="1.0" package="com.android.customlocale"> + android:versionName="1.0" + package="com.android.customlocale2" + > + + <uses-sdk android:minSdkVersion="3" /> + + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + <application android:icon="@drawable/icon" - android:label="@string/app_name"> + android:label="@string/app_name" > + <activity - android:label="@string/app_name" android:name="CustomLocaleActivity"> + android:label="@string/app_name" + android:name="CustomLocaleActivity" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity - android:name="NewLocaleDialog" - android:theme="@android:style/Theme.Dialog" /> + android:theme="@android:style/Theme.Dialog" + android:name="NewLocaleDialog" /> + + <receiver android:name="CustomLocaleReceiver" > + <intent-filter> + <action android:name="com.android.intent.action.SET_LOCALE" /> + </intent-filter> + </receiver> </application> - <uses-sdk android:minSdkVersion="3" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> - <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> </manifest> diff --git a/apps/CustomLocale/res/layout/list_item.xml b/apps/CustomLocale/res/layout/list_item.xml deleted file mode 100644 index 0ffb8b064..000000000 --- a/apps/CustomLocale/res/layout/list_item.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="5dip"> - <TextView - android:id="@+id/locale_code" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:textAppearanceLarge" - android:layout_weight="1" - android:text="@string/locale_default" /> - <TextView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/locale_name" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textAppearance="?android:textAppearance" - android:layout_weight="1" /> -</LinearLayout>
\ No newline at end of file diff --git a/apps/CustomLocale/res/layout/main.xml b/apps/CustomLocale/res/layout/main.xml index 55bd90cc6..e700a8ba1 100644 --- a/apps/CustomLocale/res/layout/main.xml +++ b/apps/CustomLocale/res/layout/main.xml @@ -17,7 +17,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" @@ -25,13 +26,15 @@ android:text="@string/header_current_locale" android:textAppearance="@style/TextAppearance.header" android:gravity="center_horizontal" - android:background="@color/header_background" /> + android:background="@color/header_background" + /> <TextView android:id="@+id/current_locale" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0" - android:padding="5dip" /> + android:padding="5dip" + /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" @@ -39,24 +42,61 @@ android:text="@string/header_locale_list" android:textAppearance="@style/TextAppearance.header" android:gravity="center_horizontal" - android:background="@color/header_background" /> - <ListView - android:id="@id/android:list" + android:background="@color/header_background" + /> + + <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" - android:padding="8dip" /> - <TextView - android:id="@id/android:empty" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:text="@string/no_data_label" /> - <Button - android:id="@+id/new_locale" + > + <ListView + android:id="@id/android:list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fastScrollEnabled="true" + android:focusable="true" + android:focusableInTouchMode="true" + android:padding="8dip" + /> + <TextView + android:id="@id/android:empty" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="@string/no_data_label" + android:gravity="center" + /> + </FrameLayout> + + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0" - android:paddingLeft="8dip" - android:paddingRight="8dip" - android:text="@string/add_new_locale_button" /> + > + <Button + android:id="@+id/select_locale_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="0.9" + android:layout_gravity="center_vertical" + android:text="@string/select_locale_button" + /> + <Button + android:id="@+id/add_new_locale_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:text="@string/add_new_locale_button" + /> + <Button + android:id="@+id/remove_locale_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginLeft="5dip" + android:layout_gravity="center_vertical" + android:text="@string/remove_locale_button" + /> + </LinearLayout> </LinearLayout> diff --git a/apps/CustomLocale/res/values-fr/strings.xml b/apps/CustomLocale/res/values-fr/strings.xml index 8852a9aaa..8c9bc3867 100644 --- a/apps/CustomLocale/res/values-fr/strings.xml +++ b/apps/CustomLocale/res/values-fr/strings.xml @@ -15,11 +15,34 @@ --> <resources> <string name="app_name">Locales Personalisées</string> - <string name="add_new_locale_button">Ajouter une nouvelle locale</string> - <string name="new_locale_label">Code de la nouvelle locale:</string> - <string name="add_button">Ajouter</string> - <string name="no_data_label">Aucune locale</string> + + <!-- Top labels in CustomLocaleActivity --> + <string name="header_current_locale">Locale courrante</string> <string name="header_locale_list">Liste des locales</string> - <string name="add_select_button">Ajouter et sélectionner</string> + <string name="no_data_label">Aucune locale</string> + + <!-- Buttons in CustomLocaleActivity --> + + <string name="select_locale_button">Choisir</string> + <string name="select_locale_1s_button">Choisir \'%1$s\'</string> + <string name="add_new_locale_button">Ajouter\u2026</string> + <string name="remove_locale_button">Retirer\u2026</string> + + <!-- Toast status in CustomLocaleActivity --> + + <string name="removed_custom_locale_1s">Locale supprimée: %1$s</string> + <string name="select_locale_1s">Locale choisie: %1$s</string> + <string name="added_custom_locale_1s">Locale ajoutée: %1$s</string> + + <!-- NewLocaleDialog --> + + <string name="new_locale_label">Code de la nouvelle locale:</string> + <string name="add_button">Ajouter</string> + <string name="add_select_button">Ajouter et choisir</string> + + <string name="confirm_remove_locale_1s">Voulez-vous supprimer la locale personalisée \'%1$s\' ?</string> + <string name="confirm_remove_locale_yes">Oui</string> + <string name="confirm_remove_locale_no">Non</string> + </resources> diff --git a/apps/CustomLocale/res/values/strings.xml b/apps/CustomLocale/res/values/strings.xml index 313fb87e4..955521cc5 100644 --- a/apps/CustomLocale/res/values/strings.xml +++ b/apps/CustomLocale/res/values/strings.xml @@ -15,12 +15,37 @@ --> <resources> <string name="app_name">Custom Locale</string> - <string name="locale_default">ex_EX</string> - <string name="add_new_locale_button">Add New Locale</string> - <string name="new_locale_label">New locale code:</string> - <string name="add_button">Add</string> - <string name="no_data_label">No data</string> + + <!-- Top labels in CustomLocaleActivity --> + <string name="header_current_locale">Current Locale</string> <string name="header_locale_list">Locale List</string> + <string name="no_data_label">No data</string> + + <!-- Buttons in CustomLocaleActivity --> + + <string name="select_locale_button">Select</string> + <string name="select_locale_1s_button">Select \'%1$s\'</string> + <string name="add_new_locale_button">Add New\u2026</string> + <string name="remove_locale_button">Remove\u2026</string> + + <!-- Toast status in CustomLocaleActivity --> + + <string name="removed_custom_locale_1s">Removed custom locale: %1$s</string> + <string name="select_locale_1s">Select locale: %1$s</string> + <string name="added_custom_locale_1s">Added custom locale: %1$s</string> + + <!-- NewLocaleDialog --> + + <string name="new_locale_label">New locale code:</string> + <string name="add_button">Add</string> <string name="add_select_button">Add and Select</string> + + <string name="confirm_remove_locale_1s">Do you want to remove the custom locale \'%1$s\'?</string> + <string name="confirm_remove_locale_yes">Yes</string> + <string name="confirm_remove_locale_no">No</string> + + <!-- Locale hint in textfield. Not translated --> + <string name="locale_default">ex_EX</string> + </resources> diff --git a/apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java b/apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java deleted file mode 100644 index 69b9c5bf2..000000000 --- a/apps/CustomLocale/src/com/android/customlocale/CustomLocaleActivity.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * 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.customlocale; - - -import android.app.ActivityManagerNative; -import android.app.IActivityManager; -import android.app.ListActivity; -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.MenuItem; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.Button; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.SimpleAdapter; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.AdapterView.AdapterContextMenuInfo; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * 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 CUSTOM_LOCALES_SEP = " "; - private static final String CUSTOM_LOCALES = "custom_locales"; - private static final String KEY_CUSTOM = "custom"; - private static final String KEY_NAME = "name"; - private static final String KEY_CODE = "code"; - - private static final String TAG = "LocaleSetup"; - private static final boolean DEBUG = true; - - /** 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; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - mPrefs = getPreferences(MODE_PRIVATE); - - Button newLocaleButton = (Button) findViewById(R.id.new_locale); - - newLocaleButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - Intent i = new Intent(CustomLocaleActivity.this, NewLocaleDialog.class); - startActivityForResult(i, UPDATE_LIST); - } - }); - - mListView = (ListView) findViewById(android.R.id.list); - mListView.setFocusable(true); - mListView.setFocusableInTouchMode(true); - mListView.requestFocus(); - registerForContextMenu(mListView); - setupLocaleList(); - - mCurrentLocaleTextView = (TextView) findViewById(R.id.current_locale); - displayCurrentLocale(); - } - - @SuppressWarnings("unchecked") - @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, "Added custom locale: " + locale, Toast.LENGTH_SHORT).show(); - - // Update list view - setupLocaleList(); - - // Find the item to select it in the list view - ListAdapter a = mListView.getAdapter(); - for (int i = 0; i < a.getCount(); i++) { - Object o = a.getItem(i); - if (o instanceof Map<?, ?>) { - String code = ((Map<String, String>) o).get(KEY_CODE); - if (code != null && code.equals(locale)) { - mListView.setSelection(i); - break; - } - } - } - - if (data.getExtras().getBoolean(NewLocaleDialog.INTENT_EXTRA_SELECT)) { - selectLocale(locale); - } - } - } - } - - private void setupLocaleList() { - if (DEBUG) { - Log.d(TAG, "Update locate list"); - } - - ArrayList<Map<String, String>> data = new ArrayList<Map<String, String>>(); - - // Insert all system locales - String[] locales = getAssets().getLocales(); - for (String locale : locales) { - Locale loc = new Locale(locale); - - Map<String, String> map = new HashMap<String, String>(1); - map.put(KEY_CODE, locale); - map.put(KEY_NAME, loc.getDisplayName()); - data.add(map); - } - 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); - - Map<String, String> map = new HashMap<String, String>(1); - map.put(KEY_CODE, locale); - map.put(KEY_NAME, loc.getDisplayName() + " [Custom]"); - // the presence of the "custom" key marks it as custom. - map.put(KEY_CUSTOM, ""); - data.add(map); - } - } - - // Sort all locales by code - Collections.sort(data, new Comparator<Map<String, String>>() { - public int compare(Map<String, String> lhs, Map<String, String> rhs) { - return lhs.get(KEY_CODE).compareTo(rhs.get(KEY_CODE)); - } - }); - - // Update the list view adapter - mListView.setAdapter(new SimpleAdapter(this, data, R.layout.list_item, new String[] { - KEY_CODE, KEY_NAME}, new int[] {R.id.locale_code, R.id.locale_name})); - } - - @SuppressWarnings("unchecked") - @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 Map<?, ?>) { - String locale = ((Map<String, String>) o).get(KEY_CODE); - String custom = ((Map<String, String>) o).get(KEY_CUSTOM); - - if (custom == null) { - 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"); - } - } - } - } - - @SuppressWarnings("unchecked") - @Override - public boolean onContextItemSelected(MenuItem item) { - - String pendingLocale = null; - boolean is_custom = false; - - ContextMenuInfo menuInfo = item.getMenuInfo(); - if (menuInfo instanceof AdapterContextMenuInfo) { - int position = ((AdapterContextMenuInfo) menuInfo).position; - Object o = mListView.getItemAtPosition(position); - if (o instanceof Map<?, ?>) { - pendingLocale = ((Map<String, String>) o).get(KEY_CODE); - is_custom = ((Map<String, String>) o).get(KEY_CUSTOM) != null; - } - } - - if (pendingLocale == null) { - // should never happen - return super.onContextItemSelected(item); - } - - if (item.getItemId() == MENU_REMOVE) { - // Get current custom locale list - String customLocales = mPrefs.getString(CUSTOM_LOCALES, ""); - - if (DEBUG) { - Log.d(TAG, "Remove " + pendingLocale + " from custom locales: " + customLocales); - } - - // Update - StringBuilder sb = new StringBuilder(); - for (String locale : customLocales.split(CUSTOM_LOCALES_SEP)) { - if (locale != null && locale.length() > 0 && !locale.equals(pendingLocale)) { - if (sb.length() > 0) { - sb.append(CUSTOM_LOCALES_SEP); - } - sb.append(locale); - } - } - String newLocales = sb.toString(); - if (!newLocales.equals(customLocales)) { - // Save prefs - mPrefs.edit().putString(CUSTOM_LOCALES, customLocales).commit(); - - Toast.makeText(this, "Removed custom locale: " + pendingLocale, Toast.LENGTH_SHORT) - .show(); - } - - } else if (item.getItemId() == MENU_APPLY) { - selectLocale(pendingLocale); - } - - return super.onContextItemSelected(item); - } - - private void selectLocale(String locale) { - if (DEBUG) { - Log.d(TAG, "Select locale " + locale); - } - - try { - IActivityManager am = ActivityManagerNative.getDefault(); - Configuration config = am.getConfiguration(); - - Locale loc = null; - - String[] langCountry = locale.split("_"); - if (langCountry.length == 2) { - loc = new Locale(langCountry[0], langCountry[1]); - } else { - loc = new Locale(locale); - } - - config.locale = loc; - - // indicate this isn't some passing default - the user wants this - // remembered - config.userSetLocale = true; - - am.updateConfiguration(config); - - Toast.makeText(this, "Select locale: " + locale, Toast.LENGTH_SHORT).show(); - } catch (RemoteException e) { - if (DEBUG) { - Log.e(TAG, "Select locale failed", e); - } - } - } - - private void displayCurrentLocale() { - try { - IActivityManager am = ActivityManagerNative.getDefault(); - Configuration config = am.getConfiguration(); - - if (config.locale != null) { - String text = String.format("%s - %s", - config.locale.toString(), - config.locale.getDisplayName()); - mCurrentLocaleTextView.setText(text); - } - } catch (RemoteException e) { - if (DEBUG) { - Log.e(TAG, "get current locale failed", e); - } - } - } -} diff --git a/apps/CustomLocale/src/com/android/customlocale2/ChangeLocale.java b/apps/CustomLocale/src/com/android/customlocale2/ChangeLocale.java new file mode 100755 index 000000000..8757064ea --- /dev/null +++ b/apps/CustomLocale/src/com/android/customlocale2/ChangeLocale.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2011 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.Locale; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.content.res.Configuration; +import android.os.RemoteException; +import android.util.Log; + +/** + * Helper class to change the system locale. + */ +public final class ChangeLocale { + + private static final String TAG = ChangeLocale.class.getSimpleName(); + private static final boolean DEBUG = true; + + /** + * Sets the system locale to the new one specified. + * + * @param locale A locale name in the form "ab_AB". Must not be null or empty. + * @return True if the locale was succesfully changed. + */ + public static boolean changeSystemLocale(String locale) { + if (DEBUG) { + Log.d(TAG, "Change locale to: " + locale); + } + + try { + IActivityManager am = ActivityManagerNative.getDefault(); + Configuration config = am.getConfiguration(); + + Locale loc = null; + + String[] langCountry = locale.split("_"); + if (langCountry.length == 2) { + loc = new Locale(langCountry[0], langCountry[1]); + } else { + loc = new Locale(locale); + } + + config.locale = loc; + + // indicate this isn't some passing default - the user wants this + // remembered + config.userSetLocale = true; + + am.updateConfiguration(config); + + return true; + + } catch (RemoteException e) { + if (DEBUG) { + Log.e(TAG, "Change locale failed", e); + } + } + + return false; + } +} diff --git a/apps/CustomLocale/src/com/android/customlocale2/CustomLocaleActivity.java b/apps/CustomLocale/src/com/android/customlocale2/CustomLocaleActivity.java new file mode 100644 index 000000000..3bf7993e6 --- /dev/null +++ b/apps/CustomLocale/src/com/android/customlocale2/CustomLocaleActivity.java @@ -0,0 +1,454 @@ +/* + * 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.ActivityManagerNative; +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 = ActivityManagerNative.getDefault(); + 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(); + } + } +} diff --git a/apps/CustomLocale/src/com/android/customlocale2/CustomLocaleReceiver.java b/apps/CustomLocale/src/com/android/customlocale2/CustomLocaleReceiver.java new file mode 100755 index 000000000..30d886e1e --- /dev/null +++ b/apps/CustomLocale/src/com/android/customlocale2/CustomLocaleReceiver.java @@ -0,0 +1,82 @@ +/*
+ * Copyright (C) 2011 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 android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+//-----------------------------------------------
+
+/**
+ * Broadcast receiver that can change the system's locale.
+ * <p/>
+ * This allows an external script such as an automated testing framework
+ * to easily trigger a locale change on an emulator as such:
+ * <pre>
+ * $ adb shell am broadcast -a com.android.intent.action.SET_LOCALE \
+ * --es com.android.intent.extra.LOCALE en_US
+ * </pre>
+ */
+public class CustomLocaleReceiver extends BroadcastReceiver {
+
+ private static final String TAG = CustomLocaleReceiver.class.getSimpleName();
+ private static final boolean DEBUG = true;
+
+ /** Intent action that triggers this receiver. */
+ public static final String ACTION_SET_LOCALE = "com.android.intent.action.SET_LOCALE";
+ /** An extra String that specifies the locale to set, in the form "en_US". */
+ public static final String EXTRA_LOCALE = "com.android.intent.extra.LOCALE";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ setResult(Activity.RESULT_CANCELED, null, null);
+ if (intent == null || ! ACTION_SET_LOCALE.equals(intent.getAction())) {
+ if (DEBUG) {
+ Log.d(TAG, "Invalid intent: " + (intent == null ? "null" : intent.toString()));
+ }
+ return;
+ }
+
+ String locale = intent.getStringExtra(EXTRA_LOCALE);
+
+ // Enforce the locale string is either in the form "ab" or "ab_cd"
+ boolean is_ok = locale != null;
+ is_ok = is_ok && (locale.length() == 2 || locale.length() == 5);
+ if (is_ok && locale.length() >= 2) {
+ is_ok = Character.isLetter(locale.charAt(0)) &&
+ Character.isLetter(locale.charAt(1));
+ }
+ if (is_ok && locale.length() == 5) {
+ is_ok = locale.charAt(2) == '_' &&
+ Character.isLetter(locale.charAt(3)) &&
+ Character.isLetter(locale.charAt(4));
+ }
+
+ if (!is_ok && DEBUG) {
+ Log.e(TAG, "Invalid locale: expected ab_CD but got " + locale);
+ } else if (is_ok) {
+ ChangeLocale.changeSystemLocale(locale);
+ setResult(Activity.RESULT_OK, locale, null);
+ }
+ }
+
+}
+
+
diff --git a/apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java b/apps/CustomLocale/src/com/android/customlocale2/NewLocaleDialog.java index 04f72223c..3764fd6fe 100644 --- a/apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java +++ b/apps/CustomLocale/src/com/android/customlocale2/NewLocaleDialog.java @@ -14,14 +14,12 @@ * limitations under the License. */ -package com.android.customlocale; +package com.android.customlocale2; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.text.TextUtils; import android.util.Log; -import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -41,7 +39,6 @@ public class NewLocaleDialog extends Activity implements View.OnClickListener { private Button mButtonAdd; private Button mButtonAddSelect; private EditText mEditText; - private boolean mWasEmpty; @Override protected void onCreate(Bundle savedInstanceState) { @@ -50,7 +47,6 @@ public class NewLocaleDialog extends Activity implements View.OnClickListener { setContentView(R.layout.new_locale); mEditText = (EditText) findViewById(R.id.value); - mWasEmpty = true; mButtonAdd = (Button) findViewById(R.id.add); mButtonAdd.setOnClickListener(this); @@ -62,7 +58,7 @@ public class NewLocaleDialog extends Activity implements View.OnClickListener { public void onClick(View v) { String locale = mEditText.getText().toString(); boolean select = v == mButtonAddSelect; - + if (DEBUG) { Log.d(TAG, "New Locale: " + locale + (select ? " + select" : "")); } |
