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
diff --git a/apps/CustomLocale/AndroidManifest.xml b/apps/CustomLocale/AndroidManifest.xml
index 9dfa896..0a446de 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 0ffb8b0..0000000
--- 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 55bd90c..e700a8b 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 8852a9a..8c9bc38 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 313fb87..955521c 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 69b9c5b..0000000
--- 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 0000000..8757064
--- /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 0000000..3bf7993
--- /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 0000000..30d886e
--- /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
similarity index 93%
rename from apps/CustomLocale/src/com/android/customlocale/NewLocaleDialog.java
rename to apps/CustomLocale/src/com/android/customlocale2/NewLocaleDialog.java
index 04f7222..3764fd6 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 @@
private Button mButtonAdd;
private Button mButtonAddSelect;
private EditText mEditText;
- private boolean mWasEmpty;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -50,7 +47,6 @@
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 void onClick(View v) {
String locale = mEditText.getText().toString();
boolean select = v == mButtonAddSelect;
-
+
if (DEBUG) {
Log.d(TAG, "New Locale: " + locale + (select ? " + select" : ""));
}